From d02827019b3e7059b6be16c5043cd89a2445e00d Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Jun 2021 13:46:01 -0400 Subject: [PATCH 01/33] [DS-4478] Ported from 6_x to main. --- dspace-api/pom.xml | 8 + .../org/dspace/app/util/Configuration.java | 29 ++- .../org/dspace/app/util/ConfigurationIT.java | 232 ++++++++++++++++++ 3 files changed, 264 insertions(+), 5 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index e3e84cccd7..835c61e92a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -889,6 +889,14 @@ 20180130 + + + com.github.stefanbirkner + system-rules + 1.19.0 + test + + com.opencsv 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..3a38daf7e7 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 @@ -51,6 +51,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("a", "all", false, + "display all values of an array property"); options.addOption("?", "Get help"); options.addOption("h", "help", false, "Get help"); @@ -90,19 +92,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('a')) { + break; // Unless --all 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('a')) { + break; // Unless --all print only one value + } + } } - System.out.println(val); } + System.exit(0); } } 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..8f18c9754e --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java @@ -0,0 +1,232 @@ +/** + * 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 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[] { + "--all", + "--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[] { + "--all", + "--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[] { + "--all", + "--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[] { + "--all", + "--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[] { + "--all", + "--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); + } +} From f058d123cfd20aceb8dc0dcf5d2cdb7614dfdf68 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 14 Feb 2022 12:49:32 -0600 Subject: [PATCH 02/33] Update Docker compose scripts to point at new Entities data & do a full reindex after loading assetstore. --- dspace/src/main/docker-compose/cli.assetstore.yml | 2 +- dspace/src/main/docker-compose/db.entities.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/src/main/docker-compose/cli.assetstore.yml b/dspace/src/main/docker-compose/cli.assetstore.yml index 7773facae6..d6a6a4395f 100644 --- a/dspace/src/main/docker-compose/cli.assetstore.yml +++ b/dspace/src/main/docker-compose/cli.assetstore.yml @@ -24,6 +24,6 @@ services: tar xvfz /tmp/assetstore.tar.gz fi - /dspace/bin/dspace index-discovery + /dspace/bin/dspace index-discovery -b /dspace/bin/dspace oai import /dspace/bin/dspace oai clean-cache diff --git a/dspace/src/main/docker-compose/db.entities.yml b/dspace/src/main/docker-compose/db.entities.yml index 762519f508..8d86f7bb83 100644 --- a/dspace/src/main/docker-compose/db.entities.yml +++ b/dspace/src/main/docker-compose/db.entities.yml @@ -13,7 +13,7 @@ services: image: dspace/dspace-postgres-pgcrypto:loadsql environment: # This SQL is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data - - LOADSQL=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-2021-04-14.sql + - LOADSQL=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/dspace7-entities-data.sql dspace: ### OVERRIDE default 'entrypoint' in 'docker-compose.yml #### # Ensure that the database is ready BEFORE starting tomcat From 18e4fed9edf8f27fe2dc8051bef754623b2cbbe9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 17 Feb 2022 14:51:00 -0600 Subject: [PATCH 03/33] Enable default value for solr.authority.server --- dspace/config/dspace.cfg | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index a7b957bef6..31b4f8a6ce 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1413,9 +1413,12 @@ plugin.selfnamed.org.dspace.content.authority.ChoiceAuthority = \ ## See manual or org.dspace.content.authority.Choices source for descriptions. authority.minconfidence = ambiguous +# Solr Authority index location +# Default is ${solr.server}/authority, unless solr.multicorePrefix is specified +solr.authority.server=${solr.server}/${solr.multicorePrefix}authority + # Configuration settings for ORCID based authority control. -# Uncomment the lines below to enable configuration. -#solr.authority.server=${solr.server}/${solr.multicorePrefix}authority +# Uncomment the lines below to enable configuration #choices.plugin.dc.contributor.author = SolrAuthorAuthority #choices.presentation.dc.contributor.author = authorLookup #authority.controlled.dc.contributor.author = true From 2e4f920d24fafced53fd5679ebc31296f2303279 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 7 Mar 2022 15:03:18 -0600 Subject: [PATCH 04/33] Move "solr.authority.server" to solrauthority.cfg --- dspace/config/dspace.cfg | 4 ---- dspace/config/modules/solrauthority.cfg | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 31b4f8a6ce..168c86d64a 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1413,10 +1413,6 @@ plugin.selfnamed.org.dspace.content.authority.ChoiceAuthority = \ ## See manual or org.dspace.content.authority.Choices source for descriptions. authority.minconfidence = ambiguous -# Solr Authority index location -# Default is ${solr.server}/authority, unless solr.multicorePrefix is specified -solr.authority.server=${solr.server}/${solr.multicorePrefix}authority - # Configuration settings for ORCID based authority control. # Uncomment the lines below to enable configuration #choices.plugin.dc.contributor.author = SolrAuthorAuthority diff --git a/dspace/config/modules/solrauthority.cfg b/dspace/config/modules/solrauthority.cfg index 9ed1855d44..afa1bdd22a 100644 --- a/dspace/config/modules/solrauthority.cfg +++ b/dspace/config/modules/solrauthority.cfg @@ -4,6 +4,10 @@ # These configs are only used by the SOLR authority index # #---------------------------------------------------------------# +# Solr Authority index location +# Default is ${solr.server}/authority, unless solr.multicorePrefix is specified +solr.authority.server=${solr.server}/${solr.multicorePrefix}authority + # Update item metadata displayed values (not the authority keys) # with the lasted cached versions when running the UpdateAuthorities index script #solrauthority.auto-update-items=false From 77b00cea7e84ff9ff7f53277fb1c97ea936cf205 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 16 Mar 2022 09:17:07 -0400 Subject: [PATCH 05/33] Rename Hibernate caches as suggested by Hibernate. #8213 --- dspace/config/hibernate-ehcache-config.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/dspace/config/hibernate-ehcache-config.xml b/dspace/config/hibernate-ehcache-config.xml index 0900ce0b50..e2edf67b60 100644 --- a/dspace/config/hibernate-ehcache-config.xml +++ b/dspace/config/hibernate-ehcache-config.xml @@ -31,20 +31,22 @@ - - + + 6000 - - + + 600 From 7e0becf666c3ca9d04a63837895cd98cb51f4ec5 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 16 Mar 2022 11:50:22 -0400 Subject: [PATCH 06/33] Replace --all with opposite --first, default off, as requested. [DS-4478] #3312 --- .../java/org/dspace/app/util/Configuration.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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 3a38daf7e7..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,8 +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("a", "all", false, - "display all values of an array property"); + 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"); @@ -103,8 +104,8 @@ public class Configuration { if (rawValue.getClass().isArray()) { for (Object value : (Object[]) rawValue) { System.out.println(value.toString()); - if (!cmd.hasOption('a')) { - break; // Unless --all print only one value + if (cmd.hasOption('f')) { + break; // If --first print only one value } } } else { // Not an array @@ -115,8 +116,8 @@ public class Configuration { String[] values = cfg.getArrayProperty(propName); for (String value : values) { System.out.println(value); - if (!cmd.hasOption('a')) { - break; // Unless --all print only one value + if (cmd.hasOption('f')) { + break; // If --first print only one value } } } From c0aee566a0cd35518a5de5aab38a3b5ea9f5134c Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 16 Mar 2022 12:08:10 -0400 Subject: [PATCH 07/33] Oops, fix the tests to understand the new options. #3312 --- .../src/test/java/org/dspace/app/util/ConfigurationIT.java | 5 ----- 1 file changed, 5 deletions(-) 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 index 8f18c9754e..20e48f84de 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java +++ b/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java @@ -94,7 +94,6 @@ public class ConfigurationIT public void testMainAllSingle() { String[] argv; argv = new String[] { - "--all", "--property", SINGLE_PROPERTY }; expectedSystemExit.expectSystemExitWithStatus(0); @@ -123,7 +122,6 @@ public class ConfigurationIT public void testMainAllArray() { String[] argv; argv = new String[] { - "--all", "--property", ARRAY_PROPERTY }; expectedSystemExit.expectSystemExitWithStatus(0); @@ -153,7 +151,6 @@ public class ConfigurationIT public void testMainAllSubstitution() { String[] argv; argv = new String[] { - "--all", "--property", PLACEHOLDER_PROPERTY }; expectedSystemExit.expectSystemExitWithStatus(0); @@ -184,7 +181,6 @@ public class ConfigurationIT // Can it handle a raw property (with substitution placeholders)? String[] argv; argv = new String[] { - "--all", "--property", PLACEHOLDER_PROPERTY, "--raw" }; @@ -215,7 +211,6 @@ public class ConfigurationIT // Can it handle an undefined property? String[] argv; argv = new String[] { - "--all", "--property", MISSING_PROPERTY }; expectedSystemExit.expectSystemExitWithStatus(0); From bdf83c73a685eeba5aa4e6f780d2bf695cab9c07 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 21 Mar 2022 09:54:07 -0400 Subject: [PATCH 08/33] Add tests for use of '--first'. #3312 --- .../org/dspace/app/util/ConfigurationIT.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) 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 index 20e48f84de..388b467e97 100644 --- a/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java +++ b/dspace-api/src/test/java/org/dspace/app/util/ConfigurationIT.java @@ -12,6 +12,7 @@ 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; @@ -224,4 +225,44 @@ public class ConfigurationIT 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); + } } From 85bbcf3dc8fd6c0d4243bf32e1e25e042db87eb3 Mon Sep 17 00:00:00 2001 From: Samuel Date: Fri, 18 Mar 2022 17:44:35 +0100 Subject: [PATCH 09/33] taskid 88773 #8023 Bitstream download filename lose non-latin characters --- .../rest/utils/HttpHeadersInitializer.java | 4 +- .../app/rest/BitstreamRestControllerIT.java | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) 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/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index f07aae876f..efb7d1776d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static java.util.UUID.randomUUID; +import static javax.mail.internet.MimeUtility.encodeText; import static org.apache.commons.codec.CharEncoding.UTF_8; import static org.apache.commons.collections.CollectionUtils.isEmpty; import static org.apache.commons.io.IOUtils.toInputStream; @@ -293,6 +294,53 @@ public class BitstreamRestControllerIT extends AbstractControllerIntegrationTest checkNumberOfStatsRecords(bitstream, 0); } + @Test + public void testBitstreamName() throws Exception { + + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community and one collection + + parentCommunity = CommunityBuilder + .createCommunity(context) + .build(); + + Collection collection = CollectionBuilder + .createCollection(context, parentCommunity) + .build(); + + //2. A public item with a bitstream + + String bitstreamContent = "0123456789"; + String bitstreamName = "ภาษาไทย"; + + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + + Item item = ItemBuilder + .createItem(context, collection) + .build(); + + bitstream = BitstreamBuilder + .createBitstream(context, item, is) + .withName(bitstreamName) + .build(); + } + + context.restoreAuthSystemState(); + + //** WHEN ** + //We download the bitstream + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + //** THEN ** + .andExpect(status().isOk()) + //We expect the content disposition to have the encoded bitstream name + .andExpect(header().string( + "Content-Disposition", + "attachment;filename=\"" + encodeText(bitstreamName) + "\"" + )); + } + @Test public void testBitstreamNotFound() throws Exception { getClient().perform(get("/api/core/bitstreams/" + UUID.randomUUID() + "/content")) From 488fffce56e5badf51cb1e04bc02586552100b11 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Mar 2022 12:10:50 -0400 Subject: [PATCH 10/33] Update deprecated Velocity configuration keys. #8192 --- dspace-api/pom.xml | 1 - dspace-api/src/main/java/org/dspace/core/Email.java | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 3155790e9d..815a455135 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -927,7 +927,6 @@ org.apache.velocity velocity-engine-core - jar 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"); } From 6f78c0b8ba43184aab1166812b97466777e02def Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Mar 2022 12:58:14 -0400 Subject: [PATCH 11/33] Update to Velocity 2.3. #8192 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 815a455135..8dd36c9b19 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -1001,7 +1001,7 @@ org.apache.velocity velocity-engine-core - 2.2 + 2.3 org.xmlunit From 70f85edf9ebfd8b85531a7f71eca12f222169e8b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 5 Jan 2022 10:12:01 -0600 Subject: [PATCH 12/33] Upgrade to h2 v2.x. Requires also updating to Hibernate 5.6.x --- dspace-api/pom.xml | 2 +- .../rdbms/migration/MigrationUtils.java | 7 +-- .../h2/V1.4__Upgrade_to_DSpace_1.4_schema.sql | 6 +-- ...9.26__DS-1582_Metadata_For_All_Objects.sql | 44 +++++++++---------- .../h2/V5.6_2016.08.23__DS-3097.sql | 4 +- ...125-fix-bundle-bitstream-delete-rights.sql | 2 +- .../h2/V6.0_2016.08.23__DS-3097.sql | 4 +- ...DV_place_after_migrating_from_DSpace_5.sql | 3 +- dspace-iiif/pom.xml | 7 +++ dspace-server-webapp/pom.xml | 7 +++ pom.xml | 13 ++++-- 11 files changed, 61 insertions(+), 38 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 6a54a96376..d3085ecdec 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -373,7 +373,7 @@ org.hibernate.javax.persistence hibernate-jpa-2.1-api - 1.0.0.Final + 1.0.2.Final 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/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-iiif/pom.xml b/dspace-iiif/pom.xml index c8f64c6f04..e606523182 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -45,6 +45,13 @@ org.springframework.boot spring-boot-starter-web ${spring-boot.version} + + + + org.hibernate.validator + hibernate-validator + + org.springframework.boot diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 20e6602347..f90ea51b15 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -260,6 +260,13 @@ org.springframework.boot spring-boot-starter-web ${spring-boot.version} + + + + org.hibernate.validator + hibernate-validator + + org.springframework.boot diff --git a/pom.xml b/pom.xml index e03dcb3b07..f050e1e36f 100644 --- a/pom.xml +++ b/pom.xml @@ -22,8 +22,8 @@ 5.2.5.RELEASE 2.2.6.RELEASE 5.2.2.RELEASE - 5.4.10.Final - 6.0.18.Final + 5.6.5.Final + 6.0.23.Final 42.3.3 8.8.1 @@ -1161,6 +1161,13 @@ ${hibernate-validator.version} + + + org.jboss.logging + jboss-logging + 3.4.3.Final + + com.rometools @@ -1691,7 +1698,7 @@ com.h2database h2 - 1.4.187 + 2.1.210 test From a35a6d4a28a6a494b5d9f2855109bc004bc943d8 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 5 Jan 2022 10:12:32 -0600 Subject: [PATCH 13/33] Upgrade to Flyway 8.2.x to support h2 v2 --- dspace-api/pom.xml | 2 +- .../storage/rdbms/EntityTypeServiceInitializer.java | 10 ++++++++++ .../dspace/storage/rdbms/GroupServiceInitializer.java | 10 ++++++++++ .../dspace/storage/rdbms/PostgreSQLCryptoChecker.java | 10 ++++++++++ .../java/org/dspace/storage/rdbms/RegistryUpdater.java | 10 ++++++++++ .../dspace/storage/rdbms/SiteServiceInitializer.java | 10 ++++++++++ 6 files changed, 51 insertions(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index d3085ecdec..572b9f8cf3 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -762,7 +762,7 @@ org.flywaydb flyway-core - 6.5.7 + 8.2.3 diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/EntityTypeServiceInitializer.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/EntityTypeServiceInitializer.java index ebf790900b..e0e41516d0 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/EntityTypeServiceInitializer.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/EntityTypeServiceInitializer.java @@ -49,6 +49,16 @@ public class EntityTypeServiceInitializer implements Callback { } } + /** + * The callback name, Flyway will use this to sort the callbacks alphabetically before executing them + * @return The callback name + */ + @Override + public String getCallbackName() { + // Return class name only (not prepended by package) + return EntityTypeServiceInitializer.class.getSimpleName(); + } + @Override public boolean supports(Event event, org.flywaydb.core.api.callback.Context context) { // Must run AFTER all migrations complete, since it is dependent on Hibernate diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/GroupServiceInitializer.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/GroupServiceInitializer.java index 7338dd75bc..54498a1c64 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/GroupServiceInitializer.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/GroupServiceInitializer.java @@ -51,6 +51,16 @@ public class GroupServiceInitializer implements Callback { } + /** + * The callback name, Flyway will use this to sort the callbacks alphabetically before executing them + * @return The callback name + */ + @Override + public String getCallbackName() { + // Return class name only (not prepended by package) + return GroupServiceInitializer.class.getSimpleName(); + } + /** * Events supported by this callback. * @param event Flyway event diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/PostgreSQLCryptoChecker.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/PostgreSQLCryptoChecker.java index 5798f4254c..5459cc3cc3 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/PostgreSQLCryptoChecker.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/PostgreSQLCryptoChecker.java @@ -97,6 +97,16 @@ public class PostgreSQLCryptoChecker implements Callback { } } + /** + * The callback name, Flyway will use this to sort the callbacks alphabetically before executing them + * @return The callback name + */ + @Override + public String getCallbackName() { + // Return class name only (not prepended by package) + return PostgreSQLCryptoChecker.class.getSimpleName(); + } + /** * Events supported by this callback. * @param event Flyway event diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java index ae8be0988a..c2a9b8a848 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/RegistryUpdater.java @@ -101,6 +101,16 @@ public class RegistryUpdater implements Callback { } + /** + * The callback name, Flyway will use this to sort the callbacks alphabetically before executing them + * @return The callback name + */ + @Override + public String getCallbackName() { + // Return class name only (not prepended by package) + return RegistryUpdater.class.getSimpleName(); + } + /** * Events supported by this callback. * @param event Flyway event diff --git a/dspace-api/src/main/java/org/dspace/storage/rdbms/SiteServiceInitializer.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/SiteServiceInitializer.java index 26e76804e1..872a633146 100644 --- a/dspace-api/src/main/java/org/dspace/storage/rdbms/SiteServiceInitializer.java +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/SiteServiceInitializer.java @@ -73,6 +73,16 @@ public class SiteServiceInitializer implements Callback { } + /** + * The callback name, Flyway will use this to sort the callbacks alphabetically before executing them + * @return The callback name + */ + @Override + public String getCallbackName() { + // Return class name only (not prepended by package) + return SiteServiceInitializer.class.getSimpleName(); + } + /** * Events supported by this callback. * @param event Flyway event From 5a046dca13e8a4c60ad5e4deef27e0fd40adc0ad Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 5 Jan 2022 10:21:25 -0600 Subject: [PATCH 14/33] Fix incorrect StringUtils import --- .../org/dspace/app/rest/RelationshipTypeRestRepositoryIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipTypeRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipTypeRestRepositoryIT.java index a2ed672a61..3295e03593 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipTypeRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipTypeRestRepositoryIT.java @@ -22,13 +22,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.sql.SQLException; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.matcher.EntityTypeMatcher; import org.dspace.app.rest.matcher.RelationshipTypeMatcher; import org.dspace.app.rest.test.AbstractEntityIntegrationTest; import org.dspace.content.RelationshipType; import org.dspace.content.service.EntityTypeService; import org.dspace.content.service.RelationshipTypeService; -import org.h2.util.StringUtils; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; From 531fc8acda9101e16fa7c327a152e2aa5798be49 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 24 Feb 2022 16:38:14 -0600 Subject: [PATCH 15/33] Upgrade to Flyway 8.4.4 --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 572b9f8cf3..b8fae8d86c 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -762,7 +762,7 @@ org.flywaydb flyway-core - 8.2.3 + 8.4.4 From 0595eeddbb4a6d9fe2f008fa4fee7e27f4410008 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 16 Feb 2022 16:35:41 -0600 Subject: [PATCH 16/33] Tiny stability fix to test (fails on Windows). Remove newline from string comparison --- .../org/dspace/iiif/canvasdimension/CanvasDimensionsIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From c4ad834a8a8c3b14c5d8f533a14acc766a3c8b52 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 18 Feb 2022 14:04:04 -0600 Subject: [PATCH 17/33] Cleanup IT logs by loading beans by name. Add note to application.properties on how to enable debug logs --- .../org/dspace/AbstractIntegrationTestWithDatabase.java | 8 +++++--- .../src/main/resources/application.properties | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java index cada4944e3..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,6 +32,7 @@ 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.jdom2.Document; import org.junit.After; @@ -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-server-webapp/src/main/resources/application.properties b/dspace-server-webapp/src/main/resources/application.properties index c695fe2fba..91e2aa73ae 100644 --- a/dspace-server-webapp/src/main/resources/application.properties +++ b/dspace-server-webapp/src/main/resources/application.properties @@ -117,6 +117,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 From 57b19fa71a728a53bce120c6c3c5a18882c191b3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 21 Dec 2021 09:12:52 -0600 Subject: [PATCH 18/33] Bug fix, ensure Solr container is *shutdown* when destroyed --- dspace-api/src/test/java/org/dspace/solr/MockSolrServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-api/src/test/java/org/dspace/solr/MockSolrServer.java b/dspace-api/src/test/java/org/dspace/solr/MockSolrServer.java index e80d5f8e17..aed0c088c3 100644 --- a/dspace-api/src/test/java/org/dspace/solr/MockSolrServer.java +++ b/dspace-api/src/test/java/org/dspace/solr/MockSolrServer.java @@ -171,6 +171,7 @@ public class MockSolrServer { * Discard the embedded Solr container. */ private static synchronized void destroyContainer() { + container.shutdown(); container = null; log.info("SOLR CoreContainer destroyed"); } From 62c0e28f5451dddb7b8f0c34c5af0a8bdca6168b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 1 Mar 2022 17:06:19 -0600 Subject: [PATCH 19/33] Remove custom Postgres Dialect. Replace with DatabaseAwareLobType --- .../org/dspace/authorize/ResourcePolicy.java | 2 +- .../org/dspace/content/MetadataValue.java | 2 +- .../rdbms/hibernate/DatabaseAwareLobType.java | 57 ++++++++++++++++ .../postgres/DSpacePostgreSQL82Dialect.java | 67 ------------------- dspace/config/dspace.cfg | 4 +- dspace/config/local.cfg.EXAMPLE | 4 +- 6 files changed, 63 insertions(+), 73 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/DatabaseAwareLobType.java delete mode 100644 dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/postgres/DSpacePostgreSQL82Dialect.java 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/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/storage/rdbms/hibernate/DatabaseAwareLobType.java b/dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/DatabaseAwareLobType.java new file mode 100644 index 0000000000..95939f9902 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/storage/rdbms/hibernate/DatabaseAwareLobType.java @@ -0,0 +1,57 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.storage.rdbms.hibernate; + +import org.apache.commons.lang.StringUtils; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.hibernate.type.AbstractSingleColumnStandardBasicType; +import org.hibernate.type.descriptor.java.StringTypeDescriptor; +import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; +import org.hibernate.type.descriptor.sql.LongVarcharTypeDescriptor; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; + +/** + * A Hibernate @Type used to properly support the CLOB in both Postgres and Oracle. + * PostgreSQL doesn't have a CLOB type, instead it's a TEXT field. + * Normally, you'd use org.hibernate.type.TextType to support TEXT, but that won't work for Oracle. + * https://github.com/hibernate/hibernate-orm/blob/5.6/hibernate-core/src/main/java/org/hibernate/type/TextType.java + * + * This Type checks if we are using PostgreSQL. + * If so, it configures Hibernate to map CLOB to LongVarChar (same as org.hibernate.type.TextType) + * If not, it uses default CLOB (which works for other databases). + */ +public class DatabaseAwareLobType extends AbstractSingleColumnStandardBasicType { + + 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/config/dspace.cfg b/dspace/config/dspace.cfg index d5c28da096..99e49e77e4 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -80,9 +80,9 @@ db.url = jdbc:postgresql://localhost:5432/dspace db.driver = org.postgresql.Driver # Database Dialect (for Hibernate) -# * For Postgres: org.dspace.storage.rdbms.hibernate.postgres.DSpacePostgreSQL82Dialect +# * For Postgres: org.hibernate.dialect.PostgreSQL94Dialect # * For Oracle: org.hibernate.dialect.Oracle10gDialect -db.dialect = org.dspace.storage.rdbms.hibernate.postgres.DSpacePostgreSQL82Dialect +db.dialect = org.hibernate.dialect.PostgreSQL94Dialect # Database username and password db.username = dspace diff --git a/dspace/config/local.cfg.EXAMPLE b/dspace/config/local.cfg.EXAMPLE index 167d2d45da..67c03808c4 100644 --- a/dspace/config/local.cfg.EXAMPLE +++ b/dspace/config/local.cfg.EXAMPLE @@ -80,9 +80,9 @@ db.url = jdbc:postgresql://localhost:5432/dspace db.driver = org.postgresql.Driver # Database Dialect (for Hibernate) -# * For Postgres: org.dspace.storage.rdbms.hibernate.postgres.DSpacePostgreSQL82Dialect +# * For Postgres: org.hibernate.dialect.PostgreSQL94Dialect # * For Oracle: org.hibernate.dialect.Oracle10gDialect -db.dialect = org.dspace.storage.rdbms.hibernate.postgres.DSpacePostgreSQL82Dialect +db.dialect = org.hibernate.dialect.PostgreSQL94Dialect # Database username and password db.username = dspace From 967e36af7a9881e6c152347e594e760807f99c3f Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 2 Mar 2022 13:52:24 -0600 Subject: [PATCH 20/33] Fix Item & Bundle tests which check order of results. H2 2.x + Hibernate 5.x returns results ordered by UUID when unspecified. --- .../org/dspace/builder/BitstreamBuilder.java | 7 +-- .../app/rest/BitstreamControllerIT.java | 37 ++++++----- .../app/rest/BitstreamRestRepositoryIT.java | 31 ++++++--- .../app/rest/BundleRestRepositoryIT.java | 28 ++++----- .../app/rest/CollectionRestRepositoryIT.java | 8 ++- .../dspace/app/rest/ItemRestRepositoryIT.java | 58 ++++++++++------- .../app/rest/iiif/IIIFControllerIT.java | 63 +++++++++---------- 7 files changed, 131 insertions(+), 101 deletions(-) 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 283091778e..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(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java index ca3c05ec30..4b1124071a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamControllerIT.java @@ -16,6 +16,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import org.apache.commons.codec.CharEncoding; @@ -144,47 +146,48 @@ public class BitstreamControllerIT extends AbstractControllerIntegrationTest { String bitstreamContent = "ThisIsSomeDummyText"; - Bundle bundle1 = BundleBuilder.createBundle(context, publicItem1) + List bundles = new ArrayList(); + bundles.add(BundleBuilder.createBundle(context, publicItem1) .withName("TEST FIRST BUNDLE") - .build(); + .build()); Bitstream bitstream = null; try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { bitstream = BitstreamBuilder. - createBitstream(context, bundle1, is) + createBitstream(context, bundles.get(0), is) .withName("Bitstream") .withDescription("description") .withMimeType("text/plain") .build(); } - Bundle bundle2 = BundleBuilder.createBundle(context, publicItem1) + bundles.add(BundleBuilder.createBundle(context, publicItem1) .withName("TEST SECOND BUNDLE") .withBitstream(bitstream) - .build(); + .build()); context.restoreAuthSystemState(); + // While in DSpace code, Bundles are *unordered*, in Hibernate v5 + H2 v2.x, they are returned sorted by UUID. + // So, we reorder this list of created Bundles by UUID to get their expected return order. + // NOTE: Once on Hibernate v6, this might need "toString()" removed as it may sort UUIDs based on RFC 4412. + Comparator compareByUUID = Comparator.comparing(b -> b.getID().toString()); + bundles.sort(compareByUUID); + String token = getAuthToken(admin.getEmail(), password); + // Expect only the first Bundle to be returned getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/bundle") .param("projection", "full")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( - BundleMatcher.matchBundle(bundle1.getName(), - bundle1.getID(), - bundle1.getHandle(), - bundle1.getType(), - bundle1.getBitstreams()) - - ))).andExpect(jsonPath("$", Matchers.not( - BundleMatcher.matchBundle(bundle2.getName(), - bundle2.getID(), - bundle2.getHandle(), - bundle2.getType(), - bundle2.getBitstreams()) + BundleMatcher.matchBundle(bundles.get(0).getName(), + bundles.get(0).getID(), + bundles.get(0).getHandle(), + bundles.get(0).getType(), + bundles.get(0).getBitstreams()) ))); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java index fd95e7f584..f9c1e469fc 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -20,6 +20,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.InputStream; +import java.util.Comparator; +import java.util.List; import java.util.UUID; import org.apache.commons.codec.CharEncoding; @@ -45,6 +47,7 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.service.BitstreamFormatService; import org.dspace.content.service.BitstreamService; +import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -67,6 +70,10 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest @Autowired private GroupService groupService; + + @Autowired + private ItemService itemService; + @Test public void findAllTest() throws Exception { //We turn off the authorization system in order to create the structure as defined below @@ -1480,11 +1487,19 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .build(); } - Bundle secondBundle = BundleBuilder.createBundle(context, publicItem1) - .withName("second bundle") - .withBitstream(bitstream).build(); + // Add default content bundle to list of bundles + List bundles = itemService.getBundles(publicItem1, Constants.CONTENT_BUNDLE_NAME); - Bundle bundle = bitstream.getBundles().get(0); + // Add this bitstream to a second bundle & append to list of bundles + bundles.add(BundleBuilder.createBundle(context, publicItem1) + .withName("second bundle") + .withBitstream(bitstream).build()); + + // While in DSpace code, Bundles are *unordered*, in Hibernate v5 + H2 v2.x, they are returned sorted by UUID. + // So, we reorder this list of created Bundles by UUID to get their expected return order. + // NOTE: Once on Hibernate v6, this might need "toString()" removed as it may sort UUIDs based on RFC 4412. + Comparator compareByUUID = Comparator.comparing(b -> b.getID().toString()); + bundles.sort(compareByUUID); //Get bundle should contain the first bundle in the list getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/bundle")) @@ -1492,10 +1507,10 @@ public class BitstreamRestRepositoryIT extends AbstractControllerIntegrationTest .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", BundleMatcher.matchProperties( - bundle.getName(), - bundle.getID(), - bundle.getHandle(), - bundle.getType() + bundles.get(0).getName(), + bundles.get(0).getID(), + bundles.get(0).getHandle(), + bundles.get(0).getType() ) )); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java index 884d551582..96385095a2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BundleRestRepositoryIT.java @@ -397,26 +397,24 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest { public void getBitstreamsForBundle() throws Exception { context.turnOffAuthorisationSystem(); + bundle1 = BundleBuilder.createBundle(context, item) + .withName("testname") + .build(); + String bitstreamContent = "Dummy content"; try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { - bitstream1 = BitstreamBuilder.createBitstream(context, item, is) + bitstream1 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName()) .withName("Bitstream") .withDescription("Description") .withMimeType("text/plain") .build(); - bitstream2 = BitstreamBuilder.createBitstream(context, item, is) + bitstream2 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName()) .withName("Bitstream2") .withDescription("Description2") .withMimeType("text/plain") .build(); } - bundle1 = BundleBuilder.createBundle(context, item) - .withName("testname") - .withBitstream(bitstream1) - .withBitstream(bitstream2) - .build(); - context.restoreAuthSystemState(); getClient().perform(get("/api/core/bundles/" + bundle1.getID() + "/bitstreams") @@ -465,26 +463,24 @@ public class BundleRestRepositoryIT extends AbstractControllerIntegrationTest { public void patchMoveBitstreams() throws Exception { context.turnOffAuthorisationSystem(); + bundle1 = BundleBuilder.createBundle(context, item) + .withName("testname") + .build(); + String bitstreamContent = "Dummy content"; try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { - bitstream1 = BitstreamBuilder.createBitstream(context, item, is) + bitstream1 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName()) .withName("Bitstream") .withDescription("Description") .withMimeType("text/plain") .build(); - bitstream2 = BitstreamBuilder.createBitstream(context, item, is) + bitstream2 = BitstreamBuilder.createBitstream(context, item, is, bundle1.getName()) .withName("Bitstream2") .withDescription("Description2") .withMimeType("text/plain") .build(); } - bundle1 = BundleBuilder.createBundle(context, item) - .withName("testname") - .withBitstream(bitstream1) - .withBitstream(bitstream2) - .build(); - context.restoreAuthSystemState(); getClient().perform(get("/api/core/bundles/" + bundle1.getID() + "/bitstreams") diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java index 92fdecf824..6ae33cb280 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java @@ -2421,9 +2421,11 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes .build(); List items = new ArrayList(); - // This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC - // and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 ) - Comparator compareByUUID = Comparator.comparing(i -> i.getID()); + // Hibernate 5.x's org.hibernate.dialect.H2Dialect sorts UUIDs as if they are Strings. + // So, we must compare UUIDs as if they are strings. + // In Hibernate 6, the H2Dialect has been updated with native UUID type support, at which point + // we'd need to update the below comparator to compare them as java.util.UUID (which sorts based on RFC 4412). + Comparator compareByUUID = Comparator.comparing(i -> i.getID().toString()); Item item0 = ItemBuilder.createItem(context, collection).withTitle("Item 0").build(); items.add(item0); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java index 372ab084dd..95ec537727 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java @@ -123,9 +123,11 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build(); List items = new ArrayList(); - // This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC - // and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 ) - Comparator compareByUUID = Comparator.comparing(i -> i.getID()); + // Hibernate 5.x's org.hibernate.dialect.H2Dialect sorts UUIDs as if they are Strings. + // So, we must compare UUIDs as if they are strings. + // In Hibernate 6, the H2Dialect has been updated with native UUID type support, at which point + // we'd need to update the below comparator to compare them as java.util.UUID (which sorts based on RFC 4412). + Comparator compareByUUID = Comparator.comparing(i -> i.getID().toString()); //2. Three public items that are readable by Anonymous with different subjects Item publicItem1 = ItemBuilder.createItem(context, col1) @@ -204,9 +206,11 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .build(); List items = new ArrayList(); - // This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC - // and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 ) - Comparator compareByUUID = Comparator.comparing(i -> i.getID()); + // Hibernate 5.x's org.hibernate.dialect.H2Dialect sorts UUIDs as if they are Strings. + // So, we must compare UUIDs as if they are strings. + // In Hibernate 6, the H2Dialect has been updated with native UUID type support, at which point + // we'd need to update the below comparator to compare them as java.util.UUID (which sorts based on RFC 4412). + Comparator compareByUUID = Comparator.comparing(i -> i.getID().toString()); //2. Three public items that are readable by Anonymous with different subjects Item publicItem1 = ItemBuilder.createItem(context, col1) @@ -3213,16 +3217,23 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { Item item = ItemBuilder.createItem(context, collection).withTitle("Item").build(); - Bundle bundle0 = BundleBuilder.createBundle(context, item).withName("Bundle 0").build(); - Bundle bundle1 = BundleBuilder.createBundle(context, item).withName("Bundle 1").build(); - Bundle bundle2 = BundleBuilder.createBundle(context, item).withName("Bundle 2").build(); - Bundle bundle3 = BundleBuilder.createBundle(context, item).withName("Bundle 3").build(); - Bundle bundle4 = BundleBuilder.createBundle(context, item).withName("Bundle 4").build(); - Bundle bundle5 = BundleBuilder.createBundle(context, item).withName("Bundle 5").build(); - Bundle bundle6 = BundleBuilder.createBundle(context, item).withName("Bundle 6").build(); - Bundle bundle7 = BundleBuilder.createBundle(context, item).withName("Bundle 7").build(); - Bundle bundle8 = BundleBuilder.createBundle(context, item).withName("Bundle 8").build(); - Bundle bundle9 = BundleBuilder.createBundle(context, item).withName("Bundle 9").build(); + List bundles = new ArrayList(); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 0").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 1").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 2").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 3").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 4").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 5").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 6").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 7").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 8").build()); + bundles.add(BundleBuilder.createBundle(context, item).withName("Bundle 9").build()); + + // While in DSpace code, Bundles are *unordered*, in Hibernate v5 + H2 v2.x, they are returned sorted by UUID. + // So, we reorder this list of created Bundles by UUID to get their expected pagination ordering. + // NOTE: Once on Hibernate v6, this might need "toString()" removed as it may sort UUIDs based on RFC 4412. + Comparator compareByUUID = Comparator.comparing(b -> b.getID().toString()); + bundles.sort(compareByUUID); context.restoreAuthSystemState(); @@ -3232,11 +3243,16 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest { .andExpect(status().isOk()) .andExpect(jsonPath("$", ItemMatcher.matchItemProperties(item))) .andExpect(jsonPath("$._embedded.bundles._embedded.bundles",Matchers.containsInAnyOrder( - BundleMatcher.matchProperties(bundle0.getName(), bundle0.getID(), bundle0.getHandle(), bundle0.getType()), - BundleMatcher.matchProperties(bundle1.getName(), bundle1.getID(), bundle1.getHandle(), bundle1.getType()), - BundleMatcher.matchProperties(bundle2.getName(), bundle2.getID(), bundle2.getHandle(), bundle2.getType()), - BundleMatcher.matchProperties(bundle3.getName(), bundle3.getID(), bundle3.getHandle(), bundle3.getType()), - BundleMatcher.matchProperties(bundle4.getName(), bundle4.getID(), bundle4.getHandle(), bundle4.getType()) + BundleMatcher.matchProperties(bundles.get(0).getName(), bundles.get(0).getID(), bundles.get(0).getHandle(), + bundles.get(0).getType()), + BundleMatcher.matchProperties(bundles.get(1).getName(), bundles.get(1).getID(), bundles.get(1).getHandle(), + bundles.get(1).getType()), + BundleMatcher.matchProperties(bundles.get(2).getName(), bundles.get(2).getID(), bundles.get(2).getHandle(), + bundles.get(2).getType()), + BundleMatcher.matchProperties(bundles.get(3).getName(), bundles.get(3).getID(), bundles.get(3).getHandle(), + bundles.get(3).getType()), + BundleMatcher.matchProperties(bundles.get(4).getName(), bundles.get(4).getID(), bundles.get(4).getHandle(), + bundles.get(4).getType()) ))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString("/api/core/items/" + item.getID()))) .andExpect(jsonPath("$._embedded.bundles.page.size", is(5))) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/iiif/IIIFControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/iiif/IIIFControllerIT.java index 71090cd70f..beafcb9622 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/iiif/IIIFControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/iiif/IIIFControllerIT.java @@ -469,40 +469,39 @@ public class IIIFControllerIT extends AbstractControllerIntegrationTest { .withMimeType("image/tiff") .build(); } - context.restoreAuthSystemState(); - // expect structures elements with label and canvas id. + + // Expected structures elements based on the above test content + // NOTE: we cannot guarantee the order of Bundles in the Manifest, therefore this test has to simply check + // that each Bundle exists in the manifest with Canvases corresponding to each bitstream. getClient().perform(get("/iiif/" + publicItem1.getID() + "/manifest")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.@context", is("http://iiif.io/api/presentation/2/context.json"))) - .andExpect(jsonPath("$.sequences[0].canvases[0].@id", - Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c0"))) - .andExpect(jsonPath("$.sequences[0].canvases[0].label", is("Global 1"))) - .andExpect(jsonPath("$.sequences[0].canvases[0].width", is(2000))) - .andExpect(jsonPath("$.sequences[0].canvases[0].height", is(3000))) - .andExpect(jsonPath("$.sequences[0].canvases[1].label", is("Global 2"))) - .andExpect(jsonPath("$.sequences[0].canvases[2].label", is("Global 3"))) - .andExpect(jsonPath("$.structures[0].@id", - Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0"))) - .andExpect(jsonPath("$.structures[0].label", is("Table of Contents"))) - .andExpect(jsonPath("$.structures[0].viewingHint", is("top"))) - .andExpect(jsonPath("$.structures[0].ranges[0]", - Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-0"))) - .andExpect(jsonPath("$.structures[0].ranges[1]", - Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-1"))) - .andExpect(jsonPath("$.structures[1].@id", - Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-0"))) - .andExpect(jsonPath("$.structures[1].label", is("ORIGINAL"))) - .andExpect(jsonPath("$.structures[1].canvases[0]", - Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c0"))) - .andExpect(jsonPath("$.structures[2].@id", - Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-1"))) - .andExpect(jsonPath("$.structures[2].label", is("IIIF"))) - .andExpect(jsonPath("$.structures[2].canvases[0]", - Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c1"))) - .andExpect(jsonPath("$.structures[2].canvases[1]", - Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c2"))) - .andExpect(jsonPath("$.service").exists()); + .andExpect(status().isOk()) + .andExpect(jsonPath("$.@context", is("http://iiif.io/api/presentation/2/context.json"))) + // should contain 3 canvases, corresponding to each bitstream + .andExpect(jsonPath("$.sequences[0].canvases[*].label", + Matchers.contains("Global 1", "Global 2", "Global 3"))) + + // First structure should be a Table of Contents + .andExpect(jsonPath("$.structures[0].@id", + Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0"))) + .andExpect(jsonPath("$.structures[0].label", is("Table of Contents"))) + .andExpect(jsonPath("$.structures[0].viewingHint", is("top"))) + .andExpect(jsonPath("$.structures[0].ranges[0]", + Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-0"))) + .andExpect(jsonPath("$.structures[0].ranges[1]", + Matchers.endsWith("/iiif/" + publicItem1.getID() + "/manifest/range/r0-1"))) + + // Should contain a structure with label=IIIF, corresponding to IIIF bundle + // It should have exactly 2 canvases (corresponding to 2 bitstreams) + .andExpect(jsonPath("$.structures[?(@.label=='IIIF')].canvases[0]").exists()) + .andExpect(jsonPath("$.structures[?(@.label=='IIIF')].canvases[1]").exists()) + .andExpect(jsonPath("$.structures[?(@.label=='IIIF')].canvases[2]").doesNotExist()) + + // Should contain a structure with label=ORIGINAL, corresponding to ORIGINAL bundle + // It should have exactly 1 canvas (corresponding to 1 bitstream) + .andExpect(jsonPath("$.structures[?(@.label=='ORIGINAL')].canvases[0]").exists()) + .andExpect(jsonPath("$.structures[?(@.label=='ORIGINAL')].canvases[1]").doesNotExist()) + .andExpect(jsonPath("$.service").exists()); } @Test From b4915868ddd09e87c975445b898b6c19d4b76a3d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 7 Mar 2022 13:06:00 -0600 Subject: [PATCH 21/33] Entity Types now come back in a different order. Updating pagination tests based on new ordering --- .../app/rest/EntityTypeRestRepositoryIT.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java index 93aacb60e4..740a2c0dc3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EntityTypeRestRepositoryIT.java @@ -86,13 +86,13 @@ public class EntityTypeRestRepositoryIT extends AbstractEntityIntegrationTest { .andExpect(jsonPath("$._links.self.href", containsString("api/core/entitytypes"))) //We have 4 facets in the default configuration, they need to all be present in the embedded section .andExpect(jsonPath("$._embedded.entitytypes", containsInAnyOrder( - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, - Constants.ENTITY_TYPE_NONE)), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Journal")), EntityTypeMatcher - .matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Publication")), - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Person")), - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Project")), - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "OrgUnit")) + .matchEntityTypeEntry(entityTypeService.findByEntityType(context, "JournalIssue")), + EntityTypeMatcher + .matchEntityTypeEntry(entityTypeService.findByEntityType(context, "JournalVolume")), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "OrgUnit")), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Person")) ))); getClient().perform(get("/api/core/entitytypes").param("size", "5").param("page", "1")) @@ -108,11 +108,12 @@ public class EntityTypeRestRepositoryIT extends AbstractEntityIntegrationTest { .andExpect(jsonPath("$._links.self.href", containsString("api/core/entitytypes"))) //We have 4 facets in the default configuration, they need to all be present in the embedded section .andExpect(jsonPath("$._embedded.entitytypes", containsInAnyOrder( - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Journal")), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Project")), EntityTypeMatcher - .matchEntityTypeEntry(entityTypeService.findByEntityType(context, "JournalVolume")), + .matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Publication")), EntityTypeMatcher - .matchEntityTypeEntry(entityTypeService.findByEntityType(context, "JournalIssue")) + .matchEntityTypeEntry(entityTypeService.findByEntityType(context, + Constants.ENTITY_TYPE_NONE)) ))); } @@ -137,10 +138,11 @@ public class EntityTypeRestRepositoryIT extends AbstractEntityIntegrationTest { .param("size", "3")) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.entitytypes", containsInAnyOrder( - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Publication")), - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Person")), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Journal")), EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, - Constants.ENTITY_TYPE_NONE)) + "JournalIssue")), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, + "JournalVolume")) ))) .andExpect(jsonPath("$._links.first.href", Matchers.allOf( Matchers.containsString("/api/core/entitytypes?"), @@ -165,7 +167,7 @@ public class EntityTypeRestRepositoryIT extends AbstractEntityIntegrationTest { .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.entitytypes", containsInAnyOrder( EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "OrgUnit")), - EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Journal")), + EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Person")), EntityTypeMatcher.matchEntityTypeEntry(entityTypeService.findByEntityType(context, "Project")) ))) .andExpect(jsonPath("$._links.first.href", Matchers.allOf( From c7a8e1b3b54ef5654b049b965f424a856f3e5d4e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 23 Mar 2022 11:52:14 -0500 Subject: [PATCH 22/33] Update to latest Solr to prove it doesn't break SWORDv2 --- dspace-rest/pom.xml | 11 ++++++++ pom.xml | 68 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 4433cffd33..5377dab901 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -102,6 +102,17 @@ com.fasterxml.jackson.module jackson-module-jaxb-annotations ${jackson.version} + + + + jakarta.activation + jakarta.activation-api + + + jakarta.xml.bind + jakarta.xml.bind-api + + diff --git a/pom.xml b/pom.xml index f050e1e36f..ff74fb8b6b 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.6.5.Final 6.0.23.Final 42.3.3 - 8.8.1 + 8.11.1 3.4.0 2.10.0 @@ -57,7 +57,7 @@ https://jena.apache.org/documentation/migrate_jena2_jena3.html --> 2.13.0 - 2.30.1 + 2.35 UTF-8 @@ -1106,6 +1106,13 @@ org.postgresql postgresql ${postgresql.driver.version} + + + + org.checkerframework + checker-qual + + + + + com.rometools + rome + + + org.tukaani + xz + com.google.protobuf protobuf-java - net.sf.ehcache - ehcache-core + com.healthmarketscience.jackcess + jackcess - jakarta.ws.rs - jakarta.ws.rs-api + com.healthmarketscience.jackcess + jackcess-encrypt - javax.xml.soap - javax.xml.soap-api + com.fasterxml.woodstox + woodstox-core + + + org.apache.james + apache-mime4j-dom + + + org.apache.commons + commons-compress + + + org.tallison + isoparser + + + org.tallison + metadata-extractor + + + com.epam + parso org.jvnet.staxex stax-ex + + + com.sun.activation + jakarta.activation + + + org.checkerframework + checker-qual + + xom From 84ffe9db9e7a91de2dc247cb25a9fa7c011c2498 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Mar 2022 11:01:06 -0500 Subject: [PATCH 23/33] Upgrade Apache POI --- .../dspace/app/mediafilter/ExcelFilter.java | 2 +- .../dspace/app/mediafilter/PoiWordFilter.java | 6 ++-- .../app/mediafilter/PowerPointFilter.java | 28 +++++++++---------- pom.xml | 18 ++---------- 4 files changed, 18 insertions(+), 36 deletions(-) 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 index c17d168c04..1ebd7b8715 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/ExcelFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/ExcelFilter.java @@ -12,8 +12,8 @@ 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.extractor.POITextExtractor; import org.apache.poi.hssf.extractor.ExcelExtractor; import org.apache.poi.xssf.extractor.XSSFExcelExtractor; import org.dspace.content.Item; 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 index 8c198c4477..942eafa04d 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.java @@ -12,10 +12,8 @@ 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.apache.poi.extractor.POITextExtractor; import org.dspace.content.Item; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,7 +53,7 @@ public class PoiWordFilter // get input stream from bitstream, pass to filter, get string back POITextExtractor extractor = ExtractorFactory.createExtractor(source); text = extractor.getText(); - } catch (IOException | OpenXML4JException | XmlException e) { + } catch (IOException e) { System.err.format("Invalid File Format: %s%n", e.getMessage()); LOG.error("Unable to parse the bitstream: ", e); throw e; 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 index 86b7096f68..a825064353 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java @@ -11,10 +11,10 @@ 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.apache.poi.extractor.POITextExtractor; +import org.apache.poi.sl.extractor.SlideShowExtractor; +import org.apache.poi.xslf.extractor.XSLFExtractor; import org.dspace.content.Item; /* @@ -70,7 +70,6 @@ public class PowerPointFilter extends MediaFilter { try { String extractedText = null; - new ExtractorFactory(); POITextExtractor pptExtractor = ExtractorFactory .createExtractor(source); @@ -78,17 +77,16 @@ public class PowerPointFilter extends MediaFilter { // 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 (pptExtractor instanceof XSLFExtractor) { + // Extract text from both slides and notes + ((XSLFExtractor) pptExtractor).setNotesByDefault(true); + ((XSLFExtractor) pptExtractor).setSlidesByDefault(true); + extractedText = ((XSLFExtractor) pptExtractor).getText(); + } else if (pptExtractor instanceof SlideShowExtractor) { // Legacy PowerPoint files + // Extract text from both slides and notes + ((SlideShowExtractor) pptExtractor).setNotesByDefault(true); + ((SlideShowExtractor) pptExtractor).setSlidesByDefault(true); + extractedText = ((SlideShowExtractor) pptExtractor).getText(); } if (extractedText != null) { // if verbose flag is set, print out extracted text diff --git a/pom.xml b/pom.xml index ff74fb8b6b..fbdcd098d2 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 9.4.41.v20210516 2.17.1 2.0.24 - 3.17 + 5.2.2 1.18.0 1.7.25 @@ -1611,25 +1611,11 @@ org.apache.poi poi-ooxml ${poi-version} - - - - com.github.virtuald - curvesapi - - org.apache.poi - poi-ooxml-schemas + poi-ooxml-full ${poi-version} - - - - org.apache.xmlbeans - xmlbeans - - org.apache.xmlbeans From b894071c1666cf24be7281136a2f8453da643b20 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Mar 2022 16:12:46 -0500 Subject: [PATCH 24/33] Switch to SolrJ & Solr-Core. We don't actually use Solr-Cell. Upgrade Tika dependencies --- dspace-api/pom.xml | 59 +++--------- dspace-server-webapp/pom.xml | 75 ++++++--------- dspace/modules/server/pom.xml | 25 ----- dspace/solr/search/conf/solrconfig.xml | 3 - pom.xml | 125 ++++++++----------------- 5 files changed, 81 insertions(+), 206 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index b8fae8d86c..009b18d0ac 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -394,7 +394,7 @@ org.ow2.asm asm-commons - + org.bouncycastle bcpkix-jdk15on @@ -606,27 +606,13 @@ 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 @@ -654,39 +640,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 @@ -707,6 +664,16 @@ xmlbeans + + + org.apache.tika + tika-core + + + org.apache.tika + tika-parsers-standard-package + + com.maxmind.geoip2 geoip2 diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index f90ea51b15..4677366a2c 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -371,13 +371,12 @@ dspace-services + + org.dspace dspace-iiif - - - org.dspace dspace-oai @@ -423,6 +422,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 @@ -495,50 +520,6 @@ - - - org.springframework.boot - spring-boot-starter-cache - ${spring-boot.version} - - - - javax.cache - cache-api - 1.1.0 - - - - org.ehcache - ehcache - ${ehcache.version} - - - 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/modules/server/pom.xml b/dspace/modules/server/pom.xml index c789cf5054..ad47074392 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -324,31 +324,6 @@ just adding new jar in the classloader mockito-inline test - - 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/solr/search/conf/solrconfig.xml b/dspace/solr/search/conf/solrconfig.xml index 204c1a056e..8d45105df5 100644 --- a/dspace/solr/search/conf/solrconfig.xml +++ b/dspace/solr/search/conf/solrconfig.xml @@ -30,9 +30,6 @@ regex='icu4j-.*\.jar'/> - - ${solr.data.dir:} diff --git a/pom.xml b/pom.xml index fbdcd098d2..c53a58d105 100644 --- a/pom.xml +++ b/pom.xml @@ -36,12 +36,15 @@ 2.3.1 1.1.0 - 9.4.41.v20210516 + 9.4.44.v20210927 2.17.1 2.0.24 5.2.2 1.18.0 1.7.25 + 2.3.0 + + 1.70 @@ -1115,28 +1118,6 @@ - - - org.apache.zookeeper - zookeeper - 3.4.14 - - - - - org.apache.james - apache-mime4j-core - 0.8.3 - - - - - org.ow2.asm - asm - 8.0.1 - - org.hibernate hibernate-core @@ -1293,7 +1274,7 @@ org.apache.solr - solr-cell + solr-solrj ${solr.client.version} @@ -1301,69 +1282,43 @@ lucene-core ${solr.client.version} - + + org.apache.tika - tika-parsers - 1.27 - - - - - com.rometools - rome - - - org.tukaani - xz - - - com.google.protobuf - protobuf-java - - - com.healthmarketscience.jackcess - jackcess - - - com.healthmarketscience.jackcess - jackcess-encrypt - - - com.fasterxml.woodstox - woodstox-core - - - org.apache.james - apache-mime4j-dom - - - org.apache.commons - commons-compress - - - org.tallison - isoparser - - - org.tallison - metadata-extractor - - - com.epam - parso - - - org.jvnet.staxex - stax-ex - - - - com.sun.activation - jakarta.activation - - + tika-core + ${tika.version} + + org.apache.tika + tika-parsers-standard-package + ${tika.version} + + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + + + + org.apache.james + apache-mime4j-core + 0.8.4 + + + + org.ow2.asm + asm + 8.0.1 + + @@ -1401,7 +1356,7 @@ org.slf4j slf4j-log4j12 - + org.apache.commons commons-csv From e49a1d86af4df9829b39e3775be99d24483d878c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Mar 2022 16:13:38 -0500 Subject: [PATCH 25/33] Fix compilation issues. Old log4j v1 update to v2 --- .../org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java | 5 +++-- .../main/java/org/dspace/content/logic/DefaultFilter.java | 5 +++-- .../dspace/content/logic/condition/InCommunityCondition.java | 5 +++-- .../dspace/content/logic/condition/IsWithdrawnCondition.java | 5 +++-- .../content/logic/condition/MetadataValueMatchCondition.java | 5 +++-- .../logic/condition/MetadataValuesMatchCondition.java | 5 +++-- .../content/logic/condition/ReadableByGroupCondition.java | 5 +++-- .../java/org/dspace/google/GoogleAsyncEventListener.java | 5 +++-- 8 files changed, 24 insertions(+), 16 deletions(-) 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/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/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; From a0438e51addb61aef44d3ced9b11ad22ea600896 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Mar 2022 09:20:37 -0500 Subject: [PATCH 26/33] Remove old version of XMLBeans. We don't use it directly and Tika brings in a new version --- dspace-api/pom.xml | 4 ---- dspace/modules/additions/pom.xml | 4 ---- pom.xml | 5 ----- 3 files changed, 13 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 009b18d0ac..eb0004b3ef 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -659,10 +659,6 @@ lucene-analyzers-stempel test - - org.apache.xmlbeans - xmlbeans - diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 9df5c8ef02..2abc12b529 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -273,10 +273,6 @@ lucene-analyzers-stempel test - - org.apache.xmlbeans - xmlbeans - junit diff --git a/pom.xml b/pom.xml index c53a58d105..9442ef1889 100644 --- a/pom.xml +++ b/pom.xml @@ -1572,11 +1572,6 @@ poi-ooxml-full ${poi-version} - - org.apache.xmlbeans - xmlbeans - 3.1.0 - xalan xalan From 508883bec8f1a16512abdd3a120665db1d61ede7 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Mar 2022 13:10:30 -0500 Subject: [PATCH 27/33] Replace format specific POI filters with TikaTextExtractionFilter --- dspace-api/pom.xml | 4 - .../dspace/app/mediafilter/ExcelFilter.java | 99 ---------- .../dspace/app/mediafilter/HTMLFilter.java | 82 -------- .../org/dspace/app/mediafilter/PDFFilter.java | 137 ------------- .../dspace/app/mediafilter/PoiWordFilter.java | 70 ------- .../app/mediafilter/PowerPointFilter.java | 111 ----------- .../mediafilter/TikaTextExtractionFilter.java | 178 +++++++++++++++++ .../indexobject/IndexFactoryImpl.java | 1 - .../app/mediafilter/PoiWordFilterTest.java | 181 ------------------ .../TikaTextExtractionFilterTest.java | 177 +++++++++++++++++ .../org/dspace/app/mediafilter/test.html | 53 +++++ .../org/dspace/app/mediafilter/test.pdf | Bin 0 -> 56812 bytes .../org/dspace/app/mediafilter/test.txt | 13 ++ dspace/config/dspace.cfg | 54 +++--- pom.xml | 21 -- 15 files changed, 451 insertions(+), 730 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/ExcelFilter.java delete mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/HTMLFilter.java delete mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/PDFFilter.java delete mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.java delete mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java create mode 100644 dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java delete mode 100644 dspace-api/src/test/java/org/dspace/app/mediafilter/PoiWordFilterTest.java create mode 100644 dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.html create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pdf create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.txt diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index eb0004b3ef..2c998572f4 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -530,10 +530,6 @@ org.apache.pdfbox fontbox - - org.apache.poi - poi-scratchpad - xalan xalan 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 1ebd7b8715..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.extractor.ExtractorFactory; -import org.apache.poi.extractor.POITextExtractor; -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/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 942eafa04d..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PoiWordFilter.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.app.mediafilter; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import org.apache.poi.extractor.ExtractorFactory; -import org.apache.poi.extractor.POITextExtractor; -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 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 a825064353..0000000000 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PowerPointFilter.java +++ /dev/null @@ -1,111 +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.extractor.ExtractorFactory; -import org.apache.poi.extractor.POITextExtractor; -import org.apache.poi.sl.extractor.SlideShowExtractor; -import org.apache.poi.xslf.extractor.XSLFExtractor; -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; - 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 XSLFExtractor) { - // Extract text from both slides and notes - ((XSLFExtractor) pptExtractor).setNotesByDefault(true); - ((XSLFExtractor) pptExtractor).setSlidesByDefault(true); - extractedText = ((XSLFExtractor) pptExtractor).getText(); - } else if (pptExtractor instanceof SlideShowExtractor) { // Legacy PowerPoint files - // Extract text from both slides and notes - ((SlideShowExtractor) pptExtractor).setNotesByDefault(true); - ((SlideShowExtractor) pptExtractor).setSlidesByDefault(true); - extractedText = ((SlideShowExtractor) pptExtractor).getText(); - } - 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..909291e450 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java @@ -0,0 +1,178 @@ +/** + * 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.println("Unable to extract text from bitstream in Item " + 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.println("OutOfMemoryError occurred when extracting text from bitstream in Item " + + currentItem.getID().toString() + + "You may wish to enable 'textextractor.use-temp-file'."); + 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) { + try { + writer.append(new String(ch), start, length); + } catch (IOException e) { + log.error("Could not append to temporary file at {} when performing text extraction", + tempExtractedTextFile.getAbsolutePath(), 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) { + try { + writer.append(new String(ch), start, length); + } catch (IOException e) { + log.error("Could not append to temporary file at {} when performing text extraction", + tempExtractedTextFile.getAbsolutePath(), 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/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/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..5ebc85c956 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java @@ -0,0 +1,177 @@ +/** + * 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; + +/** + * Drive the POI-based MS Word filter. + * + * @author mwood + */ +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("wordtest.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("wordtest.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 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")); + } + + /** + * 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/resources/org/dspace/app/mediafilter/test.html b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.html new file mode 100644 index 0000000000..7655f566cc --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.html @@ -0,0 +1,53 @@ + + + + +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.pdf b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5b3749cbff73a41baf8aa06b7f62339886bc6ca2 GIT binary patch literal 56812 zcmdSAWmH|=wk=2?K!Ow8-GXo2-QC^UIBXn(TX1&`1b26LcMmSX-5rYL-0$9V+Iz36 ze$=0$HQJtQtuf~4W6ZH;?`sR0ys#)OBOMz&3|Za#A3O{rAp;@E&;lNYo10$wn=O!D z$iUIS3S>$zZ(s^^AOyT$R-~6UurZ|s+R&;fQ4!KBIodlJIV#%&fr=oIBOxQpUlSPv zdrJu$6VUrKe}+I~!gn(uFE70e(9yv7eNI9qj=zSg=0F#qy}Uip1ZWSmF?x@|%<$&| zFE2a{(8l;LmOoMd1JqwI^gNk{)lp2z(ZCV-eqBt7jgak6u=f!=A^V@v zyM??x$Vdt3s73!?QF>*dt0TRH^?S(#|MepDuNR3w_})ukY;GV3a@BfwWgui_1Q4=u z0CWi7^RaP!NA_NUf6ZUFwoiD2&gC~ z_&%?)fuX~@$-frlUs*a3GW>f%{!IK&K}5~19D(-qqE_!&2?LEl#z1;$ppB`c86hKp zmHCgCgQGprz#1OLHN8x2J!**^$xCd@JK3Ei;gj<-Nr$w zOT0dWYUJgH&4{99vU3X9BV>Dg;lZ>GGQq|4^joM9BcM35@rf9)^fYYJpw@aVCcpM0bz1drdfU8G6jA=#C+*NKP(v>OP83QJ{k^e zO036Hyh0Jb^606Q8xp4L6@%Pc67}EIp?wI!U3Bqyzxs0q7ars7_Skbdx1y1f7}Q+? z#*OrbuHf1WHj*5*T^2#LW#o5Kq^gGc0w~{e46E`?02KY3t=Pt7)06l|fU4h)&@Utz zLWx>Mk>iS=N}&(q`L6ub>op3R>{vVG2F+5Rq1&x-UpTiDdKUu=q&ZwF4aA_sZWujy z-DcsNLz@U1rub0FMAI$Cm#U81mrALc40KB?0jlNTi_&@p{pjFYP3@p+EPQ2&IlDA1 z6Ex)y1REelpHT4w+)#q+H+4g`p^m|RyitkzMj=mcG%eJ7_;T*US0U?&4^o=yv8V!X zgO^_7XZ-6**B$*rn$bgOdR1I@8J6JzIr z$Le;hD7c7U@+BbR1v5JDSFH=3S?NOE<7blE5~&s&L)HnmR>4kEyK>$(5$|PW>pN z=W0gx+bwskC!`zg$%6OwRhmK$UI-7d<%T=glf_grj@ zu8T{UO0H>RNiE<}W<~#_1`k$O2syLDmK~@=7ww^ddIzo2|KxDZQGx zjew1V`F|h(u@?q97}=ZKI)dy8S^u(tsJXp^qmY?_Js}g|op}Fi%m@IyyBj;2IS?}a z7X$qdUH)@fL&X1)>whyf zy`T)eEXdy4z>40;fRORuY4A_Q{vVx$1pd?Mj}-V1r~hn#_by=gf3@lo#_y#xcl<^x z{$I5;Hn4I0GnWG)<6q@NuMASLG5h$iGAXYw{O{79rywvGy)`{%7U?p7{sl z|4j@2)3!nq!ha(F>0H3SbjIJL-cKuo#3Y1e3~cHDflDu}_Kxp;?cOgceRFtUsXy}W zT`T;-@OQ!fr^5&t|B5ksHA1GpLg_ye_;)cUXKxI&e_wpcf1nC2K)~(^Eb#p$^Q2(`9CQBg8Kh+pZ`tZU8Vs6giL=oga4@GziT;V zb4M#6r2yd{-AVX<2iO}JIhuoP-j5v|357vMPJgRs^1i7EE7=+t{i&*elcO2Pp7M`4 zCsYH0jNeZQz01A#jfe8}4NL~i9LxyJ9t;HL@cwTC<_J~}MhK?%KDGxlejl0q9TEO- z>W+ng&fR=%Uf`NhJ-Ao2#{QqF_F0GB7 zjDY`N0sHqV0{*=l|Mrlap@ov8HN7k$BM1H8z4yJDncheLC$Y@T0{AoGKXmb~m6c^I zxB21PFdhjUSAOh{Y$H;B2hL2(V+-Og3N7|9Pm$->7qFw4y?N$^FE>k39s%jUP1pPo zC5gx;6Q6`C1<&w_#jb?}!tbNjO7oivt50%ZN=Sd`H!&6@4pE3!H)Jj%Oa5q8+=VHQ z2K-7m20Q$*DKSftk{tvknFpgRo2+CHVEL}C8_GdVN+V3QYxr;(kPWwLfLCs5)w>Gw zn;z+k7BjM`nUn7cHUyh~YMacYl@yD>L&p44xB`qOeY-r}WG@L-a2B3{CHGPtPHhy(ir( z8WpyhS3TIJU_8$!vU{D78unQiMbcP6x3y&@=pz~1ZYlR`iclC|*KfTriq|82YBFCc za{`DIjYdNy3mXwUS97LwA6K-~p91lZdzDlZwS_QXcp6UgPJAgSwH7#g$CxYpcj>o{ zoI4@6!bb}QdiFIT+v`mAMX+Uu!i?C-AWA@6vC0Lqo^|*&SJ4^y#Iz*C071Fn0O4BX zfYZ+sf@nW^oSWvgis?-*Y4dk-6F4l`aHbVQtVMrcut5&NNJt?3M&RQD5Z?*o2e0Vj z$;;V2`NuXN-xhiXdkLq$)Zh)EHZ&dc>$DfH+0>kIZTl(Lx z!M`h;|MMC!voXAjiGMxJGQN`*fSvU(as1Ccdh7}75#Kj=my%)%l3;D|U19qr#hM12 z799;6T^UQE=IaxXW{fN?*r-Q}$-jvfqVmxn=If1a*H_8D`7u#P0qUuR7L}^91<0&ajrsJbbHJ^Pkk^4v#%tm^tM98Q{ z^0j4q(S4Se+H1>lj)a}i6s+-)y>mV1U`%&$U&$3lyoirft~ht;bq*V3h>H2dQMWTY zjBi@$5v8Y4oTSN?Zl3*`tQ{gpIh;Q)J>s%^e8BXlAgvI1`bB9yo zJcmK2F~8<~HdnEOJTmBWKb~iuC}Ux4T%0T#Lm45n5a#YuFHTs6woBKX#7G(4w!a^)r)5 z++RSa$VohoFVRK}sHQj0yZyBL5=t(k2VU4Urzba4=4jQ|&{3=FDhU9)s39|*{J12n zI!qEdAo*2CCie;T#a{e7rKy&{E@KMxKZfo(kqmzg>BV(!f_2kQ4T#AJVpw?qU(QuyRc#4{YmOT1f;$EZ^Z z7=>U#)NG{hl+SOUtxR3d_?83DU^$mNqRReWIG^|^y4fb0m5}{p^4Cn5=)XPw*Ws&O z7PrJzdlzafjGHm<-FS%9p!F*gGA>L%!Mo$?<{kOFY}{hfN=_n*jqFYe_|hEZZfqcpJ(Xa8glFD?wkHp7+4GXGt` zQkwz4O>)2NHfL_1&k@Gm!Ja?ZvEjpsd}2|YQ^ouvpTFgVtCk8E!TUTz&i!6@TkGZ6 zb)!ZN;dQ?zgW!kI^@4ZiR>y~j0VLIF-b1DefSzCI<4gk9UJM_(9XUTG>MBg%bwrmg&Q%~ZL#MIbyc2{}7xi6r| zun0yhc()8<_8dPq7oifS=oxHz`xT>p)ZywD`0#or%(4#@>hPvVAJfZHQ?IOK&{D5n zC6yw~iVK)i<_<{o418WDgxHQRkX=FBM zjbrJTjX(1D6hp4wlsTD|Q?TaG>u|BZ?i*0$q$F-q-6Zg&hnrxc ziMuS!K~JwZP5b$r&o+#GW|pxiuc&F#e(m-HE3tN&K!|Q3*T1m%s$AlPGPwW+r^z<{ z-JZF2=$F#uf~WPMWQ|svox_U5k_jucaGKt2%!WvZlW#mrkfI#M)P2^{F1)Fvb#@D{ z(@!PzmKHX4h-C_vOe&|L*)uVm52xm4CpP>83*Fx%l+yRZD(={z7u_894?e^JIU#; zrsZ?aj3=U^U#p`K+nj_nz!`6inwsqXh&AVs(b>hO$h@SELvI=&#swdDrl2u*Hj*X* z0$fO)8lnQQJ169+8KAGK81##+PwG|uKnXVxoKbTp$FHFbZ>K$(nPcWS8~Z%-72+nq z_;eWNLAhNBU6B4D5*A^g<|w5VZdt+zEUfBx*UnV~C%;b5*^le>57c2B9!XNNXZg{R zH42{`Wh^4&GnkjrN$UoW5YJVq4!Ec!Qpw45D98$m8=8>^5G}O?6g!h04vnhF5zF=D zvGj-DB3x~R*Vy}3+08|3YT8?zIvXOCCccXNCXpM0vZo>T>46A^{(*fYx-au%iU#X` zpBw)4@q3w3<*dGuzO8jlrJXnFfVH3{%mvq#@<{b0F2%AUw;S#2UI3iv$I5<#@j#zC zCu!1~fky518bwME;rZ#o0}BWv7-hE!x+u;PkHl|wtKd-79IbYT;isp`HOBCZrOhok zE8~Hu^QZOljh6Mx=1wkW$Ya?}EjjHszpsP3-PwMD!!JBC21AxsS9a2pt*$z_iM!^r zLMv+_&HzX(*d8EI+Pqx!>laS!*(O9NcoIv8ZJSNIh5ZV@A7Y5-st73}O_Wuo6o;lc84p|DP>@)V|K6Hl-BHkRBa zW?hhLfN6wde2!D@mhdFLBJr1-+%m+-Q<&(O8dj* zspk|9&2;-Vk%wUXYA~8}FmEDFR7U6hSt%@!k&X_r@|JGRXl3vxkNxt0J7sKj*%vvB z3hmPa&dm$N7_3Dd&dda#G+`pJKihW%Ae)wdw04gv;a`?oz5q{y{+eW4f>W}@Qy!GL zH%B>RcE|4Wf@>z;i<6D3+LZ3_tT;lOBQ$>grW%s*lk|?K?84@#Fpi8>UhqTaY3FlI z7_2NB4(ZnSLo93!!eCQcRvllF`pz#Mckz;1S(f!RI0D*KBu$DX9_78NG{+7QIw#il z5k^xDEnS$Ui$9Ba0FWaVR^{!5=3-f=cA5z)I+?A3eKU;lm-o@ug=U0oXnLD%U zGc<@xt&-39fLG{%zjkxV+QgqqylvjC3pqW{1L^b`WwQYiZn0{&Zp>+92QL~0=fcYv zgpH9!HTI#}$)%+Q0*>4bJ1czG0sJw&Dt2RL)2U%BVIcb3B`SdJGBBVi)y3Gd-mPH- z5$$x!UgM)@GT9EBw4}Bvnq{#!Q8Kn+5G0Qz%S_x zs)^5Ll-XvN;v%yPyd1uHYd2#KK%* ze*nk9)e2sac7l^Z^Zw13Cv6|h7u>_X&egk_#XE!iCG#$nD6;sF)Gk=Fub@`OLmb)% zMx(2WSPk-+pBHZS`%~{z+szG`#iv;=<(#h8=U5&^BjopG!6LAY8kR?L3(jS1aULglir+yX zf=7X&X_B87ICYM72E8MV=JXNi42dOi4$-M}fIifUM*ooXfeix9_4dO##)$GUYM3po zU9e@JswDU-sm=6$Th>7qAJjU68{2TK4G!h{hp#&gi02?74y^0>4{osctdnU9iPu38 zJ1T~Gs-QlwRXR?8=b*YJgeDYqIMhLF5gTgl1{K}fXn9j$Eq6mAYA_vFk%NYCfxI?U#z>X}mLr|NTBr!c<57HrVyq}voObC zHXpuXTEpJU+7VSkdYStwU<@x4C<+e2tVBHv$m2<(?`ve4epmo6htwAQN+#2(sz2bP z>@8_u9XZQKOxCF_zTFiuK#54qL+sFLN137k$T1F__wvF;*NoxxmEr$+fdIaP0XrZ! z6bk*jjK7VXFZ&=pzNngxuN?jN?M@9p@A8j{`rk}*YAQO}CPY}wW|NO&+XUDhpy!h9 zgv5)H{t6s=(SUBxHUW@bh-eLW>?uNiUQ&3aa?}=LS2cR>vJbS6AFgsd)!c4S$rwt>*u$JjZK5C@;EI%XZY16_KX1?6+&6U} z;C%$}J^;K80bgTAT33L)17t@)eU0RA*@^wb@vEhI+qKMM5l+dOPRRvaqa?Vy7ECQ+ zKV6Ea6k8IPjECD@7Zpv3W{i$ui-n|U_D5VcBFd4`5gv;L9{t-L1Y|#Yy7)em?Y9hF zus2RQPZ^h=jA;GQvp1MJhujiqnE`2PY84VUv7Wnd7lki|qc518Y&TuCub6 zVcE9bAl4xM=d8S$Ej9oiD^PfwFlP=E6wcI-86n!Hz-Ui`g(9@+U##XOmsHpwnXP7A zZ1#;hi7Lt?Vjv~P=;k9-nCKdNULYhCi{kcdYUd}A(ICkubI{h{-Erg7KRHwzChrHqpcvJo{Iz7+-fq>POdhbt^a@vX&Q z>ntYs1409v&%`yM70=AYZ+?6(N@4L$!zo~f9wBfgtm!A9oVwg(xQy_1*uju+zqfBM zZMQ)@i|$=-4Jz9lIiir_)l%By%M;aTg|PS?CtQ4^n5djXGhb2^u`r8Np>gBHqoIME zSFosF6u|B#sgz1!PK~3JRYoq6)qfISR+lh#q6qJcY(8plVr17#qiK}LK8-yREy;tA zs0_ocm}eRQ4a4&TIn{N+ylNSRaX=2gi~#Y<@`b@!lY&0mUmtUFQ*__;Ery|C_e|cT z&H8NIJV|e_uK8#WTni#il~z1KjmLB%-37XRZ^S%fwVhv4+j-4tq6~J_Zh^ynX{keK zg|5%~v>D%`B6DeFrhL2%w!X;v=Cq#E`^@vm0%zjzC{@XGuKX#oe}1Un0;qS#;uX}W z5PBBp$uiuI{eE0@XYR=&TW$~`GsYuYe6cVdKB@|dOJ_kKJ&&d~e^`Xzi$u+E{Zzgu3T1aD8 zcUL2^!72za#WTgk2--r(8`jw=8ZKG-iP{v#E%Hr-3pHqY)9yEiGbJ_4`2uUnyl1&CC8KboUii>;%v)S2Hcwbnoc9^~npT#*!mwB`wFdL@O4wLTP9 z%#7-M4vnHISEquW{(MxeC4-kw7gw2@o^KduVF5Tu3~@57K|)X*xao6KSQ>pGTFmx|+`c8{ct1T&)2Mdy^g5FSSvxqP z*KK>yyrsGk!9z9s=5wVw@^ZKm3FPY2s;q1e$WF#f(_nRWvqg=#i-KMzw z(Ac%D%h(vA$@*`|oIufl`Am$T?T4|-llm&=(shtxPNMEH?VmiL@7*`_d^i<|C2RZD zMCY?Q;yByjdWca~2Xkjy{JzB)0Gw4F-P%YM=+t^ql-rJvRisND$ef(NWW#=x#BNYh z)uK9(@bO{xT)h!)zW5DhV%ES3(}BqS@j2I9`@YeJvFQopJ0i@(!i+bkk1&37O1%Or zoIqs$9u0sq+FHv4V`nH(b{Y7I~PNDeG(2vD~l&A#LraIqDz=-5M`Lfd6`qX^#L+JiGL5AgWq=xrS*8m3x3;7>H+<{ zCV7ko2DzKT*%K&$pCTXaLut2ROoeT3S2CxW%)A@}ZmAF=%4#L3l$v|O%u!GB7c@xh zQOxR2X=iM3-EFem)nSfc2|jsC%WXt6V9>t!uy$_|-dz05R&B^gj~CjT{5A`9;61r) z#ehRTYirX;?t#;IH-oV8(2u_kN&71DWUa2h|V52NXxvfg2E!z>t8?)yZmRJiGN73fr*sm}KM z50@NU;H+OTZaUCy;PmqFTCOXplVA$NMG3ujiBi#f+7Y-hW`K^6PBS;bNjT`X-#B1fAHY> zQRczt?e9$t5ax28qT_Z65$j!cJ))FUSMxIsw~0ITdXWm`?!oM_+NO^r;AQ+B@Yd`8 zGMl;PE$}w>B6sVJ%gfh_ed}Hr9oRh$UwO&eyMe%sNce@|r|AL4b-u|FMmQ55F>gW> zGCX8*-B)M2NLiy_A6Y+7FmuoRg5$QfV%737#XBW88*bc+w$al8meve~V zd9L4KjWW__y=A&Zbzyl?)|~*JCw@V-M}3t*ouHPm5Z@4Q#2Hks+raOVbV}7GqnDqV zub11Q@)rGy@?z(|HW{FF4-C}9=@vzFF4{Wqhof=NGu?(t*5z9u!`Ly%4}mj7%@xva%J)Rz=uCW;0u|qL7p4usM1MH1l<+83cijSlmRV~n4Hlev?@x!XlGmHIK z=m&jHAC)%WbwA09^Y0!gEY!eeqgo`dw~$lCdw1sb!N6cWYL}^??y-*Ex(kzIZ2B5I zN!E^NwMv~uJ2?g}Jd=?k|CeaxTg)d~*v-NYrIm^PEA~-UpR`|}R>*bH>258Ng}ls3 zMm8l%LkKo!dbb~6vT^U4UaqAydp=T&9;XL*HGiVkr=IBhp&GUNTdg!e@c{k!PV9*w ziL3sUc9p#M*J`#8xf0{p7QF)5e1DK@>Vd`ApKZZi5z3>Wype7lD}>vZ&x?Ep+OXPsuGvvkBsZ_Wq(b+~3NDxW#o3t10e^gz&0&C%80=LwrKvtQb|xKY&3sWvkfz16<^ zGpDoeKR=#{X&djP4Q@vcS!12Xqh~ORTzJ=JyR3Kr`fC z!AT>@n*Yp_Kl{%0mI-n;&-KSsK{<=NepA~SR|JYij03XtJ}-fajn@aMN3>J?vq_LG zt9ul}hNBbGUQJZ+$Xl3f5|MXjaLY<;ryIxt(KPCU!WNO~+*4qhis-es$IlQBP?M&R zs!*{E%|X$1In;|@14}h$`Lg8F?egQM5Y+vdK!pgz6KSkoRTdN!H77?n=quibw9%mMpv9WzK2+#4J+d;la6W!>W00c`y^CUFPzI{d1H^jesUe1_C8^rz zIbYXvH9B}bu5G)sLbH36of?N9rz#EWwVy=$N3ajAiifz98GH2O1mrDL&}P3#8qhj~ zrC(NW=8+9agg_A~wQ)(=FZ!jMcv>qc)Y(r>9v=SWXVrtFlB>YxYiFEuZ*6RruZAtz zDV*O%zhD;yiV8#S6yn3@@nqiau_I%^c+VW&#+%nxHim!i&b&5cM}^_5Fd>dh3?=m> zjpZ9)6w;Q&jT%yq5=gU)&0MlXI=IN{5*ENb?J;o(|3W{Ik^bXHEK-f~!$f>ueCL<> zWk7v1dYQzHTgMNlt)pOAsAt=5HL`+{k;Dn?d&Nh?Wt&hn_>gJXyaW0qaf09#CXWry z86s`GAEROjH>z;u`s2uH|uXR z9UmN8;gE5pa&qZs;S_JG1X+p_cF60BTl;$JQ}6+HGmq@%H8)2)818NM!$>Lu(*u$% z`tf_COiJGUentCdA=v8)ZSY8VZ$L&UO*3zcf9@5EogHMs$m)5gwvmj zoYV_L+fZ;(?VD6FLu=QM>44tfzvD^O9?W!q$S0=7ZI1(R4YsxYJ(-@q9S#0||EP6U zDTmwTt_mI_s@5SY$P_`VXML5k*F0(cWem=nQj}=7+Ke(RX?xuopc%@M#0Q0=p{=W` ztgXUb)tGk=m?58uY{WabTa@PLXLo3=B!|ry`N<&lyzzdE|5k-aa?q``VZgyPj5>}q z7e@)$VbQ2-ntN7%Ac)yT#dJe321t>QtVA2>q&_PM*ag#iGK)E(qhg%<4K!lmm@?u_ z9W^_MlEUy+ABacc||N8P>0!{BW(;8mTmls5Z~8wgucWx@kkj<HBdDq+JINjTzg@k+z& z`oqy}a*3NX#@xQWjfjX$YrVI?+c^hysE^Z0U_oX*c$KSi8snNFqn6PzxmcW3rZJY4 zjGTDGFMuM`4c&tfgj^G8Ti+NE`vzgojDfG)c~^Mx;Pw-jh!J!gbM~4cp0-JSZ}tVu z2)mTOQ>kb62q?uSBXQA6&o~97qpt@%QDp6P(PC9+gKO?@pRxLeyi^N zqkZ5tc1PssDusT@+vwZKW6BQ75ydTtc3r(=?0H^SznXcev1dFtj&jCkf}w9QYYvoGZqtrgOZixhBoSN;!^%d&#UL|GNH~V23yPRsXc$pkv~tk6t#JY1?V1-}7+wmHm_v+J9F|m3`w+|Qt`TxLE@U`DQ!-M1r5*Rm>Q3^M zF^$LrMV9t$luR`yw;_*WDXu2&DmFAIan>}7I&D04#^m?fC`zeuS^E2zxEfTQm7P^= zDwy*z3X+(SZ>z7Z+;uf$viG`w7-p(BGd|rOH%P&cOV(8vRcADWsp^!p&rNE!O0K7@ znIBbF44#ha#JMEAS}f1qj^*hj(4)60U2ELxRnI(&hV_`(12ypKjgwlK(*hLvcXY+- z2ZsqX?9-0D#kuca$^Ct?A0T+0SP;K1iQ6QII-ZgrxDhIgth6(f~inT9ZbNW8;tEaB(Tj&*X{o;%KuNUss+`{BLgNvvfCkF>&DXsaIMGKNFf_ z5x^)Hrq1Q* zytn4as}^(t6@ne5<280Fm!Vx!*C)2$<*ks$T(xpIe zU2t2u)g(jZP*Y(1(5yrCZ{=+w7McQWItnydNPnLw$6HsTa~Rz|OIMkSKCP6e?a$$; zX9;%Um3?`BSV5Aw2z`hH1zV2V@W|ocKlKZKH2Ucl>GbK75e>)S{HFhL*W*z=5>~EJ zz*uydyr}&tL>qUt#UpWa=v8OPY~RWUfh!8b(?L#k4F0r2FeyAi&oL?Mwc2{4+*$8b*Q-#SPgL$K! zwws*byFS$$!C>1?U5Wk3Y`eK@nQw#FGJShl$=9ldJfywepv#kJak>4qKHC0pOuZns z0l@S#Xe14tH6<||vNNv2U6DIh0l>0}+JrsJZMSS8Z$I@b`|4rf8wNG%JkuRfat)ts z-)WS=SEH&Nv1m-sv4gfg?5HG3)k|^*$^6_RNtaXrP}?C3Wb5OKT$>?q=bQ4^OiHt; z6|NJ4TTd7Hyj2xmJClCdDHC)3!K1k4Y=^q>u{wTxCqiv#Wz{AZ~;5G2xqVh%J4AI|Dh<(7E$6;RZ2+N_e+{0s#cNVsUlw2;WQ%Czcr%RpzAup;7F#t zFloy0!37X)DdRaTIM#|cVU;Ok9nz~*lVmL!7=OIQWg2Bh)U_grxQ-@eUi0?N0n{16 zgob7SSpjVp*nlFFJglles-0zRT?0^EuA+I#dJTWge${$pd*mABs)v1lbH!;8(0eMl zk5w05{e%z7} zA4$350+qvNKEfj+e`E;apFYxRb;OJrvgzocJjM<&LXxwe#-ZdSa$`~4&61sjo)*EjUin@Vt{KQA|=`Nlf z-6?4a3H@A+q*#qh?V}bHVqz(dtUxe{hcl87|J20*N!0^-EYjt>Rx?``X5Xn5B}r&{ zBRBHr+#9$^-i$2TvJ0ElDuV)DKHUpFE394OdIx3-kx0!2Uh2%q1p>tT8)dmj_zj8$ zO5|zfij>y7nvbkfqJiHqMf#T5vr%0eXC-=P#TXR|bOn=Z&$Q-wB}#E&fHgD?y} zA=BY+p{^glekm}+$MyAB{_fT_3iaka-D6RQTwN8D!wPP(EO66&DNI{{?TB_ zu0A{nBZ3}xN+CXl-#kLiHcp;c-Z8g3g8)bP5j%>Mhd@%ZMCh~7HASugVVCMpDL-+X zE1%|+V#hzv<>T(W3MuuU$X1r-~FL*#xma6t3?s+nHmhq z`BTuI)R-fo`l9VTmT3hsHiXJ6nRaAbaIpNZ=={Q}ar&&!$uDYtWo)r+3jNXx?m$@& zJPVO6_+(8quWmAFZJ1W2o$D{ED2>TXtr;#KJTHKKLi-W#XSVt$jBrIuxg(hkgi4_m ztTmez*xS&S2#AfeewVK8{>^!#GWsChFBDpDNRMT%%}$)x<@YwO#WxV=>pweime9_J zC##Ri8yRcqo>C@HHZl4#F)KnVutA|9>~ag;+(E?*&CHm*cul#XFBSbIDY0LrByt5r zs_^;wkb1xZMUYJo!Xir7k%UB5N4sg7@8osZakERg5q%6um0GK?E{xgJiqueVD&3^} z(wXzB4+Uz_>IGrE1C(s3Z4k%pG(Tw&9c_1Ce=QZZh5n}Mj4t4ldrWFB*3meqywbBU z?TimRX;jhzMj$DQO0#i?gUc?keEOx)bw=5BC`uWcT|f!5M!eBySO{=&BUXmPdLPdc1*mN-SZEkmbb>c9JbRoTLQtI{S}<>K6|F&8$=}Kz_xWWKb!shV`f_>s_6QC zp30zVH_orNDc_pQ+R7pmlfA0=R`eF=tI!MjAh9X9l22|=#!t%ePJb>&bI0QWZZGv} z&+Ja^R=3x3_`0}bP+G^+na*g09i}{LefarVm?WiRW)5~&3>%ZbGKww+V^#m88>auN zz1f;wHPJraq^q8!TObCcR8o|s6Swv>)N8D;F*;&tvb19TyFvG9lPOqEEqT0K zh!&ZuStil2$~604ocB4p1KBRLXUBzIyE$j7kF+$`VA~ez!LImwWWlrQ;U{LJn{S_h z>CZilRK`3L-wK)V3tc1K#$CN8YM?@9Z%RFs|5e_XTXIW;+*L{?(D_ez(<~jW8WQN^NOB%3=H0C21e@8` z3*3zwD+CO^Dhw62IS)M3L;c*9x~^KAs`ZUlg~e>9od_Umr6U7eFagJLcFQ2b<-5d? z$T{LCtQs<-+ds6!i^&g%goO;-$HR8;UvhV837U5E=?}uzzumT@dB1jbw?1z-+s4n- zHp8R%yLed(h}qvxRaB%u>iKgm3po{!*wA5k>~wP_j19u~YQ>N`JP|Et8MSpMY`F57Z{Bz!~b(wMAvf z63iRrT$2RPNeJMl{v1C4C@(*6#13-Yq~@0nz8OFmNM2-8hWWU+#&*JP0VUB2mo4Wl z`Af5$+;(rI@e^uV8g327Idu51)o-nxPov;uq)`%ouKiL$OdW{9ph+uXDmK^4d{tlVR>9Fz>navRb*E_tY)WWwCf zqTscd#8lv@R0X2fi8Y5S>`;c>9)eZmD3s5hG%AF$I!XrD6&uRsYKWyjydI)JSi_3i z9wT=w0XfuntF=!HCDBMdU+Sgse>|*?fViJ0F<4(CF4w*#E!M&)XvJRqDA7IVa$EkpxVJ<*)yO7~j#YHV|!-5}=ooMWC9 zo}#IKg>Ct5l5^z4$_v43zHFty>a^9K6N#zl98&c>NRFT40C6CxJXvruW`ZI7XGT=P zfITsDu7N8|-J2oQC8>}QdDcEGk5DOc;V>G0Fk_Cu&t$0SayEWfL-41`Hr>!S*n)#1oHm}Gk?C zB&3wXrVd~O=g7IaB1c-TIfIR?SkWb!9EdULCL`}Kt>{A?W;BYo9`u%@GWUuZ!zVc$ z%q|g%<~($CB_@Z7vqrA<Ai;k4N=)lij*HI80y$;Gmn*j%{kYia?XKQEk7*nA$mw8r7gC;FGlPQ(b@q0ThGfVxR(VG-Fb>*ut5vqQdoZ zqZ}=8@EiwwImmS_?)o29NjN+*qOU@_PDezJ>+L9N*HRtw=okKidE zPZ6J^>)EQOjnNK>{A{{ff2&TuFE1>N#4WxB?QQK8YvpY`#i9(ao?;93q5?coUj%r4 zZrK@u_3?$4Vg$7Fc?A~oc5g(8KvzB5bg-QfK+3{cCe|WDV8$Y9u|dXO z6Q3Uy4{<3A5q+(m(zc*FE5rsum#!xW1p}nMj4RFo63IMxfkD8J4pGa$($Kc3|8ueQ zFWz6jrlcjPY(mQ(J9NiY3nN}z$SpVmokKUYbii*m^(Wdct4$Ojpa2+t)Sh zy`jFosUy>xZ(g@QEnlwho_*==g?`T2+5W(iufuX@xM`qxML$ZJ=Pt~hrB7PE3WA^k zesGaHj>jyR0|d+l7QX|CHtheR1B(<)6%Gtke28=~Q#kPXPJF(@A%*}$S$#N__4rLI zI?W#dl4iB58!3+DAEnpzY63z${dIj(coDfhUK&PVW}=Vip?xe8}NQXdXf-h&m>5?^NyYovJPtk<6V!0zS%~U?;eDWyX@| za+cea#I5uW=8N<-v?nvF0>gGG0-~&>fNK1z zDYConLN0^61VzD(8>OcLfKn@bXnc_3tP-hyTj%kLV_0pyRuz9_#*#~FIgck;o5IAn zg$s8-^p#(K=e{R2T7!1gPPmQ4X8sZg;$VF zV9cpe6j#Q5NCTtJ&rjgE6>XtWAVEPwFPC1JCSW>+SF;@VlG({y8k_SVjx8K=uD4JT zUmvr^vWD&BHb@&H+>MqUmQgUOj!QpLPAgO3O^bB_Tn^WXYa{*f0dXL*LmY|R>;0;4 zzh}SrIDC{k8h;8t4}Z%2H2e;KHuA1=9*Qj0?OpG^PrgqXjh~Bi9tHkjZVD*qB_mn_ zQGjXQK-d{jM^y@_f~t_B`H;H*k~-Bn)v4YPJ>!P%p9VE6haT^>F@F&^X1lkD9@4J9 zEIVM2W1oX^WCcO-QN4BWP#vtRK!@o)$3nmgZ1wxR z7PhOt>m>EHYH46(c-3@yWO!z{e0G>XPuEM+)59b{ob}e+(6%PJC3;Jg{(2O#{_sZR zKke=9upJJUF|+^*448(2AQmOWt|xp&iy#ysP6`Fgzmvy>Vr@2l<3>0vJ?(Z|Rh6~| zE8M%|gI11?R`|%+AYB8a_oIXzt9q)C2f5lxt0a+A^<0%15n0mR+G}@z>um_f_x$gc zriC$&Jsw}YbVCZ^BQ#RTb0Nr0z2eb_w^4ydZ8<`6R)#cGIy4- zAkLFPGc|IFUP@%i+)A1i2?Y5n90@dm5)Bdx4B8cviedCABo$N0+34tpW;M?e@fAws z8T$W#C>9TRs!R4_G8BBK)rImbKhThwo0@2%f-o0m(F@j8&+AC@OcE#~-7`;!S9{S_ zK(W&_zBDovg=TahN=0Qm`WSnVuwszG6o~H7k6VRtyU~`y?sN>PBKK7)J%rQPPz zPNhe=R=L48sNA6(0*93EaVNPK8|?|+mvmg_jro=YqRHT8kyvzzj4rb?O#w1$auaN7 zl4-jP?5ab-qQ@H;2#yAi25C9CKS%}NOZT!keNScbxP5wgf$hq4?W*OqtEOj0%AGTv z_y^bfN6;f9!W9VP0^uU@)+Ka?PihH1tpJ@tn-r%hu+h>)s20}N8nk177{5#PfEOu%!bQ5R>VS|XUhv>*RSoOZPrF9DADH^^ z$2)rv?bhi|=xKJVL8;k3doIIvZpm&~y7}1P<{OtSy6}^qz~!sH`)$I$7hZpOdBhVR zehI#|WT@D4)6ahXpBVWo5eu)Oj{!f3(&LvPKg9=8q;=qhK)^+s+FWhGFagAqE))O) z{)*t-Bw=9OF+Cm+>H)i?c{sd!fWqq##_`o0;cRps$5iHCC0C%%&!5I#!?f7#gl@3< zM@YlyE0@az{zwR9PfcEw0nxxHIE+Y+o}c$2H&OkI)m0A-80rG2aK|_ra05v69_ASC ze&##O7(?Sf#i2*T9-F|h_xt4-dQRL$PmA0GKP_rdeuxgaTypH=;z2)k8taGh3*|D( z`tZG?JH>8chQ0k_IXnOc{IAlMuo6KMGg1tik)li%UUhZnc^L;DnLsg>Cx_QGX7Umn zwr=oU8|)8l5;xazkhZd%m3LSISF(GlFSC0b_X@kCk5EsF6TVld-@1P%oTvUT?eh+B z1N;zry1mxtxu3buaVUOr&U+}@iv5X2{?y%OT}Cap_Q-uypY?ibgxc%d8$RHB)cUBc z!cSO_*?vm>7d7QLZ}an~IS9DZ95swvJa+rhpdI5_?oP%Jj9>uYs?S^W_6K$a4h7Bx zm_R`K4PHYwcN&Ey2Fq(7ZeBC5@D}kHUn4WS*lkms*DW2uO=L#@jFUA$!WO@x3UQI~@m*7%d^f?!F;XARSv2=CC zx@wvUr7x>yhOuB8E~91| zhH)1E-{`{|Nn%45`HzrF0f+vYJzjK}%|$1A;ja>mrEr-5H<(Mtr0SkjeXq7>tM1vV zdseb%x~#>35H5xlPtl29gfc#sVQj=)!Ets^093d)Ck6uny9a5r_r?c3oSmug^4OsL zVqBb0DST|TI321*NH30%nwqGVvwsJ-Zr!tKce5P$`Gb$W_m>k7y*RT6K4B5UTiVv# zO)Y$R`}Qrj`}e*9;cwrA+%LY@v7w>eybD*gdH|sBuzVTl)Mb~jg4RspVY7+DS~IC7 zNIDc;EaY8j$m4zJb>Z1;FLrE~mrzD^sRxV0pB2SC8{eSCLIAka?h2H~yewXmJUuB) zmQGCz)779ii4%YqgctGe3uK)@jQ~ypH@OC&OU-DSZ9t#Iry+4*$YRfhBoiRF@tSET zUQCXo{oj#{z~yRgnh$ZW;|==rr%qv|jooimW~;y=e(P`S*Qp5a8jtb!*TeA`SD|@Rj>itgo{Ld2PlFbMdM`@& zjmEUs!}6R>K#oy?S3k86CHWQVgE82sSD;`zQ)%dRdxZPlZrFf9{?x!gp0phu)wWcs zwhb+$HG>f~zuyJ1YxKK@T$8TTF4h%pdWL3ct~S+E1@o$D1bLFpccSL4^4SqmW$Wym z8R;y|plm){TRGs>lKx;q3na8)Dgu)Jh6tQrD33xJKraN) zPp2*qkBx1Z82z3t2#*DmAubg>&w7#jKA+_XsYSR^+d!M~^NppLwpnlfp zYq{~&%dbEoVbSa|`u8ZaSO~5J8|fJ`lkx>`u=ksZ18p>D7B*8m8h5OrKqH%Buf9)V zN(*{6Z(DF{VrcU|W}oHm&^_Y5!o7>{Ub=6^7kj=D`i6L*r^1}HjEBa>m+~*InB07N z^VH^Zo25D>kP{02HhHt<-}vsfk_3Wuo7ydbaM#8EUf=5T`K|n@2E7`trFl`@)36!+ zjuLK7yVqViq#f0s(`c;%4^C{*M^RddPBoqQT<;!Li!M>oxu&?k8+M!M zXt#+DbvI#`>n00QAaDA%@o*Q9IvzYlP+$*q!LAD3VmiXzwrm*ohDXCx_=nW50gIe+ z73f57*;p>T8m?|?a<6)pHc)(xp=}YYq773P47knMXB;wUL&Wjia9~$53dIaP+6Vja zvpJEMqt4IA1wZNhk<_jFaFNC4L=M=e$tjp3*bUX?_oZM@YA7|CI-O!tE_`NczOIZq ze=@z8Xi~Q-n~lxp=EG>rEt~PB5xXP5*|qP1WpEj(cr9yDg3uit3jPuWm&)8VoHC;VjaWD0LU6Ye8t{EgX_c!Zcn9 zg;dRmfX_!Kqo_3gOZALOp|~`1ei|?6$49g?8agsEUj?rf!AUWEzP@(HiuD~!8w!zV zNQ9Pz*4mP5$+yt#Wr?0dMr%y0)A}MXvM?3_D+;R=a5*d~V3DO10lm$uBH(JhPk~Fs zWf8bGxjq8dt&es{=yYizSlQC8z!lwvHk0a7@N6X08G%=2S4Y5_^lAkx4Ru9GQ9+-d zzpZUP{(l@b>UWVJI8w&Jb(n-w)0PnsMhk)$*X7O~_ts`{kMsTt_kpPmBB&z3_VciZ zhb10nc_{PH%|kEEvu0a&6Oh7P74FQ$U^v{(c9I^;t!{GTWf0YPNQS3QIMsh`x%NZ2 zXMP5ktY5-44K^OHrI4ho79zj3YDbck=Fl&>3RWkGNy#Ibfb6AvXur_6>D1x72Y#fx zXx2iz_1oHCcx=h?CRsHiL;t?0yzMjJy72t&6?RX7>(A>&80g-*B;UL8`lY$q53@$c z)*p^Pnae-)2D~c$^^IS8!L+c}P@T=fUNJO!!k;MmJqpLr7OQjU>ftT-uWxM=MeTCy z7P&=^UrX)TamT^+myg_WXw&~=>}#Ogwyres0FnSfkO24#fPbJ!5QO+AiKHaTY6K3Y+ov06{o&9;+B-gh5JS?3`U*;9#Uf8 zDghV@N*?C&G&JNh^@dqS8J8f?+cTt(Oj&_G$`BZWR$O_PxB$!Tk&G)BY>_|)(q9>2 z8+4e*Kp==W18)VqnN%Ia0OzKyhi|g&t|4 z=(L>0=JCQR%Xn8k2m`-C_V^RcI_?y@lue$11D*f^o`43v<8e6H*&PxO835J_ADXXFyW)ljxmmllC>*lcAbwxB~;OSn0Y5zi;w zY67vG#wGzgv^AQR-^P?`v8=ygbkEwY>g z9|*6>VQLWj7`L|g*DbRdZQb{o;p->kp#a}yFJBX}dw3?jwyXQj&5`E%<$(`m!-|;J zBGmv2x*uJ-?NjS^eAxcZ^55>%l|(LQ=B4Y=+ArTyt#4aS+)_#Bav{Ed2lD~Pga&(g z5bsz#x_ndzz3dB;CtHK__4eR=2@moRR8Bk`(1EAJ z6T-;$6+F?L9hZJK-ts0nk+idT0AW2|TmXq|j~l;EMofn6p`VjmwzMk4!%oFk`}#cF zZEzwU^U(026s2c6*bv;_yLJNJ>GkHaelmLeGz=0y89i`k&gfAZZbEWJC?hcbL+X$9Bsoc2a|YDPo0$g15R1)a>}eh zO1q+Jbv&Fxn>+kgDu@S>sSfL@%vwPeLeuzGniSO`u%RG7?M2=u;DY1VHK4G?p$2o8 zAox{(PO*zkg~a`O2kO%b8dcDQGO0`}$CQhTTREOR{#Ru1kThf%F&GXkI*=Qq0mxlg zN$7Q^O*}9hr~Dbp!*!pw@kOw5C`^V#s0F5nv_%*ZtFlZ(E7ITFyOQsVrQhmYv!=6i zaLt2i_we#{*Hz+PHl-yB0TgyW3I_%|JJ&2{mh>G>EbiLic695PO1diMCMo*z?aS-Y z5%&=++ZMXGYVPnALd5I~rQuh;bP|+4skDUn^|lcI)DAgP?2G^&n*Hgy%gf{~xN?EK z1y}wjc}p7J;)S=QDUW4>R0{BH62`v6(afYNXf6N~2bcDg)&|oUXSWLrG;K(*{rox@#eJ-7z){&^A z3H_LUQD=0W_*>^L{}$WG;B7tLS^S3#NCwHDYejh+D$Tl&tbJqaB`hNtCW517F#Ld4%<7C3W!M~A*K^36^H{qLv4!6AK?##YgI@xTKfYTD-;I@i&&JVUOe`pUELWiF5j9_ zD$y&Fc7z=6EDkQ~?|<~|^VepxJ$`nlvGXtJ&wZtsA<~B6F>@{fYtlaE_p4;Bs1f*~ zlD$JvlF1%9WF^55(h`L|#wKqPd4h^vbU;e4SxKW7JRw;N84m$06@ou1Q4qvR8>dv- zIHdx*89JyyQiDXABqXO@c|N8Y1@b{)m^_2gN}g(9ISuv_w9?y0F)$)AW@0(}5S57sR*dx5P#H zH$iSl9#V$V{rXUIy?B7XN93w>r=%NtzS`_Z{Sq6Iwxb)Qn{-!J+KG0GZ%DtD+?(W$ z>F2%A^Z$!?$GlM~olK|K(btL|pDc#_nvfJzfwbpFX1nV~_n35(d{fAyipgXueIxB^ zO(fM`g~&gW7~X8)@8|&m3HRf1#Cudz5b#I0&=qmDW-1UvhSy*kkeLBj{)Nm8l}fY! z3a%os3h>lfJf*D!aPb6YZ=Y?k(VTe@1v;L9J*kSD8i3?KHh_+UsV;}*Ck8| z^hE)k6J8hSNnsx1Z?V{nf)p*?L`@8>s9d#5Riw&nWxjH*;vTJ_smgSPu1xed=g=ol zWWM#sL=Ege0DL-(Zm*XP0N)(^lH>aauVjcX!l+l_mcjJHf+Gz=zPLkyBFuBBz|sdw zYff23gE|by7FZStPzMg|hw$71qUO{Zqgu~hN zK_NFqr|>!jP+p#t31z+57TGa`)r{>!lFpd;dlPuCLs{oNh8AVa0->y{Cci>i?`c!k zkqp)rP;DETh{waSA!3cy+8OAmy{JV`-xcHsDrnnVn*}bDM~~j{@!_}MzOADxr(Uys zT|Qn|{-s*kx?EYGjrhcXu0=X!B)K1bfB%oy1_dFUq;;LHta)|$AMejp1AH!rBB59f zeQf#MSigdDIoTJ>e2}?zW?m zXaPKa+`{qbDhTBm9z%myK+`zdPyCVuGo6f3 zZk8ihiRD*TLqS)Aqqfl8uhus#4-HkcKw8lXGLqeozBjz1KS_WPV;|i>Kt7q7JYCEa z)J_+-YG#U!5es^Zc)epX`O)R=BVFw|qzis(_+h&UEfRkHt|B?B*xk3jeb)N+S?ft= z7@W0!c*8I`Z<=BBRq67QY3FLlD8N82$K&Q z`XqQ;A29F;gS#&Q9rqE#9!~9}$#Ig1)+dubWYUKWv?-?#y{i?`e%|Rr?p$G~oB9{$>s2hRLg8@@jYl-^Z^`J3;bYec&2_=03+qLMrc&qkVn5MgTWJ<4AjK z;mYsG(UFm{U9BM{uk0m!v6f+4ePtRlo&sT(XS-uGF>{F)Ms^CpF<5>?6WGxTz^*|n(UkHqx+yOx^?GpZe%3A zb#yB|wRL(cO>LF7(p&L}KNXJFw@&ODo1<^W^WY(64(<6Yfy67kr%T{#IPVmL*8?_& z1T6eFME)}T!~qIc{DClr&F^5>^;FfU zdjm-6>Al^`2HtQF&T;&Sy*@0I3JGA;$b3Z@QGJd#;$QJew5BE!!(iFi72UJa%&u}) zTm_|TXmn4o{71DN4@5rp=$1|UGf_X^`+?=b(3(t)cg4*ejk`u@Ix?_+xqGDPb7xB1 zdK)*E)$T3JYleC>0=P{Pg^TosJz~Cd&+bod*|L4$f#px^(4*;GE+%E=QS_&i6}z#~ zS6tpg92$6%y8*X#+sRVja^&XTcrF)TvmM>?m68L@vHlY%iuu1-|J4}RYW>$B`mc*1 z^KP;aaAGtIilhRsvdNsq5k=O@7baSSixNj;D{n;jLjt)&FOGu8KM$uzsU&%Gl5~_L zT_i2yXS4`}x7twMa=;>~yxYo?z#5T8W#sU=tXINIF{_k}=Vw614usa)E z1GGk0W3(~Rm~2cp++`QCNqq{xnr(Py8|NDIYy(Z;hWQ4QU!18@N(<*U5irdh=2lszwIXNCp z;<34554!1_8#OXAX*@BUXU^b}DN;$gylQMFFc5W!wggZ*_NWBZenE4=XoG`Lu|v@4 zBB!}x2IrFu%9(*3s-)vxxk9(`!f`lHgEz-P3N=@l$Y@xQ2!@mhfOHYO0acePB?t~} zhD>=Nj(E zgf26aMlVEupwV_3ZQ|Nj6C{fn6|wEI4GXUeorC?TYr-|@ns&{)Jgy4@ghXv&2AgLq zOc7wYgHyY>WwvnRmf6t{-u$GH+Vo`FwdsbNcRdTdet2jhJlqhb+Us`xEu}F%l#2>8 zJ#R^G#aF$=v(Q-Uh9DIZC4)vFPaBCmZ+P;u7}lvI(sUH{;+n$ZTF5WyD9+$r#23>k z)s1&8d%yihcbh^gW(W@t+EOD)`ChJ<2@^ZhUOq$%gbMB)J*KTLeOB zJ|~n0v3W`&G)ju)Vr1bWu0Q1s>BFK=d^ z#0=pFO0#82p@h+d}G*59Vehs$7dBh0h2;%0AGv`t_CiE@W~}d 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.4.44.v20210927 2.17.1 2.0.24 - 5.2.2 1.18.0 1.7.25 2.3.0 @@ -1552,26 +1551,6 @@ fontbox ${pdfbox-version}
    - - org.apache.poi - poi - ${poi-version} - - - org.apache.poi - poi-scratchpad - ${poi-version} - - - org.apache.poi - poi-ooxml - ${poi-version} - - - org.apache.poi - poi-ooxml-full - ${poi-version} - xalan xalan From b86adf35e233940319f2fbe44b6833a3f0016e08 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 30 Mar 2022 10:55:43 -0500 Subject: [PATCH 28/33] Remove duplicative dependency. This was in POM twice. --- dspace-api/pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2c998572f4..dd86890435 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -872,13 +872,6 @@ 2.0.0 - - com.github.stefanbirkner - system-rules - 1.19.0 - test - - org.mock-server mockserver-junit-rule From e9e4cdbaac72da5086ccc593d656b38751224c92 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 30 Mar 2022 11:20:23 -0500 Subject: [PATCH 29/33] Fix dependencies in overlay modules so that example ITs pass --- dspace/modules/additions/pom.xml | 68 ++++++++++++++++++++++---------- dspace/modules/server/pom.xml | 33 ++++++++++++++++ 2 files changed, 81 insertions(+), 20 deletions(-) diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 2abc12b529..569ca8ce25 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -254,26 +254,54 @@ - - org.apache.lucene - lucene-core - - - org.apache.lucene - lucene-analyzers-icu - test - - - org.apache.lucene - lucene-analyzers-smartcn - test - - - org.apache.lucene - lucene-analyzers-stempel - test - - + + org.apache.solr + solr-solrj + ${solr.client.version} + + + + + org.apache.solr + solr-core + ${solr.client.version} + 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 + test + + + org.apache.lucene + lucene-analyzers-smartcn + test + + + org.apache.lucene + lucene-analyzers-stempel + test + junit junit diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index ad47074392..dd854c4215 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -278,6 +278,11 @@ just adding new jar in the classloader dspace-server-webapp war + + org.apache.solr + solr-solrj + ${solr.client.version} + @@ -324,6 +329,34 @@ just adding new jar in the classloader mockito-inline test + + + + org.apache.solr + solr-core + ${solr.client.version} + 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 From f02d8ab04a9fa328306767de9db8e27382437ed1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 30 Mar 2022 13:52:44 -0500 Subject: [PATCH 30/33] Add test files for major text-based formats & test to verify all are indexable. Also add CSV to list of formats & include it. --- .../TikaTextExtractionFilterTest.java | 152 ++++++++++- .../org/dspace/app/mediafilter/test.csv | 4 + .../mediafilter/{wordtest.doc => test.doc} | Bin .../mediafilter/{wordtest.docx => test.docx} | Bin .../org/dspace/app/mediafilter/test.odp | Bin 0 -> 25579 bytes .../org/dspace/app/mediafilter/test.ods | Bin 0 -> 2886 bytes .../org/dspace/app/mediafilter/test.odt | Bin 0 -> 5653 bytes .../org/dspace/app/mediafilter/test.ppt | Bin 0 -> 96256 bytes .../org/dspace/app/mediafilter/test.pptx | Bin 0 -> 37911 bytes .../org/dspace/app/mediafilter/test.rtf | 239 ++++++++++++++++++ .../org/dspace/app/mediafilter/test.xls | Bin 0 -> 23552 bytes .../org/dspace/app/mediafilter/test.xlsx | Bin 0 -> 8466 bytes dspace/config/dspace.cfg | 15 +- .../config/registries/bitstream-formats.xml | 9 + 14 files changed, 411 insertions(+), 8 deletions(-) create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.csv rename dspace-api/src/test/resources/org/dspace/app/mediafilter/{wordtest.doc => test.doc} (100%) rename dspace-api/src/test/resources/org/dspace/app/mediafilter/{wordtest.docx => test.docx} (100%) create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odp create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ods create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.odt create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.ppt create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.pptx create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.rtf create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xls create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xlsx 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 index 5ebc85c956..b7d0ed5a3b 100644 --- a/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java +++ b/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java @@ -22,9 +22,11 @@ import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.Test; /** - * Drive the POI-based MS Word filter. + * Test the TikaTextExtractionFilter using test files for all major formats. + * The test files used below are all located at [dspace-api]/src/main/resources/org/dspace/app/mediafilter/ * * @author mwood + * @author Tim Donohue */ public class TikaTextExtractionFilterTest extends AbstractUnitTest { @@ -94,7 +96,7 @@ public class TikaTextExtractionFilterTest extends AbstractUnitTest { throws Exception { TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); - InputStream source = getClass().getResourceAsStream("wordtest.doc"); + 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")); } @@ -110,11 +112,43 @@ public class TikaTextExtractionFilterTest extends AbstractUnitTest { throws Exception { TikaTextExtractionFilter instance = new TikaTextExtractionFilter(); - InputStream source = getClass().getResourceAsStream("wordtest.docx"); + 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. @@ -163,6 +197,118 @@ public class TikaTextExtractionFilterTest extends AbstractUnitTest { 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. * diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.csv b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.csv new file mode 100644 index 0000000000..07c22ff0bf --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.csv @@ -0,0 +1,4 @@ +row1,row2,row3,row4 +"data1,2","data 2,2","data3,2","data4,2" +"data1,3","data 2,3","data3,3","data4,3" +"data1,4","data2,4","data3,4","data4,4" diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/wordtest.doc b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.doc similarity index 100% rename from dspace-api/src/test/resources/org/dspace/app/mediafilter/wordtest.doc rename to dspace-api/src/test/resources/org/dspace/app/mediafilter/test.doc diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/wordtest.docx b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.docx similarity index 100% rename from dspace-api/src/test/resources/org/dspace/app/mediafilter/wordtest.docx rename to dspace-api/src/test/resources/org/dspace/app/mediafilter/test.docx 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 0000000000000000000000000000000000000000..4701884a8a62eb650dadce702c25c74b2e1e7448 GIT binary patch literal 25579 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 QaKgfv(u{)S`(d8{0^$hQa{vGU literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3c996a1f46c41bfbc1b8e7da638a57458072b45e GIT binary patch literal 5653 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bb3a3d6b41e2b2123eddf7183c1906369a89ed7c GIT binary patch literal 96256 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2c27ad1630b914d4f731dcbc905b83e21c583755 GIT binary patch literal 37911 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 ui`P$m@OP!uLlnGHY#`~m>iJRpJJfBPSmtfpE3 literal 0 HcmV?d00001 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.xls b/dspace-api/src/test/resources/org/dspace/app/mediafilter/test.xls new file mode 100644 index 0000000000000000000000000000000000000000..1ebc20bc3810a239e20fdb8d221b8830594bce07 GIT binary patch literal 23552 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 + + text/csv + CSV + Comma-Separated Values + 1 + false + csv + + application/msword Microsoft Word From 476fe7220dd2ed47b4a2ec808a3e37d53d41e154 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 30 Mar 2022 14:43:29 -0500 Subject: [PATCH 31/33] Minor fix to BitstreamFormatRestRepositoryIT after adding new format (CSV) --- .../org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java index 48ad410d00..1a6cc29ca7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamFormatRestRepositoryIT.java @@ -56,7 +56,7 @@ public class BitstreamFormatRestRepositoryIT extends AbstractControllerIntegrati @Autowired private BitstreamFormatConverter bitstreamFormatConverter; - private final int DEFAULT_AMOUNT_FORMATS = 80; + private final int DEFAULT_AMOUNT_FORMATS = 81; @Test public void findAllPaginationTest() throws Exception { From 9ef561a5d01189f37b6dcc37bb350a3981b40f58 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 30 Mar 2022 16:52:39 -0500 Subject: [PATCH 32/33] Switch to System.err.format & correct spacing in error messages. --- .../dspace/app/mediafilter/TikaTextExtractionFilter.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 index 909291e450..07b1576086 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java @@ -79,14 +79,13 @@ public class TikaTextExtractionFilter tika.setMaxStringLength(maxChars); // Tell Tika the maximum number of characters to extract extractedText = tika.parseToString(source); } catch (IOException e) { - System.err.println("Unable to extract text from bitstream in Item " + currentItem.getID().toString()); + 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.println("OutOfMemoryError occurred when extracting text from bitstream in Item " + - currentItem.getID().toString() + - "You may wish to enable 'textextractor.use-temp-file'."); + 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); From b5876852d90b7f07f25d9d1eb0228460b1686182 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 4 Apr 2022 10:28:04 -0500 Subject: [PATCH 33/33] Resolve feedback. Ensure errors are re-thrown & fix comment. --- .../mediafilter/TikaTextExtractionFilter.java | 18 ++++++++++++------ .../TikaTextExtractionFilterTest.java | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) 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 index 07b1576086..e83bf706ed 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java @@ -136,12 +136,15 @@ public class TikaTextExtractionFilter * Write all extracted characters directly to the temp file. */ @Override - public void characters(char[] ch, int start, int length) { + public void characters(char[] ch, int start, int length) throws SAXException { try { writer.append(new String(ch), start, length); } catch (IOException e) { - log.error("Could not append to temporary file at {} when performing text extraction", - tempExtractedTextFile.getAbsolutePath(), 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); } } @@ -151,12 +154,15 @@ public class TikaTextExtractionFilter * (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) { + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { try { writer.append(new String(ch), start, length); } catch (IOException e) { - log.error("Could not append to temporary file at {} when performing text extraction", - tempExtractedTextFile.getAbsolutePath(), 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); } } }); 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 index b7d0ed5a3b..9db1ef7776 100644 --- a/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java +++ b/dspace-api/src/test/java/org/dspace/app/mediafilter/TikaTextExtractionFilterTest.java @@ -23,7 +23,7 @@ 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/main/resources/org/dspace/app/mediafilter/ + * The test files used below are all located at [dspace-api]/src/test/resources/org/dspace/app/mediafilter/ * * @author mwood * @author Tim Donohue