View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.directory.server.core.changelog;
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.FileReader;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  import java.io.ObjectInputStream;
29  import java.io.ObjectOutputStream;
30  import java.io.PrintWriter;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Properties;
37  
38  import org.apache.directory.server.core.DirectoryService;
39  import org.apache.directory.server.core.authn.LdapPrincipal;
40  import org.apache.directory.server.core.cursor.Cursor;
41  import org.apache.directory.server.core.cursor.ListCursor;
42  import org.apache.directory.shared.ldap.ldif.LdifEntry;
43  import org.apache.directory.shared.ldap.util.DateUtils;
44  
45  
46  /**
47   * A change log store that keeps it's information in memory.
48   *
49   * @org.apache.xbean.XBean
50   *
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   * @version $Rev$, $Date$
53   * TODO remove the NamingException
54   */
55  public class MemoryChangeLogStore implements TaggableChangeLogStore
56  {
57      private static final String REV_FILE = "revision";
58      private static final String TAG_FILE = "tags";
59      private static final String CHANGELOG_FILE = "changelog.dat";
60  
61      private long currentRevision;
62      private Tag latest;
63      private final Map<Long,Tag> tags = new HashMap<Long,Tag>( 100 );
64      private final List<ChangeLogEvent> events = new ArrayList<ChangeLogEvent>();
65      private File workingDirectory;
66  
67  
68      public Tag tag( long revision ) throws Exception
69      {
70          if ( tags.containsKey( revision ) )
71          {
72              return tags.get( revision );
73          }
74  
75          latest = new Tag( revision, null );
76          tags.put( revision, latest );
77          return latest;
78      }
79  
80  
81      public Tag tag() throws Exception
82      {
83          if ( latest != null && latest.getRevision() == currentRevision )
84          {
85              return latest;
86          }
87  
88          latest = new Tag( currentRevision, null );
89          tags.put( currentRevision, latest );
90          return latest;
91      }
92  
93  
94      public Tag tag( String description ) throws Exception
95      {
96          if ( latest != null && latest.getRevision() == currentRevision )
97          {
98              return latest;
99          }
100 
101         latest = new Tag( currentRevision, description );
102         tags.put( currentRevision, latest );
103         return latest;
104     }
105 
106 
107     public void init( DirectoryService service ) throws Exception
108     {
109         workingDirectory = service.getWorkingDirectory();
110         loadRevision();
111         loadTags();
112         loadChangeLog();
113     }
114 
115 
116     private void loadRevision() throws Exception
117     {
118         File revFile = new File( workingDirectory, REV_FILE );
119         if ( revFile.exists() )
120         {
121             BufferedReader reader = null;
122             try
123             {
124                 reader = new BufferedReader( new FileReader( revFile ) );
125                 String line = reader.readLine();
126                 currentRevision = Long.valueOf( line );
127             }
128             catch ( IOException e )
129             {
130                 throw e;
131             }
132             finally
133             {
134                 if ( reader != null )
135                 {
136                     //noinspection EmptyCatchBlock
137                     try
138                     {
139                         reader.close();
140                     }
141                     catch ( IOException e )
142                     {
143                     }
144                 }
145             }
146         }
147     }
148 
149 
150     private void saveRevision() throws Exception
151     {
152         File revFile = new File( workingDirectory, REV_FILE );
153         if ( revFile.exists() )
154         {
155             revFile.delete();
156         }
157 
158         PrintWriter out = null;
159         try
160         {
161             out = new PrintWriter( new FileWriter( revFile ) );
162             out.println( currentRevision );
163             out.flush();
164         }
165         catch ( IOException e )
166         {
167             throw e;
168         }
169         finally
170         {
171             if ( out != null )
172             {
173                 out.close();
174             }
175         }
176     }
177 
178 
179     private void saveTags() throws Exception
180     {
181         File tagFile = new File( workingDirectory, TAG_FILE );
182         if ( tagFile.exists() )
183         {
184             tagFile.delete();
185         }
186 
187         FileOutputStream out = null;
188         try
189         {
190             out = new FileOutputStream( tagFile );
191 
192             Properties props = new Properties();
193             for ( Tag tag : tags.values() )
194             {
195                 String key = String.valueOf( tag.getRevision() );
196                 if ( tag.getDescription() == null )
197                 {
198                     props.setProperty( key, "null" );
199                 }
200                 else
201                 {
202                     props.setProperty( key, tag.getDescription() );
203                 }
204             }
205 
206             props.store( out, null );
207             out.flush();
208         }
209         catch ( IOException e )
210         {
211             throw e;
212         }
213         finally
214         {
215             if ( out != null )
216             {
217                 //noinspection EmptyCatchBlock
218                 try
219                 {
220                     out.close();
221                 }
222                 catch ( IOException e )
223                 {
224                 }
225             }
226         }
227     }
228 
229 
230     private void loadTags() throws Exception
231     {
232         File revFile = new File( workingDirectory, REV_FILE );
233         if ( revFile.exists() )
234         {
235             Properties props = new Properties();
236             FileInputStream in = null;
237             try
238             {
239                 in = new FileInputStream( revFile );
240                 props.load( in );
241                 ArrayList<Long> revList = new ArrayList<Long>();
242                 for ( Object key : props.keySet() )
243                 {
244                     revList.add( Long.valueOf( ( String ) key ) );
245                 }
246 
247                 Collections.sort( revList );
248                 Tag tag = null;
249 
250                 // @todo need some serious syncrhoization here on tags
251                 tags.clear();
252                 for ( Long lkey : revList )
253                 {
254                     String rev = String.valueOf( lkey );
255                     String desc = props.getProperty( rev );
256 
257                     if ( desc != null && desc.equals( "null" ) )
258                     {
259                         tag = new Tag( lkey, null );
260                     }
261                     else
262                     {
263                         tag = new Tag( lkey, desc );
264                     }
265 
266                     tags.put( lkey, tag );
267                 }
268 
269                 latest = tag;
270             }
271             catch ( IOException e )
272             {
273                 throw e;
274             }
275             finally
276             {
277                 if ( in != null )
278                 {
279                     //noinspection EmptyCatchBlock
280                     try
281                     {
282                         in.close();
283                     }
284                     catch ( IOException e )
285                     {
286                     }
287                 }
288             }
289         }
290     }
291 
292 
293     private void loadChangeLog() throws Exception
294     {
295         File file = new File( workingDirectory, CHANGELOG_FILE );
296         
297         if ( file.exists() )
298         {
299             ObjectInputStream in = null;
300 
301             try
302             {
303                 in = new ObjectInputStream( new FileInputStream( file ) );
304                 int size = in.readInt();
305                 
306                 ArrayList<ChangeLogEvent> changeLogEvents = new ArrayList<ChangeLogEvent>( size );
307 
308                 for ( int i = 0; i < size; i++ )
309                 {
310                     ChangeLogEvent event = ( ChangeLogEvent ) in.readObject();
311                     changeLogEvents.add( event );
312                 }
313 
314                 // @todo man o man we need some synchronization later after getting this to work
315                 this.events.clear();
316                 this.events.addAll( changeLogEvents );
317             }
318             catch ( Exception e )
319             {
320                 throw e;
321             }
322             finally
323             {
324                 if ( in != null )
325                 {
326                     //noinspection EmptyCatchBlock
327                     try
328                     {
329                         in.close();
330                     }
331                     catch ( IOException e )
332                     {
333                     }
334                 }
335             }
336         }
337     }
338 
339 
340     private void saveChangeLog() throws Exception
341     {
342         File file = new File( workingDirectory, CHANGELOG_FILE );
343         if ( file.exists() )
344         {
345             file.delete();
346         }
347 
348         try
349         {
350             file.createNewFile();
351         }
352         catch ( IOException e )
353         {
354             throw e;
355         }
356 
357         ObjectOutputStream out = null;
358 
359         try
360         {
361             out = new ObjectOutputStream( new FileOutputStream( file ) );
362 
363             out.writeInt( events.size() );
364             
365             for ( ChangeLogEvent event : events )
366             {
367                 out.writeObject( event );
368             }
369 
370             out.flush();
371         }
372         catch ( Exception e )
373         {
374             throw e;
375         }
376         finally
377         {
378             if ( out != null )
379             {
380                 //noinspection EmptyCatchBlock
381                 try
382                 {
383                     out.close();
384                 }
385                 catch ( IOException e )
386                 {
387                 }
388             }
389         }
390     }
391 
392 
393     public void sync() throws Exception
394     {
395         saveRevision();
396         saveTags();
397         saveChangeLog();
398     }
399 
400 
401     public void destroy() throws Exception
402     {
403         saveRevision();
404         saveTags();
405         saveChangeLog();
406     }
407 
408 
409     public long getCurrentRevision()
410     {
411         return currentRevision;
412     }
413 
414 
415     /**
416      * {@inheritDoc}
417      */
418     public ChangeLogEvent log( LdapPrincipal principal, LdifEntry forward, LdifEntry reverse ) throws Exception
419     {
420         currentRevision++;
421         ChangeLogEvent event = new ChangeLogEvent( currentRevision, DateUtils.getGeneralizedTime(), 
422                 principal, forward, reverse );
423         events.add( event );
424         return event;
425     }
426 
427 
428     /**
429      * {@inheritDoc}
430      */
431     public ChangeLogEvent log( LdapPrincipal principal, LdifEntry forward, List<LdifEntry> reverses ) throws Exception
432     {
433         currentRevision++;
434         ChangeLogEvent event = new ChangeLogEvent( currentRevision, DateUtils.getGeneralizedTime(), 
435                 principal, forward, reverses );
436         events.add( event );
437         return event;
438     }
439 
440 
441     public ChangeLogEvent lookup( long revision ) throws Exception
442     {
443         if ( revision < 0 )
444         {
445             throw new IllegalArgumentException( "revision must be greater than or equal to 0" );
446         }
447 
448         if ( revision > getCurrentRevision() )
449         {
450             throw new IllegalArgumentException( "The revision must not be greater than the current revision" );
451         }
452 
453         return events.get( ( int ) revision );
454     }
455 
456 
457     public Cursor<ChangeLogEvent> find() throws Exception
458     {
459         return new ListCursor<ChangeLogEvent>( events );
460     }
461 
462 
463     public Cursor<ChangeLogEvent> findBefore( long revision ) throws Exception
464     {
465         return new ListCursor<ChangeLogEvent>( events, ( int ) revision );
466     }
467 
468 
469     public Cursor<ChangeLogEvent> findAfter( long revision ) throws Exception
470     {
471         return new ListCursor<ChangeLogEvent>( ( int ) revision, events );
472     }
473 
474 
475     public Cursor<ChangeLogEvent> find( long startRevision, long endRevision ) throws Exception
476     {
477         return new ListCursor<ChangeLogEvent>( ( int ) startRevision, events, ( int ) ( endRevision + 1 ) );
478     }
479 
480 
481     public Tag getLatest() throws Exception
482     {
483         return latest;
484     }
485 
486 
487     /**
488      * @see TaggableChangeLogStore#removeTag(long)
489      */
490     public Tag removeTag( long revision ) throws Exception
491     {
492         return tags.remove( revision );
493     }
494 
495 
496     /**
497      * @see TaggableChangeLogStore#tag(long, String)
498      */
499     public Tag tag( long revision, String descrition ) throws Exception
500     {
501         if ( tags.containsKey( revision ) )
502         {
503             return tags.get( revision );
504         }
505 
506         latest = new Tag( revision, descrition );
507         tags.put( revision, latest );
508         return latest;
509     }
510     
511     
512     /**
513      * @see Object#toString()
514      */
515     public String toString()
516     {
517         StringBuilder sb = new StringBuilder();
518         
519         sb.append( "MemoryChangeLog\n" );
520         sb.append( "latest tag : " ).append( latest ).append( '\n' );
521         
522         if ( events != null )
523         {
524             sb.append( "Nb of events : " ).append( events.size() ).append( '\n' );
525             
526             int i = 0;
527             
528             for ( ChangeLogEvent event:events )
529             {
530                 sb.append( "event[" ).append( i++ ).append( "] : " );
531                 sb.append( "\n---------------------------------------\n" );
532                 sb.append( event );
533                 sb.append( "\n---------------------------------------\n" );
534             }
535         }
536         
537         
538         return sb.toString();
539     }
540 }