/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.api.ldap.codec.standalone;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import org.apache.directory.api.ldap.codec.api.ControlFactory;
import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
import org.apache.directory.api.ldap.codec.osgi.DefaultLdapCodecService;
import org.apache.directory.api.util.Strings;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The default {@link org.apache.directory.api.ldap.codec.api.LdapApiService} implementation.
* It loads the Controls and ExtendedOperations as defined in the following system parameters :
* <li>Controls :
* <ul>
* <li>apacheds.controls</li> ok
* <li>default.controls</li>
* </ul>
* </li>
* <li>ExtendedOperations
* <ul>
* <li>apacheds.extendedOperations</li> ok
* <li>default.extendedOperation.responses</li>
* <li>extra.extendedOperations</ul>
* </ul>
* </li>
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public class StandaloneLdapApiService extends DefaultLdapCodecService
{
/** A logger */
private static final Logger LOG = LoggerFactory.getLogger( StandaloneLdapApiService.class );
/** The list of controls to load at startup */
public static final String CONTROLS_LIST = "apacheds.controls";
/** The list of extended operations to load at startup */
public static final String EXTENDED_OPERATIONS_LIST = "apacheds.extendedOperations";
/** The (old) list of default controls to load at startup */
private static final String OLD_DEFAULT_CONTROLS_LIST = "default.controls";
/** The (old) list of extra extended operations to load at startup */
private static final String OLD_EXTRA_EXTENDED_OPERATION_LIST = "extra.extendedOperations";
/**
* Creates a new instance of StandaloneLdapCodecService. Optionally checks for
* system property {@link #PLUGIN_DIRECTORY_PROPERTY}.
* <br /><br />
* The following pom configuration is intended for use by unit test running
* tools like Maven's surefire:
* <pre>
* <properties>
* <codec.plugin.directory>${project.build.directory}/pluginDirectory</codec.plugin.directory>
* </properties>
*
* <build>
* <plugins>
* <plugin>
* <artifactId>maven-surefire-plugin</artifactId>
* <groupId>org.apache.maven.plugins</groupId>
* <configuration>
* <systemPropertyVariables>
* <workingDirectory>${basedir}/target</workingDirectory>
* <felix.cache.rootdir>
* ${project.build.directory}
* </felix.cache.rootdir>
* <felix.cache.locking>
* true
* </felix.cache.locking>
* <org.osgi.framework.storage.clean>
* onFirstInit
* </org.osgi.framework.storage.clean>
* <org.osgi.framework.storage>
* osgi-cache
* </org.osgi.framework.storage>
* <codec.plugin.directory>
* ${codec.plugin.directory}
* </codec.plugin.directory>
* </systemPropertyVariables>
* </configuration>
* </plugin>
*
* <plugin>
* <groupId>org.apache.maven.plugins</groupId>
* <artifactId>maven-dependency-plugin</artifactId>
* <executions>
* <execution>
* <id>copy</id>
* <phase>compile</phase>
* <goals>
* <goal>copy</goal>
* </goals>
* <configuration>
* <artifactItems>
* <artifactItem>
* <groupId>${project.groupId}</groupId>
* <artifactId>api-ldap-extras-codec</artifactId>
* <version>${project.version}</version>
* <outputDirectory>${codec.plugin.directory}</outputDirectory>
* </artifactItem>
* </artifactItems>
* </configuration>
* </execution>
* </executions>
* </plugin>
* </plugins>
* </build>
* </pre>
*/
public StandaloneLdapApiService() throws Exception
{
this( getControlsFromSystemProperties(), getExtendedOperationsFromSystemProperties() );
}
public StandaloneLdapApiService( List<String> controls, List<String> extendedOperations ) throws Exception
{
CodecFactoryUtil.loadStockControls( controlFactories, this );
CodecFactoryUtil.loadStockExtendedOperations( extendedOperationsFactories, this );
// Load the controls
loadControls( controls );
// Load the extended operations
loadExtendedOperations( extendedOperations );
if ( protocolCodecFactory == null )
{
try
{
@SuppressWarnings("unchecked")
Class<? extends ProtocolCodecFactory> clazz = ( Class<? extends ProtocolCodecFactory> )
Class.forName( DEFAULT_PROTOCOL_CODEC_FACTORY );
Constructor<? extends ProtocolCodecFactory> constructor =
clazz.getConstructor( LdapApiService.class );
if ( constructor != null )
{
protocolCodecFactory = constructor.newInstance( this );
}
else
{
protocolCodecFactory = clazz.newInstance();
}
}
catch ( Exception cause )
{
throw new RuntimeException( "Failed to load default codec factory.", cause );
}
}
}
/**
* Parses the system properties to obtain the controls list.
*
* @throws Exception
*/
private static List<String> getControlsFromSystemProperties() throws Exception
{
List<String> controlsList = new ArrayList<String>();
// Loading controls list from command line properties if it exists
String controlsString = System.getProperty( CONTROLS_LIST );
if ( !Strings.isEmpty( controlsString ) )
{
for ( String control : controlsString.split( "," ) )
{
controlsList.add( control );
}
}
else
{
// Loading old default controls list from command line properties if it exists
String oldDefaultControlsString = System.getProperty( OLD_DEFAULT_CONTROLS_LIST );
if ( !Strings.isEmpty( oldDefaultControlsString ) )
{
for ( String control : oldDefaultControlsString.split( "," ) )
{
controlsList.add( control );
}
}
}
return controlsList;
}
/**
* Parses the system properties to obtain the extended operations.
* Such extended operations are stored in the <b>apacheds.extendedOperations</b>
* and <b>default.extendedOperation.requests</b> system properties.
*/
private static List<String> getExtendedOperationsFromSystemProperties() throws Exception
{
List<String> extendedOperationsList = new ArrayList<String>();
// Loading extended operations from command line properties if it exists
String defaultExtendedOperationsList = System.getProperty( EXTENDED_OPERATIONS_LIST );
if ( !Strings.isEmpty( defaultExtendedOperationsList ) )
{
for ( String extendedOperation : defaultExtendedOperationsList.split( "," ) )
{
extendedOperationsList.add( extendedOperation );
}
}
else
{
// Loading old extra extended operations list from command line properties if it exists
String oldDefaultControlsString = System.getProperty( OLD_EXTRA_EXTENDED_OPERATION_LIST );
if ( !Strings.isEmpty( oldDefaultControlsString ) )
{
for ( String extendedOperation : oldDefaultControlsString.split( "," ) )
{
extendedOperationsList.add( extendedOperation );
}
}
}
return extendedOperationsList;
}
/**
* Loads a list of controls from their FQCN.
*/
private void loadControls( List<String> controlsList ) throws Exception
{
// Adding all controls
if ( controlsList.size() > 0 )
{
for ( String controlFQCN : controlsList )
{
loadControl( controlFQCN );
}
}
}
/**
* Loads a control from its FQCN.
*/
private void loadControl( String controlFQCN ) throws Exception
{
if ( controlFactories.containsKey( controlFQCN ) )
{
LOG.debug( "Factory for control {} was already loaded", controlFQCN );
return;
}
Class<?>[] types = new Class<?>[]
{ LdapApiService.class };
// note, trimming whitespace doesnt hurt as it is a class name and
// helps DI containers that use xml config as xml ignores whitespace
@SuppressWarnings("unchecked")
Class<? extends ControlFactory<?>> clazz = ( Class<? extends ControlFactory<?>> ) Class
.forName( controlFQCN.trim() );
Constructor<?> constructor = clazz.getConstructor( types );
ControlFactory<?> factory = ( ControlFactory<?> ) constructor.newInstance( new Object[]
{ this } );
controlFactories.put( factory.getOid(), factory );
LOG.info( "Registered control factory: {}", factory.getOid() );
}
/**
* Loads a list of extended operation from their FQCN
*/
private void loadExtendedOperations( List<String> extendedOperationsList ) throws Exception
{
// Adding all extended operations
if ( extendedOperationsList.size() > 0 )
{
for ( String extendedOperationFQCN : extendedOperationsList )
{
loadExtendedOperation( extendedOperationFQCN );
}
}
}
/**
* Loads an of extended operations from its FQCN
*/
private void loadExtendedOperation( String extendedOperationFQCN ) throws Exception
{
if ( extendedOperationsFactories.containsKey( extendedOperationFQCN ) )
{
LOG.debug( "Factory for extended operation {} was already loaded", extendedOperationFQCN );
return;
}
Class<?>[] types = new Class<?>[]
{ LdapApiService.class };
// note, trimming whitespace doesn't hurt as it is a class name and
// helps DI containers that use xml config as xml ignores whitespace
@SuppressWarnings("unchecked")
Class<? extends ExtendedOperationFactory> clazz = ( Class<? extends ExtendedOperationFactory> ) Class
.forName( extendedOperationFQCN.trim() );
Constructor<?> constructor = clazz.getConstructor( types );
ExtendedOperationFactory factory = ( ExtendedOperationFactory ) constructor
.newInstance( new Object[]
{ this } );
extendedOperationsFactories.put( factory.getOid(), factory );
LOG.info( "Registered pre-bundled extended operation factory: {}", factory.getOid() );
}
}