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    }