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  package org.apache.directory.server.core.factory;
20  
21  
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.InputStream;
25  import java.lang.reflect.Constructor;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  
30  import org.apache.directory.api.ldap.model.entry.DefaultEntry;
31  import org.apache.directory.api.ldap.model.exception.LdapException;
32  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
33  import org.apache.directory.api.ldap.model.ldif.LdifEntry;
34  import org.apache.directory.api.ldap.model.ldif.LdifReader;
35  import org.apache.directory.api.ldap.model.name.Dn;
36  import org.apache.directory.api.ldap.model.schema.SchemaManager;
37  import org.apache.directory.server.core.annotations.AnnotationUtils;
38  import org.apache.directory.server.core.annotations.ApplyLdifFiles;
39  import org.apache.directory.server.core.annotations.ApplyLdifs;
40  import org.apache.directory.server.core.annotations.ContextEntry;
41  import org.apache.directory.server.core.annotations.CreateAuthenticator;
42  import org.apache.directory.server.core.annotations.CreateDS;
43  import org.apache.directory.server.core.annotations.CreateIndex;
44  import org.apache.directory.server.core.annotations.CreatePartition;
45  import org.apache.directory.server.core.annotations.LoadSchema;
46  import org.apache.directory.server.core.api.DirectoryService;
47  import org.apache.directory.server.core.api.interceptor.Interceptor;
48  import org.apache.directory.server.core.api.partition.Partition;
49  import org.apache.directory.server.core.authn.AuthenticationInterceptor;
50  import org.apache.directory.server.core.authn.Authenticator;
51  import org.apache.directory.server.core.authn.DelegatingAuthenticator;
52  import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
53  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
54  import org.apache.directory.server.i18n.I18n;
55  import org.junit.runner.Description;
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  
59  
60  /**
61   * A Helper class used to create a DS from the annotations
62   * 
63   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
64   */
65  public class DSAnnotationProcessor
66  {
67      /** A logger for this class */
68      private static final Logger LOG = LoggerFactory.getLogger( DSAnnotationProcessor.class );
69  
70  
71      /**
72       * Create the DirectoryService
73       */
74      public static DirectoryService createDS( CreateDS dsBuilder )
75          throws Exception
76      {
77          LOG.debug( "Starting DS {}...", dsBuilder.name() );
78          Class<?> factory = dsBuilder.factory();
79          DirectoryServiceFactory dsf = ( DirectoryServiceFactory ) factory
80              .newInstance();
81  
82          DirectoryService service = dsf.getDirectoryService();
83          service.setAccessControlEnabled( dsBuilder.enableAccessControl() );
84          service.setAllowAnonymousAccess( dsBuilder.allowAnonAccess() );
85          service.getChangeLog().setEnabled( dsBuilder.enableChangeLog() );
86  
87          dsf.init( dsBuilder.name() );
88  
89          for ( Class<?> interceptorClass : dsBuilder.additionalInterceptors() )
90          {
91              service.addLast( ( Interceptor ) interceptorClass.newInstance() );
92          }
93  
94          List<Interceptor> interceptorList = service.getInterceptors();
95  
96          if ( dsBuilder.authenticators().length != 0 )
97          {
98              AuthenticationInterceptor authenticationInterceptor = null;
99  
100             for ( Interceptor interceptor : interceptorList )
101             {
102                 if ( interceptor instanceof AuthenticationInterceptor )
103                 {
104                     authenticationInterceptor = ( AuthenticationInterceptor ) interceptor;
105                     break;
106                 }
107             }
108 
109             if ( authenticationInterceptor == null )
110             {
111                 throw new IllegalStateException(
112                     "authentication interceptor not found" );
113             }
114 
115             Set<Authenticator> authenticators = new HashSet<Authenticator>();
116 
117             for ( CreateAuthenticator createAuthenticator : dsBuilder
118                 .authenticators() )
119             {
120                 Authenticator auth = createAuthenticator.type().newInstance();
121 
122                 if ( auth instanceof DelegatingAuthenticator )
123                 {
124                     DelegatingAuthenticator dauth = ( DelegatingAuthenticator ) auth;
125                     dauth.setDelegateHost( createAuthenticator.delegateHost() );
126                     dauth.setDelegatePort( createAuthenticator.delegatePort() );
127                 }
128 
129                 authenticators.add( auth );
130             }
131         }
132 
133         service.setInterceptors( interceptorList );
134 
135         SchemaManager schemaManager = service.getSchemaManager();
136 
137         // process the schemas
138         for ( LoadSchema loadedSchema : dsBuilder.loadedSchemas() )
139         {
140             String schemaName = loadedSchema.name();
141             Boolean enabled = loadedSchema.enabled();
142 
143             // Check if the schema is loaded or not
144             boolean isLoaded = schemaManager.isSchemaLoaded( schemaName );
145 
146             if ( !isLoaded )
147             {
148                 // We have to load the schema, if it exists
149                 try
150                 {
151                     isLoaded = schemaManager.load( schemaName );
152                 }
153                 catch ( LdapUnwillingToPerformException lutpe )
154                 {
155                     // Cannot load the schema, it does not exists
156                     LOG.error( lutpe.getMessage() );
157                     continue;
158                 }
159             }
160 
161             if ( isLoaded )
162             {
163                 if ( enabled )
164                 {
165                     schemaManager.enable( schemaName );
166 
167                     if ( schemaManager.isDisabled( schemaName ) )
168                     {
169                         LOG.error( "Cannot enable " + schemaName );
170                     }
171                 }
172                 else
173                 {
174                     schemaManager.disable( schemaName );
175 
176                     if ( schemaManager.isEnabled( schemaName ) )
177                     {
178                         LOG.error( "Cannot disable " + schemaName );
179                     }
180                 }
181             }
182 
183             LOG.debug( "Loading schema {}, enabled= {}", schemaName, enabled );
184         }
185 
186         // Process the Partition, if any.
187         for ( CreatePartition createPartition : dsBuilder.partitions() )
188         {
189             Partition partition;
190 
191             // Determine the partition type
192             if ( createPartition.type() == Partition.class )
193             {
194                 // The annotation does not specify a specific partition type.
195                 // We use the partition factory to create partition and index
196                 // instances.
197                 PartitionFactory partitionFactory = dsf.getPartitionFactory();
198                 partition = partitionFactory.createPartition(
199                     schemaManager,
200                     createPartition.name(),
201                     createPartition.suffix(),
202                     createPartition.cacheSize(),
203                     new File( service.getInstanceLayout().getPartitionsDirectory(), createPartition.name() ) );
204 
205                 CreateIndex[] indexes = createPartition.indexes();
206 
207                 for ( CreateIndex createIndex : indexes )
208                 {
209                     partitionFactory.addIndex( partition,
210                         createIndex.attribute(), createIndex.cacheSize() );
211                 }
212 
213                 partition.initialize();
214             }
215             else
216             {
217                 // The annotation contains a specific partition type, we use
218                 // that type.
219                 Class<?> partypes[] = new Class[]
220                     { SchemaManager.class };
221                 Constructor<?> constructor = createPartition.type().getConstructor( partypes );
222                 partition = ( Partition ) constructor.newInstance( new Object[]
223                     { schemaManager } );
224                 partition.setId( createPartition.name() );
225                 partition.setSuffixDn( new Dn( schemaManager, createPartition.suffix() ) );
226 
227                 if ( partition instanceof AbstractBTreePartition )
228                 {
229                     AbstractBTreePartition btreePartition = ( AbstractBTreePartition ) partition;
230                     btreePartition.setCacheSize( createPartition.cacheSize() );
231                     btreePartition.setPartitionPath( new File( service
232                         .getInstanceLayout().getPartitionsDirectory(),
233                         createPartition.name() ).toURI() );
234 
235                     // Process the indexes if any
236                     CreateIndex[] indexes = createPartition.indexes();
237 
238                     for ( CreateIndex createIndex : indexes )
239                     {
240                         // The annotation does not specify a specific index
241                         // type.
242                         // We use the generic index implementation.
243                         JdbmIndex index = new JdbmIndex( createIndex.attribute(), false );
244 
245                         btreePartition.addIndexedAttributes( index );
246                     }
247                 }
248             }
249 
250             partition.setSchemaManager( schemaManager );
251 
252             // Inject the partition into the DirectoryService
253             service.addPartition( partition );
254 
255             // Last, process the context entry
256             ContextEntry contextEntry = createPartition.contextEntry();
257 
258             if ( contextEntry != null )
259             {
260                 injectEntries( service, contextEntry.entryLdif() );
261             }
262         }
263 
264         return service;
265     }
266 
267 
268     /**
269      * Create a DirectoryService from a Unit test annotation
270      * 
271      * @param description The annotations containing the info from which we will create
272      *  the DS
273      * @return A valid DS
274      */
275     public static DirectoryService getDirectoryService( Description description )
276         throws Exception
277     {
278         CreateDS dsBuilder = description.getAnnotation( CreateDS.class );
279 
280         if ( dsBuilder != null )
281         {
282             return createDS( dsBuilder );
283         }
284         else
285         {
286             LOG.debug( "No {} DS.", description.getDisplayName() );
287             return null;
288         }
289     }
290 
291 
292     /**
293      * Create a DirectoryService from an annotation. The @CreateDS annotation
294      * must be associated with either the method or the encapsulating class. We
295      * will first try to get the annotation from the method, and if there is
296      * none, then we try at the class level.
297      * 
298      * @return A valid DS
299      */
300     public static DirectoryService getDirectoryService() throws Exception
301     {
302         Object instance = AnnotationUtils.getInstance( CreateDS.class );
303         CreateDS dsBuilder = null;
304 
305         if ( instance != null )
306         {
307             dsBuilder = ( CreateDS ) instance;
308 
309             // Ok, we have found a CreateDS annotation. Process it now.
310             return createDS( dsBuilder );
311         }
312 
313         throw new LdapException( I18n.err( I18n.ERR_114 ) );
314     }
315 
316 
317     /**
318      * injects an LDIF entry in the given DirectoryService
319      * 
320      * @param entry
321      *            the LdifEntry to be injected
322      * @param service
323      *            the DirectoryService
324      * @throws Exception
325      */
326     private static void injectEntry( LdifEntry entry, DirectoryService service )
327         throws LdapException
328     {
329         if ( entry.isChangeAdd() || entry.isLdifContent() )
330         {
331             service.getAdminSession().add(
332                 new DefaultEntry( service.getSchemaManager(), entry
333                     .getEntry() ) );
334         }
335         else if ( entry.isChangeModify() )
336         {
337             service.getAdminSession().modify( entry.getDn(),
338                 entry.getModifications() );
339         }
340         else
341         {
342             String message = I18n.err( I18n.ERR_117, entry.getChangeType() );
343             throw new LdapException( message );
344         }
345     }
346 
347 
348     /**
349      * injects the LDIF entries present in a LDIF file
350      * 
351      * @param service
352      *            the DirectoryService
353      * @param ldifFiles
354      *            the array of LDIF file names (only )
355      * @throws Exception
356      */
357     public static void injectLdifFiles( Class<?> clazz,
358         DirectoryService service, String[] ldifFiles ) throws Exception
359     {
360         if ( ( ldifFiles != null ) && ( ldifFiles.length > 0 ) )
361         {
362             for ( String ldifFile : ldifFiles )
363             {
364                 InputStream is = clazz.getClassLoader().getResourceAsStream(
365                     ldifFile );
366                 if ( is == null )
367                 {
368                     throw new FileNotFoundException( "LDIF file '" + ldifFile
369                         + "' not found." );
370                 }
371                 else
372                 {
373                     LdifReader ldifReader = new LdifReader( is );
374 
375                     for ( LdifEntry entry : ldifReader )
376                     {
377                         injectEntry( entry, service );
378                     }
379 
380                     ldifReader.close();
381                 }
382             }
383         }
384     }
385 
386 
387     /**
388      * Inject an ldif String into the server. Dn must be relative to the root.
389      * 
390      * @param service
391      *            the directory service to use
392      * @param ldif
393      *            the ldif containing entries to add to the server.
394      * @throws Exception
395      *             if there is a problem adding the entries from the LDIF
396      */
397     public static void injectEntries( DirectoryService service, String ldif )
398         throws Exception
399     {
400         LdifReader reader = new LdifReader();
401         List<LdifEntry> entries = reader.parseLdif( ldif );
402 
403         for ( LdifEntry entry : entries )
404         {
405             injectEntry( entry, service );
406         }
407 
408         // And close the reader
409         reader.close();
410     }
411 
412 
413     /**
414      * Load the schemas, and enable/disable them.
415      */
416     public static void loadSchemas( Description desc, DirectoryService service )
417     {
418         if ( desc == null )
419         {
420             return;
421         }
422 
423         /*for ( Class<?> loadSchema : dsBuilder.additionalInterceptors() )
424         {
425             service.addLast( ( Interceptor ) interceptorClass.newInstance() );
426         }*/
427         LoadSchema loadSchema = desc
428             .getAnnotation( LoadSchema.class );
429 
430         if ( loadSchema != null )
431         {
432             System.out.println( loadSchema );
433         }
434     }
435 
436 
437     /**
438      * Apply the LDIF entries to the given service
439      */
440     public static void applyLdifs( Description desc, DirectoryService service )
441         throws Exception
442     {
443         if ( desc == null )
444         {
445             return;
446         }
447 
448         ApplyLdifFiles applyLdifFiles = desc
449             .getAnnotation( ApplyLdifFiles.class );
450 
451         if ( applyLdifFiles != null )
452         {
453             LOG.debug( "Applying {} to {}", applyLdifFiles.value(),
454                 desc.getDisplayName() );
455             injectLdifFiles( desc.getClass(), service, applyLdifFiles.value() );
456         }
457 
458         ApplyLdifs applyLdifs = desc.getAnnotation( ApplyLdifs.class );
459 
460         if ( ( applyLdifs != null ) && ( applyLdifs.value() != null ) )
461         {
462             String[] ldifs = applyLdifs.value();
463 
464             String DN_START = "dn:";
465 
466             StringBuilder sb = new StringBuilder();
467 
468             for ( int i = 0; i < ldifs.length; )
469             {
470                 String s = ldifs[i++].trim();
471                 if ( s.startsWith( DN_START ) )
472                 {
473                     sb.append( s ).append( '\n' );
474 
475                     // read the rest of lines till we encounter Dn again
476                     while ( i < ldifs.length )
477                     {
478                         s = ldifs[i++];
479                         if ( !s.startsWith( DN_START ) )
480                         {
481                             sb.append( s ).append( '\n' );
482                         }
483                         else
484                         {
485                             break;
486                         }
487                     }
488 
489                     LOG.debug( "Applying {} to {}", sb, desc.getDisplayName() );
490                     injectEntries( service, sb.toString() );
491                     sb.setLength( 0 );
492 
493                     i--; // step up a line
494                 }
495             }
496         }
497     }
498 }