Coverage Report - org.jscsi.target.storage.RandomAccessStorageModule
 
Classes in this File Line Coverage Branch Coverage Complexity
RandomAccessStorageModule
0%
0/53
0%
0/28
0
 
 1  
 package org.jscsi.target.storage;
 2  
 
 3  
 
 4  
 import java.io.File;
 5  
 import java.io.FileNotFoundException;
 6  
 import java.io.FileOutputStream;
 7  
 import java.io.IOException;
 8  
 import java.io.RandomAccessFile;
 9  
 import java.lang.reflect.Constructor;
 10  
 import java.lang.reflect.InvocationTargetException;
 11  
 import java.nio.channels.FileChannel;
 12  
 
 13  
 import org.slf4j.Logger;
 14  
 import org.slf4j.LoggerFactory;
 15  
 
 16  
 
 17  
 /**
 18  
  * Instances of this class can be used for persistent storage of data. They are backed by a {@link RandomAccessFile},
 19  
  * which will immediately write all changes in the data to hard-disk.
 20  
  * <p>
 21  
  * This class is <b>not</b> thread-safe.
 22  
  * 
 23  
  * @see java.io.RandomAccessFile
 24  
  * @author Andreas Ergenzinger
 25  
  */
 26  
 public class RandomAccessStorageModule implements IStorageModule {
 27  
 
 28  0
     private static final Logger LOGGER = LoggerFactory.getLogger(RandomAccessStorageModule.class);
 29  
 
 30  
     /**
 31  
      * The mode {@link String} parameter used during the instantiation of {@link #randomAccessFile}.
 32  
      * <p>
 33  
      * This will create a {@link RandomAccessFile} with both read and write privileges that will immediately save all
 34  
      * written data in the file.
 35  
      */
 36  
     private static final String MODE = "rwd";
 37  
 
 38  
     /**
 39  
      * The size of the medium in blocks.
 40  
      * 
 41  
      * @see #VIRTUAL_BLOCK_SIZE
 42  
      */
 43  
     protected final long sizeInBlocks;
 44  
 
 45  
     /**
 46  
      * The {@link RandomAccessFile} used for accessing the storage medium.
 47  
      * 
 48  
      * @see #MODE
 49  
      */
 50  
     private final RandomAccessFile randomAccessFile;
 51  
 
 52  
     /**
 53  
      * Creates a new {@link RandomAccessStorageModule} backed by the specified file. If no such file exists, a
 54  
      * {@link FileNotFoundException} will be thrown.
 55  
      * 
 56  
      * @param sizeInBlocks blocksize for this module
 57  
      * @param file the path to the file serving as storage medium
 58  
      * 
 59  
      * @throws FileNotFoundException if the specified file does not exist
 60  
      */
 61  0
     public RandomAccessStorageModule (final long sizeInBlocks, final File file) throws FileNotFoundException {
 62  0
         this.sizeInBlocks = sizeInBlocks;
 63  0
         this.randomAccessFile = new RandomAccessFile(file, MODE);
 64  0
     }
 65  
 
 66  
     /**
 67  
      * {@inheritDoc}
 68  
      */
 69  
     @Override
 70  
     public void read (byte[] bytes, long storageIndex) throws IOException {
 71  0
         randomAccessFile.seek(storageIndex);
 72  0
         randomAccessFile.read(bytes, 0, bytes.length);
 73  0
     }
 74  
 
 75  
     /**
 76  
      * {@inheritDoc}
 77  
      */
 78  
     @Override
 79  
     public void write (byte[] bytes, long storageIndex) throws IOException {
 80  0
         randomAccessFile.seek(storageIndex);
 81  0
         randomAccessFile.write(bytes, 0, bytes.length);
 82  0
     }
 83  
 
 84  
     /**
 85  
      * {@inheritDoc}
 86  
      */
 87  
     @Override
 88  
     public final long getSizeInBlocks () {
 89  0
         return sizeInBlocks;
 90  
     }
 91  
 
 92  
     /**
 93  
      * {@inheritDoc}
 94  
      */
 95  
     @Override
 96  
     public final int checkBounds (final long logicalBlockAddress, final int transferLengthInBlocks) {
 97  0
         if (logicalBlockAddress < 0 || logicalBlockAddress >= sizeInBlocks) return 1;
 98  0
         if (transferLengthInBlocks < 0 || logicalBlockAddress + transferLengthInBlocks > sizeInBlocks) return 2;
 99  0
         return 0;
 100  
     }
 101  
 
 102  
     /**
 103  
      * Closes the backing {@link RandomAccessFile}.
 104  
      * 
 105  
      * @throws IOException if an I/O Error occurs
 106  
      */
 107  
     public final void close () throws IOException {
 108  0
         randomAccessFile.close();
 109  0
     }
 110  
 
 111  
     /**
 112  
      * This is the build method for creating instances of {@link RandomAccessStorageModule}. If there is no file to be
 113  
      * found at the specified <code>filePath</code>, then a {@link FileNotFoundException} will be thrown.
 114  
      * 
 115  
      * @param file a path leading to the file serving as storage medium
 116  
      * @param storageLength length of storage (if not already existing)
 117  
      * @param create should the storage be created
 118  
      * @return a new instance of {@link RandomAccessStorageModule}
 119  
      * @throws IOException
 120  
      */
 121  
     public static synchronized final IStorageModule open (final File file, final long storageLength, final boolean create, Class<? extends IStorageModule> kind) throws IOException {
 122  
         long sizeInBlocks;
 123  0
         sizeInBlocks = storageLength / VIRTUAL_BLOCK_SIZE;
 124  0
         if (create && !(kind.equals(JCloudsStorageModule.class))) {
 125  0
             createStorageVolume(file, storageLength);
 126  
         }
 127  
         // throws exc. if !file.exists()
 128  
         @SuppressWarnings ("unchecked")
 129  0
         Constructor<? extends IStorageModule> cons = (Constructor<? extends IStorageModule>) kind.getConstructors()[0];
 130  
         try {
 131  0
             IStorageModule mod = cons.newInstance(sizeInBlocks, file);
 132  0
             return mod;
 133  0
         } catch (InvocationTargetException | IllegalAccessException | InstantiationException exc) {
 134  0
             throw new IOException(exc);
 135  
         }
 136  
     }
 137  
 
 138  
     /**
 139  
      * Creating a new file if not existing at the path defined in the config. Note that it is advised to create the file
 140  
      * beforehand.
 141  
      * 
 142  
      * @param pConf configuration to be updated
 143  
      * @return true if creation successful, false if file already exists.
 144  
      * @throws IOException if anything weird happens
 145  
      */
 146  
     private static synchronized boolean createStorageVolume (final File pToCreate, final long pLength) throws IOException {
 147  0
         FileOutputStream outStream = null;
 148  
         try {
 149  
             // if file exists, remove it after questioning.
 150  0
             if (pToCreate.exists()) {
 151  0
                 if (!pToCreate.delete()) {
 152  0
                     LOGGER.debug("Removal of old storage " + pToCreate.toString() + " unsucessful.");
 153  0
                     return false;
 154  
                 }
 155  0
                 LOGGER.debug("Removal of old storage " + pToCreate.toString() + " sucessful.");
 156  
             }
 157  
 
 158  
             // create file
 159  0
             final File parent = pToCreate.getCanonicalFile().getParentFile();
 160  0
             if (!parent.exists() && !parent.mkdirs()) { throw new FileNotFoundException("Unable to create directory: " + parent.getAbsolutePath()); }
 161  
 
 162  0
             pToCreate.createNewFile();
 163  0
             outStream = new FileOutputStream(pToCreate);
 164  0
             final FileChannel fcout = outStream.getChannel();
 165  0
             fcout.position(pLength);
 166  0
             outStream.write(26); // Write EOF (not normally needed)
 167  0
             fcout.force(true);
 168  0
             LOGGER.debug("Creation of storage " + pToCreate.toString() + " sucessful.");
 169  0
             return true;
 170  0
         } catch (IOException e) {
 171  0
             LOGGER.error("Exception creating storage volume " + pToCreate.getAbsolutePath() + ": " + e.getMessage(), e);
 172  0
             throw e;
 173  
         } finally {
 174  0
             if (outStream != null) {
 175  
                 try {
 176  0
                     outStream.close();
 177  0
                 } catch (IOException e) {
 178  0
                     LOGGER.error("Exception closing storage volume: " + e.getMessage(), e);
 179  0
                 }
 180  
             }
 181  
         }
 182  
 
 183  
     }
 184  
 
 185  
     /**
 186  
      * Deleting a storage recursive. Used for deleting a databases
 187  
      * 
 188  
      * @param pFile which should be deleted included descendants
 189  
      * @return true if delete is valid
 190  
      */
 191  
     public static boolean recursiveDelete (final File pFile) {
 192  0
         if (pFile.isDirectory()) {
 193  0
             for (final File child : pFile.listFiles()) {
 194  0
                 if (!recursiveDelete(child)) { return false; }
 195  
             }
 196  
         }
 197  0
         return pFile.delete();
 198  
     }
 199  
 
 200  
 }