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}