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