package org.fcrepo.server.storage.lowlevel.defaultstore;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import org.fcrepo.common.FaultException;
import org.fcrepo.server.errors.LowlevelStorageException;
import org.fcrepo.server.errors.ObjectAlreadyInLowlevelStorageException;
import org.fcrepo.server.storage.lowlevel.DefaultLowlevelStorage;
import org.fcrepo.server.storage.lowlevel.FileSystem;
import org.fcrepo.server.storage.lowlevel.PathAlgorithm;
import org.fcrepo.server.storage.lowlevel.PathRegistry;
public class Store {
private final PathAlgorithm pathAlgorithm;
private final PathRegistry pathRegistry;
private final FileSystem fileSystem;
//private final String storeBase;
public Store(Map<String, Object> configuration)
throws LowlevelStorageException {
String registryName = (String) configuration.get(DefaultLowlevelStorage.REGISTRY_NAME);
String filesystem = (String) configuration.get(DefaultLowlevelStorage.FILESYSTEM);
String pathAlgorithm = (String) configuration.get(DefaultLowlevelStorage.PATH_ALGORITHM);
String pathRegistry = (String) configuration.get(DefaultLowlevelStorage.PATH_REGISTRY);
//storeBase = (String)configuration.get("storeBase");
Object[] parameters = new Object[] {configuration};
Class<?>[] parameterTypes = new Class[] {Map.class};
ClassLoader loader = getClass().getClassLoader();
Class<?> cclass;
Constructor<?> constructor;
String failureReason = "";
try {
failureReason = DefaultLowlevelStorage.FILESYSTEM;
cclass = loader.loadClass(filesystem);
constructor = cclass.getConstructor(parameterTypes);
fileSystem = (FileSystem) constructor.newInstance(parameters);
failureReason = DefaultLowlevelStorage.PATH_ALGORITHM;
cclass = loader.loadClass(pathAlgorithm);
constructor = cclass.getConstructor(parameterTypes);
this.pathAlgorithm =
(PathAlgorithm) constructor.newInstance(parameters);
failureReason = DefaultLowlevelStorage.PATH_REGISTRY;
cclass = loader.loadClass(pathRegistry);
constructor = cclass.getConstructor(parameterTypes);
this.pathRegistry =
(PathRegistry) constructor.newInstance(parameters);
} catch (Exception e) {
LowlevelStorageException wrapper =
new LowlevelStorageException(true, "couldn't set up "
+ failureReason + " for " + registryName, e);
throw wrapper;
}
}
/**
* Gets the keys of all stored items.
*
* @return an iterator of all keys.
*/
public Iterator<String> list() {
try {
final Enumeration<String> keys = pathRegistry.keys();
return new Iterator<String>() {
public boolean hasNext() { return keys.hasMoreElements(); }
public String next() { return keys.nextElement(); }
public void remove() { throw new UnsupportedOperationException(); }
};
} catch (LowlevelStorageException e) {
throw new FaultException(e);
}
}
/**
* compares a. path registry with OS files; and b. OS files with
* registry
*/
public void audit() throws LowlevelStorageException {
pathRegistry.auditFiles();
pathRegistry.auditRegistry();
}
/** recreates path registry from OS files */
public void rebuild() throws LowlevelStorageException {
pathRegistry.rebuild();
}
/**
* add to lowlevel store content of Fedora object not already in
* lowlevel store
* @return size - size of the object stored
*/
public final long add(String pid, InputStream content)
throws LowlevelStorageException {
String filePath;
File file = null;
//check that object is not already in store
if (pathRegistry.exists(pid)){
throw new ObjectAlreadyInLowlevelStorageException(pid);
}
filePath = pathAlgorithm.get(pid);
if (filePath == null || filePath.equals("")) { //guard against algorithm implementation
throw new LowlevelStorageException(true,
"null path from algorithm for pid " + pid);
}
try {
file = new File(filePath);
} catch (Exception eFile) { //purposefully general catch-all
throw new LowlevelStorageException(true,
"couldn't make File for " + filePath, eFile);
}
fileSystem.write(file, content);
pathRegistry.put(pid, filePath);
return file.length();
}
/**
* replace into low-level store content of Fedora object already in
* lowlevel store
*/
public final long replace(String pid, InputStream content)
throws LowlevelStorageException {
File file = getFile(pid);
fileSystem.rewrite(file, content);
return file.length();
}
/** get content of Fedora object from low-level store */
public final InputStream retrieve(String pid)
throws LowlevelStorageException {
File file = getFile(pid);
return fileSystem.read(file);
}
/** get size of datastream */
public final long getSize(String pid) throws LowlevelStorageException {
File file = getFile(pid);
return file.length();
}
/** remove Fedora object from low-level store */
public final void remove(String pid) throws LowlevelStorageException {
File file = getFile(pid);
pathRegistry.remove(pid);
fileSystem.delete(file);
}
public final boolean exists(String pid) throws LowlevelStorageException {
if (pathRegistry.exists(pid)){
if (!getFile(pid).exists()) {
throw new LowlevelStorageException(true,
"file not at indexed path from registry for " + pid
+ "at " + pathRegistry.get(pid));
}
return true;
} else {
return false;
}
}
private File getFile(String pid) throws LowlevelStorageException {
String filePath = pathRegistry.get(pid);
if (filePath == null || filePath.equals("")) { //guard against registry implementation
throw new LowlevelStorageException(true,
"null path from registry for pid " + pid);
}
try {
return new File(filePath);
} catch (Exception eFile) { //purposefully general catch-all
throw new LowlevelStorageException(true,
"couldn't make File for " + filePath, eFile);
}
}
}