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.changelog; 020 021 022import java.util.List; 023 024import org.apache.directory.api.ldap.model.exception.LdapException; 025import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException; 026import org.apache.directory.api.ldap.model.ldif.LdifEntry; 027import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 028import org.apache.directory.server.core.api.DirectoryService; 029import org.apache.directory.server.core.api.LdapPrincipal; 030import org.apache.directory.server.core.api.changelog.ChangeLog; 031import org.apache.directory.server.core.api.changelog.ChangeLogEvent; 032import org.apache.directory.server.core.api.changelog.ChangeLogSearchEngine; 033import org.apache.directory.server.core.api.changelog.ChangeLogStore; 034import org.apache.directory.server.core.api.changelog.SearchableChangeLogStore; 035import org.apache.directory.server.core.api.changelog.Tag; 036import org.apache.directory.server.core.api.changelog.TagSearchEngine; 037import org.apache.directory.server.core.api.changelog.TaggableChangeLogStore; 038import org.apache.directory.server.core.api.changelog.TaggableSearchableChangeLogStore; 039import org.apache.directory.server.core.api.partition.Partition; 040import org.apache.directory.server.i18n.I18n; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043 044 045/** 046 * The default ChangeLog service implementation. It stores operations 047 * in memory. 048 * 049 * Entries are stored into a dedicated partition, named ou=changelog, under which 050 * we have two other sub-entries : ou=tags and ou= revisions : 051 * 052 * ou=changelog 053 * | 054 * +-- ou=revisions 055 * | 056 * +-- ou=tags 057 * 058 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 059 */ 060public class DefaultChangeLog implements ChangeLog 061{ 062 /** The class logger */ 063 private static final Logger LOG = LoggerFactory.getLogger( DefaultChangeLog.class ); 064 065 /** Tells if the service is activated or not */ 066 private boolean enabled; 067 068 /** The latest tag set */ 069 private Tag latest; 070 071 /** 072 * The default store is a InMemory store. 073 **/ 074 private ChangeLogStore store; 075 076 /** A volatile flag used to avoid store switching when in use */ 077 private volatile boolean storeInitialized = false; 078 079 /** A flag used to tell if the changeLog system is visible by the clients */ 080 private boolean exposed; 081 082 // default values for ChangeLogStorePartition containers 083 private static final String DEFAULT_PARTITION_SUFFIX = "ou=changelog"; 084 private static final String DEFAULT_REV_CONTAINER_NAME = "ou=revisions"; 085 private static final String DEFAULT_TAG_CONTAINER_NAME = "ou=tags"; 086 087 // default values for ChangeLogStorePartition containers 088 private String partitionSuffix = DEFAULT_PARTITION_SUFFIX; 089 private String revContainerName = DEFAULT_REV_CONTAINER_NAME; 090 private String tagContainerName = DEFAULT_TAG_CONTAINER_NAME; 091 092 093 /** 094 * {@inheritDoc} 095 */ 096 @Override 097 public ChangeLogStore getChangeLogStore() 098 { 099 return store; 100 } 101 102 103 /** 104 * {@inheritDoc} 105 * 106 * If there is an existing changeLog store, we don't switch it 107 */ 108 @Override 109 public void setChangeLogStore( ChangeLogStore store ) 110 { 111 if ( storeInitialized ) 112 { 113 LOG.error( I18n.err( I18n.ERR_29 ) ); 114 } 115 else 116 { 117 this.store = store; 118 } 119 } 120 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override 126public long getCurrentRevision() throws LdapException 127 { 128 synchronized ( store ) 129 { 130 return store.getCurrentRevision(); 131 } 132 } 133 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public ChangeLogEvent log( LdapPrincipal principal, LdifEntry forward, LdifEntry reverse ) throws LdapException 140 { 141 if ( !enabled ) 142 { 143 throw new IllegalStateException( I18n.err( I18n.ERR_236 ) ); 144 } 145 146 try 147 { 148 return store.log( principal, forward, reverse ); 149 } 150 catch ( Exception e ) 151 { 152 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getMessage(), e ); 153 } 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 public ChangeLogEvent log( LdapPrincipal principal, LdifEntry forward, List<LdifEntry> reverses ) 162 throws LdapException 163 { 164 if ( !enabled ) 165 { 166 throw new IllegalStateException( I18n.err( I18n.ERR_236 ) ); 167 } 168 169 try 170 { 171 return store.log( principal, forward, reverses ); 172 } 173 catch ( Exception e ) 174 { 175 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, e.getMessage(), e ); 176 } 177 } 178 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override 184 public boolean isLogSearchSupported() 185 { 186 return store instanceof SearchableChangeLogStore; 187 } 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 @Override 194 public boolean isTagSearchSupported() 195 { 196 return store instanceof TaggableSearchableChangeLogStore; 197 } 198 199 200 /** 201 * {@inheritDoc} 202 */ 203 @Override 204 public boolean isTagStorageSupported() 205 { 206 return store instanceof TaggableChangeLogStore; 207 } 208 209 210 /** 211 * {@inheritDoc} 212 */ 213 @Override 214 public ChangeLogSearchEngine getChangeLogSearchEngine() 215 { 216 if ( isLogSearchSupported() ) 217 { 218 return ( ( SearchableChangeLogStore ) store ).getChangeLogSearchEngine(); 219 } 220 221 throw new UnsupportedOperationException( I18n.err( I18n.ERR_237 ) ); 222 } 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 @Override 229 public TagSearchEngine getTagSearchEngine() 230 { 231 if ( isTagSearchSupported() ) 232 { 233 return ( ( TaggableSearchableChangeLogStore ) store ).getTagSearchEngine(); 234 } 235 236 throw new UnsupportedOperationException( I18n.err( I18n.ERR_238 ) ); 237 } 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override 244 public Tag tag( long revision, String description ) throws Exception 245 { 246 if ( revision < 0 ) 247 { 248 throw new IllegalArgumentException( I18n.err( I18n.ERR_239 ) ); 249 } 250 251 if ( revision > store.getCurrentRevision() ) 252 { 253 throw new IllegalArgumentException( I18n.err( I18n.ERR_240 ) ); 254 } 255 256 if ( store instanceof TaggableChangeLogStore ) 257 { 258 latest = ( ( TaggableChangeLogStore ) store ).tag( revision ); 259 return latest; 260 } 261 262 latest = new Tag( revision, description ); 263 return latest; 264 } 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override 271 public Tag tag( long revision ) throws Exception 272 { 273 return tag( revision, null ); 274 } 275 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 public Tag tag( String description ) throws Exception 282 { 283 return tag( store.getCurrentRevision(), description ); 284 } 285 286 287 /** 288 * {@inheritDoc} 289 */ 290 @Override 291 public Tag tag() throws Exception 292 { 293 return tag( store.getCurrentRevision(), null ); 294 } 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 @Override 301 public void setEnabled( boolean enabled ) 302 { 303 this.enabled = enabled; 304 } 305 306 307 /** 308 * {@inheritDoc} 309 */ 310 @Override 311 public boolean isEnabled() 312 { 313 return enabled; 314 } 315 316 317 /** 318 * {@inheritDoc} 319 */ 320 @Override 321 public Tag getLatest() throws LdapException 322 { 323 if ( latest != null ) 324 { 325 return latest; 326 } 327 328 if ( store instanceof TaggableChangeLogStore ) 329 { 330 latest = ( ( TaggableChangeLogStore ) store ).getLatest(); 331 return latest; 332 } 333 334 return null; 335 } 336 337 338 /** 339 * Initialize the ChangeLog system. We will initialize the associated store. 340 */ 341 @Override 342 public void init( DirectoryService service ) throws LdapException 343 { 344 if ( enabled ) 345 { 346 if ( store == null ) 347 { 348 // If no store has been defined, create an In Memory store 349 store = new MemoryChangeLogStore(); 350 } 351 352 store.init( service ); 353 354 if ( exposed && isTagSearchSupported() ) 355 { 356 TaggableSearchableChangeLogStore tmp = ( TaggableSearchableChangeLogStore ) store; 357 358 tmp.createPartition( partitionSuffix, revContainerName, tagContainerName ); 359 360 Partition partition = tmp.getPartition(); 361 partition.initialize(); 362 363 service.addPartition( partition ); 364 } 365 } 366 367 // Flip the protection flag 368 storeInitialized = true; 369 } 370 371 372 /** 373 * {@inheritDoc} 374 */ 375 @Override 376 public void sync() throws LdapException 377 { 378 if ( enabled ) 379 { 380 store.sync(); 381 } 382 } 383 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override 389 public void destroy() throws LdapException 390 { 391 if ( enabled ) 392 { 393 store.destroy(); 394 } 395 396 storeInitialized = false; 397 } 398 399 400 /** 401 * {@inheritDoc} 402 */ 403 @Override 404 public boolean isExposed() 405 { 406 return exposed; 407 } 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override 414 public void setExposed( boolean exposed ) 415 { 416 this.exposed = exposed; 417 } 418 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override 424 public void setPartitionSuffix( String suffix ) 425 { 426 this.partitionSuffix = suffix; 427 } 428 429 430 /** 431 * {@inheritDoc} 432 */ 433 @Override 434 public void setRevisionsContainerName( String revContainerName ) 435 { 436 this.revContainerName = revContainerName; 437 } 438 439 440 /** 441 * {@inheritDoc} 442 */ 443 @Override 444 public void setTagsContainerName( String tagContainerName ) 445 { 446 this.tagContainerName = tagContainerName; 447 } 448 449 450 /** 451 * @see Object#toString() 452 */ 453 @Override 454 public String toString() 455 { 456 StringBuilder sb = new StringBuilder(); 457 458 sb.append( "ChangeLog tag[" ).append( latest ).append( "]\n" ); 459 sb.append( " store : \n" ).append( store ); 460 461 return sb.toString(); 462 } 463}