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.HashMap;
024import java.util.HashSet;
025import java.util.Map;
026import java.util.Set;
027
028import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
029import org.apache.directory.api.ldap.model.constants.SchemaConstants;
030import org.apache.directory.api.ldap.model.entry.Attribute;
031import org.apache.directory.api.ldap.model.entry.Entry;
032import org.apache.directory.api.ldap.model.exception.LdapException;
033import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
034import org.apache.directory.api.ldap.model.exception.LdapOtherException;
035import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException;
036import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
037import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
038import org.apache.directory.api.ldap.model.name.Dn;
039import org.apache.directory.api.ldap.model.name.Rdn;
040import org.apache.directory.api.ldap.model.schema.AttributeType;
041import org.apache.directory.api.ldap.model.schema.SchemaManager;
042import org.apache.directory.api.ldap.model.schema.SchemaObject;
043import org.apache.directory.api.ldap.model.schema.SchemaObjectWrapper;
044import org.apache.directory.api.ldap.model.schema.registries.Schema;
045import org.apache.directory.api.ldap.schema.loader.SchemaEntityFactory;
046import org.apache.directory.api.util.Strings;
047import org.apache.directory.server.i18n.I18n;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051
052/**
053 * An abstract registry synchronizer with some reused functionality.
054 *
055 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
056 */
057public abstract class AbstractRegistrySynchronizer implements RegistrySynchronizer
058{
059    /** A logger for this class */
060    private static final Logger LOG = LoggerFactory.getLogger( AbstractRegistrySynchronizer.class );
061
062    /** The global SchemaManager */
063    protected final SchemaManager schemaManager;
064
065    /** The m-oid AttributeType */
066    protected final AttributeType moidAT;
067
068    /** The Schema objetc factory */
069    protected final SchemaEntityFactory factory;
070
071    /** A map associating a SchemaObject type with its path on the partition*/
072    private static final Map<String, String> OBJECT_TYPE_TO_PATH = new HashMap<>();
073
074    static
075    {
076        // Removed the starting 'ou=' from the paths
077        OBJECT_TYPE_TO_PATH.put( SchemaConstants.ATTRIBUTE_TYPE, SchemaConstants.ATTRIBUTE_TYPES_PATH.substring( 3 ) );
078        OBJECT_TYPE_TO_PATH.put( SchemaConstants.COMPARATOR, SchemaConstants.COMPARATORS_PATH.substring( 3 ) );
079        OBJECT_TYPE_TO_PATH
080            .put( SchemaConstants.DIT_CONTENT_RULE, SchemaConstants.DIT_CONTENT_RULES_PATH.substring( 3 ) );
081        OBJECT_TYPE_TO_PATH.put( SchemaConstants.DIT_STRUCTURE_RULE,
082            SchemaConstants.DIT_STRUCTURE_RULES_PATH.substring( 3 ) );
083        OBJECT_TYPE_TO_PATH.put( SchemaConstants.MATCHING_RULE, SchemaConstants.MATCHING_RULES_PATH.substring( 3 ) );
084        OBJECT_TYPE_TO_PATH.put( SchemaConstants.MATCHING_RULE_USE,
085            SchemaConstants.MATCHING_RULE_USE_PATH.substring( 3 ) );
086        OBJECT_TYPE_TO_PATH.put( SchemaConstants.NAME_FORM, SchemaConstants.NAME_FORMS_PATH.substring( 3 ) );
087        OBJECT_TYPE_TO_PATH.put( SchemaConstants.NORMALIZER, SchemaConstants.NORMALIZERS_PATH.substring( 3 ) );
088        OBJECT_TYPE_TO_PATH.put( SchemaConstants.OBJECT_CLASS, SchemaConstants.OBJECT_CLASSES_PATH.substring( 3 ) );
089        OBJECT_TYPE_TO_PATH.put( SchemaConstants.SYNTAX, SchemaConstants.SYNTAXES_PATH.substring( 3 ) );
090        OBJECT_TYPE_TO_PATH.put( SchemaConstants.SYNTAX_CHECKER, SchemaConstants.SYNTAX_CHECKERS_PATH.substring( 3 ) );
091    }
092
093
094    protected AbstractRegistrySynchronizer( SchemaManager schemaManager )
095    {
096        this.schemaManager = schemaManager;
097        moidAT = schemaManager.getAttributeType( MetaSchemaConstants.M_OID_AT );
098        factory = new SchemaEntityFactory();
099    }
100
101
102    /**
103     * Tells if the schema the Dn references is loaded or not
104     *
105     * @param dn The SchemaObject's Dn
106     * @return true if the schema is loaded
107     * @throws LdapException If The Dn is not a SchemaObject Dn
108     */
109    protected boolean isSchemaLoaded( Dn dn ) throws LdapException
110    {
111        return schemaManager.isSchemaLoaded( getSchemaName( dn ) );
112    }
113
114
115    /**
116     * Tells if the schemaName is loaded or not
117     *
118     * @param schemaName The schema we want to check
119     * @return true if the schema is loaded
120     */
121    protected boolean isSchemaLoaded( String schemaName )
122    {
123        return schemaManager.isSchemaLoaded( schemaName );
124    }
125
126
127    /**
128     * Tells if a schema is loaded and enabled
129     *
130     * @param schemaName The schema we want to check
131     * @return true if the schema is loaded and enabled, false otherwise
132     */
133    protected boolean isSchemaEnabled( String schemaName )
134    {
135        Schema schema = schemaManager.getLoadedSchema( schemaName );
136
137        return ( schema != null ) && schema.isEnabled();
138    }
139
140
141    /**
142     * Exctract the schema name from the Dn. It is supposed to be the
143     * second Rdn in the dn :
144     * <pre>
145     * ou=schema, cn=MySchema, ...
146     * </pre>
147     * Here, the schemaName is MySchema
148     *
149     * @param dn The Dn we want to get the schema name from
150     * @return The schema name
151     * @throws LdapException If we got an error
152     */
153    protected String getSchemaName( Dn dn ) throws LdapException
154    {
155        int size = dn.size();
156
157        if ( size < 2 )
158        {
159            throw new LdapInvalidDnException( I18n.err( I18n.ERR_276 ) );
160        }
161
162        Rdn rdn = dn.getRdn( size - 2 );
163
164        return Strings.trim( rdn.getAva().getValue().getNormalized() );
165    }
166
167
168    protected void checkOidIsUnique( Entry entry ) throws LdapException
169    {
170        String oid = getOid( entry );
171
172        if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
173        {
174            throw new LdapOtherException( I18n.err( I18n.ERR_335, oid ) );
175        }
176    }
177
178
179    /**
180     * Check that a SchemaObject exists in the global OidRegsitry, and if so,
181     * return it.
182     * 
183     * @param entry The Entry we want to verify the existence of
184     * @return The found SchemaObject
185     * @throws LdapException  If the check failed
186     */
187    protected SchemaObject checkOidExists( Entry entry ) throws LdapException
188    {
189        String oid = getOid( entry );
190
191        if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
192        {
193            return schemaManager.getGlobalOidRegistry().getSchemaObject( oid );
194        }
195        else
196        {
197            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
198                I18n.err( I18n.ERR_336, oid ) );
199        }
200    }
201
202
203    /**
204     * Checks that the parent Dn is a valid Dn
205     * 
206     * @param newParent The parent DN to check
207     * @param schemaManager The SchemaManager instance
208     * @param objectType The ObjectType to check
209     * @throws LdapException  If the deletion failed
210     */
211    protected void checkParent( Dn newParent, SchemaManager schemaManager, String objectType ) throws LdapException
212    {
213        if ( newParent.size() != 3 )
214        {
215            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_337 ) );
216        }
217
218        Rdn rdn = newParent.getRdn();
219
220        if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() )
221            .equals( SchemaConstants.OU_AT_OID ) )
222        {
223            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
224                I18n.err( I18n.ERR_338, objectType ) );
225        }
226
227        if ( !rdn.getValue().equalsIgnoreCase( OBJECT_TYPE_TO_PATH.get( objectType ) ) )
228        {
229            throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
230                I18n.err( I18n.ERR_339, objectType, OBJECT_TYPE_TO_PATH.get( objectType ) ) );
231        }
232    }
233
234
235    protected void checkOidIsUnique( SchemaObject schemaObject ) throws LdapException
236    {
237        String oid = schemaObject.getOid();
238
239        if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
240        {
241            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
242                I18n.err( I18n.ERR_335, oid ) );
243        }
244    }
245
246
247    protected void checkOidIsUnique( String oid ) throws LdapException
248    {
249        if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
250        {
251            throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
252                I18n.err( I18n.ERR_335, oid ) );
253        }
254    }
255
256
257    /**
258     * Add a new SchemaObject to the schema content, assuming that
259     * it has an associated schema and that this schema is loaded
260     * 
261     * @param schemaObject The SchemaObject to add
262     * @param schemaName The Schema we want the SchemaObject to be added in
263     * @throws LdapException  If the addition failed
264     */
265    protected void addToSchema( SchemaObject schemaObject, String schemaName ) throws LdapException
266    {
267        if ( isSchemaLoaded( schemaName ) )
268        {
269            // Get the set of all the SchemaObjects associated with this schema
270            Set<SchemaObjectWrapper> schemaObjects = schemaManager.getRegistries().getObjectBySchemaName()
271                .get( schemaName );
272
273            if ( schemaObjects == null )
274            {
275                // TODO : this should never happen...
276                schemaObjects = schemaManager.getRegistries().addSchema( schemaName );
277            }
278
279            SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
280
281            if ( schemaObjects.contains( schemaObjectWrapper ) )
282            {
283                String msg = I18n.err( I18n.ERR_341, schemaObject.getName(), schemaName );
284                LOG.warn( msg );
285
286                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
287            }
288
289            schemaObjects.add( schemaObjectWrapper );
290            LOG.debug( "The SchemaObject {} has been added to the schema {}", schemaObject, schemaName );
291        }
292        else
293        {
294            String msg = I18n.err( I18n.ERR_342, schemaObject.getName(), schemaName );
295            LOG.warn( msg );
296
297            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
298        }
299    }
300
301
302    /**
303     * Delete a SchemaObject from the schema registry, assuming that
304     * it has an associated schema and that this schema is loaded
305     * 
306     * @param schemaObject The SchemaObject to delete
307     * @param schemaName The Schema we want the SchemaObject to be deleted from
308     * @throws LdapException  If the deletion failed
309     */
310    protected void deleteFromSchema( SchemaObject schemaObject, String schemaName ) throws LdapException
311    {
312        if ( isSchemaLoaded( schemaName ) )
313        {
314            Set<SchemaObjectWrapper> schemaObjects = schemaManager.getRegistries().getObjectBySchemaName()
315                .get( schemaName );
316
317            SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
318
319            if ( !schemaObjects.contains( schemaObjectWrapper ) )
320            {
321                String msg = I18n.err( I18n.ERR_343, schemaObject.getName(), schemaName );
322                LOG.warn( msg );
323
324                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
325            }
326
327            schemaObjects.remove( schemaObjectWrapper );
328            LOG.debug( "The SchemaObject {} has been removed from the schema {}", schemaObject, schemaName );
329        }
330        else
331        {
332            String msg = I18n.err( I18n.ERR_342, schemaObject.getName(), schemaName );
333            LOG.warn( msg );
334
335            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
336        }
337    }
338
339
340    protected Set<String> getOids( Set<Entry> results )
341    {
342        Set<String> oids = new HashSet<>( results.size() );
343
344        for ( Entry result : results )
345        {
346            Dn dn = result.getDn();
347            oids.add( dn.getRdn().getValue() );
348        }
349
350        return oids;
351    }
352
353
354    protected String getOid( Entry entry ) throws LdapException
355    {
356        Attribute oid = entry.get( moidAT );
357
358        if ( oid == null )
359        {
360            return null;
361        }
362
363        return oid.getString();
364    }
365
366
367    /**
368     * Unregister a SchemaObject's OID from the associated oidRegistry
369     *
370     * @param obj The SchemaObject to unregister
371     * @throws LdapException If the unregistering failed
372     */
373    protected void unregisterOids( SchemaObject obj ) throws LdapException
374    {
375        schemaManager.getGlobalOidRegistry().unregister( obj.getOid() );
376    }
377
378
379    /**
380     * Register a SchemaObject's OID in the associated oidRegistry
381     *
382     * @param obj The SchemaObject to register
383     * @throws LdapException If the registering failed
384     */
385    protected void registerOids( SchemaObject obj ) throws LdapException
386    {
387        schemaManager.getGlobalOidRegistry().register( obj );
388    }
389
390
391    /**
392     * Get a String containing the SchemaObjects referencing the
393     * given ShcemaObject
394     *
395     * @param schemaObject The SchemaObject we want the referencing SchemaObjects for
396     * @return A String containing all the SchemaObjects referencing the give SchemaObject
397     */
398    protected String getReferenced( SchemaObject schemaObject )
399    {
400        StringBuilder sb = new StringBuilder();
401
402        Set<SchemaObjectWrapper> useds = schemaManager.getRegistries().getUsedBy( schemaObject );
403
404        for ( SchemaObjectWrapper used : useds )
405        {
406            sb.append( used );
407            sb.append( '\n' );
408        }
409
410        return sb.toString();
411    }
412}