Package org.apache.directory.server.core.changelog

Source Code of org.apache.directory.server.core.changelog.ChangeLogInterceptor

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.directory.server.core.changelog;


import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.directory.server.constants.ApacheSchemaConstants;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.entry.ClonedServerEntry;
import org.apache.directory.server.core.entry.ServerAttribute;
import org.apache.directory.server.core.entry.ServerEntry;
import org.apache.directory.server.core.entry.ServerEntryUtils;
import org.apache.directory.server.core.entry.ServerModification;
import org.apache.directory.server.core.interceptor.BaseInterceptor;
import org.apache.directory.server.core.interceptor.NextInterceptor;
import org.apache.directory.server.core.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
import org.apache.directory.server.core.interceptor.context.OperationContext;
import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
import org.apache.directory.server.core.partition.ByPassConstants;
import org.apache.directory.server.core.schema.SchemaService;
import org.apache.directory.shared.ldap.entry.Entry;
import org.apache.directory.shared.ldap.entry.EntryAttribute;
import org.apache.directory.shared.ldap.entry.Modification;
import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
import org.apache.directory.shared.ldap.ldif.ChangeType;
import org.apache.directory.shared.ldap.ldif.LdifEntry;
import org.apache.directory.shared.ldap.ldif.LdifRevertor;
import org.apache.directory.shared.ldap.name.DN;
import org.apache.directory.shared.ldap.name.RDN;
import org.apache.directory.shared.ldap.schema.AttributeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* An interceptor which intercepts write operations to the directory and
* logs them with the server's ChangeLog service.
* Note: Adding/deleting a tag is not recorded as a change
*
* @org.apache.xbean.XBean
*/
public class ChangeLogInterceptor extends BaseInterceptor
{
    /** for debugging */
    private static final Logger LOG = LoggerFactory.getLogger( ChangeLogInterceptor.class );
   
    /** used to ignore modify operations to tombstone entries */
    private AttributeType entryDeleted;
   
    /** the changelog service to log changes to */
    private ChangeLog changeLog;
   
    /** we need the schema service to deal with special conditions */
    private SchemaService schemaService;

    /** OID of the 'rev' attribute used in changeLogEvent and tag objectclasses */
    private static final String REV_AT_OID = "1.3.6.1.4.1.18060.0.4.1.2.47";
   
    // -----------------------------------------------------------------------
    // Overridden init() and destroy() methods
    // -----------------------------------------------------------------------


    /**
     * The init method will initialize the local variables and load the
     * entryDeleted AttributeType.
     */
    public void init( DirectoryService directoryService ) throws Exception
    {
        super.init( directoryService );

        changeLog = directoryService.getChangeLog();
        schemaService = directoryService.getSchemaService();
        entryDeleted = directoryService.getSchemaManager()
                .lookupAttributeTypeRegistry( ApacheSchemaConstants.ENTRY_DELETED_AT_OID );
    }


    // -----------------------------------------------------------------------
    // Overridden (only change inducing) intercepted methods
    // -----------------------------------------------------------------------
   

    public void add( NextInterceptor next, AddOperationContext opContext ) throws Exception
    {
        next.add( opContext );

        if ( ! changeLog.isEnabled() || ! opContext.isFirstOperation() )
        {
            return;
        }

        ServerEntry addEntry = opContext.getEntry();

        // we don't want to record addition of a tag as a change
        if( addEntry.get( REV_AT_OID ) != null )
        {
           return;
        }
       
        LdifEntry forward = new LdifEntry();
        forward.setChangeType( ChangeType.Add );
        forward.setDn( opContext.getDn() );

        Set<AttributeType> list = addEntry.getAttributeTypes();
       
        for ( AttributeType attributeType:list )
        {
            forward.addAttribute( ((ServerAttribute)addEntry.get( attributeType) ).toClientAttribute() );
        }
       
        LdifEntry reverse = LdifRevertor.reverseAdd( opContext.getDn() );
        opContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
    }


    /**
     * The delete operation has to be stored with a way to restore the deleted element.
     * There is no way to do that but reading the entry and dump it into the LOG.
     */
    public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws Exception
    {
        // @todo make sure we're not putting in operational attributes that cannot be user modified
        // must save the entry if change log is enabled
        ServerEntry serverEntry = null;

        if ( changeLog.isEnabled() && opContext.isFirstOperation() )
        {
            serverEntry = getAttributes( opContext );
        }

        next.delete( opContext );

        if ( ! changeLog.isEnabled() || ! opContext.isFirstOperation() )
        {
            return;
        }

        // we don't want to record deleting a tag as a change
        if( serverEntry.get( REV_AT_OID ) != null )
        {
           return;
        }

        LdifEntry forward = new LdifEntry();
        forward.setChangeType( ChangeType.Delete );
        forward.setDn( opContext.getDn() );
       
        Entry reverseEntry = new DefaultClientEntry( serverEntry.getDn() );

        for ( EntryAttribute attribute : serverEntry )
        {
            // filter collective attributes, they can't be added by the revert operation
            AttributeType at = schemaService.getSchemaManager().getAttributeTypeRegistry().lookup( attribute.getId() );
            if ( !at.isCollective() )
            {
                reverseEntry.add( ( ( ServerAttribute ) attribute ).toClientAttribute() );
            }
        }

        LdifEntry reverse = LdifRevertor.reverseDel( opContext.getDn(), reverseEntry );
        opContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
    }


    /**
     * Gets attributes required for modifications.
     *
     * @param dn the dn of the entry to get
     * @return the entry's attributes (may be immutable if the schema subentry)
     * @throws Exception on error accessing the entry's attributes
     */
    private ServerEntry getAttributes( OperationContext opContext ) throws Exception
    {
        DN dn = opContext.getDn();
        ClonedServerEntry serverEntry;

        // @todo make sure we're not putting in operational attributes that cannot be user modified
        if ( schemaService.isSchemaSubentry( dn.toNormName() ) )
        {
            return schemaService.getSubschemaEntryCloned();
        }
        else
        {
            serverEntry = opContext.lookup( dn, ByPassConstants.LOOKUP_BYPASS );
        }

        return serverEntry;
    }


    /**
     *
     */
    public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
    {
        ServerEntry serverEntry = null;
        Modification modification = ServerEntryUtils.getModificationItem( opContext.getModItems(), entryDeleted );
        boolean isDelete = ( modification != null );

        if ( ! isDelete && ( changeLog.isEnabled() && opContext.isFirstOperation() ) )
        {
            // @todo make sure we're not putting in operational attributes that cannot be user modified
            serverEntry = getAttributes( opContext );
        }
       
        // Duplicate modifications so that the reverse does not contain the operational attributes
        List<Modification> clonedMods = new ArrayList<Modification>();

        for ( Modification mod : opContext.getModItems() )
        {
            clonedMods.add( mod.clone() );
        }

        // Call the next interceptor
        next.modify( opContext );

        // @TODO: needs big consideration!!!
        // NOTE: perhaps we need to log this as a system operation that cannot and should not be reapplied?
        if (
            isDelete ||  
            ! changeLog.isEnabled() ||
            ! opContext.isFirstOperation() ||
           
         // if there are no modifications due to stripping out bogus non-
         // existing attributes then we will have no modification items and
         // should ignore not this without registering it with the changelog
        
            opContext.getModItems().size() == 0
        {
            if ( isDelete )
            {
                LOG.debug( "Bypassing changelog on modify of entryDeleted attribute." );
            }
           
            return;
        }

        LdifEntry forward = new LdifEntry();
        forward.setChangeType( ChangeType.Modify );
        forward.setDn( opContext.getDn() );
       
        List<Modification> mods = new ArrayList<Modification>( clonedMods.size() );
       
        for ( Modification modItem : clonedMods )
        {
            Modification mod = ((ServerModification)modItem).toClientModification();
           
            // TODO: handle correctly http://issues.apache.org/jira/browse/DIRSERVER-1198
            mod.getAttribute().setId( modItem.getAttribute().getId() );
            mods.add( mod );
           
            forward.addModificationItem( mod );
        }
       
        Entry clientEntry = new DefaultClientEntry( serverEntry.getDn() );
       
        for ( EntryAttribute attribute:serverEntry )
        {
            clientEntry.add( ((ServerAttribute)attribute).toClientAttribute() );
        }

        LdifEntry reverse = LdifRevertor.reverseModify(
            opContext.getDn(),
            mods,
            clientEntry );
       
        opContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
    }


    // -----------------------------------------------------------------------
    // Though part left as an exercise (Not Any More!)
    // -----------------------------------------------------------------------


    public void rename ( NextInterceptor next, RenameOperationContext renameContext ) throws Exception
    {
        ServerEntry serverEntry = null;
       
        if ( renameContext.getEntry() != null )
        {
            serverEntry = renameContext.getEntry().getOriginalEntry();
        }
       
        next.rename( renameContext );
       
        // After this point, the entry has been modified. The cloned entry contains
        // the modified entry, the originalEntry has changed

        if ( ! changeLog.isEnabled() || ! renameContext.isFirstOperation() )
        {
            return;
        }

        LdifEntry forward = new LdifEntry();
        forward.setChangeType( ChangeType.ModRdn );
        forward.setDn( renameContext.getDn() );
        forward.setNewRdn( renameContext.getNewRdn().getUpName() );
        forward.setDeleteOldRdn( renameContext.getDelOldDn() );

        List<LdifEntry> reverses = LdifRevertor.reverseRename(
            serverEntry, renameContext.getNewRdn(), renameContext.getDelOldDn() );
       
        renameContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverses ) );
    }


    public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opCtx )
        throws Exception
    {
        ClonedServerEntry serverEntry = null;
       
        if ( changeLog.isEnabled() && opCtx.isFirstOperation() )
        {
            // @todo make sure we're not putting in operational attributes that cannot be user modified
            serverEntry = opCtx.lookup( opCtx.getDn(), ByPassConstants.LOOKUP_BYPASS );
        }

        next.moveAndRename( opCtx );

        if ( ! changeLog.isEnabled() || ! opCtx.isFirstOperation() )
        {
            return;
        }

        LdifEntry forward = new LdifEntry();
        forward.setChangeType( ChangeType.ModDn );
        forward.setDn( opCtx.getDn() );
        forward.setDeleteOldRdn( opCtx.getDelOldDn() );
        forward.setNewRdn( opCtx.getNewRdn().getUpName() );
        forward.setNewSuperior( opCtx.getParent().getName() );

        List<LdifEntry> reverses = LdifRevertor.reverseMoveAndRename
            serverEntry, opCtx.getParent(), new RDN( opCtx.getNewRdn() ), false );
        opCtx.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverses ) );
    }


    public void move ( NextInterceptor next, MoveOperationContext opCtx ) throws Exception
    {
        next.move( opCtx );

        if ( ! changeLog.isEnabled() || ! opCtx.isFirstOperation() )
        {
            return;
        }

        LdifEntry forward = new LdifEntry();
        forward.setChangeType( ChangeType.ModDn );
        forward.setDn( opCtx.getDn() );
        forward.setNewSuperior( opCtx.getParent().getName() );

        LdifEntry reverse = LdifRevertor.reverseMove( opCtx.getParent(), opCtx.getDn() );
        opCtx.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
    }
}
TOP

Related Classes of org.apache.directory.server.core.changelog.ChangeLogInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.