Package org.jscsi.target.storage

Source Code of org.jscsi.target.storage.RandomAccessStorageModule

package org.jscsi.target.storage;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.FileChannel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* Instances of this class can be used for persistent storage of data. They are backed by a {@link RandomAccessFile},
* which will immediately write all changes in the data to hard-disk.
* <p>
* This class is <b>not</b> thread-safe.
*
* @see java.io.RandomAccessFile
* @author Andreas Ergenzinger
*/
public class RandomAccessStorageModule implements IStorageModule {

    private static final Logger LOGGER = LoggerFactory.getLogger(RandomAccessStorageModule.class);

    /**
     * The mode {@link String} parameter used during the instantiation of {@link #randomAccessFile}.
     * <p>
     * This will create a {@link RandomAccessFile} with both read and write privileges that will immediately save all
     * written data in the file.
     */
    private static final String MODE = "rwd";

    /**
     * The size of the medium in blocks.
     *
     * @see #VIRTUAL_BLOCK_SIZE
     */
    protected final long sizeInBlocks;

    /**
     * The {@link RandomAccessFile} used for accessing the storage medium.
     *
     * @see #MODE
     */
    private final RandomAccessFile randomAccessFile;

    /**
     * Creates a new {@link RandomAccessStorageModule} backed by the specified file. If no such file exists, a
     * {@link FileNotFoundException} will be thrown.
     *
     * @param sizeInBlocks blocksize for this module
     * @param file the path to the file serving as storage medium
     *
     * @throws FileNotFoundException if the specified file does not exist
     */
    public RandomAccessStorageModule (final long sizeInBlocks, final File file) throws FileNotFoundException {
        this.sizeInBlocks = sizeInBlocks;
        this.randomAccessFile = new RandomAccessFile(file, MODE);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void read (byte[] bytes, long storageIndex) throws IOException {
        randomAccessFile.seek(storageIndex);
        randomAccessFile.read(bytes, 0, bytes.length);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void write (byte[] bytes, long storageIndex) throws IOException {
        randomAccessFile.seek(storageIndex);
        randomAccessFile.write(bytes, 0, bytes.length);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final long getSizeInBlocks () {
        return sizeInBlocks;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final int checkBounds (final long logicalBlockAddress, final int transferLengthInBlocks) {
        if (logicalBlockAddress < 0 || logicalBlockAddress >= sizeInBlocks) return 1;
        if (transferLengthInBlocks < 0 || logicalBlockAddress + transferLengthInBlocks > sizeInBlocks) return 2;
        return 0;
    }

    /**
     * Closes the backing {@link RandomAccessFile}.
     *
     * @throws IOException if an I/O Error occurs
     */
    public final void close () throws IOException {
        randomAccessFile.close();
    }

    /**
     * This is the build method for creating instances of {@link RandomAccessStorageModule}. If there is no file to be
     * found at the specified <code>filePath</code>, then a {@link FileNotFoundException} will be thrown.
     *
     * @param file a path leading to the file serving as storage medium
     * @param storageLength length of storage (if not already existing)
     * @param create should the storage be created
     * @return a new instance of {@link RandomAccessStorageModule}
     * @throws IOException
     */
    public static synchronized final IStorageModule open (final File file, final long storageLength, final boolean create, Class<? extends IStorageModule> kind) throws IOException {
        long sizeInBlocks;
        sizeInBlocks = storageLength / VIRTUAL_BLOCK_SIZE;
        if (create && !(kind.equals(JCloudsStorageModule.class))) {
            createStorageVolume(file, storageLength);
        }
        // throws exc. if !file.exists()
        @SuppressWarnings ("unchecked")
        Constructor<? extends IStorageModule> cons = (Constructor<? extends IStorageModule>) kind.getConstructors()[0];
        try {
            IStorageModule mod = cons.newInstance(sizeInBlocks, file);
            return mod;
        } catch (InvocationTargetException | IllegalAccessException | InstantiationException exc) {
            throw new IOException(exc);
        }
    }

    /**
     * Creating a new file if not existing at the path defined in the config. Note that it is advised to create the file
     * beforehand.
     *
     * @param pConf configuration to be updated
     * @return true if creation successful, false if file already exists.
     * @throws IOException if anything weird happens
     */
    private static synchronized boolean createStorageVolume (final File pToCreate, final long pLength) throws IOException {
        FileOutputStream outStream = null;
        try {
            // if file exists, remove it after questioning.
            if (pToCreate.exists()) {
                if (!pToCreate.delete()) {
                    LOGGER.debug("Removal of old storage " + pToCreate.toString() + " unsucessful.");
                    return false;
                }
                LOGGER.debug("Removal of old storage " + pToCreate.toString() + " sucessful.");
            }

            // create file
            final File parent = pToCreate.getCanonicalFile().getParentFile();
            if (!parent.exists() && !parent.mkdirs()) { throw new FileNotFoundException("Unable to create directory: " + parent.getAbsolutePath()); }

            pToCreate.createNewFile();
            outStream = new FileOutputStream(pToCreate);
            final FileChannel fcout = outStream.getChannel();
            fcout.position(pLength);
            outStream.write(26); // Write EOF (not normally needed)
            fcout.force(true);
            LOGGER.debug("Creation of storage " + pToCreate.toString() + " sucessful.");
            return true;
        } catch (IOException e) {
            LOGGER.error("Exception creating storage volume " + pToCreate.getAbsolutePath() + ": " + e.getMessage(), e);
            throw e;
        } finally {
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (IOException e) {
                    LOGGER.error("Exception closing storage volume: " + e.getMessage(), e);
                }
            }
        }

    }

    /**
     * Deleting a storage recursive. Used for deleting a databases
     *
     * @param pFile which should be deleted included descendants
     * @return true if delete is valid
     */
    public static boolean recursiveDelete (final File pFile) {
        if (pFile.isDirectory()) {
            for (final File child : pFile.listFiles()) {
                if (!recursiveDelete(child)) { return false; }
            }
        }
        return pFile.delete();
    }

}
TOP

Related Classes of org.jscsi.target.storage.RandomAccessStorageModule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.