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 */
20 package org.apache.directory.server.schema.registries;
21
22
23 import java.util.Map;
24 import java.util.Properties;
25 import java.util.Stack;
26
27 import javax.naming.NamingException;
28
29 import org.apache.directory.server.schema.bootstrap.Schema;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33
34 /**
35 * An abstract class with a utility method and setListener() implemented.
36 *
37 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
38 * @version $Rev$
39 */
40 public abstract class AbstractSchemaLoader implements SchemaLoader
41 {
42 /** static class logger */
43 private static final Logger LOG = LoggerFactory.getLogger( AbstractSchemaLoader.class );
44
45 protected SchemaLoaderListener listener;
46
47
48 public void setListener( SchemaLoaderListener listener )
49 {
50 this.listener = listener;
51 }
52
53
54 protected final void notifyListenerOrRegistries( Schema schema, Registries registries )
55 {
56 if ( listener != null )
57 {
58 listener.schemaLoaded( schema );
59 }
60
61 if ( registries instanceof SchemaLoaderListener )
62 {
63 if ( registries != listener )
64 {
65 SchemaLoaderListener listener = ( SchemaLoaderListener ) registries;
66 listener.schemaLoaded( schema );
67 }
68 }
69 }
70
71
72 /**
73 * Recursive method which loads schema's with their dependent schemas first
74 * and tracks what schemas it has seen so the recursion does not go out of
75 * control with depenency cycle detection.
76 *
77 * @param rootAncestor the triggering schema load request: the root ancestor of dependency chain
78 * @param beenthere stack of schema names we have visited and have yet to load
79 * @param notLoaded hash of schemas keyed by name which have yet to be loaded
80 * @param schema the current schema we are attempting to load
81 * @param registries the set of registries to use while loading
82 * @param props to use while trying to resolve other schemas
83 * @throws NamingException if there is a cycle detected and/or another
84 * failure results while loading, producing and or registering schema objects
85 */
86 protected final void loadDepsFirst( Schema rootAncestor, Stack<String> beenthere, Map<String,Schema> notLoaded, Schema schema,
87 Registries registries, Properties props ) throws Exception
88 {
89 if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
90 {
91 LOG.warn( "{} schema has already been loaded" + schema.getSchemaName() );
92 return;
93 }
94
95 beenthere.push( schema.getSchemaName() );
96 String[] deps = schema.getDependencies();
97
98 // if no deps then load this guy and return
99 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 }