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  package org.apache.directory.server.core.schema;
20  
21  
22  import org.apache.directory.server.constants.ApacheSchemaConstants;
23  import org.apache.directory.server.constants.ServerDNConstants;
24  import org.apache.directory.server.core.entry.DefaultServerAttribute;
25  import org.apache.directory.server.core.entry.DefaultServerEntry;
26  import org.apache.directory.server.core.entry.ServerAttribute;
27  import org.apache.directory.server.core.entry.ServerEntry;
28  import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
29  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
30  import org.apache.directory.server.schema.registries.Registries;
31  import org.apache.directory.shared.ldap.constants.SchemaConstants;
32  import org.apache.directory.shared.ldap.entry.EntryAttribute;
33  import org.apache.directory.shared.ldap.name.LdapDN;
34  import org.apache.directory.shared.ldap.schema.AttributeType;
35  import org.apache.directory.shared.ldap.schema.DITContentRule;
36  import org.apache.directory.shared.ldap.schema.DITStructureRule;
37  import org.apache.directory.shared.ldap.schema.MatchingRule;
38  import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
39  import org.apache.directory.shared.ldap.schema.NameForm;
40  import org.apache.directory.shared.ldap.schema.ObjectClass;
41  import org.apache.directory.shared.ldap.schema.SchemaUtils;
42  import org.apache.directory.shared.ldap.schema.Syntax;
43  import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
44  import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
45  import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
46  
47  import javax.naming.NamingException;
48  import java.util.HashSet;
49  import java.util.Iterator;
50  import java.util.Set;
51  
52  
53  /**
54   * Document me!
55   *
56   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
57   * @version $Rev$, $Date$
58   */
59  public class SchemaService
60  {
61      private static final String[] EMPTY_STRING_ARRAY = new String[0];
62      private static final String SCHEMA_TIMESTAMP_ENTRY_DN = "cn=schemaModifications,ou=schema";
63  
64  
65      /** cached version of the schema subentry with all attributes in it */
66      private ServerEntry schemaSubentry;
67      private final Object lock = new Object();
68  
69      /** a handle on the registries */
70      private Registries registries;
71  
72      /** a handle on the schema partition */
73      private JdbmPartition schemaPartition;
74  
75      /** schema operation control */
76      private SchemaOperationControl schemaControl;
77  
78      /**
79       * the normalized name for the schema modification attributes
80       */
81      private LdapDN schemaModificationAttributesDN;
82  
83  
84  
85      public SchemaService( Registries registries, JdbmPartition schemaPartition, SchemaOperationControl schemaControl ) throws NamingException
86      {
87          this.registries = registries;
88          this.schemaPartition = schemaPartition;
89          this.schemaControl = schemaControl;
90  
91          schemaModificationAttributesDN = new LdapDN( SCHEMA_TIMESTAMP_ENTRY_DN );
92          schemaModificationAttributesDN.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
93      }
94  
95  
96      public boolean isSchemaSubentry( String dnString ) throws NamingException
97      {
98          if ( dnString.equalsIgnoreCase( ServerDNConstants.CN_SCHEMA_DN ) ||
99               dnString.equalsIgnoreCase( ServerDNConstants.CN_SCHEMA_DN_NORMALIZED ) )
100         {
101             return true;
102         }
103 
104         LdapDN dn = new LdapDN( dnString ).normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
105         return dn.getNormName().equals( ServerDNConstants.CN_SCHEMA_DN_NORMALIZED );
106     }
107 
108 
109     public Registries getRegistries()
110     {
111         return registries;
112     }
113 
114 
115     private ServerAttribute generateComparators() throws NamingException
116     {
117         ServerAttribute attr = new DefaultServerAttribute( 
118             registries.getAttributeTypeRegistry().lookup( SchemaConstants.COMPARATORS_AT ) );
119 
120         Iterator<ComparatorDescription> list = registries.getComparatorRegistry().comparatorDescriptionIterator();
121         
122         while ( list.hasNext() )
123         {
124             ComparatorDescription description = list.next();
125             attr.add( SchemaUtils.render( description ).toString() );
126         }
127 
128         return attr;
129     }
130 
131 
132     private ServerAttribute generateNormalizers() throws NamingException
133     {
134         ServerAttribute attr = new DefaultServerAttribute( 
135             registries.getAttributeTypeRegistry().lookup( SchemaConstants.NORMALIZERS_AT ) );
136 
137         Iterator<NormalizerDescription> list = registries.getNormalizerRegistry().normalizerDescriptionIterator();
138 
139         while ( list.hasNext() )
140         {
141             NormalizerDescription normalizer = list.next();
142             attr.add( SchemaUtils.render( normalizer ).toString() );
143         }
144         
145         return attr;
146     }
147 
148 
149     private ServerAttribute generateSyntaxCheckers() throws NamingException
150     {
151         ServerAttribute attr = new DefaultServerAttribute( 
152             registries.getAttributeTypeRegistry().lookup( SchemaConstants.SYNTAX_CHECKERS_AT ) );
153 
154         Iterator<SyntaxCheckerDescription> list =
155             registries.getSyntaxCheckerRegistry().syntaxCheckerDescriptionIterator();
156 
157         while ( list.hasNext() )
158         {
159             SyntaxCheckerDescription syntaxCheckerDescription = list.next();
160             attr.add( SchemaUtils.render( syntaxCheckerDescription ).toString() );
161         }
162         
163         return attr;
164     }
165 
166 
167     private ServerAttribute generateObjectClasses() throws NamingException
168     {
169         ServerAttribute attr = new DefaultServerAttribute( 
170             registries.getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASSES_AT ) );
171 
172         Iterator<ObjectClass> list = registries.getObjectClassRegistry().iterator();
173 
174         while ( list.hasNext() )
175         {
176             ObjectClass oc = list.next();
177             attr.add( SchemaUtils.render( oc ).toString() );
178         }
179         
180         return attr;
181     }
182 
183 
184     private ServerAttribute generateAttributeTypes() throws NamingException
185     {
186         ServerAttribute attr = new DefaultServerAttribute( 
187             registries.getAttributeTypeRegistry().lookup( SchemaConstants.ATTRIBUTE_TYPES_AT ) );
188 
189         Iterator<AttributeType> list = registries.getAttributeTypeRegistry().iterator();
190 
191         while ( list.hasNext() )
192         {
193             AttributeType at = list.next();
194             attr.add( SchemaUtils.render( at ).toString() );
195         }
196 
197         return attr;
198     }
199 
200 
201     private ServerAttribute generateMatchingRules() throws NamingException
202     {
203         ServerAttribute attr = new DefaultServerAttribute( 
204             registries.getAttributeTypeRegistry().lookup( SchemaConstants.MATCHING_RULES_AT ) );
205 
206         Iterator<MatchingRule> list = registries.getMatchingRuleRegistry().iterator();
207 
208         while ( list.hasNext() )
209         {
210             MatchingRule mr = list.next();
211             attr.add( SchemaUtils.render( mr ).toString() );
212         }
213 
214         return attr;
215     }
216 
217 
218     private ServerAttribute generateMatchingRuleUses() throws NamingException
219     {
220         ServerAttribute attr = new DefaultServerAttribute( 
221             registries.getAttributeTypeRegistry().lookup( SchemaConstants.MATCHING_RULE_USE_AT ) );
222 
223         Iterator<MatchingRuleUse> list = registries.getMatchingRuleUseRegistry().iterator();
224 
225         while ( list.hasNext() )
226         {
227             MatchingRuleUse mru = list.next();
228             attr.add( SchemaUtils.render( mru ).toString() );
229         }
230 
231         return attr;
232     }
233 
234 
235     private ServerAttribute generateSyntaxes() throws NamingException
236     {
237         ServerAttribute attr = new DefaultServerAttribute( 
238             registries.getAttributeTypeRegistry().lookup( SchemaConstants.LDAP_SYNTAXES_AT ) );
239 
240         Iterator<Syntax> list = registries.getSyntaxRegistry().iterator();
241 
242         while ( list.hasNext() )
243         {
244             Syntax syntax = list.next();
245             attr.add( SchemaUtils.render( syntax ).toString() );
246         }
247 
248         return attr;
249     }
250 
251 
252     private ServerAttribute generateDitContextRules() throws NamingException
253     {
254         ServerAttribute attr = new DefaultServerAttribute( 
255             registries.getAttributeTypeRegistry().lookup( SchemaConstants.DIT_CONTENT_RULES_AT ) );
256 
257         Iterator<DITContentRule> list = registries.getDitContentRuleRegistry().iterator();
258 
259         while ( list.hasNext() )
260         {
261             DITContentRule dcr = list.next();
262             attr.add( SchemaUtils.render( dcr ).toString() );
263         }
264         
265         return attr;
266     }
267 
268 
269     private ServerAttribute generateDitStructureRules() throws NamingException
270     {
271         ServerAttribute attr = new DefaultServerAttribute( 
272             registries.getAttributeTypeRegistry().lookup( SchemaConstants.DIT_STRUCTURE_RULES_AT ) );
273 
274         Iterator<DITStructureRule> list = registries.getDitStructureRuleRegistry().iterator();
275 
276         while ( list.hasNext() )
277         {
278             DITStructureRule dsr = list.next();
279             attr.add( SchemaUtils.render( dsr ).toString() );
280         }
281         
282         return attr;
283     }
284 
285 
286     private ServerAttribute generateNameForms() throws NamingException
287     {
288         ServerAttribute attr = new DefaultServerAttribute( 
289             registries.getAttributeTypeRegistry().lookup( SchemaConstants.NAME_FORMS_AT ) );
290 
291         Iterator<NameForm> list = registries.getNameFormRegistry().iterator();
292 
293         while ( list.hasNext() )
294         {
295             NameForm nf = list.next();
296             attr.add( SchemaUtils.render( nf ).toString() );
297         }
298         
299         return attr;
300     }
301 
302 
303     private void generateSchemaSubentry( ServerEntry mods ) throws NamingException
304     {
305         ServerEntry attrs = new DefaultServerEntry( registries, mods.getDn() );
306 
307         // add the objectClass attribute
308         attrs.put( SchemaConstants.OBJECT_CLASS_AT, 
309             SchemaConstants.TOP_OC,
310             SchemaConstants.SUBSCHEMA_OC,
311             SchemaConstants.SUBENTRY_OC,
312             ApacheSchemaConstants.APACHE_SUBSCHEMA_OC
313             );
314 
315         // add the cn attribute as required for the RDN
316         attrs.put( SchemaConstants.CN_AT, "schema" );
317 
318         // generate all the other operational attributes
319         attrs.put( generateComparators() );
320         attrs.put( generateNormalizers() );
321         attrs.put( generateSyntaxCheckers() );
322         attrs.put( generateObjectClasses() );
323         attrs.put( generateAttributeTypes() );
324         attrs.put( generateMatchingRules() );
325         attrs.put( generateMatchingRuleUses() );
326         attrs.put( generateSyntaxes() );
327         attrs.put( generateDitContextRules() );
328         attrs.put( generateDitStructureRules() );
329         attrs.put( generateNameForms() );
330         attrs.put( SchemaConstants.SUBTREE_SPECIFICATION_AT, "{}" );
331 
332         // -------------------------------------------------------------------
333         // set standard operational attributes for the subentry
334         // -------------------------------------------------------------------
335 
336         // Add the createTimestamp
337         AttributeType createTimestampAT = registries.
338             getAttributeTypeRegistry().lookup( SchemaConstants.CREATE_TIMESTAMP_AT );
339         EntryAttribute createTimestamp = mods.get( createTimestampAT );
340         attrs.put( SchemaConstants.CREATE_TIMESTAMP_AT, createTimestamp.get() );
341 
342         // Add the creatorsName
343         attrs.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
344 
345         // Add the modifyTimestamp
346         AttributeType schemaModifyTimestampAT = registries.
347             getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT );
348         EntryAttribute schemaModifyTimestamp = mods.get( schemaModifyTimestampAT );
349         attrs.put( SchemaConstants.MODIFY_TIMESTAMP_AT, schemaModifyTimestamp.get() );
350 
351         // Add the modifiersName
352         AttributeType schemaModifiersNameAT = registries.
353             getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );
354         EntryAttribute schemaModifiersName = mods.get( schemaModifiersNameAT );
355         attrs.put( SchemaConstants.MODIFIERS_NAME_AT, schemaModifiersName.get() );
356 
357         // don't swap out if a request for the subentry is in progress or we
358         // can give back an inconsistent schema back to the client so we block
359         synchronized ( lock )
360         {
361             schemaSubentry = attrs;
362         }
363     }
364 
365 
366     private void addAttribute( ServerEntry attrs, String id ) throws NamingException
367     {
368         EntryAttribute attr = schemaSubentry.get( id );
369 
370         if ( attr != null )
371         {
372             attrs.put( attr );
373         }
374     }
375 
376 
377     /**
378      * A seriously unsafe (unsynchronized) means to access the schemaSubentry.
379      *
380      * @return the schemaSubentry
381      * @throws NamingException if there is a failure to access schema timestamps
382      */
383     public ServerEntry getSubschemaEntryImmutable() throws Exception
384     {
385         if ( schemaSubentry == null )
386         {
387             generateSchemaSubentry( schemaPartition.lookup(
388                     new LookupOperationContext( null, schemaModificationAttributesDN ) ) );
389         }
390 
391         return ( ServerEntry ) schemaSubentry.clone();
392     }
393 
394 
395     /**
396      * A seriously unsafe (unsynchronized) means to access the schemaSubentry.
397      *
398      * @return the schemaSubentry
399      * @throws NamingException if there is a failure to access schema timestamps
400      */
401     public ServerEntry getSubschemaEntryCloned() throws Exception
402     {
403         if ( schemaSubentry == null )
404         {
405             generateSchemaSubentry( schemaPartition.lookup(
406                     new LookupOperationContext( null, schemaModificationAttributesDN ) ) );
407         }
408 
409         return ( ServerEntry ) schemaSubentry.clone();
410     }
411 
412 
413     /**
414      * Gets the schemaSubentry based on specific search id parameters which
415      * include the special '*' and '+' operators.
416      *
417      * @param ids the ids of the attributes that should be returned from a search
418      * @return the subschema entry with the ids provided
419      * @throws NamingException if there are failures during schema info access
420      */
421     public ServerEntry getSubschemaEntry( String[] ids ) throws Exception
422     {
423         if ( ids == null )
424         {
425             ids = EMPTY_STRING_ARRAY;
426         }
427 
428         Set<String> setOids = new HashSet<String>();
429         ServerEntry attrs = new DefaultServerEntry( registries, LdapDN.EMPTY_LDAPDN );
430         boolean returnAllOperationalAttributes = false;
431 
432         synchronized( lock )
433         {
434             // ---------------------------------------------------------------
435             // Check if we need an update by looking at timestamps on disk
436             // ---------------------------------------------------------------
437 
438             ServerEntry mods = 
439                 schemaPartition.lookup( new LookupOperationContext( null, schemaModificationAttributesDN ) );
440 // @todo enable this optimization at some point but for now it
441 // is causing some problems so I will just turn it off
442 //          Attribute modifyTimeDisk = mods.get( SchemaConstants.MODIFY_TIMESTAMP_AT );
443 //
444 //          Attribute modifyTimeMemory = null;
445 //
446 //            if ( schemaSubentry != null )
447 //            {
448 //                modifyTimeMemory = schemaSubentry.get( SchemaConstants.MODIFY_TIMESTAMP_AT );
449 //                if ( modifyTimeDisk == null && modifyTimeMemory == null )
450 //                {
451 //                    // do nothing!
452 //                }
453 //                else if ( modifyTimeDisk != null && modifyTimeMemory != null )
454 //                {
455 //                    Date disk = DateUtils.getDate( ( String ) modifyTimeDisk.get() );
456 //                    Date mem = DateUtils.getDate( ( String ) modifyTimeMemory.get() );
457 //                    if ( disk.after( mem ) )
458 //                    {
459 //                        generateSchemaSubentry( mods );
460 //                    }
461 //                }
462 //                else
463 //                {
464 //                    generateSchemaSubentry( mods );
465 //                }
466 //            }
467 //            else
468 //            {
469                 generateSchemaSubentry( mods );
470 //            }
471 
472             // ---------------------------------------------------------------
473             // Prep Work: Transform the attributes to their OID counterpart
474             // ---------------------------------------------------------------
475 
476             for ( String id:ids )
477             {
478                 // Check whether the set contains a plus, and use it below to include all
479                 // operational attributes.  Due to RFC 3673, and issue DIREVE-228 in JIRA
480                 if ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( id ) )
481                 {
482                     returnAllOperationalAttributes = true;
483                 }
484                 else if ( SchemaConstants.ALL_USER_ATTRIBUTES.equals(  id ) )
485                 {
486                     setOids.add( id );
487                 }
488                 else
489                 {
490                     setOids.add( registries.getOidRegistry().getOid( id ) );
491                 }
492             }
493 
494             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.COMPARATORS_AT_OID ) )
495             {
496                 addAttribute( attrs, SchemaConstants.COMPARATORS_AT );
497             }
498 
499             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NORMALIZERS_AT_OID ) )
500             {
501                 addAttribute( attrs, SchemaConstants.NORMALIZERS_AT );
502             }
503 
504             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SYNTAX_CHECKERS_AT_OID ) )
505             {
506                 addAttribute( attrs, SchemaConstants.SYNTAX_CHECKERS_AT );
507             }
508 
509             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.OBJECT_CLASSES_AT_OID ) )
510             {
511                 addAttribute( attrs, SchemaConstants.OBJECT_CLASSES_AT );
512             }
513 
514             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.ATTRIBUTE_TYPES_AT_OID ) )
515             {
516                 addAttribute( attrs, SchemaConstants.ATTRIBUTE_TYPES_AT );
517             }
518 
519             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULES_AT_OID ) )
520             {
521                 addAttribute( attrs, SchemaConstants.MATCHING_RULES_AT );
522             }
523 
524             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULE_USE_AT_OID ) )
525             {
526                 addAttribute( attrs, SchemaConstants.MATCHING_RULE_USE_AT );
527             }
528 
529             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.LDAP_SYNTAXES_AT_OID ) )
530             {
531                 addAttribute( attrs, SchemaConstants.LDAP_SYNTAXES_AT );
532             }
533 
534             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_CONTENT_RULES_AT_OID ) )
535             {
536                 addAttribute( attrs, SchemaConstants.DIT_CONTENT_RULES_AT );
537             }
538 
539             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_STRUCTURE_RULES_AT_OID ) )
540             {
541                 addAttribute( attrs, SchemaConstants.DIT_STRUCTURE_RULES_AT );
542             }
543 
544             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NAME_FORMS_AT_OID ) )
545             {
546                 addAttribute( attrs, SchemaConstants.NAME_FORMS_AT );
547             }
548 
549             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SUBTREE_SPECIFICATION_AT_OID ) )
550             {
551                 addAttribute( attrs, SchemaConstants.SUBTREE_SPECIFICATION_AT );
552             }
553 
554             int minSetSize = 0;
555             if ( setOids.contains( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
556             {
557                 minSetSize++;
558             }
559 
560             if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) )
561             {
562                 minSetSize++;
563             }
564 
565             if ( setOids.contains( SchemaConstants.REF_AT_OID ) )
566             {
567                 minSetSize++;
568             }
569 
570             // add the objectClass attribute
571             if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
572                  setOids.contains( SchemaConstants.OBJECT_CLASS_AT_OID ) ||
573                  setOids.size() == minSetSize )
574             {
575                 addAttribute( attrs, SchemaConstants.OBJECT_CLASS_AT );
576             }
577 
578             // add the cn attribute as required for the RDN
579             if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
580                  setOids.contains( SchemaConstants.CN_AT_OID ) ||
581                  setOids.size() == minSetSize )
582             {
583                 addAttribute( attrs, SchemaConstants.CN_AT );
584             }
585 
586             // -------------------------------------------------------------------
587             // set standard operational attributes for the subentry
588             // -------------------------------------------------------------------
589 
590 
591             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATE_TIMESTAMP_AT_OID ) )
592             {
593                 addAttribute( attrs, SchemaConstants.CREATE_TIMESTAMP_AT );
594             }
595 
596             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATORS_NAME_AT_OID ) )
597             {
598                 addAttribute( attrs, SchemaConstants.CREATORS_NAME_AT );
599             }
600 
601             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFY_TIMESTAMP_AT_OID ) )
602             {
603                 addAttribute( attrs, SchemaConstants.MODIFY_TIMESTAMP_AT );
604             }
605 
606             if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFIERS_NAME_AT_OID ) )
607             {
608                 addAttribute( attrs, SchemaConstants.MODIFIERS_NAME_AT );
609             }
610         }
611 
612         return attrs;
613     }
614 
615 
616     SchemaOperationControl getSchemaControl()
617     {
618         return schemaControl;
619     }
620 }