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   * <li>Controls :
41   * <ul>
42   * <li>apacheds.controls</li> ok
43   * <li>default.controls</li>
44   * </ul>
45   * </li>
46   * <li>ExtendedOperations
47   * <ul>
48   * <li>apacheds.extendedOperations</li> ok
49   * <li>default.extendedOperation.responses</li>
50   * <li>extra.extendedOperations</ul>
51   * </ul>
52   * </li>
53   *
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev$, $Date$
56   */
57  public class StandaloneLdapApiService extends DefaultLdapCodecService
58  {
59      /** A logger */
60      private static final Logger LOG = LoggerFactory.getLogger( StandaloneLdapApiService.class );
61  
62      /** The list of controls to load at startup */
63      public static final String CONTROLS_LIST = "apacheds.controls";
64  
65      /** The list of extended operations to load at startup */
66      public static final String EXTENDED_OPERATIONS_LIST = "apacheds.extendedOperations";
67  
68      /** The (old) list of default controls to load at startup */
69      private static final String OLD_DEFAULT_CONTROLS_LIST = "default.controls";
70  
71      /** The (old) list of extra extended operations to load at startup */
72      private static final String OLD_EXTRA_EXTENDED_OPERATION_LIST = "extra.extendedOperations";
73  
74  
75      /**
76       * Creates a new instance of StandaloneLdapCodecService. Optionally checks for
77       * system property {@link #PLUGIN_DIRECTORY_PROPERTY}. 
78       * <br /><br />
79       * The following pom configuration is intended for use by unit test running 
80       * tools like Maven's surefire:
81       * <pre>
82       *   &lt;properties&gt;
83       *     &lt;codec.plugin.directory&gt;${project.build.directory}/pluginDirectory&lt;/codec.plugin.directory&gt;
84       *   &lt;/properties&gt;
85       * 
86       *   &lt;build&gt;
87       *     &lt;plugins&gt;
88       *       &lt;plugin&gt;
89       *         &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
90       *         &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
91       *         &lt;configuration&gt;
92       *           &lt;systemPropertyVariables&gt;
93       *             &lt;workingDirectory&gt;${basedir}/target&lt;/workingDirectory&gt;
94       *             &lt;felix.cache.rootdir&gt;
95       *               ${project.build.directory}
96       *             &lt;/felix.cache.rootdir&gt;
97       *             &lt;felix.cache.locking&gt;
98       *               true
99       *             &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 }