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 }