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