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.shared;
20  
21  
22  import java.io.IOException;
23  
24  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
25  import org.apache.directory.api.ldap.model.entry.Attribute;
26  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
27  import org.apache.directory.api.ldap.model.entry.DefaultEntry;
28  import org.apache.directory.api.ldap.model.entry.Entry;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.exception.LdapOtherException;
31  import org.apache.directory.api.ldap.model.name.Dn;
32  import org.apache.directory.api.ldap.model.schema.AttributeType;
33  import org.apache.directory.api.ldap.model.schema.DitContentRule;
34  import org.apache.directory.api.ldap.model.schema.DitStructureRule;
35  import org.apache.directory.api.ldap.model.schema.LdapComparator;
36  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
37  import org.apache.directory.api.ldap.model.schema.MatchingRule;
38  import org.apache.directory.api.ldap.model.schema.MatchingRuleUse;
39  import org.apache.directory.api.ldap.model.schema.NameForm;
40  import org.apache.directory.api.ldap.model.schema.Normalizer;
41  import org.apache.directory.api.ldap.model.schema.ObjectClass;
42  import org.apache.directory.api.ldap.model.schema.SchemaManager;
43  import org.apache.directory.api.ldap.model.schema.SchemaObjectRenderer;
44  import org.apache.directory.api.ldap.model.schema.SchemaUtils;
45  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
46  import org.apache.directory.api.ldap.model.schema.registries.NormalizerRegistry;
47  import org.apache.directory.server.constants.ApacheSchemaConstants;
48  import org.apache.directory.server.constants.ServerDNConstants;
49  import org.apache.directory.server.core.api.DirectoryService;
50  import org.apache.directory.server.core.api.entry.ClonedServerEntry;
51  import org.apache.directory.server.core.api.interceptor.context.FilteringOperationContext;
52  import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
53  import org.apache.directory.server.core.api.partition.Partition;
54  import org.apache.directory.server.core.api.partition.PartitionTxn;
55  
56  
57  /**
58   * This class manage the Schema's operations. 
59   *
60   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
61   */
62  public final class SchemaService
63  {
64      /** cached version of the schema subentry with all attributes in it */
65      private static Entry schemaSubentry;
66  
67      /** A lock to avid concurrent generation of the SubschemaSubentry */
68      private static Object schemaSubentrLock = new Object();
69  
70  
71      private SchemaService()
72      {
73      }
74  
75  
76      /**
77       * Generate the comparators attribute from the registry
78       */
79      private static Attribute generateComparators( SchemaManager schemaManager ) throws LdapException
80      {
81          Attribute attr = new DefaultAttribute(
82              schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COMPARATORS_AT ) );
83  
84          for ( LdapComparator<?> comparator : schemaManager.getComparatorRegistry() )
85          {
86              attr.add( SchemaUtils.render( comparator ) );
87          }
88  
89          return attr;
90      }
91  
92  
93      private static Attribute generateNormalizers( SchemaManager schemaManager ) throws LdapException
94      {
95          Attribute attr = new DefaultAttribute(
96              schemaManager.getAttributeType( SchemaConstants.NORMALIZERS_AT ) );
97  
98          NormalizerRegistry nr = schemaManager.getNormalizerRegistry();
99  
100         for ( Normalizer normalizer : nr )
101         {
102             attr.add( SchemaUtils.render( normalizer ) );
103         }
104 
105         return attr;
106     }
107 
108 
109     private static Attribute generateSyntaxCheckers( SchemaManager schemaManager ) throws LdapException
110     {
111         Attribute attr = new DefaultAttribute(
112             schemaManager.getAttributeType( SchemaConstants.SYNTAX_CHECKERS_AT ) );
113 
114         for ( SyntaxChecker syntaxChecker : schemaManager.getSyntaxCheckerRegistry() )
115         {
116             attr.add( SchemaUtils.render( syntaxChecker ) );
117         }
118 
119         return attr;
120     }
121 
122 
123     private static Attribute generateObjectClasses( SchemaManager schemaManager ) throws LdapException
124     {
125         Attribute attr = new DefaultAttribute(
126             schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASSES_AT ) );
127 
128         for ( ObjectClass objectClass : schemaManager.getObjectClassRegistry() )
129         {
130             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( objectClass ) );
131         }
132 
133         return attr;
134     }
135 
136 
137     private static Attribute generateAttributeTypes( SchemaManager schemaManager ) throws LdapException
138     {
139         Attribute attr = new DefaultAttribute(
140             schemaManager.getAttributeType( SchemaConstants.ATTRIBUTE_TYPES_AT ) );
141 
142         for ( AttributeType attributeType : schemaManager.getAttributeTypeRegistry() )
143         {
144             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( attributeType ) );
145         }
146 
147         return attr;
148     }
149 
150 
151     private static Attribute generateMatchingRules( SchemaManager schemaManager ) throws LdapException
152     {
153         Attribute attr = new DefaultAttribute(
154             schemaManager.getAttributeType( SchemaConstants.MATCHING_RULES_AT ) );
155 
156         for ( MatchingRule matchingRule : schemaManager.getMatchingRuleRegistry() )
157         {
158             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( matchingRule ) );
159         }
160 
161         return attr;
162     }
163 
164 
165     private static Attribute generateMatchingRuleUses( SchemaManager schemaManager ) throws LdapException
166     {
167         Attribute attr = new DefaultAttribute(
168             schemaManager.getAttributeType( SchemaConstants.MATCHING_RULE_USE_AT ) );
169 
170         for ( MatchingRuleUse matchingRuleUse : schemaManager.getMatchingRuleUseRegistry() )
171         {
172             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( matchingRuleUse ) );
173         }
174 
175         return attr;
176     }
177 
178 
179     private static Attribute generateSyntaxes( SchemaManager schemaManager ) throws LdapException
180     {
181         Attribute attr = new DefaultAttribute(
182             schemaManager.getAttributeType( SchemaConstants.LDAP_SYNTAXES_AT ) );
183 
184         for ( LdapSyntax syntax : schemaManager.getLdapSyntaxRegistry() )
185         {
186             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( syntax ) );
187         }
188 
189         return attr;
190     }
191 
192 
193     private static Attribute generateDitContextRules( SchemaManager schemaManager ) throws LdapException
194     {
195         Attribute attr = new DefaultAttribute(
196             schemaManager.getAttributeType( SchemaConstants.DIT_CONTENT_RULES_AT ) );
197 
198         for ( DitContentRule ditContentRule : schemaManager.getDITContentRuleRegistry() )
199         {
200             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( ditContentRule ) );
201         }
202 
203         return attr;
204     }
205 
206 
207     private static Attribute generateDitStructureRules( SchemaManager schemaManager ) throws LdapException
208     {
209         Attribute attr = new DefaultAttribute(
210             schemaManager.getAttributeType( SchemaConstants.DIT_STRUCTURE_RULES_AT ) );
211 
212         for ( DitStructureRule ditStructureRule : schemaManager.getDITStructureRuleRegistry() )
213         {
214             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( ditStructureRule ) );
215         }
216 
217         return attr;
218     }
219 
220 
221     private static Attribute generateNameForms( SchemaManager schemaManager ) throws LdapException
222     {
223         Attribute attr = new DefaultAttribute(
224             schemaManager.getAttributeType( SchemaConstants.NAME_FORMS_AT ) );
225 
226         for ( NameForm nameForm : schemaManager.getNameFormRegistry() )
227         {
228             attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( nameForm ) );
229         }
230 
231         return attr;
232     }
233 
234 
235     /**
236      * Creates the SSSE by extracting all the SchemaObjects from the registries.
237      */
238     private static void generateSchemaSubentry( SchemaManager schemaManager, Entry mods ) throws LdapException
239     {
240         Entry attrs = new DefaultEntry( schemaManager, mods.getDn() );
241 
242         // add the objectClass attribute : 'top', 'subschema', 'subentry' and 'apacheSubschema' 
243         attrs.put( SchemaConstants.OBJECT_CLASS_AT,
244             SchemaConstants.TOP_OC,
245             SchemaConstants.SUBSCHEMA_OC,
246             SchemaConstants.SUBENTRY_OC,
247             ApacheSchemaConstants.APACHE_SUBSCHEMA_OC
248             );
249 
250         // add the cn attribute as required for the Rdn
251         attrs.put( SchemaConstants.CN_AT, "schema" );
252 
253         // generate all the other operational attributes
254         attrs.put( generateComparators( schemaManager ) );
255         attrs.put( generateNormalizers( schemaManager ) );
256         attrs.put( generateSyntaxCheckers( schemaManager ) );
257         attrs.put( generateObjectClasses( schemaManager ) );
258         attrs.put( generateAttributeTypes( schemaManager ) );
259         attrs.put( generateMatchingRules( schemaManager ) );
260         attrs.put( generateMatchingRuleUses( schemaManager ) );
261         attrs.put( generateSyntaxes( schemaManager ) );
262         attrs.put( generateDitContextRules( schemaManager ) );
263         attrs.put( generateDitStructureRules( schemaManager ) );
264         attrs.put( generateNameForms( schemaManager ) );
265         attrs.put( SchemaConstants.SUBTREE_SPECIFICATION_AT, "{}" );
266 
267         // -------------------------------------------------------------------
268         // set standard operational attributes for the subentry
269         // -------------------------------------------------------------------
270 
271         // Add the createTimestamp
272         Attribute createTimestamp = mods.get( SchemaConstants.CREATE_TIMESTAMP_AT );
273         attrs.put( SchemaConstants.CREATE_TIMESTAMP_AT, createTimestamp.get() );
274 
275         // Add the creatorsName
276         attrs.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
277 
278         // Add the modifyTimestamp
279         Attribute schemaModifyTimestamp = mods.get( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT );
280         attrs.put( SchemaConstants.MODIFY_TIMESTAMP_AT, schemaModifyTimestamp.get() );
281 
282         // Add the modifiersName
283         Attribute schemaModifiersName = mods.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );
284         attrs.put( SchemaConstants.MODIFIERS_NAME_AT, schemaModifiersName.get() );
285 
286         // don't swap out if a request for the subentry is in progress or we
287         // can give back an inconsistent schema back to the client so we block
288         synchronized ( schemaSubentrLock )
289         {
290             schemaSubentry = attrs;
291         }
292     }
293 
294 
295     private static void addAttribute( Entry attrs, String id ) throws LdapException
296     {
297         Attribute attr = schemaSubentry.get( id );
298 
299         if ( ( attr != null ) && ( attr.size() > 0 ) )
300         {
301             attrs.put( attr );
302         }
303     }
304 
305 
306     /**
307      * {@inheritDoc}
308      */
309     public static Entry getSubschemaEntryImmutable( DirectoryService directoryService ) throws LdapException
310     {
311         synchronized ( schemaSubentrLock )
312         {
313             if ( schemaSubentry == null )
314             {
315                 Dn schemaModificationAttributesDn = new Dn( directoryService.getSchemaManager(),
316                     SchemaConstants.SCHEMA_MODIFICATIONS_DN );
317                 
318                 Partition partition = directoryService.getSchemaPartition();
319                 
320                 LookupOperationContexttor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( null, schemaModificationAttributesDn );
321                 lookupContext.setPartition( partition );
322 
323                 try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
324                 {
325                     lookupContext.setTransaction( partitionTxn );
326                     
327                     generateSchemaSubentry( directoryService.getSchemaManager(),
328                         directoryService.getSchemaPartition().lookup( lookupContext ) );
329                 }
330                 catch ( IOException ioe )
331                 {
332                     throw new LdapOtherException( ioe.getMessage(), ioe );
333                 }
334             }
335 
336             return schemaSubentry.clone();
337         }
338     }
339 
340 
341     /* (non-Javadoc)
342      * @see org.apache.directory.server.core.schema.SchemaService#getSubschemaEntryCloned()
343      */
344     public static Entry getSubschemaEntryCloned( DirectoryService directoryService ) throws LdapException
345     {
346         if ( schemaSubentry == null )
347         {
348             Dn schemaModificationAttributesDn = new Dn( directoryService.getSchemaManager(),
349                 SchemaConstants.SCHEMA_MODIFICATIONS_DN );
350 
351             Partition partition = directoryService.getSchemaPartition();
352             
353             LookupOperationContexttor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( null, schemaModificationAttributesDn );
354             lookupContext.setPartition( partition );
355 
356             try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
357             {
358                 lookupContext.setTransaction( partitionTxn );
359                 
360                 generateSchemaSubentry(
361                     directoryService.getSchemaManager(),
362                     directoryService.getSchemaPartition().lookup( lookupContext ) );
363             }
364             catch ( IOException ioe )
365             {
366                 throw new LdapOtherException( ioe.getMessage(), ioe );
367             }
368         }
369 
370         return schemaSubentry.clone();
371     }
372 
373 
374     /**
375      * {@inheritDoc}
376      */
377     public static Entry getSubschemaEntry( DirectoryService directoryService, FilteringOperationContext operationContext )
378         throws LdapException
379     {
380         SchemaManager schemaManager = directoryService.getSchemaManager();
381 
382         Entry attrs = new DefaultEntry( schemaManager, Dn.ROOT_DSE );
383 
384         synchronized ( schemaSubentrLock )
385         {
386             // ---------------------------------------------------------------
387             // Check if we need an update by looking at timestamps on disk
388             // ---------------------------------------------------------------
389             Dn schemaModificationAttributesDn = new Dn( directoryService.getSchemaManager(),
390                 SchemaConstants.SCHEMA_MODIFICATIONS_DN );
391 
392             Partition partition = directoryService.getSchemaPartition();
393             
394             LookupOperationContexttor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( null, schemaModificationAttributesDn, SchemaConstants.ALL_ATTRIBUTES_ARRAY );
395             lookupContext.setPartition( partition );
396 
397             try ( PartitionTxn partitionTxn = partition.beginReadTransaction() )
398             {
399                 lookupContext.setTransaction( partitionTxn );
400 
401                 Entry mods =
402                     directoryService.getSchemaPartition().lookup( lookupContext );
403                 // TODO enable this optimization at some point but for now it
404                 // is causing some problems so I will just turn it off
405                 //          Attribute modifyTimeDisk = mods.get( SchemaConstants.MODIFY_TIMESTAMP_AT );
406                 //
407                 //          Attribute modifyTimeMemory = null;
408                 //
409                 //            if ( schemaSubentry != null )
410                 //            {
411                 //                modifyTimeMemory = schemaSubentry.get( SchemaConstants.MODIFY_TIMESTAMP_AT );
412                 //                if ( modifyTimeDisk == null && modifyTimeMemory == null )
413                 //                {
414                 //                    // do nothing!
415                 //                }
416                 //                else if ( modifyTimeDisk != null && modifyTimeMemory != null )
417                 //                {
418                 //                    Date disk = DateUtils.getDate( ( String ) modifyTimeDisk.get() );
419                 //                    Date mem = DateUtils.getDate( ( String ) modifyTimeMemory.get() );
420                 //                    if ( disk.after( mem ) )
421                 //                    {
422                 //                        generateSchemaSubentry( mods );
423                 //                    }
424                 //                }
425                 //                else
426                 //                {
427                 //                    generateSchemaSubentry( mods );
428                 //                }
429                 //            }
430                 //            else
431                 //            {
432                 generateSchemaSubentry( schemaManager, mods );
433             }
434             catch ( IOException ioe )
435             {
436                 throw new LdapOtherException( ioe.getMessage(), ioe );
437             }
438 
439             // ---------------------------------------------------------------
440             // Prep Work: Transform the attributes to their OID counterpart
441             // ---------------------------------------------------------------
442             addAttribute( attrs, SchemaConstants.COMPARATORS_AT );
443             addAttribute( attrs, SchemaConstants.NORMALIZERS_AT );
444             addAttribute( attrs, SchemaConstants.SYNTAX_CHECKERS_AT );
445             addAttribute( attrs, SchemaConstants.OBJECT_CLASSES_AT );
446             addAttribute( attrs, SchemaConstants.ATTRIBUTE_TYPES_AT );
447             addAttribute( attrs, SchemaConstants.MATCHING_RULES_AT );
448             addAttribute( attrs, SchemaConstants.MATCHING_RULE_USE_AT );
449             addAttribute( attrs, SchemaConstants.LDAP_SYNTAXES_AT );
450             addAttribute( attrs, SchemaConstants.DIT_CONTENT_RULES_AT );
451             addAttribute( attrs, SchemaConstants.DIT_STRUCTURE_RULES_AT );
452             addAttribute( attrs, SchemaConstants.NAME_FORMS_AT );
453             addAttribute( attrs, SchemaConstants.SUBTREE_SPECIFICATION_AT );
454 
455             // add the objectClass attribute if needed
456             addAttribute( attrs, SchemaConstants.OBJECT_CLASS_AT );
457 
458             // add the cn attribute as required for the Rdn
459             addAttribute( attrs, SchemaConstants.CN_AT );
460 
461             // -------------------------------------------------------------------
462             // set standard operational attributes for the subentry
463             // -------------------------------------------------------------------
464             addAttribute( attrs, SchemaConstants.CREATE_TIMESTAMP_AT );
465             addAttribute( attrs, SchemaConstants.CREATORS_NAME_AT );
466             addAttribute( attrs, SchemaConstants.MODIFY_TIMESTAMP_AT );
467             addAttribute( attrs, SchemaConstants.MODIFIERS_NAME_AT );
468             addAttribute( attrs, SchemaConstants.ENTRY_UUID_AT );
469             addAttribute( attrs, SchemaConstants.ENTRY_DN_AT );
470         }
471 
472         return new ClonedServerEntry( attrs );
473     }
474 }