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.shared.ldap.schema.registries;
021
022
023 import java.util.HashMap;
024 import java.util.Iterator;
025 import java.util.Map;
026
027 import org.apache.directory.shared.asn1.primitives.OID;
028 import org.apache.directory.shared.i18n.I18n;
029 import org.apache.directory.shared.ldap.exception.LdapAttributeInUseException;
030 import org.apache.directory.shared.ldap.exception.LdapException;
031 import org.apache.directory.shared.ldap.schema.LoadableSchemaObject;
032 import org.apache.directory.shared.ldap.schema.SchemaObject;
033 import org.apache.directory.shared.ldap.schema.SchemaObjectType;
034 import org.apache.directory.shared.ldap.util.StringTools;
035 import org.slf4j.Logger;
036 import org.slf4j.LoggerFactory;
037
038
039 /**
040 * Common schema object registry interface.
041 *
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 * @version $Rev$, $Date$
044 */
045 public abstract class DefaultSchemaObjectRegistry<T extends SchemaObject> implements SchemaObjectRegistry<T>,
046 Iterable<T>
047 {
048 /** static class logger */
049 private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaObjectRegistry.class );
050
051 /** A speedup for debug */
052 private static final boolean DEBUG = LOG.isDebugEnabled();
053
054 /** a map of SchemaObject looked up by name */
055 protected Map<String, T> byName;
056
057 /** The SchemaObject type, used by the toString() method */
058 protected SchemaObjectType schemaObjectType;
059
060 /** the global OID Registry */
061 protected OidRegistry oidRegistry;
062
063
064 /**
065 * Creates a new DefaultSchemaObjectRegistry instance.
066 */
067 protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry oidRegistry )
068 {
069 byName = new HashMap<String, T>();
070 this.schemaObjectType = schemaObjectType;
071 this.oidRegistry = oidRegistry;
072 }
073
074
075 /**
076 * {@inheritDoc}
077 */
078 public boolean contains( String oid )
079 {
080 if ( !byName.containsKey( oid ) )
081 {
082 return byName.containsKey( StringTools.toLowerCase( oid ) );
083 }
084
085 return true;
086 }
087
088
089 /**
090 * {@inheritDoc}
091 */
092 public String getSchemaName( String oid ) throws LdapException
093 {
094 if ( !OID.isOID( oid ) )
095 {
096 String msg = I18n.err( I18n.ERR_04267 );
097 LOG.warn( msg );
098 throw new LdapException( msg );
099 }
100
101 SchemaObject schemaObject = byName.get( oid );
102
103 if ( schemaObject != null )
104 {
105 return schemaObject.getSchemaName();
106 }
107
108 String msg = I18n.err( I18n.ERR_04268, oid );
109 LOG.warn( msg );
110 throw new LdapException( msg );
111 }
112
113
114 /**
115 * {@inheritDoc}
116 */
117 public void renameSchema( String originalSchemaName, String newSchemaName )
118 {
119 // Loop on all the SchemaObjects stored and remove those associated
120 // with the give schemaName
121 for ( T schemaObject : this )
122 {
123 if ( originalSchemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
124 {
125 schemaObject.setSchemaName( newSchemaName );
126
127 if ( DEBUG )
128 {
129 LOG.debug( "Renamed {} schemaName to {}", schemaObject, newSchemaName );
130 }
131 }
132 }
133 }
134
135
136 /**
137 * {@inheritDoc}
138 */
139 public Iterator<T> iterator()
140 {
141 return ( Iterator<T> ) oidRegistry.iterator();
142 }
143
144
145 /**
146 * {@inheritDoc}
147 */
148 public Iterator<String> oidsIterator()
149 {
150 return byName.keySet().iterator();
151 }
152
153
154 /**
155 * {@inheritDoc}
156 */
157 public T lookup( String oid ) throws LdapException
158 {
159 if ( oid == null )
160 {
161 return null;
162 }
163
164 T schemaObject = byName.get( oid );
165
166 if ( schemaObject == null )
167 {
168 // let's try with trimming and lowercasing now
169 schemaObject = byName.get( StringTools.trim( StringTools.toLowerCase( oid ) ) );
170 }
171
172 if ( schemaObject == null )
173 {
174 String msg = I18n.err( I18n.ERR_04269, schemaObjectType.name(), oid );
175 LOG.debug( msg );
176 throw new LdapException( msg );
177 }
178
179 if ( DEBUG )
180 {
181 LOG.debug( "Found {} with oid: {}", schemaObject, oid );
182 }
183
184 return schemaObject;
185 }
186
187
188 /**
189 * {@inheritDoc}
190 */
191 public void register( T schemaObject ) throws LdapException
192 {
193 String oid = schemaObject.getOid();
194
195 if ( byName.containsKey( oid ) )
196 {
197 String msg = I18n.err( I18n.ERR_04270, schemaObjectType.name(), oid );
198 LOG.warn( msg );
199 throw new LdapAttributeInUseException( msg );
200 }
201
202 byName.put( oid, schemaObject );
203
204 /*
205 * add the aliases/names to the name map along with their toLowerCase
206 * versions of the name: this is used to make sure name lookups work
207 */
208 for ( String name : schemaObject.getNames() )
209 {
210 String lowerName = StringTools.trim( StringTools.toLowerCase( name ) );
211
212 if ( byName.containsKey( lowerName ) )
213 {
214 String msg = I18n.err( I18n.ERR_04271, schemaObjectType.name(), name );
215 LOG.warn( msg );
216 throw new LdapAttributeInUseException( msg );
217 }
218 else
219 {
220 byName.put( lowerName, schemaObject );
221 }
222 }
223
224 // And register the oid -> schemaObject relation
225 oidRegistry.register( schemaObject );
226
227 if ( LOG.isDebugEnabled() )
228 {
229 LOG.debug( "registered " + schemaObject.getName() + " for OID {}", oid );
230 }
231 }
232
233
234 /**
235 * {@inheritDoc}
236 */
237 public T unregister( String numericOid ) throws LdapException
238 {
239 if ( !OID.isOID( numericOid ) )
240 {
241 String msg = I18n.err( I18n.ERR_04272, numericOid );
242 LOG.error( msg );
243 throw new LdapException( msg );
244 }
245
246 T schemaObject = byName.remove( numericOid );
247
248 for ( String name : schemaObject.getNames() )
249 {
250 byName.remove( name );
251 }
252
253 // And remove the SchemaObject from the oidRegistry
254 oidRegistry.unregister( numericOid );
255
256 if ( DEBUG )
257 {
258 LOG.debug( "Removed {} with oid {} from the registry", schemaObject, numericOid );
259 }
260
261 return schemaObject;
262 }
263
264
265 /**
266 * {@inheritDoc}
267 */
268 public T unregister( T schemaObject ) throws LdapException
269 {
270 String oid = schemaObject.getOid();
271
272 if ( !byName.containsKey( oid ) )
273 {
274 String msg = I18n.err( I18n.ERR_04273, schemaObjectType.name(), oid );
275 LOG.warn( msg );
276 throw new LdapException( msg );
277 }
278
279 // Remove the oid
280 T removed = byName.remove( oid );
281
282 /*
283 * Remove the aliases/names from the name map along with their toLowerCase
284 * versions of the name.
285 */
286 for ( String name : schemaObject.getNames() )
287 {
288 byName.remove( StringTools.trim( StringTools.toLowerCase( name ) ) );
289 }
290
291 // And unregister the oid -> schemaObject relation
292 oidRegistry.unregister( oid );
293
294 return removed;
295 }
296
297
298 /**
299 * {@inheritDoc}
300 */
301 public void unregisterSchemaElements( String schemaName ) throws LdapException
302 {
303 if ( schemaName == null )
304 {
305 return;
306 }
307
308 // Loop on all the SchemaObjects stored and remove those associated
309 // with the give schemaName
310 for ( T schemaObject : this )
311 {
312 if ( schemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
313 {
314 String oid = schemaObject.getOid();
315 SchemaObject removed = unregister( oid );
316
317 if ( DEBUG )
318 {
319 LOG.debug( "Removed {} with oid {} from the registry", removed, oid );
320 }
321 }
322 }
323 }
324
325
326 /**
327 * {@inheritDoc}
328 */
329 public String getOidByName( String name ) throws LdapException
330 {
331 T schemaObject = byName.get( name );
332
333 if ( schemaObject == null )
334 {
335 // last resort before giving up check with lower cased version
336 String lowerCased = name.toLowerCase();
337
338 schemaObject = byName.get( lowerCased );
339
340 // ok this name is not for a schema object in the registry
341 if ( schemaObject == null )
342 {
343 throw new LdapException( I18n.err( I18n.ERR_04274, name ) );
344 }
345 }
346
347 // we found the schema object by key on the first lookup attempt
348 return schemaObject.getOid();
349 }
350
351
352 /**
353 * {@inheritDoc}
354 */
355 public SchemaObjectRegistry<T> copy( SchemaObjectRegistry<T> original )
356 {
357 // Fill the byName and OidRegistry maps, the type has already be copied
358 for ( String key : ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.keySet() )
359 {
360 // Clone each SchemaObject
361 T value = ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.get( key );
362
363 if ( value instanceof LoadableSchemaObject )
364 {
365 // Update the data structure.
366 // Comparators, Normalizers and SyntaxCheckers aren't copied,
367 // they are immutable
368 byName.put( key, value );
369
370 // Update the OidRegistry
371 oidRegistry.put( value );
372 }
373 else
374 {
375 T copiedValue = null;
376
377 // Copy the value if it's not already in the oidRegistry
378 if ( oidRegistry.contains( value.getOid() ) )
379 {
380 try
381 {
382 copiedValue = ( T ) oidRegistry.getSchemaObject( value.getOid() );
383 }
384 catch ( LdapException ne )
385 {
386 // Can't happen
387 }
388 }
389 else
390 {
391 copiedValue = ( T ) value.copy();
392 }
393
394 // Update the data structure.
395 byName.put( key, copiedValue );
396
397 // Update the OidRegistry
398 oidRegistry.put( copiedValue );
399 }
400 }
401
402 return this;
403 }
404
405
406 /**
407 * {@inheritDoc}
408 */
409 public SchemaObject get( String oid )
410 {
411 try
412 {
413 return oidRegistry.getSchemaObject( oid );
414 }
415 catch ( LdapException ne )
416 {
417 return null;
418 }
419 }
420
421
422 /**
423 * {@inheritDoc}
424 */
425 public SchemaObjectType getType()
426 {
427 return schemaObjectType;
428 }
429
430
431 /**
432 * {@inheritDoc}
433 */
434 public int size()
435 {
436 return oidRegistry.size();
437 }
438
439
440 /**
441 * @see Object#toString()
442 */
443 public String toString()
444 {
445 StringBuilder sb = new StringBuilder();
446
447 sb.append( schemaObjectType ).append( ": " );
448 boolean isFirst = true;
449
450 for ( String name : byName.keySet() )
451 {
452 if ( isFirst )
453 {
454 isFirst = false;
455 }
456 else
457 {
458 sb.append( ", " );
459 }
460
461 T schemaObject = byName.get( name );
462
463 sb.append( '<' ).append( name ).append( ", " ).append( schemaObject.getOid() ).append( '>' );
464 }
465
466 return sb.toString();
467 }
468
469
470 /**
471 * {@inheritDoc}
472 */
473 public void clear()
474 {
475 // Clear all the schemaObjects
476 for ( SchemaObject schemaObject : oidRegistry )
477 {
478 // Don't clear LoadableSchemaObject
479 if ( !( schemaObject instanceof LoadableSchemaObject ) )
480 {
481 schemaObject.clear();
482 }
483 }
484
485 // Remove the byName elements
486 byName.clear();
487
488 // Clear the OidRegistry
489 oidRegistry.clear();
490 }
491 }