001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core.schema;
021    
022    
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.HashSet;
026    import java.util.List;
027    import java.util.Map;
028    import java.util.Set;
029    
030    import org.apache.directory.server.core.entry.ClonedServerEntry;
031    import org.apache.directory.server.core.filtering.EntryFilteringCursor;
032    import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
033    import org.apache.directory.server.core.interceptor.context.ListOperationContext;
034    import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
035    import org.apache.directory.server.core.partition.Partition;
036    import org.apache.directory.server.i18n.I18n;
037    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
038    import org.apache.directory.shared.ldap.constants.SchemaConstants;
039    import org.apache.directory.shared.ldap.entry.Entry;
040    import org.apache.directory.shared.ldap.entry.EntryAttribute;
041    import org.apache.directory.shared.ldap.entry.ServerEntry;
042    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
043    import org.apache.directory.shared.ldap.name.DN;
044    import org.apache.directory.shared.ldap.schema.AttributeType;
045    import org.apache.directory.shared.ldap.schema.SchemaManager;
046    import org.apache.directory.shared.ldap.schema.parsers.LdapComparatorDescription;
047    import org.apache.directory.shared.ldap.schema.parsers.NormalizerDescription;
048    import org.apache.directory.shared.ldap.schema.parsers.SyntaxCheckerDescription;
049    import org.apache.directory.shared.ldap.schema.registries.AbstractSchemaLoader;
050    import org.apache.directory.shared.ldap.schema.registries.Registries;
051    import org.apache.directory.shared.ldap.schema.registries.Schema;
052    import org.apache.directory.shared.ldap.util.Base64;
053    import org.slf4j.Logger;
054    import org.slf4j.LoggerFactory;
055    
056    
057    /**
058     * A class that loads schemas from a partition.
059     *
060     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
061     * @version $Rev$
062     */
063    public class PartitionSchemaLoader extends AbstractSchemaLoader
064    {
065        /** static class logger */
066        private static final Logger LOG = LoggerFactory.getLogger( PartitionSchemaLoader.class );
067    
068        private final SchemaPartitionDao dao;
069        private Partition partition;
070    
071        /** The attributeType registry */
072        private SchemaManager schemaManager;
073    
074        private final AttributeType mOidAT;
075        private final AttributeType mNameAT;
076        private final AttributeType cnAT;
077        private final AttributeType byteCodeAT;
078        private final AttributeType descAT;
079        private final AttributeType fqcnAT;
080    
081        private static Map<String, DN> staticAttributeTypeDNs = new HashMap<String, DN>();
082        private static Map<String, DN> staticMatchingRulesDNs = new HashMap<String, DN>();
083        private static Map<String, DN> staticObjectClassesDNs = new HashMap<String, DN>();
084        private static Map<String, DN> staticComparatorsDNs = new HashMap<String, DN>();
085        private static Map<String, DN> staticNormalizersDNs = new HashMap<String, DN>();
086        private static Map<String, DN> staticSyntaxCheckersDNs = new HashMap<String, DN>();
087        private static Map<String, DN> staticSyntaxesDNs = new HashMap<String, DN>();
088    
089    
090        public PartitionSchemaLoader( Partition partition, SchemaManager schemaManager ) throws Exception
091        {
092            this.partition = partition;
093            this.schemaManager = schemaManager;
094    
095            dao = new SchemaPartitionDaoImpl( this.partition, schemaManager );
096            mOidAT = schemaManager.lookupAttributeTypeRegistry( MetaSchemaConstants.M_OID_AT );
097            mNameAT = schemaManager.lookupAttributeTypeRegistry( MetaSchemaConstants.M_NAME_AT );
098            cnAT = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.CN_AT );
099            byteCodeAT = schemaManager.lookupAttributeTypeRegistry( MetaSchemaConstants.M_BYTECODE_AT );
100            descAT = schemaManager.lookupAttributeTypeRegistry( MetaSchemaConstants.M_DESCRIPTION_AT );
101            fqcnAT = schemaManager.lookupAttributeTypeRegistry( MetaSchemaConstants.M_FQCN_AT );
102    
103            initStaticDNs( "system" );
104            initStaticDNs( "core" );
105            initStaticDNs( "apache" );
106            initStaticDNs( "apachemeta" );
107            initStaticDNs( MetaSchemaConstants.SCHEMA_OTHER );
108            initStaticDNs( "collective" );
109            initStaticDNs( "java" );
110            initStaticDNs( "cosine" );
111            initStaticDNs( "inetorgperson" );
112        }
113    
114    
115        private void initStaticDNs( String schemaName ) throws Exception
116        {
117    
118            // Initialize AttributeType Dns
119            DN dn = new DN( SchemaConstants.ATTRIBUTES_TYPE_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
120    
121            dn.normalize( schemaManager.getNormalizerMapping() );
122            staticAttributeTypeDNs.put( schemaName, dn );
123    
124            // Initialize ObjectClasses Dns
125            dn = new DN( SchemaConstants.OBJECT_CLASSES_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
126    
127            dn.normalize( schemaManager.getNormalizerMapping() );
128            staticObjectClassesDNs.put( schemaName, dn );
129    
130            // Initialize MatchingRules Dns
131            dn = new DN( SchemaConstants.MATCHING_RULES_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
132    
133            dn.normalize( schemaManager.getNormalizerMapping() );
134            staticMatchingRulesDNs.put( schemaName, dn );
135    
136            // Initialize Comparators Dns
137            dn = new DN( SchemaConstants.COMPARATORS_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
138    
139            dn.normalize( schemaManager.getNormalizerMapping() );
140            staticComparatorsDNs.put( schemaName, dn );
141    
142            // Initialize Normalizers Dns
143            dn = new DN( SchemaConstants.NORMALIZERS_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
144    
145            dn.normalize( schemaManager.getNormalizerMapping() );
146            staticNormalizersDNs.put( schemaName, dn );
147    
148            // Initialize SyntaxCheckers Dns
149            dn = new DN( SchemaConstants.SYNTAX_CHECKERS_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
150    
151            dn.normalize( schemaManager.getNormalizerMapping() );
152            staticSyntaxCheckersDNs.put( schemaName, dn );
153    
154            // Initialize Syntaxes Dns
155            dn = new DN( SchemaConstants.SYNTAXES_PATH, "cn=" + schemaName, SchemaConstants.OU_SCHEMA );
156    
157            dn.normalize( schemaManager.getNormalizerMapping() );
158            staticSyntaxesDNs.put( schemaName, dn );
159    
160        }
161    
162    
163        /**
164         * Helper class used to update the static DNs for each kind of Schema Object
165         */
166        private DN updateDNs( Map<String, DN> staticDNs, String path, Schema schema ) throws LdapInvalidDnException
167        {
168            DN dn = staticDNs.get( schema.getSchemaName() );
169    
170            if ( dn == null )
171            {
172                dn = new DN( path, "cn=" + schema.getSchemaName(), SchemaConstants.OU_SCHEMA );
173    
174                dn.normalize( schemaManager.getNormalizerMapping() );
175                staticDNs.put( schema.getSchemaName(), dn );
176            }
177    
178            return dn;
179        }
180    
181    
182        /**
183         * Lists the names of the schemas that depend on the schema name provided.
184         * 
185         * @param schemaName the name of the schema to find dependents for
186         * @return a set of schemas (String names) that depend on the schema
187         * @throws Exception if there are problems searching the schema partition
188         */
189        public Set<String> listDependentSchemaNames( String schemaName ) throws Exception
190        {
191            Set<String> dependees = new HashSet<String>();
192            Set<ServerEntry> results = dao.listSchemaDependents( schemaName );
193    
194            if ( results.isEmpty() )
195            {
196                return dependees;
197            }
198    
199            for ( ServerEntry sr : results )
200            {
201                EntryAttribute cn = sr.get( cnAT );
202                dependees.add( cn.getString() );
203            }
204    
205            return dependees;
206        }
207    
208    
209        /**
210         * Lists the names of the enabled schemas that depend on the schema name 
211         * provided.
212         * 
213         * @param schemaName the name of the schema to find dependents for
214         * @return a set of enabled schemas (String names) that depend on the schema
215         * @throws Exception if there are problems searching the schema partition
216         */
217        public Set<String> listEnabledDependentSchemaNames( String schemaName ) throws Exception
218        {
219            Set<String> dependees = new HashSet<String>();
220            Set<ServerEntry> results = dao.listEnabledSchemaDependents( schemaName );
221    
222            if ( results.isEmpty() )
223            {
224                return dependees;
225            }
226    
227            for ( ServerEntry sr : results )
228            {
229                EntryAttribute cn = sr.get( cnAT );
230                dependees.add( cn.getString() );
231            }
232    
233            return dependees;
234        }
235    
236    
237        public Map<String, Schema> getSchemas() throws Exception
238        {
239            return dao.getSchemas();
240        }
241    
242    
243        public Set<String> getSchemaNames() throws Exception
244        {
245            return dao.getSchemaNames();
246        }
247    
248    
249        public Schema getSchema( String schemaName )
250        {
251            try
252            {
253                return dao.getSchema( schemaName );
254            }
255            catch ( Exception e )
256            {
257                // TODO fixme
258                return null;
259            }
260        }
261    
262    
263        /**
264         * {@inheritDoc}
265         */
266        public final void load( Schema schema, Registries targetRegistries, boolean isDepLoad ) throws Exception
267        {
268            // if we're loading a dependency and it has not been enabled on 
269            // disk then enable it on disk before we proceed to load it
270            if ( schema.isDisabled() && isDepLoad )
271            {
272                dao.enableSchema( schema.getSchemaName() );
273            }
274    
275            if ( targetRegistries.isSchemaLoaded( schema.getSchemaName() ) )
276            {
277                LOG.debug( "schema {} already seems to be loaded", schema.getSchemaName() );
278                return;
279            }
280    
281            LOG.debug( "loading {} schema ...", schema.getSchemaName() );
282    
283            loadComparators( schema );
284            loadNormalizers( schema );
285            loadSyntaxCheckers( schema );
286            loadSyntaxes( schema );
287            loadMatchingRules( schema );
288            loadAttributeTypes( schema );
289            loadObjectClasses( schema );
290            loadMatchingRuleUses( schema );
291            loadDitContentRules( schema );
292            loadNameForms( schema );
293    
294            // order does matter here so some special trickery is needed
295            // we cannot load a DSR before the DSRs it depends on are loaded?
296            // TODO need to confirm this ( or we must make the class for this and use deferred 
297            // resolution until everything is available?
298    
299            loadDitStructureRules( schema );
300    
301            notifyListenerOrRegistries( schema, targetRegistries );
302        }
303    
304    
305        /**
306         * {@inheritDoc}
307         */
308        public List<Entry> loadAttributeTypes( Schema... schemas ) throws Exception
309        {
310            List<Entry> attributeTypeList = new ArrayList<Entry>();
311    
312            for ( Schema schema : schemas )
313            {
314                DN dn = updateDNs( staticAttributeTypeDNs, SchemaConstants.ATTRIBUTES_TYPE_PATH, schema );
315    
316                // Check that we don't have an entry in the Dit for this schema
317                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
318                {
319                    // No : get out, no AttributeType to load
320                    return attributeTypeList;
321                }
322    
323                LOG.debug( "{} schema: loading attributeTypes", schema.getSchemaName() );
324    
325                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
326    
327                // Loop on all the AttributeTypes and add them to the list
328                while ( list.next() )
329                {
330                    ServerEntry result = list.get();
331    
332                    attributeTypeList.add( result );
333                }
334            }
335    
336            return attributeTypeList;
337        }
338    
339    
340        /**
341         * {@inheritDoc}
342         */
343        public List<Entry> loadComparators( Schema... schemas ) throws Exception
344        {
345            List<Entry> comparatorList = new ArrayList<Entry>();
346    
347            if ( schemas == null )
348            {
349                return comparatorList;
350            }
351    
352            for ( Schema schema : schemas )
353            {
354                DN dn = updateDNs( staticComparatorsDNs, SchemaConstants.COMPARATORS_PATH, schema );
355    
356                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
357                {
358                    return comparatorList;
359                }
360    
361                LOG.debug( "{} schema: loading comparators", schema.getSchemaName() );
362    
363                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
364    
365                while ( list.next() )
366                {
367                    ClonedServerEntry entry = list.get();
368    
369                    comparatorList.add( entry );
370                }
371            }
372    
373            return comparatorList;
374        }
375    
376    
377        /**
378         * {@inheritDoc}
379         */
380        public List<Entry> loadDitContentRules( Schema... schemas ) throws Exception
381        {
382            LOG.error( I18n.err( I18n.ERR_86 ) );
383    
384            return null;
385        }
386    
387    
388        /**
389         * {@inheritDoc}
390         */
391        public List<Entry> loadDitStructureRules( Schema... schemas ) throws Exception
392        {
393            LOG.error( I18n.err( I18n.ERR_87 ) );
394    
395            return null;
396        }
397    
398    
399        /**
400         * {@inheritDoc}
401         */
402        public List<Entry> loadMatchingRules( Schema... schemas ) throws Exception
403        {
404            List<Entry> matchingRuleList = new ArrayList<Entry>();
405    
406            if ( schemas == null )
407            {
408                return matchingRuleList;
409            }
410    
411            for ( Schema schema : schemas )
412            {
413                DN dn = updateDNs( staticMatchingRulesDNs, SchemaConstants.MATCHING_RULES_PATH, schema );
414    
415                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
416                {
417                    return matchingRuleList;
418                }
419    
420                LOG.debug( "{} schema: loading matchingRules", schema.getSchemaName() );
421    
422                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
423    
424                while ( list.next() )
425                {
426                    ServerEntry entry = list.get();
427    
428                    matchingRuleList.add( entry );
429                }
430            }
431    
432            return matchingRuleList;
433        }
434    
435    
436        /**
437         * {@inheritDoc}
438         */
439        public List<Entry> loadMatchingRuleUses( Schema... schemas ) throws Exception
440        {
441            LOG.error( I18n.err( I18n.ERR_88 ) );
442    
443            return null;
444        }
445    
446    
447        /**
448         * {@inheritDoc}
449         */
450        public List<Entry> loadNameForms( Schema... schemas ) throws Exception
451        {
452            LOG.error( I18n.err( I18n.ERR_89 ) );
453    
454            return null;
455        }
456    
457    
458        /**
459         * {@inheritDoc}
460         */
461        public List<Entry> loadNormalizers( Schema... schemas ) throws Exception
462        {
463            List<Entry> normalizerList = new ArrayList<Entry>();
464    
465            if ( schemas == null )
466            {
467                return normalizerList;
468            }
469    
470            for ( Schema schema : schemas )
471            {
472                DN dn = updateDNs( staticNormalizersDNs, SchemaConstants.NORMALIZERS_PATH, schema );
473    
474                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
475                {
476                    return normalizerList;
477                }
478    
479                LOG.debug( "{} schema: loading normalizers", schema.getSchemaName() );
480    
481                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
482    
483                while ( list.next() )
484                {
485                    ClonedServerEntry entry = list.get();
486    
487                    normalizerList.add( entry );
488                }
489            }
490    
491            return normalizerList;
492        }
493    
494    
495        /**
496         * {@inheritDoc}
497         */
498        public List<Entry> loadObjectClasses( Schema... schemas ) throws Exception
499        {
500            List<Entry> objectClassList = new ArrayList<Entry>();
501    
502            if ( schemas == null )
503            {
504                return objectClassList;
505            }
506    
507            for ( Schema schema : schemas )
508            {
509                DN dn = updateDNs( staticObjectClassesDNs, SchemaConstants.OBJECT_CLASSES_PATH, schema );
510    
511                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
512                {
513                    return objectClassList;
514                }
515    
516                LOG.debug( "{} schema: loading objectClasses", schema.getSchemaName() );
517    
518                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
519    
520                while ( list.next() )
521                {
522                    ClonedServerEntry entry = list.get();
523    
524                    objectClassList.add( entry );
525                }
526            }
527    
528            return objectClassList;
529        }
530    
531    
532        /**
533         * {@inheritDoc}
534         */
535        public List<Entry> loadSyntaxes( Schema... schemas ) throws Exception
536        {
537            List<Entry> syntaxList = new ArrayList<Entry>();
538    
539            if ( schemas == null )
540            {
541                return syntaxList;
542            }
543    
544            for ( Schema schema : schemas )
545            {
546                DN dn = updateDNs( staticSyntaxesDNs, SchemaConstants.SYNTAXES_PATH, schema );
547    
548                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
549                {
550                    return syntaxList;
551                }
552    
553                LOG.debug( "{} schema: loading syntaxes", schema.getSchemaName() );
554    
555                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
556    
557                while ( list.next() )
558                {
559                    ServerEntry entry = list.get();
560    
561                    syntaxList.add( entry );
562                }
563            }
564    
565            return syntaxList;
566        }
567    
568    
569        /**
570         * {@inheritDoc}
571         */
572        public List<Entry> loadSyntaxCheckers( Schema... schemas ) throws Exception
573        {
574            List<Entry> syntaxCheckerList = new ArrayList<Entry>();
575    
576            if ( schemas == null )
577            {
578                return syntaxCheckerList;
579            }
580    
581            for ( Schema schema : schemas )
582            {
583                DN dn = updateDNs( staticSyntaxCheckersDNs, SchemaConstants.SYNTAX_CHECKERS_PATH, schema );
584    
585                if ( !partition.hasEntry( new EntryOperationContext( null, dn ) ) )
586                {
587                    return syntaxCheckerList;
588                }
589    
590                LOG.debug( "{} schema: loading syntaxCsheckers", schema.getSchemaName() );
591    
592                EntryFilteringCursor list = partition.list( new ListOperationContext( null, dn ) );
593    
594                while ( list.next() )
595                {
596                    ServerEntry entry = list.get();
597    
598                    syntaxCheckerList.add( entry );
599                }
600            }
601    
602            return syntaxCheckerList;
603        }
604    
605    
606        private String getOid( ServerEntry entry ) throws Exception
607        {
608            EntryAttribute oid = entry.get( mOidAT );
609    
610            if ( oid == null )
611            {
612                return null;
613            }
614    
615            return oid.getString();
616        }
617    
618    
619        private NormalizerDescription getNormalizerDescription( String schemaName, ServerEntry entry ) throws Exception
620        {
621            NormalizerDescription description = new NormalizerDescription( getOid( entry ) );
622            List<String> values = new ArrayList<String>();
623            values.add( schemaName );
624            description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
625            description.setFqcn( entry.get( fqcnAT ).getString() );
626    
627            EntryAttribute desc = entry.get( descAT );
628            if ( desc != null && desc.size() > 0 )
629            {
630                description.setDescription( desc.getString() );
631            }
632    
633            EntryAttribute bytecode = entry.get( byteCodeAT );
634    
635            if ( bytecode != null && bytecode.size() > 0 )
636            {
637                byte[] bytes = bytecode.getBytes();
638                description.setBytecode( new String( Base64.encode( bytes ) ) );
639            }
640    
641            return description;
642        }
643    
644    
645        private ClonedServerEntry lookupPartition( DN dn ) throws Exception
646        {
647            return partition.lookup( new LookupOperationContext( null, dn ) );
648        }
649    
650    
651        private LdapComparatorDescription getLdapComparatorDescription( String schemaName, ServerEntry entry )
652            throws Exception
653        {
654            LdapComparatorDescription description = new LdapComparatorDescription( getOid( entry ) );
655            List<String> values = new ArrayList<String>();
656            values.add( schemaName );
657            description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
658            description.setFqcn( entry.get( fqcnAT ).getString() );
659    
660            EntryAttribute desc = entry.get( descAT );
661    
662            if ( desc != null && desc.size() > 0 )
663            {
664                description.setDescription( desc.getString() );
665            }
666    
667            EntryAttribute bytecode = entry.get( byteCodeAT );
668    
669            if ( bytecode != null && bytecode.size() > 0 )
670            {
671                byte[] bytes = bytecode.getBytes();
672                description.setBytecode( new String( Base64.encode( bytes ) ) );
673            }
674    
675            return description;
676        }
677    
678    
679        private SyntaxCheckerDescription getSyntaxCheckerDescription( String schemaName, ServerEntry entry )
680            throws Exception
681        {
682            SyntaxCheckerDescription description = new SyntaxCheckerDescription( getOid( entry ) );
683            List<String> values = new ArrayList<String>();
684            values.add( schemaName );
685            description.addExtension( MetaSchemaConstants.X_SCHEMA, values );
686            description.setFqcn( entry.get( fqcnAT ).getString() );
687    
688            EntryAttribute desc = entry.get( descAT );
689    
690            if ( desc != null && desc.size() > 0 )
691            {
692                description.setDescription( desc.getString() );
693            }
694    
695            EntryAttribute bytecode = entry.get( byteCodeAT );
696    
697            if ( bytecode != null && bytecode.size() > 0 )
698            {
699                byte[] bytes = bytecode.getBytes();
700                description.setBytecode( new String( Base64.encode( bytes ) ) );
701            }
702    
703            return description;
704        }
705    
706    
707        /**
708         * @return the dao
709         */
710        public SchemaPartitionDao getDao()
711        {
712            return dao;
713        }
714    }