Fixes from testing

Conflicts:
	dspace-api/src/main/java/org/dspace/identifier/DataCiteIdentifierProvider.java
	dspace-api/src/test/java/org/dspace/identifier/DataCiteIdentifierProviderTest.java
This commit is contained in:
Mark H. Wood
2012-11-09 15:09:08 -05:00
committed by Pascal-Nicolas Becker
parent 98ee2d7e43
commit e41eb134ed
7 changed files with 473 additions and 247 deletions

View File

@@ -16,4 +16,5 @@ package org.dspace.identifier;
public class DOI
implements Identifier
{
public static final String SCHEME = "doi:";
}

View File

@@ -11,10 +11,11 @@ package org.dspace.identifier;
import java.io.IOException;
import java.net.*;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCValue;
import org.dspace.content.DSpaceObject;
@@ -60,15 +61,17 @@ public class DataCiteIdentifierProvider
private static final Logger log = LoggerFactory.getLogger(DataCiteIdentifierProvider.class);
// Configuration property names
private static final String CFG_SHOULDER = "identifier.doi.ezid.shoulder";
private static final String CFG_USER = "identifier.doi.ezid.user";
private static final String CFG_PASSWORD = "identifier.doi.ezid.password";
static final String CFG_SHOULDER = "identifier.doi.ezid.shoulder";
static final String CFG_USER = "identifier.doi.ezid.user";
static final String CFG_PASSWORD = "identifier.doi.ezid.password";
// Metadata field name elements
// XXX move these to MetadataSchema or some such
public static final String MD_SCHEMA_DSPACE = "dspace";
public static final String DSPACE_DOI_ELEMENT = "identifier";
public static final String DSPACE_DOI_QUALIFIER = "doi";
public static final String MD_SCHEMA = "dc";
public static final String DOI_ELEMENT = "identifier";
public static final String DOI_QUALIFIER = null;
private static final String DOI_SCHEME = "doi:";
/** Map DataCite metadata into local metadata. */
private static Map<String, String> crosswalk = new HashMap<String, String>();
@@ -85,7 +88,10 @@ public class DataCiteIdentifierProvider
@Override
public boolean supports(String identifier)
{
return identifier.startsWith("doi:"); // XXX more thorough test?
if (null == identifier)
return false;
else
return identifier.startsWith(DOI_SCHEME); // XXX more thorough test?
}
@Override
@@ -94,22 +100,20 @@ public class DataCiteIdentifierProvider
{
log.debug("register {}", dso);
Item item;
if (dso instanceof Item)
item = (Item)dso;
else
if (!(dso instanceof Item))
throw new IdentifierException("Unsupported object type " + dso.getTypeText());
String id;
DCValue[] previous = item.getMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT, DSPACE_DOI_QUALIFIER, null);
if ((previous.length > 0) && (null != previous[0].value))
return previous[0].value;
Item item = (Item)dso;
DCValue[] identifiers = item.getMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
for (DCValue identifier : identifiers)
if ((null != identifier.value) && (identifier.value.startsWith(DOI_SCHEME)))
return identifier.value;
id = mint(context, item);
item.addMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT, DSPACE_DOI_QUALIFIER, null, id);
String id = mint(context, item);
item.addMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, id);
try {
item.update();
context.commit();
} catch (SQLException ex) {
throw new IdentifierException("New identifier not stored", ex);
} catch (AuthorizeException ex) {
@@ -132,30 +136,29 @@ public class DataCiteIdentifierProvider
}
EZIDResponse response;
String doi = "unknown"; // In case we can't even build a name
try {
doi = getShoulder() + identifier;
EZIDRequest request = requestFactory.getInstance(doi,
getUser(), getPassword());
response = request.create(crosswalkMetadata(object));
EZIDRequest request = requestFactory.getInstance(loadAuthority(),
loadUser(), loadPassword());
response = request.create(identifier, crosswalkMetadata(object));
} catch (IdentifierException e) {
log.error("doi:{} not registered: {}", doi, e.getMessage());
log.error("Identifier '{}' not registered: {}", identifier, e.getMessage());
return;
} catch (IOException e) {
log.error("doi:{} not registered: {}", doi, e.getMessage());
log.error("Identifier '{}' not registered: {}", identifier, e.getMessage());
return;
} catch (URISyntaxException e) {
log.error("doi:{} not registered: {}", doi, e.getMessage());
log.error("Identifier '{}' not registered: {}", identifier, e.getMessage());
return;
}
if (response.isSuccess())
{
Item item = (Item)object;
item.addMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT,
DSPACE_DOI_QUALIFIER, null, identifier);
try {
item.addMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null,
idToDOI(identifier));
item.update();
context.commit();
log.info("registered {}", identifier);
} catch (SQLException ex) {
// TODO throw new IdentifierException("New identifier not stored", ex);
@@ -163,12 +166,14 @@ public class DataCiteIdentifierProvider
} catch (AuthorizeException ex) {
// TODO throw new IdentifierException("New identifier not stored", ex);
log.error("New identifier not stored", ex);
} catch (IdentifierException ex) {
log.error("New identifier not stored", ex);
}
}
else
{
log.error("doi:{} not registered -- EZID returned: {}", doi,
response.getEZIDStatusValue());
log.error("Identifier '{}' not registered -- EZID returned: {}",
identifier, response.getEZIDStatusValue());
}
}
@@ -179,29 +184,27 @@ public class DataCiteIdentifierProvider
log.debug("reserve {}", identifier);
EZIDResponse response;
String doi = "unknown"; // In case we can't even build a name
try {
doi = getShoulder() + identifier;
EZIDRequest request = requestFactory.getInstance(doi,
getUser(), getPassword());
EZIDRequest request = requestFactory.getInstance(loadAuthority(),
loadUser(), loadPassword());
Map<String, String> metadata = crosswalkMetadata(dso);
metadata.put("_status", "reserved");
response = request.create(metadata);
response = request.create(identifier, metadata);
} catch (IOException e) {
log.error("doi:{} not registered: {}", doi, e.getMessage());
log.error("Identifier '{}' not registered: {}", identifier, e.getMessage());
return;
} catch (URISyntaxException e) {
log.error("doi:{} not registered: {}", doi, e.getMessage());
log.error("Identifier '{}' not registered: {}", identifier, e.getMessage());
return;
}
if (response.isSuccess())
{
Item item = (Item)dso;
item.addMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT,
DSPACE_DOI_QUALIFIER, null, identifier);
item.addMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, idToDOI(identifier));
try {
item.update();
context.commit();
log.info("reserved {}", identifier);
} catch (SQLException ex) {
throw new IdentifierException("New identifier not stored", ex);
@@ -211,8 +214,8 @@ public class DataCiteIdentifierProvider
}
else
{
log.error("doi:{} not registered -- EZID returned: {}", doi,
response.getEZIDStatusValue());
log.error("Identifier '{}' not registered -- EZID returned: {}",
identifier, response.getEZIDStatusValue());
}
}
@@ -225,7 +228,7 @@ public class DataCiteIdentifierProvider
// Compose the request
EZIDRequest request;
try {
request = requestFactory.getInstance(getShoulder(), getUser(), getPassword());
request = requestFactory.getInstance(loadAuthority(), loadUser(), loadPassword());
} catch (URISyntaxException ex) {
log.error(ex.getMessage());
throw new IdentifierException("DOI request not sent: " + ex.getMessage());
@@ -236,8 +239,10 @@ public class DataCiteIdentifierProvider
try
{
response = request.mint(crosswalkMetadata(dso));
} catch (IOException ex)
{
} catch (IOException ex) {
log.error("Failed to send EZID request: {}", ex.getMessage());
throw new IdentifierException("DOI request not sent: " + ex.getMessage());
} catch (URISyntaxException ex) {
log.error("Failed to send EZID request: {}", ex.getMessage());
throw new IdentifierException("DOI request not sent: " + ex.getMessage());
}
@@ -275,27 +280,33 @@ public class DataCiteIdentifierProvider
{
log.debug("resolve {}", identifier);
try
{
ItemIterator found = Item.findByMetadataField(context, MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT, DSPACE_DOI_QUALIFIER,
identifier);
ItemIterator found;
try {
found = Item.findByMetadataField(context,
MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER,
idToDOI(identifier));
} catch (IdentifierException ex) {
log.error(ex.getMessage());
throw new IdentifierNotResolvableException(ex);
} 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 {
if (!found.hasNext())
throw new IdentifierNotFoundException("No Item bound to DOI " + identifier);
throw new IdentifierNotFoundException("No object bound to " + identifier);
Item found1 = found.next();
if (found.hasNext())
log.error("DOI {} multiply bound!", identifier);
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 (AuthorizeException ex)
{
log.error(ex.getMessage());
throw new IdentifierNotResolvableException(ex);
} catch (IOException ex)
{
} catch (SQLException ex) {
log.error(ex.getMessage());
throw new IdentifierNotResolvableException(ex);
}
@@ -307,16 +318,21 @@ public class DataCiteIdentifierProvider
{
log.debug("lookup {}", object);
Item item;
if (!(object instanceof Item))
throw new IllegalArgumentException("Unsupported type " + object.getTypeText());
item = (Item)object;
DCValue[] metadata = item.getMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT, DSPACE_DOI_QUALIFIER, null);
if (metadata.length > 0)
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))
{
log.debug("Found {}", metadata[0].value);
return metadata[0].value;
found = candidate;
break;
}
if (null != found)
{
log.debug("Found {}", found.value);
return found.value;
}
else
throw new IdentifierNotFoundException(object.getTypeText() + " "
@@ -332,34 +348,62 @@ public class DataCiteIdentifierProvider
if (!(dso instanceof Item))
throw new IllegalArgumentException("Unsupported type " + dso.getTypeText());
String username = configurationService.getProperty(CFG_USER);
String password = configurationService.getProperty(CFG_PASSWORD);
if (null == username || null == password)
throw new IdentifierException("Unconfigured: define " + CFG_USER
+ " and " + CFG_PASSWORD);
Item item = (Item)dso;
// delete from EZID
for (DCValue id : item.getMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT,
DSPACE_DOI_QUALIFIER, null))
DCValue[] metadata = item.getMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
List<String> remainder = new ArrayList<String>();
int skipped = 0;
for (DCValue id : metadata)
{
if (!id.value.startsWith(DOI_SCHEME))
{
remainder.add(id.value);
continue;
}
EZIDResponse response;
try {
EZIDRequest request = requestFactory.getInstance(id.value, username, password);
response = request.delete();
EZIDRequest request = requestFactory.getInstance(loadAuthority(),
loadUser(), loadPassword());
response = request.delete(DOIToId(id.value));
} catch (URISyntaxException e) {
throw new IdentifierException("Bad URI in metadata value", e);
log.error("Bad URI in metadata value: {}", e.getMessage());
remainder.add(id.value);
skipped++;
continue;
} catch (IOException e) {
throw new IdentifierException("Failed request to EZID", e);
log.error("Failed request to EZID: {}", e.getMessage());
remainder.add(id.value);
skipped++;
continue;
}
if (!response.isSuccess())
throw new IdentifierException("Unable to delete " + id.value
+ "from DataCite: " + response.getEZIDStatusValue());
{
log.error("Unable to delete {} from DataCite: {}", id.value,
response.getEZIDStatusValue());
remainder.add(id.value);
skipped++;
continue;
}
log.info("Deleted {}", id.value);
}
// delete from item
item.clearMetadata(MD_SCHEMA_DSPACE, DSPACE_DOI_ELEMENT, DSPACE_DOI_QUALIFIER, null);
item.clearMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
item.addMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null,
remainder.toArray(new String[remainder.size()]));
try {
item.update();
context.commit();
} catch (SQLException e) {
log.error("Failed to re-add identifiers: {}", e.getMessage());
} catch (AuthorizeException e) {
log.error("Failed to re-add identifiers: {}", e.getMessage());
}
if (skipped > 0)
throw new IdentifierException(skipped + " identifiers could not be deleted.");
}
@Override
@@ -368,14 +412,98 @@ public class DataCiteIdentifierProvider
{
log.debug("delete {} from {}", identifier, dso);
throw new UnsupportedOperationException("Not supported yet."); // TODO implement delete(specific)
// TODO find metadata value == identifier
// TODO delete from EZID
if (!(dso instanceof Item))
throw new IllegalArgumentException("Unsupported type " + dso.getTypeText());
// TODO delete from item NOTE!!! can't delete single MD values!
Item item = (Item)dso;
DCValue[] metadata = item.getMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
List<String> remainder = new ArrayList<String>();
int skipped = 0;
for (DCValue id : metadata)
{
if (!id.value.equals(idToDOI(identifier)))
{
remainder.add(id.value);
continue;
}
private String getUser()
EZIDResponse response;
try {
EZIDRequest request = requestFactory.getInstance(loadAuthority(),
loadUser(), loadPassword());
response = request.delete(DOIToId(id.value));
} catch (URISyntaxException e) {
log.error("Bad URI in metadata value {}: {}", id.value, e.getMessage());
remainder.add(id.value);
skipped++;
continue;
} catch (IOException e) {
log.error("Failed request to EZID: {}", e.getMessage());
remainder.add(id.value);
skipped++;
continue;
}
if (!response.isSuccess())
{
log.error("Unable to delete {} from DataCite: {}", id.value,
response.getEZIDStatusValue());
remainder.add(id.value);
skipped++;
continue;
}
log.info("Deleted {}", id.value);
}
// delete from item
item.clearMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
item.addMetadata(MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null,
remainder.toArray(new String[remainder.size()]));
try {
item.update();
context.commit();
} catch (SQLException e) {
log.error("Failed to re-add identifiers: {}", e.getMessage());
} catch (AuthorizeException e) {
log.error("Failed to re-add identifiers: {}", e.getMessage());
}
if (skipped > 0)
throw new IdentifierException(identifier + " could not be deleted.");
}
/**
* Format a naked identifier as a DOI with our configured authority prefix.
*
* @throws IdentifierException if authority prefix is not configured.
*/
String idToDOI(String id)
throws IdentifierException
{
return "doi:" + loadAuthority() + id;
}
/**
* Remove scheme and our configured authority prefix from a doi: URI string.
* @return naked local identifier.
* @throws IdentifierException if authority prefix is not configured.
*/
String DOIToId(String DOI)
throws IdentifierException
{
String prefix = "doi:" + loadAuthority();
if (DOI.startsWith(prefix))
return DOI.substring(prefix.length());
else
return DOI;
}
/**
* Get configured value of EZID username.
* @throws IdentifierException
*/
private String loadUser()
throws IdentifierException
{
String user = configurationService.getProperty(CFG_USER);
@@ -385,7 +513,11 @@ public class DataCiteIdentifierProvider
throw new IdentifierException("Unconfigured: define " + CFG_USER);
}
private String getPassword()
/**
* Get configured value of EZID password.
* @throws IdentifierException
*/
private String loadPassword()
throws IdentifierException
{
String password = configurationService.getProperty(CFG_PASSWORD);
@@ -395,7 +527,11 @@ public class DataCiteIdentifierProvider
throw new IdentifierException("Unconfigured: define " + CFG_PASSWORD);
}
private String getShoulder()
/**
* Get configured value of EZID "shoulder".
* @throws IdentifierException
*/
private String loadAuthority()
throws IdentifierException
{
String shoulder = configurationService.getProperty(CFG_SHOULDER);
@@ -423,21 +559,18 @@ public class DataCiteIdentifierProvider
for (DCValue value : values)
mapped.put(datum.getKey(), value.value);
}
// TODO find a way to get a current direct URL to the object and set _target
// mapped.put("_target", url);
return mapped;
}
/**
* @param aCrosswalk the crosswalk to set
*/
@Required
public void setCrosswalk(Map<String, String> aCrosswalk)
{
crosswalk = aCrosswalk;
}
/**
* @param aRequestFactory the requestFactory to set
*/
@Required
public static void setRequestFactory(EZIDRequestFactory aRequestFactory)
{

View File

@@ -11,7 +11,6 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -25,6 +24,7 @@ import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.dspace.identifier.DOI;
import org.dspace.identifier.IdentifierException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,42 +38,63 @@ public class EZIDRequest
{
private static final Logger log = LoggerFactory.getLogger(EZIDRequest.class);
private URI url;
private static final String ID_PATH = "/ezid/id/" + DOI.SCHEME;
private AbstractHttpClient client;
private static final String SHOULDER_PATH = "/ezid/shoulder/" + DOI.SCHEME;
private static final String UTF_8 = "UTF-8";
private static final String MD_KEY_STATUS = "_status";
private final AbstractHttpClient client;
private final String scheme;
private final String host;
private final String authority;
/**
* Prepare a context for requests concerning a specific identifier or
* authority prefix.
*
* @param url EZID API service point (and object) for this request.
* @param scheme
* @param host
* @param authority DOI authority prefix.
* @param username an EZID user identity.
* @param password user's password, or null for none.
* @param password user's password, or {@code null} for none.
* @throws URISyntaxException if host or authority is bad.
*/
EZIDRequest(URI url, String username, String password)
EZIDRequest(String scheme, String host, String authority, String username, String password)
throws URISyntaxException
{
this.url = url;
this.scheme = scheme;
this.host = host;
this.authority = authority;
client = new DefaultHttpClient();
if (null != username)
{
URI uri = new URI(scheme, host, null, null);
client.getCredentialsProvider().setCredentials(
new AuthScope(url.getHost(), url.getPort()),
new AuthScope(uri.getHost(), uri.getPort()),
new UsernamePasswordCredentials(username, password));
}
}
/**
* Fetch an identifier's metadata.
* Fetch the metadata bound to an identifier.
*
* @return
* @throws IdentifierException if the response is error or body malformed.
* @throws IOException if the HTTP request fails.
* @throws URISyntaxException
*/
public EZIDResponse lookup()
throws IdentifierException, IOException
public EZIDResponse lookup(String name)
throws IdentifierException, IOException, URISyntaxException
{
// GET path
HttpGet request;
request = new HttpGet(url);
URI uri = new URI(scheme, host, ID_PATH + authority + name, null);
request = new HttpGet(uri);
HttpResponse response = client.execute(request);
return new EZIDResponse(response);
}
@@ -86,16 +107,17 @@ public class EZIDRequest
* @param metadata ANVL-encoded key/value pairs.
* @return
*/
public EZIDResponse create(Map<String, String> metadata)
throws IOException, IdentifierException
public EZIDResponse create(String name, Map<String, String> metadata)
throws IOException, IdentifierException, URISyntaxException
{
// PUT path [+metadata]
HttpPut request;
request = new HttpPut(url);
URI uri = new URI(scheme, host, ID_PATH + authority + name, null);
request = new HttpPut(uri);
if (null != metadata)
{
try {
request.setEntity(new StringEntity(formatMetadata(metadata), "UTF-8"));
request.setEntity(new StringEntity(formatMetadata(metadata), UTF_8));
} catch (UnsupportedEncodingException ex) { /* SNH */ }
}
HttpResponse response = client.execute(request);
@@ -110,30 +132,30 @@ public class EZIDRequest
* @return
*/
public EZIDResponse mint(Map<String, String> metadata)
throws IOException, IdentifierException
throws IOException, IdentifierException, URISyntaxException
{
// POST path [+metadata]
HttpPost request;
request = new HttpPost(url);
URI uri = new URI(scheme, host, SHOULDER_PATH + authority, null);
request = new HttpPost(uri);
if (null != metadata)
{
request.setEntity(new StringEntity(formatMetadata(metadata), "UTF-8"));
request.setEntity(new StringEntity(formatMetadata(metadata), UTF_8));
}
HttpResponse response = client.execute(request);
EZIDResponse myResponse = new EZIDResponse(response);
// TODO add the identifier to the path for subsequent operations?
return myResponse;
}
/**
* Alter the identifier's metadata.
* Alter the metadata bound to an identifier.
*
* @param metadata fields to be altered. Leave a field's value empty to
* delete the field.
* @param metadata fields to be altered. Leave the value of a field's empty
* to delete the field.
* @return
*/
public EZIDResponse modify(Map<String, String> metadata)
throws IOException, IdentifierException
public EZIDResponse modify(String name, Map<String, String> metadata)
throws IOException, IdentifierException, URISyntaxException
{
if (null == metadata)
{
@@ -141,8 +163,9 @@ public class EZIDRequest
}
// POST path +metadata
HttpPost request;
request = new HttpPost(url);
request.setEntity(new StringEntity(formatMetadata(metadata), "UTF-8"));
URI uri = new URI(scheme, host, ID_PATH + authority + name, null);
request = new HttpPost(uri);
request.setEntity(new StringEntity(formatMetadata(metadata), UTF_8));
HttpResponse response = client.execute(request);
return new EZIDResponse(response);
}
@@ -150,12 +173,13 @@ public class EZIDRequest
/**
* Destroy a reserved identifier. Fails if ID was ever public.
*/
public EZIDResponse delete()
throws IOException, IdentifierException
public EZIDResponse delete(String name)
throws IOException, IdentifierException, URISyntaxException
{
// DELETE path
HttpDelete request;
request = new HttpDelete(url);
URI uri = new URI(scheme, host, ID_PATH + authority + name, null);
request = new HttpDelete(uri);
HttpResponse response = client.execute(request);
return new EZIDResponse(response);
}
@@ -163,12 +187,12 @@ public class EZIDRequest
/**
* Remove a public identifier from view.
*/
public EZIDResponse withdraw()
throws IOException, IdentifierException
public EZIDResponse withdraw(String name)
throws IOException, IdentifierException, URISyntaxException
{
Map<String, String> metadata = new HashMap<String, String>();
metadata.put("_status", "unavailable");
return modify(metadata);
metadata.put(MD_KEY_STATUS, "unavailable");
return modify(name, metadata);
}
/**
@@ -176,38 +200,39 @@ public class EZIDRequest
*
* @param reason annotation for the item's unavailability.
*/
public EZIDResponse withdraw(String reason)
throws IOException, IdentifierException
public EZIDResponse withdraw(String name, String reason)
throws IOException, IdentifierException, URISyntaxException
{
String reasonEncoded = null;
try {
reasonEncoded = URLEncoder.encode(reason, "UTF-8");
} catch (UnsupportedEncodingException e) { /* XXX SNH */ }
Map<String, String> metadata = new HashMap<String, String>();
metadata.put("_status", "unavailable | " + reasonEncoded);
return modify(metadata);
metadata.put(MD_KEY_STATUS, "unavailable | " + escape(reason));
return modify(name, metadata);
}
/**
* Create ANVL-formatted name/value pairs from a Map.
*/
private String formatMetadata(Map<String, String> raw)
private static String formatMetadata(Map<String, String> raw)
{
StringBuilder formatted = new StringBuilder();
for (Entry<String, String> entry : raw.entrySet())
formatted.append(entry.getKey())
{
formatted.append(escape(entry.getKey()))
.append(": ")
.append(entry.getValue())
.append(escape(entry.getValue()))
.append('\n');
}
// Body should be percent-encoded
String body = null;
try {
body = URLEncoder.encode(formatted.toString(), "UTF-8");
} catch (UnsupportedEncodingException ex) { // XXX SNH
log.error(ex.getMessage());
} finally {
return body;
}
return formatted.toString();
}
/**
* Percent-encode a few EZID-specific characters.
*/
private static String escape(String s)
{
return s.replace("%", "%25")
.replace("\n", "%0A")
.replace("\r", "%0D")
.replace(":", "%3A");
}
}

View File

@@ -9,7 +9,6 @@
package org.dspace.identifier.ezid;
import java.net.URISyntaxException;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.beans.factory.annotation.Required;
/**
@@ -32,42 +31,19 @@ public class EZIDRequestFactory
{
private static String EZID_SCHEME;
private static String EZID_HOST;
private static String EZID_PATH;
/**
* Configure an EZID request.
*
* @param requestPath specific request (DOI, shoulder).
* @param username
* @param password
* @param authority our DOI authority number.
* @param username EZID user name.
* @param password {@code username}'s password.
* @throws URISyntaxException
*/
public EZIDRequest getInstance(String requestPath, String username, String password)
public EZIDRequest getInstance(String authority, String username, String password)
throws URISyntaxException
{
URIBuilder uri = new URIBuilder();
uri.setScheme(EZID_SCHEME);
uri.setHost(EZID_HOST);
String head, tail;
if (EZID_PATH.endsWith("/"))
head = EZID_PATH.substring(0, EZID_PATH.length() - 1);
else
head = EZID_PATH;
if (requestPath.startsWith("/"))
tail = requestPath.substring( 0, requestPath.length() - 1);
else
tail = requestPath;
StringBuilder path = new StringBuilder();
path.append(head);
path.append('/');
path.append(tail);
uri.setPath(path.toString());
return new EZIDRequest(uri.build(), username, password);
return new EZIDRequest(EZID_SCHEME, EZID_HOST, authority, username, password);
}
/**
@@ -87,13 +63,4 @@ public class EZIDRequestFactory
{
EZID_HOST = aEZID_HOST;
}
/**
* @param aEZID_PATH the EZID path to set
*/
@Required
public static void setEZID_PATH(String aEZID_PATH)
{
EZID_PATH = aEZID_PATH;
}
}

View File

@@ -29,6 +29,8 @@ public class EZIDResponse
{
private static final Logger log = LoggerFactory.getLogger(EZIDResponse.class);
private static final String UTF_8 = "UTF-8";
private final String status;
private final String statusValue;
@@ -48,7 +50,7 @@ public class EZIDResponse
String body;
try
{
body = EntityUtils.toString(responseBody, "UTF-8");
body = EntityUtils.toString(responseBody, UTF_8);
} catch (IOException ex)
{
log.error(ex.getMessage());
@@ -83,10 +85,10 @@ public class EZIDResponse
parts = lines[i].split(":", 2);
String key = null, value = null;
try {
key = URLDecoder.decode(parts[0], "UTF-8").trim();
key = URLDecoder.decode(parts[0], UTF_8).trim();
if (parts.length > 1)
{
value = URLDecoder.decode(parts[1], "UTF-8").trim();
value = URLDecoder.decode(parts[1], UTF_8).trim();
}
else
{
@@ -120,7 +122,7 @@ public class EZIDResponse
}
/**
* Value associated with the EZID status (identifier, error text, etc.)
* Value associated with the EZID status (identifier, error text, etc.).
*/
public String getEZIDStatusValue()
{

View File

@@ -22,12 +22,11 @@
<bean class='org.dspace.identifier.ezid.EZIDRequestFactory'>
<property name='EZID_SCHEME' value='https'/>
<property name='EZID_HOST' value='n2t.net'/>
<property name='EZID_PATH' value='/ezid/shoulder/'/>
</bean>
</property>
<property name='crosswalk'>
<map>
<entry key='datacite.creator' value='dc.creator.author'/>
<entry key='datacite.creator' value='dc.contributor.author'/>
<entry key='datacite.title' value='dc.title'/>
<entry key='datacite.publisher' value='dc.publisher'/>
<entry key='datacite.publicationyear' value='dc.date.published'/>

View File

@@ -8,16 +8,17 @@
package org.dspace.identifier;
import java.util.List;
import java.io.IOException;
import java.sql.SQLException;
import java.util.UUID;
import org.dspace.AbstractUnitTest;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.*;
import org.dspace.core.Context;
import org.dspace.kernel.ServiceManager;
import org.dspace.services.ConfigurationService;
import org.dspace.workflow.WorkflowItem;
import org.dspace.workflow.WorkflowManager;
import org.junit.*;
import static org.junit.Assert.*;
@@ -28,29 +29,85 @@ import static org.junit.Assert.*;
public class DataCiteIdentifierProviderTest
extends AbstractUnitTest
{
private static final String TEST_SHOULDER = "doi:10.5072/FK2";
/** Name of the reserved EZID test authority */
private static final String TEST_SHOULDER = "10.5072/FK2";
private static ServiceManager sm = null;
private static ConfigurationService config = null;
private static Item item = null;
private static Community community;
private static Collection collection;
/** The most recently created test Item's ID */
private static int itemID;
public DataCiteIdentifierProviderTest()
{
}
private static void dumpMetadata(Item eyetem)
{
DCValue[] metadata = eyetem.getMetadata("dc", Item.ANY, Item.ANY, Item.ANY);
for (DCValue metadatum : metadata)
System.out.printf("Metadata: %s.%s.%s(%s) = %s\n",
metadatum.schema,
metadatum.element,
metadatum.qualifier,
metadatum.language,
metadatum.value);
}
/**
* Create a fresh Item, installed in the repository.
*
* @throws SQLException
* @throws AuthorizeException
* @throws IOException
*/
private Item newItem(Context ctx)
throws SQLException, AuthorizeException, IOException
{
ctx.turnOffAuthorisationSystem();
ctx.setCurrentUser(eperson);
WorkspaceItem wsItem = WorkspaceItem.create(ctx, collection, false);
WorkflowItem wfItem = WorkflowManager.start(ctx, wsItem);
WorkflowManager.advance(ctx, wfItem, ctx.getCurrentUser());
Item item = wfItem.getItem();
item.addMetadata("dc", "contributor", "author", null, "Author, A. N.");
item.addMetadata("dc", "title", null, null, "A Test Object");
item.addMetadata("dc", "publisher", null, null, "DSpace Test Harness");
item.update();
itemID = item.getID();
ctx.commit();
ctx.restoreAuthSystemState();
return item;
}
@BeforeClass
public static void setUpClass()
throws Exception
{
// Create an object to work with
Context ctx = new Context();
ctx.turnOffAuthorisationSystem();
Community community = Community.create(null, ctx);
Collection collection = community.createCollection();
WorkspaceItem wsItem = WorkspaceItem.create(ctx, collection, false);
item = wsItem.getItem();
ctx.setCurrentUser(eperson);
// Create an environment for our test objects to live in.
community = Community.create(null, ctx);
community.setMetadata("name", "A Test Community");
community.update();
collection = community.createCollection();
collection.setMetadata("name", "A Test Collection");
collection.update();
ctx.complete();
// Find the usual kernel services
@@ -58,26 +115,35 @@ public class DataCiteIdentifierProviderTest
config = kernelImpl.getConfigurationService();
// Configure the service under test
config.setProperty("identifier.doi.ezid.shoulder", TEST_SHOULDER);
config.setProperty("identifier.doi.ezid.user", "apitest");
config.setProperty("identifier.doi.ezid.password", "apitest");
// Configure the service under test.
config.setProperty(DataCiteIdentifierProvider.CFG_SHOULDER, TEST_SHOULDER);
config.setProperty(DataCiteIdentifierProvider.CFG_USER, "apitest");
config.setProperty(DataCiteIdentifierProvider.CFG_PASSWORD, "apitest");
// Don't try to send mail.
config.setProperty("mail.server.disabled", "true");
}
@AfterClass
public static void tearDownClass()
throws Exception
{
System.out.print("Tearing down\n\n");
Context ctx = new Context();
dumpMetadata(Item.find(ctx, itemID));
}
@Before
public void setUp()
{
context.setCurrentUser(eperson);
context.turnOffAuthorisationSystem();
}
@After
public void tearDown()
{
context.restoreAuthSystemState();
}
/**
@@ -103,7 +169,7 @@ public class DataCiteIdentifierProviderTest
System.out.println("supports");
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
String identifier = TEST_SHOULDER;
String identifier = "doi:" + TEST_SHOULDER;
boolean result = instance.supports(identifier);
assertTrue(identifier + " should be supported", result);
}
@@ -115,16 +181,16 @@ public class DataCiteIdentifierProviderTest
public void testRegister_Context_DSpaceObject()
throws Exception
{
System.out.println("register");
System.out.println("register 2");
List<DataCiteIdentifierProvider> instance
= (List<DataCiteIdentifierProvider>)
sm.getServicesByType(DataCiteIdentifierProvider.class);
DSpaceObject dso = item;
DSpaceObject dso = newItem(context);
String result = instance.get(0).register(context, dso);
assertTrue("Didn't get a DOI back", result.startsWith("doi:10.5072/"));
String result = instance.register(context, dso);
assertTrue("Didn't get a DOI back", result.startsWith("doi:" + TEST_SHOULDER));
System.out.println(" got identifier: " + result);
}
@@ -133,18 +199,17 @@ public class DataCiteIdentifierProviderTest
*/
@Test
public void testRegister_3args()
throws SQLException, AuthorizeException, IOException
{
System.out.println("register");
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
System.out.println("register 3");
List<DataCiteIdentifierProvider> instances
= (List<DataCiteIdentifierProvider>)
sm.getServicesByType(DataCiteIdentifierProvider.class);
DSpaceObject object = item;
DSpaceObject object = newItem(context);
String identifier = TEST_SHOULDER + "blarg"; // TODO a unique value
String identifier = UUID.randomUUID().toString();
instances.get(0).register(context, object, identifier);
}
@@ -157,13 +222,11 @@ public class DataCiteIdentifierProviderTest
throws Exception
{
System.out.println("reserve");
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
DSpaceObject dso = item;
String identifier = "";
DSpaceObject dso = newItem(context);
String identifier = UUID.randomUUID().toString();
instance.reserve(context, dso, identifier);
}
@@ -180,8 +243,7 @@ public class DataCiteIdentifierProviderTest
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
DSpaceObject dso = item;
String expResult = "";
DSpaceObject dso = newItem(context);
String result = instance.mint(context, dso);
assertEquals(expResult, result);
}
@@ -194,14 +256,14 @@ public class DataCiteIdentifierProviderTest
throws Exception
{
System.out.println("resolve");
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
String identifier = "";
String identifier = UUID.randomUUID().toString();
DSpaceObject expResult = newItem(context);
instance.register(context, expResult, identifier);
String[] attributes = null;
DSpaceObject expResult = null;
DSpaceObject result = instance.resolve(context, identifier, attributes);
assertEquals(expResult, result);
}
@@ -219,8 +281,10 @@ public class DataCiteIdentifierProviderTest
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
DSpaceObject object = item;
String expResult = "";
String identifier = UUID.randomUUID().toString();
DSpaceObject object = newItem(context);
instance.register(context, object, identifier);
String result = instance.lookup(context, object);
assertEquals(expResult, result);
}
@@ -232,31 +296,66 @@ public class DataCiteIdentifierProviderTest
public void testDelete_Context_DSpaceObject()
throws Exception
{
System.out.println("delete");
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
System.out.println("delete 2");
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
DSpaceObject dso = item;
DSpaceObject dso = newItem(context);
// Ensure that it has multiple DOIs (ooo, bad boy!)
String id1 = UUID.randomUUID().toString();
String id2 = UUID.randomUUID().toString();
instance.reserve(context, dso, id1);
instance.reserve(context, dso, id2);
// Test deletion
try {
instance.delete(context, dso);
} catch (IdentifierException e) {
// Creation of the Item registers a "public" identifier, which can't be deleted.
assertEquals("Unexpected exception", "1 identifiers could not be deleted.", e.getMessage());
}
// See if those identifiers were really deleted.
ItemIterator found;
found = Item.findByMetadataField(context,
DataCiteIdentifierProvider.MD_SCHEMA,
DataCiteIdentifierProvider.DOI_ELEMENT,
DataCiteIdentifierProvider.DOI_QUALIFIER, id1);
assertFalse("A test identifier is still present", found.hasNext());
found = Item.findByMetadataField(context,
DataCiteIdentifierProvider.MD_SCHEMA,
DataCiteIdentifierProvider.DOI_ELEMENT,
DataCiteIdentifierProvider.DOI_QUALIFIER, id2);
assertFalse("A test identifier is still present", found.hasNext());
}
/**
* Test of delete method, of class DataCiteIdentifierProvider.
*/
@Test
@Test()
public void testDelete_3args()
throws Exception
{
System.out.println("delete");
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
System.out.println("delete 3");
DataCiteIdentifierProvider instance = new DataCiteIdentifierProvider();
DSpaceObject dso = item;
String identifier = "";
DSpaceObject dso = newItem(context);
String identifier = UUID.randomUUID().toString();
// Set a known identifier on the object
instance.reserve(context, dso, identifier);
// Test deletion
instance.delete(context, dso, identifier);
// See if it is gone
ItemIterator found = Item.findByMetadataField(context,
DataCiteIdentifierProvider.MD_SCHEMA,
DataCiteIdentifierProvider.DOI_ELEMENT,
DataCiteIdentifierProvider.DOI_QUALIFIER, identifier);
assertFalse("Test identifier is still present", found.hasNext());
}
}