is-collector/src/org/gcube/informationsystem/collector/impl/xmlstorage/exist/XMLStorageManager.java

627 lines
20 KiB
Java
Executable File

package org.gcube.informationsystem.collector.impl.xmlstorage.exist;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.ResourceSet;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.modules.XMLResource;
import org.xmldb.api.modules.CollectionManagementService;
import org.exist.xmldb.DatabaseInstanceManager;
import org.exist.storage.DBBroker;
import org.gcube.common.core.utils.logging.GCUBELog;
import org.gcube.informationsystem.collector.impl.persistence.AggregatorPersistentResource;
import org.gcube.informationsystem.collector.impl.persistence.PersistentResource;
import org.gcube.informationsystem.collector.impl.persistence.PersistentResource.RESOURCETYPE;
import org.gcube.informationsystem.collector.impl.xmlstorage.exist.XMLStorageManager;
import org.gcube.informationsystem.collector.impl.xmlstorage.exist.XQuery;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
;
/**
* A thread safe manager to interact with the XML Storage repository.
*
* @author Manuele Simi (ISTI-CNR)
*
*/
public class XMLStorageManager {
protected static String URI = "xmldb:exist://";
protected static String driver = "org.exist.xmldb.DatabaseImpl";
private static GCUBELog logger = new GCUBELog(XMLStorageManager.class);
private Database database;
// private Collection currentCollection;
private Collection rootCollection;
private Collection profilesRootCollection;
// lock for write operations
private Lock writeLock;
// flag to warn when the DB is locked
private boolean locked = false;
private static final String PROPERTIES_COLLECTION_NAME = "Properties";
protected static String PROFILES_COLLECTION_NAME = "Profiles";
/**
* Creates a new manager
*
*/
public XMLStorageManager() {
logger.debug("Creating a new XMLStorageManager");
writeLock = new ReentrantLock();
}
/**
* Initializes the local XML Storage repository
*
* @throws Exception
* if the connection to eXist or its initialization fail
*/
public void initialize() throws Exception {
try {
logger.info("connecting to eXist DB...");
// this.printEnv();
// lock the instance
writeLock.lock();
// register/create the DB instance
Class<?> cl = Class.forName(driver);
this.database = (Database) cl.newInstance();
database.setProperty("create-database", "true");
DatabaseManager.registerDatabase(this.database);
// try to load the collections for props and profiles
logger.debug("Initializing the root collection");
this.rootCollection = DatabaseManager.getCollection(URI + DBBroker.ROOT_COLLECTION, "admin", "admin");
if (this.rootCollection == null) {
logger.error("invalid root collection!");
throw new Exception("unable to load root collection");
}
logger.debug("Initializing the collection Profiles");
this.profilesRootCollection = this.rootCollection.getChildCollection(XMLStorageManager.PROFILES_COLLECTION_NAME);
if (this.profilesRootCollection == null) {
logger.debug("Creating Profiles collection");
this.profilesRootCollection = this.createCollection(this.rootCollection, XMLStorageManager.PROFILES_COLLECTION_NAME);
logger.debug("Profiles collection created");
}
if (this.profilesRootCollection == null) {
throw new Exception("Unable to load/create Profiles collection");
}
this.rootCollection.setProperty("pretty", "true");
this.rootCollection.setProperty("encoding", "UTF-8");
this.profilesRootCollection.setProperty("pretty", "true");
this.profilesRootCollection.setProperty("encoding", "UTF-8");
} catch (XMLDBException edb) {
logger.error("unable to initialize XML storage ", edb);
throw new Exception("unable to initialize XML storage");
} catch (Exception e) {
logger.debug("unable to initialize XML storage ", e);
throw new Exception("unable to initialize XML storage");
} catch (java.lang.NoClassDefFoundError ncdfe) {
logger.debug("unable to initialize XML storage", ncdfe);
throw new Exception("unable to initialize XML storage");
} finally {
writeLock.unlock();
}
}
/**
* Shutdowns the XML Storage repository
*
* @return true if the operation succeed
*/
public boolean shutdown() {
writeLock.lock();
try {
DatabaseInstanceManager manager = (DatabaseInstanceManager) rootCollection.getService("DatabaseInstanceManager", "1.0");
manager.shutdown();
} catch (XMLDBException edb) {
logger.error("Unable to shutdown XML storage");
logger.error("" + edb.getCause());
} finally {
writeLock.unlock();
}
return true;
}
/**
* Loads a collection. If it does not exist, the collection is created.
*
* @param parentCollection
* the parent collection of the collection to load
* @param collectionName
* name of the collection to load
* @return the collection
*/
private Collection loadCollection(Collection parentCollection, String collectionName) {
// set the current collection
Collection currentCollection = null;
try {
currentCollection = parentCollection.getChildCollection(collectionName);
if (currentCollection == null) {
// the collection does not exist, it is created
logger.info("Creating a new collection " + collectionName + "...");
currentCollection = this.createCollection(parentCollection, collectionName);
}
currentCollection.setProperty("pretty", "true");
currentCollection.setProperty("encoding", "UTF-8");
} catch (XMLDBException edb) {
logger.error("failed to create collection " + collectionName + "!");
logger.error("" + edb.getCause());
}
return currentCollection;
}
/**
* Discards the current collection
*
*/
private void resetCollection(Collection currentCollection) {
try {
currentCollection.close();
} catch (XMLDBException edb) {
// Catch any issues with closing the exception.
logger.error("unable to close collection " + edb.getMessage());
}
currentCollection = null;
}
/**
* Loads the collection containing the WS-ResourceProperties documents. It must be used when
* quering/storing/updating WS-ResourceProperties documents
*
* @return the Collection
*/
public Collection loadPropertiesCollection() {
logger.debug("Loading collection Properties... ");
return this.loadCollection(this.rootCollection, XMLStorageManager.PROPERTIES_COLLECTION_NAME);
}
/**
* Loads from the children of the Profile Collection, the collection identified by the given
* name. It must be used when quering/storing/updating a particular kind of profile
*
* @param collectionName
* the child collection of the Profile collection to load
* @return the Collection
*/
public Collection loadProfileCollection(String collectionName) {
logger.debug("Loading collection " + collectionName + "... ");
return this.loadCollection(this.profilesRootCollection, collectionName);
}
/**
* Loads the parent collection of all collections containing resources profiles. It must be used
* when quering all the profiles at the same time
*
* @return the Collection
*/
public Collection loadAllProfilesCollection() {
logger.debug("Loading all profiles collection... ");
return this.loadCollection(this.rootCollection, XMLStorageManager.PROFILES_COLLECTION_NAME);
}
/**
* Loads the root collection. It must be used when quering all the information maintained by the
* DB instance at the same time
*
* @return the Collection
*/
public Collection loadAllCollections() {
Collection currentCollection = null;
logger.debug("Loading all collections... ");
// return this.loadCollection(this.rootCollection,
// XMLStorageManager.PROFILES_COLLECTION_NAME);
try {
currentCollection = DatabaseManager.getCollection(URI + DBBroker.ROOT_COLLECTION, "admin", "admin");
} catch (XMLDBException edb) {
logger.error("Failed to load all collections!");
logger.error("", edb);
}
return currentCollection;
}
/**
* Stores a AggregatorPersistentResource in the current collection. If the resource already
* exists in the storage, it is updated.
*
* @param resource
* the resource to store
* @throws Exception
* if the storing fails
*/
public void storeResource(PersistentResource resource) throws Exception {
Collection currentCollection = null;
if (resource.getType() == RESOURCETYPE.Profile) {
// the entry contains a gCube resource profile
currentCollection = this.loadProfileCollection(resource.getProfileType());
} else {
// the entry contains generic properties
currentCollection = this.loadPropertiesCollection();
}
if (currentCollection == null) {
logger.error("Unable to open the Collection");
return;
}
writeLock.lock();
this.locked = true;
try {
XMLResource document = (XMLResource) currentCollection.createResource(resource.getID(), "XMLResource");
document.setContent(resource.toString());
logger.debug("Storing/updating resource " + document.getId() + " in collection " + currentCollection.getName() + "...");
currentCollection.storeResource(document);
logger.debug("...done");
} catch (XMLDBException edb) {
logger.error("Failed to store resource " + resource.getID());
logger.error("" + edb.errorCode + " " + edb.getMessage(), edb);
} catch (Exception e) {
logger.error("" + e.getMessage(), e);
} finally {
this.resetCollection(currentCollection);
writeLock.unlock();
this.locked = false;
}
}
/**
*
* @return true if the connection to eXist is locked
*/
public boolean isLocked() {
return this.locked;
}
/**
* Retrieves a resource from the storage given its ID
*
* @param resourceID
* @return
* @throws Exception
*/
public AggregatorPersistentResource retrieveResourceFromID(String resourceID) throws Exception {
XMLResource res = null;
Collection currentCollection = this.loadAllCollections();
try {
res = (XMLResource) currentCollection.getResource(resourceID);
if (res == null)
logger.warn("Resource " + resourceID + " not found!");
} catch (XMLDBException edb) {
logger.error("Failed to retrieve document " + resourceID);
logger.error("" + edb.errorCode + " " + edb.getMessage(), edb);
throw new Exception();
}
return new AggregatorPersistentResource(res);
}
/**
* Retrieves a resource from the storage given its ID
*
* @param resourceID
* @return
* @throws Exception
*/
public AggregatorPersistentResource retrievePropertyResourceFromID(String resourceID) throws Exception {
XMLResource res = null;
Collection currentCollection = this.loadPropertiesCollection();
try {
res = (XMLResource) currentCollection.getResource(resourceID);
if (res == null)
logger.warn("Resource " + resourceID + " not found!");
} catch (XMLDBException edb) {
logger.error("Failed to retrieve document " + resourceID);
logger.error("" + edb.errorCode + " " + edb.getMessage(), edb);
throw new Exception();
}
return new AggregatorPersistentResource(res);
}
/**
*
* @param xpathquery
* @return
*/
public AggregatorPersistentResource executeXPathQuery(String xpathquery) {
AggregatorPersistentResource res = null;
// ArrayList<AggregatorPersistentResource> results = new
// ArrayList<AggregatorPersistentResource>();
/*
* try { // get query-service XPathQueryServiceImpl service = (XPathQueryServiceImpl)
* currentCollection.getService("XPathQueryService", "1.0"); // set pretty-printing on
* service.setProperty(OutputKeys.INDENT, "yes"); service.setProperty(OutputKeys.ENCODING,
* "UTF-8"); ResourceSet set = service.query(xpathquery); logger.debug("number of returned
* documents: " + set.getSize()); ResourceIterator i = set.getIterator();
* while(i.hasMoreResources()) { res = new AggregatorPersistentResource((XMLResource)
* i.nextResource()); System.out.println("DILIGENT resource " + i + " " + res.toString()); }
*
* for (int i = 0; i < (int) set.getSize(); i++) { res = new
* AggregatorPersistentResource((XMLResource) set.getResource((long) i));
* System.out.println("DILIGENT resource " + i + " " + res.toString()); } } catch
* (XMLDBException edb) { logger.error("failed to execute Xpath query " + xpathquery);
* edb.printStackTrace(); } catch (Exception e) { logger.error("exception " + xpathquery);
* logger.error(e.getStackTrace()); }
*/
return res;
}
/**
* Executes the given query on the current collection or on the root collection if any was
* previously loaded
*
* @param query
* - the query to run
* @return
*/
public ResourceSet executeXQuery(XQuery query) {
boolean retry = true;
int attempts = 0, max_attempts = 3;
ResourceSet result = null;
Collection currentCollection = null;
currentCollection = this.loadAllCollections();
while ((retry) && (attempts < max_attempts)) {
try {
// wait until the DB is unlocked
while (State.storage_manager.isLocked())
Thread.sleep(1000);
// execute query and get results in ResourceSet
if (currentCollection == null)
result = query.execute(this.rootCollection);
else
result = query.execute(currentCollection);
retry = false;
} catch (XMLDBException edb) {
logger.error("Failed to execute XQuery " + query.toString());
logger.error("Error details: " + edb.errorCode + " " + edb.getMessage(), edb);
// if the cause is a NullPointer, this can be due to a temporary
// lock on the database instance
if (edb.getCause() instanceof java.lang.NullPointerException) {
retry = true;
attempts++;
logger.warn("Trying a new attempt for query execution");
} else
retry = false;
} catch (Exception e) {
logger.error("", e);
// if the cause is a NullPointer, this can be due to a temporary
// lock on the database instance
if (e instanceof java.lang.NullPointerException) {
retry = true;
attempts++;
logger.warn("Trying a new attempt for query execution");
} else
retry = false;
}
}
this.resetCollection(currentCollection);
return result;
}
/**
* Deletes a WS-ResourceProperties resource identified by the given ID
*/
synchronized public void retrieveAndDeleteResourceFromID(String resourceID) throws Exception {
if (resourceID == null) {
logger.warn("Invalid resource ID");
return;
}
Collection propCollection = this.loadPropertiesCollection();
if (propCollection == null) {
logger.error("Unable to load collection Properties!");
throw new Exception();
}
try {
logger.info("Trying to remove resource " + resourceID + " from collection " + propCollection.getName());
deleteResource(resourceID, propCollection);
} catch (XMLDBException edb) {
logger.error("Failed to remove the resource from the storage! ");
logger.error("", edb);
throw new Exception();
} finally {
this.resetCollection(propCollection);
}
}
/**
* Deletes a Profile resource identified by the given ID
*/
synchronized public void retrieveAndDeleteProfileFromID(String profileID, String profileType) throws Exception {
if (profileID == null) {
logger.warn("Invalid profile ID");
return;
}
Collection profileCollection = this.loadProfileCollection(profileType);
if (profileCollection == null) {
logger.error("Uunable to load collection Profile!");
throw new Exception("unable to load collection Profile");
}
try {
logger.info("Trying to remove profile '" + profileID + "' from collection " + profileCollection.getName());
deleteResource(profileID, profileCollection);
} catch (XMLDBException edb) {
logger.error("Failed to remove the profile from the storage! ");
logger.error("", edb);
throw new Exception();
} finally {
this.resetCollection(profileCollection);
}
}
/**
* Deletes the resource with the given ID from the local storage
*
* @param resourceID
* - the ID of the resource
* @param col
* - the collection from which the resource has to be removed
* @throws Exception
*/
private void deleteResource(String resourceID, Collection col) throws Exception {
XMLResource res = null;
// lock the instance
this.writeLock.lock();
this.locked = true;
try {
res = (XMLResource) col.getResource(resourceID);
if (res == null)
logger.warn("Resource " + resourceID + " not found!");
else {
col.removeResource(res);
logger.info("Resource successfully removed");
}
} catch (XMLDBException edb) {
logger.error("Failed to retrieve resource " + resourceID);
logger.error("", edb);
throw new Exception();
} finally {
this.resetCollection(col);
this.writeLock.unlock();
this.locked = false;
}
}
/**
* Prints all the IDs of the Resources stored in the DB instance
*
*/
public void printResourcesIDs() {
String[] ress;
Collection currentCollection = null;
currentCollection = this.loadAllCollections();
try {
if (currentCollection == null) {
ress = this.rootCollection.listResources();
} else {
ress = currentCollection.listResources();
}
for (int i = 0; i < ress.length; i++) {
logger.debug("Resource ID:" + ress[i]);
}
} catch (XMLDBException edb) {
logger.error("Failed to read resource IDs ", edb);
} finally {
this.resetCollection(currentCollection);
}
}
/**
* Creates a new collection
*
* @param collectionName
* @return the create Collection object
*/
private Collection createCollection(Collection parentCollection, String collectionName) {
Collection col = null;
this.writeLock.lock();
try {
CollectionManagementService mgtService = (CollectionManagementService) parentCollection.getService("CollectionManagementService", "1.0");
col = mgtService.createCollection(collectionName);
} catch (XMLDBException edb) {
logger.error("Failed to create collection " + collectionName, edb);
} finally {
this.writeLock.unlock();
}
return col;
}
/**
* Delete the collection named PROPERTIES_COLLECTION_NAME from the storage
*/
public void deleteAllProperties() {
this.writeLock.lock();
try {
logger.info("Trying to delete the collection " + XMLStorageManager.PROPERTIES_COLLECTION_NAME + "...");
CollectionManagementService mgtService = (CollectionManagementService) rootCollection.getService("CollectionManagementService", "1.0");
mgtService.removeCollection(XMLStorageManager.PROPERTIES_COLLECTION_NAME);
logger.info("Collection deleted");
} catch (XMLDBException edb) {
logger.warn("Unable to delete the collection " + XMLStorageManager.PROPERTIES_COLLECTION_NAME + ": " + edb.toString());
} finally {
this.writeLock.unlock();
}
}
public String[] listAllPropertiesIDs() {
return listAllColletionIDs(this.loadPropertiesCollection());
}
private String[] listAllColletionIDs(Collection collection) {
String[] ids = null;
String collectionName = "";
try {
collectionName = collection.getName();
logger.debug("Retrieving all resource IDs from collection " + collectionName);
ids = new String[collection.getResourceCount()];
ids = collection.listResources();
logger.debug("Retrieved " + ids.length + " elements");
} catch (XMLDBException edb) {
logger.warn("Unable to retrieve ids from collection " + collectionName + " " + edb.toString());
}
return ids;
}
/**
* Loads a file content in a String
*
* @param file
* path and name of the file to read
* @return the file content
* @throws IOException
*/
protected static String readFile(String file) throws IOException {
BufferedReader f = new BufferedReader(new FileReader(file));
String line;
StringBuffer content = new StringBuffer();
while ((line = f.readLine()) != null)
content.append(line);
f.close();
return content.toString();
}
}