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