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.ldap.codec.standalone; 021 022 023import java.lang.reflect.Constructor; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.directory.api.i18n.I18n; 029import org.apache.directory.api.ldap.codec.StockCodecFactoryUtil; 030import org.apache.directory.api.ldap.codec.api.ControlFactory; 031import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory; 032import org.apache.directory.api.ldap.codec.api.IntermediateOperationFactory; 033import org.apache.directory.api.ldap.codec.api.LdapApiService; 034import org.apache.directory.api.ldap.codec.osgi.DefaultLdapCodecService; 035import org.apache.directory.api.ldap.extras.ExtrasCodecFactoryUtil; 036import org.apache.directory.api.ldap.model.message.Control; 037import org.apache.directory.api.util.Strings; 038import org.apache.mina.filter.codec.ProtocolCodecFactory; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042 043/** 044 * The default {@link org.apache.directory.api.ldap.codec.api.LdapApiService} implementation. 045 * It loads the Controls, ExtendedOperations and IntermediateResponses as defined in the following system parameters : 046 * <ul> 047 * <li>Controls : 048 * <ul> 049 * <li>apacheds.request.controls</li> 050 * <li>apacheds.response.controls</li> 051 * <li>default.controls</li> 052 * </ul> 053 * </li> 054 * <li>ExtendedOperations : 055 * <ul> 056 * <li>apacheds.extendedOperations</li> 057 * <li>extra.extendedOperations</li> 058 * </ul> 059 * </li> 060 * <li>IntermediateResponses : 061 * <ul> 062 * <li>apacheds.intermediateResponses</li> 063 * </ul> 064 * </li> 065 * </ul> 066 * 067 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 068 * @version $Rev$, $Date$ 069 */ 070public class StandaloneLdapApiService extends DefaultLdapCodecService 071{ 072 /** A logger */ 073 private static final Logger LOG = LoggerFactory.getLogger( StandaloneLdapApiService.class ); 074 075 /** The list of request controls to load at startup */ 076 public static final String REQUEST_CONTROLS_LIST = "apacheds.request.controls"; 077 078 /** The list of response controls to load at startup */ 079 public static final String RESPONSE_CONTROLS_LIST = "apacheds.response.controls"; 080 081 /** The list of extended operations to load at startup */ 082 public static final String EXTENDED_OPERATIONS_LIST = "apacheds.extendedOperations"; 083 084 /** The list of Intermediate responses to load at startup */ 085 public static final String INTERMEDIATE_RESPONSES_LIST = "apacheds.intermediateResponses"; 086 087 /** The (old) list of default controls to load at startup */ 088 private static final String OLD_DEFAULT_CONTROLS_LIST = "default.controls"; 089 090 /** The (old) list of extra extended operations to load at startup */ 091 private static final String OLD_EXTRA_EXTENDED_OPERATION_LIST = "extra.extendedOperations"; 092 093 /** The control's type */ 094 public enum ControlType 095 { 096 REQUEST( REQUEST_CONTROLS_LIST ), 097 RESPONSE( RESPONSE_CONTROLS_LIST ); 098 099 private String property; 100 101 ControlType( String property ) 102 { 103 this.property = property; 104 } 105 106 private String getProperty() 107 { 108 return property; 109 } 110 } 111 112 113 /** 114 * Creates a new instance of StandaloneLdapCodecService. 115 * <br><br> 116 * The following pom configuration is intended for use by unit test running 117 * tools like Maven's surefire: 118 * <pre> 119 * <properties> 120 * <codec.plugin.directory>${project.build.directory}/pluginDirectory</codec.plugin.directory> 121 * </properties> 122 * 123 * <build> 124 * <plugins> 125 * <plugin> 126 * <artifactId>maven-surefire-plugin</artifactId> 127 * <groupId>org.apache.maven.plugins</groupId> 128 * <configuration> 129 * <systemPropertyVariables> 130 * <workingDirectory>${basedir}/target</workingDirectory> 131 * <felix.cache.rootdir> 132 * ${project.build.directory} 133 * </felix.cache.rootdir> 134 * <felix.cache.locking> 135 * true 136 * </felix.cache.locking> 137 * <org.osgi.framework.storage.clean> 138 * onFirstInit 139 * </org.osgi.framework.storage.clean> 140 * <org.osgi.framework.storage> 141 * osgi-cache 142 * </org.osgi.framework.storage> 143 * <codec.plugin.directory> 144 * ${codec.plugin.directory} 145 * </codec.plugin.directory> 146 * </systemPropertyVariables> 147 * </configuration> 148 * </plugin> 149 * 150 * <plugin> 151 * <groupId>org.apache.maven.plugins</groupId> 152 * <artifactId>maven-dependency-plugin</artifactId> 153 * <executions> 154 * <execution> 155 * <id>copy</id> 156 * <phase>compile</phase> 157 * <goals> 158 * <goal>copy</goal> 159 * </goals> 160 * <configuration> 161 * <artifactItems> 162 * <artifactItem> 163 * <groupId>${project.groupId}</groupId> 164 * <artifactId>api-ldap-extras-codec</artifactId> 165 * <version>${project.version}</version> 166 * <outputDirectory>${codec.plugin.directory}</outputDirectory> 167 * </artifactItem> 168 * </artifactItems> 169 * </configuration> 170 * </execution> 171 * </executions> 172 * </plugin> 173 * </plugins> 174 * </build> 175 * </pre> 176 * 177 * @throws Exception If we had an issue initializing the LDAP service 178 */ 179 public StandaloneLdapApiService() throws Exception 180 { 181 this( getControlsFromSystemProperties( ControlType.REQUEST ), 182 getControlsFromSystemProperties( ControlType.RESPONSE ), 183 getExtendedOperationsFromSystemProperties(), 184 getIntermediateResponsesFromSystemProperties() ); 185 } 186 187 188 /** 189 * Creates a new instance of StandaloneLdapApiService. 190 * 191 * @param requestControls The list of request controls to store 192 * @param responseControls The list of response controls to store 193 * @param extendedOperations The list of extended operations to store 194 * @param intermediateResponses The list of intermediate responsess to store 195 * @throws Exception If we had an issue with one of the two lists 196 */ 197 public StandaloneLdapApiService( List<String> requestControls, 198 List<String> responseControls, List<String> extendedOperations, 199 List<String> intermediateResponses ) throws Exception 200 { 201 StockCodecFactoryUtil.loadStockControls( this ); 202 ExtrasCodecFactoryUtil.loadExtrasControls( this ); 203 ExtrasCodecFactoryUtil.loadExtrasExtendedOperations( this ); 204 ExtrasCodecFactoryUtil.loadExtrasIntermediateResponses( this ); 205 206 // Load the controls 207 loadControls( requestControls, getRequestControlFactories() ); 208 loadControls( responseControls, getResponseControlFactories() ); 209 210 // Load the extended operations 211 loadExtendedOperations( extendedOperations ); 212 213 // Load the extended operations 214 loadIntermediateResponse( intermediateResponses ); 215 216 if ( getProtocolCodecFactory() == null ) 217 { 218 try 219 { 220 @SuppressWarnings("unchecked") 221 Class<? extends ProtocolCodecFactory> clazz = ( Class<? extends ProtocolCodecFactory> ) 222 Class.forName( DEFAULT_PROTOCOL_CODEC_FACTORY ); 223 Constructor<? extends ProtocolCodecFactory> constructor = 224 clazz.getConstructor( LdapApiService.class ); 225 226 if ( constructor != null ) 227 { 228 setProtocolCodecFactory( constructor.newInstance( this ) ); 229 } 230 else 231 { 232 setProtocolCodecFactory( clazz.newInstance() ); 233 } 234 } 235 catch ( Exception cause ) 236 { 237 throw new RuntimeException( I18n.err( I18n.ERR_06000_FAILED_TO_LOAD_DEFAULT_CODEC_FACTORY ), cause ); 238 } 239 } 240 } 241 242 243 /** 244 * Parses the system properties to obtain the controls list. 245 * 246 * @param type The control's type 247 * @return A list of controls 248 */ 249 private static List<String> getControlsFromSystemProperties( ControlType type ) 250 { 251 List<String> controlsList = new ArrayList<>(); 252 253 if ( type == ControlType.REQUEST ) 254 { 255 // Loading request controls list from command line properties if it exists 256 String controlsString = System.getProperty( type.getProperty() ); 257 258 if ( !Strings.isEmpty( controlsString ) ) 259 { 260 for ( String control : controlsString.split( "," ) ) 261 { 262 controlsList.add( control ); 263 } 264 } 265 else 266 { 267 // Loading old default controls list from command line properties if it exists 268 String oldDefaultControlsString = System.getProperty( OLD_DEFAULT_CONTROLS_LIST ); 269 270 if ( !Strings.isEmpty( oldDefaultControlsString ) ) 271 { 272 for ( String control : oldDefaultControlsString.split( "," ) ) 273 { 274 controlsList.add( control ); 275 } 276 } 277 } 278 } 279 280 return controlsList; 281 } 282 283 284 /** 285 * Parses the system properties to obtain the extended operations. 286 * Such extended operations are stored in the <b>apacheds.extendedOperations</b> 287 * and <b>default.extendedOperation.requests</b> system properties. 288 * 289 * @return a list of extended operation 290 */ 291 private static List<String> getExtendedOperationsFromSystemProperties() 292 { 293 List<String> extendedOperationsList = new ArrayList<>(); 294 295 // Loading extended operations from command line properties if it exists 296 String defaultExtendedOperationsList = System.getProperty( EXTENDED_OPERATIONS_LIST ); 297 298 if ( !Strings.isEmpty( defaultExtendedOperationsList ) ) 299 { 300 for ( String extendedOperation : defaultExtendedOperationsList.split( "," ) ) 301 { 302 extendedOperationsList.add( extendedOperation ); 303 } 304 } 305 else 306 { 307 // Loading old extra extended operations list from command line properties if it exists 308 String oldDefaultExtendedOperationsString = System.getProperty( OLD_EXTRA_EXTENDED_OPERATION_LIST ); 309 310 if ( !Strings.isEmpty( oldDefaultExtendedOperationsString ) ) 311 { 312 for ( String extendedOperation : oldDefaultExtendedOperationsString.split( "," ) ) 313 { 314 extendedOperationsList.add( extendedOperation ); 315 } 316 } 317 } 318 319 return extendedOperationsList; 320 } 321 322 323 /** 324 * Parses the system properties to obtain the intermediate responses. 325 * Such intermediate responses are stored in the <b>apacheds.intermediateResponses</b> 326 * and <b>default.intermediateResponses.requests</b> system properties. 327 * 328 * @return a list of intermediate responses 329 */ 330 private static List<String> getIntermediateResponsesFromSystemProperties() 331 { 332 List<String> intermediateResponsesList = new ArrayList<>(); 333 334 // Loading extended operations from command line properties if it exists 335 String defaultIntermediateResponsesList = System.getProperty( INTERMEDIATE_RESPONSES_LIST ); 336 337 if ( !Strings.isEmpty( defaultIntermediateResponsesList ) ) 338 { 339 for ( String intermediateResponse : defaultIntermediateResponsesList.split( "," ) ) 340 { 341 intermediateResponsesList.add( intermediateResponse ); 342 } 343 } 344 345 return intermediateResponsesList; 346 } 347 348 349 /** 350 * Loads a list of controls from their FQCN. 351 * 352 * @param controlsList The list of controls to load 353 * @param controlFactories The set of control factories already loaded 354 * @throws Exception if a control could not be loaded 355 */ 356 private void loadControls( List<String> controlsList, Map<String, ControlFactory<? extends Control>> controlFactories ) 357 throws Exception 358 { 359 // Adding all controls 360 if ( !controlsList.isEmpty() ) 361 { 362 for ( String controlFQCN : controlsList ) 363 { 364 loadControl( controlFQCN, controlFactories ); 365 } 366 } 367 } 368 369 370 /** 371 * Loads a control from its FQCN. 372 * 373 * @param controlFQCN The control FQCN 374 * @param controlFactories The set of control factories already loaded 375 * @throws Exception If the control could not be loaded 376 */ 377 private void loadControl( String controlFQCN, Map<String, ControlFactory<? extends Control>> controlFactories ) 378 throws Exception 379 { 380 if ( controlFactories.containsKey( controlFQCN ) ) 381 { 382 if ( LOG.isDebugEnabled() ) 383 { 384 LOG.debug( I18n.msg( I18n.MSG_06003_CONTROL_FACTORY_ALREADY_LOADED, controlFQCN ) ); 385 } 386 387 return; 388 } 389 390 Class<?>[] types = new Class<?>[] 391 { LdapApiService.class }; 392 // note, trimming whitespace doesnt hurt as it is a class name and 393 // helps DI containers that use xml config as xml ignores whitespace 394 @SuppressWarnings("unchecked") 395 Class<? extends ControlFactory<?>> clazz = ( Class<? extends ControlFactory<?>> ) Class 396 .forName( controlFQCN.trim() ); 397 Constructor<?> constructor = clazz.getConstructor( types ); 398 399 ControlFactory<?> factory = ( ControlFactory<?> ) constructor.newInstance( this ); 400 controlFactories.put( factory.getOid(), factory ); 401 402 if ( LOG.isInfoEnabled() ) 403 { 404 LOG.info( I18n.msg( I18n.MSG_06004_REGISTERED_CONTROL_FACTORY, factory.getOid() ) ); 405 } 406 } 407 408 409 /** 410 * Loads a list of extended operation from their FQCN 411 * 412 * @param extendedOperationsList The list of extended operations to load 413 * @throws Exception If an extended operations cannot be loaded 414 */ 415 private void loadExtendedOperations( List<String> extendedOperationsList ) throws Exception 416 { 417 // Adding all extended operations 418 if ( !extendedOperationsList.isEmpty() ) 419 { 420 for ( String extendedOperationFQCN : extendedOperationsList ) 421 { 422 loadExtendedRequest( extendedOperationFQCN ); 423 } 424 } 425 } 426 427 428 /** 429 * Loads an extended request from its FQCN 430 * 431 * @param extendedRequestFQCN The extended operations to load 432 * @throws Exception If the extended operations cannot be loaded 433 */ 434 private void loadExtendedRequest( String extendedRequestFQCN ) throws Exception 435 { 436 if ( getExtendedRequestFactories().containsKey( extendedRequestFQCN ) ) 437 { 438 if ( LOG.isDebugEnabled() ) 439 { 440 LOG.debug( I18n.msg( I18n.MSG_06005_EXTENDED_OP_FACTORY_ALREADY_LOADED, extendedRequestFQCN ) ); 441 } 442 443 return; 444 } 445 446 Class<?>[] types = new Class<?>[] 447 { LdapApiService.class }; 448 449 // note, trimming whitespace doesn't hurt as it is a class name and 450 // helps DI containers that use xml config as xml ignores whitespace 451 @SuppressWarnings("unchecked") 452 Class<? extends ExtendedOperationFactory> clazz = ( Class<? extends ExtendedOperationFactory> ) Class 453 .forName( extendedRequestFQCN.trim() ); 454 Constructor<?> constructor = clazz.getConstructor( types ); 455 456 ExtendedOperationFactory factory = ( ExtendedOperationFactory ) constructor 457 .newInstance( this ); 458 getExtendedRequestFactories().put( factory.getOid(), factory ); 459 460 if ( LOG.isInfoEnabled() ) 461 { 462 LOG.info( I18n.msg( I18n.MSG_06001_REGISTERED_EXTENDED_OP_FACTORY, factory.getOid() ) ); 463 } 464 } 465 466 467 /** 468 * Loads a list of intermediate responses from their FQCN 469 * 470 * @param intermediateResponsesList The list of intermediate response to load 471 * @throws Exception If one of the intermediate response cannot be loaded 472 */ 473 private void loadIntermediateResponse( List<String> intermediateResponsesList ) throws Exception 474 { 475 // Adding all extended operations 476 if ( !intermediateResponsesList.isEmpty() ) 477 { 478 for ( String intermediateResponseFQCN : intermediateResponsesList ) 479 { 480 loadIntermediateResponse( intermediateResponseFQCN ); 481 } 482 } 483 } 484 485 486 /** 487 * Loads an intermediate responses from its FQCN 488 * 489 * @param intermediateResponseFQCN The intermediate response to load 490 * @throws Exception If the intermediate response cannot be loaded 491 */ 492 private void loadIntermediateResponse( String intermediateResponseFQCN ) throws Exception 493 { 494 if ( getIntermediateResponseFactories().containsKey( intermediateResponseFQCN ) ) 495 { 496 if ( LOG.isDebugEnabled() ) 497 { 498 LOG.debug( I18n.msg( I18n.MSG_06006_INTERMEDIATE_FACTORY_ALREADY_LOADED, intermediateResponseFQCN ) ); 499 } 500 501 return; 502 } 503 504 Class<?>[] types = new Class<?>[] 505 {}; 506 507 // note, trimming whitespace doesn't hurt as it is a class name and 508 // helps DI containers that use xml config as xml ignores whitespace 509 @SuppressWarnings("unchecked") 510 Class<? extends IntermediateOperationFactory> clazz = ( Class<? extends IntermediateOperationFactory> ) Class 511 .forName( intermediateResponseFQCN.trim() ); 512 Constructor<?> constructor = clazz.getConstructor( types ); 513 514 IntermediateOperationFactory factory = ( IntermediateOperationFactory ) constructor 515 .newInstance(); 516 getIntermediateResponseFactories().put( factory.getOid(), factory ); 517 518 if ( LOG.isInfoEnabled() ) 519 { 520 LOG.info( I18n.msg( I18n.MSG_06007_REGISTRED_INTERMEDIATE_RESP_FACTORY, factory.getOid() ) ); 521 } 522 } 523}