View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.api.ldap.codec.standalone;
21  
22  
23  import java.lang.reflect.Constructor;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.directory.api.ldap.codec.api.ControlFactory;
28  import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory;
29  import org.apache.directory.api.ldap.codec.api.LdapApiService;
30  import org.apache.directory.api.ldap.codec.osgi.DefaultLdapCodecService;
31  import org.apache.directory.api.util.Strings;
32  import org.apache.mina.filter.codec.ProtocolCodecFactory;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  
37  /**
38   * The default {@link org.apache.directory.api.ldap.codec.api.LdapApiService} implementation.
39   * It loads the Controls and ExtendedOperations as defined in the following system parameters :
40   * <ul>
41   *   <li>Controls :
42   *     <ul>
43   *       <li>apacheds.controls</li>
44   *       <li>default.controls</li>
45   *     </ul>
46   *   </li>
47   *   <li>ExtendedOperations :
48   *     <ul>
49   *       <li>apacheds.extendedOperations</li>
50   *       <li>default.extendedOperation.responses</li>
51   *       <li>extra.extendedOperations</li>
52   *     </ul>
53   *   </li>
54   * </ul>
55   *
56   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
57   * @version $Rev$, $Date$
58   */
59  public class StandaloneLdapApiService extends DefaultLdapCodecService
60  {
61      /** A logger */
62      private static final Logger LOG = LoggerFactory.getLogger( StandaloneLdapApiService.class );
63  
64      /** The list of controls to load at startup */
65      public static final String CONTROLS_LIST = "apacheds.controls";
66  
67      /** The list of extended operations to load at startup */
68      public static final String EXTENDED_OPERATIONS_LIST = "apacheds.extendedOperations";
69  
70      /** The (old) list of default controls to load at startup */
71      private static final String OLD_DEFAULT_CONTROLS_LIST = "default.controls";
72  
73      /** The (old) list of extra extended operations to load at startup */
74      private static final String OLD_EXTRA_EXTENDED_OPERATION_LIST = "extra.extendedOperations";
75  
76  
77      /**
78       * Creates a new instance of StandaloneLdapCodecService.
79       * <br><br>
80       * The following pom configuration is intended for use by unit test running 
81       * tools like Maven's surefire:
82       * <pre>
83       *   &lt;properties&gt;
84       *     &lt;codec.plugin.directory&gt;${project.build.directory}/pluginDirectory&lt;/codec.plugin.directory&gt;
85       *   &lt;/properties&gt;
86       * 
87       *   &lt;build&gt;
88       *     &lt;plugins&gt;
89       *       &lt;plugin&gt;
90       *         &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
91       *         &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
92       *         &lt;configuration&gt;
93       *           &lt;systemPropertyVariables&gt;
94       *             &lt;workingDirectory&gt;${basedir}/target&lt;/workingDirectory&gt;
95       *             &lt;felix.cache.rootdir&gt;
96       *               ${project.build.directory}
97       *             &lt;/felix.cache.rootdir&gt;
98       *             &lt;felix.cache.locking&gt;
99       *               true
100      *             &lt;/felix.cache.locking&gt;
101      *             &lt;org.osgi.framework.storage.clean&gt;
102      *               onFirstInit
103      *             &lt;/org.osgi.framework.storage.clean&gt;
104      *             &lt;org.osgi.framework.storage&gt;
105      *               osgi-cache
106      *             &lt;/org.osgi.framework.storage&gt;
107      *             &lt;codec.plugin.directory&gt;
108      *               ${codec.plugin.directory}
109      *             &lt;/codec.plugin.directory&gt;
110      *           &lt;/systemPropertyVariables&gt;
111      *         &lt;/configuration&gt;
112      *       &lt;/plugin&gt;
113      *       
114      *       &lt;plugin&gt;
115      *         &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
116      *         &lt;artifactId&gt;maven-dependency-plugin&lt;/artifactId&gt;
117      *         &lt;executions&gt;
118      *           &lt;execution&gt;
119      *             &lt;id&gt;copy&lt;/id&gt;
120      *             &lt;phase&gt;compile&lt;/phase&gt;
121      *             &lt;goals&gt;
122      *               &lt;goal&gt;copy&lt;/goal&gt;
123      *             &lt;/goals&gt;
124      *             &lt;configuration&gt;
125      *               &lt;artifactItems&gt;
126      *                 &lt;artifactItem&gt;
127      *                   &lt;groupId&gt;${project.groupId}&lt;/groupId&gt;
128      *                   &lt;artifactId&gt;api-ldap-extras-codec&lt;/artifactId&gt;
129      *                   &lt;version&gt;${project.version}&lt;/version&gt;
130      *                   &lt;outputDirectory&gt;${codec.plugin.directory}&lt;/outputDirectory&gt;
131      *                 &lt;/artifactItem&gt;
132      *               &lt;/artifactItems&gt;
133      *             &lt;/configuration&gt;
134      *           &lt;/execution&gt;
135      *         &lt;/executions&gt;
136      *       &lt;/plugin&gt;
137      *     &lt;/plugins&gt;
138      *   &lt;/build&gt;
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 }