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.Collections;
024 import java.util.HashSet;
025 import java.util.Iterator;
026 import java.util.Map;
027 import java.util.Set;
028 import java.util.concurrent.ConcurrentHashMap;
029
030 import javax.naming.NamingException;
031 import javax.naming.directory.NoSuchAttributeException;
032
033 import org.apache.directory.shared.asn1.primitives.OID;
034 import org.apache.directory.shared.ldap.schema.AttributeType;
035 import org.apache.directory.shared.ldap.schema.MatchingRule;
036 import org.apache.directory.shared.ldap.schema.normalizers.NoOpNormalizer;
037 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer;
038
039 import org.slf4j.Logger;
040 import org.slf4j.LoggerFactory;
041
042
043 /**
044 * A plain old java object implementation of an AttributeTypeRegistry.
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 * @version $Rev: 778529 $
048 */
049 public class DefaultAttributeTypeRegistry implements AttributeTypeRegistry
050 {
051 /** static class logger */
052 private static final Logger LOG = LoggerFactory.getLogger( DefaultAttributeTypeRegistry.class );
053
054 /** Speedup for DEBUG mode */
055 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
056
057 /** maps an OID to an AttributeType */
058 private final Map<String,AttributeType> byOidAT;
059
060 /** maps OIDs to a Set of descendants for that OID */
061 private final Map<String,Set<AttributeType>> oidToDescendantSet;
062
063 /** the Registry used to resolve names to OIDs */
064 private final OidRegistry oidRegistry;
065
066 /** cached Oid/normalizer mapping */
067 private transient Map<String, OidNormalizer> oidNormalizerMap;
068
069
070 // ------------------------------------------------------------------------
071 // C O N S T R U C T O R S
072 // ------------------------------------------------------------------------
073 /**
074 * Creates an empty DefaultAttributeTypeRegistry.
075 *
076 * @param oidRegistry used by this registry for OID to name resolution of
077 * dependencies and to automatically register and unregister it's aliases and OIDs
078 */
079 public DefaultAttributeTypeRegistry( OidRegistry oidRegistry )
080 {
081 byOidAT = new ConcurrentHashMap<String,AttributeType>();
082 oidToDescendantSet= new ConcurrentHashMap<String,Set<AttributeType>>();
083 oidNormalizerMap = new ConcurrentHashMap<String, OidNormalizer>();
084 this.oidRegistry = oidRegistry;
085 }
086
087
088 // ------------------------------------------------------------------------
089 // Service Methods
090 // ------------------------------------------------------------------------
091 /**
092 * {@inheritDoc}
093 */
094 public void register( AttributeType attributeType ) throws NamingException
095 {
096 if ( byOidAT.containsKey( attributeType.getOid() ) )
097 {
098 String msg = "attributeType " + attributeType.getName() + " w/ OID " + attributeType.getOid()
099 + " has already been registered!";
100 LOG.error( msg );
101 throw new NamingException( msg );
102 }
103
104 // First, register the AttributeType names and oid in the global
105 // OidRegistry
106 String[] names = attributeType.getNamesRef();
107 String oid = attributeType.getOid();
108
109 for ( String name : names )
110 {
111 oidRegistry.register( name, oid );
112 }
113
114 // Also register the oid/oid relation
115 oidRegistry.register( oid, oid );
116
117 // Inject the attributeType into the oid/normalizer map
118 addMappingFor( attributeType );
119
120 // Register this AttributeType into the Descendant map
121 registerDescendants( attributeType );
122
123 // Internally associate the OID to the registered AttributeType
124 byOidAT.put( attributeType.getOid(), attributeType );
125
126 if ( IS_DEBUG )
127 {
128 LOG.debug( "registred attributeType: {}", attributeType );
129 }
130 }
131
132
133 /**
134 * Store the AttributeType into a map associating an AttributeType to its
135 * descendants.
136 *
137 * @param attributeType The attributeType to register
138 * @throws NamingException If something went wrong
139 */
140 public void registerDescendants( AttributeType attributeType ) throws NamingException
141 {
142 // add this attribute to descendant list of other attributes in superior chain
143 onRegisterAddToAncestorDescendants( attributeType, attributeType.getSuperior() );
144 }
145
146
147 /**
148 * Recursively adds a new attributeType to the descendant's list of all ancestors
149 * until top is reached. Top will not have the new type added.
150 *
151 * @param newType the new attributeType being added
152 * @param ancestor some ancestor from superior up to and including top
153 * @throws NamingException if there are resolution failures
154 */
155 protected void onRegisterAddToAncestorDescendants( AttributeType newType, AttributeType ancestor )
156 throws NamingException
157 {
158 if ( ancestor == null )
159 {
160 return;
161 }
162
163 // Get the ancestor's descendant, if any
164 Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
165
166 // Initialize the descendant Set to store the descendants for the attributeType
167 if ( descendants == null )
168 {
169 descendants = new HashSet<AttributeType>( 1 );
170 oidToDescendantSet.put( ancestor.getOid(), descendants );
171 }
172
173 // Add the current type as a descendant
174 descendants.add( newType );
175
176 // And recurse until we reach the top of the hierarchy
177 onRegisterAddToAncestorDescendants( newType, ancestor.getSuperior() );
178 }
179
180
181 /**
182 * {@inheritDoc}
183 */
184 public AttributeType lookup( String id ) throws NamingException
185 {
186 String oid = oidRegistry.getOid( id );
187 AttributeType attributeType = byOidAT.get( oid );
188
189 if ( attributeType == null )
190 {
191 String msg = "attributeType w/ OID " + oid + " not registered!";
192 LOG.error( msg );
193 throw new NoSuchAttributeException( msg );
194 }
195
196 if ( IS_DEBUG )
197 {
198 LOG.debug( "lookup with id '{}' for attributeType: {}", oid, attributeType );
199 }
200
201 return attributeType;
202 }
203
204
205 /**
206 * {@inheritDoc}
207 */
208 public boolean hasAttributeType( String id )
209 {
210 try
211 {
212 String oid = oidRegistry.getOid( id );
213
214 if ( oid != null )
215 {
216 return byOidAT.containsKey( oid );
217 }
218
219 return false;
220 }
221 catch ( NamingException e )
222 {
223 return false;
224 }
225 }
226
227
228 /**
229 * {@inheritDoc}
230 */
231 public String getSchemaName( String id ) throws NamingException
232 {
233 AttributeType at = byOidAT.get( oidRegistry.getOid( id ) );
234
235 if ( at != null )
236 {
237 return at.getSchema();
238 }
239
240 String msg = "OID " + id + " not found in oid to " + "AttributeType map!";
241 LOG.debug( msg );
242 throw new NamingException( msg );
243 }
244
245
246 /**
247 * Remove the AttributeType normalizer from the OidNormalizer map
248 */
249 private void removeMappingFor( AttributeType type ) throws NamingException
250 {
251 if ( type == null )
252 {
253 return;
254 }
255
256 oidNormalizerMap.remove( type.getOid() );
257
258 // We also have to remove all the short names for this attribute
259 String[] aliases = type.getNamesRef();
260
261 for ( String aliase : aliases )
262 {
263 oidNormalizerMap.remove( aliase );
264 oidNormalizerMap.remove( aliase.toLowerCase() );
265 }
266 }
267
268
269 /**
270 * Add a new Oid/Normalizer couple in the OidNormalizer map
271 */
272 private void addMappingFor( AttributeType type ) throws NamingException
273 {
274 MatchingRule matchingRule = type.getEquality();
275 OidNormalizer oidNormalizer;
276 String oid = type.getOid();
277
278 if ( matchingRule == null )
279 {
280 LOG.debug( "Attribute {} does not have normalizer : using NoopNormalizer", type.getName() );
281 oidNormalizer = new OidNormalizer( oid, new NoOpNormalizer() );
282 }
283 else
284 {
285 oidNormalizer = new OidNormalizer( oid, matchingRule.getNormalizer() );
286 }
287
288 oidNormalizerMap.put( oid, oidNormalizer );
289
290 // Also inject the attributeType's short nampes in the map
291 String[] aliases = type.getNamesRef();
292
293 for ( String aliase : aliases )
294 {
295 oidNormalizerMap.put( aliase, oidNormalizer );
296 oidNormalizerMap.put( aliase.toLowerCase(), oidNormalizer );
297 }
298 }
299
300
301 /**
302 * {@inheritDoc}
303 */
304 public Map<String,OidNormalizer> getNormalizerMapping() throws NamingException
305 {
306 return Collections.unmodifiableMap( oidNormalizerMap );
307 }
308
309
310 /**
311 * {@inheritDoc}
312 */
313 public Iterator<AttributeType> descendants( String ancestorId ) throws NamingException
314 {
315 String oid = oidRegistry.getOid( ancestorId );
316 Set<AttributeType> descendants = oidToDescendantSet.get( oid );
317
318 if ( descendants == null )
319 {
320 return Collections.EMPTY_SET.iterator();
321 }
322
323 return descendants.iterator();
324 }
325
326
327 /**
328 * {@inheritDoc}
329 */
330 public boolean hasDescendants( String ancestorId ) throws NamingException
331 {
332 String oid = oidRegistry.getOid( ancestorId );
333 Set<AttributeType> descendants = oidToDescendantSet.get( oid );
334 return (descendants != null) && !descendants.isEmpty();
335 }
336
337
338 /**
339 * {@inheritDoc}
340 */
341 public Iterator<AttributeType> iterator()
342 {
343 return byOidAT.values().iterator();
344 }
345
346
347 /**
348 * {@inheritDoc}
349 */
350 public void unregister( String numericOid ) throws NamingException
351 {
352 if ( ! OID.isOID( numericOid ) )
353 {
354 String msg = "Looks like the arg " + numericOid + " is not a numeric OID";
355 LOG.error(msg );
356 throw new NamingException( msg );
357 }
358
359 removeMappingFor( byOidAT.get( numericOid ));
360 byOidAT.remove( numericOid );
361 oidToDescendantSet.remove( numericOid );
362 }
363
364
365 public String toString()
366 {
367 StringBuilder sb = new StringBuilder();
368
369 for ( String value:byOidAT.keySet() )
370 {
371 sb.append( value ).append( ":" ).append( byOidAT.get( value ) ).append( '\n' );
372 }
373
374 return sb.toString();
375 }
376 }