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