/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.server.storage.lowlevel;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import fedora.common.FaultException;
import fedora.server.errors.LowlevelStorageException;
import fedora.server.errors.ObjectAlreadyInLowlevelStorageException;
import fedora.server.errors.ObjectNotInLowlevelStorageException;
/**
* @author Bill Niebel
*/
public class DefaultLowlevelStorage
implements ILowlevelStorage, IListable {
public static final String REGISTRY_NAME = "registryName";
public static final String OBJECT_REGISTRY_TABLE = "objectPaths";
public static final String DATASTREAM_REGISTRY_TABLE = "datastreamPaths";
public static final String OBJECT_STORE_BASE = "object_store_base";
public static final String DATASTREAM_STORE_BASE = "datastream_store_base";
public static final String FILESYSTEM = "file_system";
public static final String PATH_ALGORITHM = "path_algorithm";
public static final String PATH_REGISTRY = "path_registry";
private final Store objectStore;
private final Store datastreamStore;
public DefaultLowlevelStorage(Map<String, Object> configuration)
throws LowlevelStorageException {
String objectStoreBase = (String) configuration.get(OBJECT_STORE_BASE);
String datastreamStoreBase =
(String) configuration.get(DATASTREAM_STORE_BASE);
Map<String, Object> objConfig = new HashMap<String, Object>();
objConfig.putAll(configuration);
objConfig.put(REGISTRY_NAME, OBJECT_REGISTRY_TABLE);
objConfig.put("storeBase", objectStoreBase);
objConfig.put("storeBases", new String[] {objectStoreBase});
objectStore = new Store(objConfig);
Map<String, Object> dsConfig = new HashMap<String, Object>();
dsConfig.putAll(configuration);
dsConfig.put(REGISTRY_NAME, DATASTREAM_REGISTRY_TABLE);
dsConfig.put("storeBase", datastreamStoreBase);
dsConfig.put("storeBases", new String[] {datastreamStoreBase});
datastreamStore = new Store(dsConfig);
}
public void addObject(String pid, InputStream content)
throws LowlevelStorageException {
objectStore.add(pid, content);
}
public void replaceObject(String pid, InputStream content)
throws LowlevelStorageException {
objectStore.replace(pid, content);
}
public InputStream retrieveObject(String pid)
throws LowlevelStorageException {
return objectStore.retrieve(pid);
}
public void removeObject(String pid) throws LowlevelStorageException {
objectStore.remove(pid);
}
public void rebuildObject() throws LowlevelStorageException {
objectStore.rebuild();
}
public void auditObject() throws LowlevelStorageException {
objectStore.audit();
}
public void addDatastream(String pid, InputStream content)
throws LowlevelStorageException {
datastreamStore.add(pid, content);
}
public void replaceDatastream(String pid, InputStream content)
throws LowlevelStorageException {
datastreamStore.replace(pid, content);
}
public InputStream retrieveDatastream(String pid)
throws LowlevelStorageException {
return datastreamStore.retrieve(pid);
}
public void removeDatastream(String pid) throws LowlevelStorageException {
datastreamStore.remove(pid);
}
public void rebuildDatastream() throws LowlevelStorageException {
datastreamStore.rebuild();
}
public void auditDatastream() throws LowlevelStorageException {
datastreamStore.audit();
}
public Iterator<String> listObjects() {
return objectStore.list();
}
public Iterator<String> listDatastreams() {
return datastreamStore.list();
}
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(REGISTRY_NAME);
String filesystem = (String) configuration.get(FILESYSTEM);
String pathAlgorithm = (String) configuration.get(PATH_ALGORITHM);
String pathRegistry = (String) configuration.get(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 = FILESYSTEM;
cclass = loader.loadClass(filesystem);
constructor = cclass.getConstructor(parameterTypes);
fileSystem = (FileSystem) constructor.newInstance(parameters);
failureReason = PATH_ALGORITHM;
cclass = loader.loadClass(pathAlgorithm);
constructor = cclass.getConstructor(parameterTypes);
this.pathAlgorithm =
(PathAlgorithm) constructor.newInstance(parameters);
failureReason = 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
*/
public final void add(String pid, InputStream content)
throws LowlevelStorageException {
String filePath;
File file = null;
try { //check that object is not already in store
filePath = pathRegistry.get(pid);
ObjectAlreadyInLowlevelStorageException already =
new ObjectAlreadyInLowlevelStorageException("" + pid);
throw already;
} catch (ObjectNotInLowlevelStorageException not) {
// OK: keep going
}
filePath = pathAlgorithm.get(pid);
if (filePath == null || filePath.equals("")) { //guard against algorithm implementation
LowlevelStorageException nullPath =
new LowlevelStorageException(true,
"null path from algorithm for pid "
+ pid);
throw nullPath;
}
try {
file = new File(filePath);
} catch (Exception eFile) { //purposefully general catch-all
LowlevelStorageException newFile =
new LowlevelStorageException(true,
"couldn't make File for "
+ filePath,
eFile);
throw newFile;
}
fileSystem.write(file, content);
pathRegistry.put(pid, filePath);
}
/**
* replace into low-level store content of Fedora object already in
* lowlevel store
*/
public final void replace(String pid, InputStream content)
throws LowlevelStorageException {
String filePath;
File file = null;
try {
filePath = pathRegistry.get(pid);
} catch (ObjectNotInLowlevelStorageException ffff) {
LowlevelStorageException noPath =
new LowlevelStorageException(false, "pid " + pid
+ " not in registry", ffff);
throw noPath;
}
if (filePath == null || filePath.equals("")) { //guard against registry implementation
LowlevelStorageException nullPath =
new LowlevelStorageException(true, "pid " + pid
+ " not in registry");
throw nullPath;
}
try {
file = new File(filePath);
} catch (Exception eFile) { //purposefully general catch-all
LowlevelStorageException newFile =
new LowlevelStorageException(true,
"couldn't make new File for "
+ filePath,
eFile);
throw newFile;
}
fileSystem.rewrite(file, content);
}
/** get content of Fedora object from low-level store */
public final InputStream retrieve(String pid)
throws LowlevelStorageException {
String filePath;
File file;
try {
filePath = pathRegistry.get(pid);
} catch (ObjectNotInLowlevelStorageException eReg) {
throw eReg;
}
if (filePath == null || filePath.equals("")) { //guard against registry implementation
LowlevelStorageException nullPath =
new LowlevelStorageException(true,
"null path from registry for pid "
+ pid);
throw nullPath;
}
try {
file = new File(filePath);
} catch (Exception eFile) { //purposefully general catch-all
LowlevelStorageException newFile =
new LowlevelStorageException(true,
"couldn't make File for "
+ filePath,
eFile);
throw newFile;
}
return fileSystem.read(file);
}
/** remove Fedora object from low-level store */
public final void remove(String pid) throws LowlevelStorageException {
String filePath;
File file = null;
try {
filePath = pathRegistry.get(pid);
} catch (ObjectNotInLowlevelStorageException eReg) {
throw eReg;
}
if (filePath == null || filePath.equals("")) { //guard against registry implementation
LowlevelStorageException nullPath =
new LowlevelStorageException(true,
"null path from registry for pid "
+ pid);
throw nullPath;
}
try {
file = new File(filePath);
} catch (Exception eFile) { //purposefully general catch-all
LowlevelStorageException newFile =
new LowlevelStorageException(true,
"couldn't make File for "
+ filePath,
eFile);
throw newFile;
}
pathRegistry.remove(pid);
fileSystem.delete(file);
}
}
}