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     *   &lt;properties&gt;
083     *     &lt;codec.plugin.directory&gt;${project.build.directory}/pluginDirectory&lt;/codec.plugin.directory&gt;
084     *   &lt;/properties&gt;
085     * 
086     *   &lt;build&gt;
087     *     &lt;plugins&gt;
088     *       &lt;plugin&gt;
089     *         &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
090     *         &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
091     *         &lt;configuration&gt;
092     *           &lt;systemPropertyVariables&gt;
093     *             &lt;workingDirectory&gt;${basedir}/target&lt;/workingDirectory&gt;
094     *             &lt;felix.cache.rootdir&gt;
095     *               ${project.build.directory}
096     *             &lt;/felix.cache.rootdir&gt;
097     *             &lt;felix.cache.locking&gt;
098     *               true
099     *             &lt;/felix.cache.locking&gt;
100     *             &lt;org.osgi.framework.storage.clean&gt;
101     *               onFirstInit
102     *             &lt;/org.osgi.framework.storage.clean&gt;
103     *             &lt;org.osgi.framework.storage&gt;
104     *               osgi-cache
105     *             &lt;/org.osgi.framework.storage&gt;
106     *             &lt;codec.plugin.directory&gt;
107     *               ${codec.plugin.directory}
108     *             &lt;/codec.plugin.directory&gt;
109     *           &lt;/systemPropertyVariables&gt;
110     *         &lt;/configuration&gt;
111     *       &lt;/plugin&gt;
112     *       
113     *       &lt;plugin&gt;
114     *         &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
115     *         &lt;artifactId&gt;maven-dependency-plugin&lt;/artifactId&gt;
116     *         &lt;executions&gt;
117     *           &lt;execution&gt;
118     *             &lt;id&gt;copy&lt;/id&gt;
119     *             &lt;phase&gt;compile&lt;/phase&gt;
120     *             &lt;goals&gt;
121     *               &lt;goal&gt;copy&lt;/goal&gt;
122     *             &lt;/goals&gt;
123     *             &lt;configuration&gt;
124     *               &lt;artifactItems&gt;
125     *                 &lt;artifactItem&gt;
126     *                   &lt;groupId&gt;${project.groupId}&lt;/groupId&gt;
127     *                   &lt;artifactId&gt;api-ldap-extras-codec&lt;/artifactId&gt;
128     *                   &lt;version&gt;${project.version}&lt;/version&gt;
129     *                   &lt;outputDirectory&gt;${codec.plugin.directory}&lt;/outputDirectory&gt;
130     *                 &lt;/artifactItem&gt;
131     *               &lt;/artifactItems&gt;
132     *             &lt;/configuration&gt;
133     *           &lt;/execution&gt;
134     *         &lt;/executions&gt;
135     *       &lt;/plugin&gt;
136     *     &lt;/plugins&gt;
137     *   &lt;/build&gt;
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}