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    }