|
|
|
@@ -12,13 +12,22 @@ import java.io.IOException;
|
|
|
|
|
import java.sql.SQLException;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.logging.Level;
|
|
|
|
|
import org.dspace.authorize.AuthorizeException;
|
|
|
|
|
import org.dspace.authorize.AuthorizeManager;
|
|
|
|
|
import org.dspace.content.Bitstream;
|
|
|
|
|
import org.dspace.content.DCValue;
|
|
|
|
|
import org.dspace.content.DSpaceObject;
|
|
|
|
|
import org.dspace.content.FormatIdentifier;
|
|
|
|
|
import org.dspace.content.Item;
|
|
|
|
|
import org.dspace.content.ItemIterator;
|
|
|
|
|
import org.dspace.content.crosswalk.DisseminationCrosswalk;
|
|
|
|
|
import org.dspace.core.Constants;
|
|
|
|
|
import org.dspace.core.Context;
|
|
|
|
|
import org.dspace.core.PluginManager;
|
|
|
|
|
import org.dspace.identifier.doi.RegistrationAgency;
|
|
|
|
|
import org.dspace.storage.rdbms.DatabaseManager;
|
|
|
|
|
import org.dspace.storage.rdbms.TableRow;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
@@ -51,6 +60,121 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
|
|
|
|
|
private static final String DOI_SCHEME = DOI.SCHEME;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stores the scheme used to connect to the DataCite server. It will be set
|
|
|
|
|
* by spring dependency injection.
|
|
|
|
|
*/
|
|
|
|
|
private String DATACITE_SCHEME;
|
|
|
|
|
/**
|
|
|
|
|
* Stores the hostname of the DataCite server. Set by spring dependency
|
|
|
|
|
* injection.
|
|
|
|
|
*/
|
|
|
|
|
private String DATACITE_HOST;
|
|
|
|
|
/**
|
|
|
|
|
* Path on the DataCite server used to generate DOIs. Set by spring
|
|
|
|
|
* dependency injection.
|
|
|
|
|
*/
|
|
|
|
|
private String DATACITE_DOI_PATH;
|
|
|
|
|
/**
|
|
|
|
|
* Path on the DataCite server used to register metadata. Set by spring
|
|
|
|
|
* dependency injection.
|
|
|
|
|
*/
|
|
|
|
|
private String DATACITE_METADATA_PATH;
|
|
|
|
|
/**
|
|
|
|
|
* Name of crosswalk to convert metadata into DataCite Metadata Scheme. Set
|
|
|
|
|
* by spring dependency injection.
|
|
|
|
|
*/
|
|
|
|
|
protected String CROSSWALK_NAME;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Prefix of DOI namespace. Set in dspace.cfg.
|
|
|
|
|
*/
|
|
|
|
|
protected String PREFIX;
|
|
|
|
|
/**
|
|
|
|
|
* Part of DOI to seperate several applications that generate DOIs. F.e. it
|
|
|
|
|
* could be 'dspace/' if DOIs generated by DSpace should have the form
|
|
|
|
|
* prefix/dspace/uniqueString. Set it to the empty String if DSpace should
|
|
|
|
|
* generate DOIs directly after the DOI Prefix. Set in dspace.cfg.
|
|
|
|
|
*/
|
|
|
|
|
protected String NAMESPACE_SEPARATOR;
|
|
|
|
|
|
|
|
|
|
// TODO: document how exception while initializing the crosswalks will be
|
|
|
|
|
// handeled.
|
|
|
|
|
/**
|
|
|
|
|
* DisseminationCrosswalk to map local metadata into DataCite metadata.
|
|
|
|
|
* The name of the crosswalk is set by spring dependency injection using
|
|
|
|
|
* {@link setDisseminationCrosswalk(String) setDisseminationCrosswalk} which
|
|
|
|
|
* instantiate the crosswalk.
|
|
|
|
|
*/
|
|
|
|
|
protected DisseminationCrosswalk crosswalk;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Used to set the scheme to connect the DataCite server. Used by spring
|
|
|
|
|
* dependency injection.
|
|
|
|
|
* @param DATACITE_SCHEME Probably https or http.
|
|
|
|
|
*/
|
|
|
|
|
@Required
|
|
|
|
|
public void setDATACITE_SCHEME(String DATACITE_SCHEME) {
|
|
|
|
|
this.DATACITE_SCHEME = DATACITE_SCHEME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the hostname of the DataCite server. Used by spring dependency
|
|
|
|
|
* injection.
|
|
|
|
|
* @param DATACITE_HOST Hostname to connect to register DOIs (f.e. test.datacite.org).
|
|
|
|
|
*/
|
|
|
|
|
@Required
|
|
|
|
|
public void setDATACITE_HOST(String DATACITE_HOST) {
|
|
|
|
|
this.DATACITE_HOST = DATACITE_HOST;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the path on the DataCite server to register DOIs. Used by spring
|
|
|
|
|
* dependency injection.
|
|
|
|
|
* @param DATACITE_DOI_PATH Path to register DOIs, f.e. /doi.
|
|
|
|
|
*/
|
|
|
|
|
@Required
|
|
|
|
|
public void setDATACITE_DOI_PATH(String DATACITE_DOI_PATH) {
|
|
|
|
|
this.DATACITE_DOI_PATH = DATACITE_DOI_PATH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the path to register metadata on DataCite server. Used by spring
|
|
|
|
|
* dependency injection.
|
|
|
|
|
* @param DATACITE_METADATA_PATH Path to register metadata, f.e. /mds.
|
|
|
|
|
*/
|
|
|
|
|
@Required
|
|
|
|
|
public void setDATACITE_METADATA_PATH(String DATACITE_METADATA_PATH) {
|
|
|
|
|
this.DATACITE_METADATA_PATH = DATACITE_METADATA_PATH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: document how exception while initializing the crosswalks will be
|
|
|
|
|
// handeled.
|
|
|
|
|
/**
|
|
|
|
|
* Set the name of the dissemination crosswalk used to convert the metadata
|
|
|
|
|
* into DataCite Metadata Schema. This method tries to initialize the named
|
|
|
|
|
* crosswalk. Used by spring dependency injection.
|
|
|
|
|
* @param CROSSWALK_NAME The name of the dissemination crosswalk to use. This
|
|
|
|
|
* crosswalk must be configured in dspace.cfg.
|
|
|
|
|
*/
|
|
|
|
|
@Required
|
|
|
|
|
public void setDisseminationCrosswalk(String CROSSWALK_NAME) {
|
|
|
|
|
this.CROSSWALK_NAME= CROSSWALK_NAME;
|
|
|
|
|
|
|
|
|
|
// FIXME add some caching here
|
|
|
|
|
this.crosswalk = (DisseminationCrosswalk) PluginManager.getNamedPlugin(DisseminationCrosswalk.class, CROSSWALK_NAME);
|
|
|
|
|
// FIXME handle the case if the crosswalk can't be found
|
|
|
|
|
if (this.crosswalk == null) {
|
|
|
|
|
throw new IllegalArgumentException("Can't find crosswalk '" + CROSSWALK_NAME + "'!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DOIDataCiteIdentifierProvider() {
|
|
|
|
|
// FIXME
|
|
|
|
|
PREFIX = "10.0123";
|
|
|
|
|
NAMESPACE_SEPARATOR = "DSpace/";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This identifier provider supports identifiers of type
|
|
|
|
|
* {@link org.dspace.identifier.DOI}.
|
|
|
|
@@ -90,8 +214,9 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
throws IdentifierException
|
|
|
|
|
{
|
|
|
|
|
String doi = mint(context, dso);
|
|
|
|
|
if (null != doi)
|
|
|
|
|
this.register(context, dso, doi);
|
|
|
|
|
// register tries to reserve doi if it's not already.
|
|
|
|
|
// So we don't have to reserve it here.
|
|
|
|
|
this.register(context, dso, doi);
|
|
|
|
|
return doi;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -100,10 +225,17 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
throws IdentifierException
|
|
|
|
|
{
|
|
|
|
|
String doi = formatIdentifier(identifier);
|
|
|
|
|
if (!isDOIReservedForObject(dso, doi))
|
|
|
|
|
if (isDOIRegistered(dso, doi))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (isDOIRegistered(doi))
|
|
|
|
|
throw new IllegalArgumentException("Trying to register a DOI that is registered for another object.");
|
|
|
|
|
|
|
|
|
|
if (!isDOIReserved(dso, doi))
|
|
|
|
|
{
|
|
|
|
|
if (isDOIReserved(doi))
|
|
|
|
|
throw new IllegalStateException("Trying to register a DOI that is reserved for another object.");
|
|
|
|
|
throw new IllegalArgumentException("Trying to register a DOI that is reserved for another object.");
|
|
|
|
|
|
|
|
|
|
if(!reserveDOI(dso, doi))
|
|
|
|
|
throw new IdentifierException("It was impossible to reserve the DOI "
|
|
|
|
|
+ doi + ". Take a look into the logs for further details.");
|
|
|
|
@@ -111,9 +243,7 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (registerDOI(dso, doi))
|
|
|
|
|
saveDOIToObject(context, dso, doi);
|
|
|
|
|
else
|
|
|
|
|
if (!registerDOI(context, dso, doi))
|
|
|
|
|
throw new IdentifierException("It was impossible to register the DOI "
|
|
|
|
|
+ doi + ". Take a look into the logs for further details.");
|
|
|
|
|
}
|
|
|
|
@@ -124,15 +254,23 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param context
|
|
|
|
|
* @param dso DSpaceObject the DOI should be reserved for. Some metadata of this object will be send to the registration agency.
|
|
|
|
|
* @param identifier DOI to register in a format that {@link FormatIdentifier(String)} accepts.
|
|
|
|
|
* @throws IdentifierException If the format of {@code identifier} was unrecognized or if it was impossible to reserve the DOI (registration agency denied for some reason, see logs).
|
|
|
|
|
* @throws IllegalArgumentException If {@code identifier} is a DOI already registered for another DSpaceObject then {@code dso}.
|
|
|
|
|
* @see IdentifierProvider.reserve(Context, DSpaceObject, String)
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void reserve(Context context, DSpaceObject dso, String identifier)
|
|
|
|
|
throws IdentifierException
|
|
|
|
|
throws IdentifierException, IllegalArgumentException
|
|
|
|
|
{
|
|
|
|
|
String doi = formatIdentifier(identifier);
|
|
|
|
|
if (!isDOIReservedForObject(dso, doi))
|
|
|
|
|
if (!isDOIReserved(dso, doi))
|
|
|
|
|
{
|
|
|
|
|
if (isDOIReserved(doi))
|
|
|
|
|
throw new IllegalStateException("Trying to register a DOI that is reserved for another object.");
|
|
|
|
|
throw new IllegalArgumentException("Trying to register a DOI that is reserved for another object.");
|
|
|
|
|
if(!reserveDOI(dso, doi))
|
|
|
|
|
throw new IdentifierException("It was impossible to reserve the DOI "
|
|
|
|
|
+ doi + ". Take a look into the logs for further details.");
|
|
|
|
@@ -143,8 +281,27 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
public String mint(Context context, DSpaceObject dso)
|
|
|
|
|
throws IdentifierException
|
|
|
|
|
{
|
|
|
|
|
//TODO
|
|
|
|
|
return null;
|
|
|
|
|
String doi = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
doi = getDOIByObject(context, dso);
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e)
|
|
|
|
|
{
|
|
|
|
|
log.error("Error while attemping to retrieve information about a DOI for " + dso.getTypeText() + " with ID " + dso.getID() + ".");
|
|
|
|
|
throw new RuntimeException("Error while attemping to retrieve information about a DOI for " + dso.getTypeText() + " with ID " + dso.getID() + ".", e);
|
|
|
|
|
}
|
|
|
|
|
if (null == doi) {
|
|
|
|
|
try {
|
|
|
|
|
doi = createNewIdentifier(context, dso, null);
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e)
|
|
|
|
|
{
|
|
|
|
|
log.error("Error while creating new DOI for Object of ResourceType {} with id {}.", dso.getType(), dso.getID());
|
|
|
|
|
throw new RuntimeException("Error while attemping to create a new DOI for " + dso.getTypeText() + " with ID " + dso.getID() + ".", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return doi;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@@ -152,80 +309,62 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
String... attributes)
|
|
|
|
|
throws IdentifierNotFoundException, IdentifierNotResolvableException
|
|
|
|
|
{
|
|
|
|
|
// TODO check
|
|
|
|
|
log.debug("resolve {}", identifier);
|
|
|
|
|
String doi = null;
|
|
|
|
|
try {
|
|
|
|
|
identifier = formatIdentifier(identifier);
|
|
|
|
|
doi = formatIdentifier(identifier);
|
|
|
|
|
} catch (IdentifierException e) {
|
|
|
|
|
throw new IdentifierNotFoundException(e.getMessage());
|
|
|
|
|
throw new IdentifierNotResolvableException(e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
log.debug("formated identifier as {}", identifier);
|
|
|
|
|
|
|
|
|
|
ItemIterator found;
|
|
|
|
|
try {
|
|
|
|
|
found = Item.findByMetadataField(context,
|
|
|
|
|
MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER,
|
|
|
|
|
identifier);
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
log.error(ex.getMessage());
|
|
|
|
|
throw new IdentifierNotResolvableException(ex);
|
|
|
|
|
} catch (AuthorizeException ex) {
|
|
|
|
|
log.error(ex.getMessage());
|
|
|
|
|
throw new IdentifierNotResolvableException(ex);
|
|
|
|
|
} catch (IOException ex) {
|
|
|
|
|
log.error(ex.getMessage());
|
|
|
|
|
throw new IdentifierNotResolvableException(ex);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return getObjectByDOI(context, doi);
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
if (!found.hasNext())
|
|
|
|
|
throw new IdentifierNotFoundException("No object bound to " + identifier);
|
|
|
|
|
Item found1 = found.next();
|
|
|
|
|
if (found.hasNext())
|
|
|
|
|
log.error("More than one object bound to {}!", identifier);
|
|
|
|
|
log.debug("Resolved to {}", found1);
|
|
|
|
|
return found1;
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
log.error(ex.getMessage());
|
|
|
|
|
throw new IdentifierNotResolvableException(ex);
|
|
|
|
|
catch (SQLException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Unable to retrieve information about a DOI out of database.", e);
|
|
|
|
|
}
|
|
|
|
|
catch (IdentifierException e)
|
|
|
|
|
{
|
|
|
|
|
throw new IdentifierNotResolvableException(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String lookup(Context context, DSpaceObject object)
|
|
|
|
|
public String lookup(Context context, DSpaceObject dso)
|
|
|
|
|
throws IdentifierNotFoundException, IdentifierNotResolvableException
|
|
|
|
|
{
|
|
|
|
|
// TODO: check
|
|
|
|
|
log.debug("lookup {}", object);
|
|
|
|
|
|
|
|
|
|
if (!(object instanceof Item))
|
|
|
|
|
throw new IllegalArgumentException("Unsupported type " + object.getTypeText());
|
|
|
|
|
|
|
|
|
|
Item item = (Item)object;
|
|
|
|
|
DCValue found = null;
|
|
|
|
|
for (DCValue candidate : item.getMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null))
|
|
|
|
|
if (candidate.value.startsWith(DOI_SCHEME))
|
|
|
|
|
{
|
|
|
|
|
found = candidate;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (null != found)
|
|
|
|
|
String doi = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
log.debug("Found {}", found.value);
|
|
|
|
|
return found.value;
|
|
|
|
|
doi = getDOIByObject(context, dso);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw new IdentifierNotFoundException(object.getTypeText() + " "
|
|
|
|
|
+ object.getID() + " has no DOI");
|
|
|
|
|
catch (SQLException e)
|
|
|
|
|
{
|
|
|
|
|
throw new RuntimeException("Error retrieving DOI out of database.", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (null == doi)
|
|
|
|
|
throw new IdentifierNotFoundException("No DOI for DSpaceObject of type "
|
|
|
|
|
+ dso.getTypeText() + " with ID " + dso.getID() + " found.");
|
|
|
|
|
|
|
|
|
|
return doi;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void delete(Context context, DSpaceObject dso)
|
|
|
|
|
throws IdentifierException
|
|
|
|
|
{
|
|
|
|
|
String[] DOIs = getDOIsByObject(dso);
|
|
|
|
|
for (String doi : DOIs) {
|
|
|
|
|
this.delete(context, dso, doi);
|
|
|
|
|
String doi = null;
|
|
|
|
|
try {
|
|
|
|
|
doi = getDOIByObject(context, dso);
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e)
|
|
|
|
|
{
|
|
|
|
|
log.error("Error while attemping to retrieve information about a DOI for " + dso.getTypeText() + " with ID " + dso.getID() + ".");
|
|
|
|
|
log.error(e.getMessage());
|
|
|
|
|
throw new RuntimeException("Error while attemping to retrieve information about a DOI for " + dso.getTypeText() + " with ID " + dso.getID() + ".", e);
|
|
|
|
|
}
|
|
|
|
|
this.delete(context, dso, doi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@@ -236,54 +375,70 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
String doi = formatIdentifier(identifier);
|
|
|
|
|
log.debug("formated identifier as {}", doi);
|
|
|
|
|
|
|
|
|
|
if (!deleteDOI(dso, doi))
|
|
|
|
|
if (isDOIRegistered(doi))
|
|
|
|
|
throw new IdentifierException("Unable to delete DOI " + doi +
|
|
|
|
|
". Was it already registered? Take a look into the logs for further details.");
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
deleteDOIFromObject(context, dso, doi);
|
|
|
|
|
". It is already registered.");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
removeDOIFromObject(context, dso, doi);
|
|
|
|
|
}
|
|
|
|
|
// TODO: better Exception handling.
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
catch (AuthorizeException ex)
|
|
|
|
|
{
|
|
|
|
|
throw new IdentifierException(e);
|
|
|
|
|
log.error("Not authorized to delete a DOI out of an Item.");
|
|
|
|
|
throw new IdentifierException("Not authorized to delete DOI.", ex);
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException ex)
|
|
|
|
|
{
|
|
|
|
|
log.error("SQLException occured while deleting a DOI out of an item: " + ex.getMessage());
|
|
|
|
|
throw new RuntimeException("Error while deleting a DOI out of the metadata of an Item " + dso.getID(), ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!deleteDOI(context, dso, doi))
|
|
|
|
|
{
|
|
|
|
|
// It was not possible to delete a DOI at the registry agency. We
|
|
|
|
|
// already removed the DOI out of the metadata of the object,
|
|
|
|
|
// so we should restore these information.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
saveDOIToObject(context, dso, doi);
|
|
|
|
|
}
|
|
|
|
|
catch (AuthorizeException e)
|
|
|
|
|
{
|
|
|
|
|
log.error("We were authorized to delete a DOI out of an Item, but we can't restore these informations?!");
|
|
|
|
|
log.error("DSO-Type: " + dso.getTypeText() + ", ID: " + dso.getID() +
|
|
|
|
|
", DOI: " + doi + ".");
|
|
|
|
|
throw new IllegalStateException("We were authorized to delete a DOI out of an Item, but we can't restore these informations?!", e);
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException ex)
|
|
|
|
|
{
|
|
|
|
|
log.error("SQLError while restoring DOI " + doi +
|
|
|
|
|
" for Object of Type " + dso.getTypeText() + " with ID "
|
|
|
|
|
+ dso.getID() + ".");
|
|
|
|
|
log.error("Exception message: " + ex.getMessage());
|
|
|
|
|
throw new RuntimeException("Error while restoring a DOI as metadata of an Item " + dso.getID(), ex);
|
|
|
|
|
}
|
|
|
|
|
throw new IdentifierException("Unable to delete DOI " + doi +
|
|
|
|
|
". Take a look into the logs for further details.");
|
|
|
|
|
}
|
|
|
|
|
log.info("Deleted {}", doi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This method helps to convert a DOI into a URL. It takes DOIs with or
|
|
|
|
|
* without leading DOI scheme (f.e. doi:10.123/456 as well as 10.123/456)
|
|
|
|
|
* and returns it as URL (f.e. http://dx.doi.org/10.123/456).
|
|
|
|
|
*
|
|
|
|
|
* @param id A DOI that should be returned in external form.
|
|
|
|
|
* @return A String containing a URL to the official DOI resolver.
|
|
|
|
|
* @throws IdentifierException
|
|
|
|
|
*/
|
|
|
|
|
public static String DOIToExternalForm(String id) throws IdentifierException{
|
|
|
|
|
if (id.startsWith("http://dx.doi.org/10.")) {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
String doi = formatIdentifier(id);
|
|
|
|
|
return "http://dx.doi.org/" + doi.substring(DOI_SCHEME.length());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Format any accepted identifier with DOI scheme.
|
|
|
|
|
* Recognize format of DOI and return it with leading doi-Scheme.
|
|
|
|
|
* @param identifier Identifier to format, following format are accepted: f.e. 10.123/456, doi:10.123/456, http://dx.doi.org/10.123/456.
|
|
|
|
|
* @return Given Identifier with DOI-Scheme, f.e. doi:10.123/456.
|
|
|
|
|
* @throws IdentifierException
|
|
|
|
|
* @throws IllegalArgumentException If identifier is empty or null.
|
|
|
|
|
* @throws IdentifierException If DOI could not be recognized.
|
|
|
|
|
*/
|
|
|
|
|
public static String formatIdentifier(String identifier) throws IdentifierException
|
|
|
|
|
{
|
|
|
|
|
if (null == identifier)
|
|
|
|
|
throw new IdentifierException("Identifier is null.", new NullPointerException());
|
|
|
|
|
if (identifier.isEmpty())
|
|
|
|
|
throw new IdentifierException("Cannot format an empty identifier.");
|
|
|
|
|
throw new IllegalArgumentException("Identifier is null.", new NullPointerException());
|
|
|
|
|
if (identifier.startsWith("doi:"))
|
|
|
|
|
return identifier;
|
|
|
|
|
if (identifier.isEmpty())
|
|
|
|
|
throw new IllegalArgumentException("Cannot format an empty identifier.");
|
|
|
|
|
if (identifier.startsWith("10.") && identifier.contains("/"))
|
|
|
|
|
return DOI_SCHEME + identifier;
|
|
|
|
|
if (identifier.startsWith("http://dx.doi.org/10."))
|
|
|
|
@@ -293,36 +448,231 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
|
|
|
|
|
protected boolean reserveDOI(DSpaceObject dso, String doi)
|
|
|
|
|
{
|
|
|
|
|
// send metadata as post to mds/metadata
|
|
|
|
|
// 400 -> invalid XML
|
|
|
|
|
// 401 -> no login
|
|
|
|
|
// 403 -> wrong credentials or datasets belong to another party
|
|
|
|
|
// 201 -> created / ok
|
|
|
|
|
// 500 -> try again later / internal server error
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean isDOIReserved(String doi)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
return isDOIReserved(null, doi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean isDOIReservedForObject(DSpaceObject dso, String doi)
|
|
|
|
|
protected boolean isDOIReserved(DSpaceObject dso, String doi)
|
|
|
|
|
{
|
|
|
|
|
// get mds/metadata/<doi>
|
|
|
|
|
// 410 GONE -> inactive (not clear if ever registered or reserved and deleted)
|
|
|
|
|
// 404 Not Found -> not reserved
|
|
|
|
|
// 401 -> no login
|
|
|
|
|
// 403 -> wrong credential or doi belongs to another party.
|
|
|
|
|
// 400 -> Bad Content -> f.e. non existing prefix
|
|
|
|
|
// 500 -> try again later or doi belongs to another registry agency
|
|
|
|
|
// 200 -> registered and reserved
|
|
|
|
|
// 204 no content -> reserved but not registered
|
|
|
|
|
// if (204 && dso != null) -> get mds/doi/<doi> and compare xml->alternative identifier
|
|
|
|
|
// if (200 && dso != null) -> compare url (out of response-content) with dso
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean registerDOI(DSpaceObject dso, String doi)
|
|
|
|
|
protected boolean isDOIRegistered(String doi)
|
|
|
|
|
{
|
|
|
|
|
return isDOIRegistered(null, doi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean isDOIRegistered(DSpaceObject dso, String doi)
|
|
|
|
|
{
|
|
|
|
|
// get mds/doi/<doi>
|
|
|
|
|
// 404 -> not reservered, not registered
|
|
|
|
|
// 204 -> reserved, not registered
|
|
|
|
|
// 200 -> reserved, registered
|
|
|
|
|
// if dso != null && response-code == 200 -> compare url and response
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected boolean registerDOI(Context context, DSpaceObject dso, String doi)
|
|
|
|
|
throws SQLException
|
|
|
|
|
{
|
|
|
|
|
// send doi=<doi>\nurl=<url> to mds/doi
|
|
|
|
|
// 412 -> prediction failed -> not reserved
|
|
|
|
|
// 403 -> wrong credential, quota exceeded
|
|
|
|
|
// 401 -> no login
|
|
|
|
|
// 400 -> wrong domain, wrong prefix, wrong request body
|
|
|
|
|
// 201 -> created/updated
|
|
|
|
|
// 500 -> try again later / internal server error
|
|
|
|
|
|
|
|
|
|
// saveDOIToObject(context, dso, doi);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected boolean deleteDOI(DSpaceObject dso, String doi)
|
|
|
|
|
protected boolean deleteDOI(Context context, DSpaceObject dso, String doi)
|
|
|
|
|
{
|
|
|
|
|
if (isDOIRegistered(doi))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// look for doi in DB
|
|
|
|
|
// if it exists compare resource type and id
|
|
|
|
|
// if not okay throw exception
|
|
|
|
|
|
|
|
|
|
// delete mds/metadata/<doi>
|
|
|
|
|
// 200 -> ok
|
|
|
|
|
// 401 -> no login
|
|
|
|
|
// 403 -> wrong credential or doi belongs another party
|
|
|
|
|
// 404 -> doi neither reserved nor registered
|
|
|
|
|
// 500 -> internal server error
|
|
|
|
|
|
|
|
|
|
// delete doi from database
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected String[] getDOIsByObject(DSpaceObject dso)
|
|
|
|
|
throws IdentifierException
|
|
|
|
|
/**
|
|
|
|
|
* Returns a DSpaceObject depending on its DOI.
|
|
|
|
|
* @param context the context
|
|
|
|
|
* @param identifier The DOI in a format that is accepted by
|
|
|
|
|
* {@link formatIdentifier(String)}.
|
|
|
|
|
* @return Null if the DOI couldn't be found or the associated DSpaceObject.
|
|
|
|
|
* @throws SQLException
|
|
|
|
|
* @throws IdentifierException If {@code identifier} is null or an empty string.
|
|
|
|
|
* @throws IllegalArgumentException If the identifier couldn't be recognized as DOI.
|
|
|
|
|
*/
|
|
|
|
|
public static DSpaceObject getObjectByDOI(Context context, String identifier)
|
|
|
|
|
throws SQLException, IdentifierException, IllegalArgumentException
|
|
|
|
|
{
|
|
|
|
|
// TODO
|
|
|
|
|
return (new String[0]);
|
|
|
|
|
String doi = formatIdentifier(identifier);
|
|
|
|
|
TableRow doiRow = DatabaseManager.findByUnique(context, "Doi", "doi", doi);
|
|
|
|
|
|
|
|
|
|
if (null == doiRow)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
if (doiRow.isColumnNull("resource_type_id") ||
|
|
|
|
|
doiRow.isColumnNull("resource_id"))
|
|
|
|
|
{
|
|
|
|
|
throw new IllegalStateException("Found DOI " + doi +
|
|
|
|
|
" in database, but no assigned Object could be found.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DSpaceObject.find(context,
|
|
|
|
|
doiRow.getIntColumn("resource_type_id"),
|
|
|
|
|
doiRow.getIntColumn("resource_id"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Search the database for a DOI, using the type and id of an DSpaceObject.
|
|
|
|
|
*
|
|
|
|
|
* @param context
|
|
|
|
|
* @param dso DSpaceObject to find doi for.
|
|
|
|
|
* @return The DOI as String or null if DOI was not found.
|
|
|
|
|
* @throws SQLException
|
|
|
|
|
*/
|
|
|
|
|
public static String getDOIByObject(Context context, DSpaceObject dso)
|
|
|
|
|
throws SQLException
|
|
|
|
|
{
|
|
|
|
|
String sql = "SELECT * FROM Doi WHERE resource_type_id = ? " +
|
|
|
|
|
"AND resource_id = ?";
|
|
|
|
|
|
|
|
|
|
TableRow doiRow = DatabaseManager.querySingleTable(context, "Doi", sql, dso.getType(), dso.getID());
|
|
|
|
|
if (null == doiRow)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
if (doiRow.isColumnNull("doi"))
|
|
|
|
|
{
|
|
|
|
|
log.error("A DOI with an empty doi column was found in the database. DSO-Type: "
|
|
|
|
|
+ dso.getTypeText() + ", ID: " + dso.getID() + ".");
|
|
|
|
|
throw new IllegalStateException("A DOI with an empty doi column was found in the database. DSO-Type: "
|
|
|
|
|
+ dso.getTypeText() + ", ID: " + dso.getID() + ".");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DOI_SCHEME + doiRow.getStringColumn("doi");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new DOI and saves it into the database.
|
|
|
|
|
*
|
|
|
|
|
* @param context
|
|
|
|
|
* @param dso The DSpaceObject the DOI should be created for.
|
|
|
|
|
* @param doi A DOI or null if DOI should be generated.
|
|
|
|
|
* @return The generated DOI or @doi if it was saved into the database.
|
|
|
|
|
* @throws SQLException In case of an error using the database.
|
|
|
|
|
* @throws IllegalArgumentException If the given DOI already exists for an other object.
|
|
|
|
|
*/
|
|
|
|
|
protected String createNewIdentifier(Context context, DSpaceObject dso, String doi)
|
|
|
|
|
throws SQLException, IllegalArgumentException
|
|
|
|
|
{
|
|
|
|
|
TableRow doiRow = null;
|
|
|
|
|
if (null != doi)
|
|
|
|
|
{
|
|
|
|
|
// check if DOI is already in Database, remove DOI_SCHEME before searching in DB.
|
|
|
|
|
doiRow = DatabaseManager.findByUnique(context, "Doi", "doi", doi.substring(DOI_SCHEME.length()));
|
|
|
|
|
if (null != doiRow)
|
|
|
|
|
{
|
|
|
|
|
// check if DOI already belongs to dso
|
|
|
|
|
if (doiRow.getIntColumn("resource_id") == dso.getID() &&
|
|
|
|
|
doiRow.getIntColumn("resource_type_id") == dso.getType())
|
|
|
|
|
{
|
|
|
|
|
return doi;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw new IllegalArgumentException("Trying to save a DOI that is already reserved for another object.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// doi is not null but was not found in db
|
|
|
|
|
// check if it registered online
|
|
|
|
|
if (isDOIRegistered(doi) || isDOIReserved(doi))
|
|
|
|
|
throw new IllegalArgumentException("Trying to save a DOI that is already reserved for another object.");
|
|
|
|
|
// safe it to db (see below)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
doiRow = DatabaseManager.create(context, "Doi");
|
|
|
|
|
if (null != doi)
|
|
|
|
|
{
|
|
|
|
|
doi = doi.substring(DOI_SCHEME.length());
|
|
|
|
|
if (!doi.startsWith(PREFIX + "/"))
|
|
|
|
|
throw new IllegalArgumentException("Trying to create a DOI that's not part of our Namespace!");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
doi = PREFIX + "/" + NAMESPACE_SEPARATOR + doiRow.getIntColumn("doi_id");
|
|
|
|
|
doiRow.setColumn("doi", doi);
|
|
|
|
|
doiRow.setColumn("resource_type_id", dso.getType());
|
|
|
|
|
doiRow.setColumn("resource_id", dso.getID());
|
|
|
|
|
DatabaseManager.update(context, doiRow);
|
|
|
|
|
|
|
|
|
|
return DOI_SCHEME + doi;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected String getDOIOutOfObject(DSpaceObject dso) {
|
|
|
|
|
// FIXME
|
|
|
|
|
if (!(dso instanceof Item))
|
|
|
|
|
throw new IllegalArgumentException("Unsupported type " + dso.getTypeText());
|
|
|
|
|
|
|
|
|
|
Item item = (Item)dso;
|
|
|
|
|
|
|
|
|
|
DCValue[] metadata = item.getMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
|
|
|
|
|
for (DCValue id : metadata)
|
|
|
|
|
{
|
|
|
|
|
if (!id.value.startsWith(DOI_SCHEME))
|
|
|
|
|
return id.value;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds a DOI to the Metadata of an item.
|
|
|
|
|
*
|
|
|
|
|
* @param context
|
|
|
|
|
* @param dso DSpaceObject the DOI should be added to.
|
|
|
|
|
* @param doi The DOI that should be added as metadata.
|
|
|
|
|
* @throws SQLException
|
|
|
|
|
* @throws AuthorizeException
|
|
|
|
|
*/
|
|
|
|
|
protected void saveDOIToObject(Context context, DSpaceObject dso, String doi)
|
|
|
|
|
throws SQLException, AuthorizeException
|
|
|
|
|
{
|
|
|
|
@@ -344,7 +694,16 @@ public class DOIDataCiteIdentifierProvider
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void deleteDOIFromObject(Context context, DSpaceObject dso, String doi)
|
|
|
|
|
/**
|
|
|
|
|
* Removes a DOI out of the metadata of a DSpaceObject.
|
|
|
|
|
*
|
|
|
|
|
* @param context
|
|
|
|
|
* @param dso The DSpaceObject the DOI should be removed from.
|
|
|
|
|
* @param doi The DOI to remove out of the metadata.
|
|
|
|
|
* @throws AuthorizeException
|
|
|
|
|
* @throws SQLException
|
|
|
|
|
*/
|
|
|
|
|
protected void removeDOIFromObject(Context context, DSpaceObject dso, String doi)
|
|
|
|
|
throws AuthorizeException, SQLException
|
|
|
|
|
{
|
|
|
|
|
// FIXME
|
|
|
|
|