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