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 }