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