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