/* 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.resourceIndex;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jrdf.graph.ObjectNode;
import org.jrdf.graph.PredicateNode;
import org.jrdf.graph.SubjectNode;
import org.jrdf.graph.Triple;
import org.trippi.FlushErrorHandler;
import org.trippi.RDFFormat;
import org.trippi.TripleIterator;
import org.trippi.TripleUpdate;
import org.trippi.TriplestoreConnector;
import org.trippi.TrippiException;
import org.trippi.TupleIterator;
import fedora.common.Constants;
import fedora.server.Module;
import fedora.server.Parameterized;
import fedora.server.Server;
import fedora.server.errors.ModuleInitializationException;
import fedora.server.errors.ModuleShutdownException;
import fedora.server.errors.ResourceIndexException;
import fedora.server.storage.DOReader;
import fedora.server.utilities.status.ServerState;
/**
* Fedora's <code>ResourceIndex</code> as a configurable module.
*
* @author Chris Wilper
*/
public class ResourceIndexModule
extends Module
implements ResourceIndex {
/**
* The instance this module wraps.
*/
private ResourceIndex _ri;
/////////////////////////////////////
// Initialization & Module Methods //
/////////////////////////////////////
/**
* Instantiate the module.
*/
public ResourceIndexModule(Map<String, String> parameters,
Server server,
String role)
throws ModuleInitializationException {
super(parameters, server, role);
}
/**
* Perform post-initialization of this module. ResourceIndexModule takes the
* following parameters:
* <ul>
* <li> level (required, integer between 0 and 1)<br/> The level of
* indexing that should be performed. Values correspond to
* <code>INDEX_LEVEL_OFF</code>, and <code>INDEX_LEVEL_ON</code>.
* </li>
* <li> datastore (required)<br/> The name of the datastore element that
* contains the Trippi Connector configuration.
* <li> connectionPool (optional, default is ConnectionPoolManager's
* default)<br/> Which connection pool to use for updating the database of
* method information. </li>
* <li> syncUpdates (optional, default is false)<br/> Whether to flush the
* triple buffer before returning from object modification operations.
* Specifying this as true will ensure that RI queries always reflect the
* latest triples. </li>
* <li> alias:xyz (optional, uri)<br/> Any parameter starting with "alias:"
* will be put into Trippi's alias map, and can be used for queries. For
* example, alias:xyz with a value of urn:example:long:uri:x:y:z: will make
* it possible to use "xyz:a" to mean "urn:example:long:uri:x:y:z:a" in
* queries. </li>
* </ul>
*/
@Override
public void postInitModule() throws ModuleInitializationException {
int level = getRequiredInt("level", 0, 1);
if (level == 0) {
return;
}
boolean syncUpdates = getBoolean("syncUpdates", false);
try {
TriplestoreConnector connector =
getConnector(getServer()
.getDatastoreConfig(getRequired("datastore")));
TripleGenerator generator = new ModelBasedTripleGenerator();
_ri = new ResourceIndexImpl(connector,
generator,
level,
syncUpdates);
setAliasMap(getAliases());
} catch (Exception e) {
throw new ModuleInitializationException("Error initializing RI",
getRole(),
e);
}
}
private TriplestoreConnector getConnector(Parameterized datastore)
throws Exception {
if (datastore == null) {
throw new ModuleInitializationException("Specifed datastore "
+ "does not exist in fedora.fcfg", getRole());
}
Map<String, String> config = datastore.getParameters();
// make sure path, if specified and relative, is translated
// to an absolute path based on the value of FEDORA_HOME
String path = config.get("path");
if (path != null) {
config.put("path", datastore.getParameter("path", true));
}
String className = config.get("connectorClassName");
if (className == null) {
throw new ResourceIndexException("Required datastore parameter "
+ "is missing: connectorClassName");
}
getServer().getStatusFile().append(ServerState.STARTING,
"Initializing Triplestore");
return TriplestoreConnector.init(className, config);
}
private Map<String, String> getAliases() {
HashMap<String, String> map = new HashMap<String, String>();
Iterator<String> iter = parameterNames();
while (iter.hasNext()) {
String pName = iter.next();
String[] parts = pName.split(":");
if (parts.length == 2 && parts[0].equals("alias")) {
map.put(parts[1], getParameter(pName));
}
}
map.put("fedora", Constants.FEDORA.uri);
map.put("dc", Constants.DC.uri);
map.put("fedora-model", Constants.MODEL.uri);
map.put("fedora-rels-ext", Constants.RELS_EXT.uri);
map.put("fedora-view", Constants.VIEW.uri);
map.put("rdf", Constants.RDF.uri);
map.put("mulgara", Constants.MULGARA.uri);
map.put("xml-schema", Constants.RDF_XSD.uri);
return map;
}
private int getRequiredInt(String name, int min, int max)
throws ModuleInitializationException {
try {
int value = Integer.parseInt(getRequired(name));
if (value < min || value > max) {
throw new ModuleInitializationException(name
+ " parameter is out of range, expected [" + min + "-"
+ max + "]", getRole());
}
return value;
} catch (NumberFormatException e) {
throw new ModuleInitializationException(name
+ " parameter must be " + "an integer", getRole());
}
}
private boolean getBoolean(String name, boolean defaultValue)
throws ModuleInitializationException {
String value = getParameter(name);
if (value == null) {
return defaultValue;
}
value = value.toLowerCase();
if (value.equals("true") || value.equals("yes") || value.equals("on")) {
return true;
} else if (value.equals("false") || value.equals("no")
|| value.equals("off")) {
return false;
} else {
throw new ModuleInitializationException(name + " parameter, if "
+ "specified, must be a boolean (true or false)", getRole());
}
}
private String getRequired(String name)
throws ModuleInitializationException {
String value = getParameter(name);
if (value != null) {
return value;
} else {
throw new ModuleInitializationException(name + " parameter "
+ "is required", getRole());
}
}
/**
* Shutdown the RI module by closing the wrapped ResourceIndex.
*
* @throws ModuleShutdownException
* if any error occurs while closing.
*/
@Override
public void shutdownModule() throws ModuleShutdownException {
if (_ri != null) {
try {
_ri.close();
} catch (TrippiException e) {
throw new ModuleShutdownException("Error closing RI",
getRole(),
e);
}
}
}
///////////////////////////
// ResourceIndex Methods //
///////////////////////////
/**
* {@inheritDoc}
*/
public int getIndexLevel() {
if (_ri == null) {
return 0;
} else {
return _ri.getIndexLevel();
}
}
/**
* {@inheritDoc}
*/
public void addObject(DOReader reader) throws ResourceIndexException {
_ri.addObject(reader);
}
/**
* {@inheritDoc}
*/
public void modifyObject(DOReader oldReader, DOReader newReader)
throws ResourceIndexException {
_ri.modifyObject(oldReader, newReader);
}
/**
* {@inheritDoc}
*/
public void deleteObject(DOReader oldReader) throws ResourceIndexException {
_ri.deleteObject(oldReader);
}
/**
* {@inheritDoc}
*/
public void export(OutputStream out, RDFFormat format)
throws ResourceIndexException {
_ri.export(out, format);
}
///////////////////////////////
// TriplestoreReader methods //
///////////////////////////////
/**
* {@inheritDoc}
*/
public void setAliasMap(Map<String, String> aliasToPrefix)
throws TrippiException {
_ri.setAliasMap(aliasToPrefix);
}
/**
* {@inheritDoc}
*/
public Map<String, String> getAliasMap() throws TrippiException {
return _ri.getAliasMap();
}
/**
* {@inheritDoc}
*/
public TupleIterator findTuples(String queryLang,
String tupleQuery,
int limit,
boolean distinct) throws TrippiException {
return _ri.findTuples(queryLang, tupleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public int countTuples(String queryLang,
String tupleQuery,
int limit,
boolean distinct) throws TrippiException {
return _ri.countTuples(queryLang, tupleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public TripleIterator findTriples(String queryLang,
String tripleQuery,
int limit,
boolean distinct) throws TrippiException {
return _ri.findTriples(queryLang, tripleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public int countTriples(String queryLang,
String tripleQuery,
int limit,
boolean distinct) throws TrippiException {
return _ri.countTriples(queryLang, tripleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public TripleIterator findTriples(SubjectNode subject,
PredicateNode predicate,
ObjectNode object,
int limit) throws TrippiException {
return _ri.findTriples(subject, predicate, object, limit);
}
/**
* {@inheritDoc}
*/
public int countTriples(SubjectNode subject,
PredicateNode predicate,
ObjectNode object,
int limit) throws TrippiException {
return _ri.countTriples(subject, predicate, object, limit);
}
/**
* {@inheritDoc}
*/
public TripleIterator findTriples(String queryLang,
String tupleQuery,
String tripleTemplate,
int limit,
boolean distinct) throws TrippiException {
return _ri.findTriples(queryLang,
tupleQuery,
tripleTemplate,
limit,
distinct);
}
/**
* {@inheritDoc}
*/
public int countTriples(String queryLang,
String tupleQuery,
String tripleTemplate,
int limit,
boolean distinct) throws TrippiException {
return _ri.countTriples(queryLang,
tupleQuery,
tripleTemplate,
limit,
distinct);
}
/**
* {@inheritDoc}
*/
public String[] listTupleLanguages() {
return _ri.listTupleLanguages();
}
/**
* {@inheritDoc}
*/
public String[] listTripleLanguages() {
return _ri.listTripleLanguages();
}
/**
* {@inheritDoc}
*/
public void close() throws TrippiException {
// no-op; closing must be done via Module.shutdownModule
}
///////////////////////////////
// TriplestoreWriter methods //
///////////////////////////////
/**
* {@inheritDoc}
*/
public void add(List<Triple> triples, boolean flush) throws IOException,
TrippiException {
_ri.add(triples, flush);
}
/**
* {@inheritDoc}
*/
public void add(TripleIterator triples, boolean flush) throws IOException,
TrippiException {
_ri.add(triples, flush);
}
/**
* {@inheritDoc}
*/
public void add(Triple triple, boolean flush) throws IOException,
TrippiException {
_ri.add(triple, flush);
}
/**
* {@inheritDoc}
*/
public void delete(List<Triple> triples, boolean flush) throws IOException,
TrippiException {
_ri.delete(triples, flush);
}
/**
* {@inheritDoc}
*/
public void delete(TripleIterator triples, boolean flush)
throws IOException, TrippiException {
_ri.delete(triples, flush);
}
/**
* {@inheritDoc}
*/
public void delete(Triple triple, boolean flush) throws IOException,
TrippiException {
_ri.delete(triple, flush);
}
/**
* {@inheritDoc}
*/
public void flushBuffer() throws IOException, TrippiException {
_ri.flushBuffer();
}
/**
* {@inheritDoc}
*/
public void setFlushErrorHandler(FlushErrorHandler h) {
_ri.setFlushErrorHandler(h);
}
/**
* {@inheritDoc}
*/
public int getBufferSize() {
return _ri.getBufferSize();
}
/**
* {@inheritDoc}
*/
public List<TripleUpdate> findBufferedUpdates(SubjectNode subject,
PredicateNode predicate,
ObjectNode object,
int updateType) {
return _ri.findBufferedUpdates(subject, predicate, object, updateType);
}
}