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 org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
024    import org.apache.directory.server.i18n.I18n;
025    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
026    import org.apache.directory.shared.ldap.constants.SchemaConstants;
027    import org.apache.directory.shared.ldap.entry.ServerEntry;
028    import org.apache.directory.shared.ldap.exception.LdapException;
029    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
030    import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
031    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
032    import org.apache.directory.shared.ldap.name.DN;
033    import org.apache.directory.shared.ldap.name.RDN;
034    import org.apache.directory.shared.ldap.schema.ObjectClass;
035    import org.apache.directory.shared.ldap.schema.SchemaManager;
036    import org.apache.directory.shared.ldap.schema.registries.Schema;
037    import org.apache.directory.shared.ldap.util.StringTools;
038    import org.slf4j.Logger;
039    import org.slf4j.LoggerFactory;
040    
041    
042    /**
043     * 
044     *
045     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
046     * @version $Rev$, $Date$
047     */
048    public class ObjectClassSynchronizer extends AbstractRegistrySynchronizer
049    {
050        /** A logger for this class */
051        private static final Logger LOG = LoggerFactory.getLogger( ObjectClassSynchronizer.class );
052    
053    
054        /**
055         * Creates a new instance of ObjectClassSynchronizer.
056         *
057         * @param schemaManager The global schemaManager
058         * @throws Exception If the initialization failed
059         */
060        public ObjectClassSynchronizer( SchemaManager schemaManager ) throws Exception
061        {
062            super( schemaManager );
063        }
064    
065    
066        /**
067         * {@inheritDoc}
068         */
069        public boolean modify( ModifyOperationContext opContext, ServerEntry targetEntry, boolean cascade )
070            throws Exception
071        {
072            DN name = opContext.getDn();
073            ServerEntry entry = opContext.getEntry();
074            String oid = getOid( entry );
075            ObjectClass oc = factory.getObjectClass( schemaManager, targetEntry, schemaManager.getRegistries(),
076                getSchemaName( name ) );
077            String schemaName = getSchemaName( entry.getDn() );
078    
079            if ( isSchemaEnabled( schemaName ) )
080            {
081                schemaManager.unregisterObjectClass( oid );
082                schemaManager.add( oc );
083    
084                return SCHEMA_MODIFIED;
085            }
086    
087            return SCHEMA_UNCHANGED;
088        }
089    
090    
091        /**
092         * {@inheritDoc}
093         */
094        public void add( ServerEntry entry ) throws Exception
095        {
096            DN dn = entry.getDn();
097            DN parentDn = ( DN ) dn.clone();
098            parentDn.remove( parentDn.size() - 1 );
099    
100            // The parent DN must be ou=objectclasses,cn=<schemaName>,ou=schema
101            checkParent( parentDn, schemaManager, SchemaConstants.OBJECT_CLASS );
102    
103            // The new schemaObject's OID must not already exist
104            checkOidIsUnique( entry );
105    
106            // Build the new ObjectClass from the given entry
107            String schemaName = getSchemaName( dn );
108    
109            ObjectClass objectClass = factory.getObjectClass( schemaManager, entry, schemaManager.getRegistries(),
110                schemaName );
111    
112            // At this point, the constructed ObjectClass has not been checked against the 
113            // existing Registries. It may be broken (missing SUP, or such), it will be checked
114            // there, if the schema and the ObjectClass are both enabled.
115            Schema schema = schemaManager.getLoadedSchema( schemaName );
116    
117            if ( schema.isEnabled() && objectClass.isEnabled() )
118            {
119                if ( schemaManager.add( objectClass ) )
120                {
121                    LOG.debug( "Added {} into the enabled schema {}", dn.getName(), schemaName );
122                }
123                else
124                {
125                    // We have some error : reject the addition and get out
126                    String msg = I18n.err( I18n.ERR_373, entry.getDn().getName(), 
127                        StringTools.listToString( schemaManager.getErrors() ) );
128                    LOG.info( msg );
129                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
130                }
131    
132            }
133            else
134            {
135                LOG.debug( "The ObjectClass {} cannot be added in the disabled schema {}.", objectClass, schemaName );
136            }
137        }
138    
139    
140        /**
141         * {@inheritDoc}
142         */
143        public void delete( ServerEntry entry, boolean cascade ) throws Exception
144        {
145            DN dn = entry.getDn();
146            DN parentDn = ( DN ) dn.clone();
147            parentDn.remove( parentDn.size() - 1 );
148    
149            // The parent DN must be ou=objectclasses,cn=<schemaName>,ou=schema
150            checkParent( parentDn, schemaManager, SchemaConstants.OBJECT_CLASS );
151    
152            // Get the ObjectClass from the given entry ( it has been grabbed from the server earlier)
153            String schemaName = getSchemaName( entry.getDn() );
154            
155            // Get the schema 
156            Schema schema = schemaManager.getLoadedSchema( schemaName );
157    
158            if ( schema.isDisabled() )
159            {
160                // The schema is disabled, nothing to do.
161                LOG.debug( "The ObjectClass {} cannot be removed from the disabled schema {}.", 
162                    dn.getName(), schemaName );
163                
164                return;
165            }
166            
167            // Test that the Oid exists
168            ObjectClass objectClass = ( ObjectClass ) checkOidExists( entry );
169    
170            if ( schema.isEnabled() && objectClass.isEnabled() )
171            {
172                if ( schemaManager.delete( objectClass ) )
173                {
174                    LOG.debug( "Removed {} from the schema {}", objectClass, schemaName );
175                }
176                else
177                {
178                    // We have some error : reject the deletion and get out
179                    String msg = I18n.err( I18n.ERR_374, entry.getDn().getName(),
180                        StringTools.listToString( schemaManager.getErrors() ) );
181                    LOG.info( msg );
182                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
183                }
184            }
185            else
186            {
187                LOG.debug( "Removed {} from the disabled schema {}", objectClass, schemaName );
188            }
189        }
190    
191    
192        /**
193         * {@inheritDoc}
194         */
195        public void rename( ServerEntry entry, RDN newRdn, boolean cascade ) throws Exception
196        {
197            String schemaName = getSchemaName( entry.getDn() );
198            ObjectClass oldOc = factory.getObjectClass( schemaManager, entry, schemaManager.getRegistries(), schemaName );
199    
200            // Dependency constraints are not managed by this class
201            //        Set<ServerEntry> dependees = dao.listObjectClassDependents( oldOc );
202            //        
203            //        if ( dependees != null && dependees.size() > 0 )
204            //        {
205            //            throw new LdapUnwillingToPerformException( "The objectClass with OID " + oldOc.getOid()
206            //                + " cannot be deleted until all entities" 
207            //                + " using this objectClass have also been deleted.  The following dependees exist: " 
208            //                + getOids( dependees ), 
209            //                ResultCodeEnum.UNWILLING_TO_PERFORM );
210            //        }
211    
212            ServerEntry targetEntry = ( ServerEntry ) entry.clone();
213            String newOid = ( String ) newRdn.getNormValue();
214            targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
215    
216            // Inject the new DN
217            DN newDn = new DN( targetEntry.getDn() );
218            newDn.remove( newDn.size() - 1 );
219            newDn.add( newRdn );
220    
221            checkOidIsUnique( newOid );
222            ObjectClass oc = factory.getObjectClass( schemaManager, targetEntry, schemaManager.getRegistries(), schemaName );
223    
224            if ( isSchemaEnabled( schemaName ) )
225            {
226                // Check that the entry has no descendant
227                if ( schemaManager.getObjectClassRegistry().hasDescendants( oldOc.getOid() ) )
228                {
229                    String msg = I18n.err( I18n.ERR_375, entry.getDn().getName(), newDn );
230    
231                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
232                }
233    
234                schemaManager.unregisterObjectClass( oldOc.getOid() );
235                schemaManager.add( oc );
236            }
237            else
238            {
239                unregisterOids( oldOc );
240                registerOids( oc );
241            }
242        }
243    
244    
245        public void moveAndRename( DN oriChildName, DN newParentName, RDN newRdn, boolean deleteOldRn,
246            ServerEntry entry, boolean cascade ) throws Exception
247        {
248            checkNewParent( newParentName );
249            String oldSchemaName = getSchemaName( oriChildName );
250            ObjectClass oldOc = factory.getObjectClass( schemaManager, entry, schemaManager.getRegistries(), oldSchemaName );
251    
252            // this class does not handle dependencies
253            //        Set<ServerEntry> dependees = dao.listObjectClassDependents( oldOc );
254            //        if ( dependees != null && dependees.size() > 0 )
255            //        {
256            //            throw new LdapUnwillingToPerformException( "The objectClass with OID " + oldOc.getOid()
257            //                + " cannot be deleted until all entities" 
258            //                + " using this objectClass have also been deleted.  The following dependees exist: " 
259            //                + getOids( dependees ), 
260            //                ResultCodeEnum.UNWILLING_TO_PERFORM );
261            //        }
262    
263            String newSchemaName = getSchemaName( newParentName );
264            ServerEntry targetEntry = ( ServerEntry ) entry.clone();
265            String newOid = ( String ) newRdn.getNormValue();
266            checkOidIsUnique( newOid );
267            targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid );
268            ObjectClass oc = factory.getObjectClass( schemaManager, targetEntry, schemaManager.getRegistries(),
269                newSchemaName );
270    
271            if ( isSchemaEnabled( oldSchemaName ) )
272            {
273                schemaManager.unregisterObjectClass( oldOc.getOid() );
274            }
275            else
276            {
277                unregisterOids( oldOc );
278            }
279    
280            if ( isSchemaEnabled( newSchemaName ) )
281            {
282                schemaManager.add( oc );
283            }
284            else
285            {
286                registerOids( oc );
287            }
288        }
289    
290    
291        public void move( DN oriChildName, DN newParentName, ServerEntry entry, boolean cascade ) throws Exception
292        {
293            checkNewParent( newParentName );
294            String oldSchemaName = getSchemaName( oriChildName );
295            String newSchemaName = getSchemaName( newParentName );
296            ObjectClass oldAt = factory.getObjectClass( schemaManager, entry, schemaManager.getRegistries(), oldSchemaName );
297    
298            // dependencies are not managed by this class
299            //        Set<ServerEntry> dependees = dao.listObjectClassDependents( oldAt );
300            //        if ( dependees != null && dependees.size() > 0 )
301            //        {s
302            //            throw new LdapUnwillingToPerformException( "The objectClass with OID " + oldAt.getOid() 
303            //                + " cannot be deleted until all entities" 
304            //                + " using this objectClass have also been deleted.  The following dependees exist: " 
305            //                + getOids( dependees ), 
306            //                ResultCodeEnum.UNWILLING_TO_PERFORM );
307            //        }
308    
309            ObjectClass oc = factory.getObjectClass( schemaManager, entry, schemaManager.getRegistries(), newSchemaName );
310    
311            if ( isSchemaEnabled( oldSchemaName ) )
312            {
313                schemaManager.unregisterObjectClass( oldAt.getOid() );
314            }
315            else
316            {
317                unregisterOids( oldAt );
318            }
319    
320            if ( isSchemaEnabled( newSchemaName ) )
321            {
322                schemaManager.add( oc );
323            }
324            else
325            {
326                registerOids( oc );
327            }
328        }
329    
330    
331        private void checkNewParent( DN newParent ) throws LdapException
332        {
333            if ( newParent.size() != 3 )
334            {
335                throw new LdapInvalidDnException(
336                    ResultCodeEnum.NAMING_VIOLATION,
337                    "The parent dn of a objectClass should be at most 3 name components in length." );
338            }
339    
340            RDN rdn = newParent.getRdn();
341    
342            if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals(
343                SchemaConstants.OU_AT_OID ) )
344            {
345                throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
346                    I18n.err( I18n.ERR_376 ) );
347            }
348    
349            if ( !( ( String ) rdn.getNormValue() ).equalsIgnoreCase( SchemaConstants.OBJECT_CLASSES_AT ) )
350            {
351                throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
352                    I18n.err( I18n.ERR_377 ) );
353            }
354        }
355    }