View Javadoc

1   /*
2    * Copyright 2004 Charles Blaxland
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package mnemosyne.archiver;
17  
18  import mnemosyne.util.Util;
19  import mnemosyne.util.PersistenceRuntimeException;
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  
23  import java.io.*;
24  import java.text.DecimalFormat;
25  import java.util.SortedSet;
26  import java.util.TreeSet;
27  
28  /***
29   * @version $Id: ArchiveDirectory.java,v 1.1.1.1 2004/08/07 06:40:49 charlesblaxland Exp $
30   */
31  public abstract class ArchiveDirectory
32  {
33      private static final Log log = LogFactory.getLog(ArchiveDirectory.class);
34  
35      private static final String IN_PROGRESS_SUFFIX = "_";
36      private static final String SEQUENCE_FORMAT = "0000000000000000000";
37      private static final DecimalFormat sequenceFormatter = new DecimalFormat(SEQUENCE_FORMAT);
38  
39      private File directory;
40      protected SortedSet archiveFiles;
41      private ArchiveSequence sequence;
42  
43      protected ArchiveDirectory(String directoryName, ArchiveSequence sequence)
44      {
45          if (!Util.ensureDirectoryExists(directoryName))
46          {
47              throw new PersistenceRuntimeException("Unable to create archive directory");
48          }
49          this.directory = new File(directoryName);
50          this.sequence = sequence;
51          scanDirectory();
52      }
53  
54      protected abstract String prefix();
55      protected abstract String postfix();
56      protected abstract String extension();
57  
58      protected ObjectOutputStream createOutputStream(OutputStream output) throws IOException
59      {
60          return new ArchiveOutputStream(output);
61      }
62  
63      protected ObjectInputStream createInputStream(InputStream input) throws IOException
64      {
65          return new ArchiveInputStream(input);
66      }
67  
68      private void scanDirectory()
69      {
70          log.debug("Scanning archive directory " + directory.getPath());
71          File[] files = directory.listFiles(new FilenameFilter() {
72              public boolean accept(File dir, String name)
73              {
74                  return isValidArchiveFile(name);
75              }
76          });
77  
78          initializeFileCollection(files);
79      }
80  
81      protected boolean isValidArchiveFile(String filename)
82      {
83          boolean isValid =
84                  filename.startsWith(prefix()) &&
85                  filename.endsWith(postfix() + "." + extension()) &&
86                  filename.length() == prefix().length() + SEQUENCE_FORMAT.length() + postfix().length() + ".".length() + extension().length();
87  
88          return isValid;
89      }
90  
91      private void initializeFileCollection(File[] files)
92      {
93          archiveFiles = new TreeSet();
94          long maxSequenceNumber = -1;
95          for (int i = 0; i < files.length; i++)
96          {
97              File file = files[i];
98              try
99              {
100                 long sequenceNumber = extractFileSequenceNumber(file.getName());
101                 log.debug("Found archive file " + file.getName());
102                 if (sequenceNumber > maxSequenceNumber)
103                 {
104                     maxSequenceNumber = sequenceNumber;
105                 }
106                 archiveFiles.add(files[i].getName());
107             }
108             catch (NumberFormatException e)
109             {
110                 log.debug("Invalid archive file found in archive directory: " + file.getName());
111             }
112         }
113 
114         sequence.advanceValue(maxSequenceNumber + 1);
115     }
116 
117     protected boolean isEmpty()
118     {
119         return archiveFiles.isEmpty();
120     }
121 
122     protected long extractFileSequenceNumber(String filename) throws NumberFormatException
123     {
124         int startSequenceIndex = prefix().length();
125         int finishSequenceIndex = filename.indexOf(".") - postfix().length();
126         String sequenceNumber = filename.substring(startSequenceIndex, finishSequenceIndex);
127         long retval = Long.parseLong(sequenceNumber);
128         log.debug("Extracted sequence " + retval + " from filename " + filename);
129         return retval;
130     }
131 
132     protected File allocateNewInProgressArchiveFile()
133     {
134         return new File(directory, buildArchiveFilename(sequence.getNext()) + IN_PROGRESS_SUFFIX);
135     }
136 
137     protected File constructArchiveFile(String archiveFilename)
138     {
139         return new File(directory, archiveFilename);
140     }
141 
142     protected String buildArchiveFilename(long sequenceNumber)
143     {
144         return prefix() + sequenceFormatter.format(sequenceNumber) + postfix() + "." + extension();
145     }
146 
147     protected Object readArchiveFile(File archiveFile) throws ArchiverException
148     {
149         if (log.isDebugEnabled())
150             log.debug("Loading file " + archiveFile.getPath());
151 
152         FileInputStream fileInput = null;
153         ObjectInputStream input = null;
154         Object retval = null;
155         try
156         {
157             fileInput = new FileInputStream(archiveFile);
158             input = createInputStream(new BufferedInputStream(fileInput));
159             retval = input.readObject();
160         }
161         catch (ClassNotFoundException e)
162         {
163             throw new ArchiverException("Exception occurred while trying to load archive " + archiveFile.getPath(), e);
164         }
165         catch (IOException e)
166         {
167             throw new ArchiverException("Exception occurred while trying to load archive" + archiveFile.getPath(), e);
168         }
169         finally
170         {
171             try
172             {
173                 if (input != null)
174                 {
175                     input.close();
176                 }
177                 else if (fileInput != null)
178                 {
179                     fileInput.close();
180                 }
181             }
182             catch (IOException e)
183             {
184                 log.warn("Unable to close archive file " + archiveFile.getPath());
185             }
186         }
187         return retval;
188     }
189 
190     public void writeArchiveFile(Object obj) throws ArchiverException
191     {
192         File outputFile = allocateNewInProgressArchiveFile();
193         if (log.isDebugEnabled())
194             log.debug("Writing archive to " + outputFile.getPath());
195 
196         boolean removeOutputFile = false;
197         FileOutputStream fileOutput = null;
198         ObjectOutputStream output = null;
199         try
200         {
201             fileOutput = new FileOutputStream(outputFile);
202             output = createOutputStream(new BufferedOutputStream(fileOutput));
203             output.writeObject(obj);
204         }
205         catch (IOException e)
206         {
207             removeOutputFile = true;
208             throw new ArchiverException("Exception occurred while trying to save archive", e);
209         }
210         finally
211         {
212             try
213             {
214                 if (output != null)
215                 {
216                     output.flush();
217                     output.close();
218                 }
219                 else if (fileOutput != null)
220                 {
221                     fileOutput.flush();
222                     fileOutput.close();
223                 }
224             }
225             catch (IOException e)
226             {
227                 log.warn("Unable to close archive file " + outputFile.getPath(), e);
228             }
229 
230             if (removeOutputFile && outputFile.exists())
231             {
232                 outputFile.delete();
233             }
234         }
235 
236         onArchiveComplete(outputFile);
237     }
238 
239     protected void onArchiveComplete(File inProgressFile) throws ArchiverException
240     {
241         String inProgressPath = inProgressFile.getPath();
242         String completeFileName = inProgressPath.substring(0, inProgressPath.length() - IN_PROGRESS_SUFFIX.length());
243         File completeFile = new File(completeFileName);
244         if (log.isDebugEnabled())
245             log.debug("Renaming in progress archive file from " + inProgressPath + " to " + completeFile.getPath());
246         if (!inProgressFile.renameTo(completeFile))
247         {
248             throw new ArchiverException("Unable to rename in progress archive file");
249         }
250 
251         archiveFiles.add(completeFile.getName());
252     }
253 
254 }