Merge pull request #3329 from atmire/w2p-80200_Retain_UUIDs

Fix #3330: Retain UUIDs on packager restore
This commit is contained in:
Tim Donohue
2021-10-07 11:12:08 -05:00
committed by GitHub
18 changed files with 528 additions and 41 deletions

View File

@@ -14,6 +14,7 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
@@ -104,6 +105,16 @@ public class Collection extends DSpaceObject implements DSpaceObjectLegacySuppor
} }
/**
* Takes a pre-determined UUID to be passed to the object to allow for the
* restoration of previously defined UUID's.
*
* @param uuid Takes a uuid to be passed to the Pre-Defined UUID Generator
*/
protected Collection(UUID uuid) {
this.predefinedUUID = uuid;
}
@Override @Override
public String getName() { public String getName() {
String value = getCollectionService() String value = getCollectionService()

View File

@@ -130,11 +130,22 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
@Override @Override
public Collection create(Context context, Community community, String handle) public Collection create(Context context, Community community, String handle)
throws SQLException, AuthorizeException { throws SQLException, AuthorizeException {
return create(context, community, handle, null);
}
@Override
public Collection create(Context context, Community community,
String handle, UUID uuid) throws SQLException, AuthorizeException {
if (community == null) { if (community == null) {
throw new IllegalArgumentException("Community cannot be null when creating a new collection."); throw new IllegalArgumentException("Community cannot be null when creating a new collection.");
} }
Collection newCollection = collectionDAO.create(context, new Collection()); Collection newCollection;
if (uuid != null) {
newCollection = collectionDAO.create(context, new Collection(uuid));
} else {
newCollection = collectionDAO.create(context, new Collection());
}
//Add our newly created collection to our community, authorization checks occur in THIS method //Add our newly created collection to our community, authorization checks occur in THIS method
communityService.addCollection(context, community, newCollection); communityService.addCollection(context, community, newCollection);
@@ -148,7 +159,8 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
authorizeService authorizeService
.createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.DEFAULT_ITEM_READ, null); .createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.DEFAULT_ITEM_READ, null);
authorizeService authorizeService
.createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.DEFAULT_BITSTREAM_READ, null); .createResourcePolicy(context, newCollection, anonymousGroup, null,
Constants.DEFAULT_BITSTREAM_READ, null);
collectionDAO.save(context, newCollection); collectionDAO.save(context, newCollection);

View File

@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
@@ -89,6 +90,16 @@ public class Community extends DSpaceObject implements DSpaceObjectLegacySupport
} }
/**
* Takes a pre-determined UUID to be passed to the object to allow for the
* restoration of previously defined UUID's.
*
* @param uuid Takes a uuid to be passed to the Pre-Defined UUID Generator
*/
protected Community(UUID uuid) {
this.predefinedUUID = uuid;
}
void addSubCommunity(Community subCommunity) { void addSubCommunity(Community subCommunity) {
subCommunities.add(subCommunity); subCommunities.add(subCommunity);
setModified(); setModified();

View File

@@ -86,13 +86,24 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
@Override @Override
public Community create(Community parent, Context context, String handle) throws SQLException, AuthorizeException { public Community create(Community parent, Context context, String handle) throws SQLException, AuthorizeException {
return create(parent, context, handle, null);
}
@Override
public Community create(Community parent, Context context, String handle,
UUID uuid) throws SQLException, AuthorizeException {
if (!(authorizeService.isAdmin(context) || if (!(authorizeService.isAdmin(context) ||
(parent != null && authorizeService.authorizeActionBoolean(context, parent, Constants.ADD)))) { (parent != null && authorizeService.authorizeActionBoolean(context, parent, Constants.ADD)))) {
throw new AuthorizeException( throw new AuthorizeException(
"Only administrators can create communities"); "Only administrators can create communities");
} }
Community newCommunity = communityDAO.create(context, new Community()); Community newCommunity;
if (uuid != null) {
newCommunity = communityDAO.create(context, new Community(uuid));
} else {
newCommunity = communityDAO.create(context, new Community());
}
if (parent != null) { if (parent != null) {
parent.addSubCommunity(newCommunity); parent.addSubCommunity(newCommunity);
@@ -387,13 +398,22 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
return createSubcommunity(context, parentCommunity, null); return createSubcommunity(context, parentCommunity, null);
} }
@Override @Override
public Community createSubcommunity(Context context, Community parentCommunity, String handle) public Community createSubcommunity(Context context, Community parentCommunity, String handle)
throws SQLException, AuthorizeException { throws SQLException, AuthorizeException {
return createSubcommunity(context, parentCommunity, handle, null);
}
@Override
public Community createSubcommunity(Context context, Community parentCommunity, String handle,
UUID uuid) throws SQLException, AuthorizeException {
// Check authorisation // Check authorisation
authorizeService.authorizeAction(context, parentCommunity, Constants.ADD); authorizeService.authorizeAction(context, parentCommunity, Constants.ADD);
Community c = create(parentCommunity, context, handle); Community c;
c = create(parentCommunity, context, handle, uuid);
addSubcommunity(context, parentCommunity, c); addSubcommunity(context, parentCommunity, c);
return c; return c;

View File

@@ -38,8 +38,8 @@ import org.hibernate.annotations.GenericGenerator;
@Table(name = "dspaceobject") @Table(name = "dspaceobject")
public abstract class DSpaceObject implements Serializable, ReloadableEntity<java.util.UUID> { public abstract class DSpaceObject implements Serializable, ReloadableEntity<java.util.UUID> {
@Id @Id
@GeneratedValue(generator = "system-uuid") @GeneratedValue(generator = "predefined-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid2") @GenericGenerator(name = "predefined-uuid", strategy = "org.dspace.content.PredefinedUUIDGenerator")
@Column(name = "uuid", unique = true, nullable = false, insertable = true, updatable = false) @Column(name = "uuid", unique = true, nullable = false, insertable = true, updatable = false)
protected java.util.UUID id; protected java.util.UUID id;
@@ -76,6 +76,15 @@ public abstract class DSpaceObject implements Serializable, ReloadableEntity<jav
@Transient @Transient
private boolean modified = false; private boolean modified = false;
/**
* This will read our predefinedUUID property to pass it along to the UUID generator
*/
@Transient
protected UUID predefinedUUID;
public UUID getPredefinedUUID() {
return predefinedUUID;
}
protected DSpaceObject() { protected DSpaceObject() {
} }

View File

@@ -13,6 +13,7 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
@@ -122,6 +123,16 @@ public class Item extends DSpaceObject implements DSpaceObjectLegacySupport {
} }
/**
* Takes a pre-determined UUID to be passed to the object to allow for the
* restoration of previously defined UUID's.
*
* @param uuid Takes a uuid to be passed to the Pre-Defined UUID Generator
*/
protected Item(UUID uuid) {
this.predefinedUUID = uuid;
}
/** /**
* Find out if the item is part of the main archive * Find out if the item is part of the main archive
* *

View File

@@ -176,11 +176,24 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Override @Override
public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException { public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException {
return create(context, workspaceItem, null);
}
@Override
public Item create(Context context, WorkspaceItem workspaceItem,
UUID uuid) throws SQLException, AuthorizeException {
Collection collection = workspaceItem.getCollection();
authorizeService.authorizeAction(context, collection, Constants.ADD);
if (workspaceItem.getItem() != null) { if (workspaceItem.getItem() != null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Attempting to create an item for a workspace item that already contains an item"); "Attempting to create an item for a workspace item that already contains an item");
} }
Item item = createItem(context); Item item = null;
if (uuid != null) {
item = createItem(context, uuid);
} else {
item = createItem(context);
}
workspaceItem.setItem(item); workspaceItem.setItem(item);
@@ -418,6 +431,30 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
return bitstreamList; return bitstreamList;
} }
protected Item createItem(Context context, UUID uuid) throws SQLException, AuthorizeException {
Item item;
if (uuid != null) {
item = itemDAO.create(context, new Item(uuid));
} else {
item = itemDAO.create(context, new Item());
}
// set discoverable to true (default)
item.setDiscoverable(true);
// Call update to give the item a last modified date. OK this isn't
// amazingly efficient but creates don't happen that often.
context.turnOffAuthorisationSystem();
update(context, item);
context.restoreAuthSystemState();
context.addEvent(new Event(Event.CREATE, Constants.ITEM, item.getID(),
null, getIdentifiers(context, item)));
log.info(LogHelper.getHeader(context, "create_item", "item_id=" + item.getID()));
return item;
}
protected Item createItem(Context context) throws SQLException, AuthorizeException { protected Item createItem(Context context) throws SQLException, AuthorizeException {
Item item = itemDAO.create(context, new Item()); Item item = itemDAO.create(context, new Item());
// set discoverable to true (default) // set discoverable to true (default)

View File

@@ -0,0 +1,33 @@
/**
* 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.content;
import java.io.Serializable;
import java.util.UUID;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.UUIDGenerator;
/**
* Allows DSpaceObjects to provide a pre-determined UUID
*
* @author April Herron
*/
public class PredefinedUUIDGenerator extends UUIDGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) {
if (object instanceof DSpaceObject) {
UUID uuid = ((DSpaceObject) object).getPredefinedUUID();
if (uuid != null) {
return uuid;
}
}
return super.generate(session, object);
}
}

View File

@@ -12,6 +12,7 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.app.util.DCInputsReaderException; import org.dspace.app.util.DCInputsReaderException;
@@ -81,6 +82,12 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
@Override @Override
public WorkspaceItem create(Context context, Collection collection, boolean template) public WorkspaceItem create(Context context, Collection collection, boolean template)
throws AuthorizeException, SQLException { throws AuthorizeException, SQLException {
return create(context, collection, null, template);
}
@Override
public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template)
throws AuthorizeException, SQLException {
// Check the user has permission to ADD to the collection // Check the user has permission to ADD to the collection
authorizeService.authorizeAction(context, collection, Constants.ADD); authorizeService.authorizeAction(context, collection, Constants.ADD);
@@ -89,7 +96,12 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
// Create an item // Create an item
Item item = itemService.create(context, workspaceItem); Item item;
if (uuid != null) {
item = itemService.create(context, workspaceItem, uuid);
} else {
item = itemService.create(context, workspaceItem);
}
item.setSubmitter(context.getCurrentUser()); item.setSubmitter(context.getCurrentUser());
// Now create the policies for the submitter to modify item and contents // Now create the policies for the submitter to modify item and contents

View File

@@ -777,9 +777,6 @@ public abstract class AbstractMETSDisseminator
Mets mets = new Mets(); Mets mets = new Mets();
String identifier = "DB-ID-" + dso.getID(); String identifier = "DB-ID-" + dso.getID();
if (dso.getHandle() != null) {
identifier = dso.getHandle().replace('/', '-');
}
// this ID should be globally unique (format: DSpace_[objType]_[handle with slash replaced with a dash]) // this ID should be globally unique (format: DSpace_[objType]_[handle with slash replaced with a dash])
mets.setID("DSpace_" + Constants.typeText[dso.getType()] + "_" + identifier); mets.setID("DSpace_" + Constants.typeText[dso.getType()] + "_" + identifier);

View File

@@ -16,6 +16,7 @@ import java.net.URLConnection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
@@ -409,6 +410,7 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
// get handle from manifest // get handle from manifest
handle = getObjectHandle(manifest); handle = getObjectHandle(manifest);
} }
UUID uuid = getObjectID(manifest);
// -- Step 2 -- // -- Step 2 --
// Create our DSpace Object based on info parsed from manifest, and // Create our DSpace Object based on info parsed from manifest, and
@@ -416,7 +418,7 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
DSpaceObject dso; DSpaceObject dso;
try { try {
dso = PackageUtils.createDSpaceObject(context, parent, dso = PackageUtils.createDSpaceObject(context, parent,
type, handle, params); type, handle, uuid, params);
} catch (SQLException sqle) { } catch (SQLException sqle) {
throw new PackageValidationException("Exception while ingesting " throw new PackageValidationException("Exception while ingesting "
+ pkgFile.getPath(), sqle); + pkgFile.getPath(), sqle);
@@ -727,7 +729,6 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
// retrieve path/name of file in manifest // retrieve path/name of file in manifest
String path = METSManifest.getFileName(mfile); String path = METSManifest.getFileName(mfile);
// extract the file input stream from package (or retrieve // extract the file input stream from package (or retrieve
// externally, if it is an externally referenced file) // externally, if it is an externally referenced file)
InputStream fileStream = getFileInputStream(pkgFile, params, path); InputStream fileStream = getFileInputStream(pkgFile, params, path);
@@ -1506,4 +1507,22 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
*/ */
public abstract String getConfigurationName(); public abstract String getConfigurationName();
public UUID getObjectID(METSManifest manifest)
throws PackageValidationException {
Element mets = manifest.getMets();
String idStr = mets.getAttributeValue("ID");
if (idStr == null || idStr.length() == 0) {
throw new PackageValidationException("Manifest is missing the required mets@ID attribute.");
}
if (idStr.contains("DB-ID-")) {
idStr = idStr.substring(idStr.lastIndexOf("DB-ID-") + 6, idStr.length());
}
try {
return UUID.fromString(idStr);
} catch (IllegalArgumentException ignored) {
//do nothing
}
return null;
}
} }

View File

@@ -17,6 +17,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -447,6 +448,7 @@ public class PackageUtils {
* @param parent Parent Object * @param parent Parent Object
* @param type Type of new Object * @param type Type of new Object
* @param handle Handle of new Object (may be null) * @param handle Handle of new Object (may be null)
* @param uuid
* @param params Properties-style list of options (interpreted by each packager). * @param params Properties-style list of options (interpreted by each packager).
* @return newly created DSpace Object (or null) * @return newly created DSpace Object (or null)
* @throws AuthorizeException if authorization error * @throws AuthorizeException if authorization error
@@ -454,29 +456,55 @@ public class PackageUtils {
* @throws IOException if IO error * @throws IOException if IO error
*/ */
public static DSpaceObject createDSpaceObject(Context context, DSpaceObject parent, int type, String handle, public static DSpaceObject createDSpaceObject(Context context, DSpaceObject parent, int type, String handle,
PackageParameters params) UUID uuid, PackageParameters params)
throws AuthorizeException, SQLException, IOException { throws AuthorizeException, SQLException, IOException {
DSpaceObject dso = null; DSpaceObject dso = null;
switch (type) { switch (type) {
case Constants.COLLECTION: case Constants.COLLECTION:
Collection collection = collectionService.find(context, uuid);
if (collection != null) {
dso = collectionService.create(context, (Community) parent, handle); dso = collectionService.create(context, (Community) parent, handle);
} else {
dso = collectionService.create(context, (Community) parent, handle, uuid);
}
return dso; return dso;
case Constants.COMMUNITY: case Constants.COMMUNITY:
// top-level community? // top-level community?
if (parent == null || parent.getType() == Constants.SITE) { if (parent == null || parent.getType() == Constants.SITE) {
Community community = communityService.find(context, uuid);
if (community != null) {
dso = communityService.create(null, context, handle); dso = communityService.create(null, context, handle);
} else { } else {
dso = communityService.create(null, context, handle, uuid);
}
} else {
Community community = communityService.find(context, uuid);
if (community != null) {
dso = communityService.createSubcommunity(context, ((Community) parent), handle); dso = communityService.createSubcommunity(context, ((Community) parent), handle);
} else {
dso = communityService.createSubcommunity(context, ((Community) parent), handle, uuid);
}
} }
return dso; return dso;
case Constants.ITEM: case Constants.ITEM:
//Initialize a WorkspaceItem //Initialize a WorkspaceItem
//(Note: Handle is not set until item is finished) //(Note: Handle is not set until item is finished)
WorkspaceItem wsi = workspaceItemService Item item = itemService.find(context, uuid);
.create(context, (Collection) parent, params.useCollectionTemplate()); if (item != null) {
return item;
}
WorkspaceItem wsi = null;
if (!params.replaceModeEnabled()) {
wsi = workspaceItemService.create(context, (Collection)parent, params.useCollectionTemplate());
} else {
wsi = workspaceItemService.create(context, (Collection)parent,
uuid, params.useCollectionTemplate());
}
// Please note that we are returning an Item which is *NOT* yet in the Archive, // Please note that we are returning an Item which is *NOT* yet in the Archive,
// and doesn't yet have a handle assigned. // and doesn't yet have a handle assigned.

View File

@@ -12,6 +12,7 @@ import java.io.InputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream; import org.dspace.content.Bitstream;
@@ -60,6 +61,21 @@ public interface CollectionService
public Collection create(Context context, Community community, String handle) throws SQLException, public Collection create(Context context, Community community, String handle) throws SQLException,
AuthorizeException; AuthorizeException;
/**
* Create a new collection with the supplied handle and ID.
* Once created the collection is added to the given community
*
* @param context DSpace context object
* @param community DSpace Community (parent)
* @param handle the pre-determined Handle to assign to the new collection
* @param uuid the pre-determined UUID to assign to the new collection
* @return the newly created collection
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Collection create(Context context, Community community, String handle, UUID uuid) throws SQLException,
AuthorizeException;
/** /**
* Get all collections in the system. These are alphabetically sorted by * Get all collections in the system. These are alphabetically sorted by
* collection name. * collection name.

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream; import org.dspace.content.Bitstream;
@@ -53,6 +54,20 @@ public interface CommunityService extends DSpaceObjectService<Community>, DSpace
public Community create(Community parent, Context context, String handle) public Community create(Community parent, Context context, String handle)
throws SQLException, AuthorizeException; throws SQLException, AuthorizeException;
/**
* Create a new top-level community, with a new ID.
*
* @param parent parent community
* @param context DSpace context object
* @param handle the pre-determined Handle to assign to the new community
* @param uuid the pre-determined uuid to assign to the new community
* @return the newly created community
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Community create(Community parent, Context context,
String handle, UUID uuid) throws SQLException, AuthorizeException;
/** /**
* Get a list of all communities in the system. These are alphabetically * Get a list of all communities in the system. These are alphabetically
@@ -202,6 +217,20 @@ public interface CommunityService extends DSpaceObjectService<Community>, DSpace
public Community createSubcommunity(Context context, Community parentCommunity, String handle) public Community createSubcommunity(Context context, Community parentCommunity, String handle)
throws SQLException, AuthorizeException; throws SQLException, AuthorizeException;
/**
* Create a new sub-community within this community.
*
* @param context context
* @param handle the pre-determined Handle to assign to the new community
* @param parentCommunity parent community
* @param uuid the pre-determined UUID to assign to the new community
* @return the new community
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Community createSubcommunity(Context context, Community parentCommunity, String handle, UUID uuid)
throws SQLException, AuthorizeException;
/** /**
* Add an existing community as a subcommunity to the community * Add an existing community as a subcommunity to the community
* *

View File

@@ -43,9 +43,8 @@ public interface ItemService
public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException; public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException;
/** /**
* Create a new item, with a new internal ID. This method is not public, * Create a new item, with a new internal ID. Authorization is done
* since items need to be created as workspace items. Authorisation is the * inside of this method.
* responsibility of the caller.
* *
* @param context DSpace context object * @param context DSpace context object
* @param workspaceItem in progress workspace item * @param workspaceItem in progress workspace item
@@ -55,6 +54,19 @@ public interface ItemService
*/ */
public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException; public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException;
/**
* Create a new item, with a provided ID. Authorisation is done
* inside of this method.
*
* @param context DSpace context object
* @param workspaceItem in progress workspace item
* @param uuid the pre-determined UUID to assign to the new item
* @return the newly created item
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Item create(Context context, WorkspaceItem workspaceItem, UUID uuid) throws SQLException, AuthorizeException;
/** /**
* Create an empty template item for this collection. If one already exists, * Create an empty template item for this collection. If one already exists,
* no action is taken. Caution: Make sure you call <code>update</code> on * no action is taken. Caution: Make sure you call <code>update</code> on

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection; import org.dspace.content.Collection;
@@ -55,6 +56,22 @@ public interface WorkspaceItemService extends InProgressSubmissionService<Worksp
public WorkspaceItem create(Context context, Collection collection, boolean template) public WorkspaceItem create(Context context, Collection collection, boolean template)
throws AuthorizeException, SQLException; throws AuthorizeException, SQLException;
/**
* Create a new workspace item, with a new ID. An Item is also created. The
* submitter is the current user in the context.
*
* @param context DSpace context object
* @param collection Collection being submitted to
* @param uuid the preferred uuid of the new item (used if restoring an item and retaining old uuid)
* @param template if <code>true</code>, the workspace item starts as a copy
* of the collection's template item
* @return the newly created workspace item
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template)
throws AuthorizeException, SQLException;
public WorkspaceItem create(Context c, WorkflowItem wfi) throws SQLException, AuthorizeException; public WorkspaceItem create(Context c, WorkflowItem wfi) throws SQLException, AuthorizeException;

View File

@@ -0,0 +1,205 @@
/**
* 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.app.packager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.google.common.collect.Iterators;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.crosswalk.MetadataValidationException;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.packager.METSManifest;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.jdom.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Basic integration testing for the Packager restore feature
*
* @author Nathan Buckingham
*/
public class PackagerIT extends AbstractIntegrationTestWithDatabase {
private ItemService itemService = ContentServiceFactory.getInstance().getItemService();
private CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
private CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
private WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
protected static final InstallItemService installItemService = ContentServiceFactory.getInstance()
.getInstallItemService();
protected ConfigurationService configService = DSpaceServicesFactory.getInstance().getConfigurationService();
protected Community child1;
protected Collection col1;
protected Item article;
File tempFile;
@Before
public void setup() throws IOException {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
// Create a new Publication (which is an Article)
article = ItemBuilder.createItem(context, col1)
.withTitle("Article")
.withIssueDate("2017-10-17")
.withEntityType("Publication")
.build();
tempFile = File.createTempFile("packagerExportTest", ".zip");
context.restoreAuthSystemState();
}
@After
public void destroy() throws SQLException, IOException {
tempFile.delete();
}
@Test
public void packagerExportUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
performExportScript(article.getHandle(), tempFile);
assertTrue(tempFile.length() > 0);
String idStr = getID();
assertEquals(idStr, article.getID().toString());
}
@Test
public void packagerImportUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
//Item
performExportScript(article.getHandle(), tempFile);
String idStr = getID();
itemService.delete(context, article);
performImportScript(tempFile);
Item item = itemService.find(context, UUID.fromString(idStr));
assertNotNull(item);
}
@Test
public void packagerImportColUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
configService.setProperty("upload.temp.dir",tempFile.getParent());
performExportScript(col1.getHandle(), tempFile);
String idStr = getID();
collectionService.delete(context, col1);
performImportScript(tempFile);
Collection collection = collectionService.find(context, UUID.fromString(idStr));
assertNotNull(collection);
}
@Test
public void packagerImportComUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
configService.setProperty("upload.temp.dir",tempFile.getParent());
//Community
performExportScript(child1.getHandle(), tempFile);
String idStr = getID();
communityService.delete(context, child1);
performImportScript(tempFile);
Community community = communityService.find(context, UUID.fromString(idStr));
assertNotNull(community);
}
@Test
public void packagerUUIDAlreadyExistTest() throws Exception {
context.turnOffAuthorisationSystem();
//Item should be overwritten if UUID already Exists
performExportScript(article.getHandle(), tempFile);
performImportScript(tempFile);
Iterator<Item> items = itemService.findByCollection(context, col1);
assertEquals(1, Iterators.size(items));
}
@Test
public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception {
context.turnOffAuthorisationSystem();
//should fail to restore the item because the uuid already exists.
performExportScript(article.getHandle(), tempFile);
UUID id = article.getID();
itemService.delete(context, article);
WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false);
installItemService.installItem(context, workspaceItem, "123456789/0100");
performImportNoForceScript(tempFile);
Iterator<Item> items = itemService.findByCollection(context, col1);
Item testItem = items.next();
assertFalse(items.hasNext()); //check to make sure there is only 1 item
assertEquals("123456789/0100", testItem.getHandle()); //check to make sure the item wasn't overwritten as
// it would have the old handle.
itemService.delete(context, testItem);
}
private String getID() throws IOException, MetadataValidationException {
//this method gets the UUID from the mets file thats stored in the attribute element
METSManifest manifest = null;
ZipFile zip = new ZipFile(tempFile);
ZipEntry manifestEntry = zip.getEntry(METSManifest.MANIFEST_FILE);
if (manifestEntry != null) {
// parse the manifest and sanity-check it.
manifest = METSManifest.create(zip.getInputStream(manifestEntry),
false, "AIP");
}
Element mets = manifest.getMets();
String idStr = mets.getAttributeValue("ID");
if (idStr.contains("DB-ID-")) {
idStr = idStr.substring(idStr.lastIndexOf("DB-ID-") + 6, idStr.length());
}
return idStr;
}
private void performExportScript(String handle, File outputFile) throws Exception {
runDSpaceScript("packager", "-d", "-e", "admin@email.com", "-i", handle, "-t",
"AIP", outputFile.getPath());
}
private void performImportNoForceScript(File outputFile) throws Exception {
runDSpaceScript("packager", "-r", "-u", "-e", "admin@email.com", "-t",
"AIP", outputFile.getPath());
}
private void performImportScript(File outputFile) throws Exception {
runDSpaceScript("packager", "-r", "-f", "-u", "-e", "admin@email.com", "-t",
"AIP", outputFile.getPath());
}
}

View File

@@ -14,6 +14,8 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
@@ -33,12 +35,15 @@ import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService; import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
/** /**
@@ -46,6 +51,7 @@ import org.springframework.test.util.ReflectionTestUtils;
* *
* @author pvillega * @author pvillega
*/ */
@RunWith(MockitoJUnitRunner.class)
public class WorkspaceItemTest extends AbstractUnitTest { public class WorkspaceItemTest extends AbstractUnitTest {
/** /**
@@ -98,6 +104,7 @@ public class WorkspaceItemTest extends AbstractUnitTest {
// "Wire" our spy to be used by the current loaded object services // "Wire" our spy to be used by the current loaded object services
// (To ensure these services use the spy instead of the real service) // (To ensure these services use the spy instead of the real service)
ReflectionTestUtils.setField(workspaceItemService, "authorizeService", authorizeServiceSpy); ReflectionTestUtils.setField(workspaceItemService, "authorizeService", authorizeServiceSpy);
ReflectionTestUtils.setField(itemService, "authorizeService", authorizeServiceSpy);
ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy); ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy);
ReflectionTestUtils.setField(communityService, "authorizeService", authorizeServiceSpy); ReflectionTestUtils.setField(communityService, "authorizeService", authorizeServiceSpy);
} catch (AuthorizeException ex) { } catch (AuthorizeException ex) {
@@ -158,7 +165,8 @@ public class WorkspaceItemTest extends AbstractUnitTest {
@Test @Test
public void testCreateAuth() throws Exception { public void testCreateAuth() throws Exception {
// Allow Collection ADD perms // Allow Collection ADD perms
doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD); doNothing().when(authorizeServiceSpy).authorizeAction(any(Context.class),
any(Collection.class), eq(Constants.ADD));
boolean template; boolean template;
WorkspaceItem created; WorkspaceItem created;