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     */
020    package org.apache.directory.server.core.schema.registries.synchronizers;
021    
022    
023    import java.util.HashMap;
024    import java.util.HashSet;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import javax.naming.NamingException;
029    
030    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
031    import org.apache.directory.server.i18n.I18n;
032    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
033    import org.apache.directory.shared.ldap.constants.SchemaConstants;
034    import org.apache.directory.shared.ldap.entry.EntryAttribute;
035    import org.apache.directory.shared.ldap.entry.ServerEntry;
036    import org.apache.directory.shared.ldap.exception.LdapException;
037    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
038    import org.apache.directory.shared.ldap.exception.LdapOtherException;
039    import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
040    import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
041    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
042    import org.apache.directory.shared.ldap.name.DN;
043    import org.apache.directory.shared.ldap.name.RDN;
044    import org.apache.directory.shared.ldap.schema.AttributeType;
045    import org.apache.directory.shared.ldap.schema.SchemaManager;
046    import org.apache.directory.shared.ldap.schema.SchemaObject;
047    import org.apache.directory.shared.ldap.schema.SchemaObjectWrapper;
048    import org.apache.directory.shared.ldap.schema.loader.ldif.SchemaEntityFactory;
049    import org.apache.directory.shared.ldap.schema.registries.Schema;
050    import org.slf4j.Logger;
051    import org.slf4j.LoggerFactory;
052    
053    
054    /**
055     * An abstract registry synchronizer with some reused functionality.
056     *
057     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
058     * @version $Rev$
059     */
060    public abstract class AbstractRegistrySynchronizer implements RegistrySynchronizer
061    {
062        /** A logger for this class */
063        private static final Logger LOG = LoggerFactory.getLogger( AbstractRegistrySynchronizer.class );
064    
065        /** The global SchemaManager */
066        protected final SchemaManager schemaManager;
067        
068        /** The m-oid AttributeType */
069        protected final AttributeType m_oidAT;
070        
071        /** The Schema objetc factory */
072        protected final SchemaEntityFactory factory;
073        
074        /** A map associating a SchemaObject type with its path on the partition*/
075        private final static Map<String, String> OBJECT_TYPE_TO_PATH = new HashMap<String, String>();
076    
077        static
078        {
079            // Removed the starting 'ou=' from the paths
080            OBJECT_TYPE_TO_PATH.put( SchemaConstants.ATTRIBUTE_TYPE, SchemaConstants.ATTRIBUTES_TYPE_PATH.substring( 3 ) ); 
081            OBJECT_TYPE_TO_PATH.put( SchemaConstants.COMPARATOR, SchemaConstants.COMPARATORS_PATH.substring( 3 ) );
082            OBJECT_TYPE_TO_PATH.put( SchemaConstants.DIT_CONTENT_RULE, SchemaConstants.DIT_CONTENT_RULES_PATH.substring( 3 ) );
083            OBJECT_TYPE_TO_PATH.put( SchemaConstants.DIT_STRUCTURE_RULE, SchemaConstants.DIT_STRUCTURE_RULES_PATH.substring( 3 ) );
084            OBJECT_TYPE_TO_PATH.put( SchemaConstants.MATCHING_RULE, SchemaConstants.MATCHING_RULES_PATH.substring( 3 ) );
085            OBJECT_TYPE_TO_PATH.put( SchemaConstants.MATCHING_RULE_USE, 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 ) throws Exception
095        {
096            this.schemaManager = schemaManager;
097            m_oidAT = schemaManager.lookupAttributeTypeRegistry( 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 Exception If The DN is not a SchemaObject DN
108         */
109        protected boolean isSchemaLoaded( DN dn ) throws Exception
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 NamingException If we got an error
152         */
153        protected String getSchemaName( DN dn ) throws NamingException
154        {
155            if ( dn.size() < 2 )
156            {
157                throw new NamingException( I18n.err( I18n.ERR_276 ) );
158            }
159            
160            RDN rdn = dn.getRdn( 1 );
161            return ( String ) rdn.getNormValue();
162        }
163    
164    
165        protected void checkOidIsUnique( ServerEntry entry ) throws Exception
166        {
167            String oid = getOid( entry );
168    
169            if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
170            {
171                throw new LdapOtherException( I18n.err( I18n.ERR_335, oid ) );
172            }
173        }
174    
175        
176        /**
177         * Check that a SchemaObject exists in the global OidRegsitry, and if so,
178         * return it.
179         */
180        protected SchemaObject checkOidExists( ServerEntry entry ) throws Exception
181        {
182            String oid = getOid( entry );
183    
184            if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
185            {
186                return schemaManager.getGlobalOidRegistry().getSchemaObject( oid );
187            }
188            else
189            {
190                throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
191                    I18n.err( I18n.ERR_336, oid ) );
192            }
193        }
194    
195        
196        /**
197         * Checks that the parent DN is a valid DN
198         */
199        protected void checkParent( DN newParent, SchemaManager schemaManager, String objectType ) throws LdapException
200        {
201            if ( newParent.size() != 3 )
202            {
203                throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_337 ) );
204            }
205            
206            RDN rdn = newParent.getRdn();
207            
208            if ( ! schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals( SchemaConstants.OU_AT_OID ) )
209            {
210                throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, 
211                    I18n.err( I18n.ERR_338, objectType ) );
212            }
213            
214            if ( ! ( ( String ) rdn.getNormValue() ).equalsIgnoreCase( OBJECT_TYPE_TO_PATH.get( objectType ) ) )
215            {
216                throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, 
217                    I18n.err( I18n.ERR_339, objectType,  OBJECT_TYPE_TO_PATH.get( objectType ) ) );
218            }
219        }
220    
221        protected void checkOidIsUnique( SchemaObject schemaObject ) throws Exception
222        {
223            String oid = schemaObject.getOid();
224    
225            if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
226            {
227                throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
228                    I18n.err( I18n.ERR_335, oid ) );
229            }
230        }
231    
232    
233        protected void checkOidIsUnique( String oid ) throws Exception
234        {
235            if ( schemaManager.getGlobalOidRegistry().contains( oid ) )
236            {
237                throw new LdapSchemaViolationException( ResultCodeEnum.OTHER,
238                    I18n.err( I18n.ERR_335, oid ) );
239            }
240        }
241    
242        
243        /**
244         * Add a new SchemaObject to the schema content, assuming that
245         * it has an associated schema and that this schema is loaded
246         */
247        protected void addToSchema( SchemaObject schemaObject, String schemaName ) throws Exception
248        {
249            if ( isSchemaLoaded( schemaName ) )
250            {
251                // Get the set of all the SchemaObjects associated with this schema
252                Set<SchemaObjectWrapper> schemaObjects = schemaManager.getRegistries().getObjectBySchemaName().get( schemaName );
253                
254                if ( schemaObjects == null )
255                {
256                    // TODO : this should never happen...
257                    schemaObjects = schemaManager.getRegistries().addSchema( schemaName );
258                }
259                
260                SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
261                
262                if ( schemaObjects.contains( schemaObjectWrapper ) )
263                {
264                    String msg = I18n.err( I18n.ERR_341, schemaObject.getName(), schemaName );
265                    LOG.warn( msg );
266                
267                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
268                }
269                
270                schemaObjects.add( schemaObjectWrapper );
271                LOG.debug( "The SchemaObject {} has been added to the schema {}", schemaObject, schemaName   );
272            }
273            else
274            {
275                String msg = I18n.err( I18n.ERR_342, schemaObject.getName(), schemaName );
276                LOG.warn( msg );
277            
278                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
279            }
280        }
281    
282        
283        
284        
285        /**
286         * Delete a SchemaObject from the schema registry, assuming that
287         * it has an associated schema and that this schema is loaded
288         */
289        protected void deleteFromSchema( SchemaObject schemaObject, String schemaName ) throws Exception
290        {
291            if ( isSchemaLoaded( schemaName ) )
292            {
293                Set<SchemaObjectWrapper> schemaObjects = schemaManager.getRegistries().getObjectBySchemaName().get( schemaName );
294    
295                SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
296                
297                if ( !schemaObjects.contains( schemaObjectWrapper ) )
298                {
299                    String msg = I18n.err( I18n.ERR_343, schemaObject.getName(), schemaName );
300                    LOG.warn( msg );
301                
302                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
303                }
304                
305                schemaObjects.remove( schemaObjectWrapper );
306                LOG.debug(  "The SchemaObject {} has been removed from the schema {}", schemaObject, schemaName );
307            }
308            else
309            {
310                String msg = I18n.err( I18n.ERR_342, schemaObject.getName(), schemaName );
311                LOG.warn( msg );
312            
313                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
314            }
315        }
316    
317        
318        /**
319         * {@inheritDoc}
320         */
321        public abstract boolean modify( ModifyOperationContext opContext, ServerEntry targetEntry, boolean cascade ) 
322            throws Exception;
323        
324        
325        /*public final boolean modify( DN name, ModificationOperation modOp, ServerEntry mods, ServerEntry entry, ServerEntry targetEntry, 
326            boolean cascade ) throws Exception
327        {
328            return modify( name, entry, targetEntry, cascade );
329        }
330    
331    
332        public final boolean modify( DN name, List<Modification> mods, ServerEntry entry,
333            ServerEntry targetEntry, boolean cascade ) throws Exception
334        {
335            return modify( name, entry, targetEntry, cascade );
336        }
337        */
338        
339        
340        protected Set<String> getOids( Set<ServerEntry> results ) throws Exception
341        {
342            Set<String> oids = new HashSet<String>( results.size() );
343            
344            for ( ServerEntry result : results )
345            {
346                DN dn = result.getDn();
347                dn.normalize( schemaManager.getNormalizerMapping() );
348                oids.add( ( String ) dn.getRdn().getNormValue() );
349            }
350            
351            return oids;
352        }
353        
354        
355        protected String getOid( ServerEntry entry ) throws Exception
356        {
357            EntryAttribute oid = entry.get( m_oidAT );
358            
359            if ( oid == null )
360            {
361                return null;
362            }
363            
364            return oid.getString();
365        }
366        
367        
368        /**
369         * Unregister a SchemaObject's OID from the associated oidRegistry
370         * 
371         * @param obj The SchemaObject to unregister
372         * @throws Exception If the unregistering failed
373         */
374        protected void unregisterOids( SchemaObject obj ) throws Exception
375        {
376            schemaManager.getGlobalOidRegistry().unregister( obj.getOid() );
377        }
378        
379        
380        /**
381         * Register a SchemaObject's OID in the associated oidRegistry
382         * 
383         * @param obj The SchemaObject to register
384         * @throws Exception If the registering failed
385         */
386        protected void registerOids( SchemaObject obj ) throws Exception
387        {
388            schemaManager.getGlobalOidRegistry().register( obj );
389        }
390        
391        
392        /**
393         * Get a String containing the SchemaObjects referencing the 
394         * given ShcemaObject
395         *
396         * @param schemaObject The SchemaObject we want the referencing SchemaObjects for
397         * @return A String containing all the SchemaObjects referencing the give SchemaObject
398         */
399        protected String getReferenced( SchemaObject schemaObject )
400        {
401            StringBuilder sb = new StringBuilder();
402            
403            Set<SchemaObjectWrapper> useds = schemaManager.getRegistries().getUsedBy( schemaObject );
404            
405            for ( SchemaObjectWrapper used:useds )
406            {
407                sb.append( used );
408                sb.append( '\n' );
409            }
410            
411            return sb.toString();
412        }
413    }