You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2009/08/30 19:12:04 UTC

svn commit: r809383 - /directory/shared/branches/shared-schema/ldap-schema-loader/src/main/java/org/apache/directory/shared/schema/loader/ldif/LdifSchemaLoader.java

Author: akarasulu
Date: Sun Aug 30 17:12:04 2009
New Revision: 809383

URL: http://svn.apache.org/viewvc?rev=809383&view=rev
Log:
added dependency based deferred loading capability to loader for schema object interdepenencies within the same schema that were not being observed by the way the ldif files were loaded

Modified:
    directory/shared/branches/shared-schema/ldap-schema-loader/src/main/java/org/apache/directory/shared/schema/loader/ldif/LdifSchemaLoader.java

Modified: directory/shared/branches/shared-schema/ldap-schema-loader/src/main/java/org/apache/directory/shared/schema/loader/ldif/LdifSchemaLoader.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/shared-schema/ldap-schema-loader/src/main/java/org/apache/directory/shared/schema/loader/ldif/LdifSchemaLoader.java?rev=809383&r1=809382&r2=809383&view=diff
==============================================================================
--- directory/shared/branches/shared-schema/ldap-schema-loader/src/main/java/org/apache/directory/shared/schema/loader/ldif/LdifSchemaLoader.java (original)
+++ directory/shared/branches/shared-schema/ldap-schema-loader/src/main/java/org/apache/directory/shared/schema/loader/ldif/LdifSchemaLoader.java Sun Aug 30 17:12:04 2009
@@ -39,6 +39,8 @@
 import org.apache.directory.shared.ldap.util.DateUtils;
 import org.apache.directory.shared.ldap.constants.SchemaConstants;
 import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
 import org.apache.directory.shared.ldap.ldif.LdifEntry;
 import org.apache.directory.shared.ldap.ldif.LdifReader;
 import org.apache.directory.shared.ldap.ldif.LdifUtils;
@@ -49,8 +51,10 @@
 import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.FilenameFilter;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Stack;
 
@@ -543,24 +547,134 @@
      */
     private void loadAttributeTypes( Schema schema, Registries registries ) throws Exception
     {
-        File attributeTypesDirectory = new File ( getSchemaDirectory( schema ), 
-            ATTRIBUTE_TYPES_DIRNAME );
-        
+    	/*
+    	 * AT's can depend on another AT via the superior relationship.  
+    	 * Because we separate each schema object into its own LDIF file and
+    	 * the file system scan producing the listing of files used to order
+    	 * the LDIF loads does not consider these dependencies we can have
+    	 * a situation where the superior may not be loaded when a dependent
+    	 * AT is loaded.
+    	 * 
+    	 * For this reason we must defer the loading of some LDIF entries 
+    	 * until their superior AT is actually loaded.  This hash stores
+    	 * LDIF entries keyed by the name of their superior AT.  When the
+    	 * superior is loaded, the deferred entries depending on that superior
+    	 * are loaded and the list of dependent entries are removed from this 
+    	 * hash map.
+    	 * 
+    	 * NOTE: Because we don't have an OID and must use a potentially 
+    	 * case varying String as the key in this map, we reduce the String to
+    	 * it's lower cased cannonical form.
+    	 */
+    	Map<String,List<LdifEntry>> deferredEntries = new HashMap<String, List<LdifEntry>>();
+
+    	// check that the attributeTypes directory exists for the schema
+        File attributeTypesDirectory = new File ( getSchemaDirectory( schema ), ATTRIBUTE_TYPES_DIRNAME );
         if ( ! attributeTypesDirectory.exists() )
         {
             return;
         }
         
+        // get list of attributeType LDIF schema files in attributeTypes
         File[] attributeTypeFiles = attributeTypesDirectory.listFiles( ldifFilter );
         for ( File ldifFile : attributeTypeFiles )
         {
             LdifReader reader = new LdifReader( ldifFile );
             LdifEntry entry = reader.next();
-            AttributeType attributeType = factory.getAttributeType( 
-                entry.getEntry(), registries, schema.getSchemaName() );
-            registries.getAttributeTypeRegistry().register( attributeType );
+            loadAttributeType( schema, deferredEntries, entry, registries );
+        }
+
+        
+        if ( ! deferredEntries.isEmpty() )
+        {
+        	for ( String missingSuperior : deferredEntries.keySet() )
+        	{
+        		if ( registries.getObjectClassRegistry().containsName( missingSuperior ) )
+        		{
+        			for ( LdifEntry entry : deferredEntries.get( missingSuperior ) )
+        			{
+        				if ( loadAttributeType( schema, deferredEntries, entry, registries ) )
+        				{
+        					LOG.error( "Still failed to load schema entry: {}", entry );
+        				}
+        			}
+        		}
+        	}
         }
     }
+    
+    
+    /**
+     * Recursive method that loads an AT, and other deferred ATs that depend
+     * on it.  This is separated from and used by 
+     * {@link #loadAttributeTypes(Schema, Registries)} to enter into this 
+     * method.
+     * 
+     * If the AT being loaded has deferred entries waiting on it to be loaded,
+     * then the AT is loaded then it's deferred entries are loaded by making a
+     * recursive call to this method.  This begins the process a new making 
+     * sure all deferred descendants in the AT hierarchy are load.
+     * 
+     * @param schema the schema we are loading
+     * @param deferredEntries map of deferred AT LDIF entries
+     * @param entry the AT LDIF entry to load
+     * @param registries the registries the schema objects are loaded into
+     * @return true if the AT is loaded, false otherwise
+     * @throws Exception if there any failures looking up or registering
+     */
+    private boolean loadAttributeType( Schema schema, Map<String,List<LdifEntry>> deferredEntries, LdifEntry entry,
+    		Registries registries ) throws Exception
+    {
+        // get superior name and if exists check if loaded, defer if not
+        EntryAttribute superior = entry.getEntry().get( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT );
+        if ( superior != null )
+        {
+        	String superiorName = superior.getString().toLowerCase();
+        	
+        	if ( ! registries.getAttributeTypeRegistry().containsName( superiorName ) )
+        	{
+        		List<LdifEntry> dependents = deferredEntries.get( superiorName );
+        		if ( dependents == null )
+        		{
+        			dependents = new ArrayList<LdifEntry>();
+        			deferredEntries.put( superiorName, dependents );
+        		}
+
+        		dependents.add( entry );
+        		return false;  // - return false if deferred, true when loaded
+        	}
+        }
+        
+        AttributeType attributeType = factory.getAttributeType( entry.getEntry(), registries, schema.getSchemaName() );
+        registries.getAttributeTypeRegistry().register( attributeType );
+
+        // after registering AT check if any deferred entries depend on it
+        if ( attributeType.getNames() != null )
+        {
+        	for ( String name : attributeType.getNames() )
+        	{
+        		if ( deferredEntries.containsKey( name.toLowerCase() ) )
+        		{
+        			List<LdifEntry> deferredList = deferredEntries.get( name.toLowerCase() );
+        			List<LdifEntry> copiedList = new ArrayList<LdifEntry>( deferredList );
+        			for ( LdifEntry deferred : copiedList )
+        			{
+        				if ( loadAttributeType( schema, deferredEntries, deferred, registries ) )
+        				{
+        					deferredList.remove( deferred );
+        				}
+        			}
+        			
+        			if ( deferredList.isEmpty() )
+        			{
+        				deferredEntries.remove( name.toLowerCase() );
+        			}
+        		}
+        	}
+        }
+        
+        return true;
+    }
 
 
     /**
@@ -726,22 +840,133 @@
      */
     private void loadObjectClasses( Schema schema, Registries registries ) throws Exception
     {
-        File objectClassesDirectory = new File( getSchemaDirectory( schema ),
-            OBJECT_CLASSES_DIRNAME );
-        
+    	/*
+    	 * OC's can depend on other OCs via their list of superior OCs.  
+    	 * Because we separate each schema object into its own LDIF file and
+    	 * the file system scan producing the listing of files used to order
+    	 * the LDIF loads does not consider these dependencies we can have
+    	 * a situation where the superiors may not be loaded when a dependent
+    	 * OC is loaded.
+    	 * 
+    	 * For this reason we must defer the loading of some LDIF entries 
+    	 * until their superior OCs are actually loaded.  This hash stores
+    	 * LDIF entries keyed by the name of their superior OCs.  When a
+    	 * superior is loaded, the deferred entries depending on that superior
+    	 * are loaded and the list of dependent entries are removed from this 
+    	 * hash map.
+    	 * 
+    	 * NOTE: Because we don't have an OID and must use a potentially 
+    	 * case varying String as the key in this map, we reduce the String to
+    	 * it's lower cased cannonical form.
+    	 */
+    	Map<String,List<LdifEntry>> deferredEntries = new HashMap<String, List<LdifEntry>>();
+
+    	// get objectClasses directory, check if exists, return if not
+    	File objectClassesDirectory = new File( getSchemaDirectory( schema ), OBJECT_CLASSES_DIRNAME );
         if ( ! objectClassesDirectory.exists() )
         {
             return;
         }
         
+        // get list of objectClass LDIF files from directory and load
         File[] objectClassFiles = objectClassesDirectory.listFiles( ldifFilter );
         for ( File ldifFile : objectClassFiles )
         {
             LdifReader reader = new LdifReader( ldifFile );
             LdifEntry entry = reader.next();
-            ObjectClass objectClass = factory.getObjectClass( 
-                entry.getEntry(), registries, schema.getSchemaName() );
-            registries.getObjectClassRegistry().register( objectClass );
+            loadObjectClass( schema, deferredEntries, entry, registries );
+        }
+        
+        if ( ! deferredEntries.isEmpty() )
+        {
+        	for ( String missingSuperior : deferredEntries.keySet() )
+        	{
+        		if ( registries.getObjectClassRegistry().containsName( missingSuperior ) )
+        		{
+        			for ( LdifEntry entry : deferredEntries.get( missingSuperior ) )
+        			{
+        				if ( loadObjectClass( schema, deferredEntries, entry, registries ) )
+        				{
+        					LOG.error( "Still failed to load schema entry: {}", entry );
+        				}
+        			}
+        		}
+        	}
+        }
+    }
+    
+    
+    /**
+     * Recursive method that loads an OC, and other deferred OCs that may
+     * depend on the initial OC loaded.  This is separated from and used by
+     * {@link #loadObjectClasses(Schema, Registries)} to enter into this 
+     * method.
+     * 
+     * If the OC being loaded has deferred entries waiting on it to be loaded,
+     * then the OC is loaded then it's deferred entries are loaded by making a
+     * recursive call to this method.  This begins the process a new making 
+     * sure all deferred descendants in the OC hierarchy are load.
+     * 
+     * @param schema the schema we are loading
+     * @param deferredEntries map of deferred OC LDIF entries
+     * @param entry the OC LDIF entry to load
+     * @param registries the registries the schema objects are loaded into
+     * @return true if the OC is loaded, false otherwise
+     * @throws Exception if there any failures looking up or registering
+     */
+    private boolean loadObjectClass( Schema schema, Map<String,List<LdifEntry>> deferredEntries, LdifEntry entry, 
+    		Registries registries ) throws Exception
+	{
+        // get superior name and if exists check if loaded, defer if not
+        EntryAttribute superiors = entry.getEntry().get( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT );
+        if ( superiors != null )
+        {
+        	for ( Value<?> value : superiors )
+        	{
+        		String superiorName = value.getString().toLowerCase();
+            	if ( ! registries.getObjectClassRegistry().containsName( superiorName ) )
+            	{
+            		List<LdifEntry> dependents = deferredEntries.get( superiorName );
+            		if ( dependents == null )
+            		{
+            			dependents = new ArrayList<LdifEntry>();
+            			deferredEntries.put( superiorName, dependents );
+            		}
+
+            		dependents.add( entry );
+            		return false;  // - return false if deferred, true when loaded
+            	}
+        	}
+        }
+        
+        ObjectClass objectClass = factory.getObjectClass( entry.getEntry(), registries, schema.getSchemaName() );
+        registries.getObjectClassRegistry().register( objectClass );
+
+        // after registering AT check if any deferred entries depend on it
+        if ( objectClass.getNames() != null )
+        {
+        	for ( String name : objectClass.getNames() )
+        	{
+        		if ( deferredEntries.containsKey( name.toLowerCase() ) )
+        		{
+        			List<LdifEntry> deferredList = deferredEntries.get( name.toLowerCase() );
+        			List<LdifEntry> copiedList = new ArrayList<LdifEntry>( deferredList );
+        			for ( LdifEntry deferred : copiedList )
+        			{
+        				if ( loadObjectClass( schema, deferredEntries, deferred, registries ) )
+        				{
+        					deferredList.remove( deferred );
+        				}
+        			}
+        			
+        			if ( deferredList.isEmpty() )
+        			{
+        				deferredEntries.remove( name.toLowerCase() );
+        			}
+        		}
+        	}
         }
+        
+        return true;
     }
 }