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 */
020package org.apache.directory.server.core.api.schema.registries.synchronizers;
021
022
023import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
024import org.apache.directory.api.ldap.model.constants.SchemaConstants;
025import org.apache.directory.api.ldap.model.entry.Entry;
026import org.apache.directory.api.ldap.model.exception.LdapException;
027import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
028import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException;
029import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
030import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
031import org.apache.directory.api.ldap.model.name.Dn;
032import org.apache.directory.api.ldap.model.name.Rdn;
033import org.apache.directory.api.ldap.model.schema.SchemaManager;
034import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
035import org.apache.directory.api.ldap.model.schema.registries.Schema;
036import org.apache.directory.api.util.Strings;
037import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
038import org.apache.directory.server.i18n.I18n;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042
043/**
044 * A synchronizer which detects changes to syntaxCheckers and updates the
045 * {@link SchemaManager}.
046 *
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049public class SyntaxCheckerSynchronizer extends AbstractRegistrySynchronizer
050{
051    /** A logger for this class */
052    private static final Logger LOG = LoggerFactory.getLogger( SyntaxCheckerSynchronizer.class );
053
054
055    /**
056     * Creates a new instance of SyntaxCheckerSynchronizer.
057     *
058     * @param schemaManager The global schemaManager
059     * @throws Exception If the initialization failed
060     */
061    public SyntaxCheckerSynchronizer( SchemaManager schemaManager ) throws Exception
062    {
063        super( schemaManager );
064    }
065
066
067    /**
068     * {@inheritDoc}
069     */
070    @Override
071    public boolean modify( ModifyOperationContext modifyContext, Entry targetEntry, boolean cascade )
072        throws LdapException
073    {
074        Dn name = modifyContext.getDn();
075        Entry entry = modifyContext.getEntry();
076        String schemaName = getSchemaName( name );
077        String oid = getOid( entry );
078        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( schemaManager, targetEntry, schemaManager
079            .getRegistries(), schemaName );
080
081        if ( isSchemaEnabled( schemaName ) )
082        {
083            syntaxChecker.setSchemaName( schemaName );
084
085            schemaManager.unregisterSyntaxChecker( oid );
086            schemaManager.add( syntaxChecker );
087
088            return SCHEMA_MODIFIED;
089        }
090
091        return SCHEMA_UNCHANGED;
092    }
093
094
095    /**
096     * {@inheritDoc}
097     */
098    @Override
099    public void add( Entry entry ) throws LdapException
100    {
101        Dn dn = entry.getDn();
102        Dn parentDn = dn.getParent();
103
104        // The parent Dn must be ou=syntaxcheckers,cn=<schemaName>,ou=schema
105        checkParent( parentDn, schemaManager, SchemaConstants.SYNTAX_CHECKER );
106
107        // The new schemaObject's OID must not already exist
108        checkOidIsUniqueForSyntaxChecker( entry );
109
110        // Build the new SyntaxChecker from the given entry
111        String schemaName = getSchemaName( dn );
112
113        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( schemaManager, entry, schemaManager.getRegistries(),
114            schemaName );
115
116        // At this point, the constructed SyntaxChecker has not been checked against the
117        // existing SchemaManager. It will be checked there, if the schema and the
118        // SyntaxChecker are both enabled.
119        Schema schema = schemaManager.getLoadedSchema( schemaName );
120
121        if ( schema.isEnabled() && syntaxChecker.isEnabled() )
122        {
123            if ( schemaManager.add( syntaxChecker ) )
124            {
125                LOG.debug( "Added {} into the enabled schema {}", dn.getName(), schemaName );
126            }
127            else
128            {
129                String msg = I18n.err( I18n.ERR_386, entry.getDn().getName(),
130                    Strings.listToString( schemaManager.getErrors() ) );
131                LOG.info( msg );
132                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
133            }
134        }
135        else
136        {
137            LOG.debug( "The SyntaxChecker {} cannot be added in the disabled schema {}", dn.getName(), schemaName );
138        }
139    }
140
141
142    /**
143     * {@inheritDoc}
144     */
145    @Override
146    public void delete( Entry entry, boolean cascade ) throws LdapException
147    {
148        Dn dn = entry.getDn();
149        Dn parentDn = dn.getParent();
150
151        // The parent Dn must be ou=syntaxcheckers,cn=<schemaName>,ou=schema
152        checkParent( parentDn, schemaManager, SchemaConstants.SYNTAX_CHECKER );
153
154        // Get the SyntaxChecker's instance
155        String schemaName = getSchemaName( entry.getDn() );
156
157        // Get the Schema
158        Schema schema = schemaManager.getLoadedSchema( schemaName );
159
160        if ( schema.isDisabled() )
161        {
162            // The schema is disabled, nothing to do.
163            LOG.debug( "The SyntaxChecker {} cannot be deleted from the disabled schema {}", dn.getName(), schemaName );
164
165            return;
166        }
167
168        // Test that the Oid exists
169        SyntaxChecker syntaxChecker = null;
170
171        try
172        {
173            syntaxChecker = checkSyntaxCheckerOidExists( entry );
174        }
175        catch ( LdapSchemaViolationException lsve )
176        {
177            // The syntaxChecker does not exist
178            syntaxChecker = factory.getSyntaxChecker( schemaManager, entry, schemaManager.getRegistries(), schemaName );
179
180            if ( schemaManager.getRegistries().contains( syntaxChecker ) )
181            {
182                // Remove the syntaxChecker from the schema/SchemaObject Map
183                schemaManager.getRegistries().dissociateFromSchema( syntaxChecker );
184
185                // Ok, we can exit.
186                return;
187            }
188            else
189            {
190                // Ok, definitively an error
191                String msg = I18n.err( I18n.ERR_387, entry.getDn().getName() );
192                LOG.info( msg );
193                throw new LdapSchemaViolationException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
194            }
195        }
196
197        if ( schema.isEnabled() && syntaxChecker.isEnabled() )
198        {
199            if ( schemaManager.delete( syntaxChecker ) )
200            {
201                LOG.debug( "Deleted {} from the enabled schema {}", dn.getName(), schemaName );
202            }
203            else
204            {
205                String msg = I18n.err( I18n.ERR_386, entry.getDn().getName(),
206                    Strings.listToString( schemaManager.getErrors() ) );
207                LOG.info( msg );
208                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
209            }
210        }
211        else
212        {
213            LOG.debug( "The syntaxChecker {} cannot be deleted from the disabled schema {}", dn.getName(), schemaName );
214        }
215    }
216
217
218    /**
219     * {@inheritDoc}
220     */
221    @Override
222    public void rename( Entry entry, Rdn newRdn, boolean cascade ) throws LdapException
223    {
224        String oldOid = getOid( entry );
225        String schemaName = getSchemaName( entry.getDn() );
226
227        if ( schemaManager.getLdapSyntaxRegistry().contains( oldOid ) )
228        {
229            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
230                I18n.err( I18n.ERR_389, oldOid ) );
231        }
232
233        Entry targetEntry = entry.clone();
234        String newOid = newRdn.getValue();
235
236        if ( schemaManager.getSyntaxCheckerRegistry().contains( newOid ) )
237        {
238            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
239                I18n.err( I18n.ERR_390, newOid ) );
240        }
241
242        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
243
244        if ( isSchemaEnabled( schemaName ) )
245        {
246            SyntaxChecker syntaxChecker = factory.getSyntaxChecker( schemaManager, targetEntry, schemaManager
247                .getRegistries(), schemaName );
248            schemaManager.unregisterSyntaxChecker( oldOid );
249            schemaManager.add( syntaxChecker );
250        }
251    }
252
253
254    /**
255     * {@inheritDoc}
256     */
257    @Override
258    public void moveAndRename( Dn oriChildName, Dn newParentName, Rdn newRdn, boolean deleteOldRn,
259        Entry entry, boolean cascade ) throws LdapException
260    {
261        checkNewParent( newParentName );
262        String oldOid = getOid( entry );
263        String oldSchemaName = getSchemaName( oriChildName );
264        String newSchemaName = getSchemaName( newParentName );
265
266        if ( schemaManager.getLdapSyntaxRegistry().contains( oldOid ) )
267        {
268            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
269                I18n.err( I18n.ERR_391, oldOid ) );
270        }
271
272        Entry targetEntry = entry.clone();
273
274        String newOid = newRdn.getValue();
275
276        if ( schemaManager.getSyntaxCheckerRegistry().contains( newOid ) )
277        {
278            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
279                I18n.err( I18n.ERR_390, newOid ) );
280        }
281
282        targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
283        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( schemaManager, targetEntry, schemaManager
284            .getRegistries(), newSchemaName );
285
286        if ( isSchemaEnabled( oldSchemaName ) )
287        {
288            schemaManager.unregisterSyntaxChecker( oldOid );
289        }
290
291        if ( isSchemaEnabled( newSchemaName ) )
292        {
293            schemaManager.add( syntaxChecker );
294        }
295    }
296
297
298    /**
299     * {@inheritDoc}
300     */
301    @Override
302    public void move( Dn oriChildName, Dn newParentName, Entry entry, boolean cascade ) throws LdapException
303    {
304        checkNewParent( newParentName );
305        String oid = getOid( entry );
306        String oldSchemaName = getSchemaName( oriChildName );
307        String newSchemaName = getSchemaName( newParentName );
308
309        if ( schemaManager.getLdapSyntaxRegistry().contains( oid ) )
310        {
311            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
312                I18n.err( I18n.ERR_393, oid ) );
313        }
314
315        SyntaxChecker syntaxChecker = factory.getSyntaxChecker( schemaManager, entry, schemaManager.getRegistries(),
316            newSchemaName );
317
318        if ( isSchemaEnabled( oldSchemaName ) )
319        {
320            schemaManager.unregisterSyntaxChecker( oid );
321        }
322
323        if ( isSchemaEnabled( newSchemaName ) )
324        {
325            schemaManager.add( syntaxChecker );
326        }
327    }
328
329
330    private void checkOidIsUniqueForSyntaxChecker( Entry entry ) throws LdapException
331    {
332        String oid = getOid( entry );
333
334        if ( schemaManager.getNormalizerRegistry().contains( oid ) )
335        {
336            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
337                I18n.err( I18n.ERR_390, oid ) );
338        }
339    }
340
341
342    /**
343     * Check that a SyntaxChecker exists in the SyntaxCheckerRegistry, and if so,
344     * return it.
345     * 
346     * @param entry The entry to check
347     * @return The found SyntaxChecker
348     * @throws LdapException If the SyntaxChecker does not exist
349     */
350    protected SyntaxChecker checkSyntaxCheckerOidExists( Entry entry ) throws LdapException
351    {
352        String oid = getOid( entry );
353
354        if ( schemaManager.getSyntaxCheckerRegistry().contains( oid ) )
355        {
356            return schemaManager.getSyntaxCheckerRegistry().get( oid );
357        }
358        else
359        {
360            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
361                I18n.err( I18n.ERR_336, oid ) );
362        }
363    }
364
365
366    private void checkNewParent( Dn newParent ) throws LdapException
367    {
368        if ( newParent.size() != 3 )
369        {
370            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
371                I18n.err( I18n.ERR_396 ) );
372        }
373
374        Rdn rdn = newParent.getRdn();
375        if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals(
376            SchemaConstants.OU_AT_OID ) )
377        {
378            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
379                I18n.err( I18n.ERR_397 ) );
380        }
381
382        if ( !rdn.getValue().equalsIgnoreCase( SchemaConstants.SYNTAX_CHECKERS_AT ) )
383        {
384            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
385                I18n.err( I18n.ERR_372 ) );
386        }
387    }
388}