View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.core.api.schema.registries.synchronizers;
21  
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
27  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
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.LdapInvalidDnException;
31  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
32  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
33  import org.apache.directory.api.ldap.model.name.Dn;
34  import org.apache.directory.api.ldap.model.name.Rdn;
35  import org.apache.directory.api.ldap.model.schema.AttributeType;
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.SchemaManager;
39  import org.apache.directory.api.ldap.model.schema.SchemaObject;
40  import org.apache.directory.api.ldap.model.schema.registries.Schema;
41  import org.apache.directory.api.util.Strings;
42  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
43  import org.apache.directory.server.i18n.I18n;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  
48  /**
49   * A syntax specific registry synchronizer which responds to syntax entry 
50   * changes in the DIT to update the syntax registry.
51   *
52   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
53   */
54  public class SyntaxSynchronizer extends AbstractRegistrySynchronizer
55  {
56      /** A logger for this class */
57      private static final Logger LOG = LoggerFactory.getLogger( SyntaxSynchronizer.class );
58  
59  
60      /**
61       * Creates a new instance of SyntaxSynchronizer.
62       *
63       * @param schemaManager The global schemaManager
64       * @throws Exception If the initialization failed
65       */
66      public SyntaxSynchronizer( SchemaManager schemaManager ) throws Exception
67      {
68          super( schemaManager );
69      }
70  
71  
72      /**
73       * {@inheritDoc}
74       */
75      @Override
76      public boolean modify( ModifyOperationContext modifyContext, Entry targetEntry, boolean cascade )
77          throws LdapException
78      {
79          Dn name = modifyContext.getDn();
80          Entry entry = modifyContext.getEntry();
81          String oid = getOid( entry );
82          LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(),
83              getSchemaName( name ) );
84          String schemaName = getSchemaName( entry.getDn() );
85  
86          if ( isSchemaEnabled( schemaName ) )
87          {
88              schemaManager.unregisterLdapSyntax( oid );
89              schemaManager.add( syntax );
90  
91              return SCHEMA_MODIFIED;
92          }
93  
94          return SCHEMA_UNCHANGED;
95      }
96  
97  
98      /**
99       * {@inheritDoc}
100      */
101     @Override
102     public void add( Entry entry ) throws LdapException
103     {
104         Dn dn = entry.getDn();
105         Dn parentDn = dn.getParent();
106 
107         // The parent Dn must be ou=syntaxes,cn=<schemaName>,ou=schema
108         checkParent( parentDn, schemaManager, SchemaConstants.SYNTAX );
109 
110         // The new schemaObject's OID must not already exist
111         checkOidIsUnique( entry );
112 
113         // Build the new Syntax from the given entry
114         String schemaName = getSchemaName( dn );
115 
116         LdapSyntax syntax = factory.getSyntax( schemaManager, entry, schemaManager.getRegistries(), schemaName );
117 
118         // At this point, the constructed Syntax has not been checked against the 
119         // existing Registries. It may be broken (missing SUP, or such), it will be checked
120         // there, if the schema and the Syntax are both enabled.
121         Schema schema = schemaManager.getLoadedSchema( schemaName );
122 
123         if ( schema.isEnabled() && syntax.isEnabled() )
124         {
125             if ( schemaManager.add( syntax ) )
126             {
127                 LOG.debug( "Added {} into the enabled schema {}", dn.getName(), schemaName );
128             }
129             else
130             {
131                 // We have some error : reject the addition and get out
132                 String msg = I18n.err( I18n.ERR_399, entry.getDn().getName(),
133                     Strings.listToString( schemaManager.getErrors() ) );
134                 LOG.info( msg );
135                 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
136             }
137         }
138         else
139         {
140             LOG.debug( "The Syntax {} cannot be added in the disabled schema {}", dn.getName(), schemaName );
141         }
142     }
143 
144 
145     /**
146      * Check if a syntax is used by an AT or a MR
147      */
148     private List<SchemaObject> checkInUse( String oid )
149     {
150         List<SchemaObject> dependees = new ArrayList<>();
151 
152         for ( AttributeType attributeType : schemaManager.getAttributeTypeRegistry() )
153         {
154             if ( oid.equals( attributeType.getSyntax().getOid() ) )
155             {
156                 dependees.add( attributeType );
157             }
158         }
159 
160         for ( MatchingRule matchingRule : schemaManager.getMatchingRuleRegistry() )
161         {
162             if ( oid.equals( matchingRule.getSyntax().getOid() ) )
163             {
164                 dependees.add( matchingRule );
165             }
166         }
167 
168         return dependees;
169     }
170 
171 
172     /**
173      * Get the list of SchemaObject's name using a given syntax
174      */
175     private String getNames( List<SchemaObject> schemaObjects )
176     {
177         StringBuilder sb = new StringBuilder();
178         boolean isFirst = true;
179 
180         for ( SchemaObject schemaObject : schemaObjects )
181         {
182             if ( isFirst )
183             {
184                 isFirst = false;
185             }
186             else
187             {
188                 sb.append( ", " );
189             }
190 
191             sb.append( schemaObject.getName() );
192         }
193 
194         return sb.toString();
195     }
196 
197 
198     /**
199      * {@inheritDoc}
200      */
201     @Override
202     public void delete( Entry entry, boolean cascade ) throws LdapException
203     {
204         Dn dn = entry.getDn();
205         Dn parentDn = dn.getParent();
206 
207         // The parent Dn must be ou=syntaxes,cn=<schemaName>,ou=schema
208         checkParent( parentDn, schemaManager, SchemaConstants.SYNTAX );
209 
210         // Get the Syntax from the given entry ( it has been grabbed from the server earlier)
211         String schemaName = getSchemaName( entry.getDn() );
212 
213         // Get the schema 
214         Schema schema = schemaManager.getLoadedSchema( schemaName );
215 
216         if ( schema.isDisabled() )
217         {
218             // The schema is disabled, nothing to do.
219             LOG.debug( "The Syntax {} cannot be removed from the disabled schema {}.",
220                 dn.getName(), schemaName );
221 
222             return;
223         }
224 
225         // Test that the Oid exists
226         LdapSyntax syntax = ( LdapSyntax ) checkOidExists( entry );
227 
228         List<Throwable> errors = new ArrayList<>();
229 
230         if ( schema.isEnabled() && syntax.isEnabled() )
231         {
232             if ( schemaManager.delete( syntax ) )
233             {
234                 LOG.debug( "Removed {} from the schema {}", syntax, schemaName );
235             }
236             else
237             {
238                 // We have some error : reject the deletion and get out
239                 String msg = I18n.err( I18n.ERR_400, entry.getDn().getName(),
240                     Strings.listToString( errors ) );
241                 LOG.info( msg );
242                 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
243             }
244         }
245         else
246         {
247             LOG.debug( "Removed {} from the disabled schema {}", syntax, schemaName );
248         }
249     }
250 
251 
252     /**
253      * {@inheritDoc}
254      */
255     @Override
256     public void rename( Entry entry, Rdn newRdn, boolean cascade ) throws LdapException
257     {
258         String oldOid = getOid( entry );
259         String schemaName = getSchemaName( entry.getDn() );
260 
261         // Check that this syntax is not used by an AttributeType
262         List<SchemaObject> dependees = checkInUse( oldOid );
263 
264         if ( !dependees.isEmpty() )
265         {
266             throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err( I18n.ERR_401,
267                 oldOid,
268                 getNames( dependees ) ) );
269         }
270 
271         Entry targetEntry = entry.clone();
272         String newOid = newRdn.getValue();
273         checkOidIsUnique( newOid );
274 
275         targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
276         LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(),
277             getSchemaName( entry.getDn() ) );
278 
279         if ( isSchemaEnabled( schemaName ) )
280         {
281             schemaManager.unregisterLdapSyntax( oldOid );
282             schemaManager.add( syntax );
283         }
284         else
285         {
286             // always remove old OIDs that are not in schema anymore
287             unregisterOids( syntax );
288             // even for disabled schemas add OIDs
289             registerOids( syntax );
290         }
291     }
292 
293 
294     /**
295      * {@inheritDoc}
296      */
297     @Override
298     public void moveAndRename( Dn oriChildName, Dn newParentName, Rdn newRn, boolean deleteOldRn,
299         Entry entry, boolean cascade ) throws LdapException
300     {
301         checkNewParent( newParentName );
302         String oldOid = getOid( entry );
303         String oldSchemaName = getSchemaName( oriChildName );
304         String newSchemaName = getSchemaName( newParentName );
305 
306         // Check that this syntax is not used by an AttributeType
307         List<SchemaObject> dependees = checkInUse( oldOid );
308 
309         if ( !dependees.isEmpty() )
310         {
311             throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
312                 I18n.err( I18n.ERR_401, oldOid, getNames( dependees ) ) );
313         }
314 
315         Entry targetEntry = entry.clone();
316         String newOid = newRn.getValue();
317         checkOidIsUnique( newOid );
318 
319         targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
320         LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(),
321             getSchemaName( newParentName ) );
322 
323         if ( isSchemaEnabled( oldSchemaName ) )
324         {
325             schemaManager.unregisterLdapSyntax( oldOid );
326         }
327         else
328         {
329             unregisterOids( syntax );
330         }
331 
332         if ( isSchemaEnabled( newSchemaName ) )
333         {
334             schemaManager.add( syntax );
335         }
336         else
337         {
338             // register new syntax OIDs even if schema is disabled 
339             registerOids( syntax );
340         }
341     }
342 
343 
344     /**
345      * {@inheritDoc}
346      */
347     @Override
348     public void move( Dn oriChildName, Dn newParentName, Entry entry, boolean cascade ) throws LdapException
349     {
350         checkNewParent( newParentName );
351         String oid = getOid( entry );
352         String oldSchemaName = getSchemaName( oriChildName );
353         String newSchemaName = getSchemaName( newParentName );
354 
355         LdapSyntax syntax = factory.getSyntax( schemaManager, entry, schemaManager.getRegistries(),
356             getSchemaName( newParentName ) );
357 
358         if ( isSchemaEnabled( oldSchemaName ) )
359         {
360             schemaManager.unregisterLdapSyntax( oid );
361         }
362         else
363         {
364             unregisterOids( syntax );
365         }
366 
367         if ( isSchemaEnabled( newSchemaName ) )
368         {
369             schemaManager.add( syntax );
370         }
371         else
372         {
373             registerOids( syntax );
374         }
375     }
376 
377 
378     private void checkNewParent( Dn newParent ) throws LdapException
379     {
380         if ( newParent.size() != 3 )
381         {
382             throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
383                 I18n.err( I18n.ERR_402 ) );
384         }
385 
386         Rdn rdn = newParent.getRdn();
387         if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals(
388             SchemaConstants.OU_AT_OID ) )
389         {
390             throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_403 ) );
391         }
392 
393         if ( !rdn.getValue().equalsIgnoreCase( SchemaConstants.SYNTAXES ) )
394         {
395             throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_363 ) );
396         }
397     }
398 }