Merge branch 'master' into DS-4389-improving-patch-system-framework

This commit is contained in:
Kevin Van de Velde
2019-11-27 14:55:16 +01:00
350 changed files with 15118 additions and 3628 deletions

View File

@@ -193,7 +193,7 @@ public class MetadataImport {
* @param workflowNotify If the workflows should be used, whether to send notifications or not
* @param useTemplate Use collection template if create new item
* @return An array of BulkEditChange elements representing the items that have changed
* @throws MetadataImportException if something goes wrong
* @throws MetadataImportException if something goes wrong
*/
public List<BulkEditChange> runImport(boolean change,
boolean useWorkflow,
@@ -780,8 +780,8 @@ public class MetadataImport {
}
// Create the relationship
int leftPlace = relationshipService.findLeftPlaceByLeftItem(c, leftItem) + 1;
int rightPlace = relationshipService.findRightPlaceByRightItem(c, rightItem) + 1;
int leftPlace = relationshipService.findNextLeftPlaceByLeftItem(c, leftItem);
int rightPlace = relationshipService.findNextRightPlaceByRightItem(c, rightItem);
Relationship persistedRelationship = relationshipService.create(c, leftItem, rightItem,
foundRelationshipType, leftPlace, rightPlace);
relationshipService.update(c, persistedRelationship);

View File

@@ -13,6 +13,12 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.TreeMap;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.scripts.factory.ScriptServiceFactory;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
import org.dspace.scripts.handler.impl.CommandLineDSpaceRunnableHandler;
import org.dspace.servicemanager.DSpaceKernelImpl;
import org.dspace.servicemanager.DSpaceKernelInit;
import org.dspace.services.RequestService;
@@ -27,6 +33,9 @@ import org.jdom.input.SAXBuilder;
* @author Mark Diggory
*/
public class ScriptLauncher {
private static final Logger log = Logger.getLogger(ScriptLauncher.class);
/**
* The service manager kernel
*/
@@ -76,8 +85,9 @@ public class ScriptLauncher {
}
// Look up command in the configuration, and execute.
int status;
status = runOneCommand(commandConfigs, args);
CommandLineDSpaceRunnableHandler commandLineDSpaceRunnableHandler = new CommandLineDSpaceRunnableHandler();
int status = handleScript(args, commandConfigs, commandLineDSpaceRunnableHandler, kernelImpl);
// Destroy the service kernel if it is still alive
if (kernelImpl != null) {
@@ -86,6 +96,50 @@ public class ScriptLauncher {
}
System.exit(status);
}
/**
* This method will take the arguments from a commandline input and it'll find the script that the first argument
* refers to and it'll execute this script.
* It can return a 1 or a 0 depending on whether the script failed or passed respectively
* @param args The arguments for the script and the script as first one in the array
* @param commandConfigs The Document
* @param dSpaceRunnableHandler The DSpaceRunnableHandler for this execution
* @param kernelImpl The relevant DSpaceKernelImpl
* @return A 1 or 0 depending on whether the script failed or passed respectively
*/
public static int handleScript(String[] args, Document commandConfigs,
DSpaceRunnableHandler dSpaceRunnableHandler,
DSpaceKernelImpl kernelImpl) {
int status;
DSpaceRunnable script = ScriptServiceFactory.getInstance().getScriptService().getScriptForName(args[0]);
if (script != null) {
status = executeScript(args, dSpaceRunnableHandler, script);
} else {
status = runOneCommand(commandConfigs, args, kernelImpl);
}
return status;
}
/**
* This method will simply execute the script
* @param args The arguments of the script with the script name as first place in the array
* @param dSpaceRunnableHandler The relevant DSpaceRunnableHandler
* @param script The script to be executed
* @return A 1 or 0 depending on whether the script failed or passed respectively
*/
private static int executeScript(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler,
DSpaceRunnable script) {
try {
script.initialize(args, dSpaceRunnableHandler);
script.run();
return 0;
} catch (ParseException e) {
script.printHelp();
e.printStackTrace();
return 1;
}
}
protected static int runOneCommand(Document commandConfigs, String[] args) {
@@ -98,7 +152,7 @@ public class ScriptLauncher {
* @param commandConfigs Document
* @param args the command line arguments given
*/
public static int runOneCommand(Document commandConfigs, String[] args, DSpaceKernelImpl kernelImpl) {
protected static int runOneCommand(Document commandConfigs, String[] args, DSpaceKernelImpl kernelImpl) {
String request = args[0];
Element root = commandConfigs.getRootElement();
List<Element> commands = root.getChildren("command");

View File

@@ -15,6 +15,9 @@ import java.util.List;
* @author Andrea Bollini
*/
public class SHERPAPublisher {
private String id;
private String name;
private String alias;
@@ -49,7 +52,7 @@ public class SHERPAPublisher {
private String dateupdated;
public SHERPAPublisher(String name, String alias, String homeurl,
public SHERPAPublisher(String id, String name, String alias, String homeurl,
String prearchiving, List<String> prerestriction,
String postarchiving, List<String> postrestriction,
String pubarchiving, List<String> pubrestriction,
@@ -57,6 +60,8 @@ public class SHERPAPublisher {
String paidaccessname, String paidaccessnotes,
List<String[]> copyright, String romeocolour, String datedded,
String dateupdated) {
this.id = id;
this.name = name;
this.alias = alias;
@@ -160,4 +165,11 @@ public class SHERPAPublisher {
return dateupdated;
}
/**
* Generic getter for the id
* @return the id value of this SHERPAPublisher
*/
public String getId() {
return id;
}
}

View File

@@ -14,6 +14,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -24,7 +25,9 @@ import org.w3c.dom.Element;
* @author Andrea Bollini
*/
public class SHERPAResponse {
private boolean error;
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SHERPAResponse.class);
private int numHits;
private String message;
@@ -57,12 +60,13 @@ public class SHERPAResponse {
Element publishersElement = XMLUtils.getSingleElement(xmlRoot,
"publishers");
message = XMLUtils.getElementValue(headersElement, "message");
if (StringUtils.isNotBlank(message)) {
error = true;
return;
String numhitsString = XMLUtils.getElementValue(headersElement, "numhits");
if (StringUtils.isNotBlank(numhitsString)) {
numHits = Integer.parseInt(numhitsString);
} else {
numHits = 0;
}
message = XMLUtils.getElementValue(headersElement, "message");
license = XMLUtils.getElementValue(headersElement, "license");
licenseURL = XMLUtils.getElementValue(headersElement, "licenseurl");
@@ -112,9 +116,8 @@ public class SHERPAResponse {
Element copyrightlinksElement = XMLUtils.getSingleElement(
publisherElement, "copyrightlinks");
publishers
.add(new SHERPAPublisher(XMLUtils.getElementValue(
.add(new SHERPAPublisher(publisherElement.getAttribute("id"), XMLUtils.getElementValue(
publisherElement, "name"),
XMLUtils.getElementValue(publisherElement,
"alias"), XMLUtils.getElementValue(
@@ -162,17 +165,12 @@ public class SHERPAResponse {
}
}
} catch (Exception e) {
error = true;
log.error("Error parsing SHERPA API Response", e);
}
}
public SHERPAResponse(String message) {
this.message = message;
this.error = true;
}
public boolean isError() {
return error;
}
public String getMessage() {
@@ -198,4 +196,8 @@ public class SHERPAResponse {
public List<SHERPAPublisher> getPublishers() {
return publishers;
}
public int getNumHits() {
return numHits;
}
}

View File

@@ -52,11 +52,11 @@ public class InitializeEntities {
private RelationshipService relationshipService;
private EntityTypeService entityTypeService;
private InitializeEntities() {
relationshipTypeService = ContentServiceFactory.getInstance().getRelationshipTypeService();
relationshipService = ContentServiceFactory.getInstance().getRelationshipService();
entityTypeService = ContentServiceFactory.getInstance().getEntityTypeService();
}
/**

View File

@@ -35,6 +35,7 @@ public abstract class AuthorityServiceFactory {
public abstract AuthorityService getAuthorityService();
public abstract List<AuthorityIndexerInterface> getAuthorityIndexers();
public static AuthorityServiceFactory getInstance() {

View File

@@ -1,191 +0,0 @@
/**
* 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.authority.orcid;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.SolrAuthorityInterface;
import org.dspace.authority.orcid.xml.XMLtoBio;
import org.dspace.authority.rest.RESTConnector;
import org.json.JSONObject;
import org.orcid.jaxb.model.record_v2.Person;
/**
* @author Jonas Van Goolen (jonas at atmire dot com)
* This class contains all methods for retrieving "Person" objects calling the ORCID (version 2) endpoints.
* Additionally, this can also create AuthorityValues based on these returned Person objects
*/
public class Orcidv2 implements SolrAuthorityInterface {
private static Logger log = LogManager.getLogger(Orcidv2.class);
public RESTConnector restConnector;
private String OAUTHUrl;
private String clientId;
private String clientSecret;
private String accessToken;
/**
* Initialize the accessToken that is required for all subsequent calls to ORCID.
*
* @throws java.io.IOException passed through from HTTPclient.
*/
public void init() throws IOException {
if (StringUtils.isNotBlank(accessToken) && StringUtils.isNotBlank(clientSecret)) {
String authenticationParameters = "?client_id=" + clientId +
"&client_secret=" + clientSecret +
"&scope=/read-public&grant_type=client_credentials";
HttpPost httpPost = new HttpPost(OAUTHUrl + authenticationParameters);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse getResponse = httpClient.execute(httpPost);
InputStream is = getResponse.getEntity().getContent();
BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
JSONObject responseObject = null;
String inputStr;
while ((inputStr = streamReader.readLine()) != null && responseObject == null) {
if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) {
try {
responseObject = new JSONObject(inputStr);
} catch (Exception e) {
//Not as valid as I'd hoped, move along
responseObject = null;
}
}
}
if (responseObject != null && responseObject.has("access_token")) {
accessToken = (String) responseObject.get("access_token");
}
}
}
/**
* Makes an instance of the Orcidv2 class based on the provided parameters.
* This constructor is called through the spring bean initialization
*/
private Orcidv2(String url, String OAUTHUrl, String clientId, String clientSecret) {
this.restConnector = new RESTConnector(url);
this.OAUTHUrl = OAUTHUrl;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/**
* Makes an instance of the Orcidv2 class based on the provided parameters.
* This constructor is called through the spring bean initialization
*/
private Orcidv2(String url) {
this.restConnector = new RESTConnector(url);
}
/**
* Makes an instance of the AuthorityValue with the given information.
* @param text search string
* @return List<AuthorityValue>
*/
@Override
public List<AuthorityValue> queryAuthorities(String text, int max) {
List<Person> bios = queryBio(text, max);
List<AuthorityValue> result = new ArrayList<>();
for (Person person : bios) {
AuthorityValue orcidAuthorityValue = Orcidv2AuthorityValue.create(person);
if (orcidAuthorityValue != null) {
result.add(orcidAuthorityValue);
}
}
return result;
}
/**
* Create an AuthorityValue from a Person retrieved using the given orcid identifier.
* @param id orcid identifier
* @return AuthorityValue
*/
public AuthorityValue queryAuthorityID(String id) {
Person person = getBio(id);
AuthorityValue valueFromPerson = Orcidv2AuthorityValue.create(person);
return valueFromPerson;
}
/**
* Retrieve a Person object based on a given orcid identifier
* @param id orcid identifier
* @return Person
*/
public Person getBio(String id) {
log.debug("getBio called with ID=" + id);
if (!isValid(id)) {
return null;
}
InputStream bioDocument = restConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken);
XMLtoBio converter = new XMLtoBio();
Person person = converter.convertSinglePerson(bioDocument);
return person;
}
/**
* Retrieve a list of Person objects.
* @param text search string
* @param start offset to use
* @param rows how many rows to return
* @return List<Person>
*/
public List<Person> queryBio(String text, int start, int rows) {
if (rows > 100) {
throw new IllegalArgumentException("The maximum number of results to retrieve cannot exceed 100.");
}
String searchPath = "search?q=" + URLEncoder.encode(text) + "&start=" + start + "&rows=" + rows;
log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken);
InputStream bioDocument = restConnector.get(searchPath, accessToken);
XMLtoBio converter = new XMLtoBio();
List<Person> bios = converter.convert(bioDocument);
return bios;
}
/**
* Retrieve a list of Person objects.
* @param text search string
* @param max how many rows to return
* @return List<Person>
*/
public List<Person> queryBio(String text, int max) {
return queryBio(text, 0, max);
}
/**
* Check to see if the provided text has the correct ORCID syntax.
* Since only searching on ORCID id is allowed, this way, we filter out any queries that would return a
* blank result anyway
*/
private boolean isValid(String text) {
return StringUtils.isNotBlank(text) && text.matches(Orcidv2AuthorityValue.ORCID_ID_SYNTAX);
}
}

View File

@@ -1,342 +0,0 @@
/**
* 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.authority.orcid;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.AuthorityValueServiceImpl;
import org.dspace.authority.PersonAuthorityValue;
import org.dspace.utils.DSpace;
import org.orcid.jaxb.model.common_v2.ExternalId;
import org.orcid.jaxb.model.record_v2.ExternalIdentifiers;
import org.orcid.jaxb.model.record_v2.KeywordType;
import org.orcid.jaxb.model.record_v2.NameType;
import org.orcid.jaxb.model.record_v2.Person;
import org.orcid.jaxb.model.record_v2.ResearcherUrlType;
/**
* @author Jonas Van Goolen (jonas at atmire dot com)
*/
public class Orcidv2AuthorityValue extends PersonAuthorityValue {
/*
* The ORCID identifier
*/
private String orcid_id;
/*
* Map containing key-value pairs filled in by "setValues(Person person)".
* This represents all dynamic information of the object.
*/
private Map<String, List<String>> otherMetadata = new HashMap<String, List<String>>();
/**
* The syntax that the ORCID id needs to conform to
*/
public static final String ORCID_ID_SYNTAX = "\\d{4}-\\d{4}-\\d{4}-(\\d{3}X|\\d{4})";
/**
* Creates an instance of Orcidv2AuthorityValue with only uninitialized fields.
* This is meant to be filled in with values from an existing record.
* To create a brand new Orcidv2AuthorityValue, use create()
*/
public Orcidv2AuthorityValue() {
}
public Orcidv2AuthorityValue(SolrDocument document) {
super(document);
}
public String getOrcid_id() {
return orcid_id;
}
public void setOrcid_id(String orcid_id) {
this.orcid_id = orcid_id;
}
/**
* Create an empty authority.
* @return OrcidAuthorityValue
*/
public static Orcidv2AuthorityValue create() {
Orcidv2AuthorityValue orcidAuthorityValue = new Orcidv2AuthorityValue();
orcidAuthorityValue.setId(UUID.randomUUID().toString());
orcidAuthorityValue.updateLastModifiedDate();
orcidAuthorityValue.setCreationDate(new Date());
return orcidAuthorityValue;
}
/**
* Create an authority based on a given orcid bio
* @return OrcidAuthorityValue
*/
public static Orcidv2AuthorityValue create(Person person) {
if (person == null) {
return null;
}
Orcidv2AuthorityValue authority = Orcidv2AuthorityValue.create();
authority.setValues(person);
return authority;
}
/**
* Initialize this instance based on a Person object
* @param person Person
*/
protected void setValues(Person person) {
NameType name = person.getName();
if (!StringUtils.equals(name.getPath(), this.getOrcid_id())) {
this.setOrcid_id(name.getPath());
}
if (!StringUtils.equals(name.getFamilyName().getValue(), this.getLastName())) {
this.setLastName(name.getFamilyName().getValue());
}
if (!StringUtils.equals(name.getGivenNames().getValue(), this.getFirstName())) {
this.setFirstName(name.getGivenNames().getValue());
}
if (name.getCreditName() != null && StringUtils.isNotBlank(name.getCreditName().getValue())) {
if (!this.getNameVariants().contains(name.getCreditName().getValue())) {
this.addNameVariant(name.getCreditName().getValue());
}
}
if (person.getKeywords() != null) {
for (KeywordType keyword : person.getKeywords().getKeyword()) {
if (this.isNewMetadata("keyword", keyword.getContent())) {
this.addOtherMetadata("keyword", keyword.getContent());
}
}
}
ExternalIdentifiers externalIdentifiers = person.getExternalIdentifiers();
if (externalIdentifiers != null) {
for (ExternalId externalIdentifier : externalIdentifiers.getExternalIdentifier()) {
if (this.isNewMetadata("external_identifier", externalIdentifier.getExternalIdValue())) {
this.addOtherMetadata("external_identifier", externalIdentifier.getExternalIdValue());
}
}
}
if (person.getResearcherUrls() != null) {
for (ResearcherUrlType researcherUrl : person.getResearcherUrls().getResearcherUrl()) {
if (this.isNewMetadata("researcher_url", researcherUrl.getUrl().getValue())) {
this.addOtherMetadata("researcher_url", researcherUrl.getUrl().getValue());
}
}
}
if (person.getBiography() != null) {
if (this.isNewMetadata("biography", person.getBiography().getContent())) {
this.addOtherMetadata("biography", person.getBiography().getContent());
}
}
this.setValue(this.getName());
}
/**
* Makes an instance of the AuthorityValue with the given information.
* @param info string info
* @return AuthorityValue
*/
@Override
public AuthorityValue newInstance(String info) {
AuthorityValue authorityValue = null;
if (StringUtils.isNotBlank(info)) {
Orcidv2 orcid = new DSpace().getServiceManager().getServiceByName("AuthoritySource", Orcidv2.class);
authorityValue = orcid.queryAuthorityID(info);
} else {
authorityValue = this.create();
}
return authorityValue;
}
@Override
public void setValue(String value) {
super.setValue(value);
}
/**
* Check to see if the provided label / data pair is already present in the "otherMetadata" or not
* */
public boolean isNewMetadata(String label, String data) {
List<String> strings = getOtherMetadata().get(label);
boolean update;
if (strings == null) {
update = StringUtils.isNotBlank(data);
} else {
update = !strings.contains(data);
}
return update;
}
/**
* Add additional metadata to the otherMetadata map*/
public void addOtherMetadata(String label, String data) {
List<String> strings = otherMetadata.get(label);
if (strings == null) {
strings = new ArrayList<>();
}
strings.add(data);
otherMetadata.put(label, strings);
}
public Map<String, List<String>> getOtherMetadata() {
return otherMetadata;
}
/**
* Generate a solr record from this instance
* @return SolrInputDocument
*/
@Override
public SolrInputDocument getSolrInputDocument() {
SolrInputDocument doc = super.getSolrInputDocument();
if (StringUtils.isNotBlank(getOrcid_id())) {
doc.addField("orcid_id", getOrcid_id());
}
for (String t : otherMetadata.keySet()) {
List<String> data = otherMetadata.get(t);
for (String data_entry : data) {
doc.addField("label_" + t, data_entry);
}
}
return doc;
}
/**
* Information that can be used the choice ui
* @return map
*/
@Override
public Map<String, String> choiceSelectMap() {
Map<String, String> map = super.choiceSelectMap();
String orcid_id = getOrcid_id();
if (StringUtils.isNotBlank(orcid_id)) {
map.put("orcid", orcid_id);
}
return map;
}
@Override
public String getAuthorityType() {
return "orcid";
}
/**
* Provides a string that will allow this AuthorityType to be recognized and provides information to create a new
* instance to be created using public Orcidv2AuthorityValue newInstance(String info).
* @return see {@link org.dspace.authority.service.AuthorityValueService#GENERATE AuthorityValueService.GENERATE}
*/
@Override
public String generateString() {
String generateString = AuthorityValueServiceImpl.GENERATE + getAuthorityType() +
AuthorityValueServiceImpl.SPLIT;
if (StringUtils.isNotBlank(getOrcid_id())) {
generateString += getOrcid_id();
}
return generateString;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Orcidv2AuthorityValue that = (Orcidv2AuthorityValue) o;
if (orcid_id != null ? !orcid_id.equals(that.orcid_id) : that.orcid_id != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return orcid_id != null ? orcid_id.hashCode() : 0;
}
/**
* The regular equals() only checks if both AuthorityValues describe the same authority.
* This method checks if the AuthorityValues have different information
* E.g. it is used to decide when lastModified should be updated.
* @param o object
* @return true or false
*/
@Override
public boolean hasTheSameInformationAs(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.hasTheSameInformationAs(o)) {
return false;
}
Orcidv2AuthorityValue that = (Orcidv2AuthorityValue) o;
if (orcid_id != null ? !orcid_id.equals(that.orcid_id) : that.orcid_id != null) {
return false;
}
for (String key : otherMetadata.keySet()) {
if (otherMetadata.get(key) != null) {
List<String> metadata = otherMetadata.get(key);
List<String> otherMetadata = that.otherMetadata.get(key);
if (otherMetadata == null) {
return false;
} else {
HashSet<String> metadataSet = new HashSet<String>(metadata);
HashSet<String> otherMetadataSet = new HashSet<String>(otherMetadata);
if (!metadataSet.equals(otherMetadataSet)) {
return false;
}
}
} else {
if (that.otherMetadata.get(key) != null) {
return false;
}
}
}
return true;
}
}

View File

@@ -8,6 +8,7 @@
package org.dspace.authority.rest;
import org.dspace.authority.SolrAuthorityInterface;
import org.dspace.external.OrcidRestConnector;
/**
* @author Antoine Snyers (antoine at atmire.com)
@@ -17,9 +18,9 @@ import org.dspace.authority.SolrAuthorityInterface;
*/
public abstract class RestSource implements SolrAuthorityInterface {
protected RESTConnector restConnector;
protected OrcidRestConnector restConnector;
public RestSource(String url) {
this.restConnector = new RESTConnector(url);
this.restConnector = new OrcidRestConnector(url);
}
}

View File

@@ -7,6 +7,10 @@
*/
package org.dspace.content;
import static org.dspace.core.Constants.ADD;
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;
@@ -268,6 +272,81 @@ public class BundleServiceImpl extends DSpaceObjectServiceImpl<Bundle> implement
return authorizeService.getPolicies(context, bundle);
}
@Override
public void updateBitstreamOrder(Context context, Bundle bundle, int from, int to)
throws AuthorizeException, SQLException {
List<Bitstream> 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<UUID> 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<Bundle> 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<Bundle> 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);

View File

@@ -761,6 +761,8 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
// Remove any workflow roles
collectionRoleService.deleteByCollection(context, collection);
collection.getResourcePolicies().clear();
// Remove default administrators group
Group g = collection.getAdministrators();

View File

@@ -146,7 +146,7 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
public List<MetadataValue> getMetadataByMetadataString(T dso, String mdString) {
StringTokenizer dcf = new StringTokenizer(mdString, ".");
String[] tokens = { "", "", "" };
String[] tokens = {"", "", ""};
int i = 0;
while (dcf.hasMoreTokens()) {
tokens[i] = dcf.nextToken().trim();
@@ -250,8 +250,11 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
}
}
MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField);
//Set place to list length
metadataValue.setPlace(this.getMetadata(dso, Item.ANY, Item.ANY, Item.ANY, Item.ANY).size());
//Set place to list length of all metadatavalues for the given schema.element.qualifier combination.
// Subtract one to adhere to the 0 as first element rule
metadataValue.setPlace(
this.getMetadata(dso, metadataField.getMetadataSchema().getName(), metadataField.getElement(),
metadataField.getQualifier(), Item.ANY).size() - 1);
metadataValue.setLanguage(lang == null ? null : lang.trim());
@@ -533,7 +536,7 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
protected String[] getMDValueByField(String field) {
StringTokenizer dcf = new StringTokenizer(field, ".");
String[] tokens = { "", "", "" };
String[] tokens = {"", "", ""};
int i = 0;
while (dcf.hasMoreTokens()) {
tokens[i] = dcf.nextToken().trim();
@@ -571,7 +574,7 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
if (compare == 0) {
if (o1 instanceof RelationshipMetadataValue) {
return 1;
} else if (o2 instanceof RelationshipMetadataValue) {
} else if (o2 instanceof RelationshipMetadataValue) {
return -1;
}
}

View File

@@ -695,6 +695,11 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
log.info(LogManager.getHeader(context, "delete_item", "item_id="
+ item.getID()));
// Remove relationships
for (Relationship relationship : relationshipService.findByItem(context, item)) {
relationshipService.delete(context, relationship, false, false);
}
// Remove bundles
removeAllBundles(context, item);

View File

@@ -0,0 +1,19 @@
/**
* 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;
/**
* This Enum holds a representation of all the possible states that a Process can be in
*/
public enum ProcessStatus {
SCHEDULED,
RUNNING,
COMPLETED,
FAILED
}

View File

@@ -7,9 +7,11 @@
*/
package org.dspace.content;
import java.sql.SQLException;
import java.util.List;
import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.core.Context;
/**
* Interface used for the {@link RelationshipMetadataServiceImpl}
@@ -27,4 +29,34 @@ public interface RelationshipMetadataService {
* @return The list of MetadataValue objects constructed through the Relationships
*/
public List<RelationshipMetadataValue> getRelationshipMetadata(Item item, boolean enableVirtualMetadata);
/**
* Retrieves the list of RelationshipMetadataValue objects specific to only one Relationship of the item.
*
* This method processes one Relationship of an Item and will return a list of RelationshipMetadataValue objects
* that are generated for this specific relationship for the item through the config in VirtualMetadataPopulator
*
* It returns a combination of the output of the findVirtualMetadataFromConfiguration method and
* the getRelationMetadataFromOtherItem method.
*
* @param context The context
* @param item The item whose virtual metadata is requested
* @param entityType The entity type of the given item
* @param relationship The relationship whose virtual metadata is requested
* @param enableVirtualMetadata Determines whether the VirtualMetadataPopulator should be used.
* If false, only the relation."relationname" metadata is populated
* If true, fields from the spring config virtual metadata is included as well
* @return The list of virtual metadata values
*/
public List<RelationshipMetadataValue> findRelationshipMetadataValueForItemRelationship(
Context context, Item item, String entityType, Relationship relationship, boolean enableVirtualMetadata)
throws SQLException;
/**
* This method will retrieve the EntityType String from an item
* @param item The Item for which the entityType String will be returned
* @return A String value indicating the entityType
*/
public String getEntityTypeStringFromMetadata(Item item);
}

View File

@@ -44,8 +44,7 @@ public class RelationshipMetadataServiceImpl implements RelationshipMetadataServ
Context context = new Context();
List<RelationshipMetadataValue> fullMetadataValueList = new LinkedList<>();
try {
List<MetadataValue> list = item.getMetadata();
String entityType = getEntityTypeStringFromMetadata(list);
String entityType = getEntityTypeStringFromMetadata(item);
if (StringUtils.isNotBlank(entityType)) {
List<Relationship> relationships = relationshipService.findByItem(context, item);
for (Relationship relationship : relationships) {
@@ -61,7 +60,8 @@ public class RelationshipMetadataServiceImpl implements RelationshipMetadataServ
return fullMetadataValueList;
}
private String getEntityTypeStringFromMetadata(List<MetadataValue> list) {
public String getEntityTypeStringFromMetadata(Item item) {
List<MetadataValue> list = item.getMetadata();
for (MetadataValue mdv : list) {
if (StringUtils.equals(mdv.getMetadataField().getMetadataSchema().getName(),
"relationship")
@@ -74,22 +74,8 @@ public class RelationshipMetadataServiceImpl implements RelationshipMetadataServ
return null;
}
/**
* This method processes one Relationship of an Item and will return a list of RelationshipMetadataValue objects
* that are generated for this specific relationship for the item through the config in VirtualMetadataPopulator
*
* It returns a combination of the output of the findVirtualMetadataFromConfiguration method and
*
* @param context The context
* @param item The item whose virtual metadata is requested
* @param entityType The entity type of the given item
* @param relationship The relationship whose virtual metadata is requested
* @param enableVirtualMetadata Determines whether the VirtualMetadataPopulator should be used.
* If false, only the relation."relationname" metadata is populated
* If true, fields from the spring config virtual metadata is included as well
* @return The list of virtual metadata values
*/
private List<RelationshipMetadataValue> findRelationshipMetadataValueForItemRelationship(
@Override
public List<RelationshipMetadataValue> findRelationshipMetadataValueForItemRelationship(
Context context, Item item, String entityType, Relationship relationship, boolean enableVirtualMetadata)
throws SQLException {
List<RelationshipMetadataValue> resultingMetadataValueList = new LinkedList<>();

View File

@@ -43,6 +43,8 @@ public class RelationshipServiceImpl implements RelationshipService {
@Autowired(required = true)
protected RelationshipTypeService relationshipTypeService;
@Autowired
private RelationshipMetadataService relationshipMetadataService;
@Autowired
private VirtualMetadataPopulator virtualMetadataPopulator;
@@ -61,6 +63,8 @@ public class RelationshipServiceImpl implements RelationshipService {
return create(c, leftItem, rightItem, relationshipType, leftPlace, rightPlace, null, null);
}
@Override
public Relationship create(Context c, Item leftItem, Item rightItem, RelationshipType relationshipType,
int leftPlace, int rightPlace, String leftwardValue, String rightwardValue)
@@ -81,8 +85,12 @@ public class RelationshipServiceImpl implements RelationshipService {
if (isRelationshipValidToCreate(context, relationship)) {
if (authorizeService.authorizeActionBoolean(context, relationship.getLeftItem(), Constants.WRITE) ||
authorizeService.authorizeActionBoolean(context, relationship.getRightItem(), Constants.WRITE)) {
updatePlaceInRelationship(context, relationship, true);
return relationshipDAO.create(context, relationship);
// This order of execution should be handled in the creation (create, updateplace, update relationship)
// for a proper place allocation
Relationship relationshipToReturn = relationshipDAO.create(context, relationship);
updatePlaceInRelationship(context, relationshipToReturn);
update(context, relationshipToReturn);
return relationshipToReturn;
} else {
throw new AuthorizeException(
"You do not have write rights on this relationship's items");
@@ -94,18 +102,34 @@ public class RelationshipServiceImpl implements RelationshipService {
}
@Override
public void updatePlaceInRelationship(Context context, Relationship relationship, boolean isCreation)
public void updatePlaceInRelationship(Context context, Relationship relationship)
throws SQLException, AuthorizeException {
Item leftItem = relationship.getLeftItem();
// Max value is used to ensure that these will get added to the back of the list and thus receive the highest
// (last) place as it's set to a -1 for creation
if (relationship.getLeftPlace() == -1) {
relationship.setLeftPlace(Integer.MAX_VALUE);
}
Item rightItem = relationship.getRightItem();
if (relationship.getRightPlace() == -1) {
relationship.setRightPlace(Integer.MAX_VALUE);
}
List<Relationship> leftRelationships = findByItemAndRelationshipType(context,
leftItem,
relationship.getRelationshipType(), true);
Item rightItem = relationship.getRightItem();
List<Relationship> rightRelationships = findByItemAndRelationshipType(context,
rightItem,
relationship.getRelationshipType(),
false);
// These relationships are only deleted from the temporary lists incase they're present in them so that we can
// properly perform our place calculation later down the line in this method.
if (leftRelationships.contains(relationship)) {
leftRelationships.remove(relationship);
}
if (rightRelationships.contains(relationship)) {
rightRelationships.remove(relationship);
}
context.turnOffAuthorisationSystem();
//If useForPlace for the leftwardType is false for the relationshipType,
// we need to sort the relationships here based on leftplace.
@@ -141,10 +165,6 @@ public class RelationshipServiceImpl implements RelationshipService {
updateItem(context, rightItem);
}
if (isCreation) {
handleCreationPlaces(context, relationship);
}
context.restoreAuthSystemState();
}
@@ -156,43 +176,14 @@ public class RelationshipServiceImpl implements RelationshipService {
itemService.update(context, relatedItem);
}
//Sets the places for the Relationship properly if the updatePlaceInRelationship was called for a new creation
//of this Relationship
private void handleCreationPlaces(Context context, Relationship relationship) throws SQLException {
List<Relationship> leftRelationships;
List<Relationship> rightRelationships;
leftRelationships = findByItemAndRelationshipType(context,
relationship.getLeftItem(),
relationship.getRelationshipType(), true);
rightRelationships = findByItemAndRelationshipType(context,
relationship.getRightItem(),
relationship.getRelationshipType(),
false);
leftRelationships.sort((o1, o2) -> o2.getLeftPlace() - o1.getLeftPlace());
rightRelationships.sort((o1, o2) -> o2.getRightPlace() - o1.getRightPlace());
if (!leftRelationships.isEmpty()) {
relationship.setLeftPlace(leftRelationships.get(0).getLeftPlace() + 1);
} else {
relationship.setLeftPlace(0);
}
if (!rightRelationships.isEmpty()) {
relationship.setRightPlace(rightRelationships.get(0).getRightPlace() + 1);
} else {
relationship.setRightPlace(0);
}
@Override
public int findNextLeftPlaceByLeftItem(Context context, Item item) throws SQLException {
return relationshipDAO.findNextLeftPlaceByLeftItem(context, item);
}
@Override
public int findLeftPlaceByLeftItem(Context context, Item item) throws SQLException {
return relationshipDAO.findLeftPlaceByLeftItem(context, item);
}
@Override
public int findRightPlaceByRightItem(Context context, Item item) throws SQLException {
return relationshipDAO.findRightPlaceByRightItem(context, item);
public int findNextRightPlaceByRightItem(Context context, Item item) throws SQLException {
return relationshipDAO.findNextRightPlaceByRightItem(context, item);
}
private boolean isRelationshipValidToCreate(Context context, Relationship relationship) throws SQLException {
@@ -326,12 +317,25 @@ public class RelationshipServiceImpl implements RelationshipService {
@Override
public void delete(Context context, Relationship relationship) throws SQLException, AuthorizeException {
if (isRelationshipValidToDelete(context, relationship)) {
//TODO: retrieve default settings from configuration
delete(context, relationship, false, false);
}
@Override
public void delete(Context context, Relationship relationship, boolean copyToLeftItem, boolean copyToRightItem)
throws SQLException, AuthorizeException {
log.info(org.dspace.core.LogManager.getHeader(context, "delete_relationship",
"relationship_id=" + relationship.getID() + "&" +
"copyMetadataValuesToLeftItem=" + copyToLeftItem + "&" +
"copyMetadataValuesToRightItem=" + copyToRightItem));
if (isRelationshipValidToDelete(context, relationship) &&
copyToItemPermissionCheck(context, relationship, copyToLeftItem, copyToRightItem)) {
// To delete a relationship, a user must have WRITE permissions on one of the related Items
copyMetadataValues(context, relationship, copyToLeftItem, copyToRightItem);
if (authorizeService.authorizeActionBoolean(context, relationship.getLeftItem(), Constants.WRITE) ||
authorizeService.authorizeActionBoolean(context, relationship.getRightItem(), Constants.WRITE)) {
relationshipDAO.delete(context, relationship);
updatePlaceInRelationship(context, relationship, false);
updatePlaceInRelationship(context, relationship);
} else {
throw new AuthorizeException(
"You do not have write rights on this relationship's items");
@@ -342,6 +346,71 @@ public class RelationshipServiceImpl implements RelationshipService {
}
}
/**
* Converts virtual metadata from RelationshipMetadataValue objects to actual item metadata.
*
* @param context The relevant DSpace context
* @param relationship The relationship containing the left and right items
* @param copyToLeftItem The boolean indicating whether we want to write to left item or not
* @param copyToRightItem The boolean indicating whether we want to write to right item or not
*/
private void copyMetadataValues(Context context, Relationship relationship, boolean copyToLeftItem,
boolean copyToRightItem)
throws SQLException, AuthorizeException {
if (copyToLeftItem) {
String entityTypeString = relationshipMetadataService
.getEntityTypeStringFromMetadata(relationship.getLeftItem());
List<RelationshipMetadataValue> relationshipMetadataValues =
relationshipMetadataService.findRelationshipMetadataValueForItemRelationship(context,
relationship.getLeftItem(), entityTypeString, relationship, true);
for (RelationshipMetadataValue relationshipMetadataValue : relationshipMetadataValues) {
itemService.addMetadata(context, relationship.getLeftItem(),
relationshipMetadataValue.getMetadataField(), null,
relationshipMetadataValue.getValue() );
}
itemService.update(context, relationship.getLeftItem());
}
if (copyToRightItem) {
String entityTypeString = relationshipMetadataService
.getEntityTypeStringFromMetadata(relationship.getRightItem());
List<RelationshipMetadataValue> relationshipMetadataValues =
relationshipMetadataService.findRelationshipMetadataValueForItemRelationship(context,
relationship.getRightItem(), entityTypeString, relationship, true);
for (RelationshipMetadataValue relationshipMetadataValue : relationshipMetadataValues) {
itemService.addMetadata(context, relationship.getRightItem(),
relationshipMetadataValue.getMetadataField(), null,
relationshipMetadataValue.getValue() );
}
itemService.update(context, relationship.getRightItem());
}
}
/**
* This method will check if the current user has sufficient rights to write to the respective items if requested
* @param context The relevant DSpace context
* @param relationship The relationship containing the left and right items
* @param copyToLeftItem The boolean indicating whether we want to write to left item or not
* @param copyToRightItem The boolean indicating whether we want to write to right item or not
* @return A boolean indicating whether the permissions are okay for this request
* @throws AuthorizeException If something goes wrong
* @throws SQLException If something goes wrong
*/
private boolean copyToItemPermissionCheck(Context context, Relationship relationship,
boolean copyToLeftItem, boolean copyToRightItem) throws SQLException {
boolean isPermissionCorrect = true;
if (copyToLeftItem) {
if (!authorizeService.authorizeActionBoolean(context, relationship.getLeftItem(), Constants.WRITE)) {
isPermissionCorrect = false;
}
}
if (copyToRightItem) {
if (!authorizeService.authorizeActionBoolean(context, relationship.getRightItem(), Constants.WRITE)) {
isPermissionCorrect = false;
}
}
return isPermissionCorrect;
}
private boolean isRelationshipValidToDelete(Context context, Relationship relationship) throws SQLException {
if (relationship == null) {
log.warn("The relationship has been deemed invalid since the relation was null");

View File

@@ -1,59 +0,0 @@
/**
* 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.authority;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.message.BasicNameValuePair;
import org.dspace.content.Collection;
/**
* Sample Journal-name authority based on SHERPA/RoMEO
*
* WARNING: This is a very crude and incomplete implementation, done mainly
* as a proof-of-concept. Any site that actually wants to use it will
* probably have to refine it (and give patches back to dspace.org).
*
* @author Larry Stone
* @version $Revision $
* @see SHERPARoMEOProtocol
*/
public class SHERPARoMEOJournalTitle extends SHERPARoMEOProtocol {
private static final String RESULT = "journal";
private static final String LABEL = "jtitle";
private static final String AUTHORITY = "issn";
public SHERPARoMEOJournalTitle() {
super();
}
@Override
public Choices getMatches(String text, Collection collection, int start, int limit, String locale) {
// punt if there is no query text
if (text == null || text.trim().length() == 0) {
return new Choices(true);
}
// query args to add to SHERPA/RoMEO request URL
List<BasicNameValuePair> args = new ArrayList<BasicNameValuePair>();
args.add(new BasicNameValuePair("jtitle", text));
args.add(new BasicNameValuePair("qtype", "contains")); // OR: starts, exact
Choices result = query(RESULT, LABEL, AUTHORITY, args, start, limit);
if (result == null) {
result = new Choices(true);
}
return result;
}
@Override
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale) {
return getMatches(text, collection, start, limit, locale);
}
}

View File

@@ -1,228 +0,0 @@
/**
* 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.authority;
import java.io.IOException;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.Logger;
import org.dspace.content.Collection;
import org.dspace.core.ConfigurationManager;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/**
* Choice Authority based on SHERPA/RoMEO - for Publishers and Journals
* See the subclasses SHERPARoMEOPublisher and SHERPARoMEOJournalTitle
* for actual choice plugin implementations. This is a superclass
* containing all the common protocol logic.
*
* Reads these DSpace Config properties:
*
* # contact URL for server
* sherpa.romeo.url = http://www.sherpa.ac.uk/romeoapi11.php
*
* WARNING: This is a very crude and incomplete implementation, done mainly
* as a proof-of-concept. Any site that actually wants to use it will
* probably have to refine it (and give patches back to dspace.org).
*
* @author Larry Stone
* @version $Revision $
* @see SHERPARoMEOPublisher
* @see SHERPARoMEOJournalTitle
*/
public abstract class SHERPARoMEOProtocol implements ChoiceAuthority {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SHERPARoMEOProtocol.class);
// contact URL from configuration
private static String url = null;
public SHERPARoMEOProtocol() {
if (url == null) {
url = ConfigurationManager.getProperty("sherpa.romeo.url");
// sanity check
if (url == null) {
throw new IllegalStateException("Missing DSpace configuration keys for SHERPA/RoMEO Query");
}
}
}
// this implements the specific RoMEO API args and XML tag naming
public abstract Choices getMatches(String text, Collection collection, int start, int limit, String locale);
@Override
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
return getMatches(field, text, collection, 0, 2, locale);
}
// XXX FIXME just punt, returning value, never got around to
// implementing a reverse query.
@Override
public String getLabel(String field, String key, String locale) {
return key;
}
// NOTE - ignore limit and start for now
protected Choices query(String result, String label, String authority,
List<BasicNameValuePair> args, int start, int limit) {
HttpClient hc = new DefaultHttpClient();
String srUrl = url + "?" + URLEncodedUtils.format(args, "UTF8");
HttpGet get = new HttpGet(srUrl);
log.debug("Trying SHERPA/RoMEO Query, URL=" + srUrl);
try {
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
SRHandler handler = new SRHandler(result, label, authority);
// XXX FIXME: should turn off validation here explicitly, but
// it seems to be off by default.
xr.setFeature("http://xml.org/sax/features/namespaces", true);
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
xr.parse(new InputSource(response.getEntity().getContent()));
int confidence;
if (handler.total == 0) {
confidence = Choices.CF_NOTFOUND;
} else if (handler.total == 1) {
confidence = Choices.CF_UNCERTAIN;
} else {
confidence = Choices.CF_AMBIGUOUS;
}
return new Choices(handler.result, start, handler.total, confidence, false);
}
} catch (IOException e) {
log.error("SHERPA/RoMEO query failed: ", e);
return null;
} catch (ParserConfigurationException e) {
log.warn("Failed parsing SHERPA/RoMEO result: ", e);
return null;
} catch (SAXException e) {
log.warn("Failed parsing SHERPA/RoMEO result: ", e);
return null;
} finally {
get.releaseConnection();
}
return null;
}
// SAX handler to grab SHERPA/RoMEO (and eventually other details) from result
private static class SRHandler
extends DefaultHandler {
private Choice result[] = null;
int rindex = 0; // result index
int total = 0;
// name of element containing a result, e.g. <journal>
private String resultElement = null;
// name of element containing the label e.g. <name>
private String labelElement = null;
// name of element containing the authority value e.g. <issn>
private String authorityElement = null;
protected String textValue = null;
public SRHandler(String result, String label, String authority) {
super();
resultElement = result;
labelElement = label;
authorityElement = authority;
}
// NOTE: text value MAY be presented in multiple calls, even if
// it all one word, so be ready to splice it together.
// BEWARE: subclass's startElement method should call super()
// to null out 'value'. (Don't you miss the method combination
// options of a real object system like CLOS?)
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
String newValue = new String(ch, start, length);
if (newValue.length() > 0) {
if (textValue == null) {
textValue = newValue;
} else {
textValue += newValue;
}
}
}
// if this was the FIRST "numhits" element, it's size of results:
@Override
public void endElement(String namespaceURI, String localName,
String qName)
throws SAXException {
if (localName.equals("numhits")) {
String stotal = textValue.trim();
if (stotal.length() > 0) {
total = Integer.parseInt(stotal);
result = new Choice[total];
if (total > 0) {
result[0] = new Choice();
log.debug("Got " + total + " records in results.");
}
}
} else if (localName.equals(resultElement)) {
// after start of result element, get next hit ready
if (++rindex < result.length) {
result[rindex] = new Choice();
}
} else if (localName.equals(labelElement) && textValue != null) {
// plug in label value
result[rindex].value = textValue.trim();
result[rindex].label = result[rindex].value;
} else if (authorityElement != null && localName.equals(authorityElement) && textValue != null) {
// plug in authority value
result[rindex].authority = textValue.trim();
} else if (localName.equals("message") && textValue != null) {
// error message
log.warn("SHERPA/RoMEO response error message: " + textValue.trim());
}
}
// subclass overriding this MUST call it with super()
@Override
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts)
throws SAXException {
textValue = null;
}
@Override
public void error(SAXParseException exception)
throws SAXException {
throw new SAXException(exception);
}
@Override
public void fatalError(SAXParseException exception)
throws SAXException {
throw new SAXException(exception);
}
}
}

View File

@@ -1,61 +0,0 @@
/**
* 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.authority;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.message.BasicNameValuePair;
import org.dspace.content.Collection;
/**
* Sample Publisher name authority based on SHERPA/RoMEO
*
*
* WARNING: This is a very crude and incomplete implementation, done mainly
* as a proof-of-concept. Any site that actually wants to use it will
* probably have to refine it (and give patches back to dspace.org).
*
* @author Larry Stone
* @version $Revision $
* @see SHERPARoMEOProtocol
*/
public class SHERPARoMEOPublisher extends SHERPARoMEOProtocol {
protected static final String RESULT = "publisher";
protected static final String LABEL = "name";
// note: the publisher records have nothing we can use as authority code.
protected static final String AUTHORITY = null;
public SHERPARoMEOPublisher() {
super();
}
@Override
public Choices getMatches(String text, Collection collection, int start, int limit, String locale) {
// punt if there is no query text
if (text == null || text.trim().length() == 0) {
return new Choices(true);
}
// query args to add to SHERPA/RoMEO request URL
List<BasicNameValuePair> args = new ArrayList<BasicNameValuePair>();
args.add(new BasicNameValuePair("pub", text));
args.add(new BasicNameValuePair("qtype", "all")); // OR: starts, exact
Choices result = query(RESULT, LABEL, AUTHORITY, args, start, limit);
if (result == null) {
result = new Choices(true);
}
return result;
}
@Override
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale) {
return getMatches(text, collection, start, limit, locale);
}
}

View File

@@ -22,6 +22,7 @@ import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.Site;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.packager.DSpaceAIPIngester;
import org.dspace.content.packager.METSManifest;
@@ -195,7 +196,7 @@ public class AIPTechMDCrosswalk implements IngestionCrosswalk, DisseminationCros
public Element disseminateElement(Context context, DSpaceObject dso)
throws CrosswalkException, IOException, SQLException,
AuthorizeException {
List<MockMetadataValue> dc = new ArrayList<>();
List<MetadataValueDTO> dc = new ArrayList<>();
if (dso.getType() == Constants.ITEM) {
Item item = (Item) dso;
EPerson is = item.getSubmitter();
@@ -282,8 +283,8 @@ public class AIPTechMDCrosswalk implements IngestionCrosswalk, DisseminationCros
return XSLTDisseminationCrosswalk.createDIM(dso, dc);
}
private static MockMetadataValue makeDC(String element, String qualifier, String value) {
MockMetadataValue dcv = new MockMetadataValue();
private static MetadataValueDTO makeDC(String element, String qualifier, String value) {
MetadataValueDTO dcv = new MetadataValueDTO();
dcv.setSchema("dc");
dcv.setLanguage(null);
dcv.setElement(element);

View File

@@ -29,6 +29,7 @@ import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.Site;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
@@ -327,7 +328,7 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin
private List<Element> disseminateListInternal(DSpaceObject dso, boolean addSchema)
throws CrosswalkException, IOException, SQLException, AuthorizeException {
List<MockMetadataValue> dcvs = null;
List<MetadataValueDTO> dcvs = null;
if (dso.getType() == Constants.ITEM) {
dcvs = item2Metadata((Item) dso);
} else if (dso.getType() == Constants.COLLECTION) {
@@ -344,7 +345,7 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin
List<Element> result = new ArrayList<Element>(dcvs.size());
for (MockMetadataValue dcv : dcvs) {
for (MetadataValueDTO dcv : dcvs) {
String qdc = dcv.getSchema() + "." + dcv.getElement();
if (dcv.getQualifier() != null) {
qdc += "." + dcv.getQualifier();
@@ -418,8 +419,8 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin
* @param site The site to derive metadata from
* @return list of metadata
*/
protected List<MockMetadataValue> site2Metadata(Site site) {
List<MockMetadataValue> metadata = new ArrayList<>();
protected List<MetadataValueDTO> site2Metadata(Site site) {
List<MetadataValueDTO> metadata = new ArrayList<>();
String identifier_uri = handleService.getCanonicalPrefix()
+ site.getHandle();
@@ -449,8 +450,8 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin
* @param community The community to derive metadata from
* @return list of metadata
*/
protected List<MockMetadataValue> community2Metadata(Community community) {
List<MockMetadataValue> metadata = new ArrayList<>();
protected List<MetadataValueDTO> community2Metadata(Community community) {
List<MetadataValueDTO> metadata = new ArrayList<>();
String description = communityService.getMetadata(community, "introductory_text");
String description_abstract = communityService.getMetadata(community, "short_description");
@@ -492,8 +493,8 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin
* @param collection The collection to derive metadata from
* @return list of metadata
*/
protected List<MockMetadataValue> collection2Metadata(Collection collection) {
List<MockMetadataValue> metadata = new ArrayList<>();
protected List<MetadataValueDTO> collection2Metadata(Collection collection) {
List<MetadataValueDTO> metadata = new ArrayList<>();
String description = collectionService.getMetadata(collection, "introductory_text");
String description_abstract = collectionService.getMetadata(collection, "short_description");
@@ -546,19 +547,19 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin
* @param item The item to derive metadata from
* @return list of metadata
*/
protected List<MockMetadataValue> item2Metadata(Item item) {
protected List<MetadataValueDTO> item2Metadata(Item item) {
List<MetadataValue> dcvs = itemService.getMetadata(item, Item.ANY, Item.ANY, Item.ANY,
Item.ANY);
List<MockMetadataValue> result = new ArrayList<>();
List<MetadataValueDTO> result = new ArrayList<>();
for (MetadataValue metadataValue : dcvs) {
result.add(new MockMetadataValue(metadataValue));
result.add(new MetadataValueDTO(metadataValue));
}
return result;
}
protected MockMetadataValue createDCValue(String element, String qualifier, String value) {
MockMetadataValue dcv = new MockMetadataValue();
protected MetadataValueDTO createDCValue(String element, String qualifier, String value) {
MetadataValueDTO dcv = new MetadataValueDTO();
dcv.setSchema("dc");
dcv.setElement(element);
dcv.setQualifier(qualifier);

View File

@@ -1,101 +0,0 @@
/**
* 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.crosswalk;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
import org.dspace.content.MetadataValue;
/**
* Metadata Value is bound to a database, the dissemination crosswalk require mock metadata just need for desimanation
* This class provides a wrapper for this.
* This class should only be used for the dissemniation metadata values that aren't to be written to the database
*
* @author kevinvandevelde at atmire.com
*/
public class MockMetadataValue {
private String schema;
private String element;
private String qualifier;
private String language;
private String value;
private String authority;
private int confidence;
public MockMetadataValue(MetadataValue metadataValue) {
MetadataField metadataField = metadataValue.getMetadataField();
MetadataSchema metadataSchema = metadataField.getMetadataSchema();
schema = metadataSchema.getName();
element = metadataField.getElement();
qualifier = metadataField.getQualifier();
language = metadataValue.getLanguage();
value = metadataValue.getValue();
authority = metadataValue.getAuthority();
confidence = metadataValue.getConfidence();
}
public MockMetadataValue() {
}
public String getSchema() {
return schema;
}
public String getElement() {
return element;
}
public String getQualifier() {
return qualifier;
}
public String getValue() {
return value;
}
public void setSchema(String schema) {
this.schema = schema;
}
public void setElement(String element) {
this.element = element;
}
public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}
public void setValue(String value) {
this.value = value;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
public int getConfidence() {
return confidence;
}
public void setConfidence(int confidence) {
this.confidence = confidence;
}
}

View File

@@ -31,6 +31,7 @@ import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.Site;
import org.dspace.content.authority.Choices;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
@@ -307,13 +308,13 @@ public class XSLTDisseminationCrosswalk
* @param dcvs list of metadata
* @return element
*/
public static Element createDIM(DSpaceObject dso, List<MockMetadataValue> dcvs) {
public static Element createDIM(DSpaceObject dso, List<MetadataValueDTO> dcvs) {
Element dim = new Element("dim", DIM_NS);
String type = Constants.typeText[dso.getType()];
dim.setAttribute("dspaceType", type);
for (int i = 0; i < dcvs.size(); i++) {
MockMetadataValue dcv = dcvs.get(i);
MetadataValueDTO dcv = dcvs.get(i);
Element field =
createField(dcv.getSchema(), dcv.getElement(), dcv.getQualifier(),
dcv.getLanguage(), dcv.getValue(), dcv.getAuthority(), dcv.getConfidence());
@@ -390,12 +391,12 @@ public class XSLTDisseminationCrosswalk
}
}
protected static List<MockMetadataValue> item2Metadata(Item item) {
protected static List<MetadataValueDTO> item2Metadata(Item item) {
List<MetadataValue> dcvs = itemService.getMetadata(item, Item.ANY, Item.ANY, Item.ANY,
Item.ANY);
List<MockMetadataValue> result = new ArrayList<>();
List<MetadataValueDTO> result = new ArrayList<>();
for (MetadataValue metadataValue : dcvs) {
result.add(new MockMetadataValue(metadataValue));
result.add(new MetadataValueDTO(metadataValue));
}
return result;

View File

@@ -0,0 +1,57 @@
/**
* 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.dao;
import java.sql.SQLException;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.core.GenericDAO;
import org.dspace.scripts.Process;
/**
* This is the Data Access Object for the {@link Process} object
*/
public interface ProcessDAO extends GenericDAO<Process> {
/**
* This method will return all the Process objects in the database in a list and it'll be sorted by script name
* @param context The relevant DSpace context
* @return The list of all Process objects in the database sorted on scriptname
* @throws SQLException If something goes wrong
*/
public List<Process> findAllSortByScript(Context context) throws SQLException;
/**
* This method will return all the Process objects in the database in a list and it'll be sorted by start time.
* The most recent one will be shown first
* @param context The relevant DSpace context
* @return The list of all Process objects in the database sorted by starttime
* @throws SQLException If something goes wrong
*/
public List<Process> findAllSortByStartTime(Context context) throws SQLException;
/**
* Returns a list of all Process objects in the database
* @param context The relevant DSpace context
* @param limit The limit for the amount of Processes returned
* @param offset The offset for the Processes to be returned
* @return The list of all Process objects in the Database
* @throws SQLException If something goes wrong
*/
List<Process> findAll(Context context, int limit, int offset) throws SQLException;
/**
* Returns the total amount of Process objects in the dataase
* @param context The relevant DSpace context
* @return An integer that describes the amount of Process objects in the database
* @throws SQLException If something goes wrong
*/
int countRows(Context context) throws SQLException;
}

View File

@@ -51,26 +51,26 @@ public interface RelationshipDAO extends GenericDAO<Relationship> {
List<Relationship> findByItem(Context context, Item item, Integer limit, Integer offset) throws SQLException;
/**
* This method returns the highest leftplace integer for all the relationships where this
* item is the leftitem so that we can set a proper leftplace attribute on the next relationship
* This method returns the next leftplace integer to use for a relationship with this item as the leftItem
*
* @param context The relevant DSpace context
* @param item The item to be matched on leftItem
* @return The integer for the highest leftPlace value for all the relatonship objects
* that have the given item as leftItem
* @return The next integer to be used for the leftplace of a relationship with the given item
* as a left item
* @throws SQLException If something goes wrong
*/
int findLeftPlaceByLeftItem(Context context, Item item) throws SQLException;
int findNextLeftPlaceByLeftItem(Context context, Item item) throws SQLException;
/**
* This method returns the highest rightplace integer for all the relationships where this
* item is the rightitem so that we can set a proper rightplace attribute on the next relationship
* This method returns the next rightplace integer to use for a relationship with this item as the rightItem
*
* @param context The relevant DSpace context
* @param item The item to be matched on rightItem
* @return The integer for the highest rightPlace value for all the relatonship objects
* that have the given item as rightItem
* @return The next integer to be used for the rightplace of a relationship with the given item
* as a right item
* @throws SQLException If something goes wrong
*/
int findRightPlaceByRightItem(Context context, Item item) throws SQLException;
int findNextRightPlaceByRightItem(Context context, Item item) throws SQLException;
/**
* This method returns a list of Relationship objects for the given RelationshipType object.
@@ -113,7 +113,7 @@ public interface RelationshipDAO extends GenericDAO<Relationship> {
* @throws SQLException If something goes wrong
*/
List<Relationship> findByItemAndRelationshipType(Context context, Item item, RelationshipType relationshipType,
Integer limit, Integer offset) throws SQLException;
Integer limit, Integer offset) throws SQLException;
/**
* This method returns a list of Relationship objects for the given RelationshipType object.
@@ -157,7 +157,6 @@ public interface RelationshipDAO extends GenericDAO<Relationship> {
List<Relationship> findByTypeName(Context context, String typeName, Integer limit, Integer offset)
throws SQLException;
/**
* Count total number of relationships (rows in relationship table)
*

View File

@@ -9,7 +9,6 @@ package org.dspace.content.dao.impl;
import java.sql.SQLException;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -64,11 +63,20 @@ public class CollectionDAOImpl extends AbstractHibernateDSODAO<Collection> imple
public List<Collection> findAll(Context context, MetadataField order, Integer limit, Integer offset)
throws SQLException {
StringBuilder query = new StringBuilder();
query.append("SELECT ").append(Collection.class.getSimpleName()).append(" FROM Collection as ")
.append(Collection.class.getSimpleName()).append(" ");
addMetadataLeftJoin(query, Collection.class.getSimpleName(), Arrays.asList(order));
addMetadataSortQuery(query, Arrays.asList(order), null);
// The query has to be rather complex because we want to sort the retrieval of Collections based on the title
// We'll join the Collections with the metadata fields on the sortfield specified in the parameters
// then we'll sort on this metadata field (this is usually the title). We're also making sure that the place
// is the lowest place in the metadata fields list so that we avoid the duplication bug
query.append("SELECT c" +
" FROM Collection c" +
" left join c.metadata title on title.metadataField = :sortField and" +
" title.dSpaceObject = c.id and" +
" title.place = (select min(internal.place) " +
"from c.metadata internal " +
"where internal.metadataField = :sortField and" +
" internal.dSpaceObject = c.id)" +
" ORDER BY LOWER(title.value)");
Query hibernateQuery = createQuery(context, query.toString());
if (offset != null) {
hibernateQuery.setFirstResult(offset);
@@ -76,7 +84,7 @@ public class CollectionDAOImpl extends AbstractHibernateDSODAO<Collection> imple
if (limit != null) {
hibernateQuery.setMaxResults(limit);
}
hibernateQuery.setParameter(order.toString(), order.getID());
hibernateQuery.setParameter("sortField", order);
return list(hibernateQuery);
}

View File

@@ -0,0 +1,75 @@
/**
* 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.dao.impl;
import java.sql.SQLException;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.dspace.content.dao.ProcessDAO;
import org.dspace.core.AbstractHibernateDAO;
import org.dspace.core.Context;
import org.dspace.scripts.Process;
import org.dspace.scripts.Process_;
/**
* Implementation class for {@link ProcessDAO}
*/
public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements ProcessDAO {
@Override
public List<Process> findAllSortByScript(Context context) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
criteriaQuery.orderBy(criteriaBuilder.asc(processRoot.get(Process_.name)));
return list(context, criteriaQuery, false, Process.class, -1, -1);
}
@Override
public List<Process> findAllSortByStartTime(Context context) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
criteriaQuery.orderBy(criteriaBuilder.desc(processRoot.get(Process_.startTime)),
criteriaBuilder.desc(processRoot.get(Process_.processId)));
return list(context, criteriaQuery, false, Process.class, -1, -1);
}
@Override
public List<Process> findAll(Context context, int limit, int offset) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
return list(context, criteriaQuery, false, Process.class, limit, offset);
}
@Override
public int countRows(Context context) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
return count(context, criteriaQuery, criteriaBuilder, processRoot);
}
}

View File

@@ -61,7 +61,7 @@ public class RelationshipDAOImpl extends AbstractHibernateDAO<Relationship> impl
}
@Override
public int findLeftPlaceByLeftItem(Context context, Item item) throws SQLException {
public int findNextLeftPlaceByLeftItem(Context context, Item item) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Relationship.class);
Root<Relationship> relationshipRoot = criteriaQuery.from(Relationship.class);
@@ -70,25 +70,25 @@ public class RelationshipDAOImpl extends AbstractHibernateDAO<Relationship> impl
List<Relationship> list = list(context, criteriaQuery, false, Relationship.class, -1, -1);
list.sort((o1, o2) -> o2.getLeftPlace() - o1.getLeftPlace());
if (!list.isEmpty()) {
return list.get(0).getLeftPlace();
return list.get(0).getLeftPlace() + 1;
} else {
return 1;
return 0;
}
}
@Override
public int findRightPlaceByRightItem(Context context, Item item) throws SQLException {
public int findNextRightPlaceByRightItem(Context context, Item item) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Relationship.class);
Root<Relationship> relationshipRoot = criteriaQuery.from(Relationship.class);
criteriaQuery.select(relationshipRoot);
criteriaQuery.where(criteriaBuilder.equal(relationshipRoot.get(Relationship_.rightItem), item));
List<Relationship> list = list(context, criteriaQuery, false, Relationship.class, -1, -1);
list.sort((o1, o2) -> o2.getLeftPlace() - o1.getLeftPlace());
list.sort((o1, o2) -> o2.getRightPlace() - o1.getRightPlace());
if (!list.isEmpty()) {
return list.get(0).getLeftPlace();
return list.get(0).getRightPlace() + 1;
} else {
return 1;
return 0;
}
}

View File

@@ -0,0 +1,139 @@
/**
* 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.dto;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
import org.dspace.content.MetadataValue;
import org.dspace.content.authority.Choices;
/**
* This class acts as Data transfer object in which we can store data like in a regular MetadataValue object, but this
* one isn't saved in the DB. This can freely be used to represent Metadata without it being saved in the database,
* this will typically be used when transferring data
*
* @author kevinvandevelde at atmire.com
*/
public class MetadataValueDTO {
private String schema;
private String element;
private String qualifier;
private String language;
private String value;
private String authority;
private int confidence = Choices.CF_UNSET;
public MetadataValueDTO(MetadataValue metadataValue) {
MetadataField metadataField = metadataValue.getMetadataField();
MetadataSchema metadataSchema = metadataField.getMetadataSchema();
schema = metadataSchema.getName();
element = metadataField.getElement();
qualifier = metadataField.getQualifier();
language = metadataValue.getLanguage();
value = metadataValue.getValue();
authority = metadataValue.getAuthority();
confidence = metadataValue.getConfidence();
}
public MetadataValueDTO() {
}
/**
* Constructor for the MetadataValueDTO class
* @param schema The schema to be assigned to this MetadataValueDTO object
* @param element The element to be assigned to this MetadataValueDTO object
* @param qualifier The qualifier to be assigned to this MetadataValueDTO object
* @param language The language to be assigend to this MetadataValueDTO object
* @param value The value to be assigned to this MetadataValueDTO object
* @param authority The authority to be assigned to this MetadataValueDTO object
* @param confidence The confidence to be assigned to this MetadataValueDTO object
*/
public MetadataValueDTO(String schema, String element, String qualifier, String language, String value,
String authority, int confidence) {
this.schema = schema;
this.element = element;
this.qualifier = qualifier;
this.language = language;
this.value = value;
this.authority = authority;
this.confidence = confidence;
}
/**
* Constructor for the MetadataValueDTO class
* @param schema The schema to be assigned to this MetadataValueDTO object
* @param element The element to be assigned to this MetadataValueDTO object
* @param qualifier The qualifier to be assigned to this MetadataValueDTO object
* @param language The language to be assigend to this MetadataValueDTO object
* @param value The value to be assigned to this MetadataValueDTO object
*/
public MetadataValueDTO(String schema, String element, String qualifier, String language, String value) {
this.schema = schema;
this.element = element;
this.qualifier = qualifier;
this.language = language;
this.value = value;
}
public String getSchema() {
return schema;
}
public String getElement() {
return element;
}
public String getQualifier() {
return qualifier;
}
public String getValue() {
return value;
}
public void setSchema(String schema) {
this.schema = schema;
}
public void setElement(String element) {
this.element = element;
}
public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}
public void setValue(String value) {
this.value = value;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
public int getConfidence() {
return confidence;
}
public void setConfidence(int confidence) {
this.confidence = confidence;
}
}

View File

@@ -109,6 +109,31 @@ public interface BundleService extends DSpaceObjectService<Bundle>, DSpaceObject
public List<ResourcePolicy> getBundlePolicies(Context context, Bundle bundle) throws SQLException;
/**
* Moves a bitstream within a bundle from one place to another, shifting all other bitstreams in the process
*
* @param context DSpace Context
* @param bundle The bitstream bundle
* @param from The index of the bitstream to move
* @param to The index to move the bitstream to
* @throws AuthorizeException when an SQL error has occurred (querying DSpace)
* @throws SQLException If the user can't make the changes
*/
public void updateBitstreamOrder(Context context, Bundle bundle, int from, int to) throws AuthorizeException,
SQLException;
/**
* Moves a bitstream from its current bundle to a new target bundle
* @param context DSpace Context
* @param targetBundle The target bundle where bitstream will be moved to
* @param bitstream The bitstream being moved
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
* @throws IOException if IO error
*/
public void moveBitstreamToBundle(Context context, Bundle targetBundle, Bitstream bitstream) throws SQLException,
AuthorizeException, IOException;
/**
* Changes bitstream order according to the array
*

View File

@@ -74,26 +74,26 @@ public interface RelationshipService extends DSpaceCRUDService<Relationship> {
public Relationship create(Context context, Relationship relationship) throws SQLException, AuthorizeException;
/**
* Retrieves the highest integer value for the leftplace property of a Relationship for all relationships
* that have the given item as a left item
* This method returns the next leftplace integer to use for a relationship with this item as the leftItem
*
* @param context The relevant DSpace context
* @param item The item that has to be the leftItem of a relationship for it to qualify
* @return The integer value of the highest left place property of all relationships
* that have the given item as a leftitem property
* @return The next integer to be used for the leftplace of a relationship with the given item
* as a left item
* @throws SQLException If something goes wrong
*/
int findLeftPlaceByLeftItem(Context context, Item item) throws SQLException;
int findNextLeftPlaceByLeftItem(Context context, Item item) throws SQLException;
/**
* Retrieves the highest integer value for the rightplace property of a Relationship for all relationships
* that have the given item as a right item
* This method returns the next rightplace integer to use for a relationship with this item as the rightItem
*
* @param context The relevant DSpace context
* @param item The item that has to be the rightitem of a relationship for it to qualify
* @return The integer value of the highest right place property of all relationships
* that have the given item as a rightitem property
* @return The next integer to be used for the rightplace of a relationship with the given item
* as a right item
* @throws SQLException If something goes wrong
*/
int findRightPlaceByRightItem(Context context, Item item) throws SQLException;
int findNextRightPlaceByRightItem(Context context, Item item) throws SQLException;
/**
* This method returns a list of Relationships for which the leftItem or rightItem is equal to the given
@@ -147,10 +147,9 @@ public interface RelationshipService extends DSpaceCRUDService<Relationship> {
* @param context The relevant DSpace context
* @param relationship The Relationship object that will have it's place updated and that will be used
* to retrieve the other relationships whose place might need to be updated
* @param isCreation Is the relationship new or did it already exist
* @throws SQLException If something goes wrong
*/
public void updatePlaceInRelationship(Context context, Relationship relationship, boolean isCreation)
public void updatePlaceInRelationship(Context context, Relationship relationship)
throws SQLException, AuthorizeException;
/**
@@ -305,4 +304,15 @@ public interface RelationshipService extends DSpaceCRUDService<Relationship> {
*/
int countByTypeName(Context context, String typeName)
throws SQLException;
/**
* This method is used to delete a Relationship whilst given the possibility to copy the Virtual Metadata created
* by this relationship to the left and/or right item
* @param context The relevant DSpace context
* @param relationship The relationship to be deleted
* @param copyToLeftItem A boolean indicating whether we should copy metadata to the left item or not
* @param copyToRightItem A boolean indicating whether we should copy metadata to the right item or not
*/
void delete(Context context, Relationship relationship, boolean copyToLeftItem, boolean copyToRightItem)
throws SQLException, AuthorizeException;
}

View File

@@ -13,11 +13,8 @@ import java.util.Iterator;
import java.util.UUID;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.logging.log4j.Logger;
import org.apache.commons.cli.ParseException;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
@@ -26,103 +23,26 @@ import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.scripts.DSpaceRunnable;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Class used to reindex dspace communities/collections/items into discovery
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IndexClient {
public class IndexClient extends DSpaceRunnable {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(IndexClient.class);
private Context context;
/**
* Default constructor
*/
private IndexClient() { }
@Autowired
private IndexingService indexer;
/**
* When invoked as a command-line tool, creates, updates, removes content
* from the whole index
*
* @param args the command-line arguments, none used
* @throws SQLException An exception that provides information on a database access error or other errors.
* @throws IOException A general class of exceptions produced by failed or interrupted I/O operations.
* @throws SearchServiceException if something went wrong with querying the solr server
*/
public static void main(String[] args) throws SQLException, IOException, SearchServiceException {
private IndexClientOptions indexClientOptions;
Context context = new Context(Context.Mode.READ_ONLY);
context.turnOffAuthorisationSystem();
String usage = "org.dspace.discovery.IndexClient [-cbhf] | [-r <handle>] | [-i <handle>] or nothing to " +
"update/clean an existing index.";
Options options = new Options();
HelpFormatter formatter = new HelpFormatter();
CommandLine line = null;
options.addOption(OptionBuilder
.withArgName("handle to remove")
.hasArg(true)
.withDescription(
"remove an Item, Collection or Community from index based on its handle")
.create("r"));
options.addOption(OptionBuilder
.withArgName("handle or uuid to add or update")
.hasArg(true)
.withDescription(
"add or update an Item, Collection or Community based on its handle or uuid")
.create("i"));
options.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"clean existing index removing any documents that no longer exist in the db")
.create("c"));
options.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"(re)build index, wiping out current one if it exists")
.create("b"));
options.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"Rebuild the spellchecker, can be combined with -b and -f.")
.create("s"));
options.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"if updating existing index, force each handle to be reindexed even if uptodate")
.create("f"));
options.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"print this help message")
.create("h"));
options.addOption(OptionBuilder.isRequired(false).withDescription(
"optimize search core").create("o"));
try {
line = new PosixParser().parse(options, args);
} catch (Exception e) {
// automatically generate the help statement
formatter.printHelp(usage, e.getMessage(), options, "");
System.exit(1);
}
if (line.hasOption("h")) {
// automatically generate the help statement
formatter.printHelp(usage, options);
System.exit(1);
@Override
public void internalRun() throws Exception {
if (indexClientOptions == IndexClientOptions.HELP) {
printHelp();
return;
}
/** Acquire from dspace-services in future */
@@ -130,28 +50,29 @@ public class IndexClient {
* new DSpace.getServiceManager().getServiceByName("org.dspace.discovery.SolrIndexer");
*/
IndexingService indexer = DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(
IndexingService.class.getName(),
IndexingService.class
);
if (line.hasOption("r")) {
log.info("Removing " + line.getOptionValue("r") + " from Index");
indexer.unIndexContent(context, line.getOptionValue("r"));
} else if (line.hasOption("c")) {
log.info("Cleaning Index");
indexer.cleanIndex(line.hasOption("f"));
} else if (line.hasOption("b")) {
log.info("(Re)building index from scratch.");
if (indexClientOptions == IndexClientOptions.REMOVE) {
handler.logInfo("Removing " + commandLine.getOptionValue("r") + " from Index");
indexer.unIndexContent(context, commandLine.getOptionValue("r"));
} else if (indexClientOptions == IndexClientOptions.CLEAN) {
handler.logInfo("Cleaning Index");
indexer.cleanIndex(false);
} else if (indexClientOptions == IndexClientOptions.FORCECLEAN) {
handler.logInfo("Cleaning Index");
indexer.cleanIndex(true);
} else if (indexClientOptions == IndexClientOptions.BUILD ||
indexClientOptions == IndexClientOptions.BUILDANDSPELLCHECK) {
handler.logInfo("(Re)building index from scratch.");
indexer.createIndex(context);
checkRebuildSpellCheck(line, indexer);
} else if (line.hasOption("o")) {
log.info("Optimizing search core.");
if (indexClientOptions == IndexClientOptions.BUILDANDSPELLCHECK) {
checkRebuildSpellCheck(commandLine, indexer);
}
} else if (indexClientOptions == IndexClientOptions.OPTIMIZE) {
handler.logInfo("Optimizing search core.");
indexer.optimize();
} else if (line.hasOption('s')) {
checkRebuildSpellCheck(line, indexer);
} else if (line.hasOption('i')) {
final String param = line.getOptionValue('i');
} else if (indexClientOptions == IndexClientOptions.SPELLCHECK) {
checkRebuildSpellCheck(commandLine, indexer);
} else if (indexClientOptions == IndexClientOptions.INDEX) {
final String param = commandLine.getOptionValue('i');
UUID uuid = null;
try {
uuid = UUID.fromString(param);
@@ -171,24 +92,55 @@ public class IndexClient {
}
} else {
dso = (IndexableObject) HandleServiceFactory.getInstance()
.getHandleService().resolveToObject(context, param);
.getHandleService().resolveToObject(context, param);
}
if (dso == null) {
throw new IllegalArgumentException("Cannot resolve " + param + " to a DSpace object");
}
log.info("Indexing " + param + " force " + line.hasOption("f"));
handler.logInfo("Indexing " + param + " force " + commandLine.hasOption("f"));
final long startTimeMillis = System.currentTimeMillis();
final long count = indexAll(indexer, ContentServiceFactory.getInstance().getItemService(), context, dso);
final long count = indexAll(indexer, ContentServiceFactory.getInstance().getItemService(), context,
dso);
final long seconds = (System.currentTimeMillis() - startTimeMillis) / 1000;
log.info("Indexed " + count + " object" + (count > 1 ? "s" : "") + " in " + seconds + " seconds");
} else {
log.info("Updating and Cleaning Index");
indexer.cleanIndex(line.hasOption("f"));
indexer.updateIndex(context, line.hasOption("f"));
checkRebuildSpellCheck(line, indexer);
handler.logInfo("Indexed " + count + " object" + (count > 1 ? "s" : "") + " in " + seconds + " seconds");
} else if (indexClientOptions == IndexClientOptions.UPDATE ||
indexClientOptions == IndexClientOptions.UPDATEANDSPELLCHECK) {
handler.logInfo("Updating and Cleaning Index");
indexer.cleanIndex(false);
indexer.updateIndex(context, false);
if (indexClientOptions == IndexClientOptions.UPDATEANDSPELLCHECK) {
checkRebuildSpellCheck(commandLine, indexer);
}
} else if (indexClientOptions == IndexClientOptions.FORCEUPDATE ||
indexClientOptions == IndexClientOptions.FORCEUPDATEANDSPELLCHECK) {
handler.logInfo("Updating and Cleaning Index");
indexer.cleanIndex(true);
indexer.updateIndex(context, true);
if (indexClientOptions == IndexClientOptions.FORCEUPDATEANDSPELLCHECK) {
checkRebuildSpellCheck(commandLine, indexer);
}
}
log.info("Done with indexing");
handler.logInfo("Done with indexing");
}
public void setup() throws ParseException {
try {
context = new Context(Context.Mode.READ_ONLY);
context.turnOffAuthorisationSystem();
} catch (Exception e) {
throw new ParseException("Unable to create a new DSpace Context: " + e.getMessage());
}
indexClientOptions = IndexClientOptions.getIndexClientOption(commandLine);
}
/**
* Constructor for this class. This will ensure that the Options are created and set appropriately.
*/
private IndexClient() {
Options options = IndexClientOptions.constructOptions();
this.options = options;
}
/**
@@ -273,13 +225,12 @@ public class IndexClient {
* @param line the command line options
* @param indexer the solr indexer
* @throws SearchServiceException in case of a solr exception
* @throws java.io.IOException passed through
* @throws IOException passed through
*/
protected static void checkRebuildSpellCheck(CommandLine line, IndexingService indexer)
protected void checkRebuildSpellCheck(CommandLine line, IndexingService indexer)
throws SearchServiceException, IOException {
if (line.hasOption("s")) {
log.info("Rebuilding spell checker.");
indexer.buildSpellCheck();
}
handler.logInfo("Rebuilding spell checker.");
indexer.buildSpellCheck();
}
}

View File

@@ -0,0 +1,97 @@
/**
* 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.discovery;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
/**
* This Enum holds all the possible options and combinations for the Index discovery script
*/
public enum IndexClientOptions {
REMOVE,
CLEAN,
FORCECLEAN,
BUILD,
BUILDANDSPELLCHECK,
OPTIMIZE,
SPELLCHECK,
INDEX,
UPDATE,
FORCEUPDATE,
UPDATEANDSPELLCHECK,
FORCEUPDATEANDSPELLCHECK,
HELP;
/**
* This method resolves the CommandLine parameters to figure out which action the index-discovery script should
* perform
* @param commandLine The relevant CommandLine for the index-discovery script
* @return The index-discovery option to be ran, parsed from the CommandLine
*/
protected static IndexClientOptions getIndexClientOption(CommandLine commandLine) {
if (commandLine.hasOption("h")) {
return IndexClientOptions.HELP;
} else if (commandLine.hasOption("r")) {
return IndexClientOptions.REMOVE;
} else if (commandLine.hasOption("c")) {
if (commandLine.hasOption("f")) {
return IndexClientOptions.FORCECLEAN;
} else {
return IndexClientOptions.CLEAN;
}
} else if (commandLine.hasOption("b")) {
if (commandLine.hasOption("s")) {
return IndexClientOptions.BUILDANDSPELLCHECK;
} else {
return IndexClientOptions.BUILD;
}
} else if (commandLine.hasOption("o")) {
return IndexClientOptions.OPTIMIZE;
} else if (commandLine.hasOption("s")) {
return IndexClientOptions.SPELLCHECK;
} else if (commandLine.hasOption("i")) {
return IndexClientOptions.INDEX;
} else {
if (commandLine.hasOption("f") && commandLine.hasOption("s")) {
return IndexClientOptions.FORCEUPDATEANDSPELLCHECK;
} else if (commandLine.hasOption("f")) {
return IndexClientOptions.FORCEUPDATE;
} else if (commandLine.hasOption("s")) {
return IndexClientOptions.UPDATEANDSPELLCHECK;
} else {
return IndexClientOptions.UPDATE;
}
}
}
protected static Options constructOptions() {
Options options = new Options();
options
.addOption("r", "remove", true, "remove an Item, Collection or Community from index based on its handle");
options.getOption("r").setType(String.class);
options.addOption("i", "index", true,
"add or update an Item, Collection or Community based on its handle or uuid");
options.getOption("i").setType(boolean.class);
options.addOption("c", "clean", false,
"clean existing index removing any documents that no longer exist in the db");
options.getOption("c").setType(boolean.class);
options.addOption("b", "build", false, "(re)build index, wiping out current one if it exists");
options.getOption("b").setType(boolean.class);
options.addOption("s", "spellchecker", false, "Rebuild the spellchecker, can be combined with -b and -f.");
options.getOption("s").setType(boolean.class);
options.addOption("f", "force", false,
"if updating existing index, force each handle to be reindexed even if uptodate");
options.getOption("f").setType(boolean.class);
options.addOption("h", "help", false, "print this help message");
options.getOption("h").setType(boolean.class);
return options;
}
}

View File

@@ -5,7 +5,7 @@
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.rest;
package org.dspace.external;
import java.io.InputStream;
import java.util.Scanner;
@@ -23,16 +23,16 @@ import org.apache.logging.log4j.Logger;
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class RESTConnector {
public class OrcidRestConnector {
/**
* log4j logger
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RESTConnector.class);
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OrcidRestConnector.class);
private String url;
public RESTConnector(String url) {
public OrcidRestConnector(String url) {
this.url = url;
}

View File

@@ -0,0 +1,34 @@
/**
* 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.external.factory;
import org.dspace.external.service.ExternalDataService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Abstract factory to get services for the External package. Use ExternalServiceFactory.getInstance() to retrieve
* an implementation
*/
public abstract class ExternalServiceFactory {
/**
* Calling this method will provide an ExternalDataService bean
* @return An implementation of the ExternalDataService
*/
public abstract ExternalDataService getExternalDataService();
/**
* This method will provide you with an implementation of this class to work with
* @return An implementation of this class to work with
*/
public static ExternalServiceFactory getInstance() {
return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("externalServiceFactory", ExternalServiceFactory.class);
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.external.factory;
import org.dspace.external.service.ExternalDataService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Abstract factory to get services for the External package. Use ExternalServiceFactory.getInstance() to retrieve
* an implementation
*/
public class ExternalServiceFactoryImpl extends ExternalServiceFactory {
@Autowired(required = true)
private ExternalDataService externalDataService;
@Override
public ExternalDataService getExternalDataService() {
return externalDataService;
}
}

View File

@@ -0,0 +1,146 @@
/**
* 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.external.model;
import java.util.LinkedList;
import java.util.List;
import org.dspace.content.dto.MetadataValueDTO;
/**
* The representation model object for external data
*/
public class ExternalDataObject {
/**
* This field determines the ID for the ExternalDataObject
*/
private String id;
/**
* This field determines the value for the ExternalDataObject
*/
private String value;
/**
* This field determines where the ExternalData came from
*/
private String source;
/**
* The list of Metadata values. These our MetadataValueDTO because they won't exist in the DB
*/
private List<MetadataValueDTO> metadata = new LinkedList<>();
/**
* The display value of the ExternalDataObject
*/
private String displayValue;
/**
* Default constructor
*/
public ExternalDataObject() {
}
/**
* Constructor for the ExternalDataObject with as parameter the source of where it came from
* @param source The source where the ExternalDataObject came from
*/
public ExternalDataObject(String source) {
this.source = source;
}
/**
* Generic getter for the source
* @return The source
*/
public String getSource() {
return source;
}
/**
* Generic setter for the source
* @param source The source to be set
*/
public void setSource(String source) {
this.source = source;
}
/**
* Generic getter for the Metadata
* @return The metadata
*/
public List<MetadataValueDTO> getMetadata() {
return metadata;
}
/**
* Generic setter for the Metadata
* @param metadata The metadata to be set
*/
public void setMetadata(List<MetadataValueDTO> metadata) {
this.metadata = metadata;
}
/**
* This method will add a Metadata value to the list of metadata values
* @param metadataValueDTO The metadatavalue to be added
*/
public void addMetadata(MetadataValueDTO metadataValueDTO) {
if (metadata == null) {
metadata = new LinkedList<>();
}
metadata.add(metadataValueDTO);
}
/**
* Generic getter for the display value
* @return The display value
*/
public String getDisplayValue() {
return displayValue;
}
/**
* Generic setter for the display value
* @param displayValue The display value to be set
*/
public void setDisplayValue(String displayValue) {
this.displayValue = displayValue;
}
/**
* Generic getter for the ID
* @return The id
*/
public String getId() {
return id;
}
/**
* Generic setter for the ID
* @param id The id to be set
*/
public void setId(String id) {
this.id = id;
}
/**
* Generic getter for the value
* @return the value value of this ExternalDataObject
*/
public String getValue() {
return value;
}
/**
* Generic setter for the value
* @param value The value to be set on this ExternalDataObject
*/
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,60 @@
/**
* 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.external.provider;
import java.util.List;
import java.util.Optional;
import org.dspace.external.model.ExternalDataObject;
/**
* This interface should be implemented by all providers that will deal with external data
*/
public interface ExternalDataProvider {
/**
* This method will return the SourceIdentifier for the ExternalDataProvider that implements the interface
* @return The source identifier as a String
*/
public String getSourceIdentifier();
/**
* This method will take a String id as a parameter and it'll call the ExternalDataProvider's endpoint or data
* source to retrieve and build the ExternalDataObject
* @param id The id on which will be searched
* @return An Optional object of ExternalDataObject. This is to indicate that this object may be null.
* This ExternalDataObject will return all the data returned by the ExternalDataProvider
*/
Optional<ExternalDataObject> getExternalDataObject(String id);
/**
* This method will query the ExternalDataProvider's endpoint or data source to retrieve and build a list of
* ExternalDataObjects through a search with the given parameters
* @param query The query for the search
* @param start The start of the search
* @param limit The max amount of records to be returned by the search
* @return A list of ExternalDataObjects that were retrieved and built by this search
*/
List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit);
/**
* This method will return a boolean indicating whether this ExternalDataProvider can deal with the given source
* or not
* @param source The source on which the check needs to be done
* @return A boolean indicating whether this ExternalDataProvider can deal with this source or not
*/
public boolean supports(String source);
/**
* Returns the total amount of results that this source can return for the given query
* @param query The query to be search on and give the total amount of results
* @return The total amount of results that the source can return for the given query
*/
public int getNumberOfResults(String query);
}

View File

@@ -5,27 +5,32 @@
*
* http://www.dspace.org/license/
*/
package org.dspace.content.authority;
package org.dspace.external.provider.impl;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.content.Collection;
import org.dspace.content.DCPersonName;
import org.dspace.core.ConfigurationManager;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -51,16 +56,14 @@ import org.xml.sax.helpers.DefaultHandler;
*
* lcname.url = http://alcme.oclc.org/srw/search/lcnaf
*
* TODO: make # of results to ask for (and return) configurable.
*
* @author Larry Stone
* @version $Revision $
*/
public class LCNameAuthority implements ChoiceAuthority {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LCNameAuthority.class);
public class LCNameDataProvider implements ExternalDataProvider {
private static final Logger log = LogManager.getLogger(LCNameDataProvider.class);
// get these from configuration
protected static String url = null;
private String url;
private String sourceIdentifier;
// NS URI for SRU respones
protected static final String NS_SRU = "http://www.loc.gov/zing/srw/";
@@ -68,53 +71,53 @@ public class LCNameAuthority implements ChoiceAuthority {
// NS URI for MARC/XML
protected static final String NS_MX = "http://www.loc.gov/MARC21/slim";
// constructor does static init too..
public LCNameAuthority() {
if (url == null) {
url = ConfigurationManager.getProperty("lcname.url");
// sanity check
if (url == null) {
throw new IllegalStateException("Missing DSpace configuration keys for LCName Query");
}
}
public String getSourceIdentifier() {
return sourceIdentifier;
}
// punt! this is a poor implementation..
@Override
public Choices getBestMatch(String field, String text, Collection collection, String locale) {
return getMatches(field, text, collection, 0, 2, locale);
public Optional<ExternalDataObject> getExternalDataObject(String id) {
StringBuilder query = new StringBuilder();
query.append("local.LCCN = \"").append(id).append("\"");
List<ExternalDataObject> list = doLookup(0, 10, query);
if (list.size() > 0) {
return Optional.of(list.get(0));
} else {
return Optional.empty();
}
}
/**
* Match a proposed value against name authority records
* Value is assumed to be in "Lastname, Firstname" format.
* Generic getter for the url
* @return the url value of this LCNameDataProvider
*/
@Override
public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale) {
Choices result = queryPerson(text, start, limit);
if (result == null) {
result = new Choices(true);
}
return result;
}
// punt; supposed to get the canonical display form of a metadata authority key
// XXX FIXME implement this with a query on the authority key, cache results
@Override
public String getLabel(String field, String key, String locale) {
return key;
public String getUrl() {
return url;
}
/**
* Guts of the implementation, returns a complete Choices result, or
* null for a failure.
* Generic setter for the url
* @param url The url to be set on this LCNameDataProvider
*/
private Choices queryPerson(String text, int start, int limit) {
public void setUrl(String url) {
this.url = url;
}
/**
* Generic setter for the sourceIdentifier
* @param sourceIdentifier The sourceIdentifier to be set on this LCNameDataProvider
*/
public void setSourceIdentifier(String sourceIdentifier) {
this.sourceIdentifier = sourceIdentifier;
}
@Override
public List<ExternalDataObject> searchExternalDataObjects(String text, int start, int limit) {
// punt if there is no query text
if (text == null || text.trim().length() == 0) {
return new Choices(true);
return Collections.EMPTY_LIST;
}
// 1. build CQL query
@@ -123,12 +126,51 @@ public class LCNameAuthority implements ChoiceAuthority {
query.append("local.FirstName = \"").append(pn.getFirstNames()).
append("\" and local.FamilyName = \"").append(pn.getLastName()).
append("\"");
return doLookup(start, limit, query);
}
private List<ExternalDataObject> doLookup(int start, int limit, StringBuilder query) {
// XXX arbitrary default limit - should be configurable?
if (limit == 0) {
limit = 50;
}
HttpGet get = constructHttpGet(query, start, limit);
// 2. web request
try {
HttpClient hc = new DefaultHttpClient();
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SRUHandler handler = parseResponseToSRUHandler(response);
// this probably just means more results available..
if (handler.hits != handler.result.size()) {
log.warn("Discrepency in results, result.length=" + handler.result.size() +
", yet expected results=" + handler.hits);
}
return handler.result;
}
} catch (IOException e) {
log.error("SRU query failed: ", e);
return Collections.EMPTY_LIST;
} catch (ParserConfigurationException e) {
log.warn("Failed parsing SRU result: ", e);
return Collections.EMPTY_LIST;
} catch (SAXException e) {
log.warn("Failed parsing SRU result: ", e);
return Collections.EMPTY_LIST;
} finally {
get.releaseConnection();
}
return Collections.EMPTY_LIST;
}
public boolean supports(String source) {
return StringUtils.equalsIgnoreCase(sourceIdentifier, source);
}
private HttpGet constructHttpGet(StringBuilder query, int start, int limit) {
URI sruUri;
try {
URIBuilder builder = new URIBuilder(url);
@@ -141,66 +183,65 @@ public class LCNameAuthority implements ChoiceAuthority {
sruUri = builder.build();
} catch (URISyntaxException e) {
log.error("SRU query failed: ", e);
return new Choices(true);
return null;
}
HttpGet get = new HttpGet(sruUri);
log.debug("Trying SRU query, URL=" + sruUri);
return get;
}
@Override
public int getNumberOfResults(String query) {
// punt if there is no query text
if (query == null || query.trim().length() == 0) {
return 0;
}
// 1. build CQL query
DCPersonName pn = new DCPersonName(query);
StringBuilder queryStringBuilder = new StringBuilder();
queryStringBuilder.append("local.FirstName = \"").append(pn.getFirstNames()).
append("\" and local.FamilyName = \"").append(pn.getLastName()).
append("\"");
HttpGet get = constructHttpGet(queryStringBuilder, 0, 1);
// 2. web request
try {
HttpClient hc = new DefaultHttpClient();
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
SRUHandler handler = new SRUHandler();
// XXX FIXME: should turn off validation here explicitly, but
// it seems to be off by default.
xr.setFeature("http://xml.org/sax/features/namespaces", true);
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
HttpEntity responseBody = response.getEntity();
xr.parse(new InputSource(responseBody.getContent()));
// this probably just means more results available..
if (handler.hits != handler.result.size()) {
log.warn("Discrepency in results, result.length=" + handler.result.size() +
", yet expected results=" + handler.hits);
}
boolean more = handler.hits > (start + handler.result.size());
// XXX add non-auth option; perhaps the UI should do this?
// XXX it's really a policy matter if they allow unauth result.
// XXX good, stop it.
// handler.result.add(new Choice("", text, "Non-Authority: \""+text+"\""));
int confidence;
if (handler.hits == 0) {
confidence = Choices.CF_NOTFOUND;
} else if (handler.hits == 1) {
confidence = Choices.CF_UNCERTAIN;
} else {
confidence = Choices.CF_AMBIGUOUS;
}
return new Choices(handler.result.toArray(new Choice[handler.result.size()]),
start, handler.hits, confidence, more);
SRUHandler handler = parseResponseToSRUHandler(response);
return handler.hits;
}
} catch (IOException e) {
log.error("SRU query failed: ", e);
return new Choices(true);
} catch (ParserConfigurationException e) {
} catch (IOException | ParserConfigurationException | SAXException e) {
log.warn("Failed parsing SRU result: ", e);
return new Choices(true);
} catch (SAXException e) {
log.warn("Failed parsing SRU result: ", e);
return new Choices(true);
return 0;
} finally {
get.releaseConnection();
}
return new Choices(true);
return 0;
}
private SRUHandler parseResponseToSRUHandler(HttpResponse response)
throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
SRUHandler handler = new SRUHandler(sourceIdentifier);
// XXX FIXME: should turn off validation here explicitly, but
// it seems to be off by default.
xr.setFeature("http://xml.org/sax/features/namespaces", true);
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
HttpEntity responseBody = response.getEntity();
xr.parse(new InputSource(responseBody.getContent()));
return handler;
}
/**
@@ -212,14 +253,21 @@ public class LCNameAuthority implements ChoiceAuthority {
*/
private static class SRUHandler
extends DefaultHandler {
private List<Choice> result = new ArrayList<Choice>();
private String sourceIdentifier;
private List<ExternalDataObject> result = new ArrayList<ExternalDataObject>();
private int hits = -1;
private String textValue = null;
private String name = null;
private String birthDate = null;
private String lccn = null;
private String lastTag = null;
private String lastCode = null;
public SRUHandler(String sourceIdentifier) {
super();
this.sourceIdentifier = sourceIdentifier;
}
// NOTE: text value MAY be presented in multiple calls, even if
// it all one word, so be ready to splice it together.
// BEWARE: subclass's startElement method should call super()
@@ -259,14 +307,33 @@ public class LCNameAuthority implements ChoiceAuthority {
name = name.substring(0, name.length() - 1);
}
// XXX DEBUG
// log.debug("Got result, name="+name+", lccn="+lccn);
result.add(new Choice(lccn, name, name));
ExternalDataObject externalDataObject = new ExternalDataObject(sourceIdentifier);
externalDataObject.setDisplayValue(name);
externalDataObject.setValue(name);
externalDataObject.setId(lccn);
String[] names = name.split(", ");
String familyName = names[0];
String givenName = names.length > 1 ? names[1] : null;
if (StringUtils.isNotBlank(familyName)) {
externalDataObject
.addMetadata(new MetadataValueDTO("person", "familyName", null, null, familyName));
}
if (StringUtils.isNotBlank(givenName)) {
externalDataObject
.addMetadata(new MetadataValueDTO("person", "givenName", null, null, givenName));
}
if (StringUtils.isNotBlank(birthDate)) {
externalDataObject
.addMetadata(new MetadataValueDTO("person", "date", "birth", null, birthDate));
}
externalDataObject.addMetadata(new MetadataValueDTO("person", "identifier", "lccn", null, lccn));
result.add(externalDataObject);
} else {
log.warn("Got anomalous result, at least one of these null: lccn=" + lccn + ", name=" + name);
}
name = null;
lccn = null;
birthDate = null;
} else if (localName.equals("subfield") && namespaceURI.equals(NS_MX)) {
if (lastTag != null && lastCode != null) {
if (lastTag.equals("010") && lastCode.equals("a")) {
@@ -276,9 +343,8 @@ public class LCNameAuthority implements ChoiceAuthority {
// 100.a is the personal name
name = textValue;
}
if (lastTag.equals("100") && lastCode.equals("d") && (name != null)) {
name = name + " " + textValue;
if (lastTag.equals("100") && lastCode.equals("d")) {
birthDate = textValue;
}
}
}

View File

@@ -0,0 +1,280 @@
/**
* 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.external.provider.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.OrcidRestConnector;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
import org.dspace.external.provider.orcid.xml.XMLtoBio;
import org.json.JSONObject;
import org.orcid.jaxb.model.common_v2.OrcidId;
import org.orcid.jaxb.model.record_v2.Person;
import org.orcid.jaxb.model.search_v2.Result;
import org.springframework.beans.factory.annotation.Required;
/**
* This class is the implementation of the ExternalDataProvider interface that will deal with the OrcidV2 External
* Data lookup
*/
public class OrcidV2AuthorDataProvider implements ExternalDataProvider {
private static Logger log = LogManager.getLogger(OrcidV2AuthorDataProvider.class);
private OrcidRestConnector orcidRestConnector;
private String OAUTHUrl;
private String clientId;
private String clientSecret;
private String accessToken;
private String sourceIdentifier;
private String orcidUrl;
public static final String ORCID_ID_SYNTAX = "\\d{4}-\\d{4}-\\d{4}-(\\d{3}X|\\d{4})";
@Override
public String getSourceIdentifier() {
return sourceIdentifier;
}
/**
* Initialize the accessToken that is required for all subsequent calls to ORCID.
*
* @throws java.io.IOException passed through from HTTPclient.
*/
public void init() throws IOException {
if (StringUtils.isNotBlank(accessToken) && StringUtils.isNotBlank(clientSecret)) {
String authenticationParameters = "?client_id=" + clientId +
"&client_secret=" + clientSecret +
"&scope=/read-public&grant_type=client_credentials";
HttpPost httpPost = new HttpPost(OAUTHUrl + authenticationParameters);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse getResponse = httpClient.execute(httpPost);
JSONObject responseObject = null;
try (InputStream is = getResponse.getEntity().getContent();
BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
String inputStr;
while ((inputStr = streamReader.readLine()) != null && responseObject == null) {
if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) {
try {
responseObject = new JSONObject(inputStr);
} catch (Exception e) {
//Not as valid as I'd hoped, move along
responseObject = null;
}
}
}
}
if (responseObject != null && responseObject.has("access_token")) {
accessToken = (String) responseObject.get("access_token");
}
}
}
/**
* Makes an instance of the Orcidv2 class based on the provided parameters.
* This constructor is called through the spring bean initialization
*/
private OrcidV2AuthorDataProvider(String url) {
this.orcidRestConnector = new OrcidRestConnector(url);
}
@Override
public Optional<ExternalDataObject> getExternalDataObject(String id) {
Person person = getBio(id);
ExternalDataObject externalDataObject = convertToExternalDataObject(person);
return Optional.of(externalDataObject);
}
protected ExternalDataObject convertToExternalDataObject(Person person) {
ExternalDataObject externalDataObject = new ExternalDataObject(sourceIdentifier);
String lastName = "";
String firstName = "";
if (person.getName().getFamilyName() != null) {
lastName = person.getName().getFamilyName().getValue();
externalDataObject.addMetadata(new MetadataValueDTO("person", "familyName", null, null,
lastName));
}
if (person.getName().getGivenNames() != null) {
firstName = person.getName().getGivenNames().getValue();
externalDataObject.addMetadata(new MetadataValueDTO("person", "givenName", null, null,
firstName));
}
externalDataObject.setId(person.getName().getPath());
externalDataObject
.addMetadata(new MetadataValueDTO("dc", "identifier", "orcid", null, person.getName().getPath()));
externalDataObject
.addMetadata(new MetadataValueDTO("dc", "identifier", "uri", null, orcidUrl + person.getName().getPath()));
if (!StringUtils.isBlank(lastName) && !StringUtils.isBlank(firstName)) {
externalDataObject.setDisplayValue(lastName + ", " + firstName);
externalDataObject.setValue(lastName + ", " + firstName);
} else if (StringUtils.isBlank(firstName)) {
externalDataObject.setDisplayValue(lastName);
externalDataObject.setValue(lastName);
} else if (StringUtils.isBlank(lastName)) {
externalDataObject.setDisplayValue(firstName);
externalDataObject.setValue(firstName);
}
return externalDataObject;
}
/**
* Retrieve a Person object based on a given orcid identifier
* @param id orcid identifier
* @return Person
*/
public Person getBio(String id) {
log.debug("getBio called with ID=" + id);
if (!isValid(id)) {
return null;
}
InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken);
XMLtoBio converter = new XMLtoBio();
Person person = converter.convertSinglePerson(bioDocument);
try {
bioDocument.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return person;
}
/**
* Check to see if the provided text has the correct ORCID syntax.
* Since only searching on ORCID id is allowed, this way, we filter out any queries that would return a
* blank result anyway
*/
private boolean isValid(String text) {
return StringUtils.isNotBlank(text) && text.matches(ORCID_ID_SYNTAX);
}
@Override
public List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit) {
if (limit > 100) {
throw new IllegalArgumentException("The maximum number of results to retrieve cannot exceed 100.");
}
String searchPath = "search?q=" + URLEncoder.encode(query) + "&start=" + start + "&rows=" + limit;
log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken);
InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken);
XMLtoBio converter = new XMLtoBio();
List<Result> results = converter.convert(bioDocument);
List<Person> bios = new LinkedList<>();
for (Result result : results) {
OrcidId orcidIdentifier = result.getOrcidIdentifier();
if (orcidIdentifier != null) {
log.debug("Found OrcidId=" + orcidIdentifier.toString());
String orcid = orcidIdentifier.getUriPath();
Person bio = getBio(orcid);
if (bio != null) {
bios.add(bio);
}
}
}
try {
bioDocument.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
if (bios == null) {
return Collections.emptyList();
} else {
return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList());
}
}
@Override
public boolean supports(String source) {
return StringUtils.equalsIgnoreCase(sourceIdentifier, source);
}
@Override
public int getNumberOfResults(String query) {
String searchPath = "search?q=" + URLEncoder.encode(query) + "&start=" + 0 + "&rows=" + 0;
log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken);
InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken);
XMLtoBio converter = new XMLtoBio();
return converter.getNumberOfResultsFromXml(bioDocument);
}
/**
* Generic setter for the sourceIdentifier
* @param sourceIdentifier The sourceIdentifier to be set on this OrcidV2AuthorDataProvider
*/
@Required
public void setSourceIdentifier(String sourceIdentifier) {
this.sourceIdentifier = sourceIdentifier;
}
/**
* Generic getter for the orcidUrl
* @return the orcidUrl value of this OrcidV2AuthorDataProvider
*/
public String getOrcidUrl() {
return orcidUrl;
}
/**
* Generic setter for the orcidUrl
* @param orcidUrl The orcidUrl to be set on this OrcidV2AuthorDataProvider
*/
@Required
public void setOrcidUrl(String orcidUrl) {
this.orcidUrl = orcidUrl;
}
/**
* Generic setter for the OAUTHUrl
* @param OAUTHUrl The OAUTHUrl to be set on this OrcidV2AuthorDataProvider
*/
public void setOAUTHUrl(String OAUTHUrl) {
this.OAUTHUrl = OAUTHUrl;
}
/**
* Generic setter for the clientId
* @param clientId The clientId to be set on this OrcidV2AuthorDataProvider
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* Generic setter for the clientSecret
* @param clientSecret The clientSecret to be set on this OrcidV2AuthorDataProvider
*/
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
}

View File

@@ -0,0 +1,236 @@
/**
* 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.external.provider.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.Logger;
import org.dspace.app.sherpa.SHERPAJournal;
import org.dspace.app.sherpa.SHERPAResponse;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
/**
* This class is the implementation of the ExternalDataProvider interface that will deal with SherpaJournal External
* data lookups
*/
public class SherpaJournalDataProvider implements ExternalDataProvider {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SherpaJournalDataProvider.class);
private String url;
private String sourceIdentifier;
private String apiKey;
private CloseableHttpClient client = null;
@Override
public String getSourceIdentifier() {
return sourceIdentifier;
}
/**
* Initialise the client that we need to call the endpoint
* @throws IOException If something goes wrong
*/
public void init() throws IOException {
HttpClientBuilder builder = HttpClientBuilder.create();
// httpclient 4.3+ doesn't appear to have any sensible defaults any more. Setting conservative defaults as
// not to hammer the SHERPA service too much.
client = builder
.disableAutomaticRetries()
.setMaxConnTotal(5)
.build();
}
@Override
public Optional<ExternalDataObject> getExternalDataObject(String id) {
HttpGet method = null;
SHERPAResponse sherpaResponse = null;
int timeout = 5000;
URIBuilder uriBuilder = null;
try {
uriBuilder = new URIBuilder(url);
uriBuilder.addParameter("jtitle", id);
if (StringUtils.isNotBlank(apiKey)) {
uriBuilder.addParameter("ak", apiKey);
}
method = new HttpGet(uriBuilder.build());
method.setConfig(RequestConfig.custom()
.setConnectionRequestTimeout(timeout)
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.build());
// Execute the method.
HttpResponse response = client.execute(method);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: "
+ statusCode);
}
HttpEntity responseBody = response.getEntity();
if (null != responseBody) {
sherpaResponse = new SHERPAResponse(responseBody.getContent());
} else {
sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response");
}
} catch (Exception e) {
log.error("SHERPA/RoMEO query failed: ", e);
}
if (sherpaResponse == null) {
sherpaResponse = new SHERPAResponse(
"Error processing the SHERPA/RoMEO answer");
}
if (CollectionUtils.isNotEmpty(sherpaResponse.getJournals())) {
SHERPAJournal sherpaJournal = sherpaResponse.getJournals().get(0);
ExternalDataObject externalDataObject = constructExternalDataObjectFromSherpaJournal(sherpaJournal);
return Optional.of(externalDataObject);
}
return null;
}
private ExternalDataObject constructExternalDataObjectFromSherpaJournal(SHERPAJournal sherpaJournal) {
ExternalDataObject externalDataObject = new ExternalDataObject();
externalDataObject.setSource(sourceIdentifier);
externalDataObject.setId(sherpaJournal.getTitle());
externalDataObject
.addMetadata(new MetadataValueDTO("dc", "title", null, null, sherpaJournal.getTitle()));
externalDataObject
.addMetadata(new MetadataValueDTO("dc", "identifier", "issn", null, sherpaJournal.getIssn()));
externalDataObject.setValue(sherpaJournal.getTitle());
externalDataObject.setDisplayValue(sherpaJournal.getTitle());
return externalDataObject;
}
@Override
public List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit) {
// query args to add to SHERPA/RoMEO request URL
HttpGet get = constructHttpGet(query);
try {
HttpClient hc = new DefaultHttpClient();
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SHERPAResponse sherpaResponse = new SHERPAResponse(response.getEntity().getContent());
List<ExternalDataObject> list = sherpaResponse.getJournals().stream().map(
sherpaJournal -> constructExternalDataObjectFromSherpaJournal(sherpaJournal)).collect(
Collectors.toList());
// This is because Sherpa returns everything by default so we can't specifiy a start and limit
// in the query itself
return list.subList(start, Math.min(start + limit, list.size()));
}
} catch (IOException e) {
log.error("SHERPA/RoMEO query failed: ", e);
return null;
} finally {
get.releaseConnection();
}
return null;
}
private HttpGet constructHttpGet(String query) {
List<BasicNameValuePair> args = new ArrayList<BasicNameValuePair>();
args.add(new BasicNameValuePair("jtitle", query));
args.add(new BasicNameValuePair("qtype", "contains"));
args.add(new BasicNameValuePair("ak", apiKey));
String srUrl = url + "?" + URLEncodedUtils.format(args, "UTF8");
return new HttpGet(srUrl);
}
@Override
public boolean supports(String source) {
return StringUtils.equalsIgnoreCase(sourceIdentifier, source);
}
@Override
public int getNumberOfResults(String query) {
HttpGet get = constructHttpGet(query);
try {
HttpClient hc = new DefaultHttpClient();
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SHERPAResponse sherpaResponse = new SHERPAResponse(response.getEntity().getContent());
return sherpaResponse.getNumHits();
}
} catch (IOException e) {
log.error("SHERPA/RoMEO query failed: ", e);
return 0;
} finally {
get.releaseConnection();
}
return 0;
}
/**
* Generic setter for the sourceIdentifier
* @param sourceIdentifier The sourceIdentifier to be set on this SherpaJournalDataProvider
*/
public void setSourceIdentifier(String sourceIdentifier) {
this.sourceIdentifier = sourceIdentifier;
}
/**
* Generic getter for the url
* @return the url value of this SherpaJournalDataProvider
*/
public String getUrl() {
return url;
}
/**
* Generic setter for the url
* @param url The url to be set on this SherpaJournalDataProvider
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Generic getter for the apiKey
* @return the apiKey value of this SherpaJournalDataProvider
*/
public String getApiKey() {
return apiKey;
}
/**
* Generic setter for the apiKey
* @param apiKey The apiKey to be set on this SherpaJournalDataProvider
*/
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
}

View File

@@ -0,0 +1,194 @@
/**
* 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.external.provider.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.Logger;
import org.dspace.app.sherpa.SHERPAPublisher;
import org.dspace.app.sherpa.SHERPAResponse;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
/**
* This class is the implementation of the ExternalDataProvider interface that will deal with SherpaPublisher External
* data lookups
*/
public class SherpaPublisherDataProvider implements ExternalDataProvider {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SherpaPublisherDataProvider.class);
private String sourceIdentifier;
private String url;
private String apiKey;
@Override
public String getSourceIdentifier() {
return sourceIdentifier;
}
@Override
public Optional<ExternalDataObject> getExternalDataObject(String id) {
List<BasicNameValuePair> args = new ArrayList<BasicNameValuePair>();
args.add(new BasicNameValuePair("id", id));
args.add(new BasicNameValuePair("ak", apiKey));
HttpClient hc = new DefaultHttpClient();
String srUrl = url + "?" + URLEncodedUtils.format(args, "UTF8");
HttpGet get = new HttpGet(srUrl);
try {
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SHERPAResponse sherpaResponse = new SHERPAResponse(response.getEntity().getContent());
List<SHERPAPublisher> list = sherpaResponse.getPublishers();
if (CollectionUtils.isNotEmpty(list)) {
return Optional.of(constructExternalDataObjectFromSherpaPublisher(list.get(0)));
}
}
} catch (IOException e) {
log.error("SHERPA/RoMEO query failed: ", e);
return null;
} finally {
get.releaseConnection();
}
return null;
}
@Override
public List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit) {
HttpGet get = constructHttpGet(query);
try {
HttpClient hc = new DefaultHttpClient();
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SHERPAResponse sherpaResponse = new SHERPAResponse(response.getEntity().getContent());
List<ExternalDataObject> list = sherpaResponse.getPublishers().stream().map(
sherpaPublisher -> constructExternalDataObjectFromSherpaPublisher(sherpaPublisher)).collect(
Collectors.toList());
// This is because Sherpa returns everything by default so we can't specifiy a start and limit
// in the query itself
return list.subList(start, Math.min(start + limit, list.size()));
}
} catch (IOException e) {
log.error("SHERPA/RoMEO query failed: ", e);
return null;
} finally {
get.releaseConnection();
}
return null;
}
private HttpGet constructHttpGet(String query) {
List<BasicNameValuePair> args = new ArrayList<BasicNameValuePair>();
args.add(new BasicNameValuePair("pub", query));
args.add(new BasicNameValuePair("qtype", "all"));
args.add(new BasicNameValuePair("ak", apiKey));
String srUrl = url + "?" + URLEncodedUtils.format(args, "UTF8");
return new HttpGet(srUrl);
}
private ExternalDataObject constructExternalDataObjectFromSherpaPublisher(SHERPAPublisher sherpaPublisher) {
ExternalDataObject externalDataObject = new ExternalDataObject();
externalDataObject.setSource(sourceIdentifier);
//Text value == name
externalDataObject.addMetadata(new MetadataValueDTO("dc", "title", null, null, sherpaPublisher.getName()));
externalDataObject.setDisplayValue(sherpaPublisher.getName());
externalDataObject.setValue(sherpaPublisher.getName());
if (StringUtils.isNotBlank(sherpaPublisher.getId())) {
externalDataObject.setId(sherpaPublisher.getId());
externalDataObject
.addMetadata(
new MetadataValueDTO("dc", "identifier", "sherpaPublisher", null, sherpaPublisher.getId()));
}
//Text value == homeurl
externalDataObject
.addMetadata(new MetadataValueDTO("dc", "identifier", "other", null, sherpaPublisher.getHomeurl()));
return externalDataObject;
}
@Override
public boolean supports(String source) {
return StringUtils.equalsIgnoreCase(sourceIdentifier, source);
}
@Override
public int getNumberOfResults(String query) {
HttpGet get = constructHttpGet(query);
try {
HttpClient hc = new DefaultHttpClient();
HttpResponse response = hc.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
SHERPAResponse sherpaResponse = new SHERPAResponse(response.getEntity().getContent());
return sherpaResponse.getNumHits();
}
} catch (IOException e) {
log.error("SHERPA/RoMEO query failed: ", e);
return 0;
} finally {
get.releaseConnection();
}
return 0;
}
/**
* Generic setter for the sourceIdentifier
* @param sourceIdentifier The sourceIdentifier to be set on this SherpaPublisherDataProvider
*/
public void setSourceIdentifier(String sourceIdentifier) {
this.sourceIdentifier = sourceIdentifier;
}
/**
* Generic getter for the url
* @return the url value of this SherpaPublisherDataProvider
*/
public String getUrl() {
return url;
}
/**
* Generic setter for the url
* @param url The url to be set on this SherpaPublisherDataProvider
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Generic getter for the apiKey
* @return the apiKey value of this SherpaPublisherDataProvider
*/
public String getApiKey() {
return apiKey;
}
/**
* Generic setter for the apiKey
* @param apiKey The apiKey to be set on this SherpaPublisherDataProvider
*/
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
}

View File

@@ -5,11 +5,10 @@
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.orcid.xml;
package org.dspace.external.provider.orcid.xml;
import java.io.InputStream;
import java.net.URISyntaxException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

View File

@@ -5,7 +5,7 @@
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.orcid.xml;
package org.dspace.external.provider.orcid.xml;
import java.io.InputStream;
import java.net.URISyntaxException;
@@ -13,9 +13,6 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.dspace.authority.orcid.Orcidv2;
import org.dspace.utils.DSpace;
import org.orcid.jaxb.model.common_v2.OrcidId;
import org.orcid.jaxb.model.record_v2.Person;
import org.orcid.jaxb.model.search_v2.Result;
import org.orcid.jaxb.model.search_v2.Search;
@@ -27,7 +24,7 @@ import org.xml.sax.SAXException;
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class XMLtoBio extends Converter {
public class XMLtoBio extends Converter<List<Result>> {
/**
* log4j logger
@@ -35,29 +32,26 @@ public class XMLtoBio extends Converter {
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(XMLtoBio.class);
@Override
public List<Person> convert(InputStream xml) {
List<Person> bios = new ArrayList<>();
public List<Result> convert(InputStream xml) {
List<Result> bios = new ArrayList<>();
try {
Orcidv2 connector = new DSpace().getServiceManager().getServiceByName("AuthoritySource", Orcidv2.class);
Search search = (Search) unmarshall(xml, Search.class);
for (Result result : search.getResult()) {
OrcidId orcidIdentifier = result.getOrcidIdentifier();
if (orcidIdentifier != null) {
log.debug("Found OrcidId=" + orcidIdentifier.toString());
String orcid = orcidIdentifier.getUriPath();
Person bio = connector.getBio(orcid);
if (bio != null) {
bios.add(bio);
}
}
}
bios = search.getResult();
} catch (SAXException | URISyntaxException e) {
log.error(e);
}
return bios;
}
public int getNumberOfResultsFromXml(InputStream xml) {
try {
Search search = (Search) unmarshall(xml, Search.class);
return search.getNumFound().intValue();
} catch (SAXException | URISyntaxException e) {
log.error(e);
}
return 0;
}
public Person convertSinglePerson(InputStream xml) {
Person person = null;
try {

View File

@@ -0,0 +1,62 @@
/**
* 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.external.service;
import java.util.List;
import java.util.Optional;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
/**
* This is an interface that will deal with all Service level calls for External Data
*/
public interface ExternalDataService {
/**
* This method will return a list of ExternalDataProvider objects defined by all ExternalDataProvider spring beans
* @return A list of all ExternalDataProvider objects
*/
public List<ExternalDataProvider> getExternalDataProviders();
/**
* This method will return a single ExternalDataProvider which will support the given sourceIdentifier param
* @param sourceIdentifier The source identifier that the ExternalDataProvider that will be returned by this
* method has to support
* @return The ExternalDataProvider that supports the given source identifier
*/
public ExternalDataProvider getExternalDataProvider(String sourceIdentifier);
/**
* This method will return an Optional instance of ExternalDataObject for the given source and identifier
* It will try to retrieve one through an ExternalDataProvider as defined by the source with the given identifier
* @param source The source in which the lookup will be done
* @param identifier The identifier which will be looked up
* @return An Optional instance of ExternalDataObject
*/
public Optional<ExternalDataObject> getExternalDataObject(String source, String identifier);
/**
* This method will return a list of ExternalDataObjects as defined through the source in which they will be
* searched for, the given query start and limit parameters
* @param source The source that defines which ExternalDataProvider is to be used
* @param query The query for which the search will be done
* @param start The start of the search
* @param limit The maximum amount of records to be returned by the search
* @return A list of ExternalDataObjects that obey the rules in the parameters
*/
public List<ExternalDataObject> searchExternalDataObjects(String source, String query, int start, int limit);
/**
* This method wil return the total amount of results that will be found for the given query in the given source
* @param source The source in which the query will happen to return the number of results
* @param query The query to be ran in this source to retrieve the total amount of results
* @return The total amount of results that can be returned for this query in the given source
*/
public int getNumberOfResults(String source, String query);
}

View File

@@ -0,0 +1,68 @@
/**
* 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.external.service.impl;
import java.util.List;
import java.util.Optional;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
import org.dspace.external.service.ExternalDataService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implementation of {@link ExternalDataService}
*/
public class ExternalDataServiceImpl implements ExternalDataService {
@Autowired
private List<ExternalDataProvider> externalDataProviders;
@Override
public Optional<ExternalDataObject> getExternalDataObject(String source, String id) {
ExternalDataProvider provider = getExternalDataProvider(source);
if (provider == null) {
throw new IllegalArgumentException("Provider for: " + source + " couldn't be found");
}
return provider.getExternalDataObject(id);
}
@Override
public List<ExternalDataObject> searchExternalDataObjects(String source, String query, int start, int limit) {
ExternalDataProvider provider = getExternalDataProvider(source);
if (provider == null) {
throw new IllegalArgumentException("Provider for: " + source + " couldn't be found");
}
return provider.searchExternalDataObjects(query, start, limit);
}
@Override
public List<ExternalDataProvider> getExternalDataProviders() {
return externalDataProviders;
}
@Override
public ExternalDataProvider getExternalDataProvider(String sourceIdentifier) {
for (ExternalDataProvider externalDataProvider : externalDataProviders) {
if (externalDataProvider.supports(sourceIdentifier)) {
return externalDataProvider;
}
}
return null;
}
@Override
public int getNumberOfResults(String source, String query) {
ExternalDataProvider provider = getExternalDataProvider(source);
if (provider == null) {
throw new IllegalArgumentException("Provider for: " + source + " couldn't be found");
}
return provider.getNumberOfResults(query);
}
}

View File

@@ -13,11 +13,11 @@ import java.sql.SQLException;
import java.util.Iterator;
import org.apache.logging.log4j.Logger;
import org.dspace.app.launcher.ScriptLauncher;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.MetadataValueService;
import org.dspace.core.Context;
import org.dspace.discovery.IndexClient;
import org.dspace.handle.factory.HandleServiceFactory;
import org.dspace.handle.service.HandleService;
import org.dspace.services.ConfigurationService;
@@ -139,7 +139,7 @@ public class UpdateHandlePrefix {
try {
// Reinitialise the search and browse system
IndexClient.main(new String[] {"-b"});
ScriptLauncher.main(new String[] {"index-discovery", "-b"});
System.out.println("Browse and search indexes are ready now.");
// All done
System.out.println("\nAll done successfully. Please check the DSpace logs!\n");

View File

@@ -0,0 +1,108 @@
/**
* 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.scripts;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
/**
* This class serves as a representation of a command line parameter by holding a String name and a String value
*/
public class DSpaceCommandLineParameter {
private String name;
private String value;
public static String SEPARATOR = "|||";
/**
* This constructor will take a String key and String value and store them in their appriopriate fields
* @param key The String value to be stored as the name of the parameter
* @param value The String value to be stored as the value of the parameter
*/
public DSpaceCommandLineParameter(String key, String value) {
this.name = key;
if (StringUtils.isBlank(value)) {
this.value = null;
} else {
this.value = value;
}
}
/**
* This constructors accepts a single parameter String that is defined as e.g. "-c test" and it'll parse this
* String into the key "-c" and value "test" to then call the other constructor with those parameters
* @param parameter The String parameter
*/
protected DSpaceCommandLineParameter(String parameter) {
this(StringUtils.substringBefore(parameter, " "), StringUtils.substringAfter(parameter, " "));
}
public String getName() {
return name;
}
public void setName(String key) {
this.name = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
/**
* Converts the DSpaceCommandLineParameter into a String format by concatenating the value and the name String
* values by separating them with a space
* @return The String representation of a DSpaceCommandlineParameter object
*/
public String toString() {
String stringToReturn = "";
stringToReturn += getName();
if (StringUtils.isNotBlank(getValue())) {
stringToReturn += " ";
stringToReturn += getValue();
}
return stringToReturn;
}
/**
* This method will convert a list of DSpaceCommandLineParameter objects into a single String. This is done by
* calling the toString() method on each of the DSpaceCommandLineParameter objects in the list and concatenating
* them with the Separator defined in this class
* @param parameterList The list of DSpaceCommandLineParameter objects to be converted into a String
* @return The resulting String
*/
public static String concatenate(List<DSpaceCommandLineParameter> parameterList) {
if (parameterList.isEmpty()) {
return null;
}
return parameterList.stream().map(parameter -> parameter.toString()).collect(Collectors.joining(SEPARATOR));
}
/**
* Will return a boolean indicating whether the given param is equal to this object
* @param other The other object
* @return A boolean indicating equality
*/
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (other.getClass() != DSpaceCommandLineParameter.class) {
return false;
}
return StringUtils.equals(this.getName(), ((DSpaceCommandLineParameter) other).getName()) && StringUtils
.equals(this.getValue(), ((DSpaceCommandLineParameter) other).getValue());
}
}

View File

@@ -0,0 +1,156 @@
/**
* 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.scripts;
import java.sql.SQLException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
* This abstract class is the class that should be extended by each script.
* it provides the basic variables to be hold by the script as well as the means to initialize, parse and run the script
* Every DSpaceRunnable that is implemented in this way should be defined in the scripts.xml config file as a bean
*/
public abstract class DSpaceRunnable implements Runnable {
/**
* The name of the script
*/
private String name;
/**
* The description of the script
*/
private String description;
/**
* The CommandLine object for the script that'll hold the information
*/
protected CommandLine commandLine;
/**
* The possible options for this script
*/
protected Options options;
/**
* The handler that deals with this script. This handler can currently either be a RestDSpaceRunnableHandler or
* a CommandlineDSpaceRunnableHandler depending from where the script is called
*/
protected DSpaceRunnableHandler handler;
@Autowired
private AuthorizeService authorizeService;
public String getName() {
return name;
}
@Required
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
@Required
public void setDescription(String description) {
this.description = description;
}
public Options getOptions() {
return options;
}
/**
* This method will take the primitive array of String objects that represent the parameters given to the String
* and it'll parse these into a CommandLine object that can be used by the script to retrieve the data
* @param args The primitive array of Strings representing the parameters
* @throws ParseException If something goes wrong
*/
private void parse(String[] args) throws ParseException {
commandLine = new DefaultParser().parse(getOptions(), args);
setup();
}
/**
* This method will call upon the {@link DSpaceRunnableHandler#printHelp(Options, String)} method with the script's
* options and name
*/
public void printHelp() {
handler.printHelp(options, name);
}
/**
* This is the run() method from the Runnable interface that we implement. This method will handle the running
* of the script and all the database modifications needed for the Process object that resulted from this script
*/
@Override
public void run() {
try {
handler.start();
internalRun();
handler.handleCompletion();
} catch (Exception e) {
handler.handleException(e);
}
}
private void setHandler(DSpaceRunnableHandler dSpaceRunnableHandler) {
this.handler = dSpaceRunnableHandler;
}
/**
* This method sets the appropriate DSpaceRunnableHandler depending on where it was ran from and it parses
* the arguments given to the script
* @param args The arguments given to the script
* @param dSpaceRunnableHandler The DSpaceRunnableHandler object that defines from where the script was ran
* @throws ParseException If something goes wrong
*/
public void initialize(String[] args, DSpaceRunnableHandler dSpaceRunnableHandler) throws ParseException {
this.setHandler(dSpaceRunnableHandler);
this.parse(args);
}
/**
* This method has to be included in every script and this will be the main execution block for the script that'll
* contain all the logic needed
* @throws Exception If something goes wrong
*/
public abstract void internalRun() throws Exception;
/**
* This method has to be included in every script and handles the setup of the script by parsing the CommandLine
* and setting the variables
* @throws ParseException If something goes wrong
*/
public abstract void setup() throws ParseException;
/**
* This method will return if the script is allowed to execute in the given context. This is by default set
* to the currentUser in the context being an admin, however this can be overwritten by each script individually
* if different rules apply
* @param context The relevant DSpace context
* @return A boolean indicating whether the script is allowed to execute or not
*/
public boolean isAllowedToExecute(Context context) {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
handler.logError("Error occured when trying to verify permissions for script: " + name);
}
return false;
}
}

View File

@@ -0,0 +1,242 @@
/**
* 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.scripts;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.dspace.content.Bitstream;
import org.dspace.content.ProcessStatus;
import org.dspace.core.ReloadableEntity;
import org.dspace.eperson.EPerson;
/**
* This class is the DB Entity representation of the Process object to be stored in the Database
*/
@Entity
@Table(name = "process")
public class Process implements ReloadableEntity<Integer> {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "process_id_seq")
@SequenceGenerator(name = "process_id_seq", sequenceName = "process_id_seq", allocationSize = 1)
@Column(name = "process_id", unique = true, nullable = false)
private Integer processId;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private EPerson ePerson;
@Column(name = "start_time")
@Temporal(TemporalType.TIMESTAMP)
private Date startTime;
@Column(name = "finished_time")
@Temporal(TemporalType.TIMESTAMP)
private Date finishedTime;
@Column(name = "script", nullable = false)
private String name;
@Column(name = "status")
@Enumerated(EnumType.STRING)
private ProcessStatus processStatus;
@Column(name = "parameters")
private String parameters;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "process2bitstream",
joinColumns = {@JoinColumn(name = "process_id")},
inverseJoinColumns = {@JoinColumn(name = "bitstream_id")}
)
private List<Bitstream> bitstreams;
@Column(name = "creation_time", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date creationTime;
protected Process() {
}
/**
* This method returns the ID that the Process holds within the Database
* @return The ID that the process holds within the database
*/
public Integer getID() {
return processId;
}
public void setProcessId(Integer processId) {
this.processId = processId;
}
/**
* This method returns an EPerson object. This EPerson object is the EPerson that initially created the process
* @return The EPerson that created the process
*/
public EPerson getEPerson() {
return ePerson;
}
public void setEPerson(EPerson ePerson) {
this.ePerson = ePerson;
}
/**
* This method returns the Start time for the Process. This reflects the time when the Process was actually started
* @return The start time for the Process
*/
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
/**
* This method returns the time that Process was finished
* @return The finished time for the Process
*/
public Date getFinishedTime() {
return finishedTime;
}
public void setFinishedTime(Date finishedTime) {
this.finishedTime = finishedTime;
}
/**
* This method returns the name of the Process. For example filter-media
* @return The name of the Process
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* This method returns a ProcessStatus value that represents the current state of the Process. These values
* can be found within the {@link ProcessStatus} enum
* @return The status of the Process
*/
public ProcessStatus getProcessStatus() {
return processStatus;
}
public void setProcessStatus(ProcessStatus processStatus) {
this.processStatus = processStatus;
}
/**
* To get the parameters, use ProcessService.getParameters() to get a parsed list of DSpaceCommandLineParameters
* This String representation is the parameter in an unparsed fashion. For example "-c test"
*/
protected String getParameters() {
return parameters;
}
public void setParameters(String parameters) {
this.parameters = parameters;
}
/**
* This method returns a list of Bitstreams that will be used or created by the Process. This list contains both
* input and output bitstreams.
* @return The Bitstreams that are used or created by the process
*/
public List<Bitstream> getBitstreams() {
return bitstreams;
}
public void setBitstreams(List<Bitstream> bitstreams) {
this.bitstreams = bitstreams;
}
public void removeBitstream(Bitstream bitstream) {
getBitstreams().remove(bitstream);
}
public void addBitstream(Bitstream bitstream) {
getBitstreams().add(bitstream);
}
public void setCreationTime(Date creationTime) {
this.creationTime = creationTime;
}
/**
* This method will return the time when the Process was created. Note that this is potentially different from
* the StartTime (for example if the Process was queued)
* @return The creation time of the Process
*/
public Date getCreationTime() {
return creationTime;
}
/**
* Return <code>true</code> if <code>other</code> is the same Process
* as this object, <code>false</code> otherwise
*
* @param other object to compare to
* @return <code>true</code> if object passed in represents the same
* collection as this object
*/
@Override
public boolean equals(Object other) {
return (other instanceof Process &&
new EqualsBuilder().append(this.getID(), ((Process) other).getID())
.append(this.getName(), ((Process) other).getName())
.append(this.getBitstreams(), ((Process) other).getBitstreams())
.append(this.getProcessStatus(), ((Process) other).getProcessStatus())
.append(this.getFinishedTime(), ((Process) other).getFinishedTime())
.append(this.getStartTime(), ((Process) other).getStartTime())
.append(this.getParameters(), ((Process) other).getParameters())
.append(this.getCreationTime(), ((Process) other).getCreationTime())
.append(this.getEPerson(), ((Process) other).getEPerson())
.isEquals());
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(this.getID())
.append(this.getName())
.append(this.getBitstreams())
.append(this.getProcessStatus())
.append(this.getFinishedTime())
.append(this.getStartTime())
.append(this.getParameters())
.append(this.getCreationTime())
.append(this.getEPerson())
.toHashCode();
}
}

View File

@@ -0,0 +1,148 @@
/**
* 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.scripts;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.content.ProcessStatus;
import org.dspace.content.dao.ProcessDAO;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson;
import org.dspace.scripts.service.ProcessService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The implementation for the {@link ProcessService} class
*/
public class ProcessServiceImpl implements ProcessService {
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ProcessService.class);
@Autowired
private ProcessDAO processDAO;
@Override
public Process create(Context context, EPerson ePerson, String scriptName,
List<DSpaceCommandLineParameter> parameters) throws SQLException {
Process process = new Process();
process.setEPerson(ePerson);
process.setName(scriptName);
process.setParameters(DSpaceCommandLineParameter.concatenate(parameters));
process.setCreationTime(new Date());
Process createdProcess = processDAO.create(context, process);
log.info(LogManager.getHeader(context, "process_create",
"Process has been created for eperson with email " + ePerson.getEmail()
+ " with ID " + createdProcess.getID() + " and scriptName " +
scriptName + " and parameters " + parameters));
return createdProcess;
}
@Override
public Process find(Context context, int processId) throws SQLException {
return processDAO.findByID(context, Process.class, processId);
}
@Override
public List<Process> findAll(Context context) throws SQLException {
return processDAO.findAll(context, Process.class);
}
@Override
public List<Process> findAll(Context context, int limit, int offset) throws SQLException {
return processDAO.findAll(context, limit, offset);
}
@Override
public List<Process> findAllSortByScript(Context context) throws SQLException {
return processDAO.findAllSortByScript(context);
}
@Override
public List<Process> findAllSortByStartTime(Context context) throws SQLException {
List<Process> processes = findAll(context);
Comparator<Process> comparing = Comparator
.comparing(Process::getStartTime, Comparator.nullsLast(Comparator.naturalOrder()));
comparing = comparing.thenComparing(Process::getID);
processes.sort(comparing);
return processes;
}
@Override
public void start(Context context, Process process) throws SQLException {
process.setProcessStatus(ProcessStatus.RUNNING);
process.setStartTime(new Date());
update(context, process);
log.info(LogManager.getHeader(context, "process_start", "Process with ID " + process.getID()
+ " and name " + process.getName() + " has started"));
}
@Override
public void fail(Context context, Process process) throws SQLException {
process.setProcessStatus(ProcessStatus.FAILED);
process.setFinishedTime(new Date());
update(context, process);
log.info(LogManager.getHeader(context, "process_fail", "Process with ID " + process.getID()
+ " and name " + process.getName() + " has failed"));
}
@Override
public void complete(Context context, Process process) throws SQLException {
process.setProcessStatus(ProcessStatus.COMPLETED);
process.setFinishedTime(new Date());
update(context, process);
log.info(LogManager.getHeader(context, "process_complete", "Process with ID " + process.getID()
+ " and name " + process.getName() + " has been completed"));
}
@Override
public void delete(Context context, Process process) throws SQLException {
processDAO.delete(context, process);
log.info(LogManager.getHeader(context, "process_delete", "Process with ID " + process.getID()
+ " and name " + process.getName() + " has been deleted"));
}
@Override
public void update(Context context, Process process) throws SQLException {
processDAO.save(context, process);
}
@Override
public List<DSpaceCommandLineParameter> getParameters(Process process) {
if (StringUtils.isBlank(process.getParameters())) {
return Collections.emptyList();
}
String[] parameterArray = process.getParameters().split(Pattern.quote(DSpaceCommandLineParameter.SEPARATOR));
List<DSpaceCommandLineParameter> parameterList = new ArrayList<>();
for (String parameter : parameterArray) {
parameterList.add(new DSpaceCommandLineParameter(parameter));
}
return parameterList;
}
public int countTotal(Context context) throws SQLException {
return processDAO.countRows(context);
}
}

View File

@@ -0,0 +1,39 @@
/**
* 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.scripts;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.dspace.core.Context;
import org.dspace.scripts.service.ScriptService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The implementation for the {@link ScriptService}
*/
public class ScriptServiceImpl implements ScriptService {
@Autowired
private List<DSpaceRunnable> dSpaceRunnables;
@Override
public DSpaceRunnable getScriptForName(String name) {
return dSpaceRunnables.stream()
.filter(dSpaceRunnable -> StringUtils.equalsIgnoreCase(dSpaceRunnable.getName(), name))
.findFirst()
.orElse(null);
}
@Override
public List<DSpaceRunnable> getDSpaceRunnables(Context context) {
return dSpaceRunnables.stream().filter(
dSpaceRunnable -> dSpaceRunnable.isAllowedToExecute(context)).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.scripts.factory;
import org.dspace.scripts.service.ProcessService;
import org.dspace.scripts.service.ScriptService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Abstract factory to get services for the Script workload, use ScriptServiceFactory.getInstance() to retrieve an
* implementation
*
*/
public abstract class ScriptServiceFactory {
/**
* This method will return an instance of the ScriptService
* @return An instance of the ScriptService
*/
public abstract ScriptService getScriptService();
/**
* This method will return an instance of the ProcessService
* @return An instance of the ProcessService
*/
public abstract ProcessService getProcessService();
/**
* Use this method to retrieve an implementation of the ScriptServiceFactory to use to retrieve the different beans
* @return An implementation of the ScriptServiceFactory
*/
public static ScriptServiceFactory getInstance() {
return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("scriptServiceFactory", ScriptServiceFactory.class);
}
}

View File

@@ -0,0 +1,35 @@
/**
* 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.scripts.factory.impl;
import org.dspace.scripts.factory.ScriptServiceFactory;
import org.dspace.scripts.service.ProcessService;
import org.dspace.scripts.service.ScriptService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The implementation for the {@link ScriptServiceFactory}
*/
public class ScriptServiceFactoryImpl extends ScriptServiceFactory {
@Autowired(required = true)
private ScriptService scriptService;
@Autowired(required = true)
private ProcessService processService;
@Override
public ScriptService getScriptService() {
return scriptService;
}
@Override
public ProcessService getProcessService() {
return processService;
}
}

View File

@@ -0,0 +1,81 @@
/**
* 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.scripts.handler;
import java.sql.SQLException;
import org.apache.commons.cli.Options;
/**
* This is an interface meant to be implemented by any DSpaceRunnableHandler to specify specific execution methods
* of the script depending on where it was called from
*/
public interface DSpaceRunnableHandler {
/**
* This method handles the start of the script
* @throws SQLException If something goes wrong
*/
public void start() throws SQLException;
/**
* This method handles the completion of the script
* @throws SQLException If something goes wrong
*/
public void handleCompletion() throws SQLException;
/**
* This method handles an exception thrown by the script
* @param e The exception thrown by the script
*/
public void handleException(Exception e);
/**
* This method handles an exception thrown by the script
* @param message The String message for the exception thrown by the script
*/
public void handleException(String message);
/**
* This method handles an exception thrown by the script
* @param message The String message for the exception thrown by the script
* @param e The exception thrown by the script
*/
public void handleException(String message, Exception e);
/**
* This method will perform the debug logging of the message given
* @param message The message to be logged as debug
*/
public void logDebug(String message);
/**
* This method will perform the info logging of the message given
* @param message The message to be logged as info
*/
public void logInfo(String message);
/**
* This method will perform the warning logging of the message given
* @param message The message to be logged as warning
*/
public void logWarning(String message);
/**
* This method will perform the error logging of the message given
* @param message The message to be logged as an error
*/
public void logError(String message);
/**
* This method will print the help for the options and name
* @param options The options for the script
* @param name The name of the script
*/
public void printHelp(Options options, String name);
}

View File

@@ -0,0 +1,87 @@
/**
* 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.scripts.handler.impl;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.logging.log4j.Logger;
import org.dspace.scripts.handler.DSpaceRunnableHandler;
/**
* This is an implementation for the CommandLineDSpaceRunnables which means that these implementations
* are used by DSpaceRunnables which are called from the CommandLine
*/
public class CommandLineDSpaceRunnableHandler implements DSpaceRunnableHandler {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(CommandLineDSpaceRunnableHandler.class);
@Override
public void start() {
System.out.println("The script has started");
}
@Override
public void handleCompletion() {
System.out.println("The script has completed");
}
@Override
public void handleException(Exception e) {
handleException(null, e);
}
@Override
public void handleException(String message) {
handleException(message, null);
}
@Override
public void handleException(String message, Exception e) {
if (message != null) {
System.err.println(message);
log.error(message);
}
if (e != null) {
e.printStackTrace();
log.error(e.getMessage(), e);
}
System.exit(1);
}
@Override
public void logDebug(String message) {
log.debug(message);
}
@Override
public void logInfo(String message) {
System.out.println(message);
log.info(message);
}
@Override
public void logWarning(String message) {
System.out.println(message);
log.warn(message);
}
@Override
public void logError(String message) {
System.err.println(message);
log.error(message);
}
@Override
public void printHelp(Options options, String name) {
if (options != null) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp(name, options);
}
}
}

View File

@@ -0,0 +1,139 @@
/**
* 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.scripts.service;
import java.sql.SQLException;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.scripts.DSpaceCommandLineParameter;
import org.dspace.scripts.Process;
/**
* An interface for the ProcessService with methods regarding the Process workload
*/
public interface ProcessService {
/**
* This method will create a Process object in the database
* @param context The relevant DSpace context
* @param ePerson The ePerson for which this process will be created on
* @param scriptName The script name to be used for the process
* @param parameters The parameters to be used for the process
* @return The created process
* @throws SQLException If something goes wrong
*/
public Process create(Context context, EPerson ePerson, String scriptName,
List<DSpaceCommandLineParameter> parameters) throws SQLException;
/**
* This method will retrieve a Process object from the Database with the given ID
* @param context The relevant DSpace context
* @param processId The process id on which we'll search for in the database
* @return The process that holds the given process id
* @throws SQLException If something goes wrong
*/
public Process find(Context context, int processId) throws SQLException;
/**
* Returns a list of all Process objects in the database
* @param context The relevant DSpace context
* @return The list of all Process objects in the Database
* @throws SQLException If something goes wrong
*/
public List<Process> findAll(Context context) throws SQLException;
/**
* Returns a list of all Process objects in the database
* @param context The relevant DSpace context
* @param limit The limit for the amount of Processes returned
* @param offset The offset for the Processes to be returned
* @return The list of all Process objects in the Database
* @throws SQLException If something goes wrong
*/
public List<Process> findAll(Context context, int limit, int offset) throws SQLException;
/**
* Returns a list of all Process objects in the database sorted by script name
* @param context The relevant DSpace context
* @return The list of all Process objects in the database sorted by script name
* @throws SQLException If something goes wrong
*/
public List<Process> findAllSortByScript(Context context) throws SQLException;
/**
* Returns a list of all Process objects in the database sorted by start time
* The most recent one will be shown first
* @param context The relevant DSpace context
* @return The list of all Process objects sorted by start time
* @throws SQLException If something goes wrong
*/
public List<Process> findAllSortByStartTime(Context context) throws SQLException;
/**
* This method will perform the logic needed to update the Process object in the database to represent a
* started state. A started state refers to {@link org.dspace.content.ProcessStatus#RUNNING}
* @param context The relevant DSpace context
* @param process The Process object to be updated
* @throws SQLException If something goes wrong
*/
public void start(Context context, Process process) throws SQLException;
/**
* This method will perform the logic needed to update the Process object in the database to represent
* a failed state
* @param context The relevant DSpace context
* @param process The Process object to be updated
* @throws SQLException If something goes wrong
*/
public void fail(Context context, Process process) throws SQLException;
/**
* This method will perform the logic needed to update the Process object in the database to represent
* a complete state
* @param context The relevant DSpace context
* @param process The Process object to be updated
* @throws SQLException If something goes wrong
*/
public void complete(Context context, Process process) throws SQLException;
/**
* This method will delete the given Process object from the database
* @param context The relevant DSpace context
* @param process The Process object to be deleted
* @throws SQLException If something goes wrong
*/
public void delete(Context context, Process process) throws SQLException;
/**
* This method will be used to update the given Process object in the database
* @param context The relevant DSpace context
* @param process The Process object to be updated
* @throws SQLException If something goes wrong
*/
public void update(Context context, Process process) throws SQLException;
/**
* This method will retrieve the list of parameters from the Process in its String format and it will parse
* these parameters to a list of {@link DSpaceCommandLineParameter} objects for better usability throughout DSpace
* @param process The Process object for which we'll return the parameters
* @return The list of parsed parameters from the Process object
*/
public List<DSpaceCommandLineParameter> getParameters(Process process);
/**
* Returns the total amount of Process objects in the dataase
* @param context The relevant DSpace context
* @return An integer that describes the amount of Process objects in the database
* @throws SQLException If something goes wrong
*/
int countTotal(Context context) throws SQLException;
}

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.scripts.service;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.scripts.DSpaceRunnable;
/**
* This service will deal with logic to handle DSpaceRunnable objects
*/
public interface ScriptService {
/**
* This method will return the DSpaceRunnable that has the name that's equal to the name given in the parameters
* @param name The name that the script has to match
* @return The matching DSpaceRunnable script
*/
DSpaceRunnable getScriptForName(String name);
/**
* This method will return a list of DSpaceRunnable objects for which the given Context is authorized to use them
* @param context The relevant DSpace context
* @return The list of accessible DSpaceRunnable scripts for this context
*/
List<DSpaceRunnable> getDSpaceRunnables(Context context);
}

View File

@@ -189,16 +189,19 @@ ALTER TABLE item2bundle add primary key (item_id,bundle_id);
--Migrate Bundle2Bitsteam
ALTER TABLE bundle2bitstream ALTER COLUMN bundle_id rename to bundle_legacy_id;
ALTER TABLE bundle2bitstream ALTER COLUMN bitstream_id rename to bitstream_legacy_id;
ALTER TABLE bundle2bitstream ALTER COLUMN bitstream_order rename to bitstream_order_legacy;
ALTER TABLE bundle2bitstream ADD COLUMN bundle_id UUID NOT NULL;
ALTER TABLE bundle2bitstream ADD CONSTRAINT bundle2bitstream_bundle_id_fk FOREIGN KEY (bundle_id) REFERENCES bundle;
ALTER TABLE bundle2bitstream ADD COLUMN bitstream_id UUID NOT NULL;
ALTER TABLE bundle2bitstream ADD CONSTRAINT bundle2bitstream_bitstream_id_fk FOREIGN KEY (bitstream_id) REFERENCES bitstream;
ALTER TABLE bundle2bitstream ADD COLUMN bitstream_order INTEGER NOT NULL;
UPDATE bundle2bitstream SET bundle_id = (SELECT bundle.uuid FROM bundle WHERE bundle2bitstream.bundle_legacy_id = bundle.bundle_id);
UPDATE bundle2bitstream SET bitstream_id = (SELECT bitstream.uuid FROM bitstream WHERE bundle2bitstream.bitstream_legacy_id = bitstream.bitstream_id);
ALTER TABLE bundle2bitstream DROP COLUMN bundle_legacy_id;
ALTER TABLE bundle2bitstream DROP COLUMN bitstream_legacy_id;
ALTER TABLE bundle2bitstream DROP COLUMN bitstream_order_legacy;
ALTER TABLE bundle2bitstream DROP COLUMN id;
ALTER TABLE bundle2bitstream add primary key (bitstream_id,bundle_id);
ALTER TABLE bundle2bitstream add primary key (bitstream_id,bundle_id,bitstream_order);
-- Migrate item

View File

@@ -0,0 +1,40 @@
--
-- 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/
--
-- ===============================================================
-- WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
--
-- DO NOT MANUALLY RUN THIS DATABASE MIGRATION. IT WILL BE EXECUTED
-- AUTOMATICALLY (IF NEEDED) BY "FLYWAY" WHEN YOU STARTUP DSPACE.
-- http://flywaydb.org/
-- ===============================================================
CREATE SEQUENCE process_id_seq;
CREATE TABLE process
(
process_id INTEGER NOT NULL PRIMARY KEY,
user_id UUID NOT NULL,
start_time TIMESTAMP,
finished_time TIMESTAMP,
creation_time TIMESTAMP NOT NULL,
script VARCHAR(256) NOT NULL,
status VARCHAR(32),
parameters VARCHAR(512)
);
CREATE TABLE process2bitstream
(
process_id INTEGER REFERENCES process(process_id),
bitstream_id UUID REFERENCES bitstream(uuid),
CONSTRAINT PK_process2bitstream PRIMARY KEY (process_id, bitstream_id)
);
CREATE INDEX process_user_id_idx ON process(user_id);
CREATE INDEX process_status_idx ON process(status);
CREATE INDEX process_name_idx on process(script);
CREATE INDEX process_start_time_idx on process(start_time);

View File

@@ -0,0 +1,40 @@
--
-- 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/
--
-- ===============================================================
-- WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
--
-- DO NOT MANUALLY RUN THIS DATABASE MIGRATION. IT WILL BE EXECUTED
-- AUTOMATICALLY (IF NEEDED) BY "FLYWAY" WHEN YOU STARTUP DSPACE.
-- http://flywaydb.org/
-- ===============================================================
CREATE SEQUENCE process_id_seq;
CREATE TABLE process
(
process_id INTEGER NOT NULL PRIMARY KEY,
user_id RAW(16) NOT NULL,
start_time TIMESTAMP,
finished_time TIMESTAMP,
creation_time TIMESTAMP NOT NULL,
script VARCHAR(256) NOT NULL,
status VARCHAR(32),
parameters VARCHAR(512)
);
CREATE TABLE process2bitstream
(
process_id INTEGER REFERENCES process(process_id),
bitstream_id RAW(16) REFERENCES bitstream(uuid),
CONSTRAINT PK_process2bitstream PRIMARY KEY (process_id, bitstream_id)
);
CREATE INDEX process_user_id_idx ON process(user_id);
CREATE INDEX process_status_idx ON process(status);
CREATE INDEX process_name_idx on process(script);
CREATE INDEX process_start_time_idx on process(start_time);

View File

@@ -0,0 +1,40 @@
--
-- 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/
--
-- ===============================================================
-- WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
--
-- DO NOT MANUALLY RUN THIS DATABASE MIGRATION. IT WILL BE EXECUTED
-- AUTOMATICALLY (IF NEEDED) BY "FLYWAY" WHEN YOU STARTUP DSPACE.
-- http://flywaydb.org/
-- ===============================================================
CREATE SEQUENCE process_id_seq;
CREATE TABLE process
(
process_id INTEGER NOT NULL PRIMARY KEY,
user_id UUID NOT NULL,
start_time TIMESTAMP,
finished_time TIMESTAMP,
creation_time TIMESTAMP NOT NULL,
script VARCHAR(256) NOT NULL,
status VARCHAR(32),
parameters VARCHAR(512)
);
CREATE TABLE process2bitstream
(
process_id INTEGER REFERENCES process(process_id),
bitstream_id UUID REFERENCES bitstream(uuid),
CONSTRAINT PK_process2bitstream PRIMARY KEY (process_id, bitstream_id)
);
CREATE INDEX process_user_id_idx ON process(user_id);
CREATE INDEX process_status_idx ON process(status);
CREATE INDEX process_name_idx on process(script);
CREATE INDEX process_start_time_idx on process(start_time);

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true">
<bean class="org.dspace.external.service.impl.ExternalDataServiceImpl"/>
<bean class="org.dspace.external.provider.impl.MockDataProvider" init-method="init">
<property name="sourceIdentifier" value="mock"/>
</bean>
</beans>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="indexClient" class="org.dspace.discovery.IndexClient" scope="prototype">
<property name="name" value="index-discovery"/>
<property name="description" value="Update Discovery Solr Search Index"/>
</bean>
<bean id="MockScript" class="org.dspace.scripts.impl.MockDSpaceRunnableScript" scope="prototype">
<property name="name" value="mock-script" />
<property name="description" value="Mocking a script for testing purposes" />
</bean>
</beans>

View File

@@ -8,6 +8,7 @@
package org.dspace.content;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@@ -49,8 +50,10 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest {
protected InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService();
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
Item item;
Item authorItem;
Item leftItem;
Item rightItem;
Relationship relationship;
RelationshipType isAuthorOfPublicationRelationshipType;
/**
* This method will be run before every test as per @Before. It will
@@ -68,11 +71,11 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest {
Community community = communityService.create(null, context);
Collection col = collectionService.create(context, community);
WorkspaceItem is = workspaceItemService.create(context, col, false);
WorkspaceItem authorIs = workspaceItemService.create(context, col, false);
WorkspaceItem leftIs = workspaceItemService.create(context, col, false);
WorkspaceItem rightIs = workspaceItemService.create(context, col, false);
item = installItemService.installItem(context, is);
authorItem = installItemService.installItem(context, authorIs);
leftItem = installItemService.installItem(context, leftIs);
rightItem = installItemService.installItem(context, rightIs);
context.restoreAuthSystemState();
} catch (AuthorizeException ex) {
log.error("Authorization Error in init", ex);
@@ -81,7 +84,6 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest {
log.error("SQL Error in init", ex);
fail("SQL Error in init: " + ex.getMessage());
}
}
/**
@@ -98,34 +100,66 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest {
super.destroy();
}
@Test
public void testGetRelationshipMetadata() throws Exception {
/**
* Common function to convert leftItem to a publication item, convert rightItem to an author item,
* and relating them to each other stored in the relationship field
*/
private void initPublicationAuthor() throws SQLException, AuthorizeException {
context.turnOffAuthorisationSystem();
itemService.addMetadata(context, item, "relationship", "type", null, null, "Publication");
itemService.addMetadata(context, authorItem, "relationship", "type", null, null, "Author");
itemService.addMetadata(context, authorItem, "person", "familyName", null, null, "familyName");
itemService.addMetadata(context, authorItem, "person", "givenName", null, null, "firstName");
itemService.addMetadata(context, leftItem, "relationship", "type", null, null, "Publication");
itemService.addMetadata(context, rightItem, "relationship", "type", null, null, "Author");
itemService.addMetadata(context, rightItem, "person", "familyName", null, null, "familyName");
itemService.addMetadata(context, rightItem, "person", "givenName", null, null, "firstName");
EntityType publicationEntityType = entityTypeService.create(context, "Publication");
EntityType authorEntityType = entityTypeService.create(context, "Author");
RelationshipType isAuthorOfPublication = relationshipTypeService
.create(context, publicationEntityType, authorEntityType, "isAuthorOfPublication", "isPublicationOfAuthor",
null, null, null, null);
Relationship relationship = relationshipService.create(context, item, authorItem, isAuthorOfPublication, 0, 0);
isAuthorOfPublicationRelationshipType = relationshipTypeService
.create(context, publicationEntityType, authorEntityType,
"isAuthorOfPublication", "isPublicationOfAuthor",
null, null, null, null);
relationship = relationshipService.create(context, leftItem, rightItem,
isAuthorOfPublicationRelationshipType, 0, 0);
context.restoreAuthSystemState();
}
List<MetadataValue> authorList = itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY);
/**
* Common function to convert leftItem to a journal issue item, convert rightItem to a journal volume item,
* and relating them to each other stored in the relationship field
*/
private void initJournalVolumeIssue() throws SQLException, AuthorizeException {
context.turnOffAuthorisationSystem();
itemService.addMetadata(context, leftItem, "relationship", "type", null, null, "JournalIssue");
itemService.addMetadata(context, rightItem, "relationship", "type", null, null, "JournalVolume");
itemService.addMetadata(context, leftItem, "publicationissue", "issueNumber", null, null, "2");
itemService.addMetadata(context, rightItem, "publicationvolume", "volumeNumber", null, null, "30");
EntityType journalIssueEntityType = entityTypeService.create(context, "JournalIssue");
EntityType publicationVolumeEntityType = entityTypeService.create(context, "JournalVolume");
RelationshipType isIssueOfVolume = relationshipTypeService
.create(context, journalIssueEntityType, publicationVolumeEntityType,
"isJournalVolumeOfIssue", "isIssueOfJournalVolume",
null, null, null, null);
relationship = relationshipService.create(context, leftItem, rightItem, isIssueOfVolume, 0, 0);
context.restoreAuthSystemState();
}
@Test
public void testGetAuthorRelationshipMetadata() throws SQLException, AuthorizeException {
initPublicationAuthor();
//leftItem is the publication
//verify the dc.contributor.author virtual metadata
List<MetadataValue> authorList = itemService.getMetadata(leftItem, "dc", "contributor", "author", Item.ANY);
assertThat(authorList.size(), equalTo(1));
assertThat(authorList.get(0).getValue(), equalTo("familyName, firstName"));
//verify the relation.isAuthorOfPublication virtual metadata
List<MetadataValue> relationshipMetadataList = itemService
.getMetadata(item, "relation", "isAuthorOfPublication", null, Item.ANY);
.getMetadata(leftItem, MetadataSchemaEnum.RELATION.getName(), "isAuthorOfPublication", null, Item.ANY);
assertThat(relationshipMetadataList.size(), equalTo(1));
assertThat(relationshipMetadataList.get(0).getValue(), equalTo(String.valueOf(authorItem.getID())));
assertThat(relationshipMetadataList.get(0).getValue(), equalTo(String.valueOf(rightItem.getID())));
List<RelationshipMetadataValue> list = relationshipMetadataService.getRelationshipMetadata(item, true);
//request the virtual metadata of the publication only
List<RelationshipMetadataValue> list = relationshipMetadataService.getRelationshipMetadata(leftItem, true);
assertThat(list.size(), equalTo(2));
assertThat(list.get(0).getValue(), equalTo("familyName, firstName"));
assertThat(list.get(0).getMetadataField().getMetadataSchema().getName(), equalTo("dc"));
@@ -133,10 +167,243 @@ public class RelationshipMetadataServiceTest extends AbstractUnitTest {
assertThat(list.get(0).getMetadataField().getQualifier(), equalTo("author"));
assertThat(list.get(0).getAuthority(), equalTo("virtual::" + relationship.getID()));
assertThat(list.get(1).getValue(), equalTo(String.valueOf(authorItem.getID())));
assertThat(list.get(1).getMetadataField().getMetadataSchema().getName(), equalTo("relation"));
assertThat(list.get(1).getValue(), equalTo(String.valueOf(rightItem.getID())));
assertThat(list.get(1).getMetadataField().getMetadataSchema().getName(),
equalTo(MetadataSchemaEnum.RELATION.getName()));
assertThat(list.get(1).getMetadataField().getElement(), equalTo("isAuthorOfPublication"));
assertThat(list.get(1).getAuthority(), equalTo("virtual::" + relationship.getID()));
}
@Test
public void testDeleteAuthorRelationshipCopyToLeftItem() throws Exception {
initPublicationAuthor();
context.turnOffAuthorisationSystem();
//delete the relationship, copying the virtual metadata to actual metadata on the leftItem
//leftItem is the publication
relationshipService.delete(context, relationship, true, false);
context.restoreAuthSystemState();
//verify the dc.contributor.author actual metadata
List<MetadataValue> authorList = itemService.getMetadata(leftItem, "dc", "contributor", "author", Item.ANY);
assertThat(authorList.size(), equalTo(1));
assertThat(authorList.get(0).getValue(), equalTo("familyName, firstName"));
assertThat(authorList.get(0).getMetadataField().getMetadataSchema().getName(), equalTo("dc"));
assertThat(authorList.get(0).getMetadataField().getElement(), equalTo("contributor"));
assertThat(authorList.get(0).getMetadataField().getQualifier(), equalTo("author"));
assertNull(authorList.get(0).getAuthority());
//verify there's no relation.isAuthorOfPublication actual metadata
List<MetadataValue> relationshipMetadataList = itemService
.getMetadata(leftItem, MetadataSchemaEnum.RELATION.getName(), "isAuthorOfPublication", null, Item.ANY);
assertThat(relationshipMetadataList.size(), equalTo(0));
//request the virtual metadata of the publication only
List<RelationshipMetadataValue> list = relationshipMetadataService.getRelationshipMetadata(leftItem, true);
assertThat(list.size(), equalTo(0));
}
@Test
public void testAuthorDeleteRelationshipCopyToRightItem() throws Exception {
initPublicationAuthor();
context.turnOffAuthorisationSystem();
//delete the relationship, copying the virtual metadata to actual metadata on the rightItem
//rightItem is the author
relationshipService.delete(context, relationship, false, true);
context.restoreAuthSystemState();
//verify there's no dc.contributor.author actual metadata on the publication
List<MetadataValue> authorList = itemService.getMetadata(leftItem, "dc", "contributor", "author", Item.ANY);
assertThat(authorList.size(), equalTo(0));
//verify there's no relation.isAuthorOfPublication actual metadata on the publication
List<MetadataValue> relationshipMetadataList = itemService
.getMetadata(leftItem, MetadataSchemaEnum.RELATION.getName(), "isAuthorOfPublication", null, Item.ANY);
assertThat(relationshipMetadataList.size(), equalTo(0));
}
@Test
public void testDeleteAuthorRelationshipCopyToBothItems() throws Exception {
initPublicationAuthor();
context.turnOffAuthorisationSystem();
//delete the relationship, copying the virtual metadata to actual metadata on the both items
relationshipService.delete(context, relationship, true, true);
context.restoreAuthSystemState();
//verify the dc.contributor.author actual metadata
List<MetadataValue> authorList = itemService.getMetadata(leftItem, "dc", "contributor", "author", Item.ANY);
assertThat(authorList.size(), equalTo(1));
assertThat(authorList.get(0).getValue(), equalTo("familyName, firstName"));
assertThat(authorList.get(0).getMetadataField().getMetadataSchema().getName(), equalTo("dc"));
assertThat(authorList.get(0).getMetadataField().getElement(), equalTo("contributor"));
assertThat(authorList.get(0).getMetadataField().getQualifier(), equalTo("author"));
assertNull(authorList.get(0).getAuthority());
//verify there's no relation.isAuthorOfPublication actual metadata
List<MetadataValue> relationshipMetadataList = itemService
.getMetadata(leftItem, MetadataSchemaEnum.RELATION.getName(), "isAuthorOfPublication", null, Item.ANY);
assertThat(relationshipMetadataList.size(), equalTo(0));
}
@Test
public void testGetJournalRelationshipMetadata() throws SQLException, AuthorizeException {
initJournalVolumeIssue();
//leftItem is the journal issue item
//verify the publicationvolume.volumeNumber virtual metadata
List<MetadataValue> volumeList =
itemService.getMetadata(leftItem, "publicationvolume", "volumeNumber", null, Item.ANY);
assertThat(volumeList.size(), equalTo(1));
assertThat(volumeList.get(0).getValue(), equalTo("30"));
//rightItem is the journal volume item
//verify the publicationissue.issueNumber virtual metadata
List<MetadataValue> issueList =
itemService.getMetadata(rightItem, "publicationissue", "issueNumber", null, Item.ANY);
assertThat(issueList.size(), equalTo(1));
assertThat(issueList.get(0).getValue(), equalTo("2"));
//request the virtual metadata of the journal issue
List<RelationshipMetadataValue> issueRelList =
relationshipMetadataService.getRelationshipMetadata(leftItem, true);
assertThat(issueRelList.size(), equalTo(2));
assertThat(issueRelList.get(0).getValue(), equalTo("30"));
assertThat(issueRelList.get(0).getMetadataField().getMetadataSchema().getName(), equalTo("publicationvolume"));
assertThat(issueRelList.get(0).getMetadataField().getElement(), equalTo("volumeNumber"));
assertThat(issueRelList.get(0).getMetadataField().getQualifier(), equalTo(null));
assertThat(issueRelList.get(0).getAuthority(), equalTo("virtual::" + relationship.getID()));
assertThat(issueRelList.get(1).getValue(), equalTo(String.valueOf(rightItem.getID())));
assertThat(issueRelList.get(1).getMetadataField().getMetadataSchema().getName(),
equalTo(MetadataSchemaEnum.RELATION.getName()));
assertThat(issueRelList.get(1).getMetadataField().getElement(), equalTo("isJournalVolumeOfIssue"));
assertThat(issueRelList.get(1).getAuthority(), equalTo("virtual::" + relationship.getID()));
//request the virtual metadata of the journal volume
List<RelationshipMetadataValue> volumeRelList =
relationshipMetadataService.getRelationshipMetadata(rightItem, true);
assertThat(volumeRelList.size(), equalTo(2));
assertThat(volumeRelList.get(0).getValue(), equalTo("2"));
assertThat(volumeRelList.get(0).getMetadataField().getMetadataSchema().getName(), equalTo("publicationissue"));
assertThat(volumeRelList.get(0).getMetadataField().getElement(), equalTo("issueNumber"));
assertThat(volumeRelList.get(0).getMetadataField().getQualifier(), equalTo(null));
assertThat(volumeRelList.get(0).getAuthority(), equalTo("virtual::" + relationship.getID()));
assertThat(volumeRelList.get(1).getValue(), equalTo(String.valueOf(leftItem.getID())));
assertThat(volumeRelList.get(1).getMetadataField().getMetadataSchema().getName(),
equalTo(MetadataSchemaEnum.RELATION.getName()));
assertThat(volumeRelList.get(1).getMetadataField().getElement(), equalTo("isIssueOfJournalVolume"));
assertThat(volumeRelList.get(1).getAuthority(), equalTo("virtual::" + relationship.getID()));
}
@Test
public void testDeleteJournalRelationshipCopyToLeftItem() throws SQLException, AuthorizeException {
initJournalVolumeIssue();
context.turnOffAuthorisationSystem();
//leftItem is the journal issue item
relationshipService.delete(context, relationship, true, false);
context.restoreAuthSystemState();
//verify the left item's publicationvolume.volumeNumber actual metadata
List<MetadataValue> volumeList =
itemService.getMetadata(leftItem, "publicationvolume", "volumeNumber", null, Item.ANY);
assertThat(volumeList.size(), equalTo(1));
assertThat(volumeList.get(0).getValue(), equalTo("30"));
//verify the right item doesn't contain the actual metadata
List<MetadataValue> issueList =
itemService.getMetadata(rightItem, "publicationissue", "issueNumber", null, Item.ANY);
assertThat(issueList.size(), equalTo(0));
}
@Test
public void testJournalDeleteRelationshipCopyToRightItem() throws SQLException, AuthorizeException {
initJournalVolumeIssue();
context.turnOffAuthorisationSystem();
//rightItem is the journal volume item
relationshipService.delete(context, relationship, false, true);
context.restoreAuthSystemState();
//verify the left item doesn't contain the publicationvolume.volumeNumber actual metadata
List<MetadataValue> volumeList =
itemService.getMetadata(leftItem, "publicationvolume", "volumeNumber", null, Item.ANY);
assertThat(volumeList.size(), equalTo(0));
//verify the right item's publicationissue.issueNumber actual metadata
List<MetadataValue> issueList =
itemService.getMetadata(rightItem, "publicationissue", "issueNumber", null, Item.ANY);
assertThat(issueList.size(), equalTo(1));
assertThat(issueList.get(0).getValue(), equalTo("2"));
}
@Test
public void testDeleteJournalRelationshipCopyToBothItems() throws SQLException, AuthorizeException {
initJournalVolumeIssue();
context.turnOffAuthorisationSystem();
//leftItem is the journal issue item
//rightItem is the journal volume item
relationshipService.delete(context, relationship, true, true);
context.restoreAuthSystemState();
//verify the left item's publicationvolume.volumeNumber actual metadata
List<MetadataValue> volumeList =
itemService.getMetadata(leftItem, "publicationvolume", "volumeNumber", null, Item.ANY);
assertThat(volumeList.size(), equalTo(1));
assertThat(volumeList.get(0).getValue(), equalTo("30"));
//verify the right item's publicationissue.issueNumber actual metadata
List<MetadataValue> issueList =
itemService.getMetadata(rightItem, "publicationissue", "issueNumber", null, Item.ANY);
assertThat(issueList.size(), equalTo(1));
assertThat(issueList.get(0).getValue(), equalTo("2"));
}
@Test
public void testGetNextRightPlace() throws Exception {
assertThat(relationshipService.findNextRightPlaceByRightItem(context, rightItem), equalTo(0));
initPublicationAuthor();
assertThat(relationshipService.findNextRightPlaceByRightItem(context, rightItem), equalTo(1));
context.turnOffAuthorisationSystem();
Community community = communityService.create(null, context);
Collection col = collectionService.create(context, community);
WorkspaceItem is = workspaceItemService.create(context, col, false);
Item secondItem = installItemService.installItem(context, is);
itemService.addMetadata(context, secondItem, "relationship", "type", null, null, "Publication");
Relationship secondRelationship = relationshipService.create(context, secondItem, rightItem,
isAuthorOfPublicationRelationshipType, 0, 0);
context.restoreAuthSystemState();
assertThat(relationshipService.findNextRightPlaceByRightItem(context, rightItem), equalTo(2));
}
@Test
public void testGetNextLeftPlace() throws Exception {
assertThat(relationshipService.findNextLeftPlaceByLeftItem(context, leftItem), equalTo(0));
initPublicationAuthor();
assertThat(relationshipService.findNextLeftPlaceByLeftItem(context, leftItem), equalTo(1));
context.turnOffAuthorisationSystem();
Community community = communityService.create(null, context);
Collection col = collectionService.create(context, community);
WorkspaceItem is = workspaceItemService.create(context, col, false);
Item secondAuthor = installItemService.installItem(context, is);
itemService.addMetadata(context, secondAuthor, "relationship", "type", null, null, "Author");
itemService.addMetadata(context, secondAuthor, "person", "familyName", null, null, "familyName");
itemService.addMetadata(context, secondAuthor, "person", "givenName", null, null, "firstName");
Relationship secondRelationship = relationshipService.create(context, leftItem, secondAuthor,
isAuthorOfPublicationRelationshipType, 0, 0);
context.restoreAuthSystemState();
assertThat(relationshipService.findNextLeftPlaceByLeftItem(context, leftItem), equalTo(2));
}
}

View File

@@ -0,0 +1,428 @@
/**
* 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.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.sql.SQLException;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.dspace.AbstractUnitTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.EntityTypeService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataValueService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.RelationshipTypeService;
import org.dspace.content.service.WorkspaceItemService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class RelationshipServiceImplPlaceTest extends AbstractUnitTest {
private static final Logger log = org.apache.logging.log4j.LogManager
.getLogger(RelationshipServiceImplPlaceTest.class);
protected RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService();
protected RelationshipTypeService relationshipTypeService = ContentServiceFactory.getInstance()
.getRelationshipTypeService();
protected EntityTypeService entityTypeService = ContentServiceFactory.getInstance().getEntityTypeService();
protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
protected InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService();
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
protected MetadataValueService metadataValueService = ContentServiceFactory.getInstance().getMetadataValueService();
Community community;
Collection col;
Item item;
Item authorItem;
RelationshipType isAuthorOfPublication;
EntityType publicationEntityType;
EntityType authorEntityType;
String authorQualifier = "author";
String contributorElement = "contributor";
String dcSchema = "dc";
/**
* This method will be run before every test as per @Before. It will
* initialize resources required for the tests.
*/
@Before
@Override
public void init() {
super.init();
try {
context.turnOffAuthorisationSystem();
community = communityService.create(null, context);
col = collectionService.create(context, community);
WorkspaceItem is = workspaceItemService.create(context, col, false);
WorkspaceItem authorIs = workspaceItemService.create(context, col, false);
item = installItemService.installItem(context, is);
itemService.addMetadata(context, item, "relationship", "type", null, null, "Publication");
authorItem = installItemService.installItem(context, authorIs);
itemService.addMetadata(context, authorItem, "relationship", "type", null, null, "Person");
itemService.addMetadata(context, authorItem, "person", "familyName", null, null, "familyName");
itemService.addMetadata(context, authorItem, "person", "givenName", null, null, "firstName");
publicationEntityType = entityTypeService.create(context, "Publication");
authorEntityType = entityTypeService.create(context, "Person");
isAuthorOfPublication = relationshipTypeService
.create(context, publicationEntityType, authorEntityType,
"isAuthorOfPublication", "isPublicationOfAuthor",
null, null, null, null);
context.restoreAuthSystemState();
} catch (AuthorizeException ex) {
log.error("Authorization Error in init", ex);
fail("Authorization Error in init: " + ex.getMessage());
} catch (SQLException ex) {
log.error("SQL Error in init", ex);
fail("SQL Error in init: " + ex.getMessage());
}
}
/**
* This method will be run after every test as per @After. It will
* clean resources initialized by the @Before methods.
*/
@After
@Override
public void destroy() {
context.abort();
super.destroy();
}
/**
* This test will test the use case of having an item to which we add some metadata. After that, we'll add a single
* relationship to this Item. We'll test whether the places are correct, in this case it'll be the two metadata
* values that we created first, has to have place 0 and 1. The Relationship that we just created needs to have
* leftPlace 2 and the metadata value resulting from that Relationship needs to also have place 2.
* Once these assertions succeed, we basically repeat said process with new metadata values and a new relationship.
* We then test if the old assertions still hold true like they should and that the new ones behave as expected
* as well.
* @throws Exception If something goes wrong
*/
@Test
public void addMetadataAndRelationshipTest() throws Exception {
context.turnOffAuthorisationSystem();
// Here we add the first set of metadata to the item
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, one");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, two");
// Here we create the first Relationship to the item
Relationship relationship = relationshipService
.create(context, item, authorItem, isAuthorOfPublication, -1, -1);
context.restoreAuthSystemState();
// The code below performs the mentioned assertions to ensure the place is correct
List<MetadataValue> list = itemService
.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertThat(list.size(), equalTo(3));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, two", null, 1, list.get(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 2, list.get(2));
assertThat(relationship.getLeftPlace(), equalTo(2));
context.turnOffAuthorisationSystem();
// This is where we add the second set of metadata values
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, three");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, four");
// Here we create an Item so that we can create another relationship with this item
WorkspaceItem authorIs = workspaceItemService.create(context, col, false);
Item secondAuthorItem = installItemService.installItem(context, authorIs);
itemService.addMetadata(context, secondAuthorItem, "relationship", "type", null, null, "Person");
itemService.addMetadata(context, secondAuthorItem, "person", "familyName", null, null, "familyNameTwo");
itemService.addMetadata(context, secondAuthorItem, "person", "givenName", null, null, "firstNameTwo");
Relationship relationshipTwo = relationshipService
.create(context, item, secondAuthorItem, isAuthorOfPublication, -1, -1);
context.restoreAuthSystemState();
// Here we retrieve the list of metadata again to perform the assertions on the places below as mentioned
list = itemService.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, two", null, 1, list.get(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 2, list.get(2));
assertThat(relationship.getLeftPlace(), equalTo(2));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 3, list.get(3));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, four", null, 4, list.get(4));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyNameTwo, firstNameTwo",
"virtual::" + relationshipTwo.getID(), 5, list.get(5));
assertThat(relationshipTwo.getLeftPlace(), equalTo(5));
}
/**
* This test is virtually the same as above, only this time we'll add Relationships with leftPlaces already set
* equal to what they HAVE to be. So in the first test addMetadataAndRelationshipTest, we didn't specify a place
* and left it up to the Service to determine it, here we provide a correct place already.
* We perform the exact same logic except that we give a proper place already to the Relationships and we
* perform the same checks
* @throws Exception If something goes wrong
*/
@Test
public void AddMetadataAndRelationshipWithSpecificPlaceTest() throws Exception {
context.turnOffAuthorisationSystem();
// Here we add the first set of metadata to the item
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, one");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, two");
// Here we create the first Relationship to the item with the specific leftPlace: 2
Relationship relationship = relationshipService.create(context, item, authorItem, isAuthorOfPublication, 2, -1);
context.restoreAuthSystemState();
// The code below performs the mentioned assertions to ensure the place is correct
List<MetadataValue> list = itemService
.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertThat(list.size(), equalTo(3));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, two", null, 1, list.get(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 2, list.get(2));
assertThat(relationship.getLeftPlace(), equalTo(2));
context.turnOffAuthorisationSystem();
// This is where we add the second set of metadata values
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, three");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, four");
// Here we create an Item so that we can create another relationship with this item. We'll give this
// Relationship a specific place as well
WorkspaceItem authorIs = workspaceItemService.create(context, col, false);
Item secondAuthorItem = installItemService.installItem(context, authorIs);
itemService.addMetadata(context, secondAuthorItem, "relationship", "type", null, null, "Person");
itemService.addMetadata(context, secondAuthorItem, "person", "familyName", null, null, "familyNameTwo");
itemService.addMetadata(context, secondAuthorItem, "person", "givenName", null, null, "firstNameTwo");
Relationship relationshipTwo = relationshipService
.create(context, item, secondAuthorItem, isAuthorOfPublication, 5, -1);
context.restoreAuthSystemState();
// Here we retrieve the list of metadata again to perform the assertions on the places below as mentioned
list = itemService.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, two", null, 1, list.get(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 2, list.get(2));
assertThat(relationship.getLeftPlace(), equalTo(2));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 3, list.get(3));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, four", null, 4, list.get(4));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyNameTwo, firstNameTwo",
"virtual::" + relationshipTwo.getID(), 5, list.get(5));
assertThat(relationshipTwo.getLeftPlace(), equalTo(5));
}
/**
* In this test, our goal will be to add a bunch of metadata values to then remove one of them. We'll check the list
* of metadata values for the item and check that the places have not been altered for the metadata values IF an
* item.update hadn't been called yet. We'll then create a Relationship (by which an item.update will be called)
* and then we check that the places are set correctly.
* We then repeat this process once more and check that everything works as intended
* @throws Exception
*/
@Test
public void AddAndRemoveMetadataAndRelationshipsTest() throws Exception {
context.turnOffAuthorisationSystem();
// Here we add the first set of metadata to the item
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, one");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, two");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, three");
// Get a specific metadatavlaue to remove
MetadataValue metadataValueToRemove = itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY)
.get(1);
// Remove the actual metadata value
item.removeMetadata(metadataValueToRemove);
metadataValueService.delete(context, metadataValueToRemove);
context.restoreAuthSystemState();
// Retrieve the list of mdv again
List<MetadataValue> list = itemService
.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
// Verify we only have 2 mdv left
assertThat(list.size(), equalTo(2));
// Check that these places are still intact after the deletion as the place doesn't get updated until an
// item.update has been called
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 2, list.get(1));
context.turnOffAuthorisationSystem();
// Create a relationship with this item with a spcific place
Relationship relationship = relationshipService.create(context, item, authorItem, isAuthorOfPublication, 1, -1);
context.restoreAuthSystemState();
// Retrieve the list again and verify that the creation of the Relationship added an additional mdv
list = itemService.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertThat(list.size(), equalTo(3));
// Assert that the mdv are well placed
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 1, list.get(1));
assertThat(relationship.getLeftPlace(), equalTo(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 2, list.get(2));
context.turnOffAuthorisationSystem();
// Add two extra mdv
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, four");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, five");
//This is author "test, four" that we're removing
metadataValueToRemove = itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY).get(3);
item.removeMetadata(metadataValueToRemove);
metadataValueService.delete(context, metadataValueToRemove);
context.restoreAuthSystemState();
list = itemService.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
// Check that these places are still intact after the deletion as the place doesn't get updated until an
// item.update has been called
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 1, list.get(1));
assertThat(relationship.getLeftPlace(), equalTo(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 2, list.get(2));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, five", null, 4, list.get(3));
context.turnOffAuthorisationSystem();
// Create an additional item for another relationship
WorkspaceItem authorIs = workspaceItemService.create(context, col, false);
Item secondAuthorItem = installItemService.installItem(context, authorIs);
itemService.addMetadata(context, secondAuthorItem, "relationship", "type", null, null, "Person");
itemService.addMetadata(context, secondAuthorItem, "person", "familyName", null, null, "familyNameTwo");
itemService.addMetadata(context, secondAuthorItem, "person", "givenName", null, null, "firstNameTwo");
Relationship relationshipTwo = relationshipService
.create(context, item, secondAuthorItem, isAuthorOfPublication, 3, -1);
context.restoreAuthSystemState();
// Check that the other mdv are still okay and that the creation of the relationship added
// another correct mdv to the item
list = itemService.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 1, list.get(1));
assertThat(relationship.getLeftPlace(), equalTo(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 2, list.get(2));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyNameTwo, firstNameTwo",
"virtual::" + relationshipTwo.getID(), 3, list.get(3));
assertThat(relationshipTwo.getLeftPlace(), equalTo(3));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, five", null, 4, list.get(4));
}
@Test
public void AddAndUpdateMetadataAndRelationshipsTest() throws Exception {
context.turnOffAuthorisationSystem();
// Add metadata and relationships to the item
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, one");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, two");
itemService.addMetadata(context, item, dcSchema, contributorElement, authorQualifier, null, "test, three");
Relationship relationship = relationshipService
.create(context, item, authorItem, isAuthorOfPublication, -1, -1);
context.restoreAuthSystemState();
// Get the list of mdv and assert that they're correct
List<MetadataValue> list = itemService
.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertThat(list.size(), equalTo(4));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, two", null, 1, list.get(1));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 2, list.get(2));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 3, list.get(3));
assertThat(relationship.getLeftPlace(), equalTo(3));
context.turnOffAuthorisationSystem();
MetadataValue metadataValueToUpdate = itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY)
.get(1);
// Switching the places of this relationship and metadata value to verify in the test later on that this
// updating works
metadataValueToUpdate.setPlace(3);
metadataValueService.update(context, metadataValueToUpdate);
relationship.setLeftPlace(1);
relationshipService.update(context, relationship);
context.restoreAuthSystemState();
// Retrieve the list again and verify that the updating did indeed work
list = itemService.getMetadata(item, dcSchema, contributorElement, authorQualifier, Item.ANY);
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, one", null, 0, list.get(0));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, two", null, 3, list.get(3));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "test, three", null, 2, list.get(2));
assertMetadataValue(authorQualifier, contributorElement, dcSchema, "familyName, firstName",
"virtual::" + relationship.getID(), 1, list.get(1));
assertThat(relationship.getLeftPlace(), equalTo(1));
}
private void assertMetadataValue(String authorQualifier, String contributorElement, String dcSchema, String value,
String authority, int place, MetadataValue metadataValue) {
assertThat(metadataValue.getValue(), equalTo(value));
assertThat(metadataValue.getMetadataField().getMetadataSchema().getName(), equalTo(dcSchema));
assertThat(metadataValue.getMetadataField().getElement(), equalTo(contributorElement));
assertThat(metadataValue.getMetadataField().getQualifier(), equalTo(authorQualifier));
assertThat(metadataValue.getAuthority(), equalTo(authority));
assertThat(metadataValue.getPlace(), equalTo(place));
}
}

View File

@@ -114,11 +114,11 @@ public class RelationshipServiceImplTest {
Item item = mock(Item.class);
// Mock DAO to return mocked left place as 0
when(relationshipDAO.findLeftPlaceByLeftItem(context, item)).thenReturn(0);
when(relationshipDAO.findNextLeftPlaceByLeftItem(context, item)).thenReturn(0);
// The left place reported from out mocked item should match the DAO's report of the left place
assertEquals("TestFindLeftPlaceByLeftItem 0", relationshipDAO.findLeftPlaceByLeftItem(context, item),
relationshipService.findLeftPlaceByLeftItem(context, item));
assertEquals("TestFindLeftPlaceByLeftItem 0", relationshipDAO.findNextLeftPlaceByLeftItem(context, item),
relationshipService.findNextLeftPlaceByLeftItem(context, item));
}
@Test
@@ -127,11 +127,11 @@ public class RelationshipServiceImplTest {
Item item = mock(Item.class);
// Mock lower level DAO to return mocked right place as 0
when(relationshipDAO.findRightPlaceByRightItem(context, item)).thenReturn(0);
when(relationshipDAO.findNextRightPlaceByRightItem(context, item)).thenReturn(0);
// The right place reported from out mocked item should match the DAO's report of the right place
assertEquals("TestFindRightPlaceByRightItem 0", relationshipDAO.findRightPlaceByRightItem(context, item),
relationshipService.findRightPlaceByRightItem(context, item));
assertEquals("TestFindRightPlaceByRightItem 0", relationshipDAO.findNextRightPlaceByRightItem(context, item),
relationshipService.findNextRightPlaceByRightItem(context, item));
}
@Test

View File

@@ -138,24 +138,24 @@ public class RelationshipDAOImplTest extends AbstractIntegrationTest {
}
/**
* Test findLeftPlaceByLeftItem should return 0 given our test left Item itemOne.
* Test findNextLeftPlaceByLeftItem should return 0 given our test left Item itemOne.
*
* @throws Exception
*/
@Test
public void testFindLeftPlaceByLeftItem() throws Exception {
assertEquals("TestLeftPlaceByLeftItem 0", 0, relationshipService.findLeftPlaceByLeftItem(context,
public void testFindNextLeftPlaceByLeftItem() throws Exception {
assertEquals("TestNextLeftPlaceByLeftItem 0", 1, relationshipService.findNextLeftPlaceByLeftItem(context,
itemOne));
}
/**
* Test findRightPlaceByRightItem should return 0 given our test right Item itemTwo.
* Test findNextRightPlaceByRightItem should return 0 given our test right Item itemTwo.
*
* @throws Exception
*/
@Test
public void testFindRightPlaceByRightItem() throws Exception {
assertEquals("TestRightPlaceByRightItem 0", 0, relationshipService.findRightPlaceByRightItem(context,
public void testFindNextRightPlaceByRightItem() throws Exception {
assertEquals("TestNextRightPlaceByRightItem 0", 1, relationshipService.findNextRightPlaceByRightItem(context,
itemTwo));
}
@@ -183,4 +183,4 @@ public class RelationshipDAOImplTest extends AbstractIntegrationTest {
}
}
}

View File

@@ -0,0 +1,93 @@
/**
* 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.external.provider.impl;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.ExternalDataProvider;
public class MockDataProvider implements ExternalDataProvider {
private Map<String, ExternalDataObject> mockLookupMap;
private String sourceIdentifier;
/**
* Generic getter for the sourceIdentifier
* @return the sourceIdentifier value of this MockDataProvider
*/
public String getSourceIdentifier() {
return sourceIdentifier;
}
public Optional<ExternalDataObject> getExternalDataObject(String id) {
ExternalDataObject externalDataObject = mockLookupMap.get(id);
if (externalDataObject == null) {
return Optional.empty();
} else {
return Optional.of(externalDataObject);
}
}
public List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit) {
List<ExternalDataObject> listToReturn = new LinkedList<>();
for (Map.Entry<String, ExternalDataObject> entry : mockLookupMap.entrySet()) {
if (StringUtils.containsIgnoreCase(entry.getKey(), query)) {
listToReturn.add(entry.getValue());
}
}
return listToReturn;
}
@Override
public boolean supports(String source) {
return StringUtils.equalsIgnoreCase(sourceIdentifier, source);
}
@Override
public int getNumberOfResults(String query) {
return searchExternalDataObjects(query, 0, 100).size();
}
/**
* Generic setter for the sourceIdentifier
* @param sourceIdentifier The sourceIdentifier to be set on this MockDataProvider
*/
public void setSourceIdentifier(String sourceIdentifier) {
this.sourceIdentifier = sourceIdentifier;
}
public void init() throws IOException {
mockLookupMap = new HashMap<>();
List<String> externalDataObjectsToMake = new LinkedList<>();
externalDataObjectsToMake.add("one");
externalDataObjectsToMake.add("two");
externalDataObjectsToMake.add("three");
externalDataObjectsToMake.add("onetwo");
for (String id : externalDataObjectsToMake) {
ExternalDataObject externalDataObject = new ExternalDataObject("mock");
externalDataObject.setId(id);
externalDataObject.setValue(id);
externalDataObject.setDisplayValue(id);
List<MetadataValueDTO> list = new LinkedList<>();
list.add(new MetadataValueDTO("dc", "contributor", "author", null, "Donald, Smith"));
externalDataObject.setMetadata(list);
mockLookupMap.put(id, externalDataObject);
}
}
}

View File

@@ -0,0 +1,136 @@
/**
* 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.scripts;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.LinkedList;
import java.util.List;
import org.dspace.AbstractUnitTest;
import org.junit.Test;
public class DSpaceCommandLineParameterTest extends AbstractUnitTest {
@Test
public void constructorTest() {
String key = "-c";
String value = "test";
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(key, value);
assertThat("constructorTest 0", dSpaceCommandLineParameter.getName(), equalTo(key));
assertThat("constructorTest 1", dSpaceCommandLineParameter.getValue(), equalTo(value));
}
@Test
public void constructorTestNullValue() {
String key = "-c";
String value = null;
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(key, value);
assertThat("constructorTest 0", dSpaceCommandLineParameter.getName(), equalTo(key));
assertThat("constructorTest 1", dSpaceCommandLineParameter.getValue(), equalTo(value));
}
@Test
public void singleParameterConstructorTest() {
String parameter = "-c test";
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(parameter);
assertThat("singleParameterConstructorTest 0", dSpaceCommandLineParameter.getName(), equalTo("-c"));
assertThat("singleParameterConstructorTest 1", dSpaceCommandLineParameter.getValue(), equalTo("test"));
}
@Test
public void singleParameterConstructorTestNoValue() {
String parameter = "-c";
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(parameter);
assertThat("singleParameterConstructorTest 0", dSpaceCommandLineParameter.getName(), equalTo("-c"));
assertThat("singleParameterConstructorTest 1", dSpaceCommandLineParameter.getValue(), equalTo(null));
}
@Test
public void toStringTest() {
String key = "-c";
String value = "test";
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(key, value);
assertThat("toStringTest 0", dSpaceCommandLineParameter.getName(), equalTo(key));
assertThat("toStringTest 1", dSpaceCommandLineParameter.getValue(), equalTo(value));
assertThat("toStringTest 2", dSpaceCommandLineParameter.toString(), equalTo("-c test"));
}
@Test
public void toStringTestNullValue() {
String key = "-c";
String value = null;
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(key, value);
assertThat("toStringTest 0", dSpaceCommandLineParameter.getName(), equalTo(key));
assertThat("toStringTest 1", dSpaceCommandLineParameter.getValue(), equalTo(value));
assertThat("toStringTest 2", dSpaceCommandLineParameter.toString(), equalTo("-c"));
}
@Test
public void equalsTest() {
String key = "-c";
String value = "test";
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(key, value);
DSpaceCommandLineParameter dSpaceCommandLineParameter1 = new DSpaceCommandLineParameter(key, value);
assertThat("toStringTest 0", dSpaceCommandLineParameter.getName(), equalTo(key));
assertThat("toStringTest 1", dSpaceCommandLineParameter.getValue(), equalTo(value));
assertThat("toStringTest 0", dSpaceCommandLineParameter1.getName(), equalTo(key));
assertThat("toStringTest 1", dSpaceCommandLineParameter1.getValue(), equalTo(value));
assertTrue(dSpaceCommandLineParameter.equals(dSpaceCommandLineParameter1));
}
@Test
public void concatenateTest() {
String key = "-c";
String value = "test";
DSpaceCommandLineParameter dSpaceCommandLineParameter = new DSpaceCommandLineParameter(key, value);
DSpaceCommandLineParameter dSpaceCommandLineParameter1 = new DSpaceCommandLineParameter(key, value);
String key2 = "-r";
String value2 = "testing";
DSpaceCommandLineParameter dSpaceCommandLineParameter2 = new DSpaceCommandLineParameter(key2, value2);
String key3 = "-t";
String value3 = null;
DSpaceCommandLineParameter dSpaceCommandLineParameter3 = new DSpaceCommandLineParameter(key3, value3);
List<DSpaceCommandLineParameter> dSpaceCommandLineParameterList = new LinkedList<>();
dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter);
dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter1);
dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter2);
dSpaceCommandLineParameterList.add(dSpaceCommandLineParameter3);
String concatenedString = DSpaceCommandLineParameter.concatenate(dSpaceCommandLineParameterList);
assertThat("concatenateTest", concatenedString, equalTo(
dSpaceCommandLineParameter.toString() + DSpaceCommandLineParameter.SEPARATOR + dSpaceCommandLineParameter1
.toString() + DSpaceCommandLineParameter.SEPARATOR + dSpaceCommandLineParameter2
.toString() + DSpaceCommandLineParameter.SEPARATOR + dSpaceCommandLineParameter3.toString()));
}
}

View File

@@ -0,0 +1,42 @@
/**
* 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.scripts.impl;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dspace.scripts.DSpaceRunnable;
public class MockDSpaceRunnableScript extends DSpaceRunnable {
private MockDSpaceRunnableScript() {
Options options = constructOptions();
this.options = options;
}
@Override
public void internalRun() throws Exception {
}
@Override
public void setup() throws ParseException {
if (!commandLine.hasOption("i")) {
throw new ParseException("-i is a mandatory parameter");
}
}
private Options constructOptions() {
Options options = new Options();
options.addOption("r", "remove", true, "description r");
options.getOption("r").setType(String.class);
options.addOption("i", "index", true, "description i");
options.getOption("i").setType(boolean.class);
options.getOption("i").setRequired(true);
return options;
}
}

View File

@@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.EPersonConverter;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.AuthenticationStatusRest;
@@ -18,6 +19,7 @@ import org.dspace.app.rest.model.AuthnRest;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.hateoas.AuthenticationStatusResource;
import org.dspace.app.rest.model.hateoas.AuthnResource;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.core.Context;
@@ -49,6 +51,9 @@ public class AuthenticationRestController implements InitializingBean {
@Autowired
DiscoverableEndpointsService discoverableEndpointsService;
@Autowired
private ConverterService converter;
@Autowired
private EPersonConverter ePersonConverter;
@@ -65,24 +70,24 @@ public class AuthenticationRestController implements InitializingBean {
}
@RequestMapping(method = RequestMethod.GET)
public AuthnResource authn() throws SQLException {
AuthnResource authnResource = new AuthnResource(new AuthnRest(), utils);
halLinkService.addLinks(authnResource);
return authnResource;
public AuthnResource authn() {
AuthnRest authnRest = new AuthnRest();
authnRest.setProjection(utils.obtainProjection());
return converter.toResource(authnRest);
}
@RequestMapping(value = "/status", method = RequestMethod.GET)
public AuthenticationStatusResource status(HttpServletRequest request) throws SQLException {
Context context = ContextUtil.obtainContext(request);
EPersonRest ePersonRest = null;
Projection projection = utils.obtainProjection();
if (context.getCurrentUser() != null) {
ePersonRest = ePersonConverter.fromModelWithGroups(context, context.getCurrentUser());
ePersonRest = ePersonConverter.fromModelWithGroups(context, context.getCurrentUser(), projection);
}
AuthenticationStatusResource authenticationStatusResource = new AuthenticationStatusResource(
new AuthenticationStatusRest(ePersonRest), utils);
halLinkService.addLinks(authenticationStatusResource);
AuthenticationStatusRest authenticationStatusRest = new AuthenticationStatusRest(ePersonRest);
authenticationStatusRest.setProjection(projection);
AuthenticationStatusResource authenticationStatusResource = converter.toResource(authenticationStatusRest);
return authenticationStatusResource;
}

View File

@@ -0,0 +1,153 @@
/**
* 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.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import static org.dspace.core.Constants.BUNDLE;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.model.hateoas.BundleResource;
import org.dspace.app.rest.repository.BitstreamRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Controller to access bitstreams and the bundles they're in
* Endpoint: /api/core/bitstreams/{uuid}
* This controller can:
* - request bundle a bitstream is in (GET /api/core/bitstreams/{uuid}/bundle)
* - move bitstreams between bundles (POST /api/core/bitstreams/{uuid}/bundle (text/uri-list) -d link-to-new-bundle)
*
*/
@RestController
@RequestMapping("/api/" + BitstreamRest.CATEGORY + "/" + BitstreamRest.PLURAL_NAME
+ REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/" + BundleRest.NAME)
public class BitstreamBundleController {
@Autowired
private BitstreamService bitstreamService;
@Autowired
ConverterService converter;
@Autowired
HalLinkService halLinkService;
@Autowired
Utils utils;
@Autowired
BitstreamRestRepository bitstreamRestRepository;
/**
* This method gets the bundle of the bitstream that corresponds to to the provided bitstream uuid. When multiple
* bundles are present, only the first will be returned.
*
* @param uuid The UUID of the bitstream for which the bundle will be retrieved
* @param response The response object
* @param request The request object
* @return The wrapped resource containing the first bundle of the bitstream
* @throws IOException
* @throws SQLException
* @throws AuthorizeException
*/
@PreAuthorize("hasPermission(#uuid, 'BITSTREAM', 'READ')")
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD})
public ResponseEntity<ResourceSupport> getBundle(@PathVariable UUID uuid, HttpServletResponse response,
HttpServletRequest request)
throws IOException, SQLException, AuthorizeException {
Context context = ContextUtil.obtainContext(request);
Bitstream bitstream = bitstreamService.find(context, uuid);
if (bitstream == null) {
throw new ResourceNotFoundException(
BitstreamRest.CATEGORY + "." + BitstreamRest.NAME + " with id: " + uuid + " not found");
}
List<Bundle> bundles = bitstream.getBundles();
if (bundles.isEmpty()) {
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
}
BundleResource bundleResource = converter.toResource(
converter.toRest(bundles.get(0), utils.obtainProjection()));
return ControllerUtils.toResponseEntity(HttpStatus.OK, null, bundleResource);
}
/**
* This method moves the bitstream to the bundle corresponding the the link provided in the body of the put request
*
* @param uuid The UUID of the bitstream for which the bundle will be retrieved
* @param response The response object
* @param request The request object
* @return The wrapped resource containing the new bundle of the bitstream
* @throws SQLException
* @throws IOException
* @throws AuthorizeException
*/
@RequestMapping(method = RequestMethod.PUT, consumes = {"text/uri-list"})
@PreAuthorize("hasPermission(#uuid, 'BITSTREAM','WRITE')")
@PostAuthorize("returnObject != null")
public BundleRest move(@PathVariable UUID uuid, HttpServletResponse response,
HttpServletRequest request)
throws SQLException, IOException, AuthorizeException {
Context context = ContextUtil.obtainContext(request);
List<DSpaceObject> dsoList = utils.constructDSpaceObjectList(context, utils.getStringListFromRequest(request));
if (dsoList.size() != 1 || dsoList.get(0).getType() != BUNDLE) {
throw new UnprocessableEntityException("No bundle has been specified " +
"or the data cannot be resolved to a bundle.");
}
Bitstream bitstream = bitstreamService.find(context, uuid);
if (bitstream == null) {
throw new ResourceNotFoundException("Bitstream with id: " + uuid + " not found");
}
BundleRest bundleRest = bitstreamRestRepository.performBitstreamMove(context, bitstream,
(Bundle) dsoList.get(0));
context.commit();
return bundleRest;
}
}

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest;
import static org.dspace.app.rest.utils.ContextUtil.obtainContext;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
import java.io.IOException;
@@ -22,10 +23,11 @@ import javax.ws.rs.core.Response;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.BitstreamConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.hateoas.BitstreamResource;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.MultipartFileSender;
import org.dspace.app.rest.utils.Utils;
@@ -65,7 +67,7 @@ import org.springframework.web.bind.annotation.RestController;
*/
@RestController
@RequestMapping("/api/" + BitstreamRest.CATEGORY + "/" + BitstreamRest.PLURAL_NAME
+ "/{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}")
+ REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID)
public class BitstreamRestController {
private static final Logger log = org.apache.logging.log4j.LogManager
@@ -90,7 +92,7 @@ public class BitstreamRestController {
private ConfigurationService configurationService;
@Autowired
BitstreamConverter converter;
ConverterService converter;
@Autowired
Utils utils;
@@ -238,7 +240,7 @@ public class BitstreamRestController {
context.commit();
return (BitstreamResource) utils.getResourceRepository(BitstreamRest.CATEGORY, BitstreamRest.NAME)
.wrapResource(converter.fromModel(context.reloadEntity(bitstream)));
BitstreamRest bitstreamRest = converter.toRest(context.reloadEntity(bitstream), Projection.DEFAULT);
return converter.toResource(bitstreamRest);
}
}

View File

@@ -0,0 +1,117 @@
/**
* 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.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.model.hateoas.BitstreamResource;
import org.dspace.app.rest.repository.BundleRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.content.Bundle;
import org.dspace.content.service.BundleService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* Controller to upload bitstreams to a certain bundle, indicated by a uuid in the request
* Usage: POST /api/core/bundles/{uuid}/bitstreams (with file and properties of file in request)
* Example:
* <pre>
* {@code
* curl https://<dspace.server.url>/api/core/bundles/d3599177-0408-403b-9f8d-d300edd79edb/bitstreams
* -XPOST -H 'Content-Type: multipart/form-data' \
* -H 'Authorization: Bearer eyJhbGciOiJI...' \
* -F "file=@Downloads/test.html" \
* -F 'properties={ "name": "test.html", "metadata": { "dc.description": [ { "value": "example file", "language": null,
* "authority": null, "confidence": -1, "place": 0 } ]}, "bundleName": "ORIGINAL" };type=application/json'
* }
* </pre>
*/
@RestController
@RequestMapping("/api/" + BundleRest.CATEGORY + "/" + BundleRest.PLURAL_NAME + "/"
+ REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/" + BitstreamRest.PLURAL_NAME)
public class BundleUploadBitstreamController {
private static final Logger log = LogManager.getLogger();
@Autowired
protected Utils utils;
@Autowired
private BundleService bundleService;
@Autowired
private BundleRestRepository bundleRestRepository;
@Autowired
private ConverterService converter;
/**
* Method to upload a Bitstream to a Bundle with the given UUID in the URL. This will create a Bitstream with the
* file provided in the request and attach this to the Item that matches the UUID in the URL.
* This will only work for uploading one file, any extra files will silently be ignored
*
* @return The created BitstreamResource
*/
@RequestMapping(method = RequestMethod.POST, headers = "content-type=multipart/form-data")
@PreAuthorize("hasPermission(#uuid, 'BUNDLE', 'ADD') && hasPermission(#uuid, 'BUNDLE', 'WRITE')")
public ResponseEntity<ResourceSupport> uploadBitstream(HttpServletRequest request, @PathVariable UUID uuid,
@RequestParam("file") MultipartFile uploadfile,
@RequestParam(value = "properties", required = false) String properties) {
Context context = ContextUtil.obtainContext(request);
Bundle bundle = null;
try {
bundle = bundleService.find(context, uuid);
} catch (SQLException e) {
log.error("Something went wrong trying to find the Bundle with uuid: " + uuid, e);
}
if (bundle == null) {
throw new ResourceNotFoundException("The given uuid did not resolve to a Bundle on the server: " + uuid);
}
InputStream fileInputStream = null;
try {
fileInputStream = uploadfile.getInputStream();
} catch (IOException e) {
log.error("Something went wrong when trying to read the inputstream from the given file in the request",
e);
throw new UnprocessableEntityException("The InputStream from the file couldn't be read", e);
}
BitstreamRest bitstreamRest = bundleRestRepository.uploadBitstream(
context, bundle, uploadfile.getOriginalFilename(), fileInputStream, properties);
BitstreamResource bitstreamResource = converter.toResource(bitstreamRest);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, bitstreamResource);
}
}

View File

@@ -12,6 +12,7 @@ import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.HarvestedCollectionConverter;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.HarvestedCollectionRest;
@@ -48,6 +49,9 @@ public class CollectionHarvestSettingsController {
@Autowired
CollectionService collectionService;
@Autowired
ConverterService converter;
@Autowired
HarvestedCollectionService harvestedCollectionService;
@@ -80,9 +84,7 @@ public class CollectionHarvestSettingsController {
}
HarvestedCollectionRest harvestedCollectionRest = harvestedCollectionRestRepository.findOne(collection);
HarvestedCollectionResource resource = new HarvestedCollectionResource(harvestedCollectionRest);
halLinkService.addLinks(resource);
HarvestedCollectionResource resource = converter.toResource(harvestedCollectionRest);
return resource;
}
@@ -114,8 +116,7 @@ public class CollectionHarvestSettingsController {
// Return a harvestedCollectionResource only if a new harvestedCollection was created
if (harvestedCollectionRest != null) {
harvestedCollectionResource = new HarvestedCollectionResource(harvestedCollectionRest);
halLinkService.addLinks(harvestedCollectionResource);
harvestedCollectionResource = converter.toResource(harvestedCollectionRest);
}
context.commit();

View File

@@ -0,0 +1,160 @@
/**
* 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.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import java.io.IOException;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.BadRequestException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.ItemResource;
import org.dspace.app.rest.repository.CollectionRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
import org.dspace.content.service.CollectionService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* This RestController takes care of the creation and retrieval of Collection's Item templates
* This class will receive the UUID of a Collection and it'll perform actions on its nested objects
*/
@RestController
@RequestMapping("/api/" + CollectionRest.CATEGORY + "/" + CollectionRest.PLURAL_NAME
+ REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/itemtemplate")
public class CollectionItemtemplateController {
@Autowired
private Utils utils;
@Autowired
private CollectionRestRepository collectionRestRepository;
@Autowired
private CollectionService collectionService;
/**
* This method will create an Item and add it as a template to a Collection.
*
* Example:
* <pre>
* {@code
* curl http://<dspace.server.url>/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/itemtemplate
* -XPOST -H 'Content-Type: Content-Type:application/json' \
* -H 'Authorization: Bearer eyJhbGciOiJI...' \
* --data '{
* "metadata": {
* "dc.type": [
* {
* "value": "Journal Article",
* "language": "en",
* "authority": null,
* "confidence": -1
* }
* ]
* },
* "inArchive": false,
* "discoverable": false,
* "withdrawn": false,
* "type": "item"
* }'
* }
* </pre>
* @param request The request as described above
* @param uuid The UUID of the Collection for which the template item should be made
* @param itemBody The new item
* @return The created template
* @throws SQLException
* @throws AuthorizeException
*/
@PreAuthorize("hasPermission(#uuid, 'COLLECTION', 'WRITE')")
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<ResourceSupport> createTemplateItem(HttpServletRequest request,
@PathVariable UUID uuid,
@RequestBody(required = false) JsonNode itemBody)
throws SQLException, AuthorizeException {
if (itemBody == null) {
throw new BadRequestException("The new item should be included as json in the body of this request");
}
Context context = ContextUtil.obtainContext(request);
Collection collection = getCollection(context, uuid);
ItemRest inputItemRest;
try {
ObjectMapper mapper = new ObjectMapper();
inputItemRest = mapper.readValue(itemBody.toString(), ItemRest.class);
} catch (IOException e1) {
throw new UnprocessableEntityException("Error parsing request body", e1);
}
ItemRest templateItem = collectionRestRepository.createTemplateItem(context, collection, inputItemRest);
context.commit();
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null,
new ItemResource(templateItem, utils));
}
/**
* This method gets the template Item based on the owning Collection
*
* <pre>
* {@code
* curl http://<dspace.server.url>/api/core/collections/51715dd3-5590-49f2-b227-6a663c849921/itemtemplate
* -XGET \
* -H 'Authorization: Bearer eyJhbGciOiJI...'
* }
* </pre>
* @param request
* @param uuid The UUID of the Collection from which you want the template item
* @return The template item from the Collection in the request
* @throws SQLException
*/
@PreAuthorize("hasPermission(#uuid, 'COLLECTION', 'READ')")
@RequestMapping(method = RequestMethod.GET)
public ItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid)
throws SQLException {
Context context = ContextUtil.obtainContext(request);
Collection collection = getCollection(context, uuid);
ItemRest templateItem = collectionRestRepository.getTemplateItem(collection);
return new ItemResource(templateItem, utils);
}
private Collection getCollection(Context context, UUID uuid) throws SQLException {
Collection collection = collectionService.find(context, uuid);
if (collection == null) {
throw new ResourceNotFoundException(
"The given uuid did not resolve to a collection on the server: " + uuid);
}
return collection;
}
}

View File

@@ -14,6 +14,7 @@ import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.FacetConfigurationRest;
import org.dspace.app.rest.model.FacetResultsRest;
@@ -61,6 +62,9 @@ public class DiscoveryRestController implements InitializingBean {
@Autowired
private HalLinkService halLinkService;
@Autowired
private ConverterService converter;
@Override
public void afterPropertiesSet() throws Exception {
discoverableEndpointsService
@@ -74,8 +78,7 @@ public class DiscoveryRestController implements InitializingBean {
throws Exception {
SearchSupportRest searchSupportRest = discoveryRestRepository.getSearchSupport();
SearchSupportResource searchSupportResource = new SearchSupportResource(searchSupportRest);
halLinkService.addLinks(searchSupportResource);
SearchSupportResource searchSupportResource = converter.toResource(searchSupportRest);
return searchSupportResource;
}
@@ -91,9 +94,7 @@ public class DiscoveryRestController implements InitializingBean {
SearchConfigurationRest searchConfigurationRest = discoveryRestRepository
.getSearchConfiguration(dsoScope, configuration);
SearchConfigurationResource searchConfigurationResource = new SearchConfigurationResource(
searchConfigurationRest);
halLinkService.addLinks(searchConfigurationResource);
SearchConfigurationResource searchConfigurationResource = converter.toResource(searchConfigurationRest);
return searchConfigurationResource;
}
@@ -142,9 +143,8 @@ public class DiscoveryRestController implements InitializingBean {
}
//Get the Search results in JSON format
SearchResultsRest searchResultsRest = null;
searchResultsRest = discoveryRestRepository
.getSearchObjects(query, dsoType, dsoScope, configuration, searchFilters, page);
SearchResultsRest searchResultsRest = discoveryRestRepository
.getSearchObjects(query, dsoType, dsoScope, configuration, searchFilters, page, utils.obtainProjection());
//Convert the Search JSON results to paginated HAL resources
SearchResultsResource searchResultsResource = new SearchResultsResource(searchResultsRest, utils, page);
@@ -164,7 +164,7 @@ public class DiscoveryRestController implements InitializingBean {
FacetConfigurationRest facetConfigurationRest = discoveryRestRepository
.getFacetsConfiguration(dsoScope, configuration);
FacetConfigurationResource facetConfigurationResource = new FacetConfigurationResource(facetConfigurationRest);
FacetConfigurationResource facetConfigurationResource = converter.toResource(facetConfigurationRest);
halLinkService.addLinks(facetConfigurationResource, pageable);
return facetConfigurationResource;
@@ -192,7 +192,7 @@ public class DiscoveryRestController implements InitializingBean {
FacetResultsRest facetResultsRest = discoveryRestRepository
.getFacetObjects(facetName, prefix, query, dsoType, dsoScope, configuration, searchFilters, page);
FacetResultsResource facetResultsResource = new FacetResultsResource(facetResultsRest);
FacetResultsResource facetResultsResource = converter.toResource(facetResultsRest);
halLinkService.addLinks(facetResultsResource, page);
return facetResultsResource;

View File

@@ -0,0 +1,96 @@
/**
* 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.rest;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.ExternalSourceEntryRest;
import org.dspace.app.rest.model.hateoas.ExternalSourceEntryResource;
import org.dspace.app.rest.repository.ExternalSourceRestRepository;
import org.dspace.app.rest.utils.Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.PagedResources;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* This RestController takes care of the retrieval of External data from various endpoints and providers depending
* on the calls it receives
*/
@RestController
@RequestMapping("/api/integration/externalsources/{externalSourceName}")
public class ExternalSourcesRestController {
@Autowired
private ExternalSourceRestRepository externalSourceRestRepository;
@Autowired
protected Utils utils;
@Autowired
HalLinkService linkService;
/**
* This method will retrieve all the ExternalSourceEntries for the ExternalSource for the given externalSourceName
* param
*
* curl -X GET http://<dspace.restUrl>/api/integration/externalsources/orcidV2/entries
*
* @param externalSourceName The externalSourceName that defines which ExternalDataProvider is used
* @param query The query used in the lookup
* @param parent The parent used in the lookup
* @param pageable The pagination object
* @param assembler The assembler object
* @return A paginated list of ExternalSourceEntryResource objects that comply with the params
*/
@RequestMapping(method = RequestMethod.GET, value = "/entries")
public PagedResources<ExternalSourceEntryResource> getExternalSourceEntries(
@PathVariable("externalSourceName") String externalSourceName,
@RequestParam(name = "query") String query,
@RequestParam(name = "parent", required = false) String parent,
Pageable pageable, PagedResourcesAssembler assembler) {
Page<ExternalSourceEntryRest> externalSourceEntryRestPage = externalSourceRestRepository
.getExternalSourceEntries(externalSourceName, query, parent, pageable);
Page<ExternalSourceEntryResource> externalSourceEntryResources = externalSourceEntryRestPage
.map(externalSourceEntryRest -> new ExternalSourceEntryResource(externalSourceEntryRest));
externalSourceEntryResources.forEach(linkService::addLinks);
PagedResources<ExternalSourceEntryResource> result = assembler.toResource(externalSourceEntryResources);
return result;
}
/**
* This method will retrieve one ExternalSourceEntryResource based on the ExternalSource for the given
* externalSourceName and with the given entryId
*
* curl -X GET http://<dspace.restUrl>/api/integration/externalsources/orcidV2/entries/0000-0000-0000-0000
*
* @param externalSourceName The externalSourceName that defines which ExternalDataProvider is used
* @param entryId The entryId used for the lookup
* @return An ExternalSourceEntryResource that complies with the above params
*/
@RequestMapping(method = RequestMethod.GET, value = "/entryValues/{entryId}")
public ExternalSourceEntryResource getExternalSourceEntryValue(@PathVariable("externalSourceName") String
externalSourceName,
@PathVariable("entryId") String entryId) {
ExternalSourceEntryRest externalSourceEntryRest = externalSourceRestRepository
.getExternalSourceEntryValue(externalSourceName, entryId);
ExternalSourceEntryResource externalSourceEntryResource = new ExternalSourceEntryResource(
externalSourceEntryRest);
linkService.addLinks(externalSourceEntryResource);
return externalSourceEntryResource;
}
}

View File

@@ -12,12 +12,12 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.HarvesterMetadataRest;
import org.dspace.app.rest.model.hateoas.HarvesterMetadataResource;
import org.dspace.app.rest.utils.Utils;
import org.dspace.harvest.OAIHarvester;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -38,6 +38,9 @@ public class HarvesterMetadataController {
@Autowired
private HalLinkService halLinkService;
@Autowired
private ConverterService converter;
/**
* GET endpoint that returns all available metadata formats
* @param request The request object
@@ -46,15 +49,14 @@ public class HarvesterMetadataController {
*/
@RequestMapping(method = RequestMethod.GET)
public HarvesterMetadataResource get(HttpServletRequest request,
HttpServletResponse response) {
HttpServletResponse response) {
List<Map<String,String>> configs = OAIHarvester.getAvailableMetadataFormats();
HarvesterMetadataRest data = new HarvesterMetadataRest();
data.setProjection(utils.obtainProjection());
data.setConfigs(configs);
HarvesterMetadataResource resource = new HarvesterMetadataResource(data, utils);
halLinkService.addLinks(resource);
HarvesterMetadataResource resource = converter.toResource(data);
return resource;
}

View File

@@ -13,15 +13,15 @@ import java.io.IOException;
import java.net.URI;
import java.sql.SQLException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.atteo.evo.inflector.English;
import org.dspace.app.rest.converter.GenericDSpaceObjectConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.model.DSpaceObjectRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.identifier.IdentifierNotFoundException;
@@ -53,7 +53,10 @@ public class IdentifierRestController implements InitializingBean {
Logger.getLogger(IdentifierRestController.class);
@Autowired
private GenericDSpaceObjectConverter converter;
private ConverterService converter;
@Autowired
private Utils utils;
@Autowired
private DiscoverableEndpointsService discoverableEndpointsService;
@@ -84,7 +87,7 @@ public class IdentifierRestController implements InitializingBean {
try {
dso = identifierService.resolve(context, id);
if (dso != null) {
DSpaceObjectRest dsor = converter.convert(dso);
DSpaceObjectRest dsor = converter.toRest(dso, utils.obtainProjection());
URI link = linkTo(dsor.getController(), dsor.getCategory(),
English.plural(dsor.getType()))
.slash(dsor.getId()).toUri();

View File

@@ -0,0 +1,114 @@
/**
* 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.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import java.io.IOException;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.MetadataConverter;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.BundleResource;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.repository.ItemRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Controller to add bundles to a certain item, indicated by a uuid in the request
* Usage: POST /api/core/items/<:uuid>/bundles (with name and metadata of bundle in request json)
* Example:
* <pre>
* {@code
* curl -X POST https://<dspace.server.url>/api/core/items/1911e8a4-6939-490c-b58b-a5d70f8d91fb/bundles
* -H 'Authorization: Bearer eyJhbGciOiJI...'
* -H 'Content-Type: application/json
* -d {
* "name": "ORIGINAL",
* "metadata": {...}
* }
* }
* </pre>
*/
@RestController
@RequestMapping("/api/" + ItemRest.CATEGORY + "/" + ItemRest.PLURAL_NAME + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID
+ "/" + BundleRest.PLURAL_NAME)
public class ItemAddBundleController {
@Autowired
ConverterService converter;
@Autowired
ItemService itemService;
@Autowired
ItemRestRepository itemRestRepository;
@Autowired
MetadataConverter metadataConverter;
@Autowired
Utils utils;
/**
* Method to add a Bundle to an Item with the given UUID in the URL. This will create a Bundle with the
* name provided in the request and attach this to the Item that matches the UUID in the URL.
*
* @return The created BundleResource
*/
@RequestMapping(method = RequestMethod.POST)
@PreAuthorize("hasPermission(#uuid, 'ITEM', 'ADD')")
public ResponseEntity<ResourceSupport> addBundleToItem(@PathVariable UUID uuid,
HttpServletRequest request,
HttpServletResponse response)
throws SQLException, AuthorizeException {
Context context = ContextUtil.obtainContext(request);
Item item = itemService.find(context, uuid);
if (item == null) {
throw new ResourceNotFoundException("Could not find item with id " + uuid);
}
BundleRest bundleRest;
try {
bundleRest = new ObjectMapper().readValue(request.getInputStream(), BundleRest.class);
} catch (IOException excIO) {
throw new UnprocessableEntityException("Could not parse request body");
}
Bundle bundle = itemRestRepository.addBundleToItem(context, item, bundleRest);
BundleResource bundleResource = converter.toResource(converter.toRest(bundle, Projection.DEFAULT));
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, bundleResource);
}
}

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.app.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import static org.dspace.core.Constants.COLLECTION;
import java.io.IOException;
@@ -16,10 +17,11 @@ import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.CollectionConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
@@ -41,13 +43,11 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* This controller will handle all the incoming calls on the api/code/items/{itemUuid}/owningCollection endpoint
* where the itemUuid corresponds to the item of which you want to edit the owning collection.
* This controller will handle all the incoming calls on the api/code/items/{uuid}/owningCollection endpoint
* where the uuid corresponds to the item of which you want to edit the owning collection.
*/
@RestController
@RequestMapping("/api/core/items/" +
"{itemUuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12" +
"}}/owningCollection")
@RequestMapping("/api/core/items" + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/owningCollection")
public class ItemOwningCollectionUpdateRestController {
@Autowired
@@ -60,7 +60,7 @@ public class ItemOwningCollectionUpdateRestController {
AuthorizeService authorizeService;
@Autowired
CollectionConverter converter;
ConverterService converter;
@Autowired
Utils utils;
@@ -69,7 +69,7 @@ public class ItemOwningCollectionUpdateRestController {
* This method will update the owning collection of the item that correspond to the provided item uuid, effectively
* moving the item to the new collection.
*
* @param itemUuid The UUID of the item that will be moved
* @param uuid The UUID of the item that will be moved
* @param response The response object
* @param request The request object
* @return The wrapped resource containing the new owning collection or null when the item was not moved
@@ -78,9 +78,9 @@ public class ItemOwningCollectionUpdateRestController {
* @throws AuthorizeException If the user is not authorized to perform the move action
*/
@RequestMapping(method = RequestMethod.PUT, consumes = {"text/uri-list"})
@PreAuthorize("hasPermission(#itemUuid, 'ITEM','WRITE')")
@PreAuthorize("hasPermission(#uuid, 'ITEM','WRITE')")
@PostAuthorize("returnObject != null")
public CollectionRest move(@PathVariable UUID itemUuid, HttpServletResponse response,
public CollectionRest move(@PathVariable UUID uuid, HttpServletResponse response,
HttpServletRequest request)
throws SQLException, IOException, AuthorizeException {
Context context = ContextUtil.obtainContext(request);
@@ -92,12 +92,12 @@ public class ItemOwningCollectionUpdateRestController {
"or the data cannot be resolved to a collection.");
}
Collection targetCollection = performItemMove(context, itemUuid, (Collection) dsoList.get(0));
Collection targetCollection = performItemMove(context, uuid, (Collection) dsoList.get(0));
if (targetCollection == null) {
return null;
}
return converter.fromModel(targetCollection);
return converter.toRest(targetCollection, Projection.DEFAULT);
}

View File

@@ -1,163 +0,0 @@
/**
* 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.rest;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.BitstreamConverter;
import org.dspace.app.rest.converter.MetadataConverter;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.hateoas.BitstreamResource;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Item;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/core/items/{uuid}")
public class ItemUploadController {
private static final Logger log = LogManager.getLogger();
@Autowired
protected Utils utils;
@Autowired
private ItemService itemService;
@Autowired
private BitstreamService bitstreamService;
@Autowired
private BitstreamConverter bitstreamConverter;
@Autowired
private MetadataConverter metadataConverter;
@Autowired
private BitstreamFormatService bitstreamFormatService;
/**
* Method to upload a Bitstream to an Item with the given UUID in the URL. This will create a Bitstream with the
* file provided in the request and attach this to the Item that matches the UUID in the URL.
* This will only work for uploading one file, any extra files will silently be ignored
* @return The created BitstreamResource
*/
@RequestMapping(method = RequestMethod.POST, value = "/bitstreams", headers = "content-type=multipart/form-data")
@PreAuthorize("hasPermission(#uuid, 'ITEM', 'WRITE') && hasPermission(#uuid, 'ITEM', 'ADD')")
public BitstreamResource uploadBitstream(HttpServletRequest request, @PathVariable UUID uuid,
@RequestParam("file") MultipartFile uploadfile,
@RequestParam(value = "properties", required = false) String properties) {
Context context = ContextUtil.obtainContext(request);
Item item = null;
Bitstream bitstream = null;
try {
item = itemService.find(context, uuid);
} catch (SQLException e) {
log.error("Something went wrong trying to find the Item with uuid: " + uuid, e);
}
if (item == null) {
throw new ResourceNotFoundException("The given uuid did not resolve to an Item on the server: " + uuid);
}
InputStream fileInputStream = null;
try {
fileInputStream = uploadfile.getInputStream();
} catch (IOException e) {
log.error("Something went wrong when trying to read the inputstream from the given file in the request",
e);
throw new UnprocessableEntityException("The InputStream from the file couldn't be read", e);
}
try {
bitstream = processBitstreamCreation(context, item, fileInputStream, properties,
uploadfile.getOriginalFilename());
itemService.update(context, item);
context.commit();
} catch (AuthorizeException | IOException | SQLException e) {
String message = "Something went wrong with trying to create the single bitstream for file with filename: "
+ uploadfile.getOriginalFilename()
+ " for item with uuid: " + uuid + " and possible properties: " + properties;
log.error(message, e);
throw new RuntimeException(message, e);
}
return new BitstreamResource(bitstreamConverter.fromModel(bitstream), utils);
}
/**
* Creates the bitstream based on the given parameters
* @param context The context
* @param item The item where the bitstream should be store
* @param fileInputStream The input stream used to create the bitstream
* @param properties The properties to be assigned to the bitstream
* @param originalFilename The filename as it was uploaded
* @return The bitstream which has been created
*/
private Bitstream processBitstreamCreation(Context context, Item item, InputStream fileInputStream,
String properties, String originalFilename)
throws AuthorizeException, IOException, SQLException {
Bitstream bitstream = null;
if (StringUtils.isNotBlank(properties)) {
ObjectMapper mapper = new ObjectMapper();
BitstreamRest bitstreamRest = null;
try {
bitstreamRest = mapper.readValue(properties, BitstreamRest.class);
} catch (Exception e) {
throw new UnprocessableEntityException("The properties parameter was incorrect: " + properties);
}
String bundleName = bitstreamRest.getBundleName();
if (StringUtils.isBlank(bundleName)) {
throw new UnprocessableEntityException("Properties without a bundleName is not allowed");
}
bitstream = itemService.createSingleBitstream(context, fileInputStream, item, bundleName);
if (bitstreamRest.getMetadata() != null) {
metadataConverter.setMetadata(context, bitstream, bitstreamRest.getMetadata());
}
String name = bitstreamRest.getName();
if (StringUtils.isNotBlank(name)) {
bitstream.setName(context, name);
} else {
bitstream.setName(context, originalFilename);
}
} else {
bitstream = itemService.createSingleBitstream(context, fileInputStream, item);
bitstream.setName(context, originalFilename);
}
BitstreamFormat bitstreamFormat = bitstreamFormatService.guessFormat(context, bitstream);
bitstreamService.setFormat(context, bitstream, bitstreamFormat);
bitstreamService.update(context, bitstream);
return bitstream;
}
}

View File

@@ -0,0 +1,174 @@
/**
* 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.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import java.io.IOException;
import java.sql.SQLException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.JsonNode;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.hateoas.ItemResource;
import org.dspace.app.rest.repository.ItemRestRepository;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* This RestController takes care of the modification and retrieval of Collection's Item templates
* Contrary to CollectionItemtemplateController, this class will receive the UUID of an Item template
*/
@RestController
@RequestMapping("/api/core/itemtemplates" + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID)
public class ItemtemplateRestController {
@Autowired
private Utils utils;
@Autowired
private ItemService itemService;
@Autowired
private ItemRestRepository itemRestRepository;
/**
* This method gets a template Item based on its uuid
*
* Example:
* <pre>
* {@code
* curl http://<dspace.server.url>/api/core/itemtemplates/cb760455-837a-4159-bd12-dcdfafcadc78
* -XGET \
* -H 'Authorization: Bearer eyJhbGciOiJI...'
* }
* </pre>
* @param request
* @param uuid A UUID of a template item
* @return The template item corresponding to the UUID above
*/
@PreAuthorize("hasPermission(#uuid, 'COLLECTION', 'READ')")
@RequestMapping(method = RequestMethod.GET)
public ItemResource getTemplateItem(HttpServletRequest request, @PathVariable UUID uuid) {
Context context = ContextUtil.obtainContext(request);
ItemRest templateItem = itemRestRepository.findOne(context, uuid);
if (templateItem == null) {
throw new ResourceNotFoundException("Item with id: " + uuid + " not found");
}
if (templateItem.getTemplateItemOf() == null) {
throw new ResourceNotFoundException("The item with id " + uuid + " is not a template item");
}
return new ItemResource(templateItem, utils);
}
/**
* This method modifies installed template items
*
* Example:
* <pre>
* {@code
* curl http://<dspace.server.url>/api/core/itemtemplates/cb760455-837a-4159-bd12-dcdfafcadc78
* -XPATCH -H 'Content-Type: Content-Type:application/json' \
* -H 'Authorization: Bearer eyJhbGciOiJI...' \
* --data '[
* {
* "op": "add",
* "path": "/metadata/dc.description",
* "value": [ { "value": "Some other first description" } ]
* }
* ]'
* }
* </pre>
* @param request
* @param uuid The UUID of the template item to be modified
* @param jsonNode The data as shown above
* @return The modified item
* @throws SQLException
* @throws AuthorizeException
*/
@PreAuthorize("hasPermission(#uuid, 'ITEM', 'WRITE')")
@RequestMapping(method = RequestMethod.PATCH)
public ResponseEntity<ResourceSupport> replaceTemplateItem(HttpServletRequest request, @PathVariable UUID uuid,
@RequestBody(required = true) JsonNode jsonNode)
throws SQLException, AuthorizeException {
Context context = ContextUtil.obtainContext(request);
Item item = getTemplateItem(context, uuid);
ItemRest templateItem = itemRestRepository.patchTemplateItem(item, jsonNode);
context.commit();
return ControllerUtils.toResponseEntity(HttpStatus.OK, null,
new ItemResource(templateItem, utils));
}
/**
* This method deletes a template item from a collection.
*
* Example:
* <pre>
* {@code
* curl http://<dspace.server.url>/api/core/itemtemplates/cb760455-837a-4159-bd12-dcdfafcadc78
* -XDELETE \
* -H 'Authorization: Bearer eyJhbGciOiJI...'
* }
* </pre>
* @param request
* @param uuid
* @return Status code 204 is returned if the deletion was successful
* @throws SQLException
* @throws AuthorizeException
* @throws IOException
*/
@PreAuthorize("hasPermission(#uuid, 'ITEM', 'DELETE')")
@RequestMapping(method = RequestMethod.DELETE)
public ResponseEntity<ResourceSupport> deleteTemplateItem(HttpServletRequest request, @PathVariable UUID uuid)
throws SQLException, AuthorizeException, IOException {
Context context = ContextUtil.obtainContext(request);
Item item = getTemplateItem(context, uuid);
itemRestRepository.removeTemplateItem(context, item);
context.commit();
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
}
private Item getTemplateItem(Context context, UUID uuid) throws SQLException {
Item item = itemService.find(context, uuid);
if (item == null) {
throw new ResourceNotFoundException(
"The given uuid did not resolve to an item on the server: " + uuid);
}
if (item.getTemplateItemOf() == null) {
throw new UnprocessableEntityException("The item with id " + uuid + " is not a template item");
}
return item;
}
}

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.app.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import static org.dspace.core.Constants.COLLECTION;
import java.io.IOException;
@@ -18,7 +19,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.dspace.app.rest.converter.CollectionConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.MethodNotAllowedException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.link.HalLinkService;
@@ -46,9 +47,7 @@ import org.springframework.web.bind.annotation.RestController;
* This class will typically receive a UUID that resolves to an Item and it'll perform logic on its collections
*/
@RestController
@RequestMapping("/api/core/items/" +
"{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}" +
"/mappedCollections")
@RequestMapping("/api/core/items" + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/mappedCollections")
public class MappedCollectionRestController {
private static final Logger log = Logger.getLogger(MappedCollectionRestController.class);
@@ -57,7 +56,7 @@ public class MappedCollectionRestController {
private ItemService itemService;
@Autowired
private CollectionConverter collectionConverter;
private ConverterService converter;
@Autowired
private CollectionService collectionService;
@@ -100,18 +99,16 @@ public class MappedCollectionRestController {
List<CollectionRest> mappingCollectionRest = new LinkedList<>();
for (Collection collection : collections) {
if (collection.getID() != owningCollectionUuid) {
mappingCollectionRest.add(collectionConverter.fromModel(collection));
mappingCollectionRest.add(converter.toRest(collection, utils.obtainProjection()));
}
}
MappedCollectionRestWrapper mappingCollectionRestWrapper = new MappedCollectionRestWrapper();
mappingCollectionRestWrapper.setProjection(utils.obtainProjection());
mappingCollectionRestWrapper.setMappedCollectionRestList(mappingCollectionRest);
mappingCollectionRestWrapper.setItem(item);
MappedCollectionResourceWrapper mappingCollectionResourceWrapper = new MappedCollectionResourceWrapper(
mappingCollectionRestWrapper, utils, pageable);
halLinkService.addLinks(mappingCollectionResourceWrapper);
MappedCollectionResourceWrapper mappingCollectionResourceWrapper =
converter.toResource(mappingCollectionRestWrapper);
return mappingCollectionResourceWrapper;

View File

@@ -7,6 +7,8 @@
*/
package org.dspace.app.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -15,7 +17,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.dspace.app.rest.converter.ItemConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.MappedItemRestWrapper;
@@ -40,8 +42,7 @@ import org.springframework.web.bind.annotation.RestController;
* have the given collection as their owning collection
*/
@RestController
@RequestMapping("/api/core/collections/" +
"{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}/mappedItems")
@RequestMapping("/api/core/collections" + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/mappedItems")
public class MappedItemRestController {
private static final Logger log = Logger.getLogger(MappedItemRestController.class);
@@ -53,7 +54,7 @@ public class MappedItemRestController {
private ItemService itemService;
@Autowired
private ItemConverter itemConverter;
private ConverterService converter;
@Autowired
Utils utils;
@@ -95,11 +96,12 @@ public class MappedItemRestController {
while (itemIterator.hasNext()) {
Item item = itemIterator.next();
if (item.getOwningCollection().getID() != uuid) {
mappedItemRestList.add(itemConverter.fromModel(item));
mappedItemRestList.add(converter.toRest(item, utils.obtainProjection()));
}
}
MappedItemRestWrapper mappedItemRestWrapper = new MappedItemRestWrapper();
mappedItemRestWrapper.setProjection(utils.obtainProjection());
mappedItemRestWrapper.setMappedItemRestList(mappedItemRestList);
mappedItemRestWrapper.setCollectionUuid(uuid);
MappedItemResourceWrapper mappedItemResourceWrapper =

View File

@@ -7,6 +7,8 @@
*/
package org.dspace.app.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -29,11 +31,6 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/api/core/relationships")
public class RelationshipRestController {
/**
* Regular expression in the request mapping to accept number as identifier
*/
private static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT = "/{id:\\d+}";
@Autowired
private RelationshipRestRepository relationshipRestRepository;

View File

@@ -13,11 +13,12 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.rest.converter.RelationshipTypeConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.RelationshipTypeRest;
import org.dspace.app.rest.model.RelationshipTypeRestWrapper;
import org.dspace.app.rest.model.hateoas.RelationshipTypeResourceWrapper;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.content.EntityType;
@@ -48,7 +49,7 @@ public class RelationshipTypeRestController {
private EntityTypeService entityTypeService;
@Autowired
private RelationshipTypeConverter relationshipTypeConverter;
private ConverterService converter;
@Autowired
private Utils utils;
@@ -67,7 +68,8 @@ public class RelationshipTypeRestController {
* @throws SQLException If something goes wrong
*/
@RequestMapping(method = RequestMethod.GET)
public RelationshipTypeResourceWrapper retrieve(@PathVariable Integer id, HttpServletResponse response,
public RelationshipTypeResourceWrapper retrieve(@PathVariable Integer id,
HttpServletResponse response,
HttpServletRequest request) throws SQLException {
Context context = ContextUtil.obtainContext(request);
EntityType entityType = entityTypeService.find(context, id);
@@ -75,19 +77,19 @@ public class RelationshipTypeRestController {
List<RelationshipTypeRest> relationshipTypeRests = new LinkedList<>();
Projection projection = utils.obtainProjection();
for (RelationshipType relationshipType : list) {
relationshipTypeRests.add(relationshipTypeConverter.fromModel(relationshipType));
relationshipTypeRests.add(converter.toRest(relationshipType, projection));
}
RelationshipTypeRestWrapper relationshipTypeRestWrapper = new RelationshipTypeRestWrapper();
relationshipTypeRestWrapper.setProjection(projection);
relationshipTypeRestWrapper.setEntityTypeId(id);
relationshipTypeRestWrapper.setEntityTypeLabel(entityType.getLabel());
relationshipTypeRestWrapper.setRelationshipTypeRestList(relationshipTypeRests);
RelationshipTypeResourceWrapper relationshipTypeResourceWrapper = new RelationshipTypeResourceWrapper(
relationshipTypeRestWrapper, utils);
halLinkService.addLinks(relationshipTypeResourceWrapper);
RelationshipTypeResourceWrapper relationshipTypeResourceWrapper =
converter.toResource(relationshipTypeRestWrapper);
return relationshipTypeResourceWrapper;
}
}

View File

@@ -7,6 +7,9 @@
*/
package org.dspace.app.rest;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG;
import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
@@ -19,17 +22,16 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.atteo.evo.inflector.English;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.converter.JsonPatchConverter;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.PaginationException;
@@ -64,7 +66,6 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.UriTemplate;
@@ -95,24 +96,6 @@ import org.springframework.web.multipart.MultipartFile;
@SuppressWarnings("rawtypes")
public class RestResourceController implements InitializingBean {
/**
* Regular expression in the request mapping to accept UUID as identifier
*/
private static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID =
"/{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}";
/**
* Regular expression in the request mapping to accept a string as identifier but not the other kind of
* identifier (digits or uuid)
*/
private static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG = "/{id:^(?!^\\d+$)" +
"(?!^[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}$)[\\w+\\-]+$+}";
/**
* Regular expression in the request mapping to accept number as identifier
*/
private static final String REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT = "/{id:\\d+}";
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RestResourceController.class);
@Autowired
@@ -130,6 +113,9 @@ public class RestResourceController implements InitializingBean {
@Autowired
HalLinkService linkService;
@Autowired
ConverterService converter;
@Override
public void afterPropertiesSet() {
List<Link> links = new ArrayList<Link>();
@@ -152,22 +138,20 @@ public class RestResourceController implements InitializingBean {
*
* Note that the regular expression in the request mapping accept a number as identifier;
*
* Please see {@link RestResourceController#findOne(String, String, String, String)} for findOne with string as
* Please see {@link RestResourceController#findOne(String, String, String)} for findOne with string as
* identifier
* and see {@link RestResourceController#findOne(String, String, UUID, String)} for uuid as identifier
* and see {@link RestResourceController#findOne(String, String, UUID)} for uuid as identifier
*
* @param apiCategory
* @param model
* @param id
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT)
@SuppressWarnings("unchecked")
public DSpaceResource<RestAddressableModel> findOne(@PathVariable String apiCategory, @PathVariable String model,
@PathVariable Integer id,
@RequestParam(required = false) String projection) {
return findOneInternal(apiCategory, model, id, projection);
@PathVariable Integer id) {
return findOneInternal(apiCategory, model, id);
}
/**
@@ -186,22 +170,20 @@ public class RestResourceController implements InitializingBean {
* </pre>
*
*
* Please see {@link RestResourceController#findOne(String, String, Integer, String)} for findOne with number as
* Please see {@link RestResourceController#findOne(String, String, Integer)} for findOne with number as
* identifier
* and see {@link RestResourceController#findOne(String, String, UUID, String)} for uuid as identifier
* and see {@link RestResourceController#findOne(String, String, UUID)} for uuid as identifier
*
* @param apiCategory
* @param model
* @param id
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG)
@SuppressWarnings("unchecked")
public DSpaceResource<RestAddressableModel> findOne(@PathVariable String apiCategory, @PathVariable String model,
@PathVariable String id,
@RequestParam(required = false) String projection) {
return findOneInternal(apiCategory, model, id, projection);
@PathVariable String id) {
return findOneInternal(apiCategory, model, id);
}
/**
@@ -209,22 +191,20 @@ public class RestResourceController implements InitializingBean {
*
* Note that the regular expression in the request mapping accept a UUID as identifier;
*
* Please see {@link RestResourceController#findOne(String, String, Integer, String)} for findOne with number as
* Please see {@link RestResourceController#findOne(String, String, Integer)} for findOne with number as
* identifier
* and see {@link RestResourceController#findOne(String, String, String, String)} for string as identifier
* and see {@link RestResourceController#findOne(String, String, String)} for string as identifier
*
* @param apiCategory
* @param model
* @param uuid
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID)
@SuppressWarnings("unchecked")
public DSpaceResource<RestAddressableModel> findOne(@PathVariable String apiCategory, @PathVariable String model,
@PathVariable UUID uuid,
@RequestParam(required = false) String projection) {
return findOneInternal(apiCategory, model, uuid, projection);
@PathVariable UUID uuid) {
return findOneInternal(apiCategory, model, uuid);
}
/**
@@ -233,13 +213,10 @@ public class RestResourceController implements InitializingBean {
* @param apiCategory
* @param model
* @param id
* @param projection
* @return
*/
private <ID extends Serializable> DSpaceResource<RestAddressableModel> findOneInternal(String apiCategory,
String model, ID id,
String projection) {
checkModelPluralForm(apiCategory, model);
String model, ID id) {
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
RestAddressableModel modelObject = null;
try {
@@ -250,9 +227,7 @@ public class RestResourceController implements InitializingBean {
if (modelObject == null) {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
return result;
return converter.toResource(modelObject);
}
/**
@@ -267,7 +242,6 @@ public class RestResourceController implements InitializingBean {
* @param rel
* @param page
* @param assembler
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT + "/{rel}")
@@ -275,9 +249,8 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory,
@PathVariable String model, @PathVariable Integer id, @PathVariable String rel,
Pageable page,
PagedResourcesAssembler assembler,
@RequestParam(required = false) String projection) {
return findRelInternal(request, response, apiCategory, model, id, rel, page, assembler, projection);
PagedResourcesAssembler assembler) {
return findRelInternal(request, response, apiCategory, model, id, rel, page, assembler);
}
/**
@@ -293,7 +266,6 @@ public class RestResourceController implements InitializingBean {
* @param rel
* @param page
* @param assembler
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG +
@@ -302,9 +274,8 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory,
@PathVariable String model, @PathVariable String id, @PathVariable String rel,
Pageable page,
PagedResourcesAssembler assembler,
@RequestParam(required = false) String projection) {
return findRelInternal(request, response, apiCategory, model, id, rel, page, assembler, projection);
PagedResourcesAssembler assembler) {
return findRelInternal(request, response, apiCategory, model, id, rel, page, assembler);
}
/**
@@ -319,7 +290,6 @@ public class RestResourceController implements InitializingBean {
* @param rel
* @param page
* @param assembler
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/{rel}")
@@ -327,9 +297,8 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory,
@PathVariable String model, @PathVariable UUID uuid, @PathVariable String rel,
Pageable page,
PagedResourcesAssembler assembler,
@RequestParam(required = false) String projection) {
return findRelInternal(request, response, apiCategory, model, uuid, rel, page, assembler, projection);
PagedResourcesAssembler assembler) {
return findRelInternal(request, response, apiCategory, model, uuid, rel, page, assembler);
}
/**
@@ -362,7 +331,6 @@ public class RestResourceController implements InitializingBean {
* @param relid
* @param page
* @param assembler
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_STRING_VERSION_STRONG +
@@ -371,9 +339,8 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory,
@PathVariable String model, @PathVariable String id, @PathVariable String rel,
@PathVariable String relid,
Pageable page, PagedResourcesAssembler assembler,
@RequestParam(required = false) String projection) throws Throwable {
return findRelEntryInternal(request, response, apiCategory, model, id, rel, relid, page, assembler, projection);
Pageable page, PagedResourcesAssembler assembler) throws Throwable {
return findRelEntryInternal(request, response, apiCategory, model, id, rel, relid, page, assembler);
}
@@ -458,8 +425,7 @@ public class RestResourceController implements InitializingBean {
if (modelObject == null) {
return ControllerUtils.toEmptyResponse(HttpStatus.CREATED);
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
DSpaceResource result = converter.toResource(modelObject);
//TODO manage HTTPHeader
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
}
@@ -491,8 +457,7 @@ public class RestResourceController implements InitializingBean {
if (modelObject == null) {
return ControllerUtils.toEmptyResponse(HttpStatus.CREATED);
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
DSpaceResource result = converter.toResource(modelObject);
//TODO manage HTTPHeader
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
}
@@ -530,8 +495,7 @@ public class RestResourceController implements InitializingBean {
}
if (modelObject != null) {
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
DSpaceResource result = converter.toResource(modelObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
} else {
return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT);
@@ -622,8 +586,7 @@ public class RestResourceController implements InitializingBean {
log.error(e.getMessage(), e);
return ControllerUtils.toEmptyResponse(HttpStatus.INTERNAL_SERVER_ERROR);
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
DSpaceResource result = converter.toResource(modelObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
}
@@ -660,8 +623,7 @@ public class RestResourceController implements InitializingBean {
List<DSpaceResource> resources = new ArrayList<>();
for (T modelObject : content) {
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
DSpaceResource result = converter.toResource(modelObject);
resources.add(result);
}
return ControllerUtils.toResponseEntity(HttpStatus.OK, null, Resources.wrap(resources));
@@ -740,8 +702,7 @@ public class RestResourceController implements InitializingBean {
log.error(e.getMessage(), e);
throw e;
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
DSpaceResource result = converter.toResource(modelObject);
//TODO manage HTTPHeader
return ControllerUtils.toResponseEntity(HttpStatus.OK, null, result);
@@ -758,7 +719,6 @@ public class RestResourceController implements InitializingBean {
* @param relid
* @param page
* @param assembler
* @param projection
* @return
*/
private <ID extends Serializable> ResourceSupport findRelEntryInternal(HttpServletRequest request,
@@ -766,26 +726,24 @@ public class RestResourceController implements InitializingBean {
String apiCategory, String model,
String id, String rel, String relid,
Pageable page,
PagedResourcesAssembler assembler,
String projection) throws Throwable {
PagedResourcesAssembler assembler)
throws Throwable {
checkModelPluralForm(apiCategory, model);
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
Class<RestAddressableModel> domainClass = repository.getDomainClass();
LinkRest linkRest = utils.getLinkRest(rel, domainClass);
LinkRest linkRest = utils.getClassLevelLinkRest(rel, domainClass);
if (linkRest != null) {
LinkRestRepository linkRepository = utils.getLinkResourceRepository(apiCategory, model, linkRest.name());
Method linkMethod = repositoryUtils.getLinkMethod("getResource", linkRepository);
Method linkMethod = utils.requireMethod(linkRepository.getClass(), "getResource");
try {
Object object = linkMethod.invoke(linkRepository, request, id, relid, page, projection);
Object object = linkMethod.invoke(linkRepository, request, id, relid, page, utils.obtainProjection());
Link link = linkTo(this.getClass(), apiCategory, model).slash(id).slash(rel).slash(relid).withSelfRel();
List result = new ArrayList();
result.add(object);
PageImpl<RestAddressableModel> pageResult = new PageImpl(result, page, 1);
Page<HALResource> halResources = pageResult.map(linkRepository::wrapResource);
halResources.forEach(linkService::addLinks);
Page<HALResource> halResources = pageResult.map(restObject -> converter.toResource(restObject));
return assembler.toResource(halResources, link);
} catch (InvocationTargetException e) {
// This catch has been made to resolve the issue that caused AuthorizeDenied exceptions for the methods
@@ -812,63 +770,52 @@ public class RestResourceController implements InitializingBean {
* @param apiCategory
* @param model
* @param uuid
* @param rel
* @param page
* @param assembler
* @param projection
* @return
*/
private <ID extends Serializable> ResourceSupport findRelInternal(HttpServletRequest request,
HttpServletResponse response, String apiCategory,
String model, ID uuid, String subpath,
Pageable page, PagedResourcesAssembler assembler,
String projection) {
Pageable page,
PagedResourcesAssembler assembler) {
checkModelPluralForm(apiCategory, model);
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
Class<RestAddressableModel> domainClass = repository.getDomainClass();
LinkRest linkRest = utils.getLinkRest(subpath, domainClass);
LinkRest linkRest = utils.getClassLevelLinkRest(subpath, domainClass);
PagedResources<? extends HALResource> result;
if (linkRest != null) {
LinkRestRepository linkRepository = utils.getLinkResourceRepository(apiCategory, model, linkRest.name());
Method linkMethod = repositoryUtils.getLinkMethod(linkRest.method(), linkRepository);
Method linkMethod = utils.requireMethod(linkRepository.getClass(), linkRest.method());
try {
if (Page.class.isAssignableFrom(linkMethod.getReturnType())) {
Page<? extends RestModel> pageResult = (Page<? extends RestAddressableModel>) linkMethod
.invoke(linkRepository, request, uuid, page, utils.obtainProjection(true));
if (linkMethod == null) {
// TODO custom exception
throw new RuntimeException(
"Method for relation " + subpath + " not found: " + linkRest.name() + ":" + linkRest.method());
} else {
try {
if (Page.class.isAssignableFrom(linkMethod.getReturnType())) {
Page<? extends RestModel> pageResult = (Page<? extends RestAddressableModel>) linkMethod
.invoke(linkRepository, request, uuid, page, projection);
Link link = null;
String querystring = request.getQueryString();
if (querystring != null && querystring.length() > 0) {
link = linkTo(this.getClass(), apiCategory, model).slash(uuid)
.slash(subpath + '?' + querystring).withSelfRel();
} else {
link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath).withSelfRel();
}
Page<HALResource> halResources = pageResult.map(linkRepository::wrapResource);
halResources.forEach(linkService::addLinks);
return assembler.toResource(halResources, link);
Link link = null;
String querystring = request.getQueryString();
if (querystring != null && querystring.length() > 0) {
link = linkTo(this.getClass(), apiCategory, model).slash(uuid)
.slash(subpath + '?' + querystring).withSelfRel();
} else {
RestModel object = (RestModel) linkMethod.invoke(linkRepository, request, uuid, page,
projection);
Link link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath)
.withSelfRel();
HALResource tmpresult = linkRepository.wrapResource(object);
tmpresult.add(link);
return tmpresult;
link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath).withSelfRel();
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
Page<HALResource> halResources = pageResult.map(object -> converter.toResource(object));
return assembler.toResource(halResources, link);
} else {
RestModel object = (RestModel) linkMethod.invoke(linkRepository, request, uuid, page,
utils.obtainProjection());
Link link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath)
.withSelfRel();
HALResource tmpresult = converter.toResource(object);
tmpresult.add(link);
return tmpresult;
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
RestAddressableModel modelObject = repository.findOne(uuid);
@@ -877,8 +824,7 @@ public class RestResourceController implements InitializingBean {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + uuid + " not found");
}
DSpaceResource resource = repository.wrapResource(modelObject, subpath);
linkService.addLinks(resource);
DSpaceResource resource = converter.toResource(modelObject);
String rel = null;
@@ -919,16 +865,7 @@ public class RestResourceController implements InitializingBean {
.getResourceRepository(fullList.get(0).getCategory(), fullList.get(0).getType());
PageImpl<RestAddressableModel> pageResult = new PageImpl(fullList.subList(start, end), page,
fullList.size());
result = assembler.toResource(pageResult.map(resourceRepository::wrapResource));
for (Resource subObj : result) {
if (subObj.getContent() instanceof HALResource) {
linkService.addLinks((HALResource) subObj.getContent());
}
}
return result;
return assembler.toResource(pageResult.map(converter::toResource));
} else {
if (resource.getEmbeddedResources().get(rel) == null) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
@@ -945,7 +882,6 @@ public class RestResourceController implements InitializingBean {
* @param model
* @param page
* @param assembler
* @param projection
* @return
*/
@RequestMapping(method = RequestMethod.GET)
@@ -954,18 +890,15 @@ public class RestResourceController implements InitializingBean {
@PathVariable String model,
Pageable page,
PagedResourcesAssembler assembler,
@RequestParam(required = false)
String projection,
HttpServletResponse response) {
DSpaceRestRepository<T, ?> repository = utils.getResourceRepository(apiCategory, model);
Link link = linkTo(methodOn(this.getClass(), apiCategory, model).findAll(apiCategory, model,
page, assembler, projection, response))
page, assembler, response))
.withSelfRel();
Page<DSpaceResource<T>> resources;
try {
resources = repository.findAll(page).map(repository::wrapResource);
resources.forEach(linkService::addLinks);
resources = repository.findAll(page).map(converter::toResource);
} catch (PaginationException pe) {
resources = new PageImpl<DSpaceResource<T>>(new ArrayList<DSpaceResource<T>>(), page, pe.getTotal());
} catch (RepositoryMethodNotImplementedException mne) {
@@ -1044,18 +977,15 @@ public class RestResourceController implements InitializingBean {
if (searchResult == null) {
resources = new PageImpl(new ArrayList(), pageable, 0);
} else {
resources = ((Page<T>) searchResult).map(repository::wrapResource);
resources = ((Page<T>) searchResult).map(converter::toResource);
}
resources.forEach(linkService::addLinks);
result = assembler.toResource(resources, link);
} else {
if (searchResult == null) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
DSpaceResource<T> dsResource = repository.wrapResource((T) searchResult);
linkService.addLinks(dsResource);
result = dsResource;
return converter.toResource((T) searchResult);
}
return result;
}
@@ -1204,9 +1134,7 @@ public class RestResourceController implements InitializingBean {
if (modelObject == null) {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
return result;
return converter.toResource(modelObject);
}
/**
@@ -1229,9 +1157,6 @@ public class RestResourceController implements InitializingBean {
if (modelObject == null) {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
return result;
return converter.toResource(modelObject);
}
}

View File

@@ -9,8 +9,8 @@ package org.dspace.app.rest;
import javax.servlet.http.HttpServletRequest;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.RootRest;
import org.dspace.app.rest.model.hateoas.RootResource;
import org.dspace.app.rest.repository.RootRestRepository;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,13 +39,11 @@ public class RootRestResourceController {
@Autowired
RootRestRepository rootRestRepository;
@Autowired
ConverterService converter;
@RequestMapping(method = RequestMethod.GET)
public RootResource listDefinedEndpoint(HttpServletRequest request) {
RootRest rootRest = rootRestRepository.getRoot();
RootResource rootResource = new RootResource(rootRest);
halLinkService.addLinks(rootResource);
return rootResource;
return converter.toResource(rootRestRepository.getRoot());
}
}

View File

@@ -0,0 +1,62 @@
/**
* 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.rest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.model.ProcessRest;
import org.dspace.app.rest.model.ScriptRest;
import org.dspace.app.rest.model.hateoas.ProcessResource;
import org.dspace.app.rest.repository.ScriptRestRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ControllerUtils;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* This controller adds additional subresource methods to allow connecting scripts with processes
*/
@RestController
@RequestMapping("/api/" + ScriptRest.CATEGORY + "/" + ScriptRest.PLURAL_NAME + "/{name}/processes")
public class ScriptProcessesController {
private static final Logger log = LogManager.getLogger();
@Autowired
private ConverterService converter;
@Autowired
private ScriptRestRepository scriptRestRepository;
/**
* This method can be called by sending a POST request to the system/scripts/{name}/processes endpoint
* This will start a process for the script that matches the given name
* @param scriptName The name of the script that we want to start a process for
* @return The ProcessResource object for the created process
* @throws Exception If something goes wrong
*/
@RequestMapping(method = RequestMethod.POST)
@PreAuthorize("hasAuthority('ADMIN')")
public ResponseEntity<ResourceSupport> startProcess(@PathVariable(name = "name") String scriptName)
throws Exception {
if (log.isTraceEnabled()) {
log.trace("Starting Process for Script with name: " + scriptName);
}
ProcessRest processRest = scriptRestRepository.startProcess(scriptName);
ProcessResource processResource = converter.toResource(processRest);
return ControllerUtils.toResponseEntity(HttpStatus.ACCEPTED, null, processResource);
}
}

View File

@@ -10,6 +10,7 @@ package org.dspace.app.rest;
import java.util.Arrays;
import java.util.UUID;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.RestAddressableModel;
@@ -47,6 +48,9 @@ public class StatisticsRestController implements InitializingBean {
@Autowired
private HalLinkService halLinkService;
@Autowired
private ConverterService converter;
@Autowired
private StatisticsRestRepository statisticsRestRepository;
@@ -65,12 +69,10 @@ public class StatisticsRestController implements InitializingBean {
@RequestMapping(method = RequestMethod.GET)
public StatisticsSupportResource getStatisticsSupport() throws Exception {
StatisticsSupportRest statisticsSupportRest = statisticsRestRepository.getStatisticsSupport();
StatisticsSupportResource statisticsSupportResource = new StatisticsSupportResource(statisticsSupportRest);
halLinkService.addLinks(statisticsSupportResource);
return statisticsSupportResource;
return converter.toResource(statisticsSupportRest);
}
@RequestMapping(method = RequestMethod.GET, value = "/viewevents/{uuid}")
public PagedResources<ViewEventResource> getViewEvent(@PathVariable(name = "uuid") UUID uuid) throws Exception {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
@@ -93,13 +95,13 @@ public class StatisticsRestController implements InitializingBean {
@RequestMapping(method = RequestMethod.POST, value = "/viewevents")
public ResponseEntity<ResourceSupport> postViewEvent() throws Exception {
ViewEventResource result = new ViewEventResource(viewEventRestRepository.createViewEvent(), utils);
ViewEventResource result = converter.toResource(viewEventRestRepository.createViewEvent());
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
}
@RequestMapping(method = RequestMethod.POST, value = "/searchevents")
public ResponseEntity<ResourceSupport> postSearchEvent() throws Exception {
SearchEventResource result = new SearchEventResource(searchEventRestRepository.createSearchEvent(), utils);
SearchEventResource result = converter.toResource(searchEventRestRepository.createSearchEvent());
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, null, result);
}

View File

@@ -14,14 +14,14 @@ import java.net.URI;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.dspace.app.rest.converter.GenericDSpaceObjectConverter;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.model.DSpaceObjectRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.app.rest.utils.Utils;
import org.dspace.content.DSpaceObject;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.DSpaceObjectService;
@@ -57,6 +57,9 @@ public class UUIDLookupRestController implements InitializingBean {
@Autowired
private ContentServiceFactory contentServiceFactory;
@Autowired
private Utils utils;
private static final Logger log =
Logger.getLogger(UUIDLookupRestController.class);
@@ -64,7 +67,7 @@ public class UUIDLookupRestController implements InitializingBean {
private DiscoverableEndpointsService discoverableEndpointsService;
@Autowired
private GenericDSpaceObjectConverter converter;
private ConverterService converter;
@Override
public void afterPropertiesSet() throws Exception {
@@ -92,7 +95,7 @@ public class UUIDLookupRestController implements InitializingBean {
.getDSpaceObjectServices()) {
DSpaceObject dso = dSpaceObjectService.find(context, uuid);
if (dso != null) {
DSpaceObjectRest dsor = converter.convert(dso);
DSpaceObjectRest dsor = converter.toRest(dso, utils.obtainProjection());
URI link = linkTo(dsor.getController(), dsor.getCategory(), dsor.getTypePlural())
.slash(dsor.getId()).toUri();
response.setStatus(HttpServletResponse.SC_FOUND);

View File

@@ -17,6 +17,7 @@ import org.dspace.app.rest.model.AInprogressSubmissionRest;
import org.dspace.app.rest.model.ErrorRest;
import org.dspace.app.rest.model.SubmissionDefinitionRest;
import org.dspace.app.rest.model.SubmissionSectionRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.submit.AbstractRestProcessingStep;
import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.util.SubmissionConfigReader;
@@ -47,22 +48,13 @@ public abstract class AInprogressItemConverter<T extends InProgressSubmission<ID
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(AInprogressItemConverter.class);
@Autowired
private EPersonConverter epersonConverter;
@Autowired
private ItemConverter itemConverter;
@Autowired
private CollectionConverter collectionConverter;
protected SubmissionConfigReader submissionConfigReader;
@Autowired
private SubmissionDefinitionConverter submissionDefinitionConverter;
private ConverterService converter;
@Autowired
private SubmissionSectionConverter submissionSectionConverter;
protected SubmissionConfigReader submissionConfigReader;
@Autowired
SubmissionService submissionService;
@@ -70,7 +62,7 @@ public abstract class AInprogressItemConverter<T extends InProgressSubmission<ID
submissionConfigReader = new SubmissionConfigReader();
}
protected void fillFromModel(T obj, R witem) {
protected void fillFromModel(T obj, R witem, Projection projection) {
Collection collection = obj.getCollection();
Item item = obj.getItem();
EPerson submitter = null;
@@ -81,17 +73,17 @@ public abstract class AInprogressItemConverter<T extends InProgressSubmission<ID
}
witem.setId(obj.getID());
witem.setCollection(collection != null ? collectionConverter.convert(collection) : null);
witem.setItem(itemConverter.convert(item));
witem.setSubmitter(epersonConverter.convert(submitter));
witem.setCollection(collection != null ? converter.toRest(collection, projection) : null);
witem.setItem(converter.toRest(item, projection));
witem.setSubmitter(converter.toRest(submitter, projection));
// 1. retrieve the submission definition
// 2. iterate over the submission section to allow to plugin additional
// info
if (collection != null) {
SubmissionDefinitionRest def = submissionDefinitionConverter
.convert(submissionConfigReader.getSubmissionConfigByCollection(collection.getHandle()));
SubmissionDefinitionRest def = converter.toRest(
submissionConfigReader.getSubmissionConfigByCollection(collection.getHandle()), projection);
witem.setSubmissionDefinition(def);
for (SubmissionSectionRest sections : def.getPanels()) {
SubmissionStepConfig stepConfig = submissionSectionConverter.toModel(sections);
@@ -150,4 +142,4 @@ public abstract class AInprogressItemConverter<T extends InProgressSubmission<ID
}
}
}
}

View File

@@ -7,8 +7,8 @@
*/
package org.dspace.app.rest.converter;
import org.apache.commons.lang3.NotImplementedException;
import org.dspace.app.rest.model.AuthorityEntryRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.AuthorityUtils;
import org.dspace.content.authority.Choice;
import org.springframework.stereotype.Component;
@@ -17,7 +17,7 @@ import org.springframework.stereotype.Component;
* This is the converter from/to the Choice in the DSpace API data
* model and the REST data model.
*
* TODO please do not use this convert but use the wrapper {@link AuthorityUtils#convertEntry(Choice, String)}}
* TODO please do not use this convert but use the wrapper {@link AuthorityUtils#convertEntry(Choice, String, String)}
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
@@ -25,8 +25,9 @@ import org.springframework.stereotype.Component;
public class AuthorityEntryRestConverter implements DSpaceConverter<Choice, AuthorityEntryRest> {
@Override
public AuthorityEntryRest fromModel(Choice choice) {
public AuthorityEntryRest convert(Choice choice, Projection projection) {
AuthorityEntryRest entry = new AuthorityEntryRest();
entry.setProjection(projection);
entry.setValue(choice.value);
entry.setDisplay(choice.label);
entry.setId(choice.authority);
@@ -35,7 +36,7 @@ public class AuthorityEntryRestConverter implements DSpaceConverter<Choice, Auth
}
@Override
public Choice toModel(AuthorityEntryRest obj) {
throw new NotImplementedException("Method not implemented");
public Class<Choice> getModelClass() {
return Choice.class;
}
}

View File

@@ -7,8 +7,8 @@
*/
package org.dspace.app.rest.converter;
import org.apache.commons.lang3.NotImplementedException;
import org.dspace.app.rest.model.AuthorityRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.app.rest.utils.AuthorityUtils;
import org.dspace.content.authority.ChoiceAuthority;
import org.springframework.stereotype.Component;
@@ -18,7 +18,7 @@ import org.springframework.stereotype.Component;
* model and the REST data model
*
* TODO please do not use this convert but use the wrapper
* {@link AuthorityUtils#convertAuthority(ChoiceAuthority, String)}
* {@link AuthorityUtils#convertAuthority(ChoiceAuthority, String, String)}
*
* @author Luigi Andrea Pascarelli (luigiandrea.pascarelli at 4science.it)
*/
@@ -26,8 +26,9 @@ import org.springframework.stereotype.Component;
public class AuthorityRestConverter implements DSpaceConverter<ChoiceAuthority, AuthorityRest> {
@Override
public AuthorityRest fromModel(ChoiceAuthority step) {
public AuthorityRest convert(ChoiceAuthority step, Projection projection) {
AuthorityRest authorityRest = new AuthorityRest();
authorityRest.setProjection(projection);
authorityRest.setHierarchical(step.isHierarchical());
authorityRest.setScrollable(step.isScrollable());
authorityRest.setIdentifier(step.hasIdentifier());
@@ -35,7 +36,7 @@ public class AuthorityRestConverter implements DSpaceConverter<ChoiceAuthority,
}
@Override
public ChoiceAuthority toModel(AuthorityRest obj) {
throw new NotImplementedException("Method not implemented");
public Class<ChoiceAuthority> getModelClass() {
return ChoiceAuthority.class;
}
}

View File

@@ -10,9 +10,9 @@ package org.dspace.app.rest.converter;
import java.sql.SQLException;
import java.util.List;
import org.dspace.app.rest.model.BitstreamFormatRest;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.CheckSumRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.springframework.beans.factory.annotation.Autowired;
@@ -25,19 +25,14 @@ import org.springframework.stereotype.Component;
* @author Andrea Bollini (andrea.bollini at 4science.it)
*/
@Component
public class BitstreamConverter
extends DSpaceObjectConverter<org.dspace.content.Bitstream, org.dspace.app.rest.model.BitstreamRest> {
@Autowired(required = true)
BitstreamFormatConverter bfConverter;
public class BitstreamConverter extends DSpaceObjectConverter<Bitstream, BitstreamRest> {
@Autowired
ConverterService converter;
@Override
public org.dspace.content.Bitstream toModel(org.dspace.app.rest.model.BitstreamRest obj) {
return super.toModel(obj);
}
@Override
public BitstreamRest fromModel(org.dspace.content.Bitstream obj) {
BitstreamRest b = super.fromModel(obj);
public BitstreamRest convert(org.dspace.content.Bitstream obj, Projection projection) {
BitstreamRest b = super.convert(obj, projection);
b.setSequenceId(obj.getSequenceID());
List<Bundle> bundles = null;
try {
@@ -53,14 +48,11 @@ public class BitstreamConverter
checksum.setCheckSumAlgorithm(obj.getChecksumAlgorithm());
checksum.setValue(obj.getChecksum());
b.setCheckSum(checksum);
BitstreamFormatRest format = null;
try {
format = bfConverter.fromModel(obj.getFormat(null));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
b.setFormat(converter.toRest(obj.getFormat(null), projection));
} catch (SQLException e) {
throw new RuntimeException(e);
}
b.setFormat(format);
b.setSizeBytes(obj.getSizeBytes());
return b;
}
@@ -71,7 +63,7 @@ public class BitstreamConverter
}
@Override
protected Class<Bitstream> getModelClass() {
public Class<Bitstream> getModelClass() {
return Bitstream.class;
}
}

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest.converter;
import org.dspace.app.rest.model.BitstreamFormatRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.service.BitstreamFormatService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,8 +27,9 @@ public class BitstreamFormatConverter implements DSpaceConverter<BitstreamFormat
BitstreamFormatService bitstreamFormatService;
@Override
public BitstreamFormatRest fromModel(BitstreamFormat obj) {
public BitstreamFormatRest convert(BitstreamFormat obj, Projection projection) {
BitstreamFormatRest bf = new BitstreamFormatRest();
bf.setProjection(projection);
bf.setId(obj.getID());
bf.setShortDescription(obj.getShortDescription());
bf.setDescription(obj.getDescription());
@@ -43,8 +45,7 @@ public class BitstreamFormatConverter implements DSpaceConverter<BitstreamFormat
}
@Override
public BitstreamFormat toModel(BitstreamFormatRest obj) {
// TODO Auto-generated method stub
return null;
public Class<BitstreamFormat> getModelClass() {
return BitstreamFormat.class;
}
}

Some files were not shown because too many files have changed in this diff Show More