diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index f000f2e1ba..fc7349b379 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -1,4 +1,6 @@ - + 4.0.0 org.dspace dspace-api @@ -12,7 +14,7 @@ org.dspace dspace-parent - 7.2 + 7.3-SNAPSHOT .. @@ -335,16 +337,29 @@ + + org.apache.logging.log4j + log4j-api + org.hibernate - hibernate-ehcache - - - - org.javassist - javassist - - + hibernate-core + + + + org.javassist + javassist + + + + + org.hibernate + hibernate-jcache + + + org.ehcache + ehcache + ${ehcache.version} org.hibernate @@ -358,7 +373,7 @@ org.hibernate.javax.persistence hibernate-jpa-2.1-api - 1.0.0.Final + 1.0.2.Final @@ -379,7 +394,7 @@ org.ow2.asm asm-commons - + org.bouncycastle bcpkix-jdk15on @@ -505,7 +520,7 @@ org.jdom - jdom + jdom2 org.apache.pdfbox @@ -515,18 +530,6 @@ org.apache.pdfbox fontbox - - org.apache.poi - poi-scratchpad - - - xalan - xalan - - - xerces - xercesImpl - com.ibm.icu icu4j @@ -566,9 +569,12 @@ - org.rometools + com.rometools + rome + + + com.rometools rome-modules - 1.0 org.jbibtex @@ -579,28 +585,22 @@ org.apache.httpcomponents httpclient + + org.apache.httpcomponents + httpcore + + + org.apache.httpcomponents + httpmime + + org.apache.solr solr-solrj ${solr.client.version} - - - - org.eclipse.jetty - jetty-http - - - org.eclipse.jetty - jetty-io - - - org.eclipse.jetty - jetty-util - - - + org.apache.solr @@ -628,39 +628,10 @@ - - org.apache.solr - solr-cell - - - - org.apache.commons - commons-text - - - - org.eclipse.jetty - jetty-http - - - org.eclipse.jetty - jetty-io - - - org.eclipse.jetty - jetty-util - - - org.apache.lucene lucene-core - - - org.apache.tika - tika-parsers - org.apache.lucene lucene-analyzers-icu @@ -676,9 +647,15 @@ lucene-analyzers-stempel test + + - org.apache.xmlbeans - xmlbeans + org.apache.tika + tika-core + + + org.apache.tika + tika-parsers-standard-package @@ -702,13 +679,6 @@ 1.1.1 - - - com.google.code.gson - gson - compile - - com.google.guava guava @@ -736,7 +706,7 @@ org.flywaydb flyway-core - 6.5.7 + 8.4.4 @@ -792,44 +762,6 @@ jaxb-runtime - - - org.apache.ws.commons.axiom - axiom-impl - ${axiom.version} - - - - org.apache.geronimo.specs - * - - - - org.codehaus.woodstox - woodstox-core-asl - - - - - org.apache.ws.commons.axiom - axiom-api - ${axiom.version} - - - - org.apache.geronimo.specs - * - - - - org.codehaus.woodstox - woodstox-core-asl - - - - org.glassfish.jersey.core @@ -885,6 +817,14 @@ 20180130 + + + com.github.stefanbirkner + system-rules + 1.19.0 + test + + com.opencsv @@ -896,7 +836,6 @@ org.apache.velocity velocity-engine-core - jar @@ -918,13 +857,6 @@ 2.0.0 - - com.github.stefanbirkner - system-rules - 1.19.0 - test - - org.mock-server mockserver-junit-rule @@ -971,7 +903,7 @@ org.apache.velocity velocity-engine-core - 2.2 + 2.3 org.xmlunit diff --git a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java index 42461d7210..2677cb2050 100644 --- a/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/MetadataImporter.java @@ -11,13 +11,16 @@ import java.io.IOException; import java.sql.SQLException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.apache.xpath.XPathAPI; import org.dspace.authorize.AuthorizeException; import org.dspace.content.MetadataField; import org.dspace.content.MetadataSchema; @@ -90,7 +93,7 @@ public class MetadataImporter { public static void main(String[] args) throws ParseException, SQLException, IOException, TransformerException, ParserConfigurationException, AuthorizeException, SAXException, - NonUniqueMetadataException, RegistryImportException { + NonUniqueMetadataException, RegistryImportException, XPathExpressionException { // create an options object and populate it CommandLineParser parser = new DefaultParser(); @@ -124,8 +127,8 @@ public class MetadataImporter { * @throws RegistryImportException if import fails */ public static void loadRegistry(String file, boolean forceUpdate) - throws SQLException, IOException, TransformerException, ParserConfigurationException, - AuthorizeException, SAXException, NonUniqueMetadataException, RegistryImportException { + throws SQLException, IOException, TransformerException, ParserConfigurationException, AuthorizeException, + SAXException, NonUniqueMetadataException, RegistryImportException, XPathExpressionException { Context context = null; try { @@ -137,7 +140,9 @@ public class MetadataImporter { Document document = RegistryImporter.loadXML(file); // Get the nodes corresponding to types - NodeList schemaNodes = XPathAPI.selectNodeList(document, "/dspace-dc-types/dc-schema"); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList schemaNodes = (NodeList) xPath.compile("/dspace-dc-types/dc-schema") + .evaluate(document, XPathConstants.NODESET); // Add each one as a new format to the registry for (int i = 0; i < schemaNodes.getLength(); i++) { @@ -146,7 +151,8 @@ public class MetadataImporter { } // Get the nodes corresponding to types - NodeList typeNodes = XPathAPI.selectNodeList(document, "/dspace-dc-types/dc-type"); + NodeList typeNodes = (NodeList) xPath.compile("/dspace-dc-types/dc-type") + .evaluate(document, XPathConstants.NODESET); // Add each one as a new format to the registry for (int i = 0; i < typeNodes.getLength(); i++) { @@ -178,8 +184,8 @@ public class MetadataImporter { * @throws RegistryImportException if import fails */ private static void loadSchema(Context context, Node node, boolean updateExisting) - throws SQLException, IOException, TransformerException, - AuthorizeException, NonUniqueMetadataException, RegistryImportException { + throws SQLException, AuthorizeException, NonUniqueMetadataException, RegistryImportException, + XPathExpressionException { // Get the values String name = RegistryImporter.getElementData(node, "name"); String namespace = RegistryImporter.getElementData(node, "namespace"); @@ -236,8 +242,8 @@ public class MetadataImporter { * @throws RegistryImportException if import fails */ private static void loadType(Context context, Node node) - throws SQLException, IOException, TransformerException, - AuthorizeException, NonUniqueMetadataException, RegistryImportException { + throws SQLException, IOException, AuthorizeException, NonUniqueMetadataException, RegistryImportException, + XPathExpressionException { // Get the values String schema = RegistryImporter.getElementData(node, "schema"); String element = RegistryImporter.getElementData(node, "element"); diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java index 5b5f70412a..27a6534213 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java @@ -13,8 +13,11 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; -import org.apache.xpath.XPathAPI; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -72,9 +75,10 @@ public class RegistryImporter { * @throws TransformerException if error */ public static String getElementData(Node parentElement, String childName) - throws TransformerException { + throws XPathExpressionException { // Grab the child node - Node childNode = XPathAPI.selectSingleNode(parentElement, childName); + XPath xPath = XPathFactory.newInstance().newXPath(); + Node childNode = (Node) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODE); if (childNode == null) { // No child node, so no values @@ -115,9 +119,10 @@ public class RegistryImporter { * @throws TransformerException if error */ public static String[] getRepeatedElementData(Node parentElement, - String childName) throws TransformerException { + String childName) throws XPathExpressionException { // Grab the child node - NodeList childNodes = XPathAPI.selectNodeList(parentElement, childName); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList childNodes = (NodeList) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODESET); String[] data = new String[childNodes.getLength()]; diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index 2b6a01b558..bbf320a0d5 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -16,9 +16,12 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.logging.log4j.Logger; -import org.apache.xpath.XPathAPI; import org.dspace.authorize.AuthorizeException; import org.dspace.content.BitstreamFormat; import org.dspace.content.factory.ContentServiceFactory; @@ -122,12 +125,13 @@ public class RegistryLoader { */ public static void loadBitstreamFormats(Context context, String filename) throws SQLException, IOException, ParserConfigurationException, - SAXException, TransformerException, AuthorizeException { + SAXException, TransformerException, AuthorizeException, XPathExpressionException { Document document = loadXML(filename); // Get the nodes corresponding to formats - NodeList typeNodes = XPathAPI.selectNodeList(document, - "dspace-bitstream-types/bitstream-type"); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList typeNodes = (NodeList) xPath.compile("dspace-bitstream-types/bitstream-type") + .evaluate(document, XPathConstants.NODESET); // Add each one as a new format to the registry for (int i = 0; i < typeNodes.getLength(); i++) { @@ -151,8 +155,7 @@ public class RegistryLoader { * @throws AuthorizeException if authorization error */ private static void loadFormat(Context context, Node node) - throws SQLException, IOException, TransformerException, - AuthorizeException { + throws SQLException, AuthorizeException, XPathExpressionException { // Get the values String mimeType = getElementData(node, "mimetype"); String shortDesc = getElementData(node, "short_description"); @@ -231,9 +234,10 @@ public class RegistryLoader { * @throws TransformerException if transformer error */ private static String getElementData(Node parentElement, String childName) - throws TransformerException { + throws XPathExpressionException { // Grab the child node - Node childNode = XPathAPI.selectSingleNode(parentElement, childName); + XPath xPath = XPathFactory.newInstance().newXPath(); + Node childNode = (Node) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODE); if (childNode == null) { // No child node, so no values @@ -274,9 +278,10 @@ public class RegistryLoader { * @throws TransformerException if transformer error */ private static String[] getRepeatedElementData(Node parentElement, - String childName) throws TransformerException { + String childName) throws XPathExpressionException { // Grab the child node - NodeList childNodes = XPathAPI.selectNodeList(parentElement, childName); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList childNodes = (NodeList) xPath.compile(childName).evaluate(parentElement, XPathConstants.NODESET); String[] data = new String[childNodes.getLength()]; diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index 89d9ffe5a8..41a657dd61 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -30,6 +30,10 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -38,7 +42,6 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.apache.xpath.XPathAPI; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -52,9 +55,9 @@ import org.dspace.content.service.CommunityService; import org.dspace.core.Context; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; -import org.jdom.Element; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; +import org.jdom2.Element; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -137,8 +140,8 @@ public class StructBuilder { * @throws TransformerException if the input document is invalid. */ public static void main(String[] argv) - throws ParserConfigurationException, SQLException, - FileNotFoundException, IOException, TransformerException { + throws ParserConfigurationException, SQLException, + IOException, TransformerException, XPathExpressionException { // Define command line options. Options options = new Options(); @@ -240,7 +243,7 @@ public class StructBuilder { * @throws SQLException */ static void importStructure(Context context, InputStream input, OutputStream output) - throws IOException, ParserConfigurationException, SQLException, TransformerException { + throws IOException, ParserConfigurationException, SQLException, TransformerException, XPathExpressionException { // load the XML Document document = null; @@ -258,13 +261,15 @@ public class StructBuilder { // is properly structured. try { validate(document); - } catch (TransformerException ex) { + } catch (XPathExpressionException ex) { System.err.format("The input document is invalid: %s%n", ex.getMessage()); System.exit(1); } // Check for 'identifier' attributes -- possibly output by this class. - NodeList identifierNodes = XPathAPI.selectNodeList(document, "//*[@identifier]"); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList identifierNodes = (NodeList) xPath.compile("//*[@identifier]") + .evaluate(document, XPathConstants.NODESET); if (identifierNodes.getLength() > 0) { System.err.println("The input document has 'identifier' attributes, which will be ignored."); } @@ -287,7 +292,8 @@ public class StructBuilder { Element[] elements = new Element[]{}; try { // get the top level community list - NodeList first = XPathAPI.selectNodeList(document, "/import_structure/community"); + NodeList first = (NodeList) xPath.compile("/import_structure/community") + .evaluate(document, XPathConstants.NODESET); // run the import starting with the top level communities elements = handleCommunities(context, first, null); @@ -307,7 +313,7 @@ public class StructBuilder { } // finally write the string into the output file. - final org.jdom.Document xmlOutput = new org.jdom.Document(root); + final org.jdom2.Document xmlOutput = new org.jdom2.Document(root); try { new XMLOutputter().output(xmlOutput, output); } catch (IOException e) { @@ -411,7 +417,7 @@ public class StructBuilder { } // Now write the structure out. - org.jdom.Document xmlOutput = new org.jdom.Document(rootElement); + org.jdom2.Document xmlOutput = new org.jdom2.Document(rootElement); try { XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); outputter.output(xmlOutput, output); @@ -456,14 +462,16 @@ public class StructBuilder { * @throws TransformerException if transformer error */ private static void validate(org.w3c.dom.Document document) - throws TransformerException { + throws XPathExpressionException { StringBuilder err = new StringBuilder(); boolean trip = false; err.append("The following errors were encountered parsing the source XML.\n"); err.append("No changes have been made to the DSpace instance.\n\n"); - NodeList first = XPathAPI.selectNodeList(document, "/import_structure/community"); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList first = (NodeList) xPath.compile("/import_structure/community") + .evaluate(document, XPathConstants.NODESET); if (first.getLength() == 0) { err.append("-There are no top level communities in the source document."); System.out.println(err.toString()); @@ -493,14 +501,15 @@ public class StructBuilder { * no errors. */ private static String validateCommunities(NodeList communities, int level) - throws TransformerException { + throws XPathExpressionException { StringBuilder err = new StringBuilder(); boolean trip = false; String errs = null; + XPath xPath = XPathFactory.newInstance().newXPath(); for (int i = 0; i < communities.getLength(); i++) { Node n = communities.item(i); - NodeList name = XPathAPI.selectNodeList(n, "name"); + NodeList name = (NodeList) xPath.compile("name").evaluate(n, XPathConstants.NODESET); if (name.getLength() != 1) { String pos = Integer.toString(i + 1); err.append("-The level ").append(level) @@ -510,7 +519,7 @@ public class StructBuilder { } // validate sub communities - NodeList subCommunities = XPathAPI.selectNodeList(n, "community"); + NodeList subCommunities = (NodeList) xPath.compile("community").evaluate(n, XPathConstants.NODESET); String comErrs = validateCommunities(subCommunities, level + 1); if (comErrs != null) { err.append(comErrs); @@ -518,7 +527,7 @@ public class StructBuilder { } // validate collections - NodeList collections = XPathAPI.selectNodeList(n, "collection"); + NodeList collections = (NodeList) xPath.compile("collection").evaluate(n, XPathConstants.NODESET); String colErrs = validateCollections(collections, level + 1); if (colErrs != null) { err.append(colErrs); @@ -542,14 +551,15 @@ public class StructBuilder { * @return the errors to be generated by the calling method, or null if none */ private static String validateCollections(NodeList collections, int level) - throws TransformerException { + throws XPathExpressionException { StringBuilder err = new StringBuilder(); boolean trip = false; String errs = null; + XPath xPath = XPathFactory.newInstance().newXPath(); for (int i = 0; i < collections.getLength(); i++) { Node n = collections.item(i); - NodeList name = XPathAPI.selectNodeList(n, "name"); + NodeList name = (NodeList) xPath.compile("name").evaluate(n, XPathConstants.NODESET); if (name.getLength() != 1) { String pos = Integer.toString(i + 1); err.append("-The level ").append(level) @@ -613,8 +623,9 @@ public class StructBuilder { * created communities (e.g. the handles they have been assigned) */ private static Element[] handleCommunities(Context context, NodeList communities, Community parent) - throws TransformerException, SQLException, AuthorizeException { + throws TransformerException, SQLException, AuthorizeException, XPathExpressionException { Element[] elements = new Element[communities.getLength()]; + XPath xPath = XPathFactory.newInstance().newXPath(); for (int i = 0; i < communities.getLength(); i++) { Community community; @@ -634,7 +645,7 @@ public class StructBuilder { // now update the metadata Node tn = communities.item(i); for (Map.Entry entry : communityMap.entrySet()) { - NodeList nl = XPathAPI.selectNodeList(tn, entry.getKey()); + NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET); if (nl.getLength() == 1) { communityService.setMetadataSingleValue(context, community, entry.getValue(), null, getStringValue(nl.item(0))); @@ -700,11 +711,11 @@ public class StructBuilder { } // handle sub communities - NodeList subCommunities = XPathAPI.selectNodeList(tn, "community"); + NodeList subCommunities = (NodeList) xPath.compile("community").evaluate(tn, XPathConstants.NODESET); Element[] subCommunityElements = handleCommunities(context, subCommunities, community); // handle collections - NodeList collections = XPathAPI.selectNodeList(tn, "collection"); + NodeList collections = (NodeList) xPath.compile("collection").evaluate(tn, XPathConstants.NODESET); Element[] collectionElements = handleCollections(context, collections, community); int j; @@ -731,8 +742,9 @@ public class StructBuilder { * created collections (e.g. the handle) */ private static Element[] handleCollections(Context context, NodeList collections, Community parent) - throws TransformerException, SQLException, AuthorizeException { + throws SQLException, AuthorizeException, XPathExpressionException { Element[] elements = new Element[collections.getLength()]; + XPath xPath = XPathFactory.newInstance().newXPath(); for (int i = 0; i < collections.getLength(); i++) { Element element = new Element("collection"); @@ -745,7 +757,7 @@ public class StructBuilder { // import the rest of the metadata Node tn = collections.item(i); for (Map.Entry entry : collectionMap.entrySet()) { - NodeList nl = XPathAPI.selectNodeList(tn, entry.getKey()); + NodeList nl = (NodeList) xPath.compile(entry.getKey()).evaluate(tn, XPathConstants.NODESET); if (nl.getLength() == 1) { collectionService.setMetadataSingleValue(context, collection, entry.getValue(), null, getStringValue(nl.item(0))); diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataDeletionScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataDeletionScriptConfiguration.java index b8d41318db..9ccd53944a 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataDeletionScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataDeletionScriptConfiguration.java @@ -41,10 +41,8 @@ public class MetadataDeletionScriptConfiguration ext Options options = new Options(); options.addOption("m", "metadata", true, "metadata field name"); - options.getOption("m").setType(String.class); options.addOption("l", "list", false, "lists the metadata fields that can be deleted"); - options.getOption("l").setType(boolean.class); super.options = options; } diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java index 0c513c4667..31556afc8d 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportScriptConfiguration.java @@ -54,12 +54,9 @@ public class MetadataExportScriptConfiguration extends Options options = new Options(); options.addOption("i", "id", true, "ID or handle of thing to export (item, collection, or community)"); - options.getOption("i").setType(String.class); options.addOption("a", "all", false, "include all metadata fields that are not normally changed (e.g. provenance)"); - options.getOption("a").setType(boolean.class); options.addOption("h", "help", false, "help"); - options.getOption("h").setType(boolean.class); super.options = options; diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportCliScriptConfiguration.java index 038df616ca..7e1537fe9d 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportCliScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportCliScriptConfiguration.java @@ -19,7 +19,6 @@ public class MetadataImportCliScriptConfiguration extends MetadataImportScriptCo public Options getOptions() { Options options = super.getOptions(); options.addOption("e", "email", true, "email address or user id of user (required if adding new items)"); - options.getOption("e").setType(String.class); options.getOption("e").setRequired(true); super.options = options; return options; diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java index 07e6a9aec9..65994040ba 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImportScriptConfiguration.java @@ -59,20 +59,14 @@ public class MetadataImportScriptConfiguration extends options.getOption("f").setRequired(true); options.addOption("s", "silent", false, "silent operation - doesn't request confirmation of changes USE WITH CAUTION"); - options.getOption("s").setType(boolean.class); options.addOption("w", "workflow", false, "workflow - when adding new items, use collection workflow"); - options.getOption("w").setType(boolean.class); options.addOption("n", "notify", false, "notify - when adding new items using a workflow, send notification emails"); - options.getOption("n").setType(boolean.class); options.addOption("v", "validate-only", false, "validate - just validate the csv, don't run the import"); - options.getOption("v").setType(boolean.class); options.addOption("t", "template", false, "template - when adding new items, use the collection template (if it exists)"); - options.getOption("t").setType(boolean.class); options.addOption("h", "help", false, "help"); - options.getOption("h").setType(boolean.class); super.options = options; } diff --git a/dspace-api/src/main/java/org/dspace/app/harvest/HarvestScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/harvest/HarvestScriptConfiguration.java index 6290562143..982973e47c 100644 --- a/dspace-api/src/main/java/org/dspace/app/harvest/HarvestScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/harvest/HarvestScriptConfiguration.java @@ -43,22 +43,14 @@ public class HarvestScriptConfiguration extends ScriptConfigu public Options getOptions() { Options options = new Options(); options.addOption("p", "purge", false, "delete all items in the collection"); - options.getOption("p").setType(boolean.class); options.addOption("r", "run", false, "run the standard harvest procedure"); - options.getOption("r").setType(boolean.class); options.addOption("g", "ping", false, "test the OAI server and set"); - options.getOption("g").setType(boolean.class); options.addOption("s", "setup", false, "Set the collection up for harvesting"); - options.getOption("s").setType(boolean.class); options.addOption("S", "start", false, "start the harvest loop"); - options.getOption("S").setType(boolean.class); options.addOption("R", "reset", false, "reset harvest status on all collections"); - options.getOption("R").setType(boolean.class); options.addOption("P", "purgeCollections", false, "purge all harvestable collections"); - options.getOption("P").setType(boolean.class); options.addOption("o", "reimport", false, "reimport all items in the collection, " + "this is equivalent to -p -r, purging all items in a collection and reimporting them"); - options.getOption("o").setType(boolean.class); options.addOption("c", "collection", true, "harvesting collection (handle or id)"); options.addOption("t", "type", true, @@ -72,7 +64,6 @@ public class HarvestScriptConfiguration extends ScriptConfigu "crosswalk in dspace.cfg"); options.addOption("h", "help", false, "help"); - options.getOption("h").setType(boolean.class); return options; } diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 21566e2b9b..a034fe963c 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -51,6 +51,10 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.commons.collections4.ComparatorUtils; import org.apache.commons.io.FileDeleteStrategy; @@ -59,7 +63,6 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.logging.log4j.Logger; -import org.apache.xpath.XPathAPI; import org.dspace.app.itemimport.service.ItemImportService; import org.dspace.app.util.LocalSchemaFilenameFilter; import org.dspace.app.util.RelationshipUtils; @@ -861,7 +864,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea // Load all metadata schemas into the item. protected void loadMetadata(Context c, Item myitem, String path) throws SQLException, IOException, ParserConfigurationException, - SAXException, TransformerException, AuthorizeException { + SAXException, TransformerException, AuthorizeException, XPathExpressionException { // Load the dublin core metadata loadDublinCore(c, myitem, path + "dublin_core.xml"); @@ -875,14 +878,15 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea protected void loadDublinCore(Context c, Item myitem, String filename) throws SQLException, IOException, ParserConfigurationException, - SAXException, TransformerException, AuthorizeException { + SAXException, TransformerException, AuthorizeException, XPathExpressionException { Document document = loadXML(filename); // Get the schema, for backward compatibility we will default to the // dublin core schema if the schema name is not available in the import // file String schema; - NodeList metadata = XPathAPI.selectNodeList(document, "/dublin_core"); + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList metadata = (NodeList) xPath.compile("/dublin_core").evaluate(document, XPathConstants.NODESET); Node schemaAttr = metadata.item(0).getAttributes().getNamedItem( "schema"); if (schemaAttr == null) { @@ -892,8 +896,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea } // Get the nodes corresponding to formats - NodeList dcNodes = XPathAPI.selectNodeList(document, - "/dublin_core/dcvalue"); + NodeList dcNodes = (NodeList) xPath.compile("/dublin_core/dcvalue").evaluate(document, XPathConstants.NODESET); if (!isQuiet) { System.out.println("\tLoading dublin core from " + filename); diff --git a/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java b/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java index 5c2138a590..910eb434d1 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java +++ b/dspace-api/src/main/java/org/dspace/app/itemupdate/MetadataUtilities.java @@ -27,10 +27,12 @@ import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.commons.lang3.StringUtils; -import org.apache.xpath.XPathAPI; -import org.dspace.authorize.AuthorizeException; import org.dspace.content.Item; import org.dspace.content.MetadataField; import org.dspace.content.MetadataSchema; @@ -170,24 +172,21 @@ public class MetadataUtilities { * @param docBuilder DocumentBuilder * @param is - InputStream of dublin_core.xml * @return list of DtoMetadata representing the metadata fields relating to an Item - * @throws SQLException if database error * @throws IOException if IO error * @throws ParserConfigurationException if parser config error * @throws SAXException if XML error - * @throws TransformerException if transformer error - * @throws AuthorizeException if authorization error */ public static List loadDublinCore(DocumentBuilder docBuilder, InputStream is) - throws SQLException, IOException, ParserConfigurationException, - SAXException, TransformerException, AuthorizeException { + throws IOException, XPathExpressionException, SAXException { Document document = docBuilder.parse(is); List dtomList = new ArrayList(); // Get the schema, for backward compatibility we will default to the // dublin core schema if the schema name is not available in the import file - String schema = null; - NodeList metadata = XPathAPI.selectNodeList(document, "/dublin_core"); + String schema; + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList metadata = (NodeList) xPath.compile("/dublin_core").evaluate(document, XPathConstants.NODESET); Node schemaAttr = metadata.item(0).getAttributes().getNamedItem("schema"); if (schemaAttr == null) { schema = MetadataSchemaEnum.DC.getName(); @@ -196,7 +195,7 @@ public class MetadataUtilities { } // Get the nodes corresponding to formats - NodeList dcNodes = XPathAPI.selectNodeList(document, "/dublin_core/dcvalue"); + NodeList dcNodes = (NodeList) xPath.compile("/dublin_core/dcvalue").evaluate(document, XPathConstants.NODESET); for (int i = 0; i < dcNodes.getLength(); i++) { Node n = dcNodes.item(i); diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/CommandRunner.java b/dspace-api/src/main/java/org/dspace/app/launcher/CommandRunner.java index ce33b6655b..06c2ddb483 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/CommandRunner.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/CommandRunner.java @@ -16,7 +16,7 @@ import java.io.StreamTokenizer; import java.util.ArrayList; import java.util.List; -import org.jdom.Document; +import org.jdom2.Document; /** * @author mwood diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java index d445f9bbf3..fcb2098bd0 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java @@ -29,9 +29,9 @@ import org.dspace.scripts.service.ScriptService; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; import org.dspace.services.RequestService; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.input.SAXBuilder; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.input.SAXBuilder; /** * A DSpace script launcher. diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/ExcelFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/ExcelFilter.java deleted file mode 100644 index c17d168c04..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/ExcelFilter.java +++ /dev/null @@ -1,99 +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.mediafilter; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.IOUtils; -import org.apache.logging.log4j.Logger; -import org.apache.poi.POITextExtractor; -import org.apache.poi.extractor.ExtractorFactory; -import org.apache.poi.hssf.extractor.ExcelExtractor; -import org.apache.poi.xssf.extractor.XSSFExcelExtractor; -import org.dspace.content.Item; - -/* - * ExcelFilter - * - * Entries you must add to dspace.cfg: - * - * filter.plugins = blah, \ - * Excel Text Extractor - * - * plugin.named.org.dspace.app.mediafilter.FormatFilter = \ - * blah = blah, \ - * org.dspace.app.mediafilter.ExcelFilter = Excel Text Extractor - * - * #Configure each filter's input Formats - * filter.org.dspace.app.mediafilter.ExcelFilter.inputFormats = Microsoft Excel, Microsoft Excel XML - * - */ -public class ExcelFilter extends MediaFilter { - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ExcelFilter.class); - - public String getFilteredName(String oldFilename) { - return oldFilename + ".txt"; - } - - /** - * @return String bundle name - */ - public String getBundleName() { - return "TEXT"; - } - - /** - * @return String bitstream format - */ - public String getFormatString() { - return "Text"; - } - - /** - * @return String description - */ - public String getDescription() { - return "Extracted text"; - } - - /** - * @param item item - * @param source source input stream - * @param verbose verbose mode - * @return InputStream the resulting input stream - * @throws Exception if error - */ - @Override - public InputStream getDestinationStream(Item item, InputStream source, boolean verbose) - throws Exception { - String extractedText = null; - - try { - POITextExtractor theExtractor = ExtractorFactory.createExtractor(source); - if (theExtractor instanceof ExcelExtractor) { - // for xls file - extractedText = (theExtractor).getText(); - } else if (theExtractor instanceof XSSFExcelExtractor) { - // for xlsx file - extractedText = (theExtractor).getText(); - } - } catch (Exception e) { - log.error("Error filtering bitstream: " + e.getMessage(), e); - throw e; - } - - if (extractedText != null) { - // generate an input stream with the extracted text - return IOUtils.toInputStream(extractedText, StandardCharsets.UTF_8); - } - - return null; - } -} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/HTMLFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/HTMLFilter.java deleted file mode 100644 index 5e10f2841d..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/HTMLFilter.java +++ /dev/null @@ -1,82 +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.mediafilter; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import javax.swing.text.Document; -import javax.swing.text.html.HTMLEditorKit; - -import org.dspace.content.Item; - -/* - * - * to do: helpful error messages - can't find mediafilter.cfg - can't - * instantiate filter - bitstream format doesn't exist - * - */ -public class HTMLFilter extends MediaFilter { - - @Override - public String getFilteredName(String oldFilename) { - return oldFilename + ".txt"; - } - - /** - * @return String bundle name - */ - @Override - public String getBundleName() { - return "TEXT"; - } - - /** - * @return String bitstream format - */ - @Override - public String getFormatString() { - return "Text"; - } - - /** - * @return String description - */ - @Override - public String getDescription() { - return "Extracted text"; - } - - /** - * @param currentItem item - * @param source source input stream - * @param verbose verbose mode - * @return InputStream the resulting input stream - * @throws Exception if error - */ - @Override - public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose) - throws Exception { - // try and read the document - set to ignore character set directive, - // assuming that the input stream is already set properly (I hope) - HTMLEditorKit kit = new HTMLEditorKit(); - Document doc = kit.createDefaultDocument(); - - doc.putProperty("IgnoreCharsetDirective", Boolean.TRUE); - - kit.read(source, doc, 0); - - String extractedText = doc.getText(0, doc.getLength()); - - // generate an input stream with the extracted text - byte[] textBytes = extractedText.getBytes(StandardCharsets.UTF_8); - ByteArrayInputStream bais = new ByteArrayInputStream(textBytes); - - return bais; - } -} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java index 49ee23b924..26347c56ee 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java @@ -50,15 +50,11 @@ public class MediaFilterScriptConfiguration extends public Options getOptions() { Options options = new Options(); options.addOption("v", "verbose", false, "print all extracted text and other details to STDOUT"); - options.getOption("v").setType(boolean.class); options.addOption("q", "quiet", false, "do not print anything except in the event of errors."); - options.getOption("q").setType(boolean.class); options.addOption("f", "force", false, "force all bitstreams to be processed"); - options.getOption("f").setType(boolean.class); options.addOption("i", "identifier", true, "ONLY process bitstreams belonging to identifier"); options.addOption("m", "maximum", true, "process no more than maximum items"); options.addOption("h", "help", false, "help"); - options.getOption("h").setType(boolean.class); Option pluginOption = Option.builder("p") .longOpt("plugins") diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFFilter.java deleted file mode 100644 index c90d7c5a6e..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFFilter.java +++ /dev/null @@ -1,137 +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.mediafilter; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; - -import org.apache.logging.log4j.Logger; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException; -import org.apache.pdfbox.text.PDFTextStripper; -import org.dspace.content.Item; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; - -/* - * - * to do: helpful error messages - can't find mediafilter.cfg - can't - * instantiate filter - bitstream format doesn't exist - * - */ -public class PDFFilter extends MediaFilter { - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(PDFFilter.class); - - @Override - public String getFilteredName(String oldFilename) { - return oldFilename + ".txt"; - } - - /** - * @return String bundle name - */ - @Override - public String getBundleName() { - return "TEXT"; - } - - /** - * @return String bitstreamformat - */ - @Override - public String getFormatString() { - return "Text"; - } - - /** - * @return String description - */ - @Override - public String getDescription() { - return "Extracted text"; - } - - /** - * @param currentItem item - * @param source source input stream - * @param verbose verbose mode - * @return InputStream the resulting input stream - * @throws Exception if error - */ - @Override - public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose) - throws Exception { - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); - try { - boolean useTemporaryFile = configurationService.getBooleanProperty("pdffilter.largepdfs", false); - - // get input stream from bitstream - // pass to filter, get string back - PDFTextStripper pts = new PDFTextStripper(); - pts.setSortByPosition(true); - PDDocument pdfDoc = null; - Writer writer = null; - File tempTextFile = null; - ByteArrayOutputStream byteStream = null; - - if (useTemporaryFile) { - tempTextFile = File.createTempFile("dspacepdfextract" + source.hashCode(), ".txt"); - tempTextFile.deleteOnExit(); - writer = new OutputStreamWriter(new FileOutputStream(tempTextFile)); - } else { - byteStream = new ByteArrayOutputStream(); - writer = new OutputStreamWriter(byteStream); - } - - try { - pdfDoc = PDDocument.load(source); - pts.writeText(pdfDoc, writer); - } catch (InvalidPasswordException ex) { - log.error("PDF is encrypted. Cannot extract text (item: {})", - () -> currentItem.getHandle()); - return null; - } finally { - try { - if (pdfDoc != null) { - pdfDoc.close(); - } - } catch (Exception e) { - log.error("Error closing PDF file: " + e.getMessage(), e); - } - - try { - writer.close(); - } catch (Exception e) { - log.error("Error closing temporary extract file: " + e.getMessage(), e); - } - } - - if (useTemporaryFile) { - return new FileInputStream(tempTextFile); - } else { - byte[] bytes = byteStream.toByteArray(); - return new ByteArrayInputStream(bytes); - } - } catch (OutOfMemoryError oome) { - log.error("Error parsing PDF document " + oome.getMessage(), oome); - if (!configurationService.getBooleanProperty("pdffilter.skiponmemoryexception", false)) { - throw oome; - } - } - - return null; - } -} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.java deleted file mode 100644 index 8c198c4477..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.java +++ /dev/null @@ -1,72 +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.mediafilter; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import org.apache.poi.POITextExtractor; -import org.apache.poi.extractor.ExtractorFactory; -import org.apache.poi.openxml4j.exceptions.OpenXML4JException; -import org.apache.xmlbeans.XmlException; -import org.dspace.content.Item; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Extract flat text from Microsoft Word documents (.doc, .docx). - */ -public class PoiWordFilter - extends MediaFilter { - private static final Logger LOG = LoggerFactory.getLogger(PoiWordFilter.class); - - @Override - public String getFilteredName(String oldFilename) { - return oldFilename + ".txt"; - } - - @Override - public String getBundleName() { - return "TEXT"; - } - - @Override - public String getFormatString() { - return "Text"; - } - - @Override - public String getDescription() { - return "Extracted text"; - } - - @Override - public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose) - throws Exception { - String text; - try { - // get input stream from bitstream, pass to filter, get string back - POITextExtractor extractor = ExtractorFactory.createExtractor(source); - text = extractor.getText(); - } catch (IOException | OpenXML4JException | XmlException e) { - System.err.format("Invalid File Format: %s%n", e.getMessage()); - LOG.error("Unable to parse the bitstream: ", e); - throw e; - } - - // if verbose flag is set, print out extracted text to STDOUT - if (verbose) { - System.out.println(text); - } - - // return the extracted text as a stream. - return new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); - } -} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java deleted file mode 100644 index 86b7096f68..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java +++ /dev/null @@ -1,113 +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.mediafilter; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import org.apache.logging.log4j.Logger; -import org.apache.poi.POITextExtractor; -import org.apache.poi.extractor.ExtractorFactory; -import org.apache.poi.hslf.extractor.PowerPointExtractor; -import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor; -import org.dspace.content.Item; - -/* - * TODO: Allow user to configure extraction of only text or only notes - * - */ -public class PowerPointFilter extends MediaFilter { - - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(PowerPointFilter.class); - - @Override - public String getFilteredName(String oldFilename) { - return oldFilename + ".txt"; - } - - /** - * @return String bundle name - */ - @Override - public String getBundleName() { - return "TEXT"; - } - - /** - * @return String bitstream format - * - * TODO: Check that this is correct - */ - @Override - public String getFormatString() { - return "Text"; - } - - /** - * @return String description - */ - @Override - public String getDescription() { - return "Extracted text"; - } - - /** - * @param currentItem item - * @param source source input stream - * @param verbose verbose mode - * @return InputStream the resulting input stream - * @throws Exception if error - */ - @Override - public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose) - throws Exception { - - try { - - String extractedText = null; - new ExtractorFactory(); - POITextExtractor pptExtractor = ExtractorFactory - .createExtractor(source); - - // PowerPoint XML files and legacy format PowerPoint files - // require different classes and APIs for text extraction - - // If this is a PowerPoint XML file, extract accordingly - if (pptExtractor instanceof XSLFPowerPointExtractor) { - - // The true method arguments indicate that text from - // the slides and the notes is desired - extractedText = ((XSLFPowerPointExtractor) pptExtractor) - .getText(true, true); - } else if (pptExtractor instanceof PowerPointExtractor) { // Legacy PowerPoint files - - extractedText = ((PowerPointExtractor) pptExtractor).getText() - + " " + ((PowerPointExtractor) pptExtractor).getNotes(); - - } - if (extractedText != null) { - // if verbose flag is set, print out extracted text - // to STDOUT - if (verbose) { - System.out.println(extractedText); - } - - // generate an input stream with the extracted text - byte[] textBytes = extractedText.getBytes(); - ByteArrayInputStream bais = new ByteArrayInputStream(textBytes); - - return bais; - } - } catch (Exception e) { - log.error("Error filtering bitstream: " + e.getMessage(), e); - throw e; - } - - return null; - } -} diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java new file mode 100644 index 0000000000..e83bf706ed --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java @@ -0,0 +1,183 @@ +/** + * 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.mediafilter; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tika.Tika; +import org.apache.tika.exception.TikaException; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.parser.AutoDetectParser; +import org.apache.tika.sax.BodyContentHandler; +import org.apache.tika.sax.ContentHandlerDecorator; +import org.dspace.content.Item; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.xml.sax.SAXException; + +/** + * Text Extraction media filter which uses Apache Tika to extract text from a large number of file formats (including + * all Microsoft formats, PDF, HTML, Text, etc). For a more complete list of file formats supported by Tika see the + * Tika documentation: https://tika.apache.org/2.3.0/formats.html + */ +public class TikaTextExtractionFilter + extends MediaFilter { + private final static Logger log = LogManager.getLogger(); + + @Override + public String getFilteredName(String oldFilename) { + return oldFilename + ".txt"; + } + + @Override + public String getBundleName() { + return "TEXT"; + } + + @Override + public String getFormatString() { + return "Text"; + } + + @Override + public String getDescription() { + return "Extracted text"; + } + + @Override + public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose) + throws Exception { + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + boolean useTemporaryFile = configurationService.getBooleanProperty("textextractor.use-temp-file", false); + + if (useTemporaryFile) { + // Extract text out of source file using a temp file, returning results as InputStream + return extractUsingTempFile(source, verbose); + } + + // Not using temporary file. We'll use Tika's default in-memory parsing. + // Get maximum characters to extract. Default is 100,000 chars, which is also Tika's default setting. + String extractedText; + int maxChars = configurationService.getIntProperty("textextractor.max-chars", 100000); + try { + // Use Tika to extract text from input. Tika will automatically detect the file type. + Tika tika = new Tika(); + tika.setMaxStringLength(maxChars); // Tell Tika the maximum number of characters to extract + extractedText = tika.parseToString(source); + } catch (IOException e) { + System.err.format("Unable to extract text from bitstream in Item %s%n", currentItem.getID().toString()); + e.printStackTrace(); + log.error("Unable to extract text from bitstream in Item {}", currentItem.getID().toString(), e); + throw e; + } catch (OutOfMemoryError oe) { + System.err.format("OutOfMemoryError occurred when extracting text from bitstream in Item %s. " + + "You may wish to enable 'textextractor.use-temp-file'.%n", currentItem.getID().toString()); + oe.printStackTrace(); + log.error("OutOfMemoryError occurred when extracting text from bitstream in Item {}. " + + "You may wish to enable 'textextractor.use-temp-file'.", currentItem.getID().toString(), oe); + throw oe; + } + + if (StringUtils.isNotEmpty(extractedText)) { + // if verbose flag is set, print out extracted text to STDOUT + if (verbose) { + System.out.println("(Verbose mode) Extracted text:"); + System.out.println(extractedText); + } + + // return the extracted text as a UTF-8 stream. + return new ByteArrayInputStream(extractedText.getBytes(StandardCharsets.UTF_8)); + } + return null; + } + + /** + * Extracts the text out of a given source InputStream, using a temporary file. This decreases the amount of memory + * necessary for text extraction, but can be slower as it requires writing extracted text to a temporary file. + * @param source source InputStream + * @param verbose verbose mode enabled/disabled + * @return InputStream for temporary file containing extracted text + * @throws IOException + * @throws SAXException + * @throws TikaException + */ + private InputStream extractUsingTempFile(InputStream source, boolean verbose) + throws IOException, TikaException, SAXException { + File tempExtractedTextFile = File.createTempFile("dspacetextextract" + source.hashCode(), ".txt"); + + if (verbose) { + System.out.println("(Verbose mode) Extracted text was written to temporary file at " + + tempExtractedTextFile.getAbsolutePath()); + } else { + tempExtractedTextFile.deleteOnExit(); + } + + // Open temp file for writing + try (FileWriter writer = new FileWriter(tempExtractedTextFile, StandardCharsets.UTF_8)) { + // Initialize a custom ContentHandlerDecorator which is a BodyContentHandler. + // This mimics the behavior of Tika().parseToString(), which only extracts text from the body of the file. + // This custom Handler writes any extracted text to the temp file. + ContentHandlerDecorator handler = new BodyContentHandler(new ContentHandlerDecorator() { + /** + * Write all extracted characters directly to the temp file. + */ + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + try { + writer.append(new String(ch), start, length); + } catch (IOException e) { + String errorMsg = String.format("Could not append to temporary file at %s " + + "when performing text extraction", + tempExtractedTextFile.getAbsolutePath()); + log.error(errorMsg, e); + throw new SAXException(errorMsg, e); + } + } + + /** + * Write all ignorable whitespace directly to the temp file. + * This mimics the behaviour of Tika().parseToString() which extracts ignorableWhitespace characters + * (like blank lines, indentations, etc.), so that we get the same extracted text either way. + */ + @Override + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + try { + writer.append(new String(ch), start, length); + } catch (IOException e) { + String errorMsg = String.format("Could not append to temporary file at %s " + + "when performing text extraction", + tempExtractedTextFile.getAbsolutePath()); + log.error(errorMsg, e); + throw new SAXException(errorMsg, e); + } + } + }); + + AutoDetectParser parser = new AutoDetectParser(); + Metadata metadata = new Metadata(); + // parse our source InputStream using the above custom handler + parser.parse(source, handler, metadata); + } + + // At this point, all extracted text is written to our temp file. So, return a FileInputStream for that file + return new FileInputStream(tempExtractedTextFile); + } + + + + +} diff --git a/dspace-api/src/main/java/org/dspace/app/util/CacheSnooper.java b/dspace-api/src/main/java/org/dspace/app/util/CacheSnooper.java deleted file mode 100644 index 22ad518ea3..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/util/CacheSnooper.java +++ /dev/null @@ -1,58 +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.util; - -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import org.dspace.core.Context; -import org.dspace.servicemanager.DSpaceKernelImpl; -import org.dspace.servicemanager.DSpaceKernelInit; -import org.dspace.services.CachingService; - -/** - * List all EhCache CacheManager and Cache instances. - * - *

This is a debugging tool, not used in the daily operation of DSpace. - * Just run it from the installed instance using - * {@code bin/dspace dsrun org.dspace.app.util.CacheSnooper} - * to check that the cache configuration is what you expect it to be, - * given your configuration. - * - *

This was created to prove a specific cache configuration patch, - * but I leave it here in the hope that it may be useful to others. - * - * @author Mark H. Wood - */ -public class CacheSnooper { - private CacheSnooper() { } - - public static void main(String[] argv) { - // Ensure that the DSpace kernel is started. - DSpaceKernelImpl kernel = DSpaceKernelInit.getKernel(null); - - // Ensure that the services cache manager is started. - CachingService serviceCaches = kernel.getServiceManager() - .getServiceByName(null, CachingService.class); - - // Ensure that the database layer is started. - Context ctx = new Context(); - - // List those caches! - for (CacheManager manager : CacheManager.ALL_CACHE_MANAGERS) { - System.out.format("CacheManager: %s%n", manager); - for (String cacheName : manager.getCacheNames()) { - Cache cache = manager.getCache(cacheName); - System.out.format(" Cache: '%s'; maxHeap: %d; maxDisk: %d%n", - cacheName, - cache.getCacheConfiguration().getMaxEntriesLocalHeap(), - cache.getCacheConfiguration().getMaxEntriesLocalDisk()); - } - } - } -} diff --git a/dspace-api/src/main/java/org/dspace/app/util/Configuration.java b/dspace-api/src/main/java/org/dspace/app/util/Configuration.java index e9b125c41c..e4a59eeb4d 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/Configuration.java +++ b/dspace-api/src/main/java/org/dspace/app/util/Configuration.java @@ -37,6 +37,7 @@ public class Configuration { *

  • {@code --property name} prints the value of the DSpace configuration * property {@code name} to the standard output.
  • *
  • {@code --raw} suppresses parameter substitution in the output.
  • + *
  • {@code --first} print only the first of multiple values.
  • *
  • {@code --help} describes these options.
  • * * If the property does not exist, nothing is written. @@ -51,6 +52,8 @@ public class Configuration { "optional name of the module in which 'property' exists"); options.addOption("r", "raw", false, "do not do property substitution on the value"); + options.addOption("f", "first", false, + "display only the first value of an array property"); options.addOption("?", "Get help"); options.addOption("h", "help", false, "Get help"); @@ -90,19 +93,36 @@ public class Configuration { propNameBuilder.append(cmd.getOptionValue('p')); String propName = propNameBuilder.toString(); - // Print the property's value, if it exists + // Print the property's value(s), if it exists ConfigurationService cfg = DSpaceServicesFactory.getInstance().getConfigurationService(); if (!cfg.hasProperty(propName)) { System.out.println(); } else { - String val; if (cmd.hasOption('r')) { - val = cfg.getPropertyValue(propName).toString(); + // Print "raw" values (without property substitutions) + Object rawValue = cfg.getPropertyValue(propName); + if (rawValue.getClass().isArray()) { + for (Object value : (Object[]) rawValue) { + System.out.println(value.toString()); + if (cmd.hasOption('f')) { + break; // If --first print only one value + } + } + } else { // Not an array + System.out.println(rawValue.toString()); + } } else { - val = cfg.getProperty(propName); + // Print values with property substitutions + String[] values = cfg.getArrayProperty(propName); + for (String value : values) { + System.out.println(value); + if (cmd.hasOption('f')) { + break; // If --first print only one value + } + } } - System.out.println(val); } + System.exit(0); } } diff --git a/dspace-api/src/main/java/org/dspace/app/util/GoogleBitstreamComparator.java b/dspace-api/src/main/java/org/dspace/app/util/GoogleBitstreamComparator.java index add98af96f..ae6ba7e83f 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/GoogleBitstreamComparator.java +++ b/dspace-api/src/main/java/org/dspace/app/util/GoogleBitstreamComparator.java @@ -86,8 +86,10 @@ public class GoogleBitstreamComparator implements Comparator { if (priority1 > priority2) { return 1; } else if (priority1 == priority2) { - if (b1.getSizeBytes() <= b2.getSizeBytes()) { + if (b1.getSizeBytes() < b2.getSizeBytes()) { return 1; + } else if (b1.getSizeBytes() == b2.getSizeBytes()) { + return 0; } else { return -1; } diff --git a/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java b/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java index 0021f26700..72e2af409f 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java +++ b/dspace-api/src/main/java/org/dspace/app/util/GoogleMetadata.java @@ -42,7 +42,7 @@ import org.dspace.core.Context; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; +import org.jdom2.Element; /** * Configuration and mapping for Google Scholar output metadata diff --git a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java index 97f25cb2b2..89ca477442 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java @@ -16,10 +16,10 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import com.sun.syndication.feed.module.opensearch.OpenSearchModule; -import com.sun.syndication.feed.module.opensearch.entity.OSQuery; -import com.sun.syndication.feed.module.opensearch.impl.OpenSearchModuleImpl; -import com.sun.syndication.io.FeedException; +import com.rometools.modules.opensearch.OpenSearchModule; +import com.rometools.modules.opensearch.entity.OSQuery; +import com.rometools.modules.opensearch.impl.OpenSearchModuleImpl; +import com.rometools.rome.io.FeedException; import org.apache.logging.log4j.Logger; import org.dspace.app.util.service.OpenSearchService; import org.dspace.content.DSpaceObject; @@ -29,11 +29,11 @@ import org.dspace.discovery.IndexableObject; import org.dspace.handle.service.HandleService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.output.DOMOutputter; -import org.jdom.output.XMLOutputter; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.output.DOMOutputter; +import org.jdom2.output.XMLOutputter; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.Document; @@ -192,7 +192,7 @@ public class OpenSearchServiceImpl implements OpenSearchService { * @param scope - null for the entire repository, or a collection/community handle * @return Service Document */ - protected org.jdom.Document getServiceDocument(String scope) { + protected org.jdom2.Document getServiceDocument(String scope) { ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService(); Namespace ns = Namespace.getNamespace(osNs); @@ -245,7 +245,7 @@ public class OpenSearchServiceImpl implements OpenSearchService { url.setAttribute("template", template.toString()); root.addContent(url); } - return new org.jdom.Document(root); + return new org.jdom2.Document(root); } /** @@ -255,7 +255,7 @@ public class OpenSearchServiceImpl implements OpenSearchService { * @return W3C Document object * @throws IOException if IO error */ - protected Document jDomToW3(org.jdom.Document jdomDoc) throws IOException { + protected Document jDomToW3(org.jdom2.Document jdomDoc) throws IOException { DOMOutputter domOut = new DOMOutputter(); try { return domOut.output(jdomDoc); diff --git a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java index 2576df0193..8bbec234c9 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SyndicationFeed.java @@ -15,26 +15,26 @@ import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; -import com.sun.syndication.feed.module.DCModule; -import com.sun.syndication.feed.module.DCModuleImpl; -import com.sun.syndication.feed.module.Module; -import com.sun.syndication.feed.module.itunes.EntryInformation; -import com.sun.syndication.feed.module.itunes.EntryInformationImpl; -import com.sun.syndication.feed.module.itunes.types.Duration; -import com.sun.syndication.feed.synd.SyndContent; -import com.sun.syndication.feed.synd.SyndContentImpl; -import com.sun.syndication.feed.synd.SyndEnclosure; -import com.sun.syndication.feed.synd.SyndEnclosureImpl; -import com.sun.syndication.feed.synd.SyndEntry; -import com.sun.syndication.feed.synd.SyndEntryImpl; -import com.sun.syndication.feed.synd.SyndFeed; -import com.sun.syndication.feed.synd.SyndFeedImpl; -import com.sun.syndication.feed.synd.SyndImage; -import com.sun.syndication.feed.synd.SyndImageImpl; -import com.sun.syndication.feed.synd.SyndPerson; -import com.sun.syndication.feed.synd.SyndPersonImpl; -import com.sun.syndication.io.FeedException; -import com.sun.syndication.io.SyndFeedOutput; +import com.rometools.modules.itunes.EntryInformation; +import com.rometools.modules.itunes.EntryInformationImpl; +import com.rometools.modules.itunes.types.Duration; +import com.rometools.rome.feed.module.DCModule; +import com.rometools.rome.feed.module.DCModuleImpl; +import com.rometools.rome.feed.module.Module; +import com.rometools.rome.feed.synd.SyndContent; +import com.rometools.rome.feed.synd.SyndContentImpl; +import com.rometools.rome.feed.synd.SyndEnclosure; +import com.rometools.rome.feed.synd.SyndEnclosureImpl; +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndEntryImpl; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.feed.synd.SyndFeedImpl; +import com.rometools.rome.feed.synd.SyndImage; +import com.rometools.rome.feed.synd.SyndImageImpl; +import com.rometools.rome.feed.synd.SyndPerson; +import com.rometools.rome.feed.synd.SyndPersonImpl; +import com.rometools.rome.io.FeedException; +import com.rometools.rome.io.SyndFeedOutput; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; diff --git a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java index a913d27d62..dba5de90f3 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java @@ -20,7 +20,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -290,20 +289,13 @@ public class ShibAuthentication implements AuthenticationMethod { try { // User has not successfuly authenticated via shibboleth. if (request == null || - context.getCurrentUser() == null || - request.getSession().getAttribute("shib.authenticated") == null) { + context.getCurrentUser() == null) { return Collections.EMPTY_LIST; } - // If we have already calculated the special groups then return them. - if (request.getSession().getAttribute("shib.specialgroup") != null) { + if (context.getSpecialGroups().size() > 0 ) { log.debug("Returning cached special groups."); - List sessionGroupIds = (List) request.getSession().getAttribute("shib.specialgroup"); - List result = new ArrayList<>(); - for (UUID uuid : sessionGroupIds) { - result.add(groupService.find(context, uuid)); - } - return result; + return context.getSpecialGroups(); } log.debug("Starting to determine special groups"); @@ -396,16 +388,8 @@ public class ShibAuthentication implements AuthenticationMethod { log.info("Added current EPerson to special groups: " + groups); - List groupIds = new ArrayList<>(); - for (Group group : groups) { - groupIds.add(group.getID()); - } - - // Cache the special groups, so we don't have to recalculate them again - // for this session. - request.setAttribute("shib.specialgroup", groupIds); - return new ArrayList<>(groups); + } catch (Throwable t) { log.error("Unable to validate any sepcial groups this user may belong too because of an exception.", t); return Collections.EMPTY_LIST; diff --git a/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java b/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java index a1c3867fb9..6753a5d113 100644 --- a/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java +++ b/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java @@ -21,7 +21,8 @@ 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.log4j.Logger; +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.external.OrcidRestConnector; @@ -40,7 +41,7 @@ import org.orcid.jaxb.model.v3.release.search.Result; */ public class Orcidv3SolrAuthorityImpl implements SolrAuthorityInterface { - private static Logger log = Logger.getLogger(Orcidv3SolrAuthorityImpl.class); + private final static Logger log = LogManager.getLogger(); private OrcidRestConnector orcidRestConnector; private String OAUTHUrl; diff --git a/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java b/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java index 77568205af..6cf49ac65b 100644 --- a/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java +++ b/dspace-api/src/main/java/org/dspace/authority/util/XMLUtils.java @@ -14,11 +14,12 @@ import java.util.Iterator; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.apache.logging.log4j.Logger; -import org.apache.xpath.XPathAPI; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -62,36 +63,26 @@ public class XMLUtils { /** * @param xml The starting context (a Node or a Document, for example). - * @param NodeListXPath xpath + * @param nodeListXPath xpath * @return A Node matches the NodeListXPath * null if nothing matches the NodeListXPath * @throws XPathExpressionException if xpath error */ - public static Node getNode(Node xml, String NodeListXPath) throws XPathExpressionException { - Node result = null; - try { - result = XPathAPI.selectSingleNode(xml, NodeListXPath); - } catch (TransformerException e) { - log.error("Error", e); - } - return result; + public static Node getNode(Node xml, String nodeListXPath) throws XPathExpressionException { + XPath xPath = XPathFactory.newInstance().newXPath(); + return (Node) xPath.compile(nodeListXPath).evaluate(xml, XPathConstants.NODE); } /** * @param xml The starting context (a Node or a Document, for example). - * @param NodeListXPath xpath + * @param nodeListXPath xpath * @return A NodeList containing the nodes that match the NodeListXPath * null if nothing matches the NodeListXPath * @throws XPathExpressionException if xpath error */ - public static NodeList getNodeList(Node xml, String NodeListXPath) throws XPathExpressionException { - NodeList nodeList = null; - try { - nodeList = XPathAPI.selectNodeList(xml, NodeListXPath); - } catch (TransformerException e) { - log.error("Error", e); - } - return nodeList; + public static NodeList getNodeList(Node xml, String nodeListXPath) throws XPathExpressionException { + XPath xPath = XPathFactory.newInstance().newXPath(); + return (NodeList) xPath.compile(nodeListXPath).evaluate(xml, XPathConstants.NODESET); } public static Iterator getNodeListIterator(Node xml, String NodeListXPath) throws XPathExpressionException { diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java index a25a492a3a..954bb96990 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicy.java @@ -93,7 +93,7 @@ public class ResourcePolicy implements ReloadableEntity { private String rptype; @Lob - @Type(type = "org.hibernate.type.MaterializedClobType") + @Type(type = "org.dspace.storage.rdbms.hibernate.DatabaseAwareLobType") @Column(name = "rpdescription") private String rpdescription; diff --git a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java index 7454c8e82b..099c45d3ad 100644 --- a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java +++ b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -202,6 +203,8 @@ public class BrowseEngine { // get the table name that we are going to be getting our data from dao.setTable(browseIndex.getTableName()); + dao.setStartsWith(StringUtils.lowerCase(scope.getStartsWith())); + // tell the browse query whether we are ascending or descending on the value dao.setAscending(scope.isAscending()); @@ -248,9 +251,6 @@ public class BrowseEngine { } } - // this is the total number of results in answer to the query - int total = getTotalResults(); - // assemble the ORDER BY clause String orderBy = browseIndex.getSortField(scope.isSecondLevel()); if (scope.getSortBy() > 0) { @@ -258,6 +258,9 @@ public class BrowseEngine { } dao.setOrderField(orderBy); + // this is the total number of results in answer to the query + int total = getTotalResults(); + int offset = scope.getOffset(); String rawFocusValue = null; if (offset < 1 && (scope.hasJumpToItem() || scope.hasJumpToValue() || scope.hasStartsWith())) { @@ -269,9 +272,6 @@ public class BrowseEngine { String focusValue = normalizeJumpToValue(rawFocusValue); log.debug("browsing using focus: " + focusValue); - - // Convert the focus value into an offset - offset = getOffsetForValue(focusValue); } dao.setOffset(offset); @@ -684,13 +684,11 @@ public class BrowseEngine { // our count, storing them locally to reinstate later String focusField = dao.getJumpToField(); String focusValue = dao.getJumpToValue(); - String orderField = dao.getOrderField(); int limit = dao.getLimit(); int offset = dao.getOffset(); dao.setJumpToField(null); dao.setJumpToValue(null); - dao.setOrderField(null); dao.setLimit(-1); dao.setOffset(-1); @@ -700,7 +698,6 @@ public class BrowseEngine { // now put back the values we removed for this method dao.setJumpToField(focusField); dao.setJumpToValue(focusValue); - dao.setOrderField(orderField); dao.setLimit(limit); dao.setOffset(offset); dao.setCountValues(null); diff --git a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java index 6a960e8d75..391ed90771 100644 --- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java @@ -205,6 +205,9 @@ public class SolrBrowseDAO implements BrowseDAO { } else if (valuePartial) { query.addFilterQueries("{!field f=" + facetField + "_partial}" + value); } + if (StringUtils.isNotBlank(startsWith) && orderField != null) { + query.addFilterQueries("bi_" + orderField + "_sort:" + startsWith + "*"); + } // filter on item to be sure to don't include any other object // indexed in the Discovery Search core query.addFilterQueries("search.resourcetype:" + IndexableItem.TYPE); diff --git a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java index a0c32b6080..24778824bf 100644 --- a/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/DSpaceObjectServiceImpl.java @@ -243,67 +243,64 @@ public abstract class DSpaceObjectServiceImpl implements boolean authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField); boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField); - List newMetadata = new ArrayList<>(values.size()); + List newMetadata = new ArrayList<>(); // We will not verify that they are valid entries in the registry // until update() is called. for (int i = 0; i < values.size(); i++) { - - if (authorities != null && authorities.size() >= i) { - if (StringUtils.startsWith(authorities.get(i), Constants.VIRTUAL_AUTHORITY_PREFIX)) { - continue; - } - } - MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField); - newMetadata.add(metadataValue); - - metadataValue.setPlace(placeSupplier.get()); - - metadataValue.setLanguage(lang == null ? null : lang.trim()); - - // Logic to set Authority and Confidence: - // - normalize an empty string for authority to NULL. - // - if authority key is present, use given confidence or NOVALUE if not given - // - otherwise, preserve confidence if meaningful value was given since it may document a failed - // authority lookup - // - CF_UNSET signifies no authority nor meaningful confidence. - // - it's possible to have empty authority & CF_ACCEPTED if e.g. user deletes authority key - if (authorityControlled) { - if (authorities != null && authorities.get(i) != null && authorities.get(i).length() > 0) { - metadataValue.setAuthority(authorities.get(i)); - metadataValue.setConfidence(confidences == null ? Choices.CF_NOVALUE : confidences.get(i)); - } else { - metadataValue.setAuthority(null); - metadataValue.setConfidence(confidences == null ? Choices.CF_UNSET : confidences.get(i)); - } - // authority sanity check: if authority is required, was it supplied? - // XXX FIXME? can't throw a "real" exception here without changing all the callers to expect it, so - // use a runtime exception - if (authorityRequired && (metadataValue.getAuthority() == null || metadataValue.getAuthority() - .length() == 0)) { - throw new IllegalArgumentException("The metadata field \"" + metadataField - .toString() + "\" requires an authority key but none was provided. Value=\"" + values - .get(i) + "\""); - } - } if (values.get(i) != null) { + if (authorities != null && authorities.size() >= i) { + if (StringUtils.startsWith(authorities.get(i), Constants.VIRTUAL_AUTHORITY_PREFIX)) { + continue; + } + } + MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField); + newMetadata.add(metadataValue); + + metadataValue.setPlace(placeSupplier.get()); + + metadataValue.setLanguage(lang == null ? null : lang.trim()); + + // Logic to set Authority and Confidence: + // - normalize an empty string for authority to NULL. + // - if authority key is present, use given confidence or NOVALUE if not given + // - otherwise, preserve confidence if meaningful value was given since it may document a failed + // authority lookup + // - CF_UNSET signifies no authority nor meaningful confidence. + // - it's possible to have empty authority & CF_ACCEPTED if e.g. user deletes authority key + if (authorityControlled) { + if (authorities != null && authorities.get(i) != null && authorities.get(i).length() > 0) { + metadataValue.setAuthority(authorities.get(i)); + metadataValue.setConfidence(confidences == null ? Choices.CF_NOVALUE : confidences.get(i)); + } else { + metadataValue.setAuthority(null); + metadataValue.setConfidence(confidences == null ? Choices.CF_UNSET : confidences.get(i)); + } + // authority sanity check: if authority is required, was it supplied? + // XXX FIXME? can't throw a "real" exception here without changing all the callers to expect it, so + // use a runtime exception + if (authorityRequired && (metadataValue.getAuthority() == null || metadataValue.getAuthority() + .length() == 0)) { + throw new IllegalArgumentException("The metadata field \"" + metadataField + .toString() + "\" requires an authority key but none was provided. Value=\"" + values + .get(i) + "\""); + } + } // remove control unicode char String temp = values.get(i).trim(); char[] dcvalue = temp.toCharArray(); for (int charPos = 0; charPos < dcvalue.length; charPos++) { if (Character.isISOControl(dcvalue[charPos]) && - !String.valueOf(dcvalue[charPos]).equals("\u0009") && - !String.valueOf(dcvalue[charPos]).equals("\n") && - !String.valueOf(dcvalue[charPos]).equals("\r")) { + !String.valueOf(dcvalue[charPos]).equals("\u0009") && + !String.valueOf(dcvalue[charPos]).equals("\n") && + !String.valueOf(dcvalue[charPos]).equals("\r")) { dcvalue[charPos] = ' '; } } metadataValue.setValue(String.valueOf(dcvalue)); - } else { - metadataValue.setValue(null); - } - //An update here isn't needed, this is persited upon the merge of the owning object + //An update here isn't needed, this is persited upon the merge of the owning object // metadataValueService.update(context, metadataValue); - dso.addDetails(metadataField.toString()); + dso.addDetails(metadataField.toString()); + } } setMetadataModified(dso); return newMetadata; diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java index c1848a1ebf..539882054e 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -911,6 +911,12 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Override public void move(Context context, Item item, Collection from, Collection to) throws SQLException, AuthorizeException, IOException { + + // If the two collections are the same, do nothing. + if (from.equals(to)) { + return; + } + // Use the normal move method, and default to not inherit permissions this.move(context, item, from, to, false); } diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java index d1b636cdff..9ff3cb9ec2 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java @@ -59,7 +59,7 @@ public class MetadataValue implements ReloadableEntity { * The value of the field */ @Lob - @Type(type = "org.hibernate.type.MaterializedClobType") + @Type(type = "org.dspace.storage.rdbms.hibernate.DatabaseAwareLobType") @Column(name = "text_value") private String value; diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPDIMCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPDIMCrosswalk.java index 2d919baa9d..4b77e4807a 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPDIMCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPDIMCrosswalk.java @@ -14,8 +14,8 @@ import java.util.List; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * Crosswalk descriptive metadata to and from DIM (DSpace Intermediate diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java index 8ffddf715f..978cabfb4b 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/AIPTechMDCrosswalk.java @@ -40,8 +40,8 @@ import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * Crosswalk of technical metadata for DSpace AIP. This is diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMDisseminationCrosswalk.java index 3f4d6bd44e..4365d9a485 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMDisseminationCrosswalk.java @@ -23,8 +23,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * DIM dissemination crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMIngestionCrosswalk.java index ad922a65f2..4217308e65 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/DIMIngestionCrosswalk.java @@ -19,8 +19,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * DIM ingestion crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/DisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/DisseminationCrosswalk.java index 23e1965d7b..3e4fe21f8f 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/DisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/DisseminationCrosswalk.java @@ -14,8 +14,8 @@ import java.util.List; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * Dissemination Crosswalk plugin -- translate DSpace native diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/IngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/IngestionCrosswalk.java index 7edfb6f79f..bb73c83c45 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/IngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/IngestionCrosswalk.java @@ -14,7 +14,7 @@ import java.util.List; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.jdom.Element; +import org.jdom2.Element; /** * Ingestion Crosswalk plugin -- translate an external metadata format diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java index e44774a672..b8a4a8aef3 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java @@ -24,11 +24,11 @@ import org.dspace.core.Context; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.input.SAXBuilder; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.input.SAXBuilder; /** * METS dissemination crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java index 559d463be2..7f6622841b 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSRightsCrosswalk.java @@ -35,8 +35,8 @@ import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.GroupService; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * METSRights Ingestion and Dissemination Crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java index 182fcebe2f..57202f656e 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java @@ -15,7 +15,6 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; @@ -42,16 +41,18 @@ import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Attribute; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.Text; -import org.jdom.Verifier; -import org.jdom.input.SAXBuilder; -import org.jdom.output.XMLOutputter; -import org.jdom.xpath.XPath; +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.Text; +import org.jdom2.Verifier; +import org.jdom2.filter.Filters; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.XMLOutputter; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; /** * Configurable MODS Crosswalk @@ -156,7 +157,7 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin static class modsTriple { public String qdc = null; public Element xml = null; - public XPath xpath = null; + public XPathExpression xpath = null; /** * Initialize from text versions of QDC, XML and XPath. @@ -171,9 +172,9 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin final String postlog = ""; try { result.qdc = qdc; - result.xpath = XPath.newInstance(xpath); - result.xpath.addNamespace(MODS_NS.getPrefix(), MODS_NS.getURI()); - result.xpath.addNamespace(XLINK_NS); + result.xpath = + XPathFactory.instance() + .compile(xpath, Filters.fpassthrough(), null, MODS_NS, XLINK_NS); Document d = builder.build(new StringReader(prolog + xml + postlog)); result.xml = (Element) d.getRootElement().getContent(0); } catch (JDOMException | IOException je) { @@ -295,6 +296,7 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin * @throws IOException if IO error * @throws SQLException if database error * @throws AuthorizeException if authorization error + * @return List of Elements */ @Override public List disseminateList(Context context, DSpaceObject dso) @@ -352,37 +354,29 @@ public class MODSDisseminationCrosswalk extends SelfNamedPlugin if (trip == null) { log.warn("WARNING: " + getPluginInstanceName() + ": No MODS mapping for \"" + qdc + "\""); } else { - try { - Element me = (Element) trip.xml.clone(); - if (addSchema) { - me.setAttribute("schemaLocation", schemaLocation, XSI_NS); - } - Iterator ni = trip.xpath.selectNodes(me).iterator(); - if (!ni.hasNext()) { - log.warn("XPath \"" + trip.xpath.getXPath() + - "\" found no elements in \"" + - outputUgly.outputString(me) + - "\", qdc=" + qdc); - } - while (ni.hasNext()) { - Object what = ni.next(); - if (what instanceof Element) { - ((Element) what).setText(checkedString(value)); - } else if (what instanceof Attribute) { - ((Attribute) what).setValue(checkedString(value)); - } else if (what instanceof Text) { - ((Text) what).setText(checkedString(value)); - } else { - log.warn("Got unknown object from XPath, class=" + what.getClass().getName()); - } - } - result.add(me); - } catch (JDOMException je) { - log.error("Error following XPath in modsTriple: context=" + - outputUgly.outputString(trip.xml) + - ", xpath=" + trip.xpath.getXPath() + ", exception=" + - je.toString()); + Element me = (Element) trip.xml.clone(); + if (addSchema) { + me.setAttribute("schemaLocation", schemaLocation, XSI_NS); } + List matches = trip.xpath.evaluate(me); + if (matches.isEmpty()) { + log.warn("XPath \"" + trip.xpath.getExpression() + + "\" found no elements in \"" + + outputUgly.outputString(me) + + "\", qdc=" + qdc); + } + for (Object match: matches) { + if (match instanceof Element) { + ((Element) match).setText(checkedString(value)); + } else if (match instanceof Attribute) { + ((Attribute) match).setValue(checkedString(value)); + } else if (match instanceof Text) { + ((Text) match).setText(checkedString(value)); + } else { + log.warn("Got unknown object from XPath, class=" + match.getClass().getName()); + } + } + result.add(me); } } return result; diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/NullIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/NullIngestionCrosswalk.java index 994e15601d..562dadaca0 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/NullIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/NullIngestionCrosswalk.java @@ -15,9 +15,9 @@ import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.jdom.Element; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; +import org.jdom2.Element; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; /** * "Null" ingestion crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/OAIDCIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/OAIDCIngestionCrosswalk.java index 10bd5ce6fa..6b0ecae780 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/OAIDCIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/OAIDCIngestionCrosswalk.java @@ -20,8 +20,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * DIM ingestion crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java index 3dde093784..ac1c434322 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/OREDisseminationCrosswalk.java @@ -31,8 +31,8 @@ import org.dspace.core.Context; import org.dspace.core.Utils; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * ORE dissemination crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java index 80c424e782..f756aae225 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/OREIngestionCrosswalk.java @@ -34,12 +34,13 @@ import org.dspace.content.service.BundleService; import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.jdom.Attribute; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.xpath.XPath; +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.filter.Filters; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; /** * ORE ingestion crosswalk @@ -113,23 +114,21 @@ public class OREIngestionCrosswalk Document doc = new Document(); doc.addContent(root.detach()); - XPath xpathLinks; List aggregatedResources; String entryId; - try { - xpathLinks = XPath.newInstance("/atom:entry/atom:link[@rel=\"" + ORE_NS.getURI() + "aggregates" + "\"]"); - xpathLinks.addNamespace(ATOM_NS); - aggregatedResources = xpathLinks.selectNodes(doc); + XPathExpression xpathLinks = + XPathFactory.instance() + .compile("/atom:entry/atom:link[@rel=\"" + ORE_NS.getURI() + "aggregates" + "\"]", + Filters.element(), null, ATOM_NS); + aggregatedResources = xpathLinks.evaluate(doc); - xpathLinks = XPath.newInstance("/atom:entry/atom:link[@rel='alternate']/@href"); - xpathLinks.addNamespace(ATOM_NS); - entryId = ((Attribute) xpathLinks.selectSingleNode(doc)).getValue(); - } catch (JDOMException e) { - throw new CrosswalkException("JDOM exception occurred while ingesting the ORE", e); - } + XPathExpression xpathAltHref = + XPathFactory.instance() + .compile("/atom:entry/atom:link[@rel='alternate']/@href", + Filters.attribute(), null, ATOM_NS); + entryId = xpathAltHref.evaluateFirst(doc).getValue(); // Next for each resource, create a bitstream - XPath xpathDesc; NumberFormat nf = NumberFormat.getInstance(); nf.setGroupingUsed(false); nf.setMinimumIntegerDigits(4); @@ -140,16 +139,12 @@ public class OREIngestionCrosswalk String bundleName; Element desc = null; - try { - xpathDesc = XPath.newInstance( - "/atom:entry/oreatom:triples/rdf:Description[@rdf:about=\"" + this.encodeForURL(href) + "\"][1]"); - xpathDesc.addNamespace(ATOM_NS); - xpathDesc.addNamespace(ORE_ATOM); - xpathDesc.addNamespace(RDF_NS); - desc = (Element) xpathDesc.selectSingleNode(doc); - } catch (JDOMException e) { - log.warn("Could not find description for {}", href, e); - } + XPathExpression xpathDesc = + XPathFactory.instance() + .compile("/atom:entry/oreatom:triples/rdf:Description[@rdf:about=\"" + + this.encodeForURL(href) + "\"][1]", + Filters.element(), null, ATOM_NS, ORE_ATOM, RDF_NS); + desc = xpathDesc.evaluateFirst(doc); if (desc != null && desc.getChild("type", RDF_NS).getAttributeValue("resource", RDF_NS) .equals(DS_NS.getURI() + "DSpaceBitstream")) { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java index e4e387a3ec..39b6c8f29c 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java @@ -30,8 +30,8 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * PREMIS Crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/ParameterizedDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/ParameterizedDisseminationCrosswalk.java index 312aed3543..5d9322339d 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/ParameterizedDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/ParameterizedDisseminationCrosswalk.java @@ -14,7 +14,7 @@ import java.util.Map; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.jdom.Element; +import org.jdom2.Element; /** * Translate DSpace native metadata into an external XML format, with parameters. diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java index f3c51a5d46..2fdbaaad00 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java @@ -36,10 +36,10 @@ import org.dspace.core.Context; import org.dspace.core.SelfNamedPlugin; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.Namespace; -import org.jdom.input.SAXBuilder; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.input.SAXBuilder; /** * Configurable QDC Crosswalk @@ -290,7 +290,7 @@ public class QDCCrosswalk extends SelfNamedPlugin qdc2element.put(qdc, element); element2qdc.put(makeQualifiedTagName(element), qdc); log.debug("Building Maps: qdc=\"" + qdc + "\", element=\"" + element.toString() + "\""); - } catch (org.jdom.JDOMException je) { + } catch (org.jdom2.JDOMException je) { throw new CrosswalkInternalException( "Failed parsing XML fragment in properties file: \"" + prolog + val + postlog + "\": " + je .toString(), je); @@ -326,6 +326,7 @@ public class QDCCrosswalk extends SelfNamedPlugin * @throws IOException if IO error * @throws SQLException if database error * @throws AuthorizeException if authorization error + * @return List of Elements */ @Override public List disseminateList(Context context, DSpaceObject dso) diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java index d36ff3edf5..2c763036ce 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java @@ -26,12 +26,12 @@ import org.dspace.core.factory.CoreServiceFactory; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.workflow.WorkflowException; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.input.SAXBuilder; -import org.jdom.output.XMLOutputter; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.XMLOutputter; /** * Role Crosswalk diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/SimpleDCDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/SimpleDCDisseminationCrosswalk.java index 22ec68070a..2f91c3aa07 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/SimpleDCDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/SimpleDCDisseminationCrosswalk.java @@ -24,8 +24,8 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.SelfNamedPlugin; -import org.jdom.Element; -import org.jdom.Namespace; +import org.jdom2.Element; +import org.jdom2.Namespace; /** * Disseminator for Simple Dublin Core metadata in XML format. @@ -84,6 +84,7 @@ public class SimpleDCDisseminationCrosswalk extends SelfNamedPlugin * @throws IOException if IO error * @throws SQLException if database error * @throws AuthorizeException if authorization error + * @return List of Elements */ @Override public List disseminateList(Context context, DSpaceObject dso) diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XHTMLHeadDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XHTMLHeadDisseminationCrosswalk.java index d03d2dd887..2fbbdd9756 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XHTMLHeadDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XHTMLHeadDisseminationCrosswalk.java @@ -34,9 +34,9 @@ import org.dspace.core.Context; import org.dspace.core.SelfNamedPlugin; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; -import org.jdom.Namespace; -import org.jdom.Verifier; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.Verifier; /** * Crosswalk for creating appropriate <meta> elements to appear in the @@ -178,6 +178,7 @@ public class XHTMLHeadDisseminationCrosswalk * @throws IOException if IO error * @throws SQLException if database error * @throws AuthorizeException if authorization error + * @return List of Elements */ @Override public List disseminateList(Context context, DSpaceObject dso) throws CrosswalkException, diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java index 1c85fd82c5..d4ccebf82e 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTCrosswalk.java @@ -21,7 +21,7 @@ import javax.xml.transform.stream.StreamSource; import org.dspace.core.SelfNamedPlugin; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Namespace; +import org.jdom2.Namespace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -130,12 +130,6 @@ public abstract class XSLTCrosswalk extends SelfNamedPlugin { return aliasList.toArray(new String[aliasList.size()]); } - /** - * We need to force this, because some dependency elsewhere interferes. - */ - private static final String TRANSFORMER_FACTORY_CLASS - = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; - private Transformer transformer = null; private File transformFile = null; private long transformLastModified = 0; @@ -181,8 +175,7 @@ public abstract class XSLTCrosswalk extends SelfNamedPlugin { Source transformSource = new StreamSource(new FileInputStream(transformFile)); TransformerFactory transformerFactory - = TransformerFactory.newInstance( - TRANSFORMER_FACTORY_CLASS, null); + = TransformerFactory.newInstance(); transformer = transformerFactory.newTransformer(transformSource); transformLastModified = transformFile.lastModified(); } catch (TransformerConfigurationException | FileNotFoundException e) { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTDisseminationCrosswalk.java index 6c30c1b1a4..26371b46aa 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTDisseminationCrosswalk.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -41,14 +42,15 @@ import org.dspace.core.factory.CoreServiceFactory; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.Namespace; -import org.jdom.Verifier; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; -import org.jdom.transform.JDOMResult; -import org.jdom.transform.JDOMSource; +import org.jdom2.Content; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.Verifier; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.jdom2.transform.JDOMResult; +import org.jdom2.transform.JDOMSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -244,6 +246,7 @@ public class XSLTDisseminationCrosswalk * @throws SQLException if database error * @throws AuthorizeException if authorization error * @see DisseminationCrosswalk + * @return List of Elements */ @Override public List disseminateList(Context context, DSpaceObject dso) @@ -268,7 +271,12 @@ public class XSLTDisseminationCrosswalk try { JDOMResult result = new JDOMResult(); xform.transform(new JDOMSource(createDIM(dso).getChildren()), result); - return result.getResult(); + List contentList = result.getResult(); + // Transform List into List + List elementList = contentList.stream() + .filter(obj -> obj instanceof Element) + .map(Element.class::cast).collect(Collectors.toList()); + return elementList; } catch (TransformerException e) { LOG.error("Got error: " + e.toString()); throw new CrosswalkInternalException("XSL translation failed: " + e.toString(), e); diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java index 37a822374d..63ef5f7336 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.sql.SQLException; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -34,13 +35,14 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.factory.CoreServiceFactory; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.input.SAXBuilder; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; -import org.jdom.transform.JDOMResult; -import org.jdom.transform.JDOMSource; +import org.jdom2.Content; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.jdom2.transform.JDOMResult; +import org.jdom2.transform.JDOMSource; /** * Configurable XSLT-driven ingestion Crosswalk @@ -141,7 +143,12 @@ public class XSLTIngestionCrosswalk try { JDOMResult result = new JDOMResult(); xform.transform(new JDOMSource(metadata), result); - ingestDIM(context, dso, result.getResult(), createMissingMetadataFields); + List contentList = result.getResult(); + // Transform List into List + List elementList = contentList.stream() + .filter(obj -> obj instanceof Element) + .map(Element.class::cast).collect(Collectors.toList()); + ingestDIM(context, dso, elementList, createMissingMetadataFields); } catch (TransformerException e) { log.error("Got error: " + e.toString()); throw new CrosswalkInternalException("XSL Transformation failed: " + e.toString(), e); diff --git a/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java b/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java index c0649e9ea2..490c3949ea 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java @@ -7,7 +7,8 @@ */ package org.dspace.content.logic; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.core.Context; @@ -21,7 +22,7 @@ import org.dspace.core.Context; */ public class DefaultFilter implements Filter { private LogicalStatement statement; - private static Logger log = Logger.getLogger(Filter.class); + private final static Logger log = LogManager.getLogger(); /** * Set statement from Spring configuration in item-filters.xml diff --git a/dspace-api/src/main/java/org/dspace/content/logic/condition/InCommunityCondition.java b/dspace-api/src/main/java/org/dspace/content/logic/condition/InCommunityCondition.java index b9c1d15d2a..9f588f9c3b 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/condition/InCommunityCondition.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/condition/InCommunityCondition.java @@ -10,7 +10,8 @@ package org.dspace.content.logic.condition; import java.sql.SQLException; import java.util.List; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; @@ -26,7 +27,7 @@ import org.dspace.core.Context; * @version $Revision$ */ public class InCommunityCondition extends AbstractCondition { - private static Logger log = Logger.getLogger(InCommunityCondition.class); + private final static Logger log = LogManager.getLogger(); /** * Return true if item is in one of the specified collections diff --git a/dspace-api/src/main/java/org/dspace/content/logic/condition/IsWithdrawnCondition.java b/dspace-api/src/main/java/org/dspace/content/logic/condition/IsWithdrawnCondition.java index 6475ef09e2..6424e6f35f 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/condition/IsWithdrawnCondition.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/condition/IsWithdrawnCondition.java @@ -7,7 +7,8 @@ */ package org.dspace.content.logic.condition; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.content.logic.LogicalStatementException; import org.dspace.core.Context; @@ -19,7 +20,7 @@ import org.dspace.core.Context; * @version $Revision$ */ public class IsWithdrawnCondition extends AbstractCondition { - private static Logger log = Logger.getLogger(IsWithdrawnCondition.class); + private final static Logger log = LogManager.getLogger(); /** * Return true if item is withdrawn diff --git a/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValueMatchCondition.java b/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValueMatchCondition.java index d9c774485a..4e30c75a2a 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValueMatchCondition.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValueMatchCondition.java @@ -11,7 +11,8 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.logic.LogicalStatementException; @@ -26,7 +27,7 @@ import org.dspace.core.Context; */ public class MetadataValueMatchCondition extends AbstractCondition { - private static Logger log = Logger.getLogger(MetadataValueMatchCondition.class); + private final static Logger log = LogManager.getLogger(); /** * Return true if any value for a specified field in the item matches a specified regex pattern diff --git a/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValuesMatchCondition.java b/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValuesMatchCondition.java index df9cbfbf1d..74ccfa4ca8 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValuesMatchCondition.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/condition/MetadataValuesMatchCondition.java @@ -11,7 +11,8 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.logic.LogicalStatementException; @@ -26,7 +27,7 @@ import org.dspace.core.Context; */ public class MetadataValuesMatchCondition extends AbstractCondition { - private static Logger log = Logger.getLogger(MetadataValuesMatchCondition.class); + private final static Logger log = LogManager.getLogger(); /** * Return true if any value for a specified field in the item matches any of the specified regex patterns diff --git a/dspace-api/src/main/java/org/dspace/content/logic/condition/ReadableByGroupCondition.java b/dspace-api/src/main/java/org/dspace/content/logic/condition/ReadableByGroupCondition.java index e76772803c..65f9925222 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/condition/ReadableByGroupCondition.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/condition/ReadableByGroupCondition.java @@ -10,7 +10,8 @@ package org.dspace.content.logic.condition; import java.sql.SQLException; import java.util.List; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.factory.AuthorizeServiceFactory; import org.dspace.authorize.service.AuthorizeService; @@ -27,7 +28,7 @@ import org.dspace.core.Context; * @version $Revision$ */ public class ReadableByGroupCondition extends AbstractCondition { - private static Logger log = Logger.getLogger(ReadableByGroupCondition.class); + private final static Logger log = LogManager.getLogger(); // Authorize service AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java index 471b9ba27c..03afb5e852 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSDisseminator.java @@ -83,10 +83,10 @@ import org.dspace.license.factory.LicenseServiceFactory; import org.dspace.license.service.CreativeCommonsService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; -import org.jdom.Namespace; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; /** * Base class for disseminator of diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 9a7fffdec5..98277c4f9c 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -51,7 +51,7 @@ import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.workflow.WorkflowException; import org.dspace.workflow.factory.WorkflowServiceFactory; -import org.jdom.Element; +import org.jdom2.Element; /** * Base class for package ingester of METS (Metadata Encoding and Transmission diff --git a/dspace-api/src/main/java/org/dspace/content/packager/DSpaceAIPIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/DSpaceAIPIngester.java index 954a68bfc1..e7be7ab511 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/DSpaceAIPIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/DSpaceAIPIngester.java @@ -20,7 +20,7 @@ import org.dspace.content.crosswalk.CrosswalkException; import org.dspace.content.crosswalk.MetadataValidationException; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.jdom.Element; +import org.jdom2.Element; /** * Subclass of the METS packager framework to ingest a DSpace diff --git a/dspace-api/src/main/java/org/dspace/content/packager/DSpaceMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/DSpaceMETSIngester.java index da3965534f..380764268c 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/DSpaceMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/DSpaceMETSIngester.java @@ -23,7 +23,7 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.core.service.PluginService; -import org.jdom.Element; +import org.jdom2.Element; /** * Packager plugin to ingest a diff --git a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java index 8fb8172aeb..3399bdf0f0 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java @@ -35,15 +35,17 @@ import org.dspace.core.Context; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Content; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.Namespace; -import org.jdom.input.SAXBuilder; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; -import org.jdom.xpath.XPath; +import org.jdom2.Content; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.filter.Filters; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; /** *

    @@ -382,15 +384,12 @@ public class METSManifest { public List getMdFiles() throws MetadataValidationException { if (mdFiles == null) { - try { - // Use a special namespace with known prefix - // so we get the right prefix. - XPath xpath = XPath.newInstance("descendant::mets:mdRef"); - xpath.addNamespace(metsNS); - mdFiles = xpath.selectNodes(mets); - } catch (JDOMException je) { - throw new MetadataValidationException("Failed while searching for mdRef elements in manifest: ", je); - } + // Use a special namespace with known prefix + // so we get the right prefix. + XPathExpression xpath = + XPathFactory.instance() + .compile("descendant::mets:mdRef", Filters.element(), null, metsNS); + mdFiles = xpath.evaluate(mets); } return mdFiles; } @@ -414,25 +413,22 @@ public class METSManifest { return null; } - try { - XPath xpath = XPath.newInstance( - "mets:fileSec/mets:fileGrp[@USE=\"CONTENT\"]/mets:file[@GROUPID=\"" + groupID + "\"]"); - xpath.addNamespace(metsNS); - List oFiles = xpath.selectNodes(mets); - if (oFiles.size() > 0) { - if (log.isDebugEnabled()) { - log.debug("Got ORIGINAL file for derived=" + file.toString()); - } - Element flocat = ((Element) oFiles.get(0)).getChild("FLocat", metsNS); - if (flocat != null) { - return flocat.getAttributeValue("href", xlinkNS); - } + XPathExpression xpath = + XPathFactory.instance() + .compile( + "mets:fileSec/mets:fileGrp[@USE=\"CONTENT\"]/mets:file[@GROUPID=\"" + groupID + "\"]", + Filters.element(), null, metsNS); + List oFiles = xpath.evaluate(mets); + if (oFiles.size() > 0) { + if (log.isDebugEnabled()) { + log.debug("Got ORIGINAL file for derived=" + file.toString()); + } + Element flocat = oFiles.get(0).getChild("FLocat", metsNS); + if (flocat != null) { + return flocat.getAttributeValue("href", xlinkNS); } - return null; - } catch (JDOMException je) { - log.warn("Got exception on XPATH looking for Original file, " + je.toString()); - return null; } + return null; } // translate bundle name from METS to DSpace; METS may be "CONTENT" @@ -888,20 +884,16 @@ public class METSManifest { // use only when path varies each time you call it. protected Element getElementByXPath(String path, boolean nullOk) throws MetadataValidationException { - try { - XPath xpath = XPath.newInstance(path); - xpath.addNamespace(metsNS); - xpath.addNamespace(xlinkNS); - Object result = xpath.selectSingleNode(mets); - if (result == null && nullOk) { - return null; - } else if (result instanceof Element) { - return (Element) result; - } else { - throw new MetadataValidationException("METSManifest: Failed to resolve XPath, path=\"" + path + "\""); - } - } catch (JDOMException je) { - throw new MetadataValidationException("METSManifest: Failed to resolve XPath, path=\"" + path + "\"", je); + XPathExpression xpath = + XPathFactory.instance() + .compile(path, Filters.element(), null, metsNS, xlinkNS); + Element result = xpath.evaluateFirst(mets); + if (result == null && nullOk) { + return null; + } else if (result == null && !nullOk) { + throw new MetadataValidationException("METSManifest: Failed to resolve XPath, path=\"" + path + "\""); + } else { + return result; } } diff --git a/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java b/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java index 8643f60f6c..f627779af8 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/RoleDisseminator.java @@ -35,7 +35,7 @@ import org.dspace.eperson.PasswordHash; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.GroupService; -import org.jdom.Namespace; +import org.jdom2.Namespace; /** * Plugin to export all Group and EPerson objects in XML, perhaps for reloading. diff --git a/dspace-api/src/main/java/org/dspace/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index c0d191caf5..6db27c9e4f 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -152,14 +152,14 @@ public class Email { private static final String RESOURCE_REPOSITORY_NAME = "Email"; private static final Properties VELOCITY_PROPERTIES = new Properties(); static { - VELOCITY_PROPERTIES.put(Velocity.RESOURCE_LOADER, "string"); - VELOCITY_PROPERTIES.put("string.resource.loader.description", + VELOCITY_PROPERTIES.put(Velocity.RESOURCE_LOADERS, "string"); + VELOCITY_PROPERTIES.put("resource.loader.string.description", "Velocity StringResource loader"); - VELOCITY_PROPERTIES.put("string.resource.loader.class", + VELOCITY_PROPERTIES.put("resource.loader.string.class", StringResourceLoader.class.getName()); - VELOCITY_PROPERTIES.put("string.resource.loader.repository.name", + VELOCITY_PROPERTIES.put("resource.loader.string.repository.name", RESOURCE_REPOSITORY_NAME); - VELOCITY_PROPERTIES.put("string.resource.loader.repository.static", + VELOCITY_PROPERTIES.put("resource.loader.string.repository.static", "false"); } diff --git a/dspace-api/src/main/java/org/dspace/curate/CitationPage.java b/dspace-api/src/main/java/org/dspace/curate/CitationPage.java index dbdd070145..1b267286a4 100644 --- a/dspace-api/src/main/java/org/dspace/curate/CitationPage.java +++ b/dspace-api/src/main/java/org/dspace/curate/CitationPage.java @@ -7,6 +7,7 @@ */ package org.dspace.curate; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; @@ -154,7 +155,8 @@ public class CitationPage extends AbstractCurationTask { try { //Create the cited document InputStream citedInputStream = - citationDocument.makeCitedDocument(Curator.curationContext(), bitstream).getLeft(); + new ByteArrayInputStream( + citationDocument.makeCitedDocument(Curator.curationContext(), bitstream).getLeft()); //Add the cited document to the approiate bundle this.addCitedPageToItem(citedInputStream, bundle, pBundle, dBundle, displayMap, item, bitstream); diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java index 5e1d014873..eaa04f4778 100644 --- a/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java @@ -19,7 +19,6 @@ public class CurationCliScriptConfiguration extends CurationScriptConfiguration< public Options getOptions() { options = super.getOptions(); options.addOption("e", "eperson", true, "email address of curating eperson"); - options.getOption("e").setType(String.class); options.getOption("e").setRequired(true); return options; } diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexClientOptions.java b/dspace-api/src/main/java/org/dspace/discovery/IndexClientOptions.java index 62357bd95f..74d9ba0c3a 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexClientOptions.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexClientOptions.java @@ -74,25 +74,17 @@ public enum IndexClientOptions { 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("d", "delete", false, "delete all records from existing index"); - options.getOption("d").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; } } diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index 8660bbebc7..3c93e1c522 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -95,7 +95,6 @@ public abstract class IndexFactoryImpl implements 100000); // Use Tika's Text parser as the streams are always from the TEXT bundle (i.e. already extracted text) - // TODO: We may wish to consider using Tika to extract the text in the future. TextAndCSVParser tikaParser = new TextAndCSVParser(); BodyContentHandler tikaHandler = new BodyContentHandler(charLimit); Metadata tikaMetadata = new Metadata(); diff --git a/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java b/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java index d51a3dfc7f..c20961db75 100644 --- a/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java @@ -8,7 +8,6 @@ package org.dspace.disseminate; import java.awt.Color; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -297,7 +296,7 @@ public class CitationDocumentServiceImpl implements CitationDocumentService, Ini } @Override - public Pair makeCitedDocument(Context context, Bitstream bitstream) + public Pair makeCitedDocument(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException { PDDocument document = new PDDocument(); PDDocument sourceDocument = new PDDocument(); @@ -318,7 +317,7 @@ public class CitationDocumentServiceImpl implements CitationDocumentService, Ini document.save(out); byte[] data = out.toByteArray(); - return Pair.of(new ByteArrayInputStream(data), Long.valueOf(data.length)); + return Pair.of(data, Long.valueOf(data.length)); } } finally { diff --git a/dspace-api/src/main/java/org/dspace/disseminate/service/CitationDocumentService.java b/dspace-api/src/main/java/org/dspace/disseminate/service/CitationDocumentService.java index 4a59de3f5f..0566fc525c 100644 --- a/dspace-api/src/main/java/org/dspace/disseminate/service/CitationDocumentService.java +++ b/dspace-api/src/main/java/org/dspace/disseminate/service/CitationDocumentService.java @@ -8,7 +8,6 @@ package org.dspace.disseminate.service; import java.io.IOException; -import java.io.InputStream; import java.sql.SQLException; import org.apache.commons.lang3.tuple.Pair; @@ -84,7 +83,7 @@ public interface CitationDocumentService { * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - public Pair makeCitedDocument(Context context, Bitstream bitstream) + public Pair makeCitedDocument(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException; /** diff --git a/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java b/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java index 5e81b8ee01..feefe65717 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonConsumer.java @@ -7,10 +7,12 @@ */ package org.dspace.eperson; +import java.io.IOException; import java.util.Date; import java.util.UUID; import javax.mail.MessagingException; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -30,16 +32,17 @@ import org.dspace.services.factory.DSpaceServicesFactory; * Recommended filter: EPerson+Create * * @author Stuart Lewis - * @version $Revision$ */ public class EPersonConsumer implements Consumer { /** * log4j logger */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(EPersonConsumer.class); + private static final Logger log + = org.apache.logging.log4j.LogManager.getLogger(EPersonConsumer.class); protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @@ -74,6 +77,7 @@ public class EPersonConsumer implements Consumer { if (et == Event.CREATE) { // Notify of new user registration String notifyRecipient = configurationService.getProperty("registration.notify"); + EPerson eperson = ePersonService.find(context, id); if (notifyRecipient == null) { notifyRecipient = ""; } @@ -81,7 +85,6 @@ public class EPersonConsumer implements Consumer { if (!notifyRecipient.equals("")) { try { - EPerson eperson = ePersonService.find(context, id); Email adminEmail = Email .getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(), "registration_notify")); adminEmail.addRecipient(notifyRecipient); @@ -103,6 +106,26 @@ public class EPersonConsumer implements Consumer { "error_emailing_administrator", ""), me); } } + + // If enabled, send a "welcome" message to the new EPerson. + if (configurationService.getBooleanProperty("mail.welcome.enabled", false)) { + String addressee = eperson.getEmail(); + if (StringUtils.isNotBlank(addressee)) { + log.debug("Sending welcome email to {}", addressee); + try { + Email message = Email.getEmail( + I18nUtil.getEmailFilename(context.getCurrentLocale(), "welcome")); + message.addRecipient(addressee); + message.send(); + } catch (IOException | MessagingException ex) { + log.warn("Welcome message not sent to {}: {}", + addressee, ex.getMessage()); + } + } else { + log.warn("Welcome message not sent to EPerson {} because it has no email address.", + eperson.getID().toString()); + } + } } else if (et == Event.DELETE) { // TODO: Implement this if required } diff --git a/dspace-api/src/main/java/org/dspace/eperson/Group.java b/dspace-api/src/main/java/org/dspace/eperson/Group.java index 09b5ce189b..b2d3964895 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/Group.java +++ b/dspace-api/src/main/java/org/dspace/eperson/Group.java @@ -201,7 +201,7 @@ public class Group extends DSpaceObject implements DSpaceObjectLegacySupport { void setName(String name) throws SQLException { if (!StringUtils.equals(this.name, name) && !isPermanent()) { this.name = name; - groupsChanged = true; + setMetadataModified(); } } diff --git a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java index cf5c40976d..9cca3777ef 100644 --- a/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java +++ b/dspace-api/src/main/java/org/dspace/google/GoogleAsyncEventListener.java @@ -31,7 +31,8 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -55,7 +56,7 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener { // 20 is the event max set by the GA API private static final int GA_MAX_EVENTS = 20; private static final String ANALYTICS_BATCH_ENDPOINT = "https://www.google-analytics.com/batch"; - private static Logger log = Logger.getLogger(GoogleAsyncEventListener.class); + private final static Logger log = LogManager.getLogger(); private static String analyticsKey; private static CloseableHttpClient httpclient; private static Buffer buffer; diff --git a/dspace-api/src/main/java/org/dspace/harvest/HarvestedCollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/harvest/HarvestedCollectionServiceImpl.java index 88cec74a58..d4bb37cf94 100644 --- a/dspace-api/src/main/java/org/dspace/harvest/HarvestedCollectionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/harvest/HarvestedCollectionServiceImpl.java @@ -29,10 +29,10 @@ import org.dspace.harvest.dao.HarvestedCollectionDAO; import org.dspace.harvest.service.HarvestedCollectionService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.Namespace; -import org.jdom.input.DOMBuilder; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.input.DOMBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.DOMException; import org.xml.sax.SAXException; diff --git a/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java b/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java index 71e00d73d7..a15ed53a93 100644 --- a/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java +++ b/dspace-api/src/main/java/org/dspace/harvest/OAIHarvester.java @@ -70,11 +70,11 @@ import org.dspace.harvest.service.HarvestedCollectionService; import org.dspace.harvest.service.HarvestedItemService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.Namespace; -import org.jdom.input.DOMBuilder; -import org.jdom.output.XMLOutputter; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.input.DOMBuilder; +import org.jdom2.output.XMLOutputter; import org.xml.sax.SAXException; diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOI.java b/dspace-api/src/main/java/org/dspace/identifier/DOI.java index 67668c3abe..e99472e45c 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOI.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOI.java @@ -34,8 +34,6 @@ public class DOI implements Identifier, ReloadableEntity { public static final String SCHEME = "doi:"; - public static final String RESOLVER = "http://dx.doi.org"; - @Id @Column(name = "doi_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "doi_seq") diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index da9c3b718a..2d7914a75a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -1028,7 +1028,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { Item item = (Item) dso; List metadata = itemService.getMetadata(item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null); - String leftPart = DOI.RESOLVER + SLASH + getPrefix() + SLASH + getNamespaceSeparator(); + String leftPart = doiService.getResolver() + SLASH + getPrefix() + SLASH + getNamespaceSeparator(); for (MetadataValue id : metadata) { if (id.getValue().startsWith(leftPart)) { return doiService.DOIFromExternalFormat(id.getValue()); diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java b/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java index aca933aab6..99643db33f 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java @@ -17,11 +17,13 @@ import org.dspace.core.Context; import org.dspace.identifier.dao.DOIDAO; import org.dspace.identifier.doi.DOIIdentifierException; import org.dspace.identifier.service.DOIService; +import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; /** - * Service implementation for the DOI object. - * This class is responsible for all business logic calls for the DOI object and is autowired by spring. + * Service implementation for the {@link DOI} object. + * This class is responsible for all business logic calls for the DOI object + * and is autowired by Spring. * This class should never be accessed directly. * * @author kevinvandevelde at atmire.com @@ -31,6 +33,16 @@ public class DOIServiceImpl implements DOIService { @Autowired(required = true) protected DOIDAO doiDAO; + @Autowired(required = true) + protected ConfigurationService configurationService; + + private static final Pattern DOI_URL_PATTERN + = Pattern.compile("http(s)?://([a-z0-9-.]+)?doi.org(?/.*)", + Pattern.CASE_INSENSITIVE); + private static final String DOI_URL_PATTERN_PATH_GROUP = "path"; + + private static final String RESOLVER_DEFAULT = "https://doi.org"; + protected DOIServiceImpl() { } @@ -66,25 +78,46 @@ public class DOIServiceImpl implements DOIService { if (null == identifier) { throw new IllegalArgumentException("Identifier is null.", new NullPointerException()); } + if (identifier.isEmpty()) { throw new IllegalArgumentException("Cannot format an empty identifier."); } - if (identifier.startsWith(DOI.SCHEME)) { - return DOI.RESOLVER + "/" + identifier.substring(DOI.SCHEME.length()); + + String resolver = getResolver(); + + if (identifier.startsWith(DOI.SCHEME)) { // doi:something + StringBuilder result = new StringBuilder(resolver); + if (!resolver.endsWith("/")) { + result.append('/'); + } + result.append(identifier.substring(DOI.SCHEME.length())); + return result.toString(); } - if (identifier.startsWith("10.") && identifier.contains("/")) { - return DOI.RESOLVER + "/" + identifier; + + if (identifier.startsWith("10.") && identifier.contains("/")) { // 10.something + StringBuilder result = new StringBuilder(resolver); + if (!resolver.endsWith("/")) { + result.append('/'); + } + result.append(identifier); + return result.toString(); } - if (identifier.startsWith(DOI.RESOLVER + "/10.")) { + + if (identifier.startsWith(resolver + "/10.")) { // https://doi.org/10.something return identifier; } + Matcher matcher = DOI_URL_PATTERN.matcher(identifier); + if (matcher.matches()) { // various old URL forms + return resolver + matcher.group(DOI_URL_PATTERN_PATH_GROUP); + } + throw new IdentifierException(identifier + "does not seem to be a DOI."); } @Override public String DOIFromExternalFormat(String identifier) throws DOIIdentifierException { - Pattern pattern = Pattern.compile("^" + DOI.RESOLVER + "/+(10\\..*)$"); + Pattern pattern = Pattern.compile("^" + getResolver() + "/+(10\\..*)$"); Matcher matcher = pattern.matcher(identifier); if (matcher.find()) { return DOI.SCHEME + matcher.group(1); @@ -99,18 +132,29 @@ public class DOIServiceImpl implements DOIService { if (null == identifier) { throw new IllegalArgumentException("Identifier is null.", new NullPointerException()); } - if (identifier.startsWith(DOI.SCHEME)) { - return identifier; - } + if (identifier.isEmpty()) { throw new IllegalArgumentException("Cannot format an empty identifier."); } - if (identifier.startsWith("10.") && identifier.contains("/")) { + + if (identifier.startsWith(DOI.SCHEME)) { // doi:something + return identifier; + } + + if (identifier.startsWith("10.") && identifier.contains("/")) { // 10.something return DOI.SCHEME + identifier; } - if (identifier.startsWith(DOI.RESOLVER + "/10.")) { - return DOI.SCHEME + identifier.substring(18); + + String resolver = getResolver(); + if (identifier.startsWith(resolver + "/10.")) { //https://doi.org/10.something + return DOI.SCHEME + identifier.substring(resolver.length()); } + + Matcher matcher = DOI_URL_PATTERN.matcher(identifier); + if (matcher.matches()) { // various old URL forms + return DOI.SCHEME + matcher.group(DOI_URL_PATTERN_PATH_GROUP).substring(1); + } + throw new DOIIdentifierException(identifier + "does not seem to be a DOI.", DOIIdentifierException.UNRECOGNIZED); } @@ -126,4 +170,14 @@ public class DOIServiceImpl implements DOIService { throws SQLException { return doiDAO.findSimilarNotInState(context, doiPattern, statuses, dsoIsNotNull); } + + @Override + public String getResolver() { + String resolver = configurationService.getProperty("identifier.doi.resolver", + RESOLVER_DEFAULT); + if (resolver.endsWith("/")) { + resolver = resolver.substring(0, resolver.length() - 1); + } + return resolver; + } } diff --git a/dspace-api/src/main/java/org/dspace/identifier/DataCiteXMLCreator.java b/dspace-api/src/main/java/org/dspace/identifier/DataCiteXMLCreator.java index 0ea25ff3a4..ae2cd248d4 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DataCiteXMLCreator.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DataCiteXMLCreator.java @@ -23,8 +23,8 @@ import org.dspace.core.factory.CoreServiceFactory; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.services.ConfigurationService; import org.dspace.utils.DSpace; -import org.jdom.Element; -import org.jdom.output.XMLOutputter; +import org.jdom2.Element; +import org.jdom2.output.XMLOutputter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index bc8ea90957..57136d6143 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -45,13 +45,13 @@ import org.dspace.core.factory.CoreServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.identifier.DOI; import org.dspace.services.ConfigurationService; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.filter.ElementFilter; -import org.jdom.input.SAXBuilder; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.filter.ElementFilter; +import org.jdom2.input.SAXBuilder; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java b/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java index 9af1fd8a0a..5bd68a9061 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java +++ b/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java @@ -17,26 +17,65 @@ import org.dspace.identifier.IdentifierException; import org.dspace.identifier.doi.DOIIdentifierException; /** - * Service interface class for the DOI object. - * The implementation of this class is responsible for all business logic calls for the DOI object and is autowired - * by spring + * Service interface class for the {@link DOI} object. + * The implementation of this class is responsible for all business logic calls + * for the {@link DOI} object and is autowired by Spring. * * @author kevinvandevelde at atmire.com */ public interface DOIService { + /** + * Update a DOI in storage. + * + * @param context current DSpace session. + * @param doi the DOI to persist. + * @throws SQLException passed through. + */ public void update(Context context, DOI doi) throws SQLException; + /** + * Create a new DOI in storage. + * + * @param context current DSpace session. + * @return the new DOI. + * @throws SQLException passed through. + */ public DOI create(Context context) throws SQLException; + /** + * Find a specific DOI in storage. + * + * @param context current DSpace session. + * @param doi string representation of the DOI. + * @return the DOI object found. + * @throws SQLException passed through, can mean none found. + */ public DOI findByDoi(Context context, String doi) throws SQLException; + /** + * Find the DOI assigned to a given DSpace Object. + * + * @param context current DSpace session. + * @param dso The DSpace Object. + * @return the DSO's DOI. + * @throws SQLException passed through. + */ public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso) throws SQLException; + /** + * Find the DOI assigned to a given DSpace Object, unless it has one of a + * given set of statuses. + * + * @param context current DSpace context. + * @param dso the DSpace Object. + * @param statusToExclude uninteresting statuses. + * @return the DSO's DOI. + * @throws SQLException passed through. + */ public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso, List statusToExclude) throws SQLException; - /** * This method helps to convert a DOI into a URL. It takes DOIs in one of * the following formats and returns it as URL (f.e. @@ -49,12 +88,18 @@ public interface DOIService { * * @param identifier A DOI that should be returned in external form. * @return A String containing a URL to the official DOI resolver. - * @throws IllegalArgumentException If identifier is null or an empty String. - * @throws org.dspace.identifier.IdentifierException If identifier could not be recognized as valid DOI. + * @throws IllegalArgumentException If identifier is null or an empty String. + * @throws IdentifierException If identifier could not be recognized as valid DOI. */ public String DOIToExternalForm(String identifier) throws IdentifierException; + /** + * Convert an HTTP DOI URL (https://doi.org/10.something) to a "doi:" URI. + * @param identifier HTTP URL + * @return DOI URI + * @throws DOIIdentifierException if {@link identifier} is not recognizable. + */ public String DOIFromExternalFormat(String identifier) throws DOIIdentifierException; @@ -64,16 +109,24 @@ public interface DOIService { * @param identifier Identifier to format, following format are accepted: * f.e. 10.123/456, doi:10.123/456, http://dx.doi.org/10.123/456. * @return Given Identifier with DOI-Scheme, f.e. doi:10.123/456. - * @throws IllegalArgumentException If identifier is empty or null. - * @throws org.dspace.identifier.doi.DOIIdentifierException If DOI could not be recognized. + * @throws IllegalArgumentException If identifier is empty or null. + * @throws DOIIdentifierException If DOI could not be recognized. */ public String formatIdentifier(String identifier) throws DOIIdentifierException; + /** + * Find all DOIs that have one of a given set of statuses. + * @param context current DSpace session. + * @param statuses desired statuses. + * @return all DOIs having any of the given statuses. + * @throws SQLException passed through. + */ public List getDOIsByStatus(Context context, List statuses) throws SQLException; /** - * Find all DOIs that are similar to the specified pattern ant not in the specified states. + * Find all DOIs that are similar to the specified pattern and not in the + * specified states. * * @param context DSpace context * @param doiPattern The pattern, e.g. "10.5072/123.%" @@ -85,4 +138,11 @@ public interface DOIService { public List getSimilarDOIsNotInState(Context context, String doiPattern, List statuses, boolean dsoIsNotNull) throws SQLException; + + /** + * Get the URL stem of the DOI resolver, e.g. "https://doi.org/". + * + * @return URL to the DOI resolver. + */ + public String getResolver(); } diff --git a/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java b/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java index 56cd432d91..beeb40ceac 100644 --- a/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java +++ b/dspace-api/src/main/java/org/dspace/iiif/consumer/CanvasCacheEvictService.java @@ -23,7 +23,7 @@ public class CanvasCacheEvictService { CacheManager cacheManager; public void evictSingleCacheValue(String cacheKey) { - Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evict(cacheKey); + Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evictIfPresent(cacheKey); } } diff --git a/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java b/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java index 967d0667a6..963ce3113f 100644 --- a/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java +++ b/dspace-api/src/main/java/org/dspace/iiif/consumer/ManifestsCacheEvictService.java @@ -26,11 +26,11 @@ public class ManifestsCacheEvictService { CacheManager cacheManager; public void evictSingleCacheValue(String cacheKey) { - Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evict(cacheKey); + Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evictIfPresent(cacheKey); } public void evictAllCacheValues() { - Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).clear(); + Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).invalidate(); } } diff --git a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/metadatamapping/contributor/ArXivIdMetadataContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/metadatamapping/contributor/ArXivIdMetadataContributor.java index ed5ac5960b..7bd42cf07a 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/metadatamapping/contributor/ArXivIdMetadataContributor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/metadatamapping/contributor/ArXivIdMetadataContributor.java @@ -9,10 +9,10 @@ package org.dspace.importer.external.arxiv.metadatamapping.contributor; import java.util.Collection; -import org.apache.axiom.om.OMElement; import org.dspace.importer.external.metadatamapping.MetadatumDTO; import org.dspace.importer.external.metadatamapping.contributor.MetadataContributor; import org.dspace.importer.external.metadatamapping.contributor.SimpleXpathMetadatumContributor; +import org.jdom2.Element; /** * Arxiv specific implementation of {@link MetadataContributor} @@ -32,7 +32,7 @@ public class ArXivIdMetadataContributor extends SimpleXpathMetadatumContributor * @return a collection of import records. Only the identifier of the found records may be put in the record. */ @Override - public Collection contributeMetadata(OMElement t) { + public Collection contributeMetadata(Element t) { Collection values = super.contributeMetadata(t); parseValue(values); return values; diff --git a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java index 6b418423fa..96689e62ba 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java @@ -7,8 +7,10 @@ */ package org.dspace.importer.external.arxiv.service; +import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; @@ -20,10 +22,6 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.axiom.om.OMElement; -import org.apache.axiom.om.OMXMLBuilderFactory; -import org.apache.axiom.om.OMXMLParserWrapper; -import org.apache.axiom.om.xpath.AXIOMXPath; import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; @@ -31,7 +29,14 @@ import org.dspace.importer.external.datamodel.Query; import org.dspace.importer.external.exception.MetadataSourceException; import org.dspace.importer.external.service.AbstractImportMetadataSourceService; import org.dspace.importer.external.service.components.QuerySource; -import org.jaxen.JaxenException; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.Namespace; +import org.jdom2.filter.Filters; +import org.jdom2.input.SAXBuilder; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; /** * Implements a data source for querying ArXiv @@ -39,7 +44,7 @@ import org.jaxen.JaxenException; * @author Pasquale Cavallo (pasquale.cavallo at 4Science dot it) * */ -public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService +public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService implements QuerySource { private WebTarget webTarget; @@ -213,15 +218,20 @@ public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadata Response response = invocationBuilder.get(); if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - OMXMLParserWrapper records = OMXMLBuilderFactory.createOMBuilder(new StringReader(responseString)); - OMElement element = records.getDocumentElement(); - AXIOMXPath xpath = null; + + SAXBuilder saxBuilder = new SAXBuilder(); + Document document = saxBuilder.build(new StringReader(responseString)); + Element root = document.getRootElement(); + + List namespaces = Arrays.asList(Namespace.getNamespace("opensearch", + "http://a9.com/-/spec/opensearch/1.1/")); + XPathExpression xpath = + XPathFactory.instance().compile("opensearch:totalResults", Filters.element(), null, namespaces); + + Element count = xpath.evaluateFirst(root); try { - xpath = new AXIOMXPath("opensearch:totalResults"); - xpath.addNamespace("opensearch", "http://a9.com/-/spec/opensearch/1.1/"); - OMElement count = (OMElement) xpath.selectSingleNode(element); return Integer.parseInt(count.getText()); - } catch (JaxenException e) { + } catch (NumberFormatException e) { return null; } } else { @@ -274,8 +284,8 @@ public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadata Response response = invocationBuilder.get(); if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - List omElements = splitToRecords(responseString); - for (OMElement record : omElements) { + List elements = splitToRecords(responseString); + for (Element record : elements) { results.add(transformSourceRecords(record)); } return results; @@ -321,8 +331,8 @@ public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadata Response response = invocationBuilder.get(); if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - List omElements = splitToRecords(responseString); - for (OMElement record : omElements) { + List elements = splitToRecords(responseString); + for (Element record : elements) { results.add(transformSourceRecords(record)); } return results; @@ -359,8 +369,8 @@ public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadata Response response = invocationBuilder.get(); if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - List omElements = splitToRecords(responseString); - for (OMElement record : omElements) { + List elements = splitToRecords(responseString); + for (Element record : elements) { results.add(transformSourceRecords(record)); } return results; @@ -387,16 +397,21 @@ public class ArXivImportMetadataSourceServiceImpl extends AbstractImportMetadata } } - private List splitToRecords(String recordsSrc) { - OMXMLParserWrapper records = OMXMLBuilderFactory.createOMBuilder(new StringReader(recordsSrc)); - OMElement element = records.getDocumentElement(); - AXIOMXPath xpath = null; + private List splitToRecords(String recordsSrc) { + try { - xpath = new AXIOMXPath("ns:entry"); - xpath.addNamespace("ns", "http://www.w3.org/2005/Atom"); - List recordsList = xpath.selectNodes(element); + SAXBuilder saxBuilder = new SAXBuilder(); + Document document = saxBuilder.build(new StringReader(recordsSrc)); + Element root = document.getRootElement(); + + List namespaces = Arrays.asList(Namespace.getNamespace("ns", + "http://www.w3.org/2005/Atom")); + XPathExpression xpath = + XPathFactory.instance().compile("ns:entry", Filters.element(), null, namespaces); + + List recordsList = xpath.evaluate(root); return recordsList; - } catch (JaxenException e) { + } catch (JDOMException | IOException e) { return null; } } diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java index 87cdbfa6ed..65d6d66947 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java @@ -7,33 +7,36 @@ */ package org.dspace.importer.external.metadatamapping.contributor; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.annotation.Resource; -import org.apache.axiom.om.OMAttribute; -import org.apache.axiom.om.OMElement; -import org.apache.axiom.om.OMText; -import org.apache.axiom.om.xpath.AXIOMXPath; +import org.apache.logging.log4j.Logger; import org.dspace.importer.external.metadatamapping.MetadataFieldConfig; import org.dspace.importer.external.metadatamapping.MetadataFieldMapping; import org.dspace.importer.external.metadatamapping.MetadatumDTO; -import org.jaxen.JaxenException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.jdom2.Attribute; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.Text; +import org.jdom2.filter.Filters; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; import org.springframework.beans.factory.annotation.Autowired; /** - * Metadata contributor that takes an axiom OMElement and turns it into a metadatum + * Metadata contributor that takes a JDOM Element and turns it into a metadatum * * @author Roeland Dillen (roeland at atmire dot com) */ -public class SimpleXpathMetadatumContributor implements MetadataContributor { +public class SimpleXpathMetadatumContributor implements MetadataContributor { private MetadataFieldConfig field; - private static final Logger log = LoggerFactory.getLogger(SimpleXpathMetadatumContributor.class); + private static final Logger log + = org.apache.logging.log4j.LogManager.getLogger(); /** * Return prefixToNamespaceMapping @@ -44,14 +47,14 @@ public class SimpleXpathMetadatumContributor implements MetadataContributor> metadataFieldMapping; + private MetadataFieldMapping> metadataFieldMapping; /** * Return metadataFieldMapping * * @return MetadataFieldMapping */ - public MetadataFieldMapping> getMetadataFieldMapping() { + public MetadataFieldMapping> getMetadataFieldMapping() { return metadataFieldMapping; } @@ -62,7 +65,7 @@ public class SimpleXpathMetadatumContributor implements MetadataContributor> metadataFieldMapping) { + MetadataFieldMapping> metadataFieldMapping) { this.metadataFieldMapping = metadataFieldMapping; } @@ -140,36 +143,35 @@ public class SimpleXpathMetadatumContributor implements MetadataContributor contributeMetadata(OMElement t) { + public Collection contributeMetadata(Element t) { List values = new LinkedList<>(); - try { - AXIOMXPath xpath = new AXIOMXPath(query); - for (String ns : prefixToNamespaceMapping.keySet()) { - xpath.addNamespace(prefixToNamespaceMapping.get(ns), ns); - } - List nodes = xpath.selectNodes(t); - for (Object el : nodes) { - if (el instanceof OMElement) { - values.add(metadataFieldMapping.toDCValue(field, ((OMElement) el).getText())); - } else if (el instanceof OMAttribute) { - values.add(metadataFieldMapping.toDCValue(field, ((OMAttribute) el).getAttributeValue())); - } else if (el instanceof String) { - values.add(metadataFieldMapping.toDCValue(field, (String) el)); - } else if (el instanceof OMText) { - values.add(metadataFieldMapping.toDCValue(field, ((OMText) el).getText())); - } else { - log.error("node of type: " + el.getClass()); - } - } - return values; - } catch (JaxenException e) { - log.error(query, e); - throw new RuntimeException(e); + + List namespaces = new ArrayList<>(); + for (String ns : prefixToNamespaceMapping.keySet()) { + namespaces.add(Namespace.getNamespace(prefixToNamespaceMapping.get(ns), ns)); } + XPathExpression xpath = + XPathFactory.instance().compile(query, Filters.fpassthrough(), null, namespaces); + + List nodes = xpath.evaluate(t); + for (Object el : nodes) { + if (el instanceof Element) { + values.add(metadataFieldMapping.toDCValue(field, ((Element) el).getText())); + } else if (el instanceof Attribute) { + values.add(metadataFieldMapping.toDCValue(field, ((Attribute) el).getValue())); + } else if (el instanceof String) { + values.add(metadataFieldMapping.toDCValue(field, (String) el)); + } else if (el instanceof Text) { + values.add(metadataFieldMapping.toDCValue(field, ((Text) el).getText())); + } else { + log.error("Encountered unsupported XML node of type: {}. Skipped that node.", el.getClass()); + } + } + return values; } } diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 4802dcfa17..73768330d8 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -25,10 +25,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.google.common.io.CharStreams; -import org.apache.axiom.om.OMElement; -import org.apache.axiom.om.OMXMLBuilderFactory; -import org.apache.axiom.om.OMXMLParserWrapper; -import org.apache.axiom.om.xpath.AXIOMXPath; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -38,7 +34,13 @@ import org.dspace.importer.external.exception.MetadataSourceException; import org.dspace.importer.external.service.AbstractImportMetadataSourceService; import org.dspace.importer.external.service.components.FileSource; import org.dspace.importer.external.service.components.QuerySource; -import org.jaxen.JaxenException; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.filter.Filters; +import org.jdom2.input.SAXBuilder; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; /** * Implements a data source for querying PubMed Central @@ -46,7 +48,7 @@ import org.jaxen.JaxenException; * @author Roeland Dillen (roeland at atmire dot com) * @author Pasquale Cavallo (pasquale.cavallo at 4science dot it) */ -public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService +public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService implements QuerySource, FileSource { private String baseAddress; @@ -59,7 +61,7 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat /** * Set the file extensions supported by this metadata service * - * @param supportedExtensionsthe file extensions (xml,txt,...) supported by this service + * @param supportedExtensions the file extensions (xml,txt,...) supported by this service */ public void setSupportedExtensions(List supportedExtensions) { this.supportedExtensions = supportedExtensions; @@ -243,17 +245,21 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat private String getSingleElementValue(String src, String elementName) { - OMXMLParserWrapper records = OMXMLBuilderFactory.createOMBuilder(new StringReader(src)); - OMElement element = records.getDocumentElement(); - AXIOMXPath xpath = null; String value = null; + try { - xpath = new AXIOMXPath("//" + elementName); - List recordsList = xpath.selectNodes(element); - if (!recordsList.isEmpty()) { - value = recordsList.get(0).getText(); + SAXBuilder saxBuilder = new SAXBuilder(); + Document document = saxBuilder.build(new StringReader(src)); + Element root = document.getRootElement(); + + XPathExpression xpath = + XPathFactory.instance().compile("//" + elementName, Filters.element()); + + Element record = xpath.evaluateFirst(root); + if (record != null) { + value = record.getText(); } - } catch (JaxenException e) { + } catch (JDOMException | IOException e) { value = null; } return value; @@ -314,9 +320,9 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat invocationBuilder = getRecordsTarget.request(MediaType.TEXT_PLAIN_TYPE); response = invocationBuilder.get(); - List omElements = splitToRecords(response.readEntity(String.class)); + List elements = splitToRecords(response.readEntity(String.class)); - for (OMElement record : omElements) { + for (Element record : elements) { records.add(transformSourceRecords(record)); } @@ -324,15 +330,18 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat } } - private List splitToRecords(String recordsSrc) { - OMXMLParserWrapper records = OMXMLBuilderFactory.createOMBuilder(new StringReader(recordsSrc)); - OMElement element = records.getDocumentElement(); - AXIOMXPath xpath = null; + private List splitToRecords(String recordsSrc) { try { - xpath = new AXIOMXPath("//PubmedArticle"); - List recordsList = xpath.selectNodes(element); + SAXBuilder saxBuilder = new SAXBuilder(); + Document document = saxBuilder.build(new StringReader(recordsSrc)); + Element root = document.getRootElement(); + + XPathExpression xpath = + XPathFactory.instance().compile("//PubmedArticle", Filters.element()); + + List recordsList = xpath.evaluate(root); return recordsList; - } catch (JaxenException e) { + } catch (JDOMException | IOException e) { return null; } } @@ -362,13 +371,13 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat Response response = invocationBuilder.get(); - List omElements = splitToRecords(response.readEntity(String.class)); + List elements = splitToRecords(response.readEntity(String.class)); - if (omElements.size() == 0) { + if (elements.isEmpty()) { return null; } - return transformSourceRecords(omElements.get(0)); + return transformSourceRecords(elements.get(0)); } } @@ -441,8 +450,8 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat private List parseXMLString(String xml) { List records = new LinkedList(); - List omElements = splitToRecords(xml); - for (OMElement record : omElements) { + List elements = splitToRecords(xml); + for (Element record : elements) { records.add(transformSourceRecords(record)); } return records; diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java index 0c061d2d64..64450b796c 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java @@ -10,7 +10,7 @@ package org.dspace.license; import java.io.IOException; import java.util.Map; -import org.jdom.Document; +import org.jdom2.Document; /** * Service interface class for the Creative commons license connector service. diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 792c25d629..619227432d 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -32,13 +32,14 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.Logger; import org.dspace.services.ConfigurationService; -import org.jaxen.JaxenException; -import org.jaxen.jdom.JDOMXPath; -import org.jdom.Attribute; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.input.SAXBuilder; +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.filter.Filters; +import org.jdom2.input.SAXBuilder; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.xml.sax.InputSource; @@ -96,7 +97,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, List licenses; try (CloseableHttpResponse response = client.execute(httpGet)) { licenses = retrieveLicenses(response); - } catch (JDOMException | JaxenException | IOException e) { + } catch (JDOMException | IOException e) { log.error("Error while retrieving the license details using url: " + uri, e); licenses = Collections.emptyList(); } @@ -110,7 +111,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, try (CloseableHttpResponse response = client.execute(licenseHttpGet)) { CCLicense ccLicense = retrieveLicenseObject(license, response); ccLicenses.put(ccLicense.getLicenseId(), ccLicense); - } catch (JaxenException | JDOMException | IOException e) { + } catch (JDOMException | IOException e) { log.error("Error while retrieving the license details using url: " + licenseUri, e); } } @@ -125,25 +126,23 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, * @param response The response from the API * @return a list of license identifiers for which details need to be retrieved * @throws IOException - * @throws JaxenException * @throws JDOMException */ private List retrieveLicenses(CloseableHttpResponse response) - throws IOException, JaxenException, JDOMException { + throws IOException, JDOMException { List domains = new LinkedList<>(); String[] excludedLicenses = configurationService.getArrayProperty("cc.license.classfilter"); - String responseString = EntityUtils.toString(response.getEntity()); - JDOMXPath licenseClassXpath = new JDOMXPath("//licenses/license"); - + XPathExpression licenseClassXpath = + XPathFactory.instance().compile("//licenses/license", Filters.element()); try (StringReader stringReader = new StringReader(responseString)) { InputSource is = new InputSource(stringReader); - org.jdom.Document classDoc = this.parser.build(is); + org.jdom2.Document classDoc = this.parser.build(is); - List elements = licenseClassXpath.selectNodes(classDoc); + List elements = licenseClassXpath.evaluate(classDoc); for (Element element : elements) { String licenseId = getSingleNodeValue(element, "@id"); if (StringUtils.isNotBlank(licenseId) && !ArrayUtils.contains(excludedLicenses, licenseId)) { @@ -163,30 +162,29 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, * @param response for a specific CC License response * @return the corresponding CC License Object * @throws IOException - * @throws JaxenException * @throws JDOMException */ private CCLicense retrieveLicenseObject(final String licenseId, CloseableHttpResponse response) - throws IOException, JaxenException, JDOMException { + throws IOException, JDOMException { String responseString = EntityUtils.toString(response.getEntity()); - - JDOMXPath licenseClassXpath = new JDOMXPath("//licenseclass"); - JDOMXPath licenseFieldXpath = new JDOMXPath("field"); - + XPathExpression licenseClassXpath = + XPathFactory.instance().compile("//licenseclass", Filters.fpassthrough()); + XPathExpression licenseFieldXpath = + XPathFactory.instance().compile("field", Filters.element()); try (StringReader stringReader = new StringReader(responseString)) { InputSource is = new InputSource(stringReader); - org.jdom.Document classDoc = this.parser.build(is); + org.jdom2.Document classDoc = this.parser.build(is); - Object element = licenseClassXpath.selectSingleNode(classDoc); + Object element = licenseClassXpath.evaluateFirst(classDoc); String licenseLabel = getSingleNodeValue(element, "label"); List ccLicenseFields = new LinkedList<>(); - List licenseFields = licenseFieldXpath.selectNodes(element); + List licenseFields = licenseFieldXpath.evaluate(element); for (Element licenseField : licenseFields) { CCLicenseField ccLicenseField = parseLicenseField(licenseField); ccLicenseFields.add(ccLicenseField); @@ -196,13 +194,14 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, } } - private CCLicenseField parseLicenseField(final Element licenseField) throws JaxenException { + private CCLicenseField parseLicenseField(final Element licenseField) { String id = getSingleNodeValue(licenseField, "@id"); String label = getSingleNodeValue(licenseField, "label"); String description = getSingleNodeValue(licenseField, "description"); - JDOMXPath enumXpath = new JDOMXPath("enum"); - List enums = enumXpath.selectNodes(licenseField); + XPathExpression enumXpath = + XPathFactory.instance().compile("enum", Filters.element()); + List enums = enumXpath.evaluate(licenseField); List ccLicenseFieldEnumList = new LinkedList<>(); @@ -215,7 +214,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, } - private CCLicenseFieldEnum parseEnum(final Element enumElement) throws JaxenException { + private CCLicenseFieldEnum parseEnum(final Element enumElement) { String id = getSingleNodeValue(enumElement, "@id"); String label = getSingleNodeValue(enumElement, "label"); String description = getSingleNodeValue(enumElement, "description"); @@ -236,9 +235,10 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, } } - private String getSingleNodeValue(final Object t, String query) throws JaxenException { - JDOMXPath xpath = new JDOMXPath(query); - Object singleNode = xpath.selectSingleNode(t); + private String getSingleNodeValue(final Object t, String query) { + XPathExpression xpath = + XPathFactory.instance().compile(query, Filters.fpassthrough()); + Object singleNode = xpath.evaluateFirst(t); return getNodeValue(singleNode); } @@ -273,7 +273,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, try (CloseableHttpResponse response = client.execute(httpPost)) { return retrieveLicenseUri(response); - } catch (JDOMException | JaxenException | IOException e) { + } catch (JDOMException | IOException e) { log.error("Error while retrieving the license uri for license : " + licenseId + " with answers " + answerMap.toString(), e); } @@ -286,21 +286,20 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, * @param response for a specific CC License URI response * @return the corresponding CC License URI as a string * @throws IOException - * @throws JaxenException * @throws JDOMException */ private String retrieveLicenseUri(final CloseableHttpResponse response) - throws IOException, JaxenException, JDOMException { + throws IOException, JDOMException { String responseString = EntityUtils.toString(response.getEntity()); - JDOMXPath licenseClassXpath = new JDOMXPath("//result/license-uri"); - + XPathExpression licenseClassXpath = + XPathFactory.instance().compile("//result/license-uri", Filters.fpassthrough()); try (StringReader stringReader = new StringReader(responseString)) { InputSource is = new InputSource(stringReader); - org.jdom.Document classDoc = this.parser.build(is); + org.jdom2.Document classDoc = this.parser.build(is); - Object node = licenseClassXpath.selectSingleNode(classDoc); + Object node = licenseClassXpath.evaluateFirst(classDoc); String nodeValue = getNodeValue(node); if (StringUtils.isNotBlank(nodeValue)) { @@ -364,12 +363,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, * @return the license name */ public String retrieveLicenseName(final Document doc) { - try { - return getSingleNodeValue(doc, "//result/license-name"); - } catch (JaxenException e) { - log.error("Error while retrieving the license name from the license document", e); - } - return null; + return getSingleNodeValue(doc, "//result/license-name"); } } diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index ccc660b63b..96f110c101 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -40,8 +40,8 @@ import org.dspace.core.Utils; import org.dspace.license.service.CreativeCommonsService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Document; -import org.jdom.transform.JDOMSource; +import org.jdom2.Document; +import org.jdom2.transform.JDOMSource; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; diff --git a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java index 1f5f1ddd02..0f4911aa3e 100644 --- a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java +++ b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java @@ -18,7 +18,7 @@ import org.dspace.content.Bitstream; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.license.CCLicense; -import org.jdom.Document; +import org.jdom2.Document; /** * Service interface class for the Creative commons licensing. diff --git a/dspace-api/src/main/java/org/dspace/rdf/RDFConsumer.java b/dspace-api/src/main/java/org/dspace/rdf/RDFConsumer.java index 76ae0cd2d2..34ab572d1b 100644 --- a/dspace-api/src/main/java/org/dspace/rdf/RDFConsumer.java +++ b/dspace-api/src/main/java/org/dspace/rdf/RDFConsumer.java @@ -286,51 +286,54 @@ public class RDFConsumer implements Consumer { @Override public void end(Context ctx) throws Exception { log.debug("Started processing of queued events."); - // create a new context, to be sure to work as anonymous user - // we don't want to store private data in a triplestore with public - // SPARQL endpoint. - ctx = new Context(Context.Mode.READ_ONLY); - if (toDelete == null) { - log.debug("Deletion queue does not exists, creating empty queue."); - this.toDelete = new LinkedList<>(); - } - if (toConvert != null) { - log.debug("Starting conversion of DSpaceObjects."); + // store the context mode, set context read only for performance reasons, and restore the old mode + Context.Mode oldMode = ctx.getCurrentMode(); + try { + ctx.setMode(Context.Mode.READ_ONLY); + if (toDelete == null) { + log.debug("Deletion queue does not exists, creating empty queue."); + this.toDelete = new LinkedList<>(); + } + if (toConvert != null) { + log.debug("Starting conversion of DSpaceObjects."); + while (true) { + DSOIdentifier id; + try { + id = toConvert.removeFirst(); + } catch (NoSuchElementException ex) { + break; + } + + if (toDelete.contains(id)) { + log.debug("Skipping " + Constants.typeText[id.type] + " " + + id.id.toString() + " as it is marked for " + + "deletion as well."); + continue; + } + log.debug("Converting " + Constants.typeText[id.type] + " " + + id.id.toString() + "."); + convert(ctx, id); + } + log.debug("Conversion ended."); + } + log.debug("Starting to delete data from the triple store..."); while (true) { DSOIdentifier id; try { - id = toConvert.removeFirst(); + id = toDelete.removeFirst(); } catch (NoSuchElementException ex) { break; } - if (toDelete.contains(id)) { - log.debug("Skipping " + Constants.typeText[id.type] + " " - + id.id.toString() + " as it is marked for " - + "deletion as well."); - continue; - } - log.debug("Converting " + Constants.typeText[id.type] + " " + log.debug("Going to delete data from " + + Constants.typeText[id.type] + " " + id.id.toString() + "."); - convert(ctx, id); + delete(ctx, id); } - log.debug("Conversion ended."); + } finally { + // restore context mode + ctx.setMode(oldMode); } - log.debug("Starting to delete data from the triple store..."); - while (true) { - DSOIdentifier id; - try { - id = toDelete.removeFirst(); - } catch (NoSuchElementException ex) { - break; - } - - log.debug("Going to delete data from " + - Constants.typeText[id.type] + " " - + id.id.toString() + "."); - delete(ctx, id); - } - ctx.abort(); log.debug("Deletion finished."); } diff --git a/dspace-api/src/main/java/org/dspace/statistics/DataTermsFacet.java b/dspace-api/src/main/java/org/dspace/statistics/DataTermsFacet.java deleted file mode 100644 index 9de06b7bb8..0000000000 --- a/dspace-api/src/main/java/org/dspace/statistics/DataTermsFacet.java +++ /dev/null @@ -1,70 +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.statistics; - -import java.util.ArrayList; -import java.util.List; - -import com.google.gson.Gson; - -/** - * A neutral data object to hold data for statistics. - */ -public class DataTermsFacet { - private List terms; - - public DataTermsFacet() { - terms = new ArrayList(); - } - - public void addTermFacet(TermsFacet termsFacet) { - terms.add(termsFacet); - } - - /** - * Render this data object into JSON format. - * - * An example of the output could be of the format: - * [{"term":"247166","count":10},{"term":"247168","count":6}] - * - * @return JSON-formatted data. - */ - public String toJson() { - Gson gson = new Gson(); - return gson.toJson(terms); - } - - - public static class TermsFacet { - private String term; - private Integer count; - - public TermsFacet(String term, Integer count) { - setTerm(term); - setCount(count); - } - - public String getTerm() { - return term; - } - - public void setTerm(String term) { - this.term = term; - } - - public Integer getCount() { - return count; - } - - public void setCount(Integer count) { - this.count = count; - } - - - } -} diff --git a/dspace-api/src/main/java/org/dspace/statistics/export/RetryFailedOpenUrlTrackerScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/statistics/export/RetryFailedOpenUrlTrackerScriptConfiguration.java index b5d65aa4e5..dcae4aa4cb 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/export/RetryFailedOpenUrlTrackerScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/statistics/export/RetryFailedOpenUrlTrackerScriptConfiguration.java @@ -56,15 +56,12 @@ public class RetryFailedOpenUrlTrackerScriptConfiguration { + + public static final DatabaseAwareLobType INSTANCE = new DatabaseAwareLobType(); + + public DatabaseAwareLobType() { + super( getDbDescriptor(), StringTypeDescriptor.INSTANCE ); + } + + public static SqlTypeDescriptor getDbDescriptor() { + if ( isPostgres() ) { + return LongVarcharTypeDescriptor.INSTANCE; + } else { + return ClobTypeDescriptor.DEFAULT; + } + } + + private static boolean isPostgres() { + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + String dbDialect = configurationService.getProperty("db.dialect"); + + return StringUtils.containsIgnoreCase(dbDialect, "PostgreSQL"); + } + + @Override + public String getName() { + return "database_aware_lob"; + } +} + diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/postgres/DSpacePostgreSQL82Dialect.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/postgres/DSpacePostgreSQL82Dialect.java deleted file mode 100644 index 2701c22fd2..0000000000 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/postgres/DSpacePostgreSQL82Dialect.java +++ /dev/null @@ -1,67 +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.storage.rdbms.hibernate.postgres; - -import java.sql.Types; - -import org.hibernate.dialect.PostgreSQL82Dialect; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.PostgresUUIDType; -import org.hibernate.type.descriptor.sql.LongVarcharTypeDescriptor; -import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; - -/** - * UUID's are not supported by default in hibernate due to differences in the database in order to fix this a custom - * sql dialect is needed. - * Source: https://forum.hibernate.org/viewtopic.php?f=1&t=1014157 - * - * @author kevinvandevelde at atmire.com - */ -public class DSpacePostgreSQL82Dialect extends PostgreSQL82Dialect { - @Override - public void contributeTypes(final org.hibernate.boot.model.TypeContributions typeContributions, - final ServiceRegistry serviceRegistry) { - super.contributeTypes(typeContributions, serviceRegistry); - typeContributions.contributeType(new InternalPostgresUUIDType()); - } - - @Override - protected void registerHibernateType(int code, String name) { - super.registerHibernateType(code, name); - } - - protected static class InternalPostgresUUIDType extends PostgresUUIDType { - - @Override - protected boolean registerUnderJavaType() { - return true; - } - } - - /** - * Override is needed to properly support the CLOB on metadatavalue in Postgres and Oracle. - * - * @param sqlCode {@linkplain java.sql.Types JDBC type-code} for the column mapped by this type. - * @return Descriptor for the SQL/JDBC side of a value mapping. - */ - @Override - public SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) { - SqlTypeDescriptor descriptor; - switch (sqlCode) { - case Types.CLOB: { - descriptor = LongVarcharTypeDescriptor.INSTANCE; - break; - } - default: { - descriptor = super.getSqlTypeDescriptorOverride(sqlCode); - break; - } - } - return descriptor; - } -} diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/migration/MigrationUtils.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/migration/MigrationUtils.java index 624d0cb55a..842fc15e16 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/migration/MigrationUtils.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/migration/MigrationUtils.java @@ -86,10 +86,11 @@ public class MigrationUtils { cascade = true; break; case "h2": - // In H2, constraints are listed in the "information_schema.constraints" table + // In H2, column constraints are listed in the "INFORMATION_SCHEMA.KEY_COLUMN_USAGE" table constraintNameSQL = "SELECT DISTINCT CONSTRAINT_NAME " + - "FROM information_schema.constraints " + - "WHERE table_name = ? AND column_list = ?"; + "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE " + + "WHERE TABLE_NAME = ? AND COLUMN_NAME = ?"; + cascade = true; break; default: throw new SQLException("DBMS " + dbtype + " is unsupported in this migration."); diff --git a/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java b/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java index 362f2720bb..db1fdcdd19 100644 --- a/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java +++ b/dspace-api/src/main/java/org/dspace/submit/migration/SubmissionFormsMigration.java @@ -64,12 +64,6 @@ public class SubmissionFormsMigration extends DSpaceRunnable"; private List tempFiles = new ArrayList<>(); - /** - * We need to force this, because some dependency elsewhere interferes. - */ - private static final String TRANSFORMER_FACTORY_CLASS - = "org.apache.xalan.processor.TransformerFactoryImpl"; - @Override public void internalRun() throws TransformerException { if (help) { @@ -101,8 +95,7 @@ public class SubmissionFormsMigration extends DSpaceRunnable subVocabularies = new ArrayList<>(subNodes.getLength()); for (int i = 0; i < subNodes.getLength(); i++) { diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V1.4__Upgrade_to_DSpace_1.4_schema.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V1.4__Upgrade_to_DSpace_1.4_schema.sql index e00a651626..62d12fe5ce 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V1.4__Upgrade_to_DSpace_1.4_schema.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V1.4__Upgrade_to_DSpace_1.4_schema.sql @@ -245,13 +245,13 @@ insert into most_recent_checksum ) select bitstream.bitstream_id, - '1', + true, CASE WHEN bitstream.checksum IS NULL THEN '' ELSE bitstream.checksum END, CASE WHEN bitstream.checksum IS NULL THEN '' ELSE bitstream.checksum END, FORMATDATETIME(NOW(),'DD-MM-RRRR HH24:MI:SS'), FORMATDATETIME(NOW(),'DD-MM-RRRR HH24:MI:SS'), CASE WHEN bitstream.checksum_algorithm IS NULL THEN 'MD5' ELSE bitstream.checksum_algorithm END, - '1' + true from bitstream; -- Update all the deleted checksums @@ -263,7 +263,7 @@ update most_recent_checksum set to_be_processed = 0 where most_recent_checksum.bitstream_id in ( select bitstream_id -from bitstream where deleted = '1' ); +from bitstream where deleted = true ); -- this will insert into history table -- for the initial start diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.0_2014.09.26__DS-1582_Metadata_For_All_Objects.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.0_2014.09.26__DS-1582_Metadata_For_All_Objects.sql index 87551bdf4e..cd908279f1 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.0_2014.09.26__DS-1582_Metadata_For_All_Objects.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.0_2014.09.26__DS-1582_Metadata_For_All_Objects.sql @@ -36,7 +36,7 @@ alter table metadatavalue alter column resource_id set not null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, community_id AS resource_id, 4 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier is null) AS metadata_field_id, @@ -47,7 +47,7 @@ FROM community where not introductory_text is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, community_id AS resource_id, 4 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier = 'abstract') AS metadata_field_id, @@ -58,7 +58,7 @@ FROM community where not short_description is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, community_id AS resource_id, 4 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier = 'tableofcontents') AS metadata_field_id, @@ -69,7 +69,7 @@ FROM community where not side_bar_text is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, community_id AS resource_id, 4 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'rights' and qualifier is null) AS metadata_field_id, @@ -80,7 +80,7 @@ FROM community where not copyright_text is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, community_id AS resource_id, 4 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'title' and qualifier is null) AS metadata_field_id, @@ -104,7 +104,7 @@ alter table community drop column name; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier is null) AS metadata_field_id, @@ -115,7 +115,7 @@ FROM collection where not introductory_text is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier = 'abstract') AS metadata_field_id, @@ -126,7 +126,7 @@ FROM collection where not short_description is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier = 'tableofcontents') AS metadata_field_id, @@ -137,7 +137,7 @@ FROM collection where not side_bar_text is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'rights' and qualifier is null) AS metadata_field_id, @@ -148,7 +148,7 @@ FROM collection where not copyright_text is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'title' and qualifier is null) AS metadata_field_id, @@ -159,7 +159,7 @@ FROM collection where not name is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'provenance' and qualifier is null) AS metadata_field_id, @@ -170,7 +170,7 @@ FROM collection where not provenance_description is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, collection_id AS resource_id, 3 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'rights' and qualifier = 'license') AS metadata_field_id, @@ -194,7 +194,7 @@ alter table collection drop column provenance_description; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, bundle_id AS resource_id, 1 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'title' and qualifier is null) AS metadata_field_id, @@ -214,7 +214,7 @@ alter table bundle drop column name; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, bitstream_id AS resource_id, 0 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'title' and qualifier is null) AS metadata_field_id, @@ -225,7 +225,7 @@ FROM bitstream where not name is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, bitstream_id AS resource_id, 0 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'description' and qualifier is null) AS metadata_field_id, @@ -236,7 +236,7 @@ FROM bitstream where not description is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, bitstream_id AS resource_id, 0 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'format' and qualifier is null) AS metadata_field_id, @@ -247,7 +247,7 @@ FROM bitstream where not user_format_description is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, bitstream_id AS resource_id, 0 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'source' and qualifier is null) AS metadata_field_id, @@ -269,7 +269,7 @@ alter table bitstream drop column source; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, eperson_group_id AS resource_id, 6 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='dc') and element = 'title' and qualifier is null) AS metadata_field_id, @@ -288,7 +288,7 @@ alter table epersongroup drop column name; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, eperson_id AS resource_id, 7 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='eperson') and element = 'firstname' and qualifier is null) AS metadata_field_id, @@ -299,7 +299,7 @@ FROM eperson where not firstname is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, eperson_id AS resource_id, 7 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='eperson') and element = 'lastname' and qualifier is null) AS metadata_field_id, @@ -310,7 +310,7 @@ FROM eperson where not lastname is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, eperson_id AS resource_id, 7 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='eperson') and element = 'phone' and qualifier is null) AS metadata_field_id, @@ -321,7 +321,7 @@ FROM eperson where not phone is null; INSERT INTO metadatavalue (metadata_value_id, resource_id, resource_type_id, metadata_field_id, text_value, text_lang, place) SELECT -metadatavalue_seq.nextval as metadata_value_id, +NEXT VALUE FOR metadatavalue_seq as metadata_value_id, eperson_id AS resource_id, 7 AS resource_type_id, (select metadata_field_id from metadatafieldregistry where metadata_schema_id=(select metadata_schema_id from metadataschemaregistry where short_id='eperson') and element = 'language' and qualifier is null) AS metadata_field_id, diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.6_2016.08.23__DS-3097.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.6_2016.08.23__DS-3097.sql index 2e09b807de..0bd68c5201 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.6_2016.08.23__DS-3097.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V5.6_2016.08.23__DS-3097.sql @@ -14,11 +14,11 @@ UPDATE resourcepolicy SET action_id = 12 where action_id = 0 and resource_type_i SELECT bundle2bitstream.bitstream_id FROM bundle2bitstream LEFT JOIN item2bundle ON bundle2bitstream.bundle_id = item2bundle.bundle_id LEFT JOIN item ON item2bundle.item_id = item.item_id - WHERE item.withdrawn = 1 + WHERE item.withdrawn = true ); UPDATE resourcepolicy SET action_id = 12 where action_id = 0 and resource_type_id = 1 and resource_id in ( SELECT item2bundle.bundle_id FROM item2bundle LEFT JOIN item ON item2bundle.item_id = item.item_id - WHERE item.withdrawn = 1 + WHERE item.withdrawn = true ); \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.04.14__DS-3125-fix-bundle-bitstream-delete-rights.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.04.14__DS-3125-fix-bundle-bitstream-delete-rights.sql index 1c98ceef2a..1ee23246ea 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.04.14__DS-3125-fix-bundle-bitstream-delete-rights.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.04.14__DS-3125-fix-bundle-bitstream-delete-rights.sql @@ -17,7 +17,7 @@ INSERT INTO resourcepolicy (policy_id, resource_type_id, resource_id, action_id, start_date, end_date, rpname, rptype, rpdescription, eperson_id, epersongroup_id, dspace_object) SELECT -resourcepolicy_seq.nextval AS policy_id, +NEXT VALUE FOR resourcepolicy_seq AS policy_id, resource_type_id, resource_id, -- Insert the Constants.DELETE action diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.08.23__DS-3097.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.08.23__DS-3097.sql index e1220c8c7c..5bb59970c5 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.08.23__DS-3097.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V6.0_2016.08.23__DS-3097.sql @@ -14,11 +14,11 @@ UPDATE resourcepolicy SET action_id = 12 where action_id = 0 and dspace_object i SELECT bundle2bitstream.bitstream_id FROM bundle2bitstream LEFT JOIN item2bundle ON bundle2bitstream.bundle_id = item2bundle.bundle_id LEFT JOIN item ON item2bundle.item_id = item.uuid - WHERE item.withdrawn = 1 + WHERE item.withdrawn = true ); UPDATE resourcepolicy SET action_id = 12 where action_id = 0 and dspace_object in ( SELECT item2bundle.bundle_id FROM item2bundle LEFT JOIN item ON item2bundle.item_id = item.uuid - WHERE item.withdrawn = 1 + WHERE item.withdrawn = true ); \ No newline at end of file diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.1_2021.10.18__Fix_MDV_place_after_migrating_from_DSpace_5.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.1_2021.10.18__Fix_MDV_place_after_migrating_from_DSpace_5.sql index 3b649a321c..7506433cdd 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.1_2021.10.18__Fix_MDV_place_after_migrating_from_DSpace_5.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.1_2021.10.18__Fix_MDV_place_after_migrating_from_DSpace_5.sql @@ -9,10 +9,11 @@ ---------------------------------------------------- -- Make sure the metadatavalue.place column starts at 0 instead of 1 ---------------------------------------------------- + CREATE LOCAL TEMPORARY TABLE mdv_minplace ( dspace_object_id UUID NOT NULL, metadata_field_id INT NOT NULL, - minplace INT NOT NULL, + minplace INT NOT NULL ); INSERT INTO mdv_minplace diff --git a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java index 402947b966..e27fb19a68 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java +++ b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java @@ -15,6 +15,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; +import org.dspace.authority.AuthoritySearchService; import org.dspace.authority.MockAuthoritySolrServiceImpl; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.AbstractBuilder; @@ -31,8 +32,9 @@ import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.statistics.MockSolrLoggerServiceImpl; import org.dspace.statistics.MockSolrStatisticsCore; +import org.dspace.statistics.SolrStatisticsCore; import org.dspace.storage.rdbms.DatabaseUtils; -import org.jdom.Document; +import org.jdom2.Document; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -183,15 +185,15 @@ public class AbstractIntegrationTestWithDatabase extends AbstractDSpaceIntegrati searchService.reset(); // Clear the statistics core. serviceManager - .getServiceByName(null, MockSolrStatisticsCore.class) + .getServiceByName(SolrStatisticsCore.class.getName(), MockSolrStatisticsCore.class) .reset(); MockSolrLoggerServiceImpl statisticsService = serviceManager - .getServiceByName(null, MockSolrLoggerServiceImpl.class); + .getServiceByName("solrLoggerService", MockSolrLoggerServiceImpl.class); statisticsService.reset(); MockAuthoritySolrServiceImpl authorityService = serviceManager - .getServiceByName(null, MockAuthoritySolrServiceImpl.class); + .getServiceByName(AuthoritySearchService.class.getName(), MockAuthoritySolrServiceImpl.class); authorityService.reset(); // Reload our ConfigurationService (to reset configs to defaults again) diff --git a/dspace-api/src/test/java/org/dspace/app/mediafilter/PoiWordFilterTest.java b/dspace-api/src/test/java/org/dspace/app/mediafilter/PoiWordFilterTest.java deleted file mode 100644 index 4d2353a29a..0000000000 --- a/dspace-api/src/test/java/org/dspace/app/mediafilter/PoiWordFilterTest.java +++ /dev/null @@ -1,181 +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.mediafilter; - -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import org.dspace.content.Item; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * Drive the POI-based MS Word filter. - * - * @author mwood - */ -public class PoiWordFilterTest { - - public PoiWordFilterTest() { - } - - @BeforeClass - public static void setUpClass() { - } - - @AfterClass - public static void tearDownClass() { - } - - @Before - public void setUp() { - } - - @After - public void tearDown() { - } - - /** - * Test of getFilteredName method, of class PoiWordFilter. - */ -/* - @Test - public void testGetFilteredName() - { - System.out.println("getFilteredName"); - String oldFilename = ""; - PoiWordFilter instance = new PoiWordFilter(); - String expResult = ""; - String result = instance.getFilteredName(oldFilename); - assertEquals(expResult, result); - // TODO review the generated test code and remove the default call to fail. - fail("The test case is a prototype."); - } -*/ - - /** - * Test of getBundleName method, of class PoiWordFilter. - */ -/* - @Test - public void testGetBundleName() - { - System.out.println("getBundleName"); - PoiWordFilter instance = new PoiWordFilter(); - String expResult = ""; - String result = instance.getBundleName(); - assertEquals(expResult, result); - // TODO review the generated test code and remove the default call to fail. - fail("The test case is a prototype."); - } -*/ - - /** - * Test of getFormatString method, of class PoiWordFilter. - */ -/* - @Test - public void testGetFormatString() - { - System.out.println("getFormatString"); - PoiWordFilter instance = new PoiWordFilter(); - String expResult = ""; - String result = instance.getFormatString(); - assertEquals(expResult, result); - // TODO review the generated test code and remove the default call to fail. - fail("The test case is a prototype."); - } -*/ - - /** - * Test of getDescription method, of class PoiWordFilter. - */ -/* - @Test - public void testGetDescription() - { - System.out.println("getDescription"); - PoiWordFilter instance = new PoiWordFilter(); - String expResult = ""; - String result = instance.getDescription(); - assertEquals(expResult, result); - // TODO review the generated test code and remove the default call to fail. - fail("The test case is a prototype."); - } -*/ - - /** - * Test of getDestinationStream method, of class PoiWordFilter. - * Read a constant .doc document and examine the extracted text. - * - * @throws java.lang.Exception passed through. - */ - @Test - public void testGetDestinationStreamDoc() - throws Exception { - System.out.println("getDestinationStream"); - Item currentItem = null; - InputStream source; - boolean verbose = false; - PoiWordFilter instance = new PoiWordFilter(); - InputStream result; - - source = getClass().getResourceAsStream("wordtest.doc"); - result = instance.getDestinationStream(currentItem, source, verbose); - assertTrue("Known content was not found", readAll(result).contains("quick brown fox")); - } - - /** - * Test of getDestinationStream method, of class PoiWordFilter. - * Read a constant .docx document and examine the extracted text. - * - * @throws java.lang.Exception passed through. - */ - @Test - public void testGetDestinationStreamDocx() - throws Exception { - System.out.println("getDestinationStream"); - Item currentItem = null; - InputStream source; - boolean verbose = false; - PoiWordFilter instance = new PoiWordFilter(); - InputStream result; - - source = getClass().getResourceAsStream("wordtest.docx"); - result = instance.getDestinationStream(currentItem, source, verbose); - assertTrue("Known content was not found", readAll(result).contains("quick brown fox")); - } - - /** - * Read the entire content of a stream into a String. - * - * @param stream a stream of UTF-8 characters. - * @return complete content of {@link stream} - * @throws IOException - */ - private static String readAll(InputStream stream) - throws IOException { - if (null == stream) { - return null; - } - - byte[] bytes = new byte[stream.available()]; - StringBuilder resultSb = new StringBuilder(bytes.length / 2); // Guess: average 2 bytes per character - int howmany; - while ((howmany = stream.read(bytes)) > 0) { - resultSb.append(new String(bytes, 0, howmany, StandardCharsets.UTF_8)); - } - return resultSb.toString(); - } -} diff --git a/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java b/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java new file mode 100644 index 0000000000..9db1ef7776 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java @@ -0,0 +1,323 @@ +/** + * 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.mediafilter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.dspace.AbstractUnitTest; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.Test; + +/** + * Test the TikaTextExtractionFilter using test files for all major formats. + * The test files used below are all located at [dspace-api]/src/test/resources/org/dspace/app/mediafilter/ + * + * @author mwood + * @author Tim Donohue + */ +public class TikaTextExtractionFilterTest extends AbstractUnitTest { + + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + /** + * Test of getDestinationStream method using temp file for text extraction + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithUseTempFile() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + // Extract text from file with "use-temp-file=true" + configurationService.setProperty("textextractor.use-temp-file", "true"); + InputStream source = getClass().getResourceAsStream("test.pdf"); + InputStream result = instance.getDestinationStream(null, source, false); + String tempFileExtractedText = readAll(result); + + // Verify text extracted successfully + assertTrue("Known content was not found in .pdf", tempFileExtractedText.contains("quick brown fox")); + + // Now, extract text from same file using default, in-memory + configurationService.setProperty("textextractor.use-temp-file", "false"); + source = getClass().getResourceAsStream("test.pdf"); + result = instance.getDestinationStream(null, source, false); + String inMemoryExtractedText = readAll(result); + + // Verify the two results are equal + assertEquals("Extracted text via temp file is the same as in-memory.", + inMemoryExtractedText, tempFileExtractedText); + } + + /** + * Test of getDestinationStream method when max characters is less than file size + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithMaxChars() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + // Set "max-chars" to a small value of 100 chars, which is less than the text size of the file. + configurationService.setProperty("textextractor.max-chars", "100"); + InputStream source = getClass().getResourceAsStream("test.pdf"); + InputStream result = instance.getDestinationStream(null, source, false); + String extractedText = readAll(result); + + // Verify we have exactly the first 100 characters + assertEquals(100, extractedText.length()); + // Verify it has some text at the beginning of the file, but NOT text near the end + assertTrue("Known beginning content was found", extractedText.contains("This is a text.")); + assertFalse("Known ending content was not found", extractedText.contains("Emergency Broadcast System")); + } + + /** + * Test of getDestinationStream method using older Microsoft Word document. + * Read a constant .doc document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithDoc() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.doc"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .doc", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using newer Microsoft Word document. + * Read a constant .docx document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithDocx() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.docx"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .docx", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using an ODT document + * Read a constant .odt document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithODT() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.odt"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .odt", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using an RTF document + * Read a constant .rtf document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithRTF() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.rtf"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .rtf", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using a PDF document + * Read a constant .pdf document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithPDF() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.pdf"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .pdf", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using an HTML document + * Read a constant .html document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithHTML() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.html"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .html", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using a TXT document + * Read a constant .txt document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithTxt() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.txt"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .txt", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using a CSV document + * Read a constant .csv document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithCsv() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.csv"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .csv", readAll(result).contains("data3,3")); + } + + /** + * Test of getDestinationStream method using an XLS document + * Read a constant .xls document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithXLS() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.xls"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .xls", readAll(result).contains("data3,3")); + } + + /** + * Test of getDestinationStream method using an XLSX document + * Read a constant .xlsx document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithXLSX() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.xlsx"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .xlsx", readAll(result).contains("data3,3")); + } + + /** + * Test of getDestinationStream method using an ODS document + * Read a constant .ods document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithODS() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.ods"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .ods", readAll(result).contains("Data on the second sheet")); + } + + /** + * Test of getDestinationStream method using an PPT document + * Read a constant .ppt document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithPPT() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.ppt"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .ppt", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using an PPTX document + * Read a constant .pptx document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithPPTX() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.pptx"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .pptx", readAll(result).contains("quick brown fox")); + } + + /** + * Test of getDestinationStream method using an ODP document + * Read a constant .odp document and examine the extracted text. + * + * @throws java.lang.Exception passed through. + */ + @Test + public void testGetDestinationStreamWithODP() + throws Exception { + TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); + + InputStream source = getClass().getResourceAsStream("test.odp"); + InputStream result = instance.getDestinationStream(null, source, false); + assertTrue("Known content was not found in .odp", readAll(result).contains("quick brown fox")); + } + + /** + * Read the entire content of a stream into a String. + * + * @param stream a stream of UTF-8 characters. + * @return complete content of stream as a String + * @throws IOException + */ + private static String readAll(InputStream stream) + throws IOException { + return IOUtils.toString(stream, StandardCharsets.UTF_8); + } +} diff --git a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java index c814d2d9f6..7d808ab871 100644 --- a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java @@ -38,7 +38,7 @@ import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Element; +import org.jdom2.Element; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java b/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java new file mode 100644 index 0000000000..388b467e97 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java @@ -0,0 +1,268 @@ +/** + * 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.util; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.collection.IsArrayContainingInAnyOrder.arrayContainingInAnyOrder; +import static org.junit.Assert.assertEquals; + +import org.dspace.AbstractDSpaceTest; +import org.dspace.services.ConfigurationService; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.Assertion; +import org.junit.contrib.java.lang.system.ExpectedSystemExit; +import org.junit.contrib.java.lang.system.SystemErrRule; +import org.junit.contrib.java.lang.system.SystemOutRule; + +/** + * Tests for configuration utilities. + * + * Because our command-line tools call System.exit(), we can't expect any code + * (such as assertions) following the call to main() to be executed. Instead we + * set up expectations in advance and attach them to an exit() trapper. + * + * @author mhwood + */ +public class ConfigurationIT + extends AbstractDSpaceTest { + + private static ConfigurationService cfg; + + private static final String SINGLE_PROPERTY = "test.single"; + private static final String SINGLE_VALUE = "value"; + + private static final String ARRAY_PROPERTY = "test.array"; + private static final String[] ARRAY_VALUE = { "one", "two" }; + + private static final String PLACEHOLDER_PROPERTY = "test.substituted"; + private static final String PLACEHOLDER_VALUE = "insert ${test.single} here"; // Keep aligned with SINGLE_NAME + private static final String SUBSTITUTED_VALUE = "insert value here"; // Keep aligned with SINGLE_VALUE + + private static final String MISSING_PROPERTY = "test.missing"; + + /** Capture standard output. */ + @Rule + public final SystemOutRule systemOutRule = new SystemOutRule(); + + /** Capture standard error. */ + @Rule + public final SystemErrRule systemErrRule = new SystemErrRule(); + + /** Capture System.exit() value. */ + @Rule + public final ExpectedSystemExit expectedSystemExit = ExpectedSystemExit.none(); + + /** + * Create some expected properties before all tests. + */ + @BeforeClass + public static void setupSuite() { + cfg = kernelImpl.getConfigurationService(); + + cfg.setProperty(SINGLE_PROPERTY, SINGLE_VALUE); + cfg.setProperty(ARRAY_PROPERTY, ARRAY_VALUE); + cfg.setProperty(PLACEHOLDER_PROPERTY, PLACEHOLDER_VALUE); + cfg.setProperty(MISSING_PROPERTY, null); // Ensure that this one is undefined + } + + /** + * After all tests, remove the properties that were created at entry. + */ + @AfterClass + public static void teardownSuite() { + if (null != cfg) { + cfg.setProperty(SINGLE_PROPERTY, null); + cfg.setProperty(ARRAY_PROPERTY, null); + cfg.setProperty(PLACEHOLDER_PROPERTY, null); + } + } + + /** + * Test fetching all values of a single-valued property. + */ + @Test + public void testMainAllSingle() { + String[] argv; + argv = new String[] { + "--property", SINGLE_PROPERTY + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output, arrayWithSize(1)); + } + }); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output[0], equalTo(SINGLE_VALUE)); + } + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } + + /** + * Test fetching all values of an array property. + */ + @Test + public void testMainAllArray() { + String[] argv; + argv = new String[] { + "--property", ARRAY_PROPERTY + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output, arrayWithSize(ARRAY_VALUE.length)); + } + }); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output, arrayContainingInAnyOrder(ARRAY_VALUE)); + } + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } + + /** + * Test fetching all values of a single-valued property containing property + * placeholders. + */ + @Test + public void testMainAllSubstitution() { + String[] argv; + argv = new String[] { + "--property", PLACEHOLDER_PROPERTY + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output, arrayWithSize(1)); + } + }); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output[0], equalTo(SUBSTITUTED_VALUE)); + } + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } + + /** + * Test fetching all values of a single-valued property containing property + * placeholders, suppressing property substitution. + */ + @Test + public void testMainAllRaw() { + // Can it handle a raw property (with substitution placeholders)? + String[] argv; + argv = new String[] { + "--property", PLACEHOLDER_PROPERTY, + "--raw" + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output, arrayWithSize(1)); + } + }); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String[] output = systemOutRule.getLogWithNormalizedLineSeparator() + .split("\n"); + assertThat(output[0], equalTo(PLACEHOLDER_VALUE)); + } + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } + + /** + * Test fetching all values of an undefined property. + */ + @Test + public void testMainAllUndefined() { + // Can it handle an undefined property? + String[] argv; + argv = new String[] { + "--property", MISSING_PROPERTY + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(new Assertion() { + @Override public void checkAssertion() { + String outputs = systemOutRule.getLogWithNormalizedLineSeparator(); + String[] output = outputs.split("\n"); + assertThat(output, arrayWithSize(0)); // Huh? Shouldn't split() return { "" } ? + } + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } + + /** + * Test fetching only the first value of an array property. + */ + @Test + public void testMainFirstArray() { + String[] argv = new String[] { + "--property", ARRAY_PROPERTY, + "--first" + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(() -> { + String outputs = systemOutRule.getLogWithNormalizedLineSeparator(); + String[] output = outputs.split("\n"); + assertThat(output, arrayWithSize(1)); + assertEquals("--first should return first value", output[0], ARRAY_VALUE[0]); + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } + + /** + * Test fetching a single-valued property using {@code --first} + */ + @Test + public void testMainFirstSingle() { + String[] argv = new String[] { + "--property", SINGLE_PROPERTY, + "--first" + }; + expectedSystemExit.expectSystemExitWithStatus(0); + expectedSystemExit.checkAssertionAfterwards(() -> { + String outputs = systemOutRule.getLogWithNormalizedLineSeparator(); + String[] output = outputs.split("\n"); + assertThat(output, arrayWithSize(1)); + assertEquals("--first should return only value", output[0], SINGLE_VALUE); + }); + systemOutRule.enableLog(); + Configuration.main(argv); + } +} diff --git a/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java b/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java index 84e776b983..78142c9258 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/GoogleBitstreamComparatorTest.java @@ -164,6 +164,12 @@ public class GoogleBitstreamComparatorTest extends AbstractUnitTest { toSort.get(1).getName()); assertEquals("Bitstreams have same size and type, so order should remain unchanged", "bitstream3", toSort.get(2).getName()); + + // Also, verify all bitstreams are considered equal (comparison returns 0) + GoogleBitstreamComparator comparator = new GoogleBitstreamComparator(context, settings); + assertEquals(0, comparator.compare(bitstream1, bitstream2)); + assertEquals(0, comparator.compare(bitstream2, bitstream3)); + assertEquals(0, comparator.compare(bitstream3, bitstream1)); } /** diff --git a/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java b/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java index e2b49ab76a..ee6723480e 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java +++ b/dspace-api/src/test/java/org/dspace/app/util/GoogleMetadataTest.java @@ -8,18 +8,25 @@ package org.dspace.app.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.util.Date; import java.util.List; +import java.util.Map; import com.google.common.base.Splitter; import org.apache.logging.log4j.Logger; import org.dspace.AbstractUnitTest; import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.Collection; @@ -30,6 +37,14 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BundleService; +import org.dspace.core.Constants; +import org.dspace.eperson.Group; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.GroupService; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.MutablePeriod; +import org.joda.time.format.PeriodFormat; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -52,6 +67,10 @@ public class GoogleMetadataTest extends AbstractUnitTest { private BitstreamService bitstreamService; + private ResourcePolicyService resourcePolicyService; + + private GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); + private Community community; /** @@ -80,6 +99,8 @@ public class GoogleMetadataTest extends AbstractUnitTest { bundleService = ContentServiceFactory.getInstance().getBundleService(); bitstreamFormatService = ContentServiceFactory.getInstance().getBitstreamFormatService(); bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + resourcePolicyService = AuthorizeServiceFactory.getInstance().getResourcePolicyService(); + groupService = EPersonServiceFactory.getInstance().getGroupService(); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); fail("Authorization Error in init: " + ex.getMessage()); @@ -326,6 +347,45 @@ public class GoogleMetadataTest extends AbstractUnitTest { assertEquals("small", urlSplitted.get(urlSplitted.size() - 1)); } + /** + * Verify there is no mapping for {@link GoogleMetadata#PDF} if there are only embargoed (non-publically accessible + * bitstream) files + */ + @Test + public void testGetPdfUrlOfEmbargoed() throws Exception { + context.turnOffAuthorisationSystem(); + Bundle bundle = ContentServiceFactory.getInstance().getBundleService().create(context, it, "ORIGINAL"); + + Bitstream b = bitstreamService.create( + context, new ByteArrayInputStream("Larger file than primary".getBytes(StandardCharsets.UTF_8))); + b.setName(context, "first"); + b.setFormat(context, bitstreamFormatService.create(context)); + b.getFormat(context).setMIMEType("unknown"); + bundleService.addBitstream(context, bundle, b); + // Set 3 month embargo on pdf + MutablePeriod period = PeriodFormat.getDefault().parseMutablePeriod("3 months"); + Date embargoDate = DateTime.now(DateTimeZone.UTC).plus(period).toDate(); + Group anonGroup = groupService.findByName(context, Group.ANONYMOUS); + authorizeService.removeAllPolicies(context, b); + resourcePolicyService.removeAllPolicies(context, b); + ResourcePolicy rp = authorizeService.createOrModifyPolicy(null, context, null, anonGroup, + null, embargoDate, Constants.READ, "GoogleMetadataTest", b); + if (rp != null) { + resourcePolicyService.update(context, rp); + } + + GoogleMetadata gm = new GoogleMetadata(this.context, it); + assertTrue(gm.getPDFURL().isEmpty()); + // No value for citation_pdf_url because only one embargoed bitstream + boolean containsPdfUrl = false; + for (Map.Entry mapping: gm.getMappings()) { + if (mapping.getKey().equalsIgnoreCase(gm.PDF)) { + containsPdfUrl = true; + } + } + assertFalse(containsPdfUrl); + } + @After @Override public void destroy() { diff --git a/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java b/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java index f98befe57f..424833e5cc 100644 --- a/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/BitstreamBuilder.java @@ -18,6 +18,7 @@ import org.dspace.content.BitstreamFormat; import org.dspace.content.Bundle; import org.dspace.content.Item; import org.dspace.content.service.DSpaceObjectService; +import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.Group; @@ -26,8 +27,6 @@ import org.dspace.eperson.Group; */ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder { - public static final String ORIGINAL = "ORIGINAL"; - private Bitstream bitstream; private Item item; private Group readerGroup; @@ -158,12 +157,12 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder { } private Bundle getOriginalBundle(Item item) throws SQLException, AuthorizeException { - List bundles = itemService.getBundles(item, ORIGINAL); + List bundles = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME); Bundle targetBundle = null; if (bundles.size() < 1) { // not found, create a new one - targetBundle = bundleService.create(context, item, ORIGINAL); + targetBundle = bundleService.create(context, item, Constants.CONTENT_BUNDLE_NAME); } else { // put bitstreams into first bundle targetBundle = bundles.iterator().next(); @@ -206,6 +205,7 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup bitstream = c.reloadEntity(bitstream); diff --git a/dspace-api/src/test/java/org/dspace/builder/BitstreamFormatBuilder.java b/dspace-api/src/test/java/org/dspace/builder/BitstreamFormatBuilder.java index 1051712326..a13783ceef 100644 --- a/dspace-api/src/test/java/org/dspace/builder/BitstreamFormatBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/BitstreamFormatBuilder.java @@ -34,6 +34,7 @@ public class BitstreamFormatBuilder extends AbstractCRUDBuilder @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup bitstreamFormat = c.reloadEntity(bitstreamFormat); diff --git a/dspace-api/src/test/java/org/dspace/builder/BundleBuilder.java b/dspace-api/src/test/java/org/dspace/builder/BundleBuilder.java index 614cd54c6d..1776921ac6 100644 --- a/dspace-api/src/test/java/org/dspace/builder/BundleBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/BundleBuilder.java @@ -55,6 +55,7 @@ public class BundleBuilder extends AbstractDSpaceObjectBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup bundle = c.reloadEntity(bundle); diff --git a/dspace-api/src/test/java/org/dspace/builder/ClaimedTaskBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ClaimedTaskBuilder.java index 63c03c4a91..aed712f2d2 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ClaimedTaskBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ClaimedTaskBuilder.java @@ -124,6 +124,7 @@ public class ClaimedTaskBuilder extends AbstractBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup collection = c.reloadEntity(collection); diff --git a/dspace-api/src/test/java/org/dspace/builder/CommunityBuilder.java b/dspace-api/src/test/java/org/dspace/builder/CommunityBuilder.java index 5ba36af8f4..a01aef8498 100644 --- a/dspace-api/src/test/java/org/dspace/builder/CommunityBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/CommunityBuilder.java @@ -116,6 +116,7 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup community = c.reloadEntity(community); diff --git a/dspace-api/src/test/java/org/dspace/builder/EPersonBuilder.java b/dspace-api/src/test/java/org/dspace/builder/EPersonBuilder.java index 2010aef2c1..c6c1efd461 100644 --- a/dspace-api/src/test/java/org/dspace/builder/EPersonBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/EPersonBuilder.java @@ -32,6 +32,7 @@ public class EPersonBuilder extends AbstractDSpaceObjectBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup ePerson = c.reloadEntity(ePerson); diff --git a/dspace-api/src/test/java/org/dspace/builder/EntityTypeBuilder.java b/dspace-api/src/test/java/org/dspace/builder/EntityTypeBuilder.java index ef3c840bc2..36d9654adf 100644 --- a/dspace-api/src/test/java/org/dspace/builder/EntityTypeBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/EntityTypeBuilder.java @@ -36,6 +36,7 @@ public class EntityTypeBuilder extends AbstractBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup group = c.reloadEntity(group); diff --git a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java index 365ca57889..2c9594c593 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java @@ -7,6 +7,8 @@ */ package org.dspace.builder; +import static org.dspace.content.LicenseUtils.getLicenseText; + import java.io.IOException; import java.sql.SQLException; import java.util.UUID; @@ -15,6 +17,7 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.DCDate; import org.dspace.content.Item; +import org.dspace.content.LicenseUtils; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.WorkspaceItem; import org.dspace.content.service.DSpaceObjectService; @@ -213,6 +216,7 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup item = c.reloadEntity(item); @@ -249,4 +253,16 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder { } } + public ItemBuilder grantLicense() { + String license; + try { + EPerson submitter = workspaceItem.getSubmitter(); + submitter = context.reloadEntity(submitter); + license = getLicenseText(context.getCurrentLocale(), workspaceItem.getCollection(), item, submitter); + LicenseUtils.grantLicense(context, item, license, null); + } catch (Exception e) { + handleException(e); + } + return this; + } } diff --git a/dspace-api/src/test/java/org/dspace/builder/MetadataFieldBuilder.java b/dspace-api/src/test/java/org/dspace/builder/MetadataFieldBuilder.java index dfc9112a3f..52acf9d5ed 100644 --- a/dspace-api/src/test/java/org/dspace/builder/MetadataFieldBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/MetadataFieldBuilder.java @@ -38,6 +38,7 @@ public class MetadataFieldBuilder extends AbstractBuilder @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup workspaceItem = c.reloadEntity(workspaceItem); diff --git a/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java index 6970cd57c3..981ce63493 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ProcessBuilder.java @@ -60,6 +60,7 @@ public class ProcessBuilder extends AbstractBuilder { @Override public void cleanup() throws Exception { try (Context c = new Context()) { + c.setDispatcher("noindex"); c.turnOffAuthorisationSystem(); // Ensure object and any related objects are reloaded before checking to see what needs cleanup process = c.reloadEntity(process); diff --git a/dspace-api/src/test/java/org/dspace/builder/RelationshipBuilder.java b/dspace-api/src/test/java/org/dspace/builder/RelationshipBuilder.java index 454315c1a7..c8c5cf85bf 100644 --- a/dspace-api/src/test/java/org/dspace/builder/RelationshipBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/RelationshipBuilder.java @@ -39,6 +39,7 @@ public class RelationshipBuilder extends AbstractBuilder { - private static final Logger log = Logger.getLogger(VersionBuilder.class); + private static final Logger log = LogManager.getLogger(VersionBuilder.class); private Version version; @@ -85,6 +86,7 @@ public class VersionBuilder extends AbstractBuilder public void delete(Version version) throws Exception { try (Context context = new Context()) { context.turnOffAuthorisationSystem(); + context.setDispatcher("noindex"); Version attachedTab = context.reloadEntity(version); if (attachedTab != null) { getService().delete(context, attachedTab); diff --git a/dspace-api/src/test/java/org/dspace/builder/WorkflowItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/WorkflowItemBuilder.java index b4a3b930fb..e06819d0ca 100644 --- a/dspace-api/src/test/java/org/dspace/builder/WorkflowItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/WorkflowItemBuilder.java @@ -116,6 +116,7 @@ public class WorkflowItemBuilder extends AbstractBuilder 1) { + it.removeCollection(it.getCollections().get(0)); + } + + Collection collection = it.getCollections().get(0); + it.setOwningCollection(collection); + ItemService itemServiceSpy = spy(itemService); + + itemService.move(context, it, collection, collection); + context.restoreAuthSystemState(); + assertThat("testMoveSameCollection 0", it.getOwningCollection(), notNullValue()); + assertThat("testMoveSameCollection 1", it.getOwningCollection(), equalTo(collection)); + verify(itemServiceSpy, times(0)).delete(context, it); + } + /** * Test of hasUploadedFiles method, of class Item. */ diff --git a/dspace-api/src/test/java/org/dspace/content/crosswalk/QDCCrosswalkTest.java b/dspace-api/src/test/java/org/dspace/content/crosswalk/QDCCrosswalkTest.java index efed8ad8dc..2eafc03986 100644 --- a/dspace-api/src/test/java/org/dspace/content/crosswalk/QDCCrosswalkTest.java +++ b/dspace-api/src/test/java/org/dspace/content/crosswalk/QDCCrosswalkTest.java @@ -14,7 +14,7 @@ import org.dspace.AbstractDSpaceTest; import org.dspace.core.service.PluginService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.jdom.Namespace; +import org.jdom2.Namespace; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; diff --git a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java index 52cda18df5..b9dbbba647 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java @@ -191,7 +191,7 @@ public class DOIIdentifierProviderTest List remainder = new ArrayList<>(); for (MetadataValue id : metadata) { - if (!id.getValue().startsWith(DOI.RESOLVER)) { + if (!id.getValue().startsWith(doiService.getResolver())) { remainder.add(id.getValue()); } } @@ -278,11 +278,11 @@ public class DOIIdentifierProviderTest PREFIX + "/" + NAMESPACE_SEPARATOR + "lkjljasd1234", DOI.SCHEME + "10.5072/123abc-lkj/kljl", "http://dx.doi.org/10.5072/123abc-lkj/kljl", - DOI.RESOLVER + "/10.5072/123abc-lkj/kljl" + doiService.getResolver() + "/10.5072/123abc-lkj/kljl" }; for (String doi : validDOIs) { - assertTrue("DOI should be supported", provider.supports(doi)); + assertTrue("DOI " + doi + " should be supported", provider.supports(doi)); } } diff --git a/dspace-api/src/test/java/org/dspace/iiif/canvasdimension/CanvasDimensionsIT.java b/dspace-api/src/test/java/org/dspace/iiif/canvasdimension/CanvasDimensionsIT.java index 502266da06..2711f46c31 100644 --- a/dspace-api/src/test/java/org/dspace/iiif/canvasdimension/CanvasDimensionsIT.java +++ b/dspace-api/src/test/java/org/dspace/iiif/canvasdimension/CanvasDimensionsIT.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import org.apache.commons.lang.StringUtils; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.BitstreamBuilder; import org.dspace.builder.CollectionBuilder; @@ -408,7 +409,7 @@ public class CanvasDimensionsIT extends AbstractIntegrationTestWithDatabase { execCanvasScriptWithMaxRecs(id); // check System.out for number of items processed. - assertEquals("2 IIIF items were processed.\n", outContent.toString()); + assertEquals("2 IIIF items were processed.", StringUtils.chomp(outContent.toString())); } @Test diff --git a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index 8545c4187d..30a5a3a9b5 100644 --- a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -15,8 +15,8 @@ import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; -import org.jdom.Document; -import org.jdom.JDOMException; +import org.jdom2.Document; +import org.jdom2.JDOMException; /** * Mock implementation for the Creative commons license connector service. diff --git a/dspace-api/src/test/java/org/dspace/scripts/MockDSpaceRunnableScriptConfiguration.java b/dspace-api/src/test/java/org/dspace/scripts/MockDSpaceRunnableScriptConfiguration.java index 1197370e32..f69c0e3af7 100644 --- a/dspace-api/src/test/java/org/dspace/scripts/MockDSpaceRunnableScriptConfiguration.java +++ b/dspace-api/src/test/java/org/dspace/scripts/MockDSpaceRunnableScriptConfiguration.java @@ -54,9 +54,7 @@ public class MockDSpaceRunnableScriptConfiguration + + + +A Text Extraction Test Document for DSpace + + + + +
    + +

    A Text Extraction Test Document

    + +

    for

    + +

    DSpace

    + +

    + +

    This is a text. For the next sixty seconds this software +will conduct a test of the DSpace text extraction facility. This is only a +text.

    + +

    This is a paragraph that followed the first that lived in +the document that Jack built.

    + +

    Lorem ipsum dolor sit amet. The quick brown fox jumped over +the lazy dog. Yow! Are we having fun yet?

    + +

    This has been a test of the DSpace text extraction system. +In the event of actual content you would care what is written here.

    + +
    + +
    + +
    + +
    + +

    Tip o’ the hat to the U.S. Emergency Broadcast System for the format that I have +irreverently borrowed.

    + +
    + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odp b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odp new file mode 100644 index 0000000000..4701884a8a Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odp differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ods b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ods new file mode 100644 index 0000000000..94ad873c1a Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ods differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odt b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odt new file mode 100644 index 0000000000..3c996a1f46 Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odt differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pdf b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pdf new file mode 100644 index 0000000000..5b3749cbff Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pdf differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ppt b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ppt new file mode 100644 index 0000000000..bb3a3d6b41 Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ppt differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pptx b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pptx new file mode 100644 index 0000000000..2c27ad1630 Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pptx differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.rtf b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.rtf new file mode 100644 index 0000000000..3b841917b2 --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.rtf @@ -0,0 +1,239 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff46\deff0\stshfdbch45\stshfloch43\stshfhich43\stshfbi46\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f43\fbidi \froman\fcharset0\fprq2 Liberation Serif{\*\falt Times New Roman};} +{\f44\fbidi \fswiss\fcharset0\fprq2 Liberation Sans{\*\falt Arial};}{\f45\fbidi \froman\fcharset0\fprq0{\*\panose 00000000000000000000}AR PL SungtiL GB;}{\f46\fbidi \froman\fcharset0\fprq0{\*\panose 00000000000000000000}Lohit Hindi;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1504\fbidi \froman\fcharset238\fprq2 Cambria Math CE;} +{\f1505\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f1507\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f1508\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f1511\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} +{\f1512\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;} +{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} +{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} +{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);} +{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);} +{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f1164\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\f1165\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f1167\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f1168\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f1169\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f1170\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f1171\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f1172\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255; +\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0; +\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;}{\*\defchp \fs24\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\langfenp2052 }{\*\defpap +\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 \fs24\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 \snext11 \ssemihidden \sunhideused +Normal Table;}{\*\cs15 \additive \sqformat \spriority0 Footnote Characters;}{\*\cs16 \additive \super \spriority0 Footnote Anchor;}{\*\cs17 \additive \super \spriority0 Endnote Anchor;}{\*\cs18 \additive \sqformat \spriority0 Endnote Characters;}{ +\s19\ql \li0\ri0\sb240\sa120\keepn\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs28\alang1081 \ltrch\fcs0 \fs28\lang1033\langfe2052\loch\f44\hich\af44\dbch\af45\cgrid\langnp1033\langfenp2052 +\sbasedon0 \snext20 \sqformat \spriority0 Heading;}{\s20\ql \li0\ri0\sa140\sl288\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 \sbasedon0 \snext20 \spriority0 Body Text;}{\s21\ql \li0\ri0\sa140\sl288\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af46\afs24\alang1081 \ltrch\fcs0 \fs24\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 \sbasedon20 \snext21 \spriority0 List;}{ +\s22\ql \li0\ri0\sb120\sa120\widctlpar\noline\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ai\af46\afs24\alang1081 \ltrch\fcs0 \i\fs24\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 +\sbasedon0 \snext22 \sqformat \spriority0 caption;}{\s23\ql \li0\ri0\widctlpar\noline\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 \sbasedon0 \snext23 \sqformat \spriority0 Index;}{\s24\ql \fi-339\li339\ri0\widctlpar\noline\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin339\itap0 \rtlch\fcs1 +\af46\afs20\alang1081 \ltrch\fcs0 \fs20\lang1033\langfe2052\loch\f43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 \sbasedon0 \snext24 \spriority0 footnote text;}}{\*\rsidtbl \rsid6097384\rsid16590483\rsid16671749}{\mmathPr\mmathFont34\mbrkBin0 +\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\title A Text Extraction Test Document for DSpace}{\author Mark Wood}{\operator Tim Donohue}{\creatim\yr2022\mo3\dy30\hr13\min54} +{\revtim\yr2022\mo3\dy30\hr13\min54}{\version2}{\edmins0}{\nofpages1}{\nofwords75}{\nofchars433}{\nofcharsws507}{\vern43}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} +\paperw12240\paperh15840\margl1134\margr1134\margt1134\margb1134\gutter0\ltrsect +\deftab709\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1 +\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin450\dgvorigin0\dghshow1\dgvshow1 +\jexpand\viewkind5\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct +\asianbrkrule\rsidroot6097384\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 +{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0{\*\ftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 +\fs24\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\linex0\headery0\footery0\endnhere\sectunlocked1\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3 +\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} +{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar +\qc \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af46\afs24\alang1081 \ltrch\fcs0 \fs24\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af46\afs30 \ltrch\fcs0 +\fs30\insrsid16671749 \hich\af43\dbch\af45\loch\f43 A Text Extraction Test Document}{\rtlch\fcs1 \af46\afs30 \ltrch\fcs0 \fs30\insrsid6097384 +\par }{\rtlch\fcs1 \af46\afs20 \ltrch\fcs0 \fs20\insrsid16671749 \hich\af43\dbch\af45\loch\f43 for}{\rtlch\fcs1 \af46\afs20 \ltrch\fcs0 \fs20\insrsid6097384 +\par }{\rtlch\fcs1 \af46\afs30 \ltrch\fcs0 \fs30\insrsid16671749 \hich\af43\dbch\af45\loch\f43 DSpace}{\rtlch\fcs1 \af46\afs30 \ltrch\fcs0 \fs30\insrsid6097384 +\par +\par }\pard \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 {\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \hich\af43\dbch\af45\loch\f43 +This is a text. For the next sixty seconds this software will conduct a test of the DSpace text extraction facility. This is only a text.}{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid6097384 +\par +\par }{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \hich\af43\dbch\af45\loch\f43 This is a paragraph that followed the first that lived in the \hich\af43\dbch\af45\loch\f43 document that Jack built.}{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid6097384 +\par +\par }{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \hich\af43\dbch\af45\loch\f43 Lorem ipsum dolor sit amet. The quick brown fox jumped over the lazy dog. Yow! Are we having fun yet?}{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid6097384 +\par +\par }{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \hich\af43\dbch\af45\loch\f43 This has been a test of the DSpace text extraction system. In the event of actual content you would care what is written he\hich\af43\dbch\af45\loch\f43 re.}{\rtlch\fcs1 +\af46 \ltrch\fcs0 \cs16\super\insrsid16671749 \chftn {\footnote \ltrpar \pard\plain \ltrpar\s24\ql \fi-339\li339\ri0\widctlpar\noline\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin339\itap0 \rtlch\fcs1 \af46\afs20\alang1081 \ltrch\fcs0 +\fs20\lang1033\langfe2052\loch\af43\hich\af43\dbch\af45\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid16671749 \chftn \tab \hich\af43\dbch\af45\loch\f43 Tip o\hich\f43 \rquote \loch\f43 + the hat to the U.S. Emergency Broadcast System for the format that I have irreverently borrowed.}}}{\rtlch\fcs1 \af46 \ltrch\fcs0 \insrsid6097384 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f +7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd +ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d +7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b +d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52 +fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71 +b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b +fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567 +9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd +79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf +5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2 +d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1 +738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68 +2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac +5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a +b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9 +493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2 +be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f +f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64 +7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e +b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4 +6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd +f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d +7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39 +4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf +1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a +faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2 +67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9 +416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27 +1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b +8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4 +8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65 +2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36 +3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e +3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985 +0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000 +0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000 +000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000 +7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000 +000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000 +000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax376\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; +\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; +\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; +\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; +\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; +\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; +\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; +\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; +\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; +\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; +\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; +\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; +\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; +\lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;}}{\*\datastore 01050000 +02000000180000004d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000d0af +77916744d801feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.txt b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.txt new file mode 100644 index 0000000000..edd9160b1d --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.txt @@ -0,0 +1,13 @@ +A Text Extraction Test Document +for +DSpace + +This is a text. For the next sixty seconds this software will conduct a test of the DSpace text extraction facility. This is only a text. + +This is a paragraph that followed the first that lived in the document that Jack built. + +Lorem ipsum dolor sit amet. The quick brown fox jumped over the lazy dog. Yow! Are we having fun yet? + +This has been a test of the DSpace text extraction system. In the event of actual content you would care what is written here. + +Tip o’ the hat to the U.S. Emergency Broadcast System for the format that I have irreverently borrowed. \ No newline at end of file diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xls b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xls new file mode 100644 index 0000000000..1ebc20bc38 Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xls differ diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xlsx b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xlsx new file mode 100644 index 0000000000..47e0f7387f Binary files /dev/null and b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xlsx differ diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index ce1dc8a326..e606523182 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.2 + 7.3-SNAPSHOT .. @@ -45,6 +45,13 @@ org.springframework.boot spring-boot-starter-web ${spring-boot.version} + + + + org.hibernate.validator + hibernate-validator + + org.springframework.boot @@ -72,7 +79,7 @@ org.ehcache ehcache - 3.4.0 + ${ehcache.version} diff --git a/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java b/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java index 0e614fae2a..da50f33582 100644 --- a/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java +++ b/dspace-iiif/src/main/java/org/dspace/app/iiif/service/WordHighlightSolrSearch.java @@ -12,14 +12,11 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.validator.routines.UrlValidator; import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrQuery; @@ -35,7 +32,6 @@ import org.dspace.app.iiif.model.generator.ContentAsTextGenerator; import org.dspace.app.iiif.model.generator.ManifestGenerator; import org.dspace.app.iiif.model.generator.SearchResultGenerator; import org.dspace.app.iiif.service.utils.IIIFUtils; -import org.dspace.discovery.SolrSearchCore; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -66,9 +62,6 @@ public class WordHighlightSolrSearch implements SearchAnnotationService { @Autowired SearchResultGenerator searchResult; - @Autowired - SolrSearchCore solrSearchCore; - @Autowired ManifestGenerator manifestGenerator; @@ -167,26 +160,49 @@ public class WordHighlightSolrSearch implements SearchAnnotationService { private String getAnnotationList(UUID uuid, String json, String query) { searchResult.setIdentifier(manifestId + "/search?q=" + URLEncoder.encode(query, StandardCharsets.UTF_8)); - GsonBuilder builder = new GsonBuilder(); - Gson gson = builder.create(); - JsonObject body = gson.fromJson(json, JsonObject.class); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode body = null; + try { + body = mapper.readTree(json); + } catch (JsonProcessingException e) { + log.error("Unable to process json response.", e); + } + // If error occurred or no body, return immediately if (body == null) { - log.warn("Unable to process json response."); return utils.asJson(searchResult.generateResource()); } - // outer ocr highlight element - JsonObject highs = body.getAsJsonObject("ocrHighlighting"); - // highlight entries - for (Map.Entry ocrIds: highs.entrySet()) { - // ocr_text - JsonObject ocrObj = ocrIds.getValue().getAsJsonObject().getAsJsonObject("ocr_text"); - // snippets array - if (ocrObj != null) { - for (JsonElement snippetArray : ocrObj.getAsJsonObject().get("snippets").getAsJsonArray()) { - String pageId = getCanvasId(snippetArray.getAsJsonObject().get("pages")); - for (JsonElement highlights : snippetArray.getAsJsonObject().getAsJsonArray("highlights")) { - for (JsonElement highlight : highlights.getAsJsonArray()) { - searchResult.addResource(getAnnotation(highlight, pageId, uuid)); + + // Example structure of Solr response available at + // https://github.com/dbmdz/solr-ocrhighlighting/blob/main/docs/query.md + // Get the outer ocrHighlighting node + JsonNode highs = body.get("ocrHighlighting"); + if (highs != null) { + // Loop through each highlight entry under ocrHighlighting + for (final JsonNode highEntry : highs) { + // Get the ocr_text node under the entry + JsonNode ocrNode = highEntry.get("ocr_text"); + if (ocrNode != null) { + // Loop through the snippets array under that + for (final JsonNode snippet : ocrNode.get("snippets")) { + if (snippet != null) { + // Get a canvas ID based on snippet's pages + String pageId = getCanvasId(snippet.get("pages")); + if (pageId != null) { + // Loop through array of highlights for each snippet. + for (final JsonNode highlights : snippet.get("highlights")) { + if (highlights != null) { + // May be multiple word highlights on a page, so loop through them. + for (int i = 0; i < highlights.size(); i++) { + // Add annotation associated with each highlight + AnnotationGenerator anno = getAnnotation(highlights.get(i), pageId, uuid); + if (anno != null) { + searchResult.addResource(anno); + } + } + } + } + } } } } @@ -198,21 +214,24 @@ public class WordHighlightSolrSearch implements SearchAnnotationService { /** * Returns the annotation generator for the highlight. - * @param highlight highlight element from solor response + * @param highlight highlight node from Solr response * @param pageId page id from solr response * @return generator for a single annotation */ - private AnnotationGenerator getAnnotation(JsonElement highlight, String pageId, UUID uuid) { - JsonObject hcoords = highlight.getAsJsonObject(); - String text = (hcoords.get("text").getAsString()); - int ulx = hcoords.get("ulx").getAsInt(); - int uly = hcoords.get("uly").getAsInt(); - int lrx = hcoords.get("lrx").getAsInt(); - int lry = hcoords.get("lry").getAsInt(); - String w = Integer.toString(lrx - ulx); - String h = Integer.toString(lry - uly); - String params = ulx + "," + uly + "," + w + "," + h; - return createSearchResultAnnotation(params, text, pageId, uuid); + private AnnotationGenerator getAnnotation(JsonNode highlight, String pageId, UUID uuid) { + String text = highlight.get("text") != null ? highlight.get("text").asText() : null; + int ulx = highlight.get("ulx") != null ? highlight.get("ulx").asInt() : -1; + int uly = highlight.get("uly") != null ? highlight.get("uly").asInt() : -1; + int lrx = highlight.get("lrx") != null ? highlight.get("lrx").asInt() : -1; + int lry = highlight.get("lry") != null ? highlight.get("lry").asInt() : -1; + String w = (lrx >= 0 && ulx >= 0) ? Integer.toString(lrx - ulx) : null; + String h = (lry >= 0 && uly >= 0) ? Integer.toString(lry - uly) : null; + + if (text != null && w != null && h != null) { + String params = ulx + "," + uly + "," + w + "," + h; + return createSearchResultAnnotation(params, text, pageId, uuid); + } + return null; } /** @@ -221,15 +240,22 @@ public class WordHighlightSolrSearch implements SearchAnnotationService { * delimited with a "." and that the integer corresponds to the * canvas identifier in the manifest. For METS/ALTO documents, the page * order can be derived from the METS file when loading the solr index. - * @param element the pages element - * @return canvas id + * @param pagesNode the pages node + * @return canvas id or null if node was null */ - private String getCanvasId(JsonElement element) { - JsonArray pages = element.getAsJsonArray(); - JsonObject page = pages.get(0).getAsJsonObject(); - String[] identArr = page.get("id").getAsString().split("\\."); - // the canvas id. - return "c" + identArr[1]; + private String getCanvasId(JsonNode pagesNode) { + if (pagesNode != null) { + JsonNode page = pagesNode.get(0); + if (page != null) { + JsonNode pageId = page.get("id"); + if (pageId != null) { + String[] identArr = pageId.asText().split("\\."); + // the canvas id. + return "c" + identArr[1]; + } + } + } + return null; } /** diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index f4acb0d297..d233f4a772 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.2 + 7.3-SNAPSHOT .. @@ -82,10 +82,6 @@ org.mockito mockito-all - - xml-apis - xml-apis - org.apache.commons commons-lang3 diff --git a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java index 26dd976495..e67e9c56bd 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/services/impl/resources/DSpaceResourceResolver.java @@ -22,6 +22,7 @@ import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; public class DSpaceResourceResolver implements ResourceResolver { + // Requires usage of Saxon as OAI-PMH uses some XSLT 2 functions private static final TransformerFactory transformerFactory = TransformerFactory .newInstance("net.sf.saxon.TransformerFactoryImpl", null); diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java index 6fab56b526..42dbed04b6 100644 --- a/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java +++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/stylesheets/AbstractXSLTest.java @@ -19,6 +19,7 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.io.IOUtils; public abstract class AbstractXSLTest { + // Requires usage of Saxon as OAI-PMH uses some XSLT 2 functions private static final TransformerFactory factory = TransformerFactory .newInstance("net.sf.saxon.TransformerFactoryImpl", null); diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 8a25dece7e..b6209efb1c 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.2 + 7.3-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index c60383384f..27e4a168dc 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.2 + 7.3-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.2 + 7.3-SNAPSHOT .. @@ -63,45 +63,11 @@ org.glassfish.jersey.media jersey-media-json-jackson ${jersey.version} - - - com.fasterxml.jackson.core - jackson-module-jaxb-annotations - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-base - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - - - com.fasterxml.jackson.module - jackson-module-jaxb-annotations - - - - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} - com.fasterxml.jackson.jaxrs - jackson-jaxrs-base - ${jackson.version} - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - ${jackson.version} - - - com.fasterxml.jackson.module - jackson-module-jaxb-annotations - ${jackson.version} + org.glassfish.jersey.media + jersey-media-jaxb + ${jersey.version} @@ -156,11 +122,6 @@ jakarta.annotation jakarta.annotation-api - - - org.ow2.asm - asm-commons - @@ -193,30 +154,9 @@ ${spring-security.version} - - cglib - cglib - 2.2.2 - - - org.dspace dspace-api - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-annotations - - diff --git a/dspace-server-webapp/README.md b/dspace-server-webapp/README.md index e71039308a..8d3853e8cc 100644 --- a/dspace-server-webapp/README.md +++ b/dspace-server-webapp/README.md @@ -22,7 +22,10 @@ The only tested way right now is to run this webapp inside your IDE (Eclipse). J > dspace.dir = d:/install/dspace7 ## HAL Browser -The modified version of the HAL Browser from the Spring Data REST project is included, the index.html file is overriden locally to support the /api baseURL (see [DATAREST-971](https://jira.spring.io/browse/DATAREST-971)) + +The modified version of the HAL Browser from https://github.com/mikekelly/hal-browser + +We've updated/customized the HAL Browser to integrate better with our authentication system, provide CSRF support, and use a more recent version of its dependencies. ## Packages and main classes *[org.dspace.app.rest.Application](src/main/java/org/dspace/app/rest/Application.java)* is the spring boot main class it initializes diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 20395013ce..eff8deae46 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.2 + 7.3-SNAPSHOT .. @@ -68,7 +68,9 @@ src/main/webapp/index.html src/main/webapp/login.html + src/main/webapp/styles.css src/main/webapp/js/hal/** + src/main/webapp/js/vendor/** @@ -238,28 +240,18 @@ - - - - - - + org.springframework.boot spring-boot-starter-web ${spring-boot.version} + + + + org.hibernate.validator + hibernate-validator + + org.springframework.boot @@ -286,20 +278,24 @@ 0.4.6 - + + + - org.springframework.data - spring-data-rest-hal-browser - ${spring-hal-browser.version} + org.webjars + hal-browser + ad9b865 - + org.webjars.bowergithub.jquery jquery-dist - 3.5.1 + 3.6.0 @@ -308,17 +304,46 @@ toastr 2.1.4 - + + org.webjars.bowergithub.medialize + uri.js + 1.19.10 + + + + org.webjars.bowergithub.jashkenas + underscore + 1.13.2 + + + + org.webjars.bowergithub.jashkenas + backbone + 1.4.1 + + + + org.webjars.npm + json-editor__json-editor + 2.6.1 + + org.webjars.bowergithub.twbs bootstrap - 4.5.2 + 4.6.1 - org.springframework.boot @@ -364,13 +389,12 @@ dspace-services + + org.dspace dspace-iiif - - - org.dspace dspace-oai @@ -416,6 +440,32 @@ ${nimbus-jose-jwt.version} + + org.apache.solr + solr-solrj + ${solr.client.version} + + + + + org.springframework.boot + spring-boot-starter-cache + ${spring-boot.version} + + + + javax.cache + cache-api + 1.1.0 + + + + org.ehcache + ehcache + ${ehcache.version} + + org.springframework.boot @@ -431,13 +481,11 @@ com.jayway.jsonpath json-path - ${json-path.version} test com.jayway.jsonpath json-path-assert - ${json-path.version} test @@ -488,50 +536,6 @@ - - - org.springframework.boot - spring-boot-starter-cache - ${spring-boot.version} - - - - javax.cache - cache-api - 1.1.0 - - - - org.ehcache - ehcache - 3.4.0 - - - org.apache.solr - solr-cell - test - - - - org.apache.commons - commons-text - - - - org.eclipse.jetty - jetty-http - - - org.eclipse.jetty - jetty-io - - - org.eclipse.jetty - jetty-util - - - org.apache.lucene lucene-analyzers-icu diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java index e0ea0cccb0..459cfe0dee 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/Application.java @@ -40,6 +40,7 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** @@ -192,13 +193,31 @@ public class Application extends SpringBootServletInitializer { } } + /** + * Add a ViewController for the root path, to load HAL Browser + * @param registry ViewControllerRegistry + */ + @Override + public void addViewControllers(ViewControllerRegistry registry) { + // Ensure accessing the root path will load the index.html of the HAL Browser + registry.addViewController("/").setViewName("forward:/index.html"); + } + /** * Add a new ResourceHandler to allow us to use WebJars.org to pull in web dependencies - * dynamically for HAL Browser, and access them off the /webjars path. + * dynamically for HAL Browser, etc. * @param registry ResourceHandlerRegistry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { + // First, "mount" the Hal Browser resources at the /browser path + // NOTE: the hal-browser directory uses the version of the Hal browser, so this needs to be synced + // with the org.webjars.hal-browser version in the POM + registry + .addResourceHandler("/browser/**") + .addResourceLocations("/webjars/hal-browser/ad9b865/"); + + // Make all other Webjars available off the /webjars path registry .addResourceHandler("/webjars/**") .addResourceLocations("/webjars/"); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java index 9660d0af56..7d9cb470f9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java @@ -80,7 +80,7 @@ public class AuthenticationRestController implements InitializingBean { @Override public void afterPropertiesSet() { discoverableEndpointsService - .register(this, Arrays.asList(new Link("/api/" + AuthnRest.CATEGORY, AuthnRest.NAME))); + .register(this, Arrays.asList(Link.of("/api/" + AuthnRest.CATEGORY, AuthnRest.NAME))); } @RequestMapping(method = RequestMethod.GET) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index 75d3c9886c..183aee83d0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -12,7 +12,6 @@ import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFI import static org.springframework.web.bind.annotation.RequestMethod.PUT; import java.io.IOException; -import java.io.InputStream; import java.sql.SQLException; import java.util.List; import java.util.UUID; @@ -22,7 +21,6 @@ import javax.ws.rs.core.Response; import org.apache.catalina.connector.ClientAbortException; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.exception.DSpaceBadRequestException; @@ -133,19 +131,12 @@ public class BitstreamRestController { } try { - long filesize; - if (citationDocumentService.isCitationEnabledForBitstream(bit, context)) { - final Pair citedDocument = citationDocumentService.makeCitedDocument(context, bit); - filesize = citedDocument.getRight(); - citedDocument.getLeft().close(); - } else { - filesize = bit.getSizeBytes(); - } + long filesize = bit.getSizeBytes(); + Boolean citationEnabledForBitstream = citationDocumentService.isCitationEnabledForBitstream(bit, context); HttpHeadersInitializer httpHeadersInitializer = new HttpHeadersInitializer() .withBufferSize(BUFFER_SIZE) .withFileName(name) - .withLength(filesize) .withChecksum(bit.getChecksum()) .withMimetype(mimetype) .with(request) @@ -161,11 +152,9 @@ public class BitstreamRestController { httpHeadersInitializer.withDisposition(HttpHeadersInitializer.CONTENT_DISPOSITION_ATTACHMENT); } - - org.dspace.app.rest.utils.BitstreamResource bitstreamResource = new org.dspace.app.rest.utils.BitstreamResource( - bit, name, uuid, filesize, currentUser != null ? currentUser.getID() : null); + name, uuid, currentUser != null ? currentUser.getID() : null, citationEnabledForBitstream); //We have all the data we need, close the connection to the database so that it doesn't stay open during //download/streaming diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BundleUploadBitstreamController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BundleUploadBitstreamController.java index a0f5d5f71e..0cb6bc47e0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BundleUploadBitstreamController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BundleUploadBitstreamController.java @@ -59,7 +59,7 @@ import org.springframework.web.multipart.MultipartFile; * */ @RestController -@RequestMapping("/api/" + BundleRest.CATEGORY + "/" + BundleRest.PLURAL_NAME + "/" +@RequestMapping("/api/" + BundleRest.CATEGORY + "/" + BundleRest.PLURAL_NAME + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + "/" + BitstreamRest.PLURAL_NAME) public class BundleUploadBitstreamController { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/DiscoveryRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/DiscoveryRestController.java index 5ecbe19176..947515ca54 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/DiscoveryRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/DiscoveryRestController.java @@ -73,7 +73,7 @@ public class DiscoveryRestController implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { discoverableEndpointsService - .register(this, Arrays.asList(new Link("/api/" + SearchResultsRest.CATEGORY, SearchResultsRest.CATEGORY))); + .register(this, Arrays.asList(Link.of("/api/" + SearchResultsRest.CATEGORY, SearchResultsRest.CATEGORY))); } @RequestMapping(method = RequestMethod.GET) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/IdentifierRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/IdentifierRestController.java index d3a6ef981d..5f7a473b91 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/IdentifierRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/IdentifierRestController.java @@ -66,8 +66,8 @@ public class IdentifierRestController implements InitializingBean { discoverableEndpointsService .register(this, Arrays.asList( - new Link( - new UriTemplate("/api/" + CATEGORY + "/" + ACTION, + Link.of( + UriTemplate.of("/api/" + CATEGORY + "/" + ACTION, new TemplateVariables( new TemplateVariable(PARAM, VariableType.REQUEST_PARAM))), CATEGORY))); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OidcRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OidcRestController.java index ab34a72a8d..db04b3b7cd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OidcRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OidcRestController.java @@ -45,7 +45,7 @@ public class OidcRestController { @PostConstruct public void afterPropertiesSet() { - discoverableEndpointsService.register(this, List.of(new Link("/api/" + AuthnRest.CATEGORY, "oidc"))); + discoverableEndpointsService.register(this, List.of(Link.of("/api/" + AuthnRest.CATEGORY, "oidc"))); } @RequestMapping(method = RequestMethod.GET) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java index 7c79a85701..b82b483075 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java @@ -127,7 +127,7 @@ public class RestResourceController implements InitializingBean { // Link l = linkTo(this.getClass(), r).withRel(r); String[] split = r.split("\\.", 2); String plural = English.plural(split[1]); - Link l = new Link("/api/" + split[0] + "/" + plural, plural); + Link l = Link.of("/api/" + split[0] + "/" + plural, plural); links.add(l); log.debug(l.getRel().value() + " " + l.getHref()); } @@ -821,7 +821,7 @@ public class RestResourceController implements InitializingBean { link = linkTo(this.getClass(), apiCategory, model).slash(uuid).slash(subpath).withSelfRel(); } - return new EntityModel(new EmbeddedPage(link.getHref(), + return EntityModel.of(new EmbeddedPage(link.getHref(), pageResult.map(converter::toResource), null, subpath)); } else { RestModel object = (RestModel) linkMethod.invoke(linkRepository, request, diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ScriptProcessesController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ScriptProcessesController.java index 1197e13c98..196cade5dd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ScriptProcessesController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ScriptProcessesController.java @@ -55,13 +55,15 @@ public class ScriptProcessesController { * 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 + * @param files (Optional) any files that need to be passed to the script for it to run * @return The ProcessResource object for the created process * @throws Exception If something goes wrong */ @RequestMapping(method = RequestMethod.POST) @PreAuthorize("hasAuthority('ADMIN')") - public ResponseEntity> startProcess(@PathVariable(name = "name") String scriptName, - @RequestParam(name = "file") List files) + public ResponseEntity> startProcess( + @PathVariable(name = "name") String scriptName, + @RequestParam(name = "file", required = false) List files) throws Exception { if (log.isTraceEnabled()) { log.trace("Starting Process for Script with name: " + scriptName); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/StatisticsRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/StatisticsRestController.java index 4c2dc54574..a491157f53 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/StatisticsRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/StatisticsRestController.java @@ -57,7 +57,7 @@ public class StatisticsRestController implements InitializingBean { public void afterPropertiesSet() throws Exception { discoverableEndpointsService .register(this, Arrays - .asList(new Link("/api/" + RestAddressableModel.STATISTICS, RestAddressableModel.STATISTICS))); + .asList(Link.of("/api/" + RestAddressableModel.STATISTICS, RestAddressableModel.STATISTICS))); } @RequestMapping(method = RequestMethod.GET) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java index 78e71d91fd..c32d551cbe 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java @@ -132,7 +132,7 @@ public class SubmissionCCLicenseUrlRepository extends DSpaceRestRepository { - @Autowired - ConverterService converter; - @Override public BitstreamRest convert(org.dspace.content.Bitstream obj, Projection projection) { BitstreamRest b = super.convert(obj, projection); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java index cc7459955f..5238b8888a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClaimedTaskConverter.java @@ -14,6 +14,7 @@ import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.storedcomponents.ClaimedTask; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -26,6 +27,8 @@ import org.springframework.stereotype.Component; public class ClaimedTaskConverter implements IndexableObjectConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java index 18fc119eef..0f7b47239e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ConverterService.java @@ -38,6 +38,7 @@ import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.Lazy; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.data.domain.Page; @@ -52,9 +53,12 @@ import org.springframework.stereotype.Service; /** * Converts domain objects from the DSpace service layer to rest objects, and from rest objects to resource * objects, applying {@link Projection}s where applicable. - * + *

    + * MUST be loaded @Lazy, as this service requires other services to be preloaded (especially DSpaceConverter components) + * and that can result in circular references if those services need this ConverterService (and many do). * @author Luca Giamminonni (luca.giamminonni at 4science dot it) */ +@Lazy @Service public class ConverterService { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/DSpaceObjectConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/DSpaceObjectConverter.java index 0c730265ef..a89d5ec4e9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/DSpaceObjectConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/DSpaceObjectConverter.java @@ -24,6 +24,7 @@ import org.dspace.content.MetadataValue; import org.dspace.core.Context; import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; /** * This is the base converter from/to objects in the DSpace API data model and @@ -38,6 +39,8 @@ public abstract class DSpaceObjectConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataConverter.java index 14189a4401..76aca4be23 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/MetadataConverter.java @@ -28,6 +28,7 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Context; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -39,6 +40,8 @@ public class MetadataConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java index 48299dd362..548dc63753 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PoolTaskConverter.java @@ -13,6 +13,7 @@ import org.dspace.discovery.IndexableObject; import org.dspace.xmlworkflow.storedcomponents.PoolTask; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -25,6 +26,8 @@ import org.springframework.stereotype.Component; public class PoolTaskConverter implements IndexableObjectConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java index 4a794c9b85..de03d60630 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ProcessConverter.java @@ -15,6 +15,7 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.scripts.Process; import org.dspace.scripts.service.ProcessService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -23,6 +24,8 @@ import org.springframework.stereotype.Component; @Component public class ProcessConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipConverter.java index f9d0cf52ec..1459db1a94 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipConverter.java @@ -11,6 +11,7 @@ import org.dspace.app.rest.model.RelationshipRest; import org.dspace.app.rest.projection.Projection; import org.dspace.content.Relationship; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -20,6 +21,8 @@ import org.springframework.stereotype.Component; @Component public class RelationshipConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipTypeConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipTypeConverter.java index c20249c025..44a5eb3d13 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipTypeConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/RelationshipTypeConverter.java @@ -11,6 +11,7 @@ import org.dspace.app.rest.model.RelationshipTypeRest; import org.dspace.app.rest.projection.Projection; import org.dspace.content.RelationshipType; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -20,6 +21,8 @@ import org.springframework.stereotype.Component; @Component public class RelationshipTypeConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java index ab8694874c..6b233fed8b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ResourcePolicyConverter.java @@ -12,6 +12,7 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.ResourcePolicyService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -26,6 +27,8 @@ public class ResourcePolicyConverter implements DSpaceConverter getModelClass() { return ScriptConfiguration.class; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java index bf6b92a618..ad6e2d7962 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java @@ -16,6 +16,7 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.license.CCLicense; import org.dspace.license.CCLicenseField; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -25,6 +26,8 @@ import org.springframework.stereotype.Component; @Component public class SubmissionCCLicenseConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java index 782056dc1c..70a1a4d76c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java @@ -16,6 +16,7 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.license.CCLicenseField; import org.dspace.license.CCLicenseFieldEnum; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -27,6 +28,8 @@ import org.springframework.stereotype.Component; public class SubmissionCCLicenseFieldConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionDefinitionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionDefinitionConverter.java index f380a6695f..eccd9cba41 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionDefinitionConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionDefinitionConverter.java @@ -27,6 +27,7 @@ import org.dspace.content.Collection; import org.dspace.core.Context; import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -47,6 +48,8 @@ public class SubmissionDefinitionConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java index 04af851e8b..e7f322a20c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/WorkflowDefinitionConverter.java @@ -15,6 +15,7 @@ import org.dspace.app.rest.projection.Projection; import org.dspace.xmlworkflow.factory.XmlWorkflowFactory; import org.dspace.xmlworkflow.state.Workflow; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -28,6 +29,8 @@ public class WorkflowDefinitionConverter implements DSpaceConverter { + // Must be loaded @Lazy, as ConverterService autowires all DSpaceConverter components + @Lazy @Autowired ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceAccessDeniedHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceAccessDeniedHandler.java index 9e20eca4c2..c2842d33d2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceAccessDeniedHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceAccessDeniedHandler.java @@ -12,9 +12,9 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.dspace.app.rest.security.WebSecurityConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Lazy; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.csrf.CsrfToken; @@ -40,8 +40,9 @@ import org.springframework.web.servlet.HandlerExceptionResolver; @Component public class DSpaceAccessDeniedHandler implements AccessDeniedHandler { + @Lazy @Autowired - private WebSecurityConfiguration webSecurityConfiguration; + private CsrfTokenRepository csrfTokenRepository; @Autowired @Qualifier("handlerExceptionResolver") @@ -69,9 +70,6 @@ public class DSpaceAccessDeniedHandler implements AccessDeniedHandler { // switched clients (from HAL Browser to UI or visa versa) and has an out-of-sync token. // NOTE: this logic is tested in AuthenticationRestControllerIT.testRefreshTokenWithInvalidCSRF() if (ex instanceof InvalidCsrfTokenException) { - // Get access to our enabled CSRF token repository - CsrfTokenRepository csrfTokenRepository = webSecurityConfiguration.getCsrfTokenRepository(); - // Remove current token & generate a new one csrfTokenRepository.saveToken(null, request, response); CsrfToken newToken = csrfTokenRepository.generateToken(request); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index d57e637047..70bc2c7eed 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -11,8 +11,10 @@ import static org.springframework.web.servlet.DispatcherServlet.EXCEPTION_ATTRIB import java.io.IOException; import java.sql.SQLException; +import java.util.HashSet; import java.util.Objects; import java.util.Set; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -21,6 +23,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; import org.springframework.beans.TypeMismatchException; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.repository.support.QueryMethodParameterConversionException; @@ -51,12 +54,18 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep */ @ControllerAdvice public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionHandler { - private static final Logger log = LogManager.getLogger(DSpaceApiExceptionControllerAdvice.class); + private static final Logger log = LogManager.getLogger(); /** - * Set of HTTP error codes to log as ERROR with full stacktrace. + * Default collection of HTTP error codes to log as ERROR with full stack trace. */ - private static final Set LOG_AS_ERROR = Set.of(422); + private static final String[] LOG_AS_ERROR_DEFAULT = { "422" }; + + /** Configuration parameter for ERROR treatment. */ + private static final String P_LOG_AS_ERROR = "logging.server.include-stacktrace-for-httpcode"; + + @Inject + private ConfigurationService configurationService; @ExceptionHandler({AuthorizeException.class, RESTAuthorizationException.class, AccessDeniedException.class}) protected void handleAuthorizeException(HttpServletRequest request, HttpServletResponse response, Exception ex) @@ -126,8 +135,14 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH /** * Add user-friendly error messages to the response body for selected errors. - * Since the error messages will be exposed to the API user, the exception classes are expected to implement - * {@link TranslatableException} such that the error messages can be translated. + * Since the error messages will be exposed to the API user, the + * exception classes are expected to implement {@link TranslatableException} + * such that the error messages can be translated. + * + * @param request the client's request + * @param response our response + * @param ex exception thrown in handling request + * @throws java.io.IOException passed through. */ @ExceptionHandler({ RESTEmptyWorkflowGroupException.class, @@ -192,9 +207,12 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH /** * Send the error to the response. - * 5xx errors will be logged as ERROR with a full stack trace, 4xx errors will be logged as WARN without a - * stacktrace. Specific 4xx errors where an ERROR log with full stacktrace is more appropriate are configured in - * {@link #LOG_AS_ERROR} + * 5xx errors will be logged as ERROR with a full stack trace. 4xx errors + * will be logged as WARN without a stack trace. Specific 4xx errors where + * an ERROR log with full stack trace is more appropriate are configured + * using property {@code logging.server.include-stacktrace-for-httpcode} + * (see {@link P_LOG_AS_ERROR} and {@link LOG_AS_ERROR_DEFAULT}). + * * @param request current request * @param response current response * @param ex Exception thrown @@ -202,18 +220,44 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH * @param statusCode status code to send in response * @throws IOException */ - private void sendErrorResponse(final HttpServletRequest request, final HttpServletResponse response, - final Exception ex, final String message, final int statusCode) throws IOException { + private void sendErrorResponse(final HttpServletRequest request, + final HttpServletResponse response, + final Exception ex, final String message, final int statusCode) + throws IOException { //Make sure Spring picks up this exception request.setAttribute(EXCEPTION_ATTRIBUTE, ex); + // Which status codes should be treated as ERROR? + final Set LOG_AS_ERROR = new HashSet<>(); + String[] error_codes = configurationService.getArrayProperty( + P_LOG_AS_ERROR, LOG_AS_ERROR_DEFAULT); + for (String code : error_codes) { + try { + LOG_AS_ERROR.add(Integer.valueOf(code)); + } catch (NumberFormatException e) { + log.warn("Non-integer HTTP status code {} in {}", code, P_LOG_AS_ERROR); + // And continue + } + } + // We don't want to fill logs with bad/invalid REST API requests. if (HttpStatus.valueOf(statusCode).is5xxServerError() || LOG_AS_ERROR.contains(statusCode)) { // Log the full error and status code log.error("{} (status:{})", message, statusCode, ex); } else if (HttpStatus.valueOf(statusCode).is4xxClientError()) { // Log the error as a single-line WARN - log.warn("{} (status:{})", message, statusCode); + String location; + String exceptionMessage; + if (null == ex) { + exceptionMessage = "none"; + location = "unknown"; + } else { + exceptionMessage = ex.getMessage(); + StackTraceElement[] trace = ex.getStackTrace(); + location = trace.length <= 0 ? "unknown" : trace[0].toString(); + } + log.warn("{} (status:{} exception: {} at: {})", message, statusCode, + exceptionMessage, location); } //Exception properties will be set by org.springframework.boot.web.support.ErrorPageFilter diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java index 093c93a182..659c0be517 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java @@ -56,9 +56,7 @@ public abstract class HalLinkFactory { } protected Link buildLink(String rel, String href) { - Link link = new Link(href, rel); - - return link; + return Link.of(href, rel); } protected CONTROLLER getMethodOn(Object... parameters) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AbstractDSpaceRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AbstractDSpaceRestRepository.java index f835098d55..0ccfa5249f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AbstractDSpaceRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/AbstractDSpaceRestRepository.java @@ -14,6 +14,7 @@ import org.dspace.core.Context; import org.dspace.services.RequestService; import org.dspace.utils.DSpace; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; /** * This is the base class for any Rest Repository. It provides utility method to @@ -26,6 +27,7 @@ public abstract class AbstractDSpaceRestRepository { @Autowired protected Utils utils; + @Lazy @Autowired protected ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskRestRepository.java index 3c0c81a88a..dbd37093a9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClaimedTaskRestRepository.java @@ -287,7 +287,7 @@ public class ClaimedTaskRestRepository extends DSpaceRestRepository extends AbstractDSpaceRestRepository - implements PagingAndSortingRepository { + implements PagingAndSortingRepository, BeanNameAware { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(DSpaceRestRepository.class); - //Trick to make inner-calls to ourselves that are checked by Spring security - //See: - // https://stackoverflow.com/questions/13564627/spring-aop-not-working-for-method-call-inside-another-method - // https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#aop-understanding-aop-proxies - @Autowired + private String thisRepositoryBeanName; private DSpaceRestRepository thisRepository; + @Autowired + private ApplicationContext applicationContext; + @Autowired private MetadataFieldService metadataFieldService; + /** + * From BeanNameAware. Allows us to capture the name of the bean, so we can load it into thisRepository. + * See getThisRepository() method. + * @param beanName name of ourselves + */ + @Override + public void setBeanName(String beanName) { + this.thisRepositoryBeanName = beanName; + } + + /** + * Get access to our current DSpaceRestRepository bean. This is a trick to make inner-calls to ourselves that are + * checked by Spring Security + * See: + * https://stackoverflow.com/questions/13564627/spring-aop-not-working-for-method-call-inside-another-method + * https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-understanding-aop-proxies + * @return current DSpaceRestRepository + */ + private DSpaceRestRepository getThisRepository() { + if (thisRepository == null) { + thisRepository = (DSpaceRestRepository) applicationContext.getBean(thisRepositoryBeanName); + } + return thisRepository; + } + @Override public S save(S entity) { Context context = null; @@ -108,7 +135,7 @@ public abstract class DSpaceRestRepository findById(ID id) { Context context = obtainContext(); - final T object = thisRepository.findOne(context, id); + final T object = getThisRepository().findOne(context, id); if (object == null) { return Optional.empty(); } else { @@ -171,7 +198,7 @@ public abstract class DSpaceRestRepository ids) { + // TODO Auto-generated method stub + } + @Override /** * Method to implement to support bulk delete of ALL entity instances @@ -235,7 +270,7 @@ public abstract class DSpaceRestRepository findAll(Pageable pageable) { Context context = obtainContext(); - return thisRepository.findAll(context, pageable); + return getThisRepository().findAll(context, pageable); } /** @@ -263,7 +298,7 @@ public abstract class DSpaceRestRepository extends PatchOperation { checkOperationValue(operation.getValue()); if (this.supports(resource, operation)) { ResourcePolicy resourcePolicy = (ResourcePolicy) resource; - resourcePolicyUtils.checkResourcePolicyForExistingEndDateValue(resourcePolicy, operation); resourcePolicyUtils.checkResourcePolicyForConsistentEndDateValue(resourcePolicy, operation); this.add(resourcePolicy, operation); return resource; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java index 5902841669..d58c21093f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/resourcePolicy/ResourcePolicyUtils.java @@ -134,7 +134,7 @@ public class ResourcePolicyUtils { String dateS = (String) operation.getValue(); try { Date date = simpleDateFormat.parse(dateS); - if (resource.getEndDate() != null && resource.getStartDate().after(date)) { + if (resource.getStartDate() != null && resource.getStartDate().after(date)) { throw new DSpaceBadRequestException("Attempting to set an invalid endDate smaller than the startDate."); } } catch (ParseException e) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java index 38e8c1d7f4..23e6356216 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/WebSecurityConfiguration.java @@ -13,7 +13,9 @@ import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -102,7 +104,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { // (both are defined below as methods). // While we primarily use JWT in headers, CSRF protection is needed because we also support JWT via Cookies .csrf() - .csrfTokenRepository(this.getCsrfTokenRepository()) + .csrfTokenRepository(this.csrfTokenRepository()) .sessionAuthenticationStrategy(this.sessionAuthenticationStrategy()) .and() .exceptionHandling() @@ -168,7 +170,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { * * @return CsrfTokenRepository as described above */ - public CsrfTokenRepository getCsrfTokenRepository() { + @Lazy + @Bean + public CsrfTokenRepository csrfTokenRepository() { return new DSpaceCsrfTokenRepository(); } @@ -177,7 +181,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { * is only refreshed when it is used (or attempted to be used) by the client. */ private SessionAuthenticationStrategy sessionAuthenticationStrategy() { - return new DSpaceCsrfAuthenticationStrategy(getCsrfTokenRepository()); + return new DSpaceCsrfAuthenticationStrategy(csrfTokenRepository()); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java index 5c685f61af..c28729ff83 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java @@ -21,7 +21,6 @@ import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.wrapper.AuthenticationToken; import org.dspace.app.rest.security.DSpaceAuthentication; import org.dspace.app.rest.security.RestAuthenticationService; -import org.dspace.app.rest.security.WebSecurityConfiguration; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authenticate.AuthenticationMethod; import org.dspace.authenticate.service.AuthenticationService; @@ -32,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.http.ResponseCookie; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; @@ -65,8 +65,9 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication @Autowired private AuthenticationService authenticationService; + @Lazy @Autowired - private WebSecurityConfiguration webSecurityConfiguration; + private CsrfTokenRepository csrfTokenRepository; @Override public void afterPropertiesSet() throws Exception { @@ -331,9 +332,6 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication * @param response current response */ private void resetCSRFToken(HttpServletRequest request, HttpServletResponse response) { - // Get access to our enabled CSRF token repository - CsrfTokenRepository csrfTokenRepository = webSecurityConfiguration.getCsrfTokenRepository(); - // Remove current CSRF token & generate a new one // We do this as we want the token to change anytime you login or logout csrfTokenRepository.saveToken(null, request, response); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java index 8eb1c1c119..76de36dde6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java @@ -63,6 +63,7 @@ import org.dspace.workflow.WorkflowItemService; import org.dspace.workflow.WorkflowService; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.data.rest.webmvc.json.patch.PatchException; import org.springframework.jdbc.datasource.init.UncategorizedScriptException; import org.springframework.stereotype.Component; @@ -94,6 +95,7 @@ public class SubmissionService { protected CreativeCommonsService creativeCommonsService; @Autowired private RequestService requestService; + @Lazy @Autowired private ConverterService converter; @Autowired @@ -304,7 +306,9 @@ public class SubmissionService { result.setRights(creativeCommonsService.getLicenseName(item)); Bitstream licenseRdfBitstream = creativeCommonsService.getLicenseRdfBitstream(item); - result.setFile(converter.toRest(licenseRdfBitstream, Projection.DEFAULT)); + if (licenseRdfBitstream != null) { + result.setFile(converter.toRest(licenseRdfBitstream, Projection.DEFAULT)); + } return result; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/AbstractValidation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/AbstractValidation.java index 4bb0fd6099..cc285ead00 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/AbstractValidation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/AbstractValidation.java @@ -7,7 +7,6 @@ */ package org.dspace.app.rest.submit.step.validation; -import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -22,8 +21,6 @@ public abstract class AbstractValidation implements Validation { private String name; - private List errors = new ArrayList(); - /** * An unique name to identify the validation implementation */ @@ -36,25 +33,29 @@ public abstract class AbstractValidation implements Validation { } /** - * Add an error message (i18nKey) for a specific json path - * - * @param i18nKey - * the validation error message as a key to internationalize - * @param path - * the json path that identify the wrong data in the submission. It could be as specific as a single - * value in a multivalued attribute or general of a "whole" section + * Add an error message (i18nKey) for a specific json path, to list provided in input. + * + * @param errors the list to which error must be added + * @param i18nKey the validation error message as a key to internationalize + * @param path the json path that identify the wrong data in the submission. + * It could be as specific as a single value in a multivalued + * attribute or general of a "whole" section */ - public void addError(String i18nKey, String path) { + public void addError(List errors, String i18nKey, String path) { + + if (StringUtils.isBlank(i18nKey)) { + return; + } + boolean found = false; - if (StringUtils.isNotBlank(i18nKey)) { - for (ErrorRest error : errors) { - if (i18nKey.equals(error.getMessage())) { - error.getPaths().add(path); - found = true; - break; - } + for (ErrorRest error : errors) { + if (i18nKey.equals(error.getMessage())) { + error.getPaths().add(path); + found = true; + break; } } + if (!found) { ErrorRest error = new ErrorRest(); error.setMessage(i18nKey); @@ -62,13 +63,4 @@ public abstract class AbstractValidation implements Validation { errors.add(error); } } - - /** - * Expose the identified errors - * - * @return the list of identified {@link ErrorRest} - */ - public List getErrors() { - return errors; - } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/LicenseValidation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/LicenseValidation.java index 68c80504ef..f237189ab1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/LicenseValidation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/LicenseValidation.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.submit.step.validation; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.LogManager; @@ -42,13 +43,14 @@ public class LicenseValidation extends AbstractValidation { public List validate(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws DCInputsReaderException, SQLException { + List errors = new ArrayList<>(); Bitstream bitstream = bitstreamService .getBitstreamByName(obj.getItem(), Constants.LICENSE_BUNDLE_NAME, Constants.LICENSE_BITSTREAM_NAME); if (bitstream == null) { - addError(ERROR_VALIDATION_LICENSEREQUIRED, + addError(errors, ERROR_VALIDATION_LICENSEREQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId()); } - return getErrors(); + return errors; } public BitstreamService getBitstreamService() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java index 9a01326746..e5ba916e0f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/MetadataValidation.java @@ -54,6 +54,7 @@ public class MetadataValidation extends AbstractValidation { public List validate(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws DCInputsReaderException, SQLException { + List errors = new ArrayList<>(); DCInputSet inputConfig = getInputReader().getInputsByFormName(config.getId()); for (DCInput[] row : inputConfig.getFields()) { for (DCInput input : row) { @@ -70,14 +71,14 @@ public class MetadataValidation extends AbstractValidation { for (int i = 1; i < inputPairs.size(); i += 2) { String fullFieldname = input.getFieldName() + "." + (String) inputPairs.get(i); List mdv = itemService.getMetadataByMetadataString(obj.getItem(), fullFieldname); - validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey); + validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors); if (mdv.size() > 0 && input.isVisible(DCInput.SUBMISSION_SCOPE)) { foundResult = true; } } if (input.isRequired() && ! foundResult) { // for this required qualdrop no value was found, add to the list of error fields - addError(ERROR_VALIDATION_REQUIRED, + addError(errors, ERROR_VALIDATION_REQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" + input.getFieldName()); } @@ -88,25 +89,26 @@ public class MetadataValidation extends AbstractValidation { for (String fieldName : fieldsName) { List mdv = itemService.getMetadataByMetadataString(obj.getItem(), fieldName); - validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey); + validateMetadataValues(mdv, input, config, isAuthorityControlled, fieldKey, errors); if ((input.isRequired() && mdv.size() == 0) && input.isVisible(DCInput.SUBMISSION_SCOPE)) { // since this field is missing add to list of error // fields - addError(ERROR_VALIDATION_REQUIRED, + addError(errors, ERROR_VALIDATION_REQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" + input.getFieldName()); } } } } - return getErrors(); + return errors; } private void validateMetadataValues(List mdv, DCInput input, SubmissionStepConfig config, - boolean isAuthorityControlled, String fieldKey) { + boolean isAuthorityControlled, String fieldKey, + List errors) { for (MetadataValue md : mdv) { if (! (input.validate(md.getValue()))) { - addError(ERROR_VALIDATION_REGEX, + addError(errors, ERROR_VALIDATION_REGEX, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" + input.getFieldName() + "/" + md.getPlace()); } @@ -114,7 +116,7 @@ public class MetadataValidation extends AbstractValidation { String authKey = md.getAuthority(); if (metadataAuthorityService.isAuthorityRequired(fieldKey) && StringUtils.isBlank(authKey)) { - addError(ERROR_VALIDATION_AUTHORITY_REQUIRED, + addError(errors, ERROR_VALIDATION_AUTHORITY_REQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId() + "/" + input.getFieldName() + "/" + md.getPlace()); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/UploadValidation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/UploadValidation.java index 0c896db9aa..3734d6ffdd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/UploadValidation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/validation/UploadValidation.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.submit.step.validation; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.Logger; @@ -40,14 +41,14 @@ public class UploadValidation extends AbstractValidation { public List validate(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws DCInputsReaderException, SQLException { //TODO MANAGE METADATA - + List errors = new ArrayList<>(); UploadConfiguration uploadConfig = uploadConfigurationService.getMap().get(config.getId()); if (uploadConfig.isRequired() && !itemService.hasUploadedFiles(obj.getItem())) { - addError(ERROR_VALIDATION_FILEREQUIRED, + addError(errors, ERROR_VALIDATION_FILEREQUIRED, "/" + WorkspaceItemRestRepository.OPERATION_PATH_SECTIONS + "/" + config.getId()); } - return getErrors(); + return errors; } public ItemService getItemService() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java index 7170072e13..c2136781f9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java @@ -11,7 +11,6 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.data.web.config.EnableSpringDataWebSupport; /** * This class provides extra configuration for our Spring Boot Application @@ -23,7 +22,6 @@ import org.springframework.data.web.config.EnableSpringDataWebSupport; * @author Tim Donohue */ @Configuration -@EnableSpringDataWebSupport @ComponentScan( {"org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils", "org.dspace.app.configuration", "org.dspace.iiif", "org.dspace.app.iiif"}) public class ApplicationConfig { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java index 1a2a071fe1..dabb0448a9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/AuthorityUtils.java @@ -17,6 +17,7 @@ import org.dspace.content.authority.Choice; import org.dspace.content.authority.ChoiceAuthority; import org.dspace.content.authority.service.ChoiceAuthorityService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** @@ -38,6 +39,9 @@ public class AuthorityUtils { @Autowired private ChoiceAuthorityService cas; + // Lazy load required so that AuthorityUtils can be used from DSpaceConverter components + // (because ConverterService autowires all DSpaceConverter components) + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java index 5b8c54629c..694e6a254a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java @@ -7,11 +7,14 @@ */ package org.dspace.app.rest.utils; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.UUID; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.factory.ContentServiceFactory; @@ -32,24 +35,46 @@ import org.springframework.core.io.AbstractResource; */ public class BitstreamResource extends AbstractResource { - private Bitstream bitstream; private String name; private UUID uuid; - private long sizeBytes; private UUID currentUserUUID; + private boolean shouldGenerateCoverPage; + private byte[] file; private BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); private CitationDocumentService citationDocumentService = new DSpace().getServiceManager() - .getServicesByType(CitationDocumentService.class).get(0); + .getServicesByType(CitationDocumentService.class).get(0); - public BitstreamResource(Bitstream bitstream, String name, UUID uuid, long sizeBytes, UUID currentUserUUID) { - this.bitstream = bitstream; + public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, + boolean shouldGenerateCoverPage) { this.name = name; this.uuid = uuid; - this.sizeBytes = sizeBytes; this.currentUserUUID = currentUserUUID; + this.shouldGenerateCoverPage = shouldGenerateCoverPage; + } + + /** + * Get Potential cover page by array, this method should only be called when a coverpage should be generated + * In case of failure the original file will be returned + * + * @param context the DSpace context + * @param bitstream the pdf for which we want to generate a coverpage + * @return a byte array containing the cover page + */ + private byte[] getCoverpageByteArray(Context context, Bitstream bitstream) + throws IOException, SQLException, AuthorizeException { + if (file == null) { + try { + Pair citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); + this.file = citedDocument.getLeft(); + } catch (Exception e) { + // Return the original bitstream without the cover page + this.file = IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); + } + } + return file; } @Override @@ -59,27 +84,22 @@ public class BitstreamResource extends AbstractResource { @Override public InputStream getInputStream() throws IOException { - Context context = new Context(); - try { + try (Context context = new Context()) { EPerson currentUser = ePersonService.find(context, currentUserUUID); context.setCurrentUser(currentUser); + Bitstream bitstream = bitstreamService.find(context, uuid); InputStream out; - if (citationDocumentService.isCitationEnabledForBitstream(bitstream, context)) { - out = citationDocumentService.makeCitedDocument(context, bitstream).getLeft(); + if (shouldGenerateCoverPage) { + out = new ByteArrayInputStream(getCoverpageByteArray(context, bitstream)); } else { out = bitstreamService.retrieve(context, bitstream); } + this.file = null; return out; } catch (SQLException | AuthorizeException e) { throw new IOException(e); - } finally { - try { - context.complete(); - } catch (SQLException e) { - throw new IOException(e); - } } } @@ -90,6 +110,17 @@ public class BitstreamResource extends AbstractResource { @Override public long contentLength() throws IOException { - return sizeBytes; + try (Context context = new Context()) { + EPerson currentUser = ePersonService.find(context, currentUserUUID); + context.setCurrentUser(currentUser); + Bitstream bitstream = bitstreamService.find(context, uuid); + if (shouldGenerateCoverPage) { + return getCoverpageByteArray(context, bitstream).length; + } else { + return bitstream.getSizeBytes(); + } + } catch (SQLException | AuthorizeException e) { + throw new IOException(e); + } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java index 22ae8ad3d9..ffe2acc2e0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java @@ -9,6 +9,7 @@ package org.dspace.app.rest.utils; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; +import static javax.mail.internet.MimeUtility.encodeText; import java.io.IOException; import java.util.Arrays; @@ -163,7 +164,8 @@ public class HttpHeadersInitializer { } httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(String.format(CONTENT_DISPOSITION_FORMAT, - disposition, fileName))); + disposition, + encodeText(fileName)))); log.debug("Content-Disposition : {}", disposition); // Content phase diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java index 99c350214c..8e1bb37005 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/Utils.java @@ -84,6 +84,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Lazy; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.ConversionService; import org.springframework.data.domain.Page; @@ -131,6 +132,8 @@ public class Utils { @Autowired private BitstreamFormatService bitstreamFormatService; + // Must be loaded @Lazy, as ConverterService also autowires Utils + @Lazy @Autowired private ConverterService converter; diff --git a/dspace-server-webapp/src/main/resources/application.properties b/dspace-server-webapp/src/main/resources/application.properties index c695fe2fba..5992ded040 100644 --- a/dspace-server-webapp/src/main/resources/application.properties +++ b/dspace-server-webapp/src/main/resources/application.properties @@ -38,11 +38,6 @@ # interact with or read its configuration from dspace.cfg. dspace.dir=${dspace.dir} -######################## -# Spring DATA Rest settings -# -spring.data.rest.basePath= - ######################## # Jackson serialization settings # @@ -60,11 +55,9 @@ spring.messages.encoding=UTF-8 # # # Charset of HTTP requests and responses. Added to the "Content-Type" header if not set explicitly. -spring.http.encoding.charset=UTF-8 -# Enable http encoding support. -spring.http.encoding.enabled=true +server.servlet.encoding.charset=UTF-8 # Force the encoding to the configured charset on HTTP requests and responses. -spring.http.encoding.force=true +server.servlet.encoding.force=true ########################### # Server Properties @@ -117,6 +110,8 @@ spring.main.allow-bean-definition-overriding = true ######################### # Spring Boot Logging levels # +# NOTE: The below settings can be uncommented to debug issues in Spring Boot/WebMVC. +# These "logging.level" settings will also override defaults in "logging.config" below. #logging.level.org.springframework.boot=DEBUG #logging.level.org.springframework.web=DEBUG #logging.level.org.hibernate=ERROR diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml index 359f0a7a3e..0dc968674a 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-addon-validation-services.xml @@ -25,4 +25,8 @@ + + + + diff --git a/dspace-server-webapp/src/main/webapp/index.html b/dspace-server-webapp/src/main/webapp/index.html index 829c614393..c780286107 100644 --- a/dspace-server-webapp/src/main/webapp/index.html +++ b/dspace-server-webapp/src/main/webapp/index.html @@ -6,51 +6,54 @@ * This DSpace version has be customized to include: * * Download file functionality (see new downloadFile() method) * * Improved AuthorizationHeader parsing (see new getAuthorizationHeader() method) -* * Upgraded third party dependencies (JQuery) +* * Upgraded third party dependencies via WebJars +* * Updated to use Bootstrap 4, based loosely on this PR to HAL Browser: + https://github.com/mikekelly/hal-browser/pull/102 --> - The HAL Browser (customized for DSpace Server Webapp) - + The HAL Browser (customized for DSpace) + - - + -