Coverage Report - org.apache.commons.transaction.file.FileSequence
 
Classes in this File Line Coverage Branch Coverage Complexity
FileSequence
0%
0/116
0%
0/36
5.091
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.transaction.file;
 18  
 
 19  
 import java.io.BufferedReader;
 20  
 import java.io.BufferedWriter;
 21  
 import java.io.File;
 22  
 import java.io.FileInputStream;
 23  
 import java.io.FileNotFoundException;
 24  
 import java.io.FileOutputStream;
 25  
 import java.io.IOException;
 26  
 import java.io.InputStream;
 27  
 import java.io.InputStreamReader;
 28  
 import java.io.OutputStream;
 29  
 import java.io.OutputStreamWriter;
 30  
 import java.io.UnsupportedEncodingException;
 31  
 
 32  
 import org.apache.commons.transaction.util.FileHelper;
 33  
 import org.apache.commons.transaction.util.LoggerFacade;
 34  
 
 35  
 /**
 36  
  * Fail-Safe sequence store implementation using the file system. Works by versioning
 37  
  * values of sequences and throwing away all versions, but the current and the previous one.
 38  
  * 
 39  
  * @version $Id: FileSequence.java 493628 2007-01-07 01:42:48Z joerg $
 40  
  */
 41  
 public class FileSequence {
 42  
 
 43  
     protected final String storeDir;
 44  
     protected final LoggerFacade logger;
 45  
 
 46  
     /**
 47  
      * Creates a new resouce manager operation on the specified directories.
 48  
      * 
 49  
      * @param storeDir directory where sequence information is stored
 50  
      * @param logger logger used for warnings only
 51  
      */
 52  0
     public FileSequence(String storeDir, LoggerFacade logger) throws ResourceManagerException {
 53  0
         this.storeDir = storeDir;
 54  0
         this.logger = logger;
 55  0
         File file = new File(storeDir);
 56  0
         file.mkdirs();
 57  0
         if (!file.exists()) {
 58  0
             throw new ResourceManagerException("Can not create working directory " + storeDir);
 59  
         }
 60  0
     }
 61  
 
 62  
         /**
 63  
          * Checks if the sequence already exists.
 64  
          * 
 65  
          * @param sequenceName the name of the sequence you want to check 
 66  
          * @return <code>true</code> if the sequence already exists, <code>false</code> otherwise
 67  
          */
 68  
     public synchronized boolean exists(String sequenceName) {
 69  0
         String pathI = getPathI(sequenceName);
 70  0
         String pathII = getPathII(sequenceName);
 71  
 
 72  0
         return (FileHelper.fileExists(pathI) || FileHelper.fileExists(pathII));
 73  
     }
 74  
 
 75  
         /**
 76  
          * Creates a sequence if it does not already exist.
 77  
          * 
 78  
          * @param sequenceName the name of the sequence you want to create 
 79  
          * @return <code>true</code> if the sequence has been created, <code>false</code> if it already existed
 80  
          * @throws ResourceManagerException if anything goes wrong while accessing the sequence 
 81  
          */
 82  
     public synchronized boolean create(String sequenceName, long initialValue) throws ResourceManagerException {
 83  0
         if (exists(sequenceName))
 84  0
             return false;
 85  0
         write(sequenceName, initialValue);
 86  0
         return true;
 87  
     }
 88  
 
 89  
         /**
 90  
          * Deletes a sequence if it exists.
 91  
          * 
 92  
          * @param sequenceName the name of the sequence you want to delete 
 93  
          * @return <code>true</code> if the sequence has been deleted, <code>false</code> if not
 94  
          */
 95  
     public synchronized boolean delete(String sequenceName) {
 96  0
         if (!exists(sequenceName))
 97  0
             return false;
 98  0
         String pathI = getPathI(sequenceName);
 99  0
         String pathII = getPathII(sequenceName);
 100  
 
 101  
         // XXX be careful no to use shortcut eval with || might not delete second file        
 102  0
         boolean res1 = FileHelper.deleteFile(pathI);
 103  0
         boolean res2 = FileHelper.deleteFile(pathII);
 104  
 
 105  0
         return (res1 || res2);
 106  
     }
 107  
 
 108  
         /**
 109  
          * Gets the next value of the sequence. 
 110  
          * 
 111  
          * @param sequenceName the name of the sequence you want the next value for
 112  
          * @param increment the increment for the sequence, i.e. how much to add to the sequence with this call
 113  
          * @return the next value of the sequence <em>not yet incremented</em>, i.e. the increment is recorded
 114  
          * internally, but not returned with the next call to this method
 115  
          * @throws ResourceManagerException if anything goes wrong while accessing the sequence 
 116  
          */
 117  
     public synchronized long nextSequenceValueBottom(String sequenceName, long increment)
 118  
         throws ResourceManagerException {
 119  0
         if (!exists(sequenceName)) {
 120  0
             throw new ResourceManagerException("Sequence " + sequenceName + " does not exist");
 121  
         }
 122  0
         if (increment <= 0) {
 123  0
             throw new IllegalArgumentException("Increment must be greater than 0, was " + increment);
 124  
         }
 125  0
         long value = read(sequenceName);
 126  0
         long newValue = value + increment;
 127  0
         write(sequenceName, newValue);
 128  0
         return value;
 129  
     }
 130  
 
 131  
     protected long read(String sequenceName) throws ResourceManagerException {
 132  0
         String pathI = getPathI(sequenceName);
 133  0
         String pathII = getPathII(sequenceName);
 134  
 
 135  0
         long returnValue = -1;
 136  
 
 137  0
         long valueI = -1;
 138  0
         if (FileHelper.fileExists(pathI)) {
 139  
             try {
 140  0
                 valueI = readFromPath(pathI);
 141  0
             } catch (NumberFormatException e) {
 142  0
                 throw new ResourceManagerException("Fatal internal error: Backup sequence value corrupted");
 143  0
             } catch (FileNotFoundException e) {
 144  0
                 throw new ResourceManagerException("Fatal internal error: Backup sequence vanished");
 145  0
             } catch (IOException e) {
 146  0
                 throw new ResourceManagerException("Fatal internal error: Backup sequence value corrupted");
 147  0
             }
 148  
         }
 149  
 
 150  0
         long valueII = -1;
 151  0
         if (FileHelper.fileExists(pathII)) {
 152  
             try {
 153  0
                 valueII = readFromPath(pathII);
 154  0
                 if (valueII > valueI) {
 155  0
                     returnValue = valueII;
 156  
                 } else {
 157  
                     // if it is smaller than previous this *must* be an error as we constantly increment
 158  0
                     logger.logWarning("Latest sequence value smaller than previous, reverting to previous");
 159  0
                     FileHelper.deleteFile(pathII);
 160  0
                     returnValue = valueI;
 161  
                 }
 162  0
             } catch (NumberFormatException e) {
 163  0
                 logger.logWarning("Latest sequence value corrupted, reverting to previous");
 164  0
                 FileHelper.deleteFile(pathII);
 165  0
                 returnValue = valueI;
 166  0
             } catch (FileNotFoundException e) {
 167  0
                 logger.logWarning("Can not find latest sequence value, reverting to previous");
 168  0
                 FileHelper.deleteFile(pathII);
 169  0
                 returnValue = valueI;
 170  0
             } catch (IOException e) {
 171  0
                 logger.logWarning("Can not read latest sequence value, reverting to previous");
 172  0
                 FileHelper.deleteFile(pathII);
 173  0
                 returnValue = valueI;
 174  0
             }
 175  
         } else {
 176  0
             logger.logWarning("Can not read latest sequence value, reverting to previous");
 177  0
             returnValue = valueI;
 178  
         }
 179  
 
 180  0
         if (returnValue != -1) {
 181  0
             return returnValue;
 182  
         } else {
 183  0
             throw new ResourceManagerException("Fatal internal error: Could not compute valid sequence value");
 184  
         }
 185  
     }
 186  
 
 187  
     protected void write(String sequenceName, long value) throws ResourceManagerException {
 188  0
         String pathII = getPathII(sequenceName);
 189  
 
 190  0
         File f2 = new File(pathII);
 191  
         // by contract when this method is called an f2 exists it must be valid
 192  0
         if (f2.exists()) {
 193  
             // move previous value to backup position
 194  0
             String pathI = getPathI(sequenceName);
 195  0
             File f1 = new File(pathI);
 196  0
             f1.delete();
 197  0
             if (!f2.renameTo(f1)) {
 198  0
                 throw new ResourceManagerException("Fatal internal error: Can not create backup value at" + pathI);
 199  
             }
 200  
         }
 201  
         try {
 202  0
             if (!f2.createNewFile()) {
 203  0
                 throw new ResourceManagerException("Fatal internal error: Can not create new value at" + pathII);
 204  
             }
 205  0
         } catch (IOException e) {
 206  0
             throw new ResourceManagerException("Fatal internal error: Can not create new value at" + pathII, e);
 207  0
         }
 208  0
         writeToPath(pathII, value);
 209  0
     }
 210  
 
 211  
     protected String getPathI(String sequenceName) {
 212  0
         return storeDir + "/" + sequenceName + "_1.seq";
 213  
     }
 214  
 
 215  
     protected String getPathII(String sequenceName) {
 216  0
         return storeDir + "/" + sequenceName + "_2.seq";
 217  
     }
 218  
 
 219  
     protected long readFromPath(String path)
 220  
         throws ResourceManagerException, NumberFormatException, FileNotFoundException, IOException {
 221  0
         File file = new File(path);
 222  0
         BufferedReader reader = null;
 223  
         try {
 224  0
             InputStream is = new FileInputStream(file);
 225  
 
 226  
             // we do not care for encoding as we only have numbers
 227  0
             reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
 228  0
             String valueString = reader.readLine();
 229  0
             long value = Long.parseLong(valueString);
 230  0
             return value;
 231  0
         } catch (UnsupportedEncodingException e) {
 232  0
             throw new ResourceManagerException("Fatal internal error, encoding UTF-8 unknown");
 233  
         } finally {
 234  0
             if (reader != null) {
 235  
                 try {
 236  0
                     reader.close();
 237  0
                 } catch (IOException e) {
 238  0
                 }
 239  
 
 240  
             }
 241  
         }
 242  
     }
 243  
 
 244  
     protected void writeToPath(String path, long value) throws ResourceManagerException {
 245  0
         File file = new File(path);
 246  0
         BufferedWriter writer = null;
 247  
         try {
 248  0
             OutputStream os = new FileOutputStream(file);
 249  0
             writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
 250  0
             String valueString = Long.toString(value);
 251  0
             writer.write(valueString);
 252  0
             writer.write('\n');
 253  0
         } catch (FileNotFoundException e) {
 254  0
             throw new ResourceManagerException("Fatal internal error: Can not find sequence at " + path);
 255  0
         } catch (IOException e) {
 256  0
             throw new ResourceManagerException("Fatal internal error: Can not write to sequence at " + path);
 257  
         } finally {
 258  0
             if (writer != null) {
 259  
                 try {
 260  0
                     writer.close();
 261  0
                 } catch (IOException e) {
 262  0
                 }
 263  
 
 264  
             }
 265  0
         }
 266  0
     }
 267  
 }