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.loader.ldif;
021    
022    
023    import java.lang.reflect.Constructor;
024    import java.lang.reflect.Method;
025    import java.util.ArrayList;
026    import java.util.HashSet;
027    import java.util.List;
028    import java.util.Set;
029    
030    import org.apache.directory.shared.asn1.primitives.OID;
031    import org.apache.directory.shared.i18n.I18n;
032    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
033    import org.apache.directory.shared.ldap.constants.SchemaConstants;
034    import org.apache.directory.shared.ldap.entry.Entry;
035    import org.apache.directory.shared.ldap.entry.EntryAttribute;
036    import org.apache.directory.shared.ldap.entry.Value;
037    import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
038    import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
039    import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
040    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
041    import org.apache.directory.shared.ldap.schema.AttributeType;
042    import org.apache.directory.shared.ldap.schema.EntityFactory;
043    import org.apache.directory.shared.ldap.schema.LdapComparator;
044    import org.apache.directory.shared.ldap.schema.LdapSyntax;
045    import org.apache.directory.shared.ldap.schema.LoadableSchemaObject;
046    import org.apache.directory.shared.ldap.schema.MatchingRule;
047    import org.apache.directory.shared.ldap.schema.Normalizer;
048    import org.apache.directory.shared.ldap.schema.ObjectClass;
049    import org.apache.directory.shared.ldap.schema.ObjectClassTypeEnum;
050    import org.apache.directory.shared.ldap.schema.SchemaManager;
051    import org.apache.directory.shared.ldap.schema.SchemaObject;
052    import org.apache.directory.shared.ldap.schema.SyntaxChecker;
053    import org.apache.directory.shared.ldap.schema.UsageEnum;
054    import org.apache.directory.shared.ldap.schema.parsers.LdapComparatorDescription;
055    import org.apache.directory.shared.ldap.schema.parsers.NormalizerDescription;
056    import org.apache.directory.shared.ldap.schema.parsers.SyntaxCheckerDescription;
057    import org.apache.directory.shared.ldap.schema.registries.DefaultSchema;
058    import org.apache.directory.shared.ldap.schema.registries.Registries;
059    import org.apache.directory.shared.ldap.schema.registries.Schema;
060    import org.apache.directory.shared.ldap.util.Base64;
061    import org.apache.directory.shared.ldap.util.StringTools;
062    import org.slf4j.Logger;
063    import org.slf4j.LoggerFactory;
064    
065    
066    /**
067     * Showing how it's done ...
068     *
069     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
070     * @version $Rev$
071     */
072    public class SchemaEntityFactory implements EntityFactory
073    {
074        /** Slf4j logger */
075        private final static Logger LOG = LoggerFactory.getLogger( SchemaEntityFactory.class );
076    
077        /** for fast debug checks */
078        private static final boolean IS_DEBUG = LOG.isDebugEnabled();
079    
080        /** Used for looking up the setRegistries(Registries) method */
081        private final static Class<?>[] parameterTypes = new Class[]
082            { Registries.class };
083    
084        private static final List<String> EMPTY_LIST = new ArrayList<String>();
085        private static final String[] EMPTY_ARRAY = new String[]
086            {};
087    
088        /** A special ClassLoader that loads a class from the bytecode attribute */
089        private final AttributeClassLoader classLoader;
090    
091    
092        public SchemaEntityFactory() throws Exception
093        {
094            this.classLoader = new AttributeClassLoader();
095        }
096    
097    
098        /**
099         * Get an OID from an entry. Handles the bad cases (null OID, 
100         * not a valid OID, ...)
101         */
102        private String getOid( Entry entry, String objectType ) throws LdapInvalidAttributeValueException
103        {
104            // The OID
105            EntryAttribute mOid = entry.get( MetaSchemaConstants.M_OID_AT );
106    
107            if ( mOid == null )
108            {
109                String msg = I18n.err( I18n.ERR_10005, objectType, MetaSchemaConstants.M_OID_AT );
110                LOG.warn( msg );
111                throw new NullPointerException( msg );
112            }
113    
114            String oid = mOid.getString();
115    
116            if ( !OID.isOID( oid ) )
117            {
118                String msg = I18n.err( I18n.ERR_10006, oid );
119                LOG.warn( msg );
120                throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
121            }
122    
123            return oid;
124        }
125    
126    
127        /**
128         * Get an OID from an entry. Handles the bad cases (null OID, 
129         * not a valid OID, ...)
130         */
131        private String getOid( SchemaObject description, String objectType ) throws LdapInvalidAttributeValueException
132        {
133            // The OID
134            String oid = description.getOid();
135    
136            if ( oid == null )
137            {
138                String msg = I18n.err( I18n.ERR_10005, objectType, MetaSchemaConstants.M_OID_AT );
139                LOG.warn( msg );
140                throw new NullPointerException( msg );
141            }
142    
143            if ( !OID.isOID( oid ) )
144            {
145                String msg = I18n.err( I18n.ERR_10006, oid );
146                LOG.warn( msg );
147                throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
148            }
149    
150            return oid;
151        }
152    
153    
154        /**
155         * Check that the Entry is not null
156         */
157        private void checkEntry( Entry entry, String schemaEntity )
158        {
159            if ( entry == null )
160            {
161                String msg = I18n.err( I18n.ERR_10007, schemaEntity );
162                LOG.warn( msg );
163                throw new NullPointerException( msg );
164            }
165        }
166    
167    
168        /**
169         * Check that the Description is not null
170         */
171        private void checkDescription( SchemaObject description, String schemaEntity )
172        {
173            if ( description == null )
174            {
175                String msg = I18n.err( I18n.ERR_10008, schemaEntity );
176                LOG.warn( msg );
177                throw new NullPointerException( msg );
178            }
179        }
180    
181    
182        /**
183         * Get the schema from its name. Return the Other reference if there
184         * is no schema name. Throws a NPE if the schema is not loaded.
185         */
186        private Schema getSchema( String schemaName, Registries registries )
187        {
188            if ( StringTools.isEmpty( schemaName ) )
189            {
190                schemaName = MetaSchemaConstants.SCHEMA_OTHER;
191            }
192    
193            Schema schema = registries.getLoadedSchema( schemaName );
194    
195            if ( schema == null )
196            {
197                String msg = I18n.err( I18n.ERR_10009, schemaName );
198                LOG.error( msg );
199            }
200    
201            return schema;
202        }
203    
204    
205        /**
206         * {@inheritDoc}
207         */
208        public Schema getSchema( Entry entry ) throws Exception
209        {
210            String name;
211            String owner;
212            String[] dependencies = EMPTY_ARRAY;
213            boolean isDisabled = false;
214    
215            if ( entry == null )
216            {
217                throw new NullPointerException( I18n.err( I18n.ERR_10010 ) );
218            }
219    
220            if ( entry.get( SchemaConstants.CN_AT ) == null )
221            {
222                throw new NullPointerException( I18n.err( I18n.ERR_10011 ) );
223            }
224    
225            name = entry.get( SchemaConstants.CN_AT ).getString();
226    
227            if ( entry.get( SchemaConstants.CREATORS_NAME_AT ) == null )
228            {
229                throw new NullPointerException( I18n.err( I18n.ERR_10012, SchemaConstants.CREATORS_NAME_AT ) );
230            }
231    
232            owner = entry.get( SchemaConstants.CREATORS_NAME_AT ).getString();
233    
234            if ( entry.get( MetaSchemaConstants.M_DISABLED_AT ) != null )
235            {
236                String value = entry.get( MetaSchemaConstants.M_DISABLED_AT ).getString();
237                value = value.toUpperCase();
238                isDisabled = value.equals( "TRUE" );
239            }
240    
241            if ( entry.get( MetaSchemaConstants.M_DEPENDENCIES_AT ) != null )
242            {
243                Set<String> depsSet = new HashSet<String>();
244                EntryAttribute depsAttr = entry.get( MetaSchemaConstants.M_DEPENDENCIES_AT );
245    
246                for ( Value<?> value : depsAttr )
247                {
248                    depsSet.add( value.getString() );
249                }
250    
251                dependencies = depsSet.toArray( EMPTY_ARRAY );
252            }
253    
254            return new DefaultSchema( name, owner, dependencies, isDisabled );
255        }
256    
257    
258        /**
259         * Class load a syntaxChecker instance
260         */
261        private SyntaxChecker classLoadSyntaxChecker( SchemaManager schemaManager, String oid, String className,
262            EntryAttribute byteCode, Registries targetRegistries ) throws Exception
263        {
264            // Try to class load the syntaxChecker
265            Class<?> clazz = null;
266            SyntaxChecker syntaxChecker = null;
267            String byteCodeStr = StringTools.EMPTY;
268    
269            if ( byteCode == null )
270            {
271                clazz = Class.forName( className );
272            }
273            else
274            {
275                classLoader.setAttribute( byteCode );
276                clazz = classLoader.loadClass( className );
277                byteCodeStr = new String( Base64.encode( byteCode.getBytes() ) );
278            }
279    
280            // Create the syntaxChecker instance
281            syntaxChecker = ( SyntaxChecker ) clazz.newInstance();
282    
283            // Update the common fields
284            syntaxChecker.setBytecode( byteCodeStr );
285            syntaxChecker.setFqcn( className );
286    
287            // Inject the new OID, as the loaded syntaxChecker might have its own
288            syntaxChecker.setOid( oid );
289    
290            return syntaxChecker;
291        }
292    
293    
294        /**
295         * {@inheritDoc}
296         */
297        public SyntaxChecker getSyntaxChecker( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
298            String schemaName ) throws Exception
299        {
300            checkEntry( entry, SchemaConstants.SYNTAX_CHECKER );
301    
302            // The SyntaxChecker OID
303            String oid = getOid( entry, SchemaConstants.SYNTAX_CHECKER );
304    
305            // Get the schema
306            if ( !schemaManager.isSchemaLoaded( schemaName ) )
307            {
308                // The schema is not loaded. We can't create the requested Normalizer
309                String msg = I18n.err( I18n.ERR_10013, entry.getDn().getName(), schemaName );
310                LOG.warn( msg );
311                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
312            }
313    
314            Schema schema = getSchema( schemaName, targetRegistries );
315    
316            if ( schema == null )
317            {
318                // The schema is disabled. We still have to update the backend
319                String msg = I18n.err( I18n.ERR_10014, entry.getDn().getName(), schemaName );
320                LOG.info( msg );
321                schema = schemaManager.getLoadedSchema( schemaName );
322            }
323    
324            // The FQCN
325            String className = getFqcn( entry, SchemaConstants.SYNTAX_CHECKER );
326    
327            // The ByteCode
328            EntryAttribute byteCode = entry.get( MetaSchemaConstants.M_BYTECODE_AT );
329    
330            // Class load the syntaxChecker
331            SyntaxChecker syntaxChecker = classLoadSyntaxChecker( schemaManager, oid, className, byteCode, targetRegistries );
332    
333            // Update the common fields
334            setSchemaObjectProperties( syntaxChecker, entry, schema );
335    
336            // return the resulting syntaxChecker
337            return syntaxChecker;
338        }
339    
340    
341        /**
342         * {@inheritDoc}
343         */
344        public SyntaxChecker getSyntaxChecker( SchemaManager schemaManager,
345            SyntaxCheckerDescription syntaxCheckerDescription, Registries targetRegistries, String schemaName )
346            throws Exception
347        {
348            checkDescription( syntaxCheckerDescription, SchemaConstants.SYNTAX_CHECKER );
349    
350            // The Comparator OID
351            String oid = getOid( syntaxCheckerDescription, SchemaConstants.SYNTAX_CHECKER );
352    
353            // Get the schema
354            Schema schema = getSchema( schemaName, targetRegistries );
355    
356            if ( schema == null )
357            {
358                // The schema is not loaded. We can't create the requested SyntaxChecker
359                String msg = I18n.err( I18n.ERR_10013, syntaxCheckerDescription.getName(), schemaName );
360                LOG.warn( msg );
361                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
362            }
363    
364            // The FQCN
365            String fqcn = getFqcn( syntaxCheckerDescription, SchemaConstants.SYNTAX_CHECKER );
366    
367            // get the byteCode
368            EntryAttribute byteCode = getByteCode( syntaxCheckerDescription, SchemaConstants.SYNTAX_CHECKER );
369    
370            // Class load the SyntaxChecker
371            SyntaxChecker syntaxChecker = classLoadSyntaxChecker( schemaManager, oid, fqcn, byteCode, targetRegistries );
372    
373            // Update the common fields
374            setSchemaObjectProperties( syntaxChecker, syntaxCheckerDescription, schema );
375    
376            return syntaxChecker;
377        }
378    
379    
380        /**
381         * Class load a comparator instances
382         */
383        private LdapComparator<?> classLoadComparator( SchemaManager schemaManager, String oid, String className,
384            EntryAttribute byteCode, Registries targetRegistries ) throws Exception
385        {
386            // Try to class load the comparator
387            LdapComparator<?> comparator = null;
388            Class<?> clazz = null;
389            String byteCodeStr = StringTools.EMPTY;
390    
391            if ( byteCode == null )
392            {
393                clazz = Class.forName( className );
394            }
395            else
396            {
397                classLoader.setAttribute( byteCode );
398                clazz = classLoader.loadClass( className );
399                byteCodeStr = new String( Base64.encode( byteCode.getBytes() ) );
400            }
401    
402            // Create the comparator instance. Either we have a no argument constructor,
403            // or we have one which takes an OID. Lets try the one with an OID argument first
404            try
405            {
406                Constructor<?> constructor = clazz.getConstructor( new Class[]
407                    { String.class } );
408                comparator = ( LdapComparator<?> ) constructor.newInstance( new Object[]
409                    { oid } );
410            }
411            catch ( NoSuchMethodException nsme )
412            {
413                // Ok, let's try with the constructor without argument.
414                // In this case, we will have to check that the OID is the same than
415                // the one we got in the Comparator entry
416                Constructor<?> constructor = clazz.getConstructor();
417                comparator = ( LdapComparator<?> ) clazz.newInstance();
418                
419                if ( !comparator.getOid().equals( oid ) )
420                {
421                    String msg = I18n.err( I18n.ERR_10015, oid, comparator.getOid() );
422                    throw new LdapInvalidAttributeValueException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
423                }
424            }
425    
426            // Update the loadable fields
427            comparator.setBytecode( byteCodeStr );
428            comparator.setFqcn( className );
429    
430            // Inject the SchemaManager for the comparator who needs it
431            comparator.setSchemaManager( schemaManager );
432    
433            return comparator;
434        }
435    
436    
437        /**
438         * {@inheritDoc}
439         */
440        public LdapComparator<?> getLdapComparator( SchemaManager schemaManager,
441            LdapComparatorDescription comparatorDescription, Registries targetRegistries, String schemaName )
442            throws Exception
443        {
444            checkDescription( comparatorDescription, SchemaConstants.COMPARATOR );
445    
446            // The Comparator OID
447            String oid = getOid( comparatorDescription, SchemaConstants.COMPARATOR );
448    
449            // Get the schema
450            Schema schema = getSchema( schemaName, targetRegistries );
451    
452            if ( schema == null )
453            {
454                // The schema is not loaded. We can't create the requested Comparator
455                String msg = I18n.err( I18n.ERR_10016, comparatorDescription.getName(), schemaName );
456                LOG.warn( msg );
457                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
458            }
459    
460            // The FQCN
461            String fqcn = getFqcn( comparatorDescription, SchemaConstants.COMPARATOR );
462    
463            // get the byteCode
464            EntryAttribute byteCode = getByteCode( comparatorDescription, SchemaConstants.COMPARATOR );
465    
466            // Class load the comparator
467            LdapComparator<?> comparator = classLoadComparator( schemaManager, oid, fqcn, byteCode, targetRegistries );
468    
469            // Update the common fields
470            setSchemaObjectProperties( comparator, comparatorDescription, schema );
471    
472            return comparator;
473        }
474    
475    
476        /**
477         * {@inheritDoc}
478         */
479        public LdapComparator<?> getLdapComparator( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
480            String schemaName ) throws Exception
481        {
482            checkEntry( entry, SchemaConstants.COMPARATOR );
483    
484            // The Comparator OID
485            String oid = getOid( entry, SchemaConstants.COMPARATOR );
486    
487            // Get the schema
488            if ( !schemaManager.isSchemaLoaded( schemaName ) )
489            {
490                // The schema is not loaded. We can't create the requested Comparator
491                String msg = I18n.err( I18n.ERR_10016, entry.getDn().getName(), schemaName );
492                LOG.warn( msg );
493                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
494            }
495    
496            Schema schema = getSchema( schemaName, targetRegistries );
497    
498            if ( schema == null )
499            {
500                // The schema is disabled. We still have to update the backend
501                String msg = I18n.err( I18n.ERR_10017, entry.getDn().getName(), schemaName );
502                LOG.info( msg );
503                schema = schemaManager.getLoadedSchema( schemaName );
504            }
505    
506            // The FQCN
507            String fqcn = getFqcn( entry, SchemaConstants.COMPARATOR );
508    
509            // The ByteCode
510            EntryAttribute byteCode = entry.get( MetaSchemaConstants.M_BYTECODE_AT );
511    
512            // Class load the comparator
513            LdapComparator<?> comparator = classLoadComparator( schemaManager, oid, fqcn, byteCode, targetRegistries );
514    
515            // Update the common fields
516            setSchemaObjectProperties( comparator, entry, schema );
517    
518            // return the resulting comparator
519            return comparator;
520        }
521    
522    
523        /**
524         * Class load a normalizer instances
525         */
526        private Normalizer classLoadNormalizer( SchemaManager schemaManager, String oid, String className,
527            EntryAttribute byteCode, Registries targetRegistries ) throws Exception
528        {
529            // Try to class load the normalizer
530            Class<?> clazz = null;
531            Normalizer normalizer = null;
532            String byteCodeStr = StringTools.EMPTY;
533    
534            if ( byteCode == null )
535            {
536                clazz = Class.forName( className );
537            }
538            else
539            {
540                classLoader.setAttribute( byteCode );
541                clazz = classLoader.loadClass( className );
542                byteCodeStr = new String( Base64.encode( byteCode.getBytes() ) );
543            }
544    
545            // Create the normalizer instance
546            normalizer = ( Normalizer ) clazz.newInstance();
547            
548            // Update the common fields
549            normalizer.setBytecode( byteCodeStr );
550            normalizer.setFqcn( className );
551    
552            // Inject the new OID, as the loaded normalizer might have its own
553            normalizer.setOid( oid );
554    
555            // Inject the SchemaManager for the normalizer who needs it
556            normalizer.setSchemaManager( schemaManager );
557    
558            return normalizer;
559        }
560    
561    
562        /**
563         * {@inheritDoc}
564         */
565        public Normalizer getNormalizer( SchemaManager schemaManager, NormalizerDescription normalizerDescription,
566            Registries targetRegistries, String schemaName ) throws Exception
567        {
568            checkDescription( normalizerDescription, SchemaConstants.NORMALIZER );
569    
570            // The Comparator OID
571            String oid = getOid( normalizerDescription, SchemaConstants.NORMALIZER );
572    
573            // Get the schema
574            Schema schema = getSchema( schemaName, targetRegistries );
575    
576            if ( schema == null )
577            {
578                // The schema is not loaded. We can't create the requested Normalizer
579                String msg = I18n.err( I18n.ERR_10018, normalizerDescription.getName(), schemaName );
580                LOG.warn( msg );
581                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
582            }
583    
584            // The FQCN
585            String fqcn = getFqcn( normalizerDescription, SchemaConstants.NORMALIZER );
586    
587            // get the byteCode
588            EntryAttribute byteCode = getByteCode( normalizerDescription, SchemaConstants.NORMALIZER );
589    
590            // Class load the normalizer
591            Normalizer normalizer = classLoadNormalizer( schemaManager, oid, fqcn, byteCode, targetRegistries );
592    
593            // Update the common fields
594            setSchemaObjectProperties( normalizer, normalizerDescription, schema );
595    
596            return normalizer;
597        }
598    
599    
600        /**
601         * {@inheritDoc}
602         */
603        public Normalizer getNormalizer( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
604            String schemaName ) throws Exception
605        {
606            checkEntry( entry, SchemaConstants.NORMALIZER );
607    
608            // The Normalizer OID
609            String oid = getOid( entry, SchemaConstants.NORMALIZER );
610    
611            // Get the schema
612            if ( !schemaManager.isSchemaLoaded( schemaName ) )
613            {
614                // The schema is not loaded. We can't create the requested Normalizer
615                String msg = I18n.err( I18n.ERR_10018, entry.getDn().getName(), schemaName );
616                LOG.warn( msg );
617                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
618            }
619    
620            Schema schema = getSchema( schemaName, targetRegistries );
621    
622            if ( schema == null )
623            {
624                // The schema is disabled. We still have to update the backend
625                String msg = I18n.err( I18n.ERR_10019, entry.getDn().getName(), schemaName );
626                LOG.info( msg );
627                schema = schemaManager.getLoadedSchema( schemaName );
628            }
629    
630            // The FQCN
631            String className = getFqcn( entry, SchemaConstants.NORMALIZER );
632    
633            // The ByteCode
634            EntryAttribute byteCode = entry.get( MetaSchemaConstants.M_BYTECODE_AT );
635    
636            // Class load the Normalizer
637            Normalizer normalizer = classLoadNormalizer( schemaManager, oid, className, byteCode, targetRegistries );
638    
639            // Update the common fields
640            setSchemaObjectProperties( normalizer, entry, schema );
641    
642            // return the resulting Normalizer
643            return normalizer;
644        }
645    
646    
647        /**
648         * Uses reflection to see if a setRegistries( Registries ) method exists on the
649         * object's class.  If so then the registries are dependency injected into the 
650         * new schema object.
651         * 
652         * @param obj a schema object to have a Registries dependency injected.
653         */
654        private void injectRegistries( Object obj, Registries targetRegistries ) throws Exception
655        {
656            Method method = null;
657    
658            try
659            {
660                method = obj.getClass().getMethod( "setRegistries", parameterTypes );
661            }
662            catch ( NoSuchMethodException e )
663            {
664                if ( IS_DEBUG )
665                {
666                    LOG.debug( obj.getClass() + " has no setRegistries() method." );
667                }
668    
669                return;
670            }
671    
672            if ( method == null )
673            {
674                return;
675            }
676    
677            Object[] args = new Object[]
678                { targetRegistries };
679            method.invoke( obj, args );
680        }
681    
682    
683        /**
684         * {@inheritDoc}
685         * @throws LdapInvalidAttributeValueException 
686         * @throws LdapUnwillingToPerformException 
687         */
688        public LdapSyntax getSyntax( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
689            String schemaName ) throws LdapInvalidAttributeValueException, LdapUnwillingToPerformException
690        {
691            checkEntry( entry, SchemaConstants.SYNTAX );
692    
693            // The Syntax OID
694            String oid = getOid( entry, SchemaConstants.SYNTAX );
695    
696            // Get the schema
697            if ( !schemaManager.isSchemaLoaded( schemaName ) )
698            {
699                // The schema is not loaded. We can't create the requested Syntax
700                String msg = I18n.err( I18n.ERR_10020, entry.getDn().getName(), schemaName );
701                LOG.warn( msg );
702                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
703            }
704    
705            Schema schema = getSchema( schemaName, targetRegistries );
706    
707            if ( schema == null )
708            {
709                // The schema is disabled. We still have to update the backend
710                String msg = I18n.err( I18n.ERR_10021, entry.getDn().getName(), schemaName );
711                LOG.info( msg );
712                schema = schemaManager.getLoadedSchema( schemaName );
713            }
714    
715            // Create the new LdapSyntax instance
716            LdapSyntax syntax = new LdapSyntax( oid );
717    
718            // The isHumanReadable field
719            EntryAttribute mHumanReadable = entry.get( MetaSchemaConstants.X_HUMAN_READABLE_AT );
720    
721            if ( mHumanReadable != null )
722            {
723                String val = mHumanReadable.getString();
724                syntax.setHumanReadable( val.toUpperCase().equals( "TRUE" ) );
725            }
726    
727            // Common properties
728            setSchemaObjectProperties( syntax, entry, schema );
729    
730            return syntax;
731        }
732    
733    
734        /**
735         * {@inheritDoc}
736         * @throws LdapUnwillingToPerformException 
737         * @throws LdapInvalidAttributeValueException 
738         */
739        public MatchingRule getMatchingRule( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
740            String schemaName ) throws LdapUnwillingToPerformException, LdapInvalidAttributeValueException
741        {
742            checkEntry( entry, SchemaConstants.MATCHING_RULE );
743    
744            // The MatchingRule OID
745            String oid = getOid( entry, SchemaConstants.MATCHING_RULE );
746    
747            // Get the schema
748            if ( !schemaManager.isSchemaLoaded( schemaName ) )
749            {
750                // The schema is not loaded. We can't create the requested MatchingRule
751                String msg = I18n.err( I18n.ERR_10022, entry.getDn().getName(), schemaName );
752                LOG.warn( msg );
753                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
754            }
755    
756            Schema schema = getSchema( schemaName, targetRegistries );
757    
758            if ( schema == null )
759            {
760                // The schema is disabled. We still have to update the backend
761                String msg = I18n.err( I18n.ERR_10023, entry.getDn().getName(), schemaName );
762                LOG.info( msg );
763                schema = schemaManager.getLoadedSchema( schemaName );
764            }
765    
766            MatchingRule matchingRule = new MatchingRule( oid );
767    
768            // The syntax field
769            EntryAttribute mSyntax = entry.get( MetaSchemaConstants.M_SYNTAX_AT );
770    
771            if ( mSyntax != null )
772            {
773                matchingRule.setSyntaxOid( mSyntax.getString() );
774            }
775    
776            // The normalizer and comparator fields will be updated when we will
777            // apply the registry 
778    
779            // Common properties
780            setSchemaObjectProperties( matchingRule, entry, schema );
781    
782            return matchingRule;
783        }
784    
785    
786        /**
787         * Create a list of string from a multivalued attribute's values
788         */
789        private List<String> getStrings( EntryAttribute attr )
790        {
791            if ( attr == null )
792            {
793                return EMPTY_LIST;
794            }
795    
796            List<String> strings = new ArrayList<String>( attr.size() );
797    
798            for ( Value<?> value : attr )
799            {
800                strings.add( value.getString() );
801            }
802    
803            return strings;
804        }
805    
806    
807        /**
808         * {@inheritDoc}
809         */
810        public ObjectClass getObjectClass( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
811            String schemaName ) throws Exception
812        {
813            checkEntry( entry, SchemaConstants.OBJECT_CLASS );
814    
815            // The ObjectClass OID
816            String oid = getOid( entry, SchemaConstants.OBJECT_CLASS );
817    
818            // Get the schema
819            if ( !schemaManager.isSchemaLoaded( schemaName ) )
820            {
821                // The schema is not loaded. We can't create the requested ObjectClass
822                String msg = I18n.err( I18n.ERR_10024, entry.getDn().getName(), schemaName );
823                LOG.warn( msg );
824                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
825            }
826    
827            Schema schema = getSchema( schemaName, targetRegistries );
828    
829            if ( schema == null )
830            {
831                // The schema is disabled. We still have to update the backend
832                String msg = I18n.err( I18n.ERR_10025, entry.getDn().getName(), schemaName );
833                LOG.info( msg );
834                schema = schemaManager.getLoadedSchema( schemaName );
835            }
836    
837            // Create the ObjectClass instance
838            ObjectClass oc = new ObjectClass( oid );
839    
840            // The Sup field
841            EntryAttribute mSuperiors = entry.get( MetaSchemaConstants.M_SUP_OBJECT_CLASS_AT );
842    
843            if ( mSuperiors != null )
844            {
845                oc.setSuperiorOids( getStrings( mSuperiors ) );
846            }
847    
848            // The May field
849            EntryAttribute mMay = entry.get( MetaSchemaConstants.M_MAY_AT );
850    
851            if ( mMay != null )
852            {
853                oc.setMayAttributeTypeOids( getStrings( mMay ) );
854            }
855    
856            // The Must field
857            EntryAttribute mMust = entry.get( MetaSchemaConstants.M_MUST_AT );
858    
859            if ( mMust != null )
860            {
861                oc.setMustAttributeTypeOids( getStrings( mMust ) );
862            }
863    
864            // The objectClassType field
865            EntryAttribute mTypeObjectClass = entry.get( MetaSchemaConstants.M_TYPE_OBJECT_CLASS_AT );
866    
867            if ( mTypeObjectClass != null )
868            {
869                String type = mTypeObjectClass.getString();
870                oc.setType( ObjectClassTypeEnum.getClassType( type ) );
871            }
872    
873            // Common properties
874            setSchemaObjectProperties( oc, entry, schema );
875    
876            return oc;
877        }
878    
879    
880        /**
881         * {@inheritDoc}
882         * @throws LdapInvalidAttributeValueException 
883         * @throws LdapUnwillingToPerformException 
884         */
885        public AttributeType getAttributeType( SchemaManager schemaManager, Entry entry, Registries targetRegistries,
886            String schemaName ) throws LdapInvalidAttributeValueException, LdapUnwillingToPerformException
887        {
888            checkEntry( entry, SchemaConstants.ATTRIBUTE_TYPE );
889    
890            // The AttributeType OID
891            String oid = getOid( entry, SchemaConstants.ATTRIBUTE_TYPE );
892    
893            // Get the schema
894            if ( !schemaManager.isSchemaLoaded( schemaName ) )
895            {
896                // The schema is not loaded, this is an error
897                String msg = I18n.err( I18n.ERR_10026, entry.getDn().getName(),  schemaName );
898                LOG.warn( msg );
899                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
900            }
901    
902            Schema schema = getSchema( schemaName, targetRegistries );
903    
904            if ( schema == null )
905            {
906                // The schema is disabled. We still have to update the backend
907                String msg = I18n.err( I18n.ERR_10027, entry.getDn().getName(), schemaName );
908                LOG.info( msg );
909                schema = schemaManager.getLoadedSchema( schemaName );
910            }
911    
912            // Create the new AttributeType
913            AttributeType attributeType = new AttributeType( oid );
914    
915            // Syntax
916            EntryAttribute mSyntax = entry.get( MetaSchemaConstants.M_SYNTAX_AT );
917    
918            if ( ( mSyntax != null ) && ( mSyntax.get() != null ) )
919            {
920                attributeType.setSyntaxOid( mSyntax.getString() );
921            }
922    
923            // Syntax Length
924            EntryAttribute mSyntaxLength = entry.get( MetaSchemaConstants.M_LENGTH_AT );
925    
926            if ( mSyntaxLength != null )
927            {
928                attributeType.setSyntaxLength( Integer.parseInt( mSyntaxLength.getString() ) );
929            }
930    
931            // Equality
932            EntryAttribute mEquality = entry.get( MetaSchemaConstants.M_EQUALITY_AT );
933    
934            if ( mEquality != null )
935            {
936                attributeType.setEqualityOid( mEquality.getString() );
937            }
938    
939            // Ordering
940            EntryAttribute mOrdering = entry.get( MetaSchemaConstants.M_ORDERING_AT );
941    
942            if ( mOrdering != null )
943            {
944                attributeType.setOrderingOid( mOrdering.getString() );
945            }
946    
947            // Substr
948            EntryAttribute mSubstr = entry.get( MetaSchemaConstants.M_SUBSTR_AT );
949    
950            if ( mSubstr != null )
951            {
952                attributeType.setSubstringOid( mSubstr.getString() );
953            }
954    
955            EntryAttribute mSupAttributeType = entry.get( MetaSchemaConstants.M_SUP_ATTRIBUTE_TYPE_AT );
956    
957            // Sup
958            if ( mSupAttributeType != null )
959            {
960                attributeType.setSuperiorOid( mSupAttributeType.getString() );
961            }
962    
963            // isCollective
964            EntryAttribute mCollective = entry.get( MetaSchemaConstants.M_COLLECTIVE_AT );
965    
966            if ( mCollective != null )
967            {
968                String val = mCollective.getString();
969                attributeType.setCollective( val.equalsIgnoreCase( "TRUE" ) );
970            }
971    
972            // isSingleValued
973            EntryAttribute mSingleValued = entry.get( MetaSchemaConstants.M_SINGLE_VALUE_AT );
974    
975            if ( mSingleValued != null )
976            {
977                String val = mSingleValued.getString();
978                attributeType.setSingleValued( val.equalsIgnoreCase( "TRUE" ) );
979            }
980    
981            // isReadOnly
982            EntryAttribute mNoUserModification = entry.get( MetaSchemaConstants.M_NO_USER_MODIFICATION_AT );
983    
984            if ( mNoUserModification != null )
985            {
986                String val = mNoUserModification.getString();
987                attributeType.setUserModifiable( !val.equalsIgnoreCase( "TRUE" ) );
988            }
989    
990            // Usage
991            EntryAttribute mUsage = entry.get( MetaSchemaConstants.M_USAGE_AT );
992    
993            if ( mUsage != null )
994            {
995                attributeType.setUsage( UsageEnum.getUsage( mUsage.getString() ) );
996            }
997    
998            // Common properties
999            setSchemaObjectProperties( attributeType, entry, schema );
1000    
1001            return attributeType;
1002        }
1003    
1004    
1005        /**
1006         * Process the FQCN attribute
1007         * @throws LdapInvalidAttributeValueException 
1008         */
1009        private String getFqcn( Entry entry, String objectType ) throws LdapInvalidAttributeValueException
1010        {
1011            // The FQCN
1012            EntryAttribute mFqcn = entry.get( MetaSchemaConstants.M_FQCN_AT );
1013    
1014            if ( mFqcn == null )
1015            {
1016                String msg = I18n.err( I18n.ERR_10028, objectType, MetaSchemaConstants.M_FQCN_AT );
1017                LOG.warn( msg );
1018                throw new NullPointerException( msg );
1019            }
1020    
1021            return mFqcn.getString();
1022        }
1023    
1024    
1025        /**
1026         * Process the FQCN attribute
1027         */
1028        private String getFqcn( LoadableSchemaObject description, String objectType )
1029        {
1030            // The FQCN
1031            String mFqcn = description.getFqcn();
1032    
1033            if ( mFqcn == null )
1034            {
1035                String msg = I18n.err( I18n.ERR_10028, objectType, MetaSchemaConstants.M_FQCN_AT );
1036                LOG.warn( msg );
1037                throw new NullPointerException( msg );
1038            }
1039    
1040            return mFqcn;
1041        }
1042    
1043    
1044        /**
1045         * Process the ByteCode attribute
1046         */
1047        private EntryAttribute getByteCode( Entry entry, String objectType )
1048        {
1049            EntryAttribute byteCode = entry.get( MetaSchemaConstants.M_BYTECODE_AT );
1050    
1051            if ( byteCode == null )
1052            {
1053                String msg = I18n.err( I18n.ERR_10028, objectType, MetaSchemaConstants.M_BYTECODE_AT );
1054                LOG.warn( msg );
1055                throw new NullPointerException( msg );
1056            }
1057    
1058            return byteCode;
1059        }
1060    
1061    
1062        /**
1063         * Process the ByteCode attribute
1064         */
1065        private EntryAttribute getByteCode( LoadableSchemaObject description, String objectType )
1066        {
1067            String byteCodeString = description.getBytecode();
1068    
1069            if ( byteCodeString == null )
1070            {
1071                String msg = I18n.err( I18n.ERR_10028, objectType, MetaSchemaConstants.M_BYTECODE_AT );
1072                LOG.warn( msg );
1073                throw new NullPointerException( msg );
1074            }
1075    
1076            byte[] bytecode = Base64.decode( byteCodeString.toCharArray() );
1077            EntryAttribute attr = new DefaultClientAttribute( MetaSchemaConstants.M_BYTECODE_AT, bytecode );
1078    
1079            return attr;
1080        }
1081    
1082    
1083        /**
1084         * Process the common attributes to all SchemaObjects :
1085         *  - obsolete
1086         *  - description
1087         *  - names
1088         *  - schemaName
1089         *  - specification (if any)
1090         *  - extensions
1091         *  - isReadOnly
1092         *  - isEnabled
1093         * @throws LdapInvalidAttributeValueException 
1094         */
1095        private void setSchemaObjectProperties( SchemaObject schemaObject, Entry entry, Schema schema ) throws LdapInvalidAttributeValueException
1096        {
1097            // The isObsolete field
1098            EntryAttribute mObsolete = entry.get( MetaSchemaConstants.M_OBSOLETE_AT );
1099    
1100            if ( mObsolete != null )
1101            {
1102                String val = mObsolete.getString();
1103                schemaObject.setObsolete( val.equalsIgnoreCase( "TRUE" ) );
1104            }
1105    
1106            // The description field
1107            EntryAttribute mDescription = entry.get( MetaSchemaConstants.M_DESCRIPTION_AT );
1108    
1109            if ( mDescription != null )
1110            {
1111                schemaObject.setDescription( mDescription.getString() );
1112            }
1113    
1114            // The names field
1115            EntryAttribute names = entry.get( MetaSchemaConstants.M_NAME_AT );
1116    
1117            if ( names != null )
1118            {
1119                List<String> values = new ArrayList<String>();
1120    
1121                for ( Value<?> name : names )
1122                {
1123                    values.add( name.getString() );
1124                }
1125    
1126                schemaObject.setNames( values );
1127            }
1128    
1129            // The isEnabled field
1130            EntryAttribute mDisabled = entry.get( MetaSchemaConstants.M_DISABLED_AT );
1131    
1132            // If the SchemaObject has an explicit m-disabled attribute, then use it.
1133            // Otherwise, inherit it from the schema
1134            if ( mDisabled != null )
1135            {
1136                String val = mDisabled.getString();
1137                schemaObject.setEnabled( !val.equalsIgnoreCase( "TRUE" ) );
1138            }
1139            else
1140            {
1141                schemaObject.setEnabled( schema != null && schema.isEnabled() );
1142            }
1143    
1144            // The isReadOnly field
1145            EntryAttribute mIsReadOnly = entry.get( MetaSchemaConstants.M_NO_USER_MODIFICATION_AT );
1146    
1147            if ( mIsReadOnly != null )
1148            {
1149                String val = mIsReadOnly.getString();
1150                schemaObject.setReadOnly( val.equalsIgnoreCase( "TRUE" ) );
1151            }
1152    
1153            // The specification field
1154            /*
1155             * TODO : create the M_SPECIFICATION_AT
1156            EntryAttribute mSpecification = entry.get( MetaSchemaConstants.M_SPECIFICATION_AT );
1157            
1158            if ( mSpecification != null )
1159            {
1160                so.setSpecification( mSpecification.getString() ); 
1161            }
1162            */
1163    
1164            // The schemaName field
1165            schemaObject.setSchemaName( schema.getSchemaName() );
1166    
1167            // The extensions field
1168            /*
1169             * TODO create the M_EXTENSION_AT AT
1170            EntryAttribute extensions = entry.get( MetaSchemaConstants.M_EXTENSION_AT );
1171            
1172            if ( extensions != null )
1173            {
1174                List<String> extensions = new ArrayList<String>();
1175                
1176                for ( Value<?> extension:extensions )
1177                {
1178                    values.add( extension() );
1179                }
1180                
1181                so.setExtensions( values );
1182            }
1183            */
1184        }
1185    
1186    
1187        /**
1188         * Process the common attributes to all SchemaObjects :
1189         *  - obsolete
1190         *  - description
1191         *  - names
1192         *  - schemaName
1193         *  - specification (if any)
1194         *  - extensions
1195         *  - isReadOnly
1196         *  - isEnabled
1197         */
1198        private void setSchemaObjectProperties( SchemaObject schemaObject, SchemaObject description, Schema schema )
1199        {
1200            // The isObsolete field
1201            schemaObject.setObsolete( description.isObsolete() );
1202    
1203            // The description field
1204            schemaObject.setDescription( description.getDescription() );
1205    
1206            // The names field
1207            schemaObject.setNames( description.getNames() );
1208    
1209            // The isEnabled field. Has the description does not hold a 
1210            // Disable field, we will inherit from the schema enable field
1211            schemaObject.setEnabled( schema.isEnabled() );
1212    
1213            // The isReadOnly field. We don't have this data in the description,
1214            // so set it to false
1215            // TODO : should it be a X-READONLY extension ?
1216            schemaObject.setReadOnly( false );
1217    
1218            // The specification field
1219            schemaObject.setSpecification( description.getSpecification() );
1220    
1221            // The schemaName field
1222            schemaObject.setSchemaName( schema.getSchemaName() );
1223    
1224            // The extensions field
1225            schemaObject.setExtensions( description.getExtensions() );
1226        }
1227    }