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    package org.apache.directory.server.core.journal;
020    
021    
022    import java.util.Set;
023    import java.util.concurrent.atomic.AtomicLong;
024    
025    import org.apache.directory.server.core.DirectoryService;
026    import org.apache.directory.server.core.interceptor.BaseInterceptor;
027    import org.apache.directory.server.core.interceptor.NextInterceptor;
028    import org.apache.directory.server.core.interceptor.context.AddOperationContext;
029    import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
030    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
031    import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
032    import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
033    import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
034    import org.apache.directory.shared.ldap.entry.Modification;
035    import org.apache.directory.shared.ldap.entry.ServerEntry;
036    import org.apache.directory.shared.ldap.ldif.ChangeType;
037    import org.apache.directory.shared.ldap.ldif.LdifEntry;
038    import org.apache.directory.shared.ldap.schema.AttributeType;
039    import org.slf4j.Logger;
040    import org.slf4j.LoggerFactory;
041    
042    
043    /**
044     * An interceptor which intercepts write operations to the directory and
045     * logs them into a journal.
046     * 
047     * @org.apache.xbean.XBean
048     */
049    public class JournalInterceptor extends BaseInterceptor
050    {
051        /** for debugging */
052        private static final Logger LOG = LoggerFactory.getLogger( JournalInterceptor.class );
053        
054        /** A flag set to true if the journal interceptor is enabled */
055        private boolean journalEnabled;
056        
057        /** A shared number stored within each change */ 
058        private AtomicLong revision;
059        
060        /** the Journal service to log changes to */
061        private Journal journal;
062        
063    
064        // -----------------------------------------------------------------------
065        // Overridden init() and destroy() methods
066        // -----------------------------------------------------------------------
067        /**
068         * The init method will initialize the local variables and load the 
069         * entryDeleted AttributeType.
070         */
071        public void init( DirectoryService directoryService ) throws Exception
072        {
073            super.init( directoryService );
074            
075            if ( directoryService.getJournal().isEnabled() )
076            {
077                journalEnabled = true; 
078                journal = directoryService.getJournal();
079                revision = new AtomicLong( System.currentTimeMillis() );
080            }
081    
082            LOG.debug( "JournalInterceptor has been initialized" );
083        }
084        
085        
086        /**
087         * Log the operation, manage the logs rotations.
088         */
089        private void log( long revision, LdifEntry ldif ) throws Exception
090        {
091            journal.log( getPrincipal(), revision, ldif );
092        }
093        
094        
095        // -----------------------------------------------------------------------
096        // Overridden (only change inducing) intercepted methods
097        // -----------------------------------------------------------------------
098        /**
099         * {@inheritDoc}
100         */
101        public void add( NextInterceptor next, AddOperationContext opContext ) throws Exception
102        {
103            long opRevision = 0;
104            
105            if ( journalEnabled )
106            {
107                opRevision = revision.incrementAndGet();
108                
109                // Store the added entry
110                ServerEntry addEntry = opContext.getEntry();
111    
112                LdifEntry ldif = new LdifEntry();
113                ldif.setChangeType( ChangeType.Add );
114                ldif.setDn( opContext.getDn() );
115    
116                Set<AttributeType> list = addEntry.getAttributeTypes();
117                
118                for ( AttributeType attributeType:list )
119                {
120                    ldif.addAttribute( addEntry.get( attributeType).toClientAttribute() );
121                }
122                
123                log( opRevision, ldif );
124            }
125    
126            try
127            {
128                next.add( opContext );
129    
130                if ( journalEnabled )
131                {
132                    // log the ACK
133                    journal.ack( opRevision );
134                }
135            }
136            catch( Exception e )
137            {
138                if ( journalEnabled )
139                {
140                    // log the NACK
141                    journal.nack( opRevision );
142                }
143                throw e;
144            }
145        }
146    
147    
148        /**
149         * {@inheritDoc}
150         */
151        public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws Exception
152        {
153            long opRevision = 0;
154            
155            if ( journalEnabled )
156            {
157                opRevision = revision.incrementAndGet();
158                
159                // Store the deleted entry
160                LdifEntry ldif = new LdifEntry();
161                ldif.setChangeType( ChangeType.Delete );
162                ldif.setDn( opContext.getDn() );
163                
164                journal.log( getPrincipal(), opRevision, ldif );
165            }
166    
167            try
168            {
169                next.delete( opContext );
170    
171                if ( journalEnabled )
172                {
173                    // log the ACK
174                    journal.ack( opRevision );
175                }
176            }
177            catch( Exception e )
178            {
179                if ( journalEnabled )
180                {
181                    // log the NACK
182                    journal.nack( opRevision );
183                }
184                throw e;
185            }
186        }
187    
188    
189        /**
190         * {@inheritDoc}
191         */
192        public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
193        {
194            long opRevision = 0;
195            
196            if ( journalEnabled )
197            {
198                opRevision = revision.incrementAndGet();
199                
200                // Store the modified entry
201                LdifEntry ldif = new LdifEntry();
202                ldif.setChangeType( ChangeType.Modify );
203                ldif.setDn( opContext.getDn() );
204                
205                // Store the modifications 
206                for ( Modification modification:opContext.getModItems() )
207                {
208                    ldif.addModificationItem( modification );
209                }
210                
211                journal.log( getPrincipal(), opRevision, ldif );
212            }
213            
214            try
215            {
216                next.modify( opContext );
217    
218                if ( journalEnabled )
219                {
220                    // log the ACK
221                    journal.ack( opRevision );
222                }
223            }
224            catch( Exception e )
225            {
226                if ( journalEnabled )
227                {
228                    // log the NACK
229                    journal.nack( opRevision );
230                }
231                throw e;
232            }
233        }
234    
235    
236        /**
237         * {@inheritDoc}
238         */
239        public void rename ( NextInterceptor next, RenameOperationContext opContext ) throws Exception
240        {
241            long opRevision = 0;
242            
243            if ( journalEnabled )
244            {
245                opRevision = revision.incrementAndGet();
246                
247                // Store the renamed entry
248                LdifEntry ldif = new LdifEntry();
249                ldif.setChangeType( ChangeType.ModRdn );
250                ldif.setDn( opContext.getDn() );
251                ldif.setNewRdn( opContext.getNewRdn().getNormName() );
252                ldif.setDeleteOldRdn( opContext.getDelOldDn() );
253                
254                journal.log( getPrincipal(), opRevision, ldif );
255            }
256            
257            try
258            {
259                next.rename( opContext );
260        
261                if ( journalEnabled )
262                {
263                    // log the ACK
264                    journal.ack( opRevision );
265                }
266            }
267            catch( Exception e )
268            {
269                if ( journalEnabled )
270                {
271                    // log the NACK
272                    journal.nack( opRevision );
273                }
274                throw e;
275            }
276        }
277    
278    
279        /**
280         * {@inheritDoc}
281         */
282        public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext )
283            throws Exception
284        {
285            long opRevision = 0;
286            
287            if ( journalEnabled )
288            {
289                opRevision = revision.incrementAndGet();
290                
291                // Store the renamed entry
292                LdifEntry ldif = new LdifEntry();
293                ldif.setChangeType( ChangeType.ModDn );
294                ldif.setDn( opContext.getDn() );
295                ldif.setNewRdn( opContext.getNewRdn().getNormName() );
296                ldif.setDeleteOldRdn( opContext.getDelOldDn() );
297                ldif.setNewSuperior( opContext.getNewDn().getNormName() );
298                
299                journal.log( getPrincipal(), opRevision, ldif );
300            }
301            
302            try
303            {
304                next.moveAndRename( opContext );
305                
306                if ( journalEnabled )
307                {
308                    // log the ACK
309                    journal.ack( opRevision );
310                }
311            }
312            catch( Exception e )
313            {
314                if ( journalEnabled )
315                {
316                    // log the NACK
317                    journal.nack( opRevision );
318                }
319                throw e;
320            }
321        }
322    
323    
324        /**
325         * {@inheritDoc}
326         */
327        public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception
328        {
329            long opRevision = 0;
330            
331            if ( journalEnabled )
332            {
333                opRevision = revision.incrementAndGet();
334                
335                // Store the moved entry
336                LdifEntry ldif = new LdifEntry();
337                ldif.setChangeType( ChangeType.ModDn );
338                ldif.setDn( opContext.getDn() );
339                ldif.setNewSuperior( opContext.getParent().getNormName() );
340                
341                journal.log( getPrincipal(), opRevision, ldif );
342            }
343            
344            try
345            {
346                next.move( opContext );
347                
348                if ( journalEnabled )
349                {
350                    // log the ACK
351                    journal.ack( opRevision );
352                }
353            }
354            catch( Exception e )
355            {
356                if ( journalEnabled )
357                {
358                    // log the NACK
359                    journal.nack( opRevision );
360                }
361                throw e;
362            }
363       }
364    }