
org.dspace.content.BundleServiceImpl 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.content;
import static org.dspace.core.Constants.ADD;
import static org.dspace.core.Constants.READ;
import static org.dspace.core.Constants.REMOVE;
import static org.dspace.core.Constants.WRITE;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.dao.BundleDAO;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.BundleService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Service implementation for the Bundle object.
* This class is responsible for all business logic calls for the Bundle object and is autowired by spring.
* This class should never be accessed directly.
*
* @author kevinvandevelde at atmire.com
*/
public class BundleServiceImpl extends DSpaceObjectServiceImpl implements BundleService {
/**
* log4j logger
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(Bundle.class);
@Autowired(required = true)
protected BundleDAO bundleDAO;
@Autowired(required = true)
protected BitstreamService bitstreamService;
@Autowired(required = true)
protected ItemService itemService;
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Autowired(required = true)
protected ResourcePolicyService resourcePolicyService;
protected BundleServiceImpl() {
super();
}
@Override
public Bundle find(Context context, UUID id) throws SQLException {
// First check the cache
Bundle bundle = bundleDAO.findByID(context, Bundle.class, id);
if (bundle == null) {
if (log.isDebugEnabled()) {
log.debug(LogHelper.getHeader(context, "find_bundle",
"not_found,bundle_id=" + id));
}
return null;
} else {
if (log.isDebugEnabled()) {
log.debug(LogHelper.getHeader(context, "find_bundle",
"bundle_id=" + id));
}
return bundle;
}
}
@Override
public Bundle create(Context context, Item item, String name) throws SQLException, AuthorizeException {
if (StringUtils.isBlank(name)) {
throw new SQLException("Bundle must be created with non-null name");
}
authorizeService.authorizeAction(context, item, Constants.ADD);
// Create a table row
Bundle bundle = bundleDAO.create(context, new Bundle());
bundle.setName(context, name);
itemService.addBundle(context, item, bundle);
if (!bundle.getItems().contains(item)) {
bundle.addItem(item);
}
log.info(LogHelper.getHeader(context, "create_bundle", "bundle_id="
+ bundle.getID()));
// if we ever use the identifier service for bundles, we should
// create the bundle before we create the Event and should add all
// identifiers to it.
context.addEvent(new Event(Event.CREATE, Constants.BUNDLE, bundle.getID(), null));
return bundle;
}
@Override
public Bitstream getBitstreamByName(Bundle bundle, String name) {
Bitstream target = null;
for (Bitstream bitstream : bundle.getBitstreams()) {
if (name.equals(bitstream.getName())) {
target = bitstream;
break;
}
}
return target;
}
@Override
public void addBitstream(Context context, Bundle bundle, Bitstream bitstream)
throws SQLException, AuthorizeException {
// Check authorisation
authorizeService.authorizeAction(context, bundle, Constants.ADD);
log.info(LogHelper.getHeader(context, "add_bitstream", "bundle_id="
+ bundle.getID() + ",bitstream_id=" + bitstream.getID()));
// First check that the bitstream isn't already in the list
List bitstreams = bundle.getBitstreams();
int topOrder = 0;
// First check that the bitstream isn't already in the list
for (Bitstream bs : bitstreams) {
if (bitstream.getID().equals(bs.getID())) {
// Bitstream is already there; no change
return;
}
}
// Ensure that the last modified from the item is triggered !
Item owningItem = (Item) getParentObject(context, bundle);
if (owningItem != null) {
itemService.updateLastModified(context, owningItem);
itemService.update(context, owningItem);
}
bundle.addBitstream(bitstream);
// If a bitstream is moved from one bundle to another it may be temporarily flagged as deleted
// (when removed from the original bundle)
if (bitstream.isDeleted()) {
bitstream.setDeleted(false);
}
bitstream.getBundles().add(bundle);
context.addEvent(new Event(Event.ADD, Constants.BUNDLE, bundle.getID(),
Constants.BITSTREAM, bitstream.getID(), String.valueOf(bitstream.getSequenceID()),
getIdentifiers(context, bundle)));
// copy authorization policies from bundle to bitstream
// FIXME: multiple inclusion is affected by this...
authorizeService.inheritPolicies(context, bundle, bitstream);
// The next logic is a bit overly cautious but ensures that if there are any future start dates
// on the item or bitstream read policies, that we'll skip inheriting anything from the owning collection
// just in case. In practice, the item install process would overwrite these anyway but it may satisfy
// some other bitstream creation methods and integration tests
boolean isEmbargoed = false;
for (ResourcePolicy resourcePolicy : authorizeService.getPoliciesActionFilter(context, owningItem, READ)) {
if (!resourcePolicyService.isDateValid(resourcePolicy)) {
isEmbargoed = true;
break;
}
}
if (owningItem != null && !isEmbargoed) {
// Resolve owning collection
Collection owningCollection = owningItem.getOwningCollection();
if (owningCollection != null) {
// Get DEFAULT_BITSTREAM_READ policy from the collection
List defaultBitstreamReadGroups =
authorizeService.getAuthorizedGroups(context, owningCollection,
Constants.DEFAULT_BITSTREAM_READ);
// If this collection is configured with a DEFAULT_BITSTREAM_READ group, overwrite the READ policy
// inherited from the bundle with this policy.
if (!defaultBitstreamReadGroups.isEmpty()) {
// Remove read policies from the bitstream
authorizeService.removePoliciesActionFilter(context, bitstream, Constants.READ);
for (Group defaultBitstreamReadGroup : defaultBitstreamReadGroups) {
// Inherit this policy as READ, directly from the collection roles
authorizeService.addPolicy(context, bitstream,
Constants.READ, defaultBitstreamReadGroup, ResourcePolicy.TYPE_INHERITED);
}
}
}
}
bitstreamService.update(context, bitstream);
}
@Override
public void removeBitstream(Context context, Bundle bundle, Bitstream bitstream)
throws AuthorizeException, SQLException, IOException {
// Check authorisation
authorizeService.authorizeAction(context, bundle, Constants.REMOVE);
log.info(LogHelper.getHeader(context, "remove_bitstream",
"bundle_id=" + bundle.getID() + ",bitstream_id=" + bitstream.getID()));
context.addEvent(new Event(Event.REMOVE, Constants.BUNDLE, bundle.getID(),
Constants.BITSTREAM, bitstream.getID(), String.valueOf(bitstream.getSequenceID()),
getIdentifiers(context, bundle)));
//Ensure that the last modified from the item is triggered !
Item owningItem = (Item) getParentObject(context, bundle);
if (owningItem != null) {
itemService.updateLastModified(context, owningItem);
itemService.update(context, owningItem);
}
// In the event that the bitstream to remove is actually
// the primary bitstream, be sure to unset the primary
// bitstream.
if (bitstream.equals(bundle.getPrimaryBitstream())) {
bundle.unsetPrimaryBitstreamID();
}
// Check if our bitstream is part of a single or no bundle.
// Bitstream.getBundles() may be empty (the delete() method clears
// the bundles). We should not delete the bitstream, if it is used
// in another bundle, instead we just remove the link between bitstream
// and this bundle.
if (bitstream.getBundles().size() <= 1) {
// We don't need to remove the link between bundle & bitstream,
// this will be handled in the delete() method.
bitstreamService.delete(context, bitstream);
} else {
bundle.removeBitstream(bitstream);
bitstream.getBundles().remove(bundle);
}
}
@Override
public void inheritCollectionDefaultPolicies(Context context, Bundle bundle, Collection collection)
throws SQLException, AuthorizeException {
List policies = authorizeService.getPoliciesActionFilter(context, collection,
Constants.DEFAULT_BITSTREAM_READ);
// change the action to just READ
// just don't call update on the resourcepolicies!!!
Iterator i = policies.iterator();
if (!i.hasNext()) {
throw new java.sql.SQLException("Collection " + collection.getID()
+ " has no default bitstream READ policies");
}
List newPolicies = new ArrayList();
while (i.hasNext()) {
ResourcePolicy rp = resourcePolicyService.clone(context, i.next());
rp.setAction(Constants.READ);
newPolicies.add(rp);
}
replaceAllBitstreamPolicies(context, bundle, newPolicies);
}
@Override
public void replaceAllBitstreamPolicies(Context context, Bundle bundle, List newpolicies)
throws SQLException, AuthorizeException {
List bitstreams = bundle.getBitstreams();
if (CollectionUtils.isNotEmpty(bitstreams)) {
for (Bitstream bs : bitstreams) {
// change bitstream policies
authorizeService.removeAllPolicies(context, bs);
authorizeService.addPolicies(context, newpolicies, bs);
}
}
// change bundle policies
authorizeService.removeAllPolicies(context, bundle);
authorizeService.addPolicies(context, newpolicies, bundle);
}
@Override
public List getBitstreamPolicies(Context context, Bundle bundle) throws SQLException {
List list = new ArrayList();
List bitstreams = bundle.getBitstreams();
if (CollectionUtils.isNotEmpty(bitstreams)) {
for (Bitstream bs : bitstreams) {
list.addAll(authorizeService.getPolicies(context, bs));
}
}
return list;
}
@Override
public List getBundlePolicies(Context context, Bundle bundle) throws SQLException {
return authorizeService.getPolicies(context, bundle);
}
@Override
public void updateBitstreamOrder(Context context, Bundle bundle, int from, int to)
throws AuthorizeException, SQLException {
List bitstreams = bundle.getBitstreams();
if (bitstreams.size() < 1 || from >= bitstreams.size() || to >= bitstreams.size() || from < 0 || to < 0) {
throw new IllegalArgumentException(
"Invalid 'from' and 'to' arguments supplied for moving a bitstream within bundle " +
bundle.getID() + ". from: " + from + "; to: " + to
);
}
List bitstreamIds = new LinkedList<>();
for (Bitstream bitstream : bitstreams) {
bitstreamIds.add(bitstream.getID());
}
if (from < to) {
bitstreamIds.add(to + 1, bitstreamIds.get(from));
bitstreamIds.remove(from);
} else {
bitstreamIds.add(to, bitstreamIds.get(from));
bitstreamIds.remove(from + 1);
}
setOrder(context, bundle, bitstreamIds.toArray(new UUID[bitstreamIds.size()]));
}
@Override
public void moveBitstreamToBundle(Context context, Bundle targetBundle, Bitstream bitstream)
throws SQLException, AuthorizeException, IOException {
List bundles = new LinkedList<>();
bundles.addAll(bitstream.getBundles());
if (hasSufficientMovePermissions(context, bundles, targetBundle)) {
this.addBitstream(context, targetBundle, bitstream);
this.update(context, targetBundle);
for (Bundle bundle : bundles) {
this.removeBitstream(context, bundle, bitstream);
this.update(context, bundle);
}
}
}
/**
* Verifies if the context (user) has sufficient rights to the bundles in order to move a bitstream
*
* @param context The context
* @param bundles The current bundles in which the bitstream resides
* @param targetBundle The target bundle
* @return true when the context has sufficient rights
* @throws AuthorizeException When one of the necessary rights is not present
*/
private boolean hasSufficientMovePermissions(final Context context, final List bundles,
final Bundle targetBundle) throws SQLException, AuthorizeException {
for (Bundle bundle : bundles) {
if (!authorizeService.authorizeActionBoolean(context, bundle, WRITE) || !authorizeService
.authorizeActionBoolean(context, bundle, REMOVE)) {
throw new AuthorizeException(
"The current user does not have WRITE and REMOVE access to the current bundle: " + bundle
.getID());
}
}
if (!authorizeService.authorizeActionBoolean(context, targetBundle, WRITE) || !authorizeService
.authorizeActionBoolean(context, targetBundle, ADD)) {
throw new AuthorizeException(
"The current user does not have WRITE and ADD access to the target bundle: " + targetBundle
.getID());
}
for (Item item : targetBundle.getItems()) {
if (!authorizeService.authorizeActionBoolean(context, item, WRITE)) {
throw new AuthorizeException(
"The current user does not have WRITE access to the target bundle's item: " + item.getID());
}
}
return true;
}
@Override
public void setOrder(Context context, Bundle bundle, UUID[] bitstreamIds) throws AuthorizeException, SQLException {
authorizeService.authorizeAction(context, bundle, Constants.WRITE);
List currentBitstreams = bundle.getBitstreams();
List updatedBitstreams = new ArrayList();
// Loop through and ensure these Bitstream IDs are all valid. Add them to list of updatedBitstreams.
for (int i = 0; i < bitstreamIds.length; i++) {
UUID bitstreamId = bitstreamIds[i];
Bitstream bitstream = bitstreamService.find(context, bitstreamId);
// If we have an invalid Bitstream ID, just ignore it, but log a warning
if (bitstream == null) {
//This should never occur but just in case
log.warn(LogHelper.getHeader(context, "Invalid bitstream id while changing bitstream order",
"Bundle: " + bundle.getID() + ", bitstream id: " + bitstreamId));
continue;
}
// If we have a Bitstream not in the current list, log a warning & exit immediately
if (!currentBitstreams.contains(bitstream)) {
log.warn(LogHelper.getHeader(context,
"Encountered a bitstream not in this bundle while changing bitstream " +
"order. Bitstream order will not be changed.",
"Bundle: " + bundle.getID() + ", bitstream id: " + bitstreamId));
return;
}
updatedBitstreams.add(bitstream);
}
// If our lists are different sizes, exit immediately
if (updatedBitstreams.size() != currentBitstreams.size()) {
log.warn(LogHelper.getHeader(context,
"Size of old list and new list do not match. Bitstream order will not be " +
"changed.",
"Bundle: " + bundle.getID()));
return;
}
// As long as the order has changed, update it
if (CollectionUtils.isNotEmpty(updatedBitstreams) && !updatedBitstreams.equals(currentBitstreams)) {
//First clear out the existing list of bitstreams
bundle.clearBitstreams();
// Now add them back in the proper order
for (Bitstream bitstream : updatedBitstreams) {
bitstream.getBundles().remove(bundle);
bundle.addBitstream(bitstream);
bitstream.getBundles().add(bundle);
bitstreamService.update(context, bitstream);
}
//The order of the bitstreams has changed, ensure that we update the last modified of our item
Item owningItem = (Item) getParentObject(context, bundle);
if (owningItem != null) {
itemService.updateLastModified(context, owningItem);
itemService.update(context, owningItem);
}
}
}
@Override
public DSpaceObject getAdminObject(Context context, Bundle bundle, int action) throws SQLException {
DSpaceObject adminObject = null;
Item item = (Item) getParentObject(context, bundle);
Collection collection = null;
Community community = null;
if (item != null) {
collection = item.getOwningCollection();
if (collection != null) {
community = collection.getCommunities().get(0);
}
}
switch (action) {
case Constants.REMOVE:
if (AuthorizeConfiguration.canItemAdminPerformBitstreamDeletion()) {
adminObject = item;
} else if (AuthorizeConfiguration.canCollectionAdminPerformBitstreamDeletion()) {
adminObject = collection;
} else if (AuthorizeConfiguration
.canCommunityAdminPerformBitstreamDeletion()) {
adminObject = community;
}
break;
case Constants.ADD:
if (AuthorizeConfiguration.canItemAdminPerformBitstreamCreation()) {
adminObject = item;
} else if (AuthorizeConfiguration
.canCollectionAdminPerformBitstreamCreation()) {
adminObject = collection;
} else if (AuthorizeConfiguration
.canCommunityAdminPerformBitstreamCreation()) {
adminObject = community;
}
break;
default:
adminObject = bundle;
break;
}
return adminObject;
}
@Override
public DSpaceObject getParentObject(Context context, Bundle bundle) throws SQLException {
List- items = bundle.getItems();
if (CollectionUtils.isNotEmpty(items)) {
return items.iterator().next();
} else {
return null;
}
}
@Override
public void updateLastModified(Context context, Bundle dso) {
//No implemented for bundle
}
@Override
public void update(Context context, Bundle bundle) throws SQLException, AuthorizeException {
// Check authorisation
//AuthorizeManager.authorizeAction(ourContext, this, Constants.WRITE);
log.info(LogHelper.getHeader(context, "update_bundle", "bundle_id="
+ bundle.getID()));
super.update(context, bundle);
bundleDAO.save(context, bundle);
if (bundle.isModified() || bundle.isMetadataModified()) {
if (bundle.isMetadataModified()) {
context.addEvent(new Event(Event.MODIFY_METADATA, bundle.getType(), bundle.getID(), bundle.getDetails(),
getIdentifiers(context, bundle)));
}
context.addEvent(new Event(Event.MODIFY, Constants.BUNDLE, bundle.getID(),
null, getIdentifiers(context, bundle)));
bundle.clearModified();
bundle.clearDetails();
}
}
@Override
public void delete(Context context, Bundle bundle) throws SQLException, AuthorizeException, IOException {
log.info(LogHelper.getHeader(context, "delete_bundle", "bundle_id="
+ bundle.getID()));
authorizeService.authorizeAction(context, bundle, Constants.DELETE);
context.addEvent(new Event(Event.DELETE, Constants.BUNDLE, bundle.getID(),
bundle.getName(), getIdentifiers(context, bundle)));
// Remove bitstreams
List
bitstreams = bundle.getBitstreams();
for (Bitstream bitstream : bitstreams) {
removeBitstream(context, bundle, bitstream);
}
bundle.clearBitstreams();
List- items = new LinkedList<>(bundle.getItems());
bundle.getItems().clear();
for (Item item : items) {
item.removeBundle(bundle);
}
// Remove ourself
bundleDAO.delete(context, bundle);
}
@Override
public int getSupportsTypeConstant() {
return Constants.BUNDLE;
}
@Override
public Bundle findByIdOrLegacyId(Context context, String id) throws SQLException {
try {
if (StringUtils.isNumeric(id)) {
return findByLegacyId(context, Integer.parseInt(id));
} else {
return find(context, UUID.fromString(id));
}
} catch (IllegalArgumentException e) {
// Not a valid legacy ID or valid UUID
return null;
}
}
@Override
public Bundle findByLegacyId(Context context, int id) throws SQLException {
return bundleDAO.findByLegacyId(context, id, Bundle.class);
}
@Override
public int countTotal(Context context) throws SQLException {
return bundleDAO.countRows(context);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy