
org.dspace.handle.HandleServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
The newest version!
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.handle;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.content.service.SiteService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.handle.dao.HandleDAO;
import org.dspace.handle.service.HandleService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Interface to the CNRI Handle
* System.
*
*
* Currently, this class simply maps handles to local facilities; handles which
* are owned by other sites (including other DSpaces) are treated as
* non-existent.
*
*
* @author Peter Breton
*/
public class HandleServiceImpl implements HandleService {
/**
* log category
*/
private static final Logger log = LogManager.getLogger();
/**
* Prefix registered to no one
*/
static final String EXAMPLE_PREFIX = "123456789";
@Autowired(required = true)
protected HandleDAO handleDAO;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired
protected SiteService siteService;
private static final Pattern[] IDENTIFIER_PATTERNS = {
Pattern.compile("^hdl:(.*)$"),
Pattern.compile("^info:hdl/(.*)$"),
Pattern.compile("^https?://hdl\\.handle\\.net/(.*)$"),
Pattern.compile("^https?://.+/handle/(.*)$")
};
/**
* Public Constructor
*/
protected HandleServiceImpl() {
}
@Override
public String resolveToURL(Context context, String handle)
throws SQLException {
Handle dbhandle = findHandleInternal(context, handle);
if (dbhandle == null) {
return null;
}
String url = configurationService.getProperty("dspace.ui.url")
+ "/handle/" + handle;
log.debug("Resolved {} to {}", handle, url);
return url;
}
@Override
public String resolveUrlToHandle(Context context, String url)
throws SQLException {
String dspaceUrl = configurationService.getProperty("dspace.ui.url")
+ "/handle/";
String handleResolver = getCanonicalPrefix();
String handle = null;
if (url.startsWith(dspaceUrl)) {
handle = url.substring(dspaceUrl.length());
}
if (url.startsWith(handleResolver)) {
handle = url.substring(handleResolver.length());
}
if (null == handle) {
return null;
}
// remove trailing slashes
while (handle.startsWith("/")) {
handle = handle.substring(1);
}
Handle dbhandle = findHandleInternal(context, handle);
return (null == dbhandle) ? null : handle;
}
@Override
public String getCanonicalPrefix() {
// Let the admin define a new prefix, if not then we'll use the
// CNRI default. This allows the admin to use "hdl:" if they want to or
// use a locally branded prefix handle.myuni.edu.
String handlePrefix = configurationService.getProperty("handle.canonical.prefix",
"https://hdl.handle.net/");
return handlePrefix;
}
@Override
public String getCanonicalForm(String handle) {
return getCanonicalPrefix() + handle;
}
@Override
public String createHandle(Context context, DSpaceObject dso)
throws SQLException {
Handle handle = handleDAO.create(context, new Handle());
String handleId = createId(context);
handle.setHandle(handleId);
handle.setDSpaceObject(dso);
dso.addHandle(handle);
handle.setResourceTypeId(dso.getType());
handleDAO.save(context, handle);
log.debug("Created new handle for {} (ID={}) {}",
() -> Constants.typeText[dso.getType()],
() -> dso.getID(),
() -> handleId);
return handleId;
}
@Override
public String createHandle(Context context, DSpaceObject dso,
String suppliedHandle) throws SQLException, IllegalStateException {
return createHandle(context, dso, suppliedHandle, false);
}
@Override
public String createHandle(Context context, DSpaceObject dso,
String suppliedHandle, boolean force) throws SQLException, IllegalStateException {
//Check if the supplied handle is already in use -- cannot use the same handle twice
Handle handle = findHandleInternal(context, suppliedHandle);
if (handle != null && handle.getDSpaceObject() != null) {
//Check if this handle is already linked up to this specified DSpace Object
if (handle.getDSpaceObject().getID().equals(dso.getID())) {
//This handle already links to this DSpace Object -- so, there's nothing else we need to do
return suppliedHandle;
} else {
//handle found in DB table & already in use by another existing resource
throw new IllegalStateException(
"Attempted to create a handle which is already in use: " + suppliedHandle);
}
} else if (handle != null && handle.getResourceTypeId() != null) {
//If there is a 'resource_type_id' (but 'resource_id' is empty), then the object using
// this handle was previously unbound (see unbindHandle() method) -- likely because object was deleted
int previousType = handle.getResourceTypeId();
//Since we are restoring an object to a pre-existing handle, double check we are restoring the same
// *type* of object
// (e.g. we will not allow an Item to be restored to a handle previously used by a Collection)
if (previousType != dso.getType()) {
throw new IllegalStateException("Attempted to reuse a handle previously used by a " +
Constants.typeText[previousType] + " for a new " +
Constants.typeText[dso.getType()]);
}
} else if (handle == null) {
//if handle not found, create it
//handle not found in DB table -- create a new table entry
handle = handleDAO.create(context, new Handle());
handle.setHandle(suppliedHandle);
}
handle.setResourceTypeId(dso.getType());
handle.setDSpaceObject(dso);
dso.addHandle(handle);
handleDAO.save(context, handle);
log.debug("Created new handle for {} (ID={}) {}",
() -> Constants.typeText[dso.getType()],
() -> dso.getID(),
() -> suppliedHandle);
return suppliedHandle;
}
@Override
public void unbindHandle(Context context, DSpaceObject dso)
throws SQLException {
Iterator handles = dso.getHandles().iterator();
if (handles.hasNext()) {
while (handles.hasNext()) {
final Handle handle = handles.next();
handles.remove();
//Only set the "resouce_id" column to null when unbinding a handle.
// We want to keep around the "resource_type_id" value, so that we
// can verify during a restore whether the same *type* of resource
// is reusing this handle!
handle.setDSpaceObject(null);
handleDAO.save(context, handle);
log.debug("Unbound Handle {} from object {} id={}",
() -> handle.getHandle(),
() -> Constants.typeText[dso.getType()],
() -> dso.getID());
}
} else {
log.trace(
"Cannot find Handle entry to unbind for object {} id={}. Handle could have been unbound before.",
Constants.typeText[dso.getType()], dso.getID());
}
}
@Override
public DSpaceObject resolveToObject(Context context, String handle)
throws IllegalStateException, SQLException {
Handle dbhandle = findHandleInternal(context, handle);
// check if handle was allocated previously, but is currently not
// associated with a DSpaceObject
// (this may occur when 'unbindHandle()' is called for an obj that was removed)
if (dbhandle == null || (dbhandle.getDSpaceObject() == null)
|| (dbhandle.getResourceTypeId() == null)) {
//if handle has been unbound, just return null (as this will result in a PageNotFound)
return null;
}
return dbhandle.getDSpaceObject();
}
@Override
public String findHandle(Context context, DSpaceObject dso)
throws SQLException {
List handles = dso.getHandles();
if (CollectionUtils.isEmpty(handles)) {
return null;
} else {
//TODO: Move this code away from the HandleService & into the Identifier provider
//Attempt to retrieve a handle that does NOT look like {handle.part}/{handle.part}.{version}
String result = handles.iterator().next().getHandle();
for (Handle handle : handles) {
//Ensure that the handle doesn't look like this 12346/213.{version}
//If we find a match that indicates that we have a proper handle
if (!handle.getHandle().matches(".*/.*\\.\\d+")) {
result = handle.getHandle();
}
}
return result;
}
}
@Override
public List getHandlesForPrefix(Context context, String prefix)
throws SQLException {
List handles = handleDAO.findByPrefix(context, prefix);
List handleStrings = new ArrayList<>(handles.size());
for (Handle handle : handles) {
handleStrings.add(handle.getHandle());
}
return handleStrings;
}
@Override
public String getPrefix() {
String prefix = configurationService.getProperty("handle.prefix");
if (StringUtils.isBlank(prefix)) {
prefix = EXAMPLE_PREFIX; // XXX no good way to exit cleanly
log.error("handle.prefix is not configured; using {}", prefix);
}
return prefix;
}
@Override
public long countHandlesByPrefix(Context context, String prefix) throws SQLException {
return handleDAO.countHandlesByPrefix(context, prefix);
}
@Override
public int updateHandlesWithNewPrefix(Context context, String newPrefix, String oldPrefix) throws SQLException {
return handleDAO.updateHandlesWithNewPrefix(context, newPrefix, oldPrefix);
}
@Override
public void modifyHandleDSpaceObject(Context context, String handle, DSpaceObject newOwner) throws SQLException {
Handle dbHandle = findHandleInternal(context, handle);
if (dbHandle != null) {
// Check if we have to remove the handle from the current handle list
// or if object is already deleted.
if (dbHandle.getDSpaceObject() != null) {
// Remove the old handle from the current handle list
dbHandle.getDSpaceObject().getHandles().remove(dbHandle);
}
// Transfer the current handle to the new object
dbHandle.setDSpaceObject(newOwner);
dbHandle.setResourceTypeId(newOwner.getType());
newOwner.getHandles().add(0, dbHandle);
handleDAO.save(context, dbHandle);
}
}
////////////////////////////////////////
// Internal methods
////////////////////////////////////////
/**
* Find the database row corresponding to handle.
*
* @param context DSpace context
* @param handle The handle to resolve
* @return The database row corresponding to the handle
* @throws SQLException If a database error occurs
*/
protected Handle findHandleInternal(Context context, String handle)
throws SQLException {
if (handle == null) {
throw new IllegalArgumentException("Handle is null");
}
return handleDAO.findByHandle(context, handle);
}
/**
* Create/mint a new handle id.
*
* @param context DSpace Context
* @return A new handle id
* @throws SQLException If a database error occurs
*/
protected String createId(Context context) throws SQLException {
// Get configured prefix
String handlePrefix = getPrefix();
// Get next available suffix (as a Long, since DSpace uses an incrementing sequence)
Long handleSuffix = handleDAO.getNextHandleSuffix(context);
return handlePrefix + (handlePrefix.endsWith("/") ? "" : "/") + handleSuffix.toString();
}
@Override
public int countTotal(Context context) throws SQLException {
return handleDAO.countRows(context);
}
@Override
public String parseHandle(String identifier) {
if (identifier == null) {
return null;
}
if (identifier.startsWith(getPrefix() + "/")) {
// prefix is the equivalent of 123456789 in 123456789/???; don't strip
return identifier;
}
String canonicalPrefix = configurationService.getProperty("handle.canonical.prefix");
if (identifier.startsWith(canonicalPrefix + "/")) {
// prefix is the equivalent of https://hdl.handle.net/ in https://hdl.handle.net/123456789/???; strip
return StringUtils.stripStart(identifier, canonicalPrefix);
}
for (Pattern pattern : IDENTIFIER_PATTERNS) {
Matcher matcher = pattern.matcher(identifier);
if (matcher.matches()) {
return matcher.group(1);
}
}
// Check additional prefixes supported in the config file
String[] additionalPrefixes = getAdditionalPrefixes();
for (String additionalPrefix : additionalPrefixes) {
if (identifier.startsWith(additionalPrefix + "/")) {
// prefix is the equivalent of 123456789 in 123456789/???; don't strip
return identifier;
}
}
return null;
}
@Override
public String[] getAdditionalPrefixes() {
return configurationService.getArrayProperty("handle.additional.prefixes");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy