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 package org.apache.directory.server.core.schema;
020
021
022 import java.util.HashSet;
023 import java.util.Set;
024
025 import org.apache.directory.server.constants.ApacheSchemaConstants;
026 import org.apache.directory.server.constants.ServerDNConstants;
027 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
028 import org.apache.directory.shared.ldap.constants.SchemaConstants;
029 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute;
030 import org.apache.directory.shared.ldap.entry.DefaultServerEntry;
031 import org.apache.directory.shared.ldap.entry.EntryAttribute;
032 import org.apache.directory.shared.ldap.entry.ServerEntry;
033 import org.apache.directory.shared.ldap.exception.LdapException;
034 import org.apache.directory.shared.ldap.name.DN;
035 import org.apache.directory.shared.ldap.schema.AttributeType;
036 import org.apache.directory.shared.ldap.schema.DITContentRule;
037 import org.apache.directory.shared.ldap.schema.DITStructureRule;
038 import org.apache.directory.shared.ldap.schema.LdapComparator;
039 import org.apache.directory.shared.ldap.schema.LdapSyntax;
040 import org.apache.directory.shared.ldap.schema.MatchingRule;
041 import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
042 import org.apache.directory.shared.ldap.schema.NameForm;
043 import org.apache.directory.shared.ldap.schema.Normalizer;
044 import org.apache.directory.shared.ldap.schema.ObjectClass;
045 import org.apache.directory.shared.ldap.schema.SchemaManager;
046 import org.apache.directory.shared.ldap.schema.SchemaUtils;
047 import org.apache.directory.shared.ldap.schema.SyntaxChecker;
048 import org.apache.directory.shared.ldap.schema.registries.NormalizerRegistry;
049
050
051 /**
052 * This class manage the Schema's operations.
053 *
054 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
055 * @version $Rev$, $Date$
056 */
057 public class DefaultSchemaService implements SchemaService
058 {
059 private static final String[] EMPTY_STRING_ARRAY = new String[0];
060
061 /** cached version of the schema subentry with all attributes in it */
062 private ServerEntry schemaSubentry;
063 private final Object lock = new Object();
064
065 /** a handle on the schema partition */
066 private SchemaPartition schemaPartition;
067
068 /** the normalized name for the schema modification attributes */
069 private DN schemaModificationAttributesDN;
070
071 /** A lock to avid concurrent generation of the SubschemaSubentry */
072 private Object schemaSubentrLock = new Object();
073
074
075 public DefaultSchemaService() throws Exception
076 {
077 schemaPartition = new SchemaPartition();
078 }
079
080
081
082 /* (non-Javadoc)
083 * @see org.apache.directory.server.core.schema.SchemaService#isSchemaSubentry(java.lang.String)
084 */
085 public boolean isSchemaSubentry( String dnString ) throws LdapException
086 {
087 if ( ServerDNConstants.CN_SCHEMA_DN.equalsIgnoreCase( dnString ) ||
088 ServerDNConstants.CN_SCHEMA_DN_NORMALIZED.equalsIgnoreCase( dnString ) )
089 {
090 return true;
091 }
092
093 DN dn = new DN( dnString ).normalize( schemaPartition.getSchemaManager().getNormalizerMapping() );
094 return dn.getNormName().equals( ServerDNConstants.CN_SCHEMA_DN_NORMALIZED );
095 }
096
097
098 public final SchemaManager getSchemaManager()
099 {
100 return schemaPartition.getSchemaManager();
101 }
102
103
104 /* (non-Javadoc)
105 * @see org.apache.directory.server.core.schema.SchemaService#getSchemaPartition()
106 */
107 public SchemaPartition getSchemaPartition()
108 {
109 return schemaPartition;
110 }
111
112
113 /* (non-Javadoc)
114 * @see org.apache.directory.server.core.schema.SchemaService#setSchemaPartition(org.apache.directory.server.core.schema.SchemaPartition)
115 */
116 public void setSchemaPartition( SchemaPartition schemaPartition )
117 {
118 this.schemaPartition = schemaPartition;
119 }
120
121
122 /**
123 * Generate the comparators attribute from the registry
124 */
125 private EntryAttribute generateComparators() throws LdapException
126 {
127 EntryAttribute attr = new DefaultServerAttribute(
128 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.COMPARATORS_AT ) );
129
130 for ( LdapComparator<?> comparator : getSchemaManager().getComparatorRegistry() )
131 {
132 attr.add( SchemaUtils.render( comparator ) );
133 }
134
135 return attr;
136 }
137
138
139 private EntryAttribute generateNormalizers() throws LdapException
140 {
141 EntryAttribute attr = new DefaultServerAttribute(
142 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.NORMALIZERS_AT ) );
143
144 NormalizerRegistry nr = getSchemaManager().getNormalizerRegistry();
145
146 for ( Normalizer normalizer : nr )
147 {
148 attr.add( SchemaUtils.render( normalizer ) );
149 }
150
151 return attr;
152 }
153
154
155 private EntryAttribute generateSyntaxCheckers() throws LdapException
156 {
157 EntryAttribute attr = new DefaultServerAttribute(
158 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.SYNTAX_CHECKERS_AT ) );
159
160 for ( SyntaxChecker syntaxChecker : getSchemaManager().getSyntaxCheckerRegistry() )
161 {
162 attr.add( SchemaUtils.render( syntaxChecker ) );
163 }
164
165 return attr;
166 }
167
168
169 private EntryAttribute generateObjectClasses() throws LdapException
170 {
171 EntryAttribute attr = new DefaultServerAttribute(
172 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.OBJECT_CLASSES_AT ) );
173
174 for ( ObjectClass objectClass : getSchemaManager().getObjectClassRegistry() )
175 {
176 attr.add( SchemaUtils.render( objectClass ).toString() );
177 }
178
179 return attr;
180 }
181
182
183 private EntryAttribute generateAttributeTypes() throws LdapException
184 {
185 EntryAttribute attr = new DefaultServerAttribute(
186 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.ATTRIBUTE_TYPES_AT ) );
187
188 for ( AttributeType attributeType : getSchemaManager().getAttributeTypeRegistry() )
189 {
190 attr.add( SchemaUtils.render( attributeType ).toString() );
191 }
192
193 return attr;
194 }
195
196
197 private EntryAttribute generateMatchingRules() throws LdapException
198 {
199 EntryAttribute attr = new DefaultServerAttribute(
200 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.MATCHING_RULES_AT ) );
201
202 for ( MatchingRule matchingRule : getSchemaManager().getMatchingRuleRegistry() )
203 {
204 attr.add( SchemaUtils.render( matchingRule ).toString() );
205 }
206
207 return attr;
208 }
209
210
211 private EntryAttribute generateMatchingRuleUses() throws LdapException
212 {
213 EntryAttribute attr = new DefaultServerAttribute(
214 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.MATCHING_RULE_USE_AT ) );
215
216 for ( MatchingRuleUse matchingRuleUse : getSchemaManager().getMatchingRuleUseRegistry() )
217 {
218 attr.add( SchemaUtils.render( matchingRuleUse ).toString() );
219 }
220
221 return attr;
222 }
223
224
225 private EntryAttribute generateSyntaxes() throws LdapException
226 {
227 EntryAttribute attr = new DefaultServerAttribute(
228 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.LDAP_SYNTAXES_AT ) );
229
230 for ( LdapSyntax syntax : getSchemaManager().getLdapSyntaxRegistry() )
231 {
232 attr.add( SchemaUtils.render( syntax ).toString() );
233 }
234
235 return attr;
236 }
237
238
239 private EntryAttribute generateDitContextRules() throws LdapException
240 {
241 EntryAttribute attr = new DefaultServerAttribute(
242 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.DIT_CONTENT_RULES_AT ) );
243
244 for ( DITContentRule ditContentRule : getSchemaManager().getDITContentRuleRegistry() )
245 {
246 attr.add( SchemaUtils.render( ditContentRule ).toString() );
247 }
248
249 return attr;
250 }
251
252
253 private EntryAttribute generateDitStructureRules() throws LdapException
254 {
255 EntryAttribute attr = new DefaultServerAttribute(
256 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.DIT_STRUCTURE_RULES_AT ) );
257
258 for ( DITStructureRule ditStructureRule : getSchemaManager().getDITStructureRuleRegistry() )
259 {
260 attr.add( SchemaUtils.render( ditStructureRule ).toString() );
261 }
262
263 return attr;
264 }
265
266
267 private EntryAttribute generateNameForms() throws LdapException
268 {
269 EntryAttribute attr = new DefaultServerAttribute(
270 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.NAME_FORMS_AT ) );
271
272 for ( NameForm nameForm : getSchemaManager().getNameFormRegistry() )
273 {
274 attr.add( SchemaUtils.render( nameForm ).toString() );
275 }
276
277 return attr;
278 }
279
280
281 /**
282 * Creates the SSSE by extracting all the SchemaObjects from the registries.
283 */
284 private void generateSchemaSubentry( ServerEntry mods ) throws LdapException
285 {
286 ServerEntry attrs = new DefaultServerEntry( getSchemaManager(), mods.getDn() );
287
288 // add the objectClass attribute : 'top', 'subschema', 'subentry' and 'apacheSubschema'
289 attrs.put( SchemaConstants.OBJECT_CLASS_AT,
290 SchemaConstants.TOP_OC,
291 SchemaConstants.SUBSCHEMA_OC,
292 SchemaConstants.SUBENTRY_OC,
293 ApacheSchemaConstants.APACHE_SUBSCHEMA_OC
294 );
295
296 // add the cn attribute as required for the RDN
297 attrs.put( SchemaConstants.CN_AT, "schema" );
298
299 // generate all the other operational attributes
300 attrs.put( generateComparators() );
301 attrs.put( generateNormalizers() );
302 attrs.put( generateSyntaxCheckers() );
303 attrs.put( generateObjectClasses() );
304 attrs.put( generateAttributeTypes() );
305 attrs.put( generateMatchingRules() );
306 attrs.put( generateMatchingRuleUses() );
307 attrs.put( generateSyntaxes() );
308 attrs.put( generateDitContextRules() );
309 attrs.put( generateDitStructureRules() );
310 attrs.put( generateNameForms() );
311 attrs.put( SchemaConstants.SUBTREE_SPECIFICATION_AT, "{}" );
312
313 // -------------------------------------------------------------------
314 // set standard operational attributes for the subentry
315 // -------------------------------------------------------------------
316
317 // Add the createTimestamp
318 EntryAttribute createTimestamp = mods.get( SchemaConstants.CREATE_TIMESTAMP_AT );
319 attrs.put( SchemaConstants.CREATE_TIMESTAMP_AT, createTimestamp.get() );
320
321 // Add the creatorsName
322 attrs.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
323
324 // Add the modifyTimestamp
325 EntryAttribute schemaModifyTimestamp = mods.get( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT );
326 attrs.put( SchemaConstants.MODIFY_TIMESTAMP_AT, schemaModifyTimestamp.get() );
327
328 // Add the modifiersName
329 EntryAttribute schemaModifiersName = mods.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );
330 attrs.put( SchemaConstants.MODIFIERS_NAME_AT, schemaModifiersName.get() );
331
332 // don't swap out if a request for the subentry is in progress or we
333 // can give back an inconsistent schema back to the client so we block
334 synchronized ( lock )
335 {
336 schemaSubentry = attrs;
337 }
338 }
339
340
341 private void addAttribute( ServerEntry attrs, String id ) throws LdapException
342 {
343 EntryAttribute attr = schemaSubentry.get( id );
344
345 if ( attr != null )
346 {
347 attrs.put( attr );
348 }
349 }
350
351
352 /**
353 * {@inheritDoc}
354 */
355 public ServerEntry getSubschemaEntryImmutable() throws Exception
356 {
357 synchronized ( schemaSubentrLock )
358 {
359 if ( schemaSubentry == null )
360 {
361 generateSchemaSubentry( schemaPartition.lookup(
362 new LookupOperationContext( null, schemaModificationAttributesDN ) ) );
363 }
364
365 return ( ServerEntry ) schemaSubentry.clone();
366 }
367 }
368
369
370 /**
371 * Initializes the SchemaService
372 *
373 * @throws Exception If the initializaion fails
374 */
375 public void initialize() throws Exception
376 {
377 try
378 {
379 schemaModificationAttributesDN = new DN( ServerDNConstants.SCHEMA_MODIFICATIONS_DN );
380 schemaModificationAttributesDN.normalize(
381 getSchemaManager().getNormalizerMapping() );
382 }
383 catch ( LdapException e )
384 {
385 throw new RuntimeException( e );
386 }
387 }
388
389
390 /* (non-Javadoc)
391 * @see org.apache.directory.server.core.schema.SchemaService#getSubschemaEntryCloned()
392 */
393 public ServerEntry getSubschemaEntryCloned() throws Exception
394 {
395 if ( schemaSubentry == null )
396 {
397 generateSchemaSubentry( schemaPartition.lookup(
398 new LookupOperationContext( null, schemaModificationAttributesDN ) ) );
399 }
400
401 return ( ServerEntry ) schemaSubentry.clone();
402 }
403
404
405 /**
406 * {@inheritDoc}
407 */
408 public ServerEntry getSubschemaEntry( String[] ids ) throws Exception
409 {
410 if ( ids == null )
411 {
412 ids = EMPTY_STRING_ARRAY;
413 }
414
415 Set<String> setOids = new HashSet<String>();
416 ServerEntry attrs = new DefaultServerEntry( getSchemaManager(), DN.EMPTY_DN );
417 boolean returnAllOperationalAttributes = false;
418
419 synchronized( lock )
420 {
421 // ---------------------------------------------------------------
422 // Check if we need an update by looking at timestamps on disk
423 // ---------------------------------------------------------------
424
425 ServerEntry mods =
426 schemaPartition.lookup(
427 new LookupOperationContext( null, schemaModificationAttributesDN ) );
428
429 // @todo enable this optimization at some point but for now it
430 // is causing some problems so I will just turn it off
431 // Attribute modifyTimeDisk = mods.get( SchemaConstants.MODIFY_TIMESTAMP_AT );
432 //
433 // Attribute modifyTimeMemory = null;
434 //
435 // if ( schemaSubentry != null )
436 // {
437 // modifyTimeMemory = schemaSubentry.get( SchemaConstants.MODIFY_TIMESTAMP_AT );
438 // if ( modifyTimeDisk == null && modifyTimeMemory == null )
439 // {
440 // // do nothing!
441 // }
442 // else if ( modifyTimeDisk != null && modifyTimeMemory != null )
443 // {
444 // Date disk = DateUtils.getDate( ( String ) modifyTimeDisk.get() );
445 // Date mem = DateUtils.getDate( ( String ) modifyTimeMemory.get() );
446 // if ( disk.after( mem ) )
447 // {
448 // generateSchemaSubentry( mods );
449 // }
450 // }
451 // else
452 // {
453 // generateSchemaSubentry( mods );
454 // }
455 // }
456 // else
457 // {
458 generateSchemaSubentry( mods );
459 // }
460
461 // ---------------------------------------------------------------
462 // Prep Work: Transform the attributes to their OID counterpart
463 // ---------------------------------------------------------------
464
465 for ( String id:ids )
466 {
467 // Check whether the set contains a plus, and use it below to include all
468 // operational attributes. Due to RFC 3673, and issue DIREVE-228 in JIRA
469 if ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( id ) )
470 {
471 returnAllOperationalAttributes = true;
472 }
473 else if ( SchemaConstants.ALL_USER_ATTRIBUTES.equals( id ) )
474 {
475 setOids.add( id );
476 }
477 else
478 {
479 setOids.add( getSchemaManager().getAttributeTypeRegistry().getOidByName( id ) );
480 }
481 }
482
483 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.COMPARATORS_AT_OID ) )
484 {
485 addAttribute( attrs, SchemaConstants.COMPARATORS_AT );
486 }
487
488 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NORMALIZERS_AT_OID ) )
489 {
490 addAttribute( attrs, SchemaConstants.NORMALIZERS_AT );
491 }
492
493 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SYNTAX_CHECKERS_AT_OID ) )
494 {
495 addAttribute( attrs, SchemaConstants.SYNTAX_CHECKERS_AT );
496 }
497
498 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.OBJECT_CLASSES_AT_OID ) )
499 {
500 addAttribute( attrs, SchemaConstants.OBJECT_CLASSES_AT );
501 }
502
503 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.ATTRIBUTE_TYPES_AT_OID ) )
504 {
505 addAttribute( attrs, SchemaConstants.ATTRIBUTE_TYPES_AT );
506 }
507
508 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULES_AT_OID ) )
509 {
510 addAttribute( attrs, SchemaConstants.MATCHING_RULES_AT );
511 }
512
513 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULE_USE_AT_OID ) )
514 {
515 addAttribute( attrs, SchemaConstants.MATCHING_RULE_USE_AT );
516 }
517
518 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.LDAP_SYNTAXES_AT_OID ) )
519 {
520 addAttribute( attrs, SchemaConstants.LDAP_SYNTAXES_AT );
521 }
522
523 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_CONTENT_RULES_AT_OID ) )
524 {
525 addAttribute( attrs, SchemaConstants.DIT_CONTENT_RULES_AT );
526 }
527
528 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_STRUCTURE_RULES_AT_OID ) )
529 {
530 addAttribute( attrs, SchemaConstants.DIT_STRUCTURE_RULES_AT );
531 }
532
533 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NAME_FORMS_AT_OID ) )
534 {
535 addAttribute( attrs, SchemaConstants.NAME_FORMS_AT );
536 }
537
538 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SUBTREE_SPECIFICATION_AT_OID ) )
539 {
540 addAttribute( attrs, SchemaConstants.SUBTREE_SPECIFICATION_AT );
541 }
542
543 int minSetSize = 0;
544
545 if ( setOids.contains( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) )
546 {
547 minSetSize++;
548 }
549
550 if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) )
551 {
552 minSetSize++;
553 }
554
555 if ( setOids.contains( SchemaConstants.REF_AT_OID ) )
556 {
557 minSetSize++;
558 }
559
560 // add the objectClass attribute
561 if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
562 setOids.contains( SchemaConstants.OBJECT_CLASS_AT_OID ) ||
563 setOids.size() == minSetSize )
564 {
565 addAttribute( attrs, SchemaConstants.OBJECT_CLASS_AT );
566 }
567
568 // add the cn attribute as required for the RDN
569 if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ||
570 setOids.contains( SchemaConstants.CN_AT_OID ) ||
571 setOids.size() == minSetSize )
572 {
573 addAttribute( attrs, SchemaConstants.CN_AT );
574 }
575
576 // -------------------------------------------------------------------
577 // set standard operational attributes for the subentry
578 // -------------------------------------------------------------------
579
580
581 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATE_TIMESTAMP_AT_OID ) )
582 {
583 addAttribute( attrs, SchemaConstants.CREATE_TIMESTAMP_AT );
584 }
585
586 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATORS_NAME_AT_OID ) )
587 {
588 addAttribute( attrs, SchemaConstants.CREATORS_NAME_AT );
589 }
590
591 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFY_TIMESTAMP_AT_OID ) )
592 {
593 addAttribute( attrs, SchemaConstants.MODIFY_TIMESTAMP_AT );
594 }
595
596 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFIERS_NAME_AT_OID ) )
597 {
598 addAttribute( attrs, SchemaConstants.MODIFIERS_NAME_AT );
599 }
600 }
601
602 return attrs;
603 }
604 }