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; 026 027import org.apache.directory.api.ldap.codec.api.ControlFactory; 028import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory; 029import org.apache.directory.api.ldap.codec.api.LdapApiService; 030import org.apache.directory.api.ldap.codec.osgi.DefaultLdapCodecService; 031import org.apache.directory.api.util.Strings; 032import org.apache.mina.filter.codec.ProtocolCodecFactory; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036 037/** 038 * The default {@link org.apache.directory.api.ldap.codec.api.LdapApiService} implementation. 039 * It loads the Controls and ExtendedOperations as defined in the following system parameters : 040 * <ul> 041 * <li>Controls : 042 * <ul> 043 * <li>apacheds.controls</li> 044 * <li>default.controls</li> 045 * </ul> 046 * </li> 047 * <li>ExtendedOperations : 048 * <ul> 049 * <li>apacheds.extendedOperations</li> 050 * <li>default.extendedOperation.responses</li> 051 * <li>extra.extendedOperations</li> 052 * </ul> 053 * </li> 054 * </ul> 055 * 056 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 057 * @version $Rev$, $Date$ 058 */ 059public class StandaloneLdapApiService extends DefaultLdapCodecService 060{ 061 /** A logger */ 062 private static final Logger LOG = LoggerFactory.getLogger( StandaloneLdapApiService.class ); 063 064 /** The list of controls to load at startup */ 065 public static final String CONTROLS_LIST = "apacheds.controls"; 066 067 /** The list of extended operations to load at startup */ 068 public static final String EXTENDED_OPERATIONS_LIST = "apacheds.extendedOperations"; 069 070 /** The (old) list of default controls to load at startup */ 071 private static final String OLD_DEFAULT_CONTROLS_LIST = "default.controls"; 072 073 /** The (old) list of extra extended operations to load at startup */ 074 private static final String OLD_EXTRA_EXTENDED_OPERATION_LIST = "extra.extendedOperations"; 075 076 077 /** 078 * Creates a new instance of StandaloneLdapCodecService. 079 * <br><br> 080 * The following pom configuration is intended for use by unit test running 081 * tools like Maven's surefire: 082 * <pre> 083 * <properties> 084 * <codec.plugin.directory>${project.build.directory}/pluginDirectory</codec.plugin.directory> 085 * </properties> 086 * 087 * <build> 088 * <plugins> 089 * <plugin> 090 * <artifactId>maven-surefire-plugin</artifactId> 091 * <groupId>org.apache.maven.plugins</groupId> 092 * <configuration> 093 * <systemPropertyVariables> 094 * <workingDirectory>${basedir}/target</workingDirectory> 095 * <felix.cache.rootdir> 096 * ${project.build.directory} 097 * </felix.cache.rootdir> 098 * <felix.cache.locking> 099 * true 100 * </felix.cache.locking> 101 * <org.osgi.framework.storage.clean> 102 * onFirstInit 103 * </org.osgi.framework.storage.clean> 104 * <org.osgi.framework.storage> 105 * osgi-cache 106 * </org.osgi.framework.storage> 107 * <codec.plugin.directory> 108 * ${codec.plugin.directory} 109 * </codec.plugin.directory> 110 * </systemPropertyVariables> 111 * </configuration> 112 * </plugin> 113 * 114 * <plugin> 115 * <groupId>org.apache.maven.plugins</groupId> 116 * <artifactId>maven-dependency-plugin</artifactId> 117 * <executions> 118 * <execution> 119 * <id>copy</id> 120 * <phase>compile</phase> 121 * <goals> 122 * <goal>copy</goal> 123 * </goals> 124 * <configuration> 125 * <artifactItems> 126 * <artifactItem> 127 * <groupId>${project.groupId}</groupId> 128 * <artifactId>api-ldap-extras-codec</artifactId> 129 * <version>${project.version}</version> 130 * <outputDirectory>${codec.plugin.directory}</outputDirectory> 131 * </artifactItem> 132 * </artifactItems> 133 * </configuration> 134 * </execution> 135 * </executions> 136 * </plugin> 137 * </plugins> 138 * </build> 139 * </pre> 140 * 141 * @throws Exception If we had an issue initializing the LDAP service 142 */ 143 public StandaloneLdapApiService() throws Exception 144 { 145 this( getControlsFromSystemProperties(), getExtendedOperationsFromSystemProperties() ); 146 } 147 148 149 /** 150 * Creates a new instance of StandaloneLdapApiService. 151 * 152 * @param controls The list of controls to store 153 * @param extendedOperations The list of extended operations to store 154 * @throws Exception If we had an issue with one of the two lists 155 */ 156 public StandaloneLdapApiService( List<String> controls, List<String> extendedOperations ) throws Exception 157 { 158 CodecFactoryUtil.loadStockControls( getControlFactories(), this ); 159 160 CodecFactoryUtil.loadStockExtendedOperations( getExtendedOperationsFactories(), this ); 161 162 // Load the controls 163 loadControls( controls ); 164 165 // Load the extended operations 166 loadExtendedOperations( extendedOperations ); 167 168 if ( getProtocolCodecFactory() == null ) 169 { 170 try 171 { 172 @SuppressWarnings("unchecked") 173 Class<? extends ProtocolCodecFactory> clazz = ( Class<? extends ProtocolCodecFactory> ) 174 Class.forName( DEFAULT_PROTOCOL_CODEC_FACTORY ); 175 Constructor<? extends ProtocolCodecFactory> constructor = 176 clazz.getConstructor( LdapApiService.class ); 177 if ( constructor != null ) 178 { 179 setProtocolCodecFactory( constructor.newInstance( this ) ); 180 } 181 else 182 { 183 setProtocolCodecFactory( clazz.newInstance() ); 184 } 185 } 186 catch ( Exception cause ) 187 { 188 throw new RuntimeException( "Failed to load default codec factory.", cause ); 189 } 190 } 191 } 192 193 194 /** 195 * Parses the system properties to obtain the controls list. 196 * 197 * @throws Exception 198 */ 199 private static List<String> getControlsFromSystemProperties() throws Exception 200 { 201 List<String> controlsList = new ArrayList<>(); 202 203 // Loading controls list from command line properties if it exists 204 String controlsString = System.getProperty( CONTROLS_LIST ); 205 206 if ( !Strings.isEmpty( controlsString ) ) 207 { 208 for ( String control : controlsString.split( "," ) ) 209 { 210 controlsList.add( control ); 211 } 212 } 213 else 214 { 215 // Loading old default controls list from command line properties if it exists 216 String oldDefaultControlsString = System.getProperty( OLD_DEFAULT_CONTROLS_LIST ); 217 218 if ( !Strings.isEmpty( oldDefaultControlsString ) ) 219 { 220 for ( String control : oldDefaultControlsString.split( "," ) ) 221 { 222 controlsList.add( control ); 223 } 224 } 225 } 226 227 return controlsList; 228 } 229 230 231 /** 232 * Parses the system properties to obtain the extended operations. 233 * Such extended operations are stored in the <b>apacheds.extendedOperations</b> 234 * and <b>default.extendedOperation.requests</b> system properties. 235 */ 236 private static List<String> getExtendedOperationsFromSystemProperties() throws Exception 237 { 238 List<String> extendedOperationsList = new ArrayList<>(); 239 240 // Loading extended operations from command line properties if it exists 241 String defaultExtendedOperationsList = System.getProperty( EXTENDED_OPERATIONS_LIST ); 242 243 if ( !Strings.isEmpty( defaultExtendedOperationsList ) ) 244 { 245 for ( String extendedOperation : defaultExtendedOperationsList.split( "," ) ) 246 { 247 extendedOperationsList.add( extendedOperation ); 248 } 249 } 250 else 251 { 252 // Loading old extra extended operations list from command line properties if it exists 253 String oldDefaultControlsString = System.getProperty( OLD_EXTRA_EXTENDED_OPERATION_LIST ); 254 255 if ( !Strings.isEmpty( oldDefaultControlsString ) ) 256 { 257 for ( String extendedOperation : oldDefaultControlsString.split( "," ) ) 258 { 259 extendedOperationsList.add( extendedOperation ); 260 } 261 } 262 } 263 264 return extendedOperationsList; 265 } 266 267 268 /** 269 * Loads a list of controls from their FQCN. 270 */ 271 private void loadControls( List<String> controlsList ) throws Exception 272 { 273 // Adding all controls 274 if ( !controlsList.isEmpty() ) 275 { 276 for ( String controlFQCN : controlsList ) 277 { 278 loadControl( controlFQCN ); 279 } 280 } 281 } 282 283 284 /** 285 * Loads a control from its FQCN. 286 */ 287 private void loadControl( String controlFQCN ) throws Exception 288 { 289 if ( getControlFactories().containsKey( controlFQCN ) ) 290 { 291 LOG.debug( "Factory for control {} was already loaded", controlFQCN ); 292 return; 293 } 294 295 Class<?>[] types = new Class<?>[] 296 { LdapApiService.class }; 297 // note, trimming whitespace doesnt hurt as it is a class name and 298 // helps DI containers that use xml config as xml ignores whitespace 299 @SuppressWarnings("unchecked") 300 Class<? extends ControlFactory<?>> clazz = ( Class<? extends ControlFactory<?>> ) Class 301 .forName( controlFQCN.trim() ); 302 Constructor<?> constructor = clazz.getConstructor( types ); 303 304 ControlFactory<?> factory = ( ControlFactory<?> ) constructor.newInstance( new Object[] 305 { this } ); 306 getControlFactories().put( factory.getOid(), factory ); 307 308 LOG.info( "Registered control factory: {}", factory.getOid() ); 309 } 310 311 312 /** 313 * Loads a list of extended operation from their FQCN 314 */ 315 private void loadExtendedOperations( List<String> extendedOperationsList ) throws Exception 316 { 317 // Adding all extended operations 318 if ( !extendedOperationsList.isEmpty() ) 319 { 320 for ( String extendedOperationFQCN : extendedOperationsList ) 321 { 322 loadExtendedOperation( extendedOperationFQCN ); 323 } 324 } 325 } 326 327 328 /** 329 * Loads an of extended operations from its FQCN 330 */ 331 private void loadExtendedOperation( String extendedOperationFQCN ) throws Exception 332 { 333 if ( getExtendedOperationsFactories().containsKey( extendedOperationFQCN ) ) 334 { 335 LOG.debug( "Factory for extended operation {} was already loaded", extendedOperationFQCN ); 336 return; 337 } 338 339 Class<?>[] types = new Class<?>[] 340 { LdapApiService.class }; 341 342 // note, trimming whitespace doesn't hurt as it is a class name and 343 // helps DI containers that use xml config as xml ignores whitespace 344 @SuppressWarnings("unchecked") 345 Class<? extends ExtendedOperationFactory> clazz = ( Class<? extends ExtendedOperationFactory> ) Class 346 .forName( extendedOperationFQCN.trim() ); 347 Constructor<?> constructor = clazz.getConstructor( types ); 348 349 ExtendedOperationFactory factory = ( ExtendedOperationFactory ) constructor 350 .newInstance( new Object[] 351 { this } ); 352 getExtendedOperationsFactories().put( factory.getOid(), factory ); 353 354 LOG.info( "Registered pre-bundled extended operation factory: {}", factory.getOid() ); 355 } 356}