/**
* DataManager.java
* Author: Francesco Rosso (rosso@eurix.it)
* Contributors: Francesco Gallo (gallo@eurix.it)
*
* This file is part of PrestoPRIME Preservation Platform (P4).
*
* Copyright (C) 2009-2012 EURIX Srl, Torino, Italy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.prestoprime.datamanagement;
import it.eurix.archtools.data.DataException;
import it.eurix.archtools.data.DataManager;
import it.eurix.archtools.data.model.AIP;
import it.eurix.archtools.data.model.DIP;
import it.eurix.archtools.data.model.DIP.DCField;
import it.eurix.archtools.data.model.IPException;
import it.eurix.archtools.data.model.InformationPackage;
import it.eurix.archtools.data.model.SIP;
import it.eurix.archtools.persistence.DatabaseException;
import it.eurix.archtools.user.UserManager.UserRole;
import it.eurix.archtools.workflow.jaxb.StatusType;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import eu.prestoprime.conf.ConfigurationManager;
import eu.prestoprime.datamanagement.P4PersistenceManager.P4Collection;
import eu.prestoprime.datamanagement.impl.P4AIP;
import eu.prestoprime.datamanagement.impl.P4DIP;
import eu.prestoprime.datamanagement.impl.P4InformationPackage;
import eu.prestoprime.datamanagement.impl.P4SIP;
import eu.prestoprime.model.ModelUtils;
import eu.prestoprime.model.ModelUtils.P4JAXBPackage;
import eu.prestoprime.model.datatypes.Datatype;
import eu.prestoprime.model.mets.MetsType;
import eu.prestoprime.model.terms.Service;
import eu.prestoprime.model.terms.Service.Terms;
public class P4DataManager extends DataManager<P4PersistenceManager.P4Collection> {
private static enum P4Query {
IP_BY_ID("xmldb/queryAIPByID.xq"),
DC_BY_ID("xmldb/queryDC.xq"),
IP_BY_MD("xmldb/queryAIPByMD.xq"),
IP_BY_DCID("xmldb/queryIPByDCID.xq"),
WFSTATUS("xmldb/queryWfStatus.xq"),
IP_BY_OAIPMH("xmldb/queryAIPByOAIPMH.xq"),
AIP_BY_FORMAT_RISK("xmldb/queryAIPByFormatRisk.xq");
private String queryFile;
private P4Query(String queryFile) {
this.queryFile = queryFile;
}
public URL getQueryFile() {
return Thread.currentThread().getContextClassLoader().getResource(queryFile);
}
@Override
public String toString() {
return queryFile;
}
};
private static P4DataManager instance;
private P4DataManager() {
super(P4PersistenceManager.getInstance());
}
public static P4DataManager getInstance() {
if (instance == null)
instance = new P4DataManager();
return instance;
}
@Override
public synchronized String createNewSIP(File file) throws DataException {
// get resource id
UUID uuid = super.generateUniqueId();
try {
// get mets
Unmarshaller unmarshaller = ModelUtils.getUnmarshaller(P4JAXBPackage.DATA_MODEL);
MetsType mets = (MetsType) unmarshaller.unmarshal(file);
// set id
mets.setOBJID(uuid.toString());
// get node
Node node = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
ModelUtils.getMarshaller(P4JAXBPackage.DATA_MODEL).marshal(mets, node);
P4SIP p4SIP = new P4SIP(uuid.toString(), node);
// write to persistence DB
p4SIP.selfRelease();
// check DC identifier uniqueness
SIP sip = this.getSIPByID(uuid.toString());
List<String> currentDCList = sip.getDCField(DCField.identifier);
for (String identifier : currentDCList) {
List<String> otherDCList = this.getSIPByDCID(identifier);
for (String tmpID : otherDCList) {
if (!tmpID.equals(uuid.toString())) {
// delete new SIP
this.deleteSIP(uuid.toString());
logger.error("Found another SIP with same DC identifier...");
throw new DataException("Found another SIP with same DC identifier...");
}
}
}
this.releaseIP(sip);
return uuid.toString();
} catch (JAXBException e) {
logger.error("Unable to parse SIP file...");
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IPException e) {
logger.error("Unable to write for first time the new SIP...");
e.printStackTrace();
}
throw new DataException("Unable to create new SIP...");
}
@Override
public synchronized SIP getSIPByID(String id) throws DataException {
String key = id;
InformationPackage IP = this.getRunningIP(key);
if (IP != null)
if (IP instanceof SIP)
return (SIP) IP;
else
throw new DataException("Error with keys: this key corresponds to a running InformationPackage, but it isn't an SIP...");
else {
logger.debug("SIP not cached, quering persistence DB for new instance...");
Map<String, String> params = new HashMap<>();
params.put("AIPID", id);
// params.put("COLLECTION", P4Collection.TEMP_COLLECTION.getCollectionName());
try {
List<String> res = persistenceManager.executeQuery(P4Query.IP_BY_ID.getQueryFile(), P4Collection.TEMP_COLLECTION, params, false);
if (res.size() == 1) {
String result = res.get(0);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
P4SIP p4SIP = new P4SIP(id, dbf.newDocumentBuilder().parse(new ByteArrayInputStream(result.getBytes())));
this.registerRunningIP(key, p4SIP);
return p4SIP;
}
} catch (DatabaseException e) {
e.printStackTrace();
} catch (SAXException | IOException | ParserConfigurationException e) {
e.printStackTrace();
}
}
throw new DataException("Unable to find an SIP with id " + id);
}
@Override
public synchronized void consolidateSIP(SIP sip) throws DataException {
// check if already consolidated
Map<String, String> params = new HashMap<>();
params.put("AIPID", sip.getId());
try {
List<String> res = persistenceManager.executeQuery(P4Query.IP_BY_ID.getQueryFile(), P4Collection.AIP_COLLECTION, params, false);
if (res.size() != 0)
throw new DataException("SIP already consolidated...");
} catch (DatabaseException e) {
e.printStackTrace();
}
if (sip instanceof P4InformationPackage) {
// if not consolidated, release
P4InformationPackage p4IP = (P4InformationPackage) sip;
while (p4IP.getCounter() != 0)
this.releaseIP(sip);
// assign or update CREATEDATE
GregorianCalendar gCal = new GregorianCalendar();
gCal.setTime(new Date());
try {
sip.setCreateDate(gCal);
} catch (IPException e) {
throw new DataException("Unable to assign create date to SIP");
}
// then consolidate
try {
P4PersistenceManager.getInstance().storeXMLResource(P4Collection.AIP_COLLECTION, sip.getId(), p4IP.getContent());
} catch (DatabaseException e) {
e.printStackTrace();
throw new DataException("Unable to store the new AIP...");
}
// finally clean up
try {
P4PersistenceManager.getInstance().deleteXMLResource(P4Collection.TEMP_COLLECTION, sip.getId());
} catch (DatabaseException e) {
e.printStackTrace();
throw new DataException("Unable to remove consolidated SIP " + sip.getId() + "...");
}
} else {
throw new DataException("Invalid consolidating SIP...");
}
}
@Override
public synchronized void deleteSIP(String id) throws DataException {
try {
P4PersistenceManager.getInstance().deleteXMLResource(P4Collection.TEMP_COLLECTION, id);
} catch (DatabaseException e) {
e.printStackTrace();
logger.error("Unable to delete from TEMP_COLLECTION SIP " + id + "...");
throw new DataException("Unable to delete from TEMP_COLLECTION SIP " + id + "...");
}
}
public synchronized void invalidateAIP(String id) throws DataException {
try {
Node aip = P4PersistenceManager.getInstance().readXMLResource(P4Collection.AIP_COLLECTION, id);
P4PersistenceManager.getInstance().deleteXMLResource(P4Collection.AIP_COLLECTION, id);
P4PersistenceManager.getInstance().storeXMLResource(P4Collection.TEMP_COLLECTION, id, aip);
} catch (DatabaseException e) {
e.printStackTrace();
throw new DataException("Unable to invalidate AIP " + id + "...");
}
}
@Override
public List<String> getAllAIP(Map<String, String> elements) throws DataException {
Map<String, String> params = new HashMap<>();
if (elements == null)
elements = new HashMap<>();
params.put("qtitle", elements.get("title") != null ? elements.get("title") : "");
params.put("qdescription", elements.get("description") != null ? elements.get("description") : "");
params.put("qformat", elements.get("format") != null ? elements.get("format") : "");
params.put("qidentifier", elements.get("identifier") != null ? elements.get("identifier") : "");
try {
List<String> res = persistenceManager.executeQuery(P4Query.DC_BY_ID.getQueryFile(), P4Collection.AIP_COLLECTION, params, false);
return res;
} catch (DatabaseException e) {
e.printStackTrace();
}
throw new DataException("Unable to execute query on DC fields...");
}
@Override
public synchronized AIP getAIPByID(String id) throws DataException {
String key = id;
InformationPackage IP = this.getRunningIP(key);
if (IP != null)
if (IP instanceof AIP)
return (AIP) IP;
else
throw new DataException("Error with keys: this key corresponds to a running InformationPackage, but it isn't an AIP...");
else {
logger.debug("AIP not cached, quering persistence DB for new instance...");
Map<String, String> params = new HashMap<>();
params.put("AIPID", id);
try {
List<String> res = persistenceManager.executeQuery(P4Query.IP_BY_ID.getQueryFile(), P4Collection.AIP_COLLECTION, params, false);
if (res.size() == 1) {
String result = res.get(0);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
P4AIP p4AIP = new P4AIP(id, dbf.newDocumentBuilder().parse(new ByteArrayInputStream(result.getBytes())));
this.registerRunningIP(key, p4AIP);
return p4AIP;
}
} catch (DatabaseException e) {
e.printStackTrace();
} catch (SAXException | IOException | ParserConfigurationException e) {
e.printStackTrace();
}
}
throw new DataException("Unable to find an AIP with id " + id);
}
/**
* Returns a list of AIP containing or not a specific metadata information
*/
@Override
public List<String> getAIPByMD(String label, boolean isAvailable) throws DataException {
Datatype datatype = ConfigurationManager.getDataTypesInstance().getDatatype(label);
String status = datatype.getSection().getStatus();
if (status == null || status.isEmpty())
status = "";
List<String> aipIdList = this.getAllAIP(null);
List<String> aipIdSelectedList = new ArrayList<>();
Map<String, String> params = null;
try {
params = new HashMap<>();
params.put("SEC_TYPE", datatype.getSection().getType());
params.put("MD_LABEL", datatype.getRef().getLabel());
params.put("STATUS", status);
aipIdSelectedList = persistenceManager.executeQuery(P4Query.IP_BY_MD.getQueryFile(), P4Collection.AIP_COLLECTION, params, true);
} catch (DatabaseException e) {
throw new DataException("Unable to execute query on datatype fields...");
}
if (isAvailable)
return aipIdSelectedList;
else {
aipIdList.removeAll(aipIdSelectedList);
return aipIdList;
}
}
//TODO: add @Override and add method in abstract parent class
//FIXME: parameterize methods and puth them into parent class
public List<String> getAIPByOAIPMH(String fromDate, String untilDate, String spec) throws DataException {
Map<String, String> params = new HashMap<>();
params.put("FROM_DATE", fromDate);
params.put("UNTIL_DATE", untilDate);
params.put("SET", spec);
try {
List<String> aipIdList = persistenceManager.executeQuery(P4Query.IP_BY_OAIPMH.getQueryFile(), P4Collection.AIP_COLLECTION, params, true);
return aipIdList;
} catch (DatabaseException e) {
throw new DataException("Unable to execute OAI-PMH query...");
}
}
public List<String> getAIPByFormatRisk() throws DataException {
Map<String, String> params = new HashMap<>();
try {
List<String> aipIdList = persistenceManager.executeQuery(P4Query.AIP_BY_FORMAT_RISK.getQueryFile(), P4Collection.AIP_COLLECTION, params, true);
return aipIdList;
} catch (DatabaseException e) {
throw new DataException("Unable to execute FORMAT-RISK query...");
}
}
public String getAIPByDCID(String dcID) throws DataException {
Map<String, String> params = new HashMap<>();
params.put("IDENTIFIER", dcID);
try {
List<String> res = persistenceManager.executeQuery(P4Query.IP_BY_DCID.getQueryFile(), P4Collection.AIP_COLLECTION, params, false);
if (res != null) {
assert (res.size() <= 1) : "Something bad is going on... found more than one IP with same DC identifier ?!?";
if (res.size() > 0) {
return res.get(0);
} else {
return null;
}
}
} catch (DatabaseException e) {
e.printStackTrace();
}
throw new DataException("Unable to execute query on DC fields...");
}
public List<String> getSIPByDCID(String dcID) throws DataException {
Map<String, String> params = new HashMap<>();
params.put("IDENTIFIER", dcID);
try {
List<String> res = persistenceManager.executeQuery(P4Query.IP_BY_DCID.getQueryFile(), P4Collection.TEMP_COLLECTION, params, false);
if (res != null) {
assert (res.size() <= 1) : "Something bad is going on... found more than one IP with same DC identifier ?!?";
return res;
}
} catch (DatabaseException e) {
e.printStackTrace();
}
throw new DataException("Unable to execute query on DC fields...");
}
public DIP getDIPByID(String id) throws DataException {
Map<String, String> params = new HashMap<>();
params.put("AIPID", id);
try {
List<String> res = persistenceManager.executeQuery(P4Query.IP_BY_ID.getQueryFile(), P4Collection.AIP_COLLECTION, params, false);
if (res.size() == 1) {
String result = res.get(0);
// TODO prepare for dissemination
// i.e. update URLs
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
P4DIP p4DIP = new P4DIP(id, dbf.newDocumentBuilder().parse(new ByteArrayInputStream(result.getBytes())));
return p4DIP;
}
} catch (DatabaseException e) {
e.printStackTrace();
} catch (SAXException | IOException | ParserConfigurationException e) {
e.printStackTrace();
}
throw new DataException("Unable to find an DIP with id " + id);
}
@Override
public List<String> getWfStatus(String userID, StatusType status) throws DataException {
Map<String, String> params = new HashMap<>();
if (userID == null)
params.put("userID", "^");
else
params.put("userID", userID);
if (status == null)
params.put("qstatus", "");
else
params.put("qstatus", status.toString());
try {
List<String> res = persistenceManager.executeQuery(P4Query.WFSTATUS.getQueryFile(), P4Collection.WF_COLLECTION, params, false);
return res;
} catch (DatabaseException e) {
e.printStackTrace();
}
throw new DataException("Unable to find wfStatus with status " + status + "...");
}
public String getTermsOfUse(UserRole role) throws DataException {
try {
Node serviceNode = persistenceManager.readXMLResource(P4Collection.ADMIN_COLLECTION, "terms.xml");
Unmarshaller unmarshaller = ModelUtils.getUnmarshaller(P4JAXBPackage.CONF);
Service p4Service = (Service) unmarshaller.unmarshal(serviceNode);
for (Terms terms : p4Service.getTerms())
if (terms.getRole().equalsIgnoreCase(role.toString()))
return terms.getValue();
} catch (DatabaseException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
throw new DataException("Unable to retrieve terms for role " + role);
}
}