Merge branch 'master' into DS-1814

# Conflicts:
#	dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
This commit is contained in:
Luigi Andrea Pascarelli
2016-08-24 18:24:57 +02:00
36 changed files with 1091 additions and 431 deletions

View File

@@ -118,15 +118,17 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService
*/
@Override
public void delete(Context context, ResourcePolicy resourcePolicy) throws SQLException, AuthorizeException {
// FIXME: authorizations
// Remove ourself
resourcePolicyDAO.delete(context, resourcePolicy);
context.turnOffAuthorisationSystem();
if(resourcePolicy.getdSpaceObject() != null)
{
//A policy for a DSpace Object has been modified, fire a modify event on the DSpace object
contentServiceFactory.getDSpaceObjectService(resourcePolicy.getdSpaceObject()).updateLastModified(context, resourcePolicy.getdSpaceObject());
}
// FIXME: authorizations
// Remove ourself
resourcePolicyDAO.delete(context, resourcePolicy);
context.restoreAuthSystemState();
}
@@ -203,26 +205,34 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService
@Override
public void removeAllPolicies(Context c, DSpaceObject o) throws SQLException, AuthorizeException {
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
resourcePolicyDAO.deleteByDso(c, o);
c.turnOffAuthorisationSystem();
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
c.restoreAuthSystemState();
}
@Override
public void removePolicies(Context c, DSpaceObject o, String type) throws SQLException, AuthorizeException {
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
resourcePolicyDAO.deleteByDsoAndType(c, o, type);
c.turnOffAuthorisationSystem();
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
c.restoreAuthSystemState();
}
@Override
public void removeDsoGroupPolicies(Context context, DSpaceObject dso, Group group) throws SQLException, AuthorizeException {
contentServiceFactory.getDSpaceObjectService(dso).updateLastModified(context, dso);
resourcePolicyDAO.deleteByDsoGroupPolicies(context, dso, group);
context.turnOffAuthorisationSystem();
contentServiceFactory.getDSpaceObjectService(dso).updateLastModified(context, dso);
context.restoreAuthSystemState();
}
@Override
public void removeDsoEPersonPolicies(Context context, DSpaceObject dso, EPerson ePerson) throws SQLException, AuthorizeException {
contentServiceFactory.getDSpaceObjectService(dso).updateLastModified(context, dso);
resourcePolicyDAO.deleteByDsoEPersonPolicies(context, dso, ePerson);
context.turnOffAuthorisationSystem();
contentServiceFactory.getDSpaceObjectService(dso).updateLastModified(context, dso);
context.restoreAuthSystemState();
}
@@ -237,15 +247,19 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService
{
removeAllPolicies(c, o);
}else{
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
resourcePolicyDAO.deleteByDsoAndAction(c, o, actionId);
c.turnOffAuthorisationSystem();
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
c.restoreAuthSystemState();
}
}
@Override
public void removeDsoAndTypeNotEqualsToPolicies(Context c, DSpaceObject o, String type) throws SQLException, AuthorizeException {
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
resourcePolicyDAO.deleteByDsoAndTypeNotEqualsTo(c, o, type);
c.turnOffAuthorisationSystem();
contentServiceFactory.getDSpaceObjectService(o).updateLastModified(c, o);
c.restoreAuthSystemState();
}
@@ -279,10 +293,12 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService
}
//Update the last modified timestamp of all related DSpace Objects
context.turnOffAuthorisationSystem();
for (DSpaceObject dSpaceObject : relatedDSpaceObjects) {
//A policy for a DSpace Object has been modified, fire a modify event on the DSpace object
contentServiceFactory.getDSpaceObjectService(dSpaceObject).updateLastModified(context, dSpaceObject);
contentServiceFactory.getDSpaceObjectService(dSpaceObject).updateLastModified(context, dSpaceObject);
}
context.restoreAuthSystemState();
}
}
}

View File

@@ -62,6 +62,7 @@ public class InstallItemServiceImpl implements InstallItemService
AuthorizeException
{
Item item = is.getItem();
Collection collection = is.getCollection();
try {
if(suppliedHandle == null)
{
@@ -75,7 +76,15 @@ public class InstallItemServiceImpl implements InstallItemService
populateMetadata(c, item);
return finishItem(c, item, is);
// Finish up / archive the item
item = finishItem(c, item, is);
// As this is a BRAND NEW item, as a final step we need to remove the
// submitter item policies created during deposit and replace them with
// the default policies from the collection.
itemService.inheritCollectionDefaultPolicies(c, item, collection);
return item;
}
@Override
@@ -102,7 +111,7 @@ public class InstallItemServiceImpl implements InstallItemService
// If the item doesn't have a date.accessioned, set it to today
List<MetadataValue> dateAccessioned = itemService.getMetadata(item, MetadataSchema.DC_SCHEMA, "date", "accessioned", Item.ANY);
if (dateAccessioned.size() == 0)
if (dateAccessioned.isEmpty())
{
itemService.addMetadata(c, item, MetadataSchema.DC_SCHEMA, "date", "accessioned", null, now.toString());
}
@@ -174,7 +183,7 @@ public class InstallItemServiceImpl implements InstallItemService
// If an issue date was passed in and it wasn't set to "today" (literal string)
// then note this previous issue date in provenance message
if (currentDateIssued.size() != 0)
if (!currentDateIssued.isEmpty())
{
String previousDateIssued = currentDateIssued.get(0).getValue();
if(previousDateIssued!=null && !previousDateIssued.equalsIgnoreCase("today"))
@@ -189,8 +198,18 @@ public class InstallItemServiceImpl implements InstallItemService
itemService.addMetadata(c, item, MetadataSchema.DC_SCHEMA, "description", "provenance", "en", provDescription);
}
// final housekeeping when adding new Item to archive
// common between installing and "restoring" items.
/**
* Final housekeeping when adding a new Item into the archive.
* This method is used by *both* installItem() and restoreItem(),
* so all actions here will be run for a newly added item or a restored item.
*
* @param c DSpace Context
* @param item Item in question
* @param is InProgressSubmission object
* @return final "archived" Item
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
protected Item finishItem(Context c, Item item, InProgressSubmission is)
throws SQLException, AuthorizeException
{
@@ -213,10 +232,6 @@ public class InstallItemServiceImpl implements InstallItemService
// remove in-progress submission
contentServiceFactory.getInProgressSubmissionService(is).deleteWrapper(c, is);
// remove the item's policies and replace them with
// the defaults from the collection
itemService.inheritCollectionDefaultPolicies(c, item, is.getCollection());
// set embargo lift date and take away read access if indicated.
embargoService.setEmbargo(c, item);

View File

@@ -47,6 +47,7 @@ import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.service.IdentifierService;
import org.dspace.services.ConfigurationService;
import org.dspace.versioning.service.VersioningService;
import org.dspace.workflow.WorkflowItemService;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -93,6 +94,12 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Autowired(required=true)
protected ConfigurationService configurationService;
@Autowired(required=true)
protected WorkspaceItemService workspaceItemService;
@Autowired(required=true)
protected WorkflowItemService workflowItemService;
protected ItemServiceImpl()
{
super();
@@ -893,28 +900,37 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
// is this collection not yet created, and an item template is created
if (item.getOwningCollection() == null)
{
return true;
if (!isInProgressSubmission(context, item)) {
return true;
}
else {
return false;
}
}
return collectionService.canEditBoolean(context, item.getOwningCollection(), false);
}
@Override
public boolean canCreateNewVersion(Context context, Item item) throws SQLException{
if (authorizeService.isAdmin(context, item))
{
return true;
}
if (context.getCurrentUser() != null
&& context.getCurrentUser().equals(item.getSubmitter()))
{
return configurationService.getPropertyAsType(
"versioning.submitterCanCreateNewVersion", false);
}
return false;
/**
* Check if the item is an inprogress submission
* @param context
* @param item
* @return <code>true</code> if the item is an inprogress submission, i.e. a WorkspaceItem or WorkflowItem
* @throws SQLException
*/
public boolean isInProgressSubmission(Context context, Item item) throws SQLException {
return workspaceItemService.findByItem(context, item) != null
|| workflowItemService.findByItem(context, item) != null;
}
/*
With every finished submission a bunch of resource policy entries with have null value for the dspace_object column are generated in the database.
prevent the generation of resource policy entry values with null dspace_object as value
*/
@Override
/**
* Add the default policies, which have not been already added to the given DSpace object
@@ -1213,4 +1229,20 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
// return count of items that are not in archive and withdrawn
return itemDAO.countItems(context, false, true);
}
public boolean canCreateNewVersion(Context context, Item item) throws SQLException{
if (authorizeService.isAdmin(context, item))
{
return true;
}
if (context.getCurrentUser() != null
&& context.getCurrentUser().equals(item.getSubmitter()))
{
return configurationService.getPropertyAsType(
"versioning.submitterCanCreateNewVersion", false);
}
return false;
}
}

View File

@@ -17,7 +17,6 @@ import java.util.Map;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
@@ -448,238 +447,167 @@ public class METSRightsCrosswalk
public void ingest(Context context, DSpaceObject dso, List<Element> ml, boolean createMissingMetadataFields)
throws CrosswalkException, IOException, SQLException, AuthorizeException
{
// we cannot crosswalk METSRights to a SITE object
// SITE objects are not supported by the METSRightsCrosswalk
if (dso.getType() == Constants.SITE)
{
throw new CrosswalkObjectNotSupported("Wrong target object type, METSRightsCrosswalk cannot crosswalk a SITE object.");
}
//First, clear all existing Policies on this DSpace Object
// as we don't want them to conflict with policies we will be adding
if(!ml.isEmpty())
// If we're fed the top-level <RightsDeclarationMD> wrapper element, recurse into its guts.
// What we need to analyze are the <Context> elements underneath it.
if(!ml.isEmpty() && ml.get(0).getName().equals("RightsDeclarationMD"))
{
authorizeService.removeAllPolicies(context, dso);
ingest(context, dso, ml.get(0).getChildren(), createMissingMetadataFields);
}
// Loop through each Element in the List
List<ResourcePolicy> policies = new ArrayList<ResourcePolicy>();
for (Element element : ml)
else
{
// if we're fed a <RightsDeclarationMD> wrapper object, recurse on its guts:
if (element.getName().equals("RightsDeclarationMD"))
// Loop through each <Context> Element in the passed in List, creating a ResourcePolicy for each
List<ResourcePolicy> policies = new ArrayList<>();
for (Element element : ml)
{
ingest(context, dso, element.getChildren(), createMissingMetadataFields);
}
// "Context" section (where permissions are stored)
else if (element.getName().equals("Context"))
{
//get what class of context this is
String contextClass = element.getAttributeValue("CONTEXTCLASS");
ResourcePolicy rp = resourcePolicyService.create(context);
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
// get reference to the <Permissions> element
// Note: we are assuming here that there will only ever be ONE <Permissions>
// element. Currently there are no known use cases for multiple.
Element permsElement = element.getChild("Permissions", METSRights_NS);
if(permsElement == null) {
log.error("No <Permissions> element was found. Skipping this <Context> element.");
continue;
}
if (element.getAttributeValue("rpName") != null)
// Must be a "Context" section (where permissions are stored)
if (element.getName().equals("Context"))
{
rp.setRpName(element.getAttributeValue("rpName"));
}
try {
if (element.getAttributeValue("start-date") != null)
{
rp.setStartDate(sdf.parse(element.getAttributeValue("start-date")));
}
if (element.getAttributeValue("end-date") != null)
{
rp.setEndDate(sdf.parse(element.getAttributeValue("end-date")));
}
}catch (ParseException ex) {
log.error("Failed to parse embargo date. The date needs to be in the format 'yyyy-MM-dd'.", ex);
}
//Check if this permission pertains to Anonymous users
if(ANONYMOUS_CONTEXTCLASS.equals(contextClass))
{
//get DSpace Anonymous group, ID=0
Group anonGroup = groupService.findByName(context, Group.ANONYMOUS);
if(anonGroup==null)
{
throw new CrosswalkInternalException("The DSpace database has not been properly initialized. The Anonymous Group is missing from the database.");
//get what class of context this is
String contextClass = element.getAttributeValue("CONTEXTCLASS");
ResourcePolicy rp = resourcePolicyService.create(context);
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );
// get reference to the <Permissions> element
// Note: we are assuming here that there will only ever be ONE <Permissions>
// element. Currently there are no known use cases for multiple.
Element permsElement = element.getChild("Permissions", METSRights_NS);
if(permsElement == null) {
log.error("No <Permissions> element was found. Skipping this <Context> element.");
continue;
}
rp.setGroup(anonGroup);
} // else if this permission declaration pertains to Administrators
else if(ADMIN_CONTEXTCLASS.equals(contextClass))
{
//get DSpace Administrator group, ID=1
Group adminGroup = groupService.findByName(context, Group.ADMIN);
if(adminGroup==null)
if (element.getAttributeValue("rpName") != null)
{
throw new CrosswalkInternalException("The DSpace database has not been properly initialized. The Administrator Group is missing from the database.");
rp.setRpName(element.getAttributeValue("rpName"));
}
rp.setGroup(adminGroup);
} // else if this permission pertains to another DSpace group
else if(GROUP_CONTEXTCLASS.equals(contextClass))
{
try
{
//we need to find the name of DSpace group it pertains to
//Get the text within the <UserName> child element,
// this is the group's name
String groupName = element.getChildTextTrim("UserName", METSRights_NS);
//Translate Group name back to internal ID format (e.g. COLLECTION_<ID>_ADMIN)
// from its external format (e.g. COLLECTION_<handle>_ADMIN)
groupName = PackageUtils.translateGroupNameForImport(context, groupName);
//Check if this group exists in DSpace already
Group group = groupService.findByName(context, groupName);
//if not found, throw an error -- user should restore group from the SITE AIP
if(group==null)
try {
if (element.getAttributeValue("start-date") != null)
{
throw new CrosswalkInternalException("Cannot restore Group permissions on object ("
rp.setStartDate(sdf.parse(element.getAttributeValue("start-date")));
}
if (element.getAttributeValue("end-date") != null)
{
rp.setEndDate(sdf.parse(element.getAttributeValue("end-date")));
}
}catch (ParseException ex) {
log.error("Failed to parse embargo date. The date needs to be in the format 'yyyy-MM-dd'.", ex);
}
//Check if this permission pertains to Anonymous users
if(ANONYMOUS_CONTEXTCLASS.equals(contextClass))
{
//get DSpace Anonymous group, ID=0
Group anonGroup = groupService.findByName(context, Group.ANONYMOUS);
if(anonGroup==null)
{
throw new CrosswalkInternalException("The DSpace database has not been properly initialized. The Anonymous Group is missing from the database.");
}
rp.setGroup(anonGroup);
} // else if this permission declaration pertains to Administrators
else if(ADMIN_CONTEXTCLASS.equals(contextClass))
{
//get DSpace Administrator group, ID=1
Group adminGroup = groupService.findByName(context, Group.ADMIN);
if(adminGroup==null)
{
throw new CrosswalkInternalException("The DSpace database has not been properly initialized. The Administrator Group is missing from the database.");
}
rp.setGroup(adminGroup);
} // else if this permission pertains to another DSpace group
else if(GROUP_CONTEXTCLASS.equals(contextClass))
{
try
{
//we need to find the name of DSpace group it pertains to
//Get the text within the <UserName> child element,
// this is the group's name
String groupName = element.getChildTextTrim("UserName", METSRights_NS);
//Translate Group name back to internal ID format (e.g. COLLECTION_<ID>_ADMIN)
// from its external format (e.g. COLLECTION_<handle>_ADMIN)
groupName = PackageUtils.translateGroupNameForImport(context, groupName);
//Check if this group exists in DSpace already
Group group = groupService.findByName(context, groupName);
//if not found, throw an error -- user should restore group from the SITE AIP
if(group==null)
{
throw new CrosswalkInternalException("Cannot restore Group permissions on object ("
+ "type=" + Constants.typeText[dso.getType()] + ", "
+ "handle=" + dso.getHandle() + ", "
+ "ID=" + dso.getID()
+ "). The Group named '" + groupName + "' is missing from DSpace. "
+ "Please restore this group using the SITE AIP, or recreate it.");
}
//assign group to policy
rp.setGroup(group);
}
catch(PackageException pe)
{
//A PackageException will only be thrown if translateDefaultGroupName() fails
//We'll just wrap it as a CrosswalkException and throw it upwards
throw new CrosswalkException(pe);
}
}// else if this permission pertains to a DSpace person
else if(PERSON_CONTEXTCLASS.equals(contextClass))
{
//we need to find the person it pertains to
// Get the text within the <UserName> child element,
// this is the person's email address
String personEmail = element.getChildTextTrim("UserName", METSRights_NS);
//Check if this person exists in DSpace already
EPerson person = ePersonService.findByEmail(context, personEmail);
//If cannot find by email, try by netID
//(though METSRights should contain email if it was exported by DSpace)
if(person==null)
{
person = ePersonService.findByNetid(context, personEmail);
}
//if not found, throw an error -- user should restore person from the SITE AIP
if(person==null)
{
throw new CrosswalkInternalException("Cannot restore Person permissions on object ("
+ "type=" + Constants.typeText[dso.getType()] + ", "
+ "handle=" + dso.getHandle() + ", "
+ "ID=" + dso.getID()
+ "). The Group named '" + groupName + "' is missing from DSpace. "
+ "Please restore this group using the SITE AIP, or recreate it.");
+ "). The Person with email/netid '" + personEmail + "' is missing from DSpace. "
+ "Please restore this Person object using the SITE AIP, or recreate it.");
}
//assign group to policy
rp.setGroup(group);
}
catch(PackageException pe)
{
//A PackageException will only be thrown if translateDefaultGroupName() fails
//We'll just wrap it as a CrosswalkException and throw it upwards
throw new CrosswalkException(pe);
}
}// else if this permission pertains to a DSpace person
else if(PERSON_CONTEXTCLASS.equals(contextClass))
{
//we need to find the person it pertains to
// Get the text within the <UserName> child element,
// this is the person's email address
String personEmail = element.getChildTextTrim("UserName", METSRights_NS);
//Check if this person exists in DSpace already
EPerson person = ePersonService.findByEmail(context, personEmail);
//If cannot find by email, try by netID
//(though METSRights should contain email if it was exported by DSpace)
if(person==null)
{
person = ePersonService.findByNetid(context, personEmail);
//assign person to the policy
rp.setEPerson(person);
}//end if Person
else {
log.error("Unrecognized CONTEXTCLASS: " + contextClass);
}
//if not found, throw an error -- user should restore person from the SITE AIP
if(person==null)
{
throw new CrosswalkInternalException("Cannot restore Person permissions on object ("
+ "type=" + Constants.typeText[dso.getType()] + ", "
+ "handle=" + dso.getHandle() + ", "
+ "ID=" + dso.getID()
+ "). The Person with email/netid '" + personEmail + "' is missing from DSpace. "
+ "Please restore this Person object using the SITE AIP, or recreate it.");
}
//set permissions on policy add to list of policies
rp.setAction(parsePermissions(permsElement));
policies.add(rp);
} //end if "Context" element
}//end for loop
//assign person to the policy
rp.setEPerson(person);
}//end if Person
else {
log.error("Unrecognized CONTEXTCLASS: " + contextClass);
}
//set permissions on policy and add to object
rp.setAction(parsePermissions(permsElement));
policies.add(rp);
assignPermissions(context, dso, policies);
} //end if "Context" element
}//end while loop
}
/**
* Parses the 'permsElement' (corresponding to a <code>Permissions</code>
* element), and assigns those permissions to the specified Group
* on the specified DSpace Object.
*
* @param context DSpace context object
* @param dso The DSpace Object
*/
private void assignPermissions(Context context, DSpaceObject dso, List<ResourcePolicy> policies)
throws SQLException, AuthorizeException
{
authorizeService.removeAllPolicies(context, dso);
if (policies == null){
throw new AuthorizeException("Policies are null");
}
else{
// Finally, we need to remove any existing policies from the current object,
// and replace them with the policies provided via METSRights. NOTE:
// if the list of policies provided by METSRights is an empty list, then
// the final object will have no policies attached.
authorizeService.removeAllPolicies(context, dso);
authorizeService.addPolicies(context, policies, dso);
}
}
private void assignPermissions(Context context, DSpaceObject dso, Group group, Element permsElement)
throws SQLException, AuthorizeException
{
//first, parse our permissions to determine which action we are allowing in DSpace
int actionID = parsePermissions(permsElement);
//If action ID is less than base READ permissions (value=0),
// then something must've gone wrong in the parsing
if(actionID < Constants.READ)
{
log.warn("Unable to properly restore all access permissions on object ("
+ "type=" + Constants.typeText[dso.getType()] + ", "
+ "handle=" + dso.getHandle() + ", "
+ "ID=" + dso.getID()
+ ") for group '" + group.getName() + "'.");
}
//Otherwise, add the appropriate group policy for this object
authorizeService.addPolicy(context, dso, actionID, group);
}
/**
* Parses the 'permsElement' (corresponding to a <code>Permissions</code>
* element), and assigns those permissions to the specified EPerson
* on the specified DSpace Object.
*
* @param context DSpace context object
* @param dso The DSpace Object
* @param person The DSpace EPerson
* @param permsElement The METSRights <code>Permissions</code> element
*/
private void assignPermissions(Context context, DSpaceObject dso, EPerson person, Element permsElement)
throws SQLException, AuthorizeException
{
//first, parse our permissions to determine which action we are allowing in DSpace
int actionID = parsePermissions(permsElement);
//If action ID is less than base READ permissions (value=0),
// then something must've gone wrong in the parsing
if(actionID < Constants.READ)
{
log.warn("Unable to properly restore all access permissions on object ("
+ "type=" + Constants.typeText[dso.getType()] + ", "
+ "handle=" + dso.getHandle() + ", "
+ "ID=" + dso.getID()
+ ") for person '" + person.getEmail() + "'.");
}
//Otherwise, add the appropriate EPerson policy for this object
authorizeService.addPolicy(context, dso, actionID, person);
} // end else
}
/**

View File

@@ -333,18 +333,18 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester
}
else
{
ZipFile zip = new ZipFile(pkgFile);
try(ZipFile zip = new ZipFile(pkgFile))
{
// Retrieve the manifest file entry (named mets.xml)
ZipEntry manifestEntry = zip.getEntry(METSManifest.MANIFEST_FILE);
// Retrieve the manifest file entry (named mets.xml)
ZipEntry manifestEntry = zip.getEntry(METSManifest.MANIFEST_FILE);
// parse the manifest and sanity-check it.
manifest = METSManifest.create(zip.getInputStream(manifestEntry),
validate, getConfigurationName());
// close the Zip file for now
// (we'll extract the other files from zip when we need them)
zip.close();
if(manifestEntry!=null)
{
// parse the manifest and sanity-check it.
manifest = METSManifest.create(zip.getInputStream(manifestEntry),
validate, getConfigurationName());
}
}
}
// return our parsed out METS manifest

View File

@@ -563,4 +563,12 @@ public interface ItemService extends DSpaceObjectService<Item>, DSpaceObjectLega
* @throws SQLException if database error
*/
int countWithdrawnItems(Context context) throws SQLException;
/**
* Check if the supplied item is an inprogress submission
* @param context
* @param item
* @return <code>true</code> if the item is linked to a workspaceitem or workflowitem
*/
boolean isInProgressSubmission(Context context, Item item) throws SQLException;
}

View File

@@ -26,8 +26,8 @@ public class Handle implements ReloadableEntity<Integer> {
@Id
@Column(name="handle_id")
@GeneratedValue(strategy = GenerationType.SEQUENCE ,generator="handle_seq")
@SequenceGenerator(name="handle_seq", sequenceName="handle_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="handle_id_seq")
@SequenceGenerator(name="handle_id_seq", sequenceName="handle_id_seq", allocationSize = 1)
private Integer id;
@Column(name = "handle", unique = true)
@@ -57,6 +57,7 @@ public class Handle implements ReloadableEntity<Integer> {
}
@Override
public Integer getID() {
return id;
}
@@ -91,6 +92,7 @@ public class Handle implements ReloadableEntity<Integer> {
return resourceTypeId;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
@@ -105,6 +107,7 @@ public class Handle implements ReloadableEntity<Integer> {
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(id)

View File

@@ -8,20 +8,22 @@
package org.dspace.handle;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.content.service.SiteService;
import org.dspace.core.ConfigurationManager;
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;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* Interface to the <a href="http://www.handle.net" target=_new>CNRI Handle
* System </a>.
@@ -46,6 +48,8 @@ public class HandleServiceImpl implements HandleService
@Autowired(required = true)
protected HandleDAO handleDAO;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired
protected SiteService siteService;
@@ -66,7 +70,7 @@ public class HandleServiceImpl implements HandleService
return null;
}
String url = ConfigurationManager.getProperty("dspace.url")
String url = configurationService.getProperty("dspace.url")
+ "/handle/" + handle;
if (log.isDebugEnabled())
@@ -81,9 +85,9 @@ public class HandleServiceImpl implements HandleService
public String resolveUrlToHandle(Context context, String url)
throws SQLException
{
String dspaceUrl = ConfigurationManager.getProperty("dspace.url")
String dspaceUrl = configurationService.getProperty("dspace.url")
+ "/handle/";
String handleResolver = ConfigurationManager.getProperty("handle.canonical.prefix");
String handleResolver = configurationService.getProperty("handle.canonical.prefix");
String handle = null;
@@ -119,8 +123,8 @@ public class HandleServiceImpl implements HandleService
// 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 = ConfigurationManager.getProperty("handle.canonical.prefix");
if (handlePrefix == null || handlePrefix.length() == 0)
String handlePrefix = configurationService.getProperty("handle.canonical.prefix");
if (StringUtils.isBlank(handlePrefix))
{
handlePrefix = "http://hdl.handle.net/";
}
@@ -133,7 +137,7 @@ public class HandleServiceImpl implements HandleService
throws SQLException
{
Handle handle = handleDAO.create(context, new Handle());
String handleId = createId(handle.getID());
String handleId = createId(context);
handle.setHandle(handleId);
handle.setDSpaceObject(dso);
@@ -302,8 +306,8 @@ public class HandleServiceImpl implements HandleService
@Override
public String getPrefix()
{
String prefix = ConfigurationManager.getProperty("handle.prefix");
if (null == prefix)
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);
@@ -386,18 +390,22 @@ public class HandleServiceImpl implements HandleService
}
/**
* Create a new handle id. The implementation uses the PK of the RDBMS
* Handle table.
* Create/mint a new handle id.
*
* @param context DSpace Context
* @return A new handle id
* @exception SQLException
* If a database error occurs
*/
protected String createId(int id) throws SQLException
protected String createId(Context context) throws SQLException
{
// Get configured prefix
String handlePrefix = getPrefix();
return handlePrefix + (handlePrefix.endsWith("/") ? "" : "/") + id;
// 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

View File

@@ -24,6 +24,8 @@ import java.util.List;
*/
public interface HandleDAO extends GenericDAO<Handle> {
public Long getNextHandleSuffix(Context context) throws SQLException;
public List<Handle> getHandlesByDSpaceObject(Context context, DSpaceObject dso) throws SQLException;
public Handle findByHandle(Context context, String handle)throws SQLException;

View File

@@ -7,6 +7,9 @@
*/
package org.dspace.handle.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.core.AbstractHibernateDAO;
@@ -15,6 +18,10 @@ import org.dspace.handle.dao.HandleDAO;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Restrictions;
import org.hibernate.dialect.Dialect;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.service.jdbc.dialect.internal.StandardDialectResolver;
import org.hibernate.service.jdbc.dialect.spi.DialectResolver;
import java.sql.SQLException;
import java.util.Collections;
@@ -29,6 +36,9 @@ import java.util.List;
*/
public class HandleDAOImpl extends AbstractHibernateDAO<Handle> implements HandleDAO
{
// The name of the sequence used to determine next available handle
private static final String HANDLE_SEQUENCE = "handle_seq";
protected HandleDAOImpl()
{
super();
@@ -94,4 +104,45 @@ public class HandleDAOImpl extends AbstractHibernateDAO<Handle> implements Handl
public int countRows(Context context) throws SQLException {
return count(createQuery(context, "SELECT count(*) FROM Handle"));
}
/**
* Return next available value of Handle suffix (based on DB sequence).
* @param context Current DSpace Context
* @return next available Handle suffix (as a Long)
* @throws SQLException if database error or sequence doesn't exist
*/
@Override
public Long getNextHandleSuffix(Context context) throws SQLException
{
// Create a new Hibernate ReturningWork, which will return the
// result of the next value in the Handle Sequence.
ReturningWork<Long> nextValReturningWork = new ReturningWork<Long>() {
@Override
public Long execute(Connection connection) throws SQLException {
Long nextVal = 0L;
// Determine what dialect we are using for this DB
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
// Find the next value in our sequence (based on DB dialect)
try (PreparedStatement preparedStatement = connection.prepareStatement(dialect.getSequenceNextValString(HANDLE_SEQUENCE)))
{
// Execute query and return results
try(ResultSet resultSet = preparedStatement.executeQuery())
{
if(resultSet.next())
{
// Return result of query (from first column)
nextVal = resultSet.getLong(1);
}
}
}
return nextVal;
}
};
// Run our work, returning the next value in the sequence (see 'nextValReturningWork' above)
return getHibernateSession(context).doReturningWork(nextValReturningWork);
}
}

View File

@@ -65,14 +65,16 @@ implements DOIConnector
// Configuration property names
static final String CFG_USER = "identifier.doi.user";
static final String CFG_PASSWORD = "identifier.doi.password";
private static final String CFG_PREFIX
static final String CFG_PREFIX
= "identifier.doi.prefix";
private static final String CFG_PUBLISHER
static final String CFG_PUBLISHER
= "crosswalk.dissemination.DataCite.publisher";
private static final String CFG_DATAMANAGER
static final String CFG_DATAMANAGER
= "crosswalk.dissemination.DataCite.dataManager";
private static final String CFG_HOSTINGINSTITUTION
static final String CFG_HOSTINGINSTITUTION
= "crosswalk.dissemination.DataCite.hostingInstitution";
static final String CFG_NAMESPACE
= "crosswalk.dissemination.DataCite.namespace";
/**
* Stores the scheme used to connect to the DataCite server. It will be set
@@ -931,7 +933,9 @@ implements DOIConnector
{
return root;
}
Element identifier = new Element("identifier", "http://datacite.org/schema/kernel-3");
Element identifier = new Element("identifier",
configurationService.getProperty(CFG_NAMESPACE,
"http://datacite.org/schema/kernel-3"));
identifier.setAttribute("identifierType", "DOI");
identifier.addContent(doi.substring(DOI.SCHEME.length()));
return root.addContent(0, identifier);

View File

@@ -15,6 +15,8 @@ import org.dspace.administer.RegistryLoader;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.workflow.factory.WorkflowServiceFactory;
import org.dspace.xmlworkflow.service.XmlWorkflowService;
import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.api.callback.FlywayCallback;
import org.slf4j.Logger;
@@ -74,8 +76,7 @@ public class DatabaseRegistryUpdater implements FlywayCallback
MetadataImporter.loadRegistry(base + "sword-metadata.xml", true);
// Check if XML Workflow is enabled in workflow.cfg
String framework = config.getProperty("workflow.framework");
if (framework!=null && framework.equals("xmlworkflow"))
if (WorkflowServiceFactory.getInstance().getWorkflowService() instanceof XmlWorkflowService)
{
// If so, load in the workflow metadata types as well
MetadataImporter.loadRegistry(base + "workflow-types.xml", true);

View File

@@ -191,13 +191,29 @@ public class DatabaseUtils
{
// Otherwise, we assume "argv[1]" is a valid migration version number
// This is only for testing! Never specify for Production!
System.out.println("Migrating database ONLY to version " + argv[1] + " ... (Check logs for details)");
String migrationVersion = argv[1];
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
System.out.println("You've specified to migrate your database ONLY to version " + migrationVersion + " ...");
System.out.println("\nWARNING: It is highly likely you will see errors in your logs when the Metadata");
System.out.println("or Bitstream Format Registry auto-update. This is because you are attempting to");
System.out.println("use an OLD version " + argv[1] + " Database with a newer DSpace API. NEVER do this in a");
System.out.println("use an OLD version " + migrationVersion + " Database with a newer DSpace API. NEVER do this in a");
System.out.println("PRODUCTION scenario. The resulting old DB is only useful for migration testing.\n");
// Update the database, to the version specified.
updateDatabase(dataSource, connection, argv[1], true);
System.out.print("Are you SURE you only want to migrate your database to version " + migrationVersion + "? [y/n]: ");
String choiceString = input.readLine();
input.close();
if (choiceString.equalsIgnoreCase("y"))
{
System.out.println("Migrating database ONLY to version " + migrationVersion + " ... (Check logs for details)");
// Update the database, to the version specified.
updateDatabase(dataSource, connection, migrationVersion, false);
}
else
{
System.out.println("No action performed.");
}
}
}
else
@@ -307,6 +323,10 @@ public class DatabaseUtils
System.out.println("Done.");
System.exit(0);
}
else
{
System.out.println("No action performed.");
}
}
catch(SQLException e)
{
@@ -550,8 +570,8 @@ public class DatabaseUtils
protected static synchronized void updateDatabase(DataSource datasource, Connection connection)
throws SQLException
{
// By default, upgrade to the *latest* version and run migrations out-of-order
updateDatabase(datasource, connection, null, true);
// By default, upgrade to the *latest* version and never run migrations out-of-order
updateDatabase(datasource, connection, null, false);
}
/**
@@ -1373,13 +1393,28 @@ public class DatabaseUtils
flywaydb = null;
}
/**
* Returns the current Flyway schema_version being used by the given database.
* (i.e. the version of the highest numbered migration that this database has run)
* @param connection current DB Connection
* @return version as string
* @throws SQLException if database error occurs
*/
public static String getCurrentFlywayState(Connection connection) throws SQLException {
PreparedStatement statement = connection.prepareStatement("SELECT \"version\" FROM \"schema_version\" ORDER BY \"installed_rank\" desc");
PreparedStatement statement = connection.prepareStatement("SELECT \"version\" FROM \"schema_version\" ORDER BY \"version\" desc");
ResultSet resultSet = statement.executeQuery();
resultSet.next();
return resultSet.getString("version");
}
/**
* Return the DSpace version that this Flyway-enabled database reports to be compatible with.
* The version is retrieved from Flyway, and parsed into a Double to represent an actual
* DSpace version number (e.g. 5.0, 6.0, etc)
* @param connection current DB Connection
* @return reported DSpace version as a Double
* @throws SQLException if database error occurs
*/
public static Double getCurrentFlywayDSpaceState(Connection connection) throws SQLException
{
String flywayState = getCurrentFlywayState(connection);

View File

@@ -7,7 +7,6 @@
*/
package org.dspace.storage.rdbms.xmlworkflow;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.storage.rdbms.DatabaseUtils;
import org.dspace.workflow.factory.WorkflowServiceFactory;
@@ -19,6 +18,18 @@ import org.flywaydb.core.internal.util.scanner.classpath.ClassPathResource;
import java.sql.Connection;
/**
* This class automatically migrates your DSpace Database to use the
* XML-based Configurable Workflow system whenever it is enabled.
* <P>
* Because XML-based Configurable Workflow existed prior to our migration, this
* class first checks for the existence of the "cwf_workflowitem" table before
* running any migrations.
* <P>
* This class represents a Flyway DB Java Migration
* http://flywaydb.org/documentation/migration/java.html
* <P>
* It can upgrade a 6.0 version of DSpace to use the XMLWorkflow.
*
* User: kevin (kevin at atmire.com)
* Date: 1/09/15
* Time: 11:34