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