001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.schema.registries;
021    
022    
023    import java.util.Map;
024    import java.util.Properties;
025    import java.util.Stack;
026    
027    import javax.naming.NamingException;
028    
029    import org.apache.directory.server.schema.bootstrap.Schema;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    
034    /**
035     * An abstract class with a utility method and setListener() implemented.
036     *
037     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038     * @version $Rev$
039     */
040    public abstract class AbstractSchemaLoader implements SchemaLoader
041    {
042        /** static class logger */
043        private static final Logger LOG = LoggerFactory.getLogger( AbstractSchemaLoader.class );
044        
045        protected SchemaLoaderListener listener;
046        
047        
048        public void setListener( SchemaLoaderListener listener )
049        {
050            this.listener = listener;
051        }
052        
053        
054        protected final void notifyListenerOrRegistries( Schema schema, Registries registries )
055        {
056            if ( listener != null )
057            {
058                listener.schemaLoaded( schema );
059            }
060            
061            if ( registries instanceof SchemaLoaderListener )
062            {
063                if ( registries != listener )
064                {
065                    SchemaLoaderListener listener = ( SchemaLoaderListener ) registries;
066                    listener.schemaLoaded( schema );
067                }
068            }
069        }
070        
071        
072        /**
073         * Recursive method which loads schema's with their dependent schemas first
074         * and tracks what schemas it has seen so the recursion does not go out of
075         * control with depenency cycle detection.
076         *
077         * @param rootAncestor the triggering schema load request: the root ancestor of dependency chain
078         * @param beenthere stack of schema names we have visited and have yet to load
079         * @param notLoaded hash of schemas keyed by name which have yet to be loaded
080         * @param schema the current schema we are attempting to load
081         * @param registries the set of registries to use while loading
082         * @param props to use while trying to resolve other schemas
083         * @throws NamingException if there is a cycle detected and/or another
084         * failure results while loading, producing and or registering schema objects
085         */
086        protected final void loadDepsFirst( Schema rootAncestor, Stack<String> beenthere, Map<String,Schema> notLoaded, Schema schema,
087            Registries registries, Properties props ) throws Exception
088        {
089            if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
090            {
091                LOG.warn( "{} schema has already been loaded" + schema.getSchemaName() );
092                return;
093            }
094            
095            beenthere.push( schema.getSchemaName() );
096            String[] deps = schema.getDependencies();
097    
098            // if no deps then load this guy and return
099            if ( deps == null || deps.length == 0 )
100            {
101                if ( rootAncestor == schema )
102                {
103                    load( schema, registries, false );
104                }
105                else
106                {
107                    load( schema, registries, true );
108                }
109                
110                notLoaded.remove( schema.getSchemaName() );
111                beenthere.pop();
112                return;
113            }
114    
115            /*
116             * We got deps and need to load them before this schema.  We go through
117             * all deps loading them with their deps first if they have not been
118             * loaded.
119             */
120            for ( String depName : deps )
121            {
122                // @todo if a dependency is not loaded it's not in this list
123                // @todo why is it not in this list?  Without being in this list
124                // @todo this for loop is absolutely useless - we will not load
125                // @todo any disabled dependencies at all.  I'm shocked that the
126                // @todo samba schema is actually loading when the nis dependency
127                // @todo is not loaded.
128    
129                if ( !notLoaded.containsKey( depName ) )
130                {
131                    continue;
132                }
133    
134                Schema dep = notLoaded.get( depName );
135    
136                // dep is not in the set of schema objects we need to try to resolve it
137                if ( dep == null )
138                {
139                    // try to load dependency with the provided properties default
140                    dep = getSchema( depName, props );
141                }
142    
143                if ( beenthere.contains( dep.getSchemaName() ) )
144                {
145                    // push again so we show the cycle in output
146                    beenthere.push( dep.getSchemaName() );
147                    throw new NamingException( "schema dependency cycle detected: " + beenthere );
148                }
149    
150                loadDepsFirst( rootAncestor, beenthere, notLoaded, dep, registries, props );
151            }
152    
153            // We have loaded all our deps so we can load this schema
154            if ( rootAncestor == schema )
155            {
156                load( schema, registries, false );
157            }
158            else
159            {
160                load( schema, registries, true );
161            }
162            
163            notLoaded.remove( schema.getSchemaName() );
164            beenthere.pop();
165        }
166    }