001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.dsmlv2; 021 022 023import java.util.Arrays; 024import java.util.Collection; 025 026import javax.xml.transform.Transformer; 027import javax.xml.transform.TransformerConfigurationException; 028import javax.xml.transform.TransformerException; 029import javax.xml.transform.TransformerFactory; 030import javax.xml.transform.stream.StreamSource; 031 032import org.apache.directory.api.dsmlv2.actions.ReadSoapHeader; 033import org.apache.directory.api.dsmlv2.request.BatchRequestDsml; 034import org.apache.directory.api.dsmlv2.request.BatchRequestDsml.Processing; 035import org.apache.directory.api.dsmlv2.request.BatchRequestDsml.ResponseOrder; 036import org.apache.directory.api.i18n.I18n; 037import org.apache.directory.api.ldap.codec.api.CodecControl; 038import org.apache.directory.api.ldap.codec.api.LdapApiService; 039import org.apache.directory.api.ldap.model.entry.BinaryValue; 040import org.apache.directory.api.ldap.model.entry.StringValue; 041import org.apache.directory.api.ldap.model.ldif.LdifUtils; 042import org.apache.directory.api.ldap.model.message.Control; 043import org.apache.directory.api.util.Base64; 044import org.apache.directory.api.util.Strings; 045import org.dom4j.Document; 046import org.dom4j.Element; 047import org.dom4j.Namespace; 048import org.dom4j.QName; 049import org.dom4j.io.DocumentResult; 050import org.dom4j.io.DocumentSource; 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053import org.xmlpull.v1.XmlPullParser; 054import org.xmlpull.v1.XmlPullParserException; 055 056 057/** 058 * This class is a Helper class for the DSML Parser 059 * 060 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 061 */ 062public final class ParserUtils 063{ 064 /** W3C XML Schema URI. */ 065 public static final String XML_SCHEMA_URI = "http://www.w3.org/2001/XMLSchema"; 066 067 /** W3C XML Schema Instance URI. */ 068 public static final String XML_SCHEMA_INSTANCE_URI = "http://www.w3.org/2001/XMLSchema-instance"; 069 070 /** Base-64 identifier. */ 071 public static final String BASE64BINARY = "base64Binary"; 072 073 /** XSI namespace prefix. */ 074 public static final String XSI = "xsi"; 075 076 /** XSD namespace prefix. */ 077 public static final String XSD = "xsd"; 078 079 /** The DSML namespace */ 080 public static final Namespace DSML_NAMESPACE = new Namespace( null, "urn:oasis:names:tc:DSML:2:0:core" ); 081 082 /** The XSD namespace */ 083 public static final Namespace XSD_NAMESPACE = new Namespace( XSD, XML_SCHEMA_URI ); 084 085 /** The XSI namespace */ 086 public static final Namespace XSI_NAMESPACE = new Namespace( XSI, XML_SCHEMA_INSTANCE_URI ); 087 088 /** A logger for this class */ 089 private static final Logger LOG = LoggerFactory.getLogger( ParserUtils.class ); 090 091 092 private ParserUtils() 093 { 094 } 095 096 097 /** 098 * Returns the value of the attribute 'type' of the "XMLSchema-instance' namespace if it exists 099 * 100 * @param xpp the XPP parser to use 101 * @return the value of the attribute 'type' of the "XMLSchema-instance' namespace if it exists 102 */ 103 public static String getXsiTypeAttributeValue( XmlPullParser xpp ) 104 { 105 String type = null; 106 int nbAttributes = xpp.getAttributeCount(); 107 108 for ( int i = 0; i < nbAttributes; i++ ) 109 { 110 // Checking if the attribute 'type' from XML Schema Instance namespace is used. 111 if ( xpp.getAttributeName( i ).equals( "type" ) 112 && xpp.getNamespace( xpp.getAttributePrefix( i ) ).equals( XML_SCHEMA_INSTANCE_URI ) ) 113 { 114 type = xpp.getAttributeValue( i ); 115 break; 116 } 117 } 118 119 return type; 120 } 121 122 123 /** 124 * Tells is the given value is a Base64 binary value 125 * 126 * @param parser the XPP parser to use 127 * @param attrValue the attribute value 128 * @return true if the value of the current tag is Base64BinaryEncoded, false if not 129 */ 130 public static boolean isBase64BinaryValue( XmlPullParser parser, String attrValue ) 131 { 132 if ( attrValue == null ) 133 { 134 return false; 135 } 136 137 // We are looking for something that should look like that: "aNameSpace:base64Binary" 138 // We split the String. The first element should be the namespace prefix and the second "base64Binary" 139 String[] splitedString = attrValue.split( ":" ); 140 141 return ( splitedString.length == 2 ) && ( XML_SCHEMA_URI.equals( parser.getNamespace( splitedString[0] ) ) ) 142 && ( BASE64BINARY.equals( splitedString[1] ) ); 143 } 144 145 146 /** 147 * Indicates if the value needs to be encoded as Base64 148 * 149 * @param value the value to check 150 * @return true if the value needs to be encoded as Base64 151 */ 152 public static boolean needsBase64Encoding( Object value ) 153 { 154 if ( value instanceof StringValue ) 155 { 156 return false; 157 } 158 else if ( value instanceof BinaryValue ) 159 { 160 return false; 161 } 162 else if ( value instanceof byte[] ) 163 { 164 return true; 165 } 166 else if ( value instanceof String ) 167 { 168 return !LdifUtils.isLDIFSafe( ( String ) value ); 169 } 170 171 return true; 172 } 173 174 175 /** 176 * Encodes the value as a Base64 String 177 * 178 * @param value the value to encode 179 * @return the value encoded as a Base64 String 180 */ 181 public static String base64Encode( Object value ) 182 { 183 if ( value instanceof byte[] ) 184 { 185 return new String( Base64.encode( ( byte[] ) value ) ); 186 } 187 else if ( value instanceof String ) 188 { 189 return new String( Base64.encode( Strings.getBytesUtf8( ( String ) value ) ) ); 190 } 191 192 return ""; 193 } 194 195 196 /** 197 * Parses and verify the parsed value of the requestID 198 * 199 * @param attributeValue the value of the attribute 200 * @param xpp the XmlPullParser 201 * @return the int value of the resquestID 202 * @throws XmlPullParserException if RequestID isn't an Integer and if requestID is below 0 203 */ 204 public static int parseAndVerifyRequestID( String attributeValue, XmlPullParser xpp ) throws XmlPullParserException 205 { 206 try 207 { 208 int requestID = Integer.parseInt( attributeValue ); 209 210 if ( requestID < 0 ) 211 { 212 throw new XmlPullParserException( I18n.err( I18n.ERR_03038, requestID ), xpp, null ); 213 } 214 215 return requestID; 216 } 217 catch ( NumberFormatException nfe ) 218 { 219 throw new XmlPullParserException( I18n.err( I18n.ERR_03039 ), xpp, nfe ); 220 } 221 } 222 223 224 /** 225 * Adds Controls to the given Element. 226 * 227 * @param codec The LDAP Service to use 228 * @param element the element to add the Controls to 229 * @param controls a List of Controls 230 */ 231 public static void addControls( LdapApiService codec, Element element, Collection<Control> controls ) 232 { 233 if ( controls != null ) 234 { 235 for ( Control control : controls ) 236 { 237 Element controlElement = element.addElement( "control" ); 238 239 if ( control.getOid() != null ) 240 { 241 controlElement.addAttribute( "type", control.getOid() ); 242 } 243 244 if ( control.isCritical() ) 245 { 246 controlElement.addAttribute( "criticality", "true" ); 247 } 248 249 byte[] value; 250 251 if ( control instanceof CodecControl<?> ) 252 { 253 value = ( ( org.apache.directory.api.ldap.codec.api.CodecControl<?> ) control ).getValue(); 254 } 255 else 256 { 257 value = codec.newControl( control ).getValue(); 258 } 259 260 if ( value != null ) 261 { 262 if ( ParserUtils.needsBase64Encoding( value ) ) 263 { 264 element.getDocument().getRootElement().add( XSD_NAMESPACE ); 265 element.getDocument().getRootElement().add( XSI_NAMESPACE ); 266 267 Element valueElement = controlElement.addElement( "controlValue" ).addText( 268 ParserUtils.base64Encode( value ) ); 269 valueElement.addAttribute( new QName( "type", XSI_NAMESPACE ), ParserUtils.XSD + ":" 270 + ParserUtils.BASE64BINARY ); 271 } 272 else 273 { 274 controlElement.addElement( "controlValue" ).setText( Arrays.toString( value ) ); 275 } 276 } 277 } 278 } 279 } 280 281 282 /** 283 * Indicates if a request ID is needed. 284 * 285 * @param container the associated container 286 * @return true if a request ID is needed (ie Processing=Parallel and ResponseOrder=Unordered) 287 * @throws XmlPullParserException if the batch request has not been parsed yet 288 */ 289 public static boolean isRequestIdNeeded( Dsmlv2Container container ) throws XmlPullParserException 290 { 291 BatchRequestDsml batchRequest = container.getBatchRequest(); 292 293 if ( batchRequest == null ) 294 { 295 throw new XmlPullParserException( I18n.err( I18n.ERR_03040 ), container.getParser(), null ); 296 } 297 298 return ( ( batchRequest.getProcessing() == Processing.PARALLEL ) && ( batchRequest.getResponseOrder() == ResponseOrder.UNORDERED ) ); 299 } 300 301 302 /** 303 * XML Pretty Printer XSLT Transformation 304 * 305 * @param document the Dom4j Document 306 * @return the transformed document 307 */ 308 public static Document styleDocument( Document document ) 309 { 310 // load the transformer using JAXP 311 TransformerFactory factory = TransformerFactory.newInstance(); 312 Transformer transformer = null; 313 314 try 315 { 316 transformer = factory.newTransformer( new StreamSource( ParserUtils.class 317 .getResourceAsStream( "/org/apache/directory/shared/dsmlv2/DSMLv2.xslt" ) ) ); 318 } 319 catch ( TransformerConfigurationException e1 ) 320 { 321 LOG.warn( "Failed to create the XSLT transformer", e1 ); 322 // return original document 323 return document; 324 } 325 326 // now lets style the given document 327 DocumentSource source = new DocumentSource( document ); 328 DocumentResult result = new DocumentResult(); 329 330 try 331 { 332 transformer.transform( source, result ); 333 } 334 catch ( TransformerException e ) 335 { 336 // return original document 337 return document; 338 } 339 340 // return the transformed document 341 Document transformedDoc = result.getDocument(); 342 return transformedDoc; 343 } 344 345 /** 346 * GrammarAction that reads the SOAP header data 347 */ 348 public static final GrammarAction READ_SOAP_HEADER = new ReadSoapHeader(); 349}