View Javadoc
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.api.ldap.model.schema.registries;
21  
22  
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.Map;
26  
27  import org.apache.directory.api.asn1.util.Oid;
28  import org.apache.directory.api.i18n.I18n;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
31  import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
32  import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject;
33  import org.apache.directory.api.ldap.model.schema.SchemaObject;
34  import org.apache.directory.api.ldap.model.schema.SchemaObjectType;
35  import org.apache.directory.api.util.Strings;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  
40  /**
41   * Common schema object registry interface.
42   *
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   */
45  public abstract class DefaultSchemaObjectRegistry<T extends SchemaObject> implements SchemaObjectRegistry<T>,
46      Iterable<T>
47  {
48      /** static class logger */
49      private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaObjectRegistry.class );
50  
51      /** A speedup for debug */
52      private static final boolean DEBUG = LOG.isDebugEnabled();
53  
54      /** a map of SchemaObject looked up by name */
55      protected Map<String, T> byName;
56  
57      /** The SchemaObject type, used by the toString() method  */
58      protected SchemaObjectType schemaObjectType;
59  
60      /** the global OID Registry */
61      protected OidRegistry<T> oidRegistry;
62  
63  
64      /**
65       * Creates a new DefaultSchemaObjectRegistry instance.
66       */
67      protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry<T> oidRegistry )
68      {
69          byName = new HashMap<String, T>();
70          this.schemaObjectType = schemaObjectType;
71          this.oidRegistry = oidRegistry;
72      }
73  
74  
75      /**
76       * {@inheritDoc}
77       */
78      public boolean contains( String oid )
79      {
80          if ( !byName.containsKey( oid ) )
81          {
82              return byName.containsKey( Strings.toLowerCase( oid ) );
83          }
84  
85          return true;
86      }
87  
88  
89      /**
90       * {@inheritDoc}
91       */
92      public String getSchemaName( String oid ) throws LdapException
93      {
94          if ( !Oid.isOid( oid ) )
95          {
96              String msg = I18n.err( I18n.ERR_04267 );
97              LOG.warn( msg );
98              throw new LdapException( msg );
99          }
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_NOT_FOUND, 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 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( Strings.trim( Strings.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             LdapSchemaException ldapSchemaException = new LdapSchemaException(
200                 LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, msg );
201             ldapSchemaException.setSourceObject( schemaObject );
202             throw ldapSchemaException;
203         }
204 
205         byName.put( oid, schemaObject );
206 
207         /*
208          * add the aliases/names to the name map along with their toLowerCase
209          * versions of the name: this is used to make sure name lookups work
210          */
211         for ( String name : schemaObject.getNames() )
212         {
213             String lowerName = Strings.trim( Strings.toLowerCase( name ) );
214 
215             if ( byName.containsKey( lowerName ) )
216             {
217                 String msg = I18n.err( I18n.ERR_04271, schemaObjectType.name(), name );
218                 LOG.warn( msg );
219                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
220                     LdapSchemaExceptionCodes.NAME_ALREADY_REGISTERED, msg );
221                 ldapSchemaException.setSourceObject( schemaObject );
222                 throw ldapSchemaException;
223             }
224             else
225             {
226                 byName.put( lowerName, schemaObject );
227             }
228         }
229 
230         // And register the oid -> schemaObject relation
231         oidRegistry.register( schemaObject );
232 
233         if ( LOG.isDebugEnabled() )
234         {
235             LOG.debug( "registered " + schemaObject.getName() + " for OID {}", oid );
236         }
237     }
238 
239 
240     /**
241      * {@inheritDoc}
242      */
243     public T unregister( String numericOid ) throws LdapException
244     {
245         if ( !Oid.isOid( numericOid ) )
246         {
247             String msg = I18n.err( I18n.ERR_04272, numericOid );
248             LOG.error( msg );
249             throw new LdapException( msg );
250         }
251 
252         T schemaObject = byName.remove( numericOid );
253 
254         for ( String name : schemaObject.getNames() )
255         {
256             byName.remove( name );
257         }
258 
259         // And remove the SchemaObject from the oidRegistry
260         oidRegistry.unregister( numericOid );
261 
262         if ( DEBUG )
263         {
264             LOG.debug( "Removed {} with oid {} from the registry", schemaObject, numericOid );
265         }
266 
267         return schemaObject;
268     }
269 
270 
271     /**
272      * {@inheritDoc}
273      */
274     public T unregister( T schemaObject ) throws LdapException
275     {
276         String oid = schemaObject.getOid();
277 
278         if ( !byName.containsKey( oid ) )
279         {
280             String msg = I18n.err( I18n.ERR_04273, schemaObjectType.name(), oid );
281             LOG.warn( msg );
282             throw new LdapException( msg );
283         }
284 
285         // Remove the oid
286         T removed = byName.remove( oid );
287 
288         /*
289          * Remove the aliases/names from the name map along with their toLowerCase
290          * versions of the name.
291          */
292         for ( String name : schemaObject.getNames() )
293         {
294             byName.remove( Strings.trim( Strings.toLowerCase( name ) ) );
295         }
296 
297         // And unregister the oid -> schemaObject relation
298         oidRegistry.unregister( oid );
299 
300         return removed;
301     }
302 
303 
304     /**
305      * {@inheritDoc}
306      */
307     public void unregisterSchemaElements( String schemaName ) throws LdapException
308     {
309         if ( schemaName == null )
310         {
311             return;
312         }
313 
314         // Loop on all the SchemaObjects stored and remove those associated
315         // with the give schemaName
316         for ( T schemaObject : this )
317         {
318             if ( schemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
319             {
320                 String oid = schemaObject.getOid();
321                 SchemaObject removed = unregister( oid );
322 
323                 if ( DEBUG )
324                 {
325                     LOG.debug( "Removed {} with oid {} from the registry", removed, oid );
326                 }
327             }
328         }
329     }
330 
331 
332     /**
333      * {@inheritDoc}
334      */
335     public String getOidByName( String name ) throws LdapException
336     {
337         T schemaObject = byName.get( name );
338 
339         if ( schemaObject == null )
340         {
341             // last resort before giving up check with lower cased version
342             String lowerCased = Strings.toLowerCase( name );
343 
344             schemaObject = byName.get( lowerCased );
345 
346             // ok this name is not for a schema object in the registry
347             if ( schemaObject == null )
348             {
349                 throw new LdapException( I18n.err( I18n.ERR_04274, name ) );
350             }
351         }
352 
353         // we found the schema object by key on the first lookup attempt
354         return schemaObject.getOid();
355     }
356 
357 
358     /**
359      * {@inheritDoc}
360      */
361     // This will suppress PMD.EmptyCatchBlock warnings in this method
362     @SuppressWarnings("unchecked")
363     public SchemaObjectRegistry<T> copy( SchemaObjectRegistry<T> original )
364     {
365         // Fill the byName and OidRegistry maps, the type has already be copied
366         for ( String key : ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.keySet() )
367         {
368             // Clone each SchemaObject
369             T value = ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.get( key );
370 
371             if ( value instanceof LoadableSchemaObject )
372             {
373                 // Update the data structure. 
374                 // Comparators, Normalizers and SyntaxCheckers aren't copied, 
375                 // they are immutable
376                 byName.put( key, value );
377 
378                 // Update the OidRegistry
379                 oidRegistry.put( value );
380             }
381             else
382             {
383                 T copiedValue = null;
384 
385                 // Copy the value if it's not already in the oidRegistry
386                 if ( oidRegistry.contains( value.getOid() ) )
387                 {
388                     try
389                     {
390                         copiedValue = oidRegistry.getSchemaObject( value.getOid() );
391                     }
392                     catch ( LdapException ne )
393                     {
394                         // Can't happen
395                     }
396                 }
397                 else
398                 {
399                     copiedValue = ( T ) value.copy();
400                 }
401 
402                 // Update the data structure. 
403                 byName.put( key, copiedValue );
404 
405                 // Update the OidRegistry
406                 oidRegistry.put( copiedValue );
407             }
408         }
409 
410         return this;
411     }
412 
413 
414     /**
415      * {@inheritDoc}
416      */
417     public T get( String oid )
418     {
419         try
420         {
421             return oidRegistry.getSchemaObject( oid );
422         }
423         catch ( LdapException ne )
424         {
425             return null;
426         }
427     }
428 
429 
430     /**
431      * {@inheritDoc}
432      */
433     public SchemaObjectType getType()
434     {
435         return schemaObjectType;
436     }
437 
438 
439     /**
440      * {@inheritDoc}
441      */
442     public int size()
443     {
444         return oidRegistry.size();
445     }
446 
447 
448     /**
449      * @see Object#toString()
450      */
451     public String toString()
452     {
453         StringBuilder sb = new StringBuilder();
454 
455         sb.append( schemaObjectType ).append( ": " );
456         boolean isFirst = true;
457 
458         for ( String name : byName.keySet() )
459         {
460             if ( isFirst )
461             {
462                 isFirst = false;
463             }
464             else
465             {
466                 sb.append( ", " );
467             }
468 
469             T schemaObject = byName.get( name );
470 
471             sb.append( '<' ).append( name ).append( ", " ).append( schemaObject.getOid() ).append( '>' );
472         }
473 
474         return sb.toString();
475     }
476 
477 
478     /**
479      * {@inheritDoc}
480      */
481     public void clear()
482     {
483         // Clear all the schemaObjects
484         for ( SchemaObject schemaObject : oidRegistry )
485         {
486             // Don't clear LoadableSchemaObject
487             if ( !( schemaObject instanceof LoadableSchemaObject ) )
488             {
489                 schemaObject.clear();
490             }
491         }
492 
493         // Remove the byName elements
494         byName.clear();
495 
496         // Clear the OidRegistry
497         oidRegistry.clear();
498     }
499 }