| /* |
| * 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.server.core.factory; |
| |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.InputStream; |
| import java.lang.reflect.Constructor; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.directory.api.ldap.model.entry.DefaultEntry; |
| import org.apache.directory.api.ldap.model.exception.LdapException; |
| import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException; |
| import org.apache.directory.api.ldap.model.ldif.LdifEntry; |
| import org.apache.directory.api.ldap.model.ldif.LdifReader; |
| import org.apache.directory.api.ldap.model.name.Dn; |
| import org.apache.directory.api.ldap.model.schema.SchemaManager; |
| import org.apache.directory.api.util.Network; |
| import org.apache.directory.api.util.Strings; |
| import org.apache.directory.server.core.annotations.AnnotationUtils; |
| import org.apache.directory.server.core.annotations.ApplyLdifFiles; |
| import org.apache.directory.server.core.annotations.ApplyLdifs; |
| import org.apache.directory.server.core.annotations.ContextEntry; |
| import org.apache.directory.server.core.annotations.CreateAuthenticator; |
| import org.apache.directory.server.core.annotations.CreateDS; |
| import org.apache.directory.server.core.annotations.CreateIndex; |
| import org.apache.directory.server.core.annotations.CreatePartition; |
| import org.apache.directory.server.core.annotations.LoadSchema; |
| import org.apache.directory.server.core.api.DirectoryService; |
| import org.apache.directory.server.core.api.DnFactory; |
| import org.apache.directory.server.core.api.interceptor.Interceptor; |
| import org.apache.directory.server.core.api.partition.Partition; |
| import org.apache.directory.server.core.authn.AuthenticationInterceptor; |
| import org.apache.directory.server.core.authn.Authenticator; |
| import org.apache.directory.server.core.authn.DelegatingAuthenticator; |
| import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition; |
| import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex; |
| import org.apache.directory.server.core.partition.impl.btree.mavibot.MavibotIndex; |
| import org.apache.directory.server.i18n.I18n; |
| import org.junit.runner.Description; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| /** |
| * A Helper class used to create a DS from the annotations |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public final class DSAnnotationProcessor |
| { |
| /** A logger for this class */ |
| private static final Logger LOG = LoggerFactory.getLogger( DSAnnotationProcessor.class ); |
| |
| |
| private DSAnnotationProcessor() |
| { |
| } |
| |
| |
| /** |
| * Create the DirectoryService |
| * |
| * @param dsBuilder The DirectoryService builder |
| * @return an instance of DirectoryService |
| * @throws Exception If the DirectoryService cannot be created |
| */ |
| public static DirectoryService createDS( CreateDS dsBuilder ) |
| throws Exception |
| { |
| if ( LOG.isDebugEnabled() ) |
| { |
| LOG.debug( "Starting DS {}...", dsBuilder.name() ); |
| } |
| |
| Class<?> factory = dsBuilder.factory(); |
| DirectoryServiceFactory dsf = ( DirectoryServiceFactory ) factory.newInstance(); |
| |
| DirectoryService service = dsf.getDirectoryService(); |
| service.setAccessControlEnabled( dsBuilder.enableAccessControl() ); |
| service.setAllowAnonymousAccess( dsBuilder.allowAnonAccess() ); |
| service.getChangeLog().setEnabled( dsBuilder.enableChangeLog() ); |
| |
| dsf.init( dsBuilder.name() ); |
| |
| for ( Class<?> interceptorClass : dsBuilder.additionalInterceptors() ) |
| { |
| service.addLast( ( Interceptor ) interceptorClass.newInstance() ); |
| } |
| |
| List<Interceptor> interceptorList = service.getInterceptors(); |
| |
| if ( dsBuilder.authenticators().length != 0 ) |
| { |
| AuthenticationInterceptor authenticationInterceptor = null; |
| |
| for ( Interceptor interceptor : interceptorList ) |
| { |
| if ( interceptor instanceof AuthenticationInterceptor ) |
| { |
| authenticationInterceptor = ( AuthenticationInterceptor ) interceptor; |
| break; |
| } |
| } |
| |
| if ( authenticationInterceptor == null ) |
| { |
| throw new IllegalStateException( |
| "authentication interceptor not found" ); |
| } |
| |
| Set<Authenticator> authenticators = new HashSet<>(); |
| |
| for ( CreateAuthenticator createAuthenticator : dsBuilder |
| .authenticators() ) |
| { |
| Authenticator auth = createAuthenticator.type().newInstance(); |
| |
| if ( auth instanceof DelegatingAuthenticator ) |
| { |
| DelegatingAuthenticator dauth = ( DelegatingAuthenticator ) auth; |
| |
| String host = createAuthenticator.delegateHost(); |
| |
| if ( Strings.isEmpty( host ) ) |
| { |
| host = Network.LOOPBACK_HOSTNAME; |
| } |
| |
| dauth.setDelegateHost( host ); |
| dauth.setDelegatePort( createAuthenticator.delegatePort() ); |
| dauth.setDelegateSsl( createAuthenticator.delegateSsl() ); |
| dauth.setDelegateTls( createAuthenticator.delegateTls() ); |
| dauth.setBaseDn( service.getDnFactory().create( createAuthenticator.baseDn() ) ); |
| dauth.setDelegateSslTrustManagerFQCN( createAuthenticator.delegateSslTrustManagerFQCN() ); |
| dauth.setDelegateTlsTrustManagerFQCN( createAuthenticator.delegateTlsTrustManagerFQCN() ); |
| } |
| |
| authenticators.add( auth ); |
| } |
| |
| authenticationInterceptor.setAuthenticators( authenticators ); |
| authenticationInterceptor.init( service ); |
| } |
| |
| service.setInterceptors( interceptorList ); |
| |
| SchemaManager schemaManager = service.getSchemaManager(); |
| |
| // process the schemas |
| for ( LoadSchema loadedSchema : dsBuilder.loadedSchemas() ) |
| { |
| String schemaName = loadedSchema.name(); |
| Boolean enabled = loadedSchema.enabled(); |
| |
| // Check if the schema is loaded or not |
| boolean isLoaded = schemaManager.isSchemaLoaded( schemaName ); |
| |
| if ( !isLoaded ) |
| { |
| // We have to load the schema, if it exists |
| try |
| { |
| isLoaded = schemaManager.load( schemaName ); |
| } |
| catch ( LdapUnwillingToPerformException lutpe ) |
| { |
| // Cannot load the schema, it does not exists |
| LOG.error( lutpe.getMessage() ); |
| continue; |
| } |
| } |
| |
| if ( isLoaded ) |
| { |
| if ( enabled ) |
| { |
| schemaManager.enable( schemaName ); |
| |
| if ( schemaManager.isDisabled( schemaName ) ) |
| { |
| LOG.error( "Cannot enable {}", schemaName ); |
| } |
| } |
| else |
| { |
| schemaManager.disable( schemaName ); |
| |
| if ( schemaManager.isEnabled( schemaName ) ) |
| { |
| LOG.error( "Cannot disable {}", schemaName ); |
| } |
| } |
| } |
| |
| LOG.debug( "Loading schema {}, enabled= {}", schemaName, enabled ); |
| } |
| |
| // Process the Partition, if any. |
| for ( CreatePartition createPartition : dsBuilder.partitions() ) |
| { |
| Partition partition; |
| |
| // Determine the partition type |
| if ( createPartition.type() == Partition.class ) |
| { |
| // The annotation does not specify a specific partition type. |
| // We use the partition factory to create partition and index |
| // instances. |
| PartitionFactory partitionFactory = dsf.getPartitionFactory(); |
| partition = partitionFactory.createPartition( |
| schemaManager, |
| service.getDnFactory(), |
| createPartition.name(), |
| createPartition.suffix(), |
| createPartition.cacheSize(), |
| new File( service.getInstanceLayout().getPartitionsDirectory(), createPartition.name() ) ); |
| |
| CreateIndex[] indexes = createPartition.indexes(); |
| |
| for ( CreateIndex createIndex : indexes ) |
| { |
| partitionFactory.addIndex( partition, |
| createIndex.attribute(), createIndex.cacheSize() ); |
| } |
| |
| partition.initialize(); |
| } |
| else |
| { |
| // The annotation contains a specific partition type, we use |
| // that type. |
| Class<?>[] partypes = new Class[] |
| { SchemaManager.class, DnFactory.class }; |
| Constructor<?> constructor = createPartition.type().getConstructor( partypes ); |
| partition = ( Partition ) constructor.newInstance( schemaManager, service.getDnFactory() ); |
| partition.setId( createPartition.name() ); |
| partition.setSuffixDn( new Dn( schemaManager, createPartition.suffix() ) ); |
| |
| if ( partition instanceof AbstractBTreePartition ) |
| { |
| AbstractBTreePartition btreePartition = ( AbstractBTreePartition ) partition; |
| btreePartition.setCacheSize( createPartition.cacheSize() ); |
| btreePartition.setPartitionPath( new File( service |
| .getInstanceLayout().getPartitionsDirectory(), |
| createPartition.name() ).toURI() ); |
| |
| // Process the indexes if any |
| CreateIndex[] indexes = createPartition.indexes(); |
| |
| for ( CreateIndex createIndex : indexes ) |
| { |
| if ( createIndex.type() == MavibotIndex.class ) |
| { |
| // Mavibot index |
| MavibotIndex index = new MavibotIndex( createIndex.attribute(), false ); |
| |
| btreePartition.addIndexedAttributes( index ); |
| } |
| else |
| { |
| // The annotation does not specify a specific index |
| // type. |
| // We use the generic index implementation. |
| JdbmIndex index = new JdbmIndex( createIndex.attribute(), false ); |
| |
| btreePartition.addIndexedAttributes( index ); |
| } |
| } |
| } |
| } |
| |
| partition.setSchemaManager( schemaManager ); |
| |
| // Inject the partition into the DirectoryService |
| service.addPartition( partition ); |
| |
| // Last, process the context entry |
| ContextEntry contextEntry = createPartition.contextEntry(); |
| |
| if ( contextEntry != null ) |
| { |
| injectEntries( service, contextEntry.entryLdif() ); |
| } |
| } |
| |
| return service; |
| } |
| |
| |
| /** |
| * Create a DirectoryService from a Unit test annotation |
| * |
| * @param description The annotations containing the info from which we will create |
| * the DS |
| * @return A valid DirectoryService |
| * @throws Exception If the DirectoryService instance can't be returned |
| */ |
| public static DirectoryService getDirectoryService( Description description ) |
| throws Exception |
| { |
| CreateDS dsBuilder = description.getAnnotation( CreateDS.class ); |
| |
| if ( dsBuilder != null ) |
| { |
| return createDS( dsBuilder ); |
| } |
| else |
| { |
| LOG.debug( "No {} DS.", description.getDisplayName() ); |
| return null; |
| } |
| } |
| |
| |
| /** |
| * Create a DirectoryService from an annotation. The @CreateDS annotation |
| * must be associated with either the method or the encapsulating class. We |
| * will first try to get the annotation from the method, and if there is |
| * none, then we try at the class level. |
| * |
| * @return A valid DS |
| * @throws Exception If the DirectoryService instance can't be returned |
| */ |
| public static DirectoryService getDirectoryService() throws Exception |
| { |
| Object instance = AnnotationUtils.getInstance( CreateDS.class ); |
| CreateDS dsBuilder = null; |
| |
| if ( instance != null ) |
| { |
| dsBuilder = ( CreateDS ) instance; |
| |
| // Ok, we have found a CreateDS annotation. Process it now. |
| return createDS( dsBuilder ); |
| } |
| |
| throw new LdapException( I18n.err( I18n.ERR_114 ) ); |
| } |
| |
| |
| /** |
| * injects an LDIF entry in the given DirectoryService |
| * |
| * @param entry the LdifEntry to be injected |
| * @param service the DirectoryService |
| * @throws Exception If the entry cannot be injected |
| */ |
| private static void injectEntry( LdifEntry entry, DirectoryService service ) |
| throws LdapException |
| { |
| if ( entry.isChangeAdd() || entry.isLdifContent() ) |
| { |
| service.getAdminSession().add( |
| new DefaultEntry( service.getSchemaManager(), entry |
| .getEntry() ) ); |
| } |
| else if ( entry.isChangeModify() ) |
| { |
| service.getAdminSession().modify( entry.getDn(), |
| entry.getModifications() ); |
| } |
| else |
| { |
| String message = I18n.err( I18n.ERR_117, entry.getChangeType() ); |
| throw new LdapException( message ); |
| } |
| } |
| |
| |
| /** |
| * injects the LDIF entries present in a LDIF file |
| * |
| * @param clazz The class which classLoaded will be use to retrieve the resources |
| * @param service the DirectoryService |
| * @param ldifFiles array of LDIF file names (only ) |
| * @throws Exception If we weren't able to inject LdifFiles |
| */ |
| public static void injectLdifFiles( Class<?> clazz, |
| DirectoryService service, String[] ldifFiles ) throws Exception |
| { |
| if ( ( ldifFiles != null ) && ( ldifFiles.length > 0 ) ) |
| { |
| for ( String ldifFile : ldifFiles ) |
| { |
| InputStream is = clazz.getClassLoader().getResourceAsStream( |
| ldifFile ); |
| if ( is == null ) |
| { |
| throw new FileNotFoundException( "LDIF file '" + ldifFile |
| + "' not found." ); |
| } |
| else |
| { |
| LdifReader ldifReader = new LdifReader( is ); |
| |
| for ( LdifEntry entry : ldifReader ) |
| { |
| injectEntry( entry, service ); |
| } |
| |
| ldifReader.close(); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Inject an ldif String into the server. Dn must be relative to the root. |
| * |
| * @param service the directory service to use |
| * @param ldif the ldif containing entries to add to the server. |
| * @throws Exception if there is a problem adding the entries from the LDIF |
| */ |
| public static void injectEntries( DirectoryService service, String ldif ) |
| throws Exception |
| { |
| try ( LdifReader reader = new LdifReader() ) |
| { |
| List<LdifEntry> entries = reader.parseLdif( ldif ); |
| |
| for ( LdifEntry entry : entries ) |
| { |
| injectEntry( entry, service ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Load the schemas, and enable/disable them. |
| * |
| * @param desc The description |
| * @param service The DirectoryService instance |
| */ |
| public static void loadSchemas( Description desc, DirectoryService service ) |
| { |
| if ( desc == null ) |
| { |
| return; |
| } |
| |
| LoadSchema loadSchema = desc |
| .getAnnotation( LoadSchema.class ); |
| |
| if ( loadSchema != null ) |
| { |
| System.out.println( loadSchema ); |
| } |
| } |
| |
| |
| private static boolean isDn( String str ) |
| { |
| if ( ( Strings.isEmpty( str ) ) | ( str.length() < 3 ) ) |
| { |
| return false; |
| } |
| |
| return ( ( ( str.charAt( 0 ) == 'd' ) || ( str.charAt( 0 ) == 'D' ) ) |
| && ( ( str.charAt( 1 ) == 'n' ) || ( str.charAt( 1 ) == 'N' ) ) |
| && ( str.charAt( 2 ) == ':' ) ); |
| } |
| |
| |
| /** |
| * Apply the LDIF entries to the given service |
| * |
| * @param desc The description |
| * @param service The DirectoryService instance |
| * @throws Exception If we can't apply the ldifs |
| */ |
| public static void applyLdifs( Description desc, DirectoryService service ) |
| throws Exception |
| { |
| if ( desc == null ) |
| { |
| return; |
| } |
| |
| ApplyLdifFiles applyLdifFiles = desc |
| .getAnnotation( ApplyLdifFiles.class ); |
| |
| if ( applyLdifFiles != null ) |
| { |
| LOG.debug( "Applying {} to {}", applyLdifFiles.value(), |
| desc.getDisplayName() ); |
| injectLdifFiles( applyLdifFiles.clazz(), service, applyLdifFiles.value() ); |
| } |
| |
| ApplyLdifs applyLdifs = desc.getAnnotation( ApplyLdifs.class ); |
| |
| if ( ( applyLdifs != null ) && ( applyLdifs.value() != null ) ) |
| { |
| String[] ldifs = applyLdifs.value(); |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| for ( int i = 0; i < ldifs.length; ) |
| { |
| String str = ldifs[i++].trim(); |
| |
| if ( isDn( str ) ) |
| { |
| sb.append( str ).append( '\n' ); |
| |
| // read the rest of lines till we encounter Dn again |
| while ( i < ldifs.length ) |
| { |
| str = ldifs[i++]; |
| |
| if ( !isDn( str ) ) |
| { |
| sb.append( str ).append( '\n' ); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| LOG.debug( "Applying {} to {}", sb, desc.getDisplayName() ); |
| injectEntries( service, sb.toString() ); |
| sb.setLength( 0 ); |
| |
| i--; // step up a line |
| } |
| } |
| } |
| } |
| } |