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