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.Normalizer;
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 *
048 *
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public class NormalizerSynchronizer extends AbstractRegistrySynchronizer
052{
053    /** A logger for this class */
054    private static final Logger LOG = LoggerFactory.getLogger( NormalizerSynchronizer.class );
055
056
057    /**
058     * Creates a new instance of NormalizerSynchronizer.
059     *
060     * @param schemaManager The server schemaManager
061     * @throws Exception If the initialization failed
062     */
063    public NormalizerSynchronizer( SchemaManager schemaManager ) throws Exception
064    {
065        super( schemaManager );
066    }
067
068
069    /**
070     * {@inheritDoc}
071     */
072    @Override
073    public boolean modify( ModifyOperationContext modifyContext, Entry targetEntry, boolean cascade )
074        throws LdapException
075    {
076        Dn name = modifyContext.getDn();
077        Entry entry = modifyContext.getEntry();
078        String schemaName = getSchemaName( name );
079        String oldOid = getOid( entry );
080        Normalizer normalizer = factory.getNormalizer( schemaManager, targetEntry, schemaManager.getRegistries(),
081            schemaName );
082
083        if ( isSchemaEnabled( schemaName ) )
084        {
085            normalizer.setSchemaName( schemaName );
086
087            schemaManager.unregisterNormalizer( oldOid );
088            schemaManager.add( normalizer );
089
090            return SCHEMA_MODIFIED;
091        }
092
093        return SCHEMA_UNCHANGED;
094    }
095
096
097    /**
098     * {@inheritDoc}
099     */
100    @Override
101    public void add( Entry entry ) throws LdapException
102    {
103        Dn dn = entry.getDn();
104        Dn parentDn = dn.getParent();
105
106        // The parent Dn must be ou=normalizers,cn=<schemaName>,ou=schema
107        checkParent( parentDn, schemaManager, SchemaConstants.NORMALIZER );
108
109        // The new schemaObject's OID must not already exist
110        checkOidIsUniqueForNormalizer( entry );
111
112        // Build the new Normalizer from the given entry
113        String schemaName = getSchemaName( dn );
114
115        Normalizer normalizer = factory.getNormalizer( schemaManager, entry, schemaManager.getRegistries(), schemaName );
116
117        // At this point, the constructed Normalizer has not been checked against the 
118        // existing Registries. It will be checked there, if the schema and the 
119        // Normalizer are both enabled.
120        Schema schema = schemaManager.getLoadedSchema( schemaName );
121        List<Throwable> errors = new ArrayList<>();
122
123        if ( schema.isEnabled() && normalizer.isEnabled() )
124        {
125            if ( schemaManager.add( normalizer ) )
126            {
127                LOG.debug( "Added {} into the enabled schema {}", dn.getName(), schemaName );
128            }
129            else
130            {
131                String msg = I18n.err( I18n.ERR_364, entry.getDn().getName(),
132                    Strings.listToString( errors ) );
133                LOG.info( msg );
134                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
135            }
136        }
137        else
138        {
139            // At least, we associates the Normalizer with the schema
140            schemaManager.getRegistries().associateWithSchema( normalizer );
141
142            if ( !errors.isEmpty() )
143            {
144                String msg = I18n.err( I18n.ERR_365, entry.getDn().getName(),
145                    Strings.listToString( errors ) );
146
147                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
148            }
149
150            LOG.debug( "The normalizer {} cannot be added in schema {}", dn.getName(), schemaName );
151        }
152    }
153
154
155    /**
156     * {@inheritDoc}
157     */
158    @Override
159    public void delete( Entry entry, boolean cascade ) throws LdapException
160    {
161        Dn dn = entry.getDn();
162        Dn parentDn = dn.getParent();
163
164        // The parent Dn must be ou=normalizers,cn=<schemaName>,ou=schema
165        checkParent( parentDn, schemaManager, SchemaConstants.NORMALIZER );
166
167        // Get the Normalizer from the given entry ( it has been grabbed from the server earlier)
168        String schemaName = getSchemaName( entry.getDn() );
169        Normalizer normalizer = factory.getNormalizer( schemaManager, entry, schemaManager.getRegistries(), schemaName );
170
171        String oid = normalizer.getOid();
172
173        if ( isSchemaEnabled( schemaName ) )
174        {
175            if ( schemaManager.getRegistries().isReferenced( normalizer ) )
176            {
177                String msg = I18n.err( I18n.ERR_366, entry.getDn().getName(), getReferenced( normalizer ) );
178                LOG.warn( msg );
179                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
180            }
181
182            // As the normalizer has the same OID than its attached MR, it won't
183            // be loaded into the schemaManager if it's disabled
184            deleteFromSchema( normalizer, schemaName );
185        }
186
187        if ( schemaManager.getNormalizerRegistry().contains( oid ) )
188        {
189            schemaManager.unregisterNormalizer( oid );
190            LOG.debug( "Removed {} from the enabled schema {}", normalizer, schemaName );
191        }
192        else
193        {
194            LOG.debug( "Removed {} from the enabled schema {}", normalizer, schemaName );
195        }
196    }
197
198
199    /**
200     * {@inheritDoc}
201     */
202    @Override
203    public void rename( Entry entry, Rdn newRdn, boolean cascade ) throws LdapException
204    {
205        String oldOid = getOid( entry );
206        String schemaName = getSchemaName( entry.getDn() );
207
208        if ( schemaManager.getMatchingRuleRegistry().contains( oldOid ) )
209        {
210            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
211                I18n.err( I18n.ERR_367, oldOid ) );
212        }
213
214        String newOid = newRdn.getValue();
215        checkOidIsUniqueForNormalizer( newOid );
216
217        if ( isSchemaEnabled( schemaName ) )
218        {
219            // Inject the new OID
220            Entry targetEntry = entry.clone();
221            targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
222
223            // Inject the new Dn
224            Dn newDn = targetEntry.getDn().getParent();
225            newDn = newDn.add( newRdn );
226            targetEntry.setDn( newDn );
227
228            Normalizer normalizer = factory.getNormalizer( schemaManager, targetEntry, schemaManager.getRegistries(),
229                schemaName );
230            schemaManager.unregisterNormalizer( oldOid );
231            schemaManager.add( normalizer );
232        }
233    }
234
235
236    /**
237     * {@inheritDoc}
238     */
239    @Override
240    public void moveAndRename( Dn oriChildName, Dn newParentName, Rdn newRdn, boolean deleteOldRn,
241        Entry entry, boolean cascade ) throws LdapException
242    {
243        checkNewParent( newParentName );
244        String oldOid = getOid( entry );
245        String oldSchemaName = getSchemaName( oriChildName );
246        String newSchemaName = getSchemaName( newParentName );
247
248        if ( schemaManager.getMatchingRuleRegistry().contains( oldOid ) )
249        {
250            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
251                I18n.err( I18n.ERR_367, oldOid ) );
252        }
253
254        String oid = newRdn.getValue();
255        checkOidIsUniqueForNormalizer( oid );
256        Normalizer normalizer = factory.getNormalizer( schemaManager, entry, schemaManager.getRegistries(),
257            newSchemaName );
258
259        if ( isSchemaEnabled( oldSchemaName ) )
260        {
261            schemaManager.unregisterNormalizer( oldOid );
262        }
263
264        if ( isSchemaEnabled( newSchemaName ) )
265        {
266            schemaManager.add( normalizer );
267        }
268    }
269
270
271    /**
272     * {@inheritDoc}
273     */
274    @Override
275    public void move( Dn oriChildName, Dn newParentName, Entry entry, boolean cascade ) throws LdapException
276    {
277        checkNewParent( newParentName );
278        String oid = getOid( entry );
279        String oldSchemaName = getSchemaName( oriChildName );
280        String newSchemaName = getSchemaName( newParentName );
281
282        if ( schemaManager.getMatchingRuleRegistry().contains( oid ) )
283        {
284            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
285                I18n.err( I18n.ERR_368, oid ) );
286        }
287
288        Normalizer normalizer = factory.getNormalizer( schemaManager, entry, schemaManager.getRegistries(),
289            newSchemaName );
290
291        if ( isSchemaEnabled( oldSchemaName ) )
292        {
293            schemaManager.unregisterNormalizer( oid );
294        }
295
296        if ( isSchemaEnabled( newSchemaName ) )
297        {
298            schemaManager.add( normalizer );
299        }
300    }
301
302
303    private void checkOidIsUniqueForNormalizer( String oid ) throws LdapException
304    {
305        if ( schemaManager.getNormalizerRegistry().contains( oid ) )
306        {
307            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
308                I18n.err( I18n.ERR_369, oid ) );
309        }
310    }
311
312
313    private void checkOidIsUniqueForNormalizer( Entry entry ) throws LdapException
314    {
315        String oid = getOid( entry );
316
317        if ( schemaManager.getNormalizerRegistry().contains( oid ) )
318        {
319            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
320                I18n.err( I18n.ERR_369, oid ) );
321        }
322    }
323
324
325    private void checkNewParent( Dn newParent ) throws LdapException
326    {
327        if ( newParent.size() != 3 )
328        {
329            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_370 ) );
330        }
331
332        Rdn rdn = newParent.getRdn();
333
334        if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals(
335            SchemaConstants.OU_AT_OID ) )
336        {
337            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_371 ) );
338        }
339
340        if ( !rdn.getValue().equalsIgnoreCase( SchemaConstants.NORMALIZERS_AT ) )
341        {
342            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_372 ) );
343        }
344    }
345}