From 27b75c3e70985a6b9b8bf1ad01171681813b7753 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Mon, 15 Jan 2024 16:58:12 +0200 Subject: [PATCH 01/67] [DSC-1470] add subscriptions_statistics email template --- dspace/config/emails/subscriptions_statistics | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 dspace/config/emails/subscriptions_statistics diff --git a/dspace/config/emails/subscriptions_statistics b/dspace/config/emails/subscriptions_statistics new file mode 100644 index 00000000000..c306d6fc7a5 --- /dev/null +++ b/dspace/config/emails/subscriptions_statistics @@ -0,0 +1,6 @@ +## E-mail sent to designated address about statistics on subscribed items +## +#set($subject = "${config.get('dspace.name')}: Statistics of records which you are subscribed") + +This automatic email is sent by ${config.get('dspace.name')} based on the subscribed statistics updates. +See additional details in the file attached. \ No newline at end of file From c1373a3d4d4bde7515e1f2ea1c58eca961f8041a Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Mon, 15 Jan 2024 16:58:34 +0200 Subject: [PATCH 02/67] [DSC-1470] update StatisticsGenerator to use subscriptions_statistics email template --- .../subscriptions/StatisticsGenerator.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java index 43ff6b71d4b..1e38e8d5794 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java @@ -11,6 +11,7 @@ import java.io.File; import java.io.FileOutputStream; import java.util.List; +import java.util.Locale; import java.util.Objects; import org.apache.logging.log4j.LogManager; @@ -25,6 +26,7 @@ import org.dspace.app.metrics.CrisMetrics; import org.dspace.core.Context; import org.dspace.core.Email; +import org.dspace.core.I18nUtil; import org.dspace.eperson.EPerson; import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; @@ -40,23 +42,17 @@ public class StatisticsGenerator { private static final Logger log = LogManager.getLogger(StatisticsGenerator.class); - @Autowired - private ConfigurationService configurationService; - public void notifyForSubscriptions(Context c, EPerson ePerson, List crisMetricsList) { try { // send the notification to the user - if (Objects.nonNull(ePerson) && !crisMetricsList.isEmpty()) { - Email email = new Email(); - String name = configurationService.getProperty("dspace.name"); - File attachment = generateExcel(crisMetricsList, c); - email.addAttachment(attachment, "subscriptions.xlsx"); - email.setSubject(name + ": Statistics of records which you are subscribed"); - email.setContent("intro", - "This automatic email is sent by " + name + " based on the subscribed statistics updates.\n\n" + - "See additional details in the file attached."); - email.send(); + if (Objects.isNull(ePerson) || crisMetricsList.isEmpty()) { + return; } + Locale supportedLocale = I18nUtil.getEPersonLocale(ePerson); + Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "subscriptions_statistics")); + email.addRecipient(ePerson.getEmail()); + email.addAttachment(generateExcel(crisMetricsList, c), "subscriptions.xlsx"); + email.send(); } catch (Exception ex) { // log this email error log.warn("cannot email user" + " eperson_id" + ePerson.getID() From c92f2ac452ab6a916c633ada7f7a6ebbd33c53ee Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Mon, 15 Jan 2024 17:00:18 +0200 Subject: [PATCH 03/67] [DSC-1470] remove unused imports --- .../main/java/org/dspace/subscriptions/StatisticsGenerator.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java index 1e38e8d5794..c6549f74490 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java @@ -28,8 +28,6 @@ import org.dspace.core.Email; import org.dspace.core.I18nUtil; import org.dspace.eperson.EPerson; -import org.dspace.services.ConfigurationService; -import org.springframework.beans.factory.annotation.Autowired; /** From 03fc08b11ccd84177ce5d37248883e1191b6c172 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Thu, 4 Jan 2024 13:53:02 +0100 Subject: [PATCH 04/67] [DSC-1460] DOIOrganiser performance improvements --- .../dspace/core/HibernateDBConnection.java | 6 ++++++ .../org/dspace/identifier/DOIServiceImpl.java | 8 ++++++- .../org/dspace/identifier/dao/DOIDAO.java | 2 +- .../identifier/dao/impl/DOIDAOImpl.java | 4 ++-- .../dspace/identifier/doi/DOIOrganiser.java | 21 +++++++++++++++---- .../dspace/identifier/service/DOIService.java | 12 +++++++++++ 6 files changed, 45 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index 858149a64f3..c76e818ebff 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -20,6 +20,7 @@ import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.handle.Handle; +import org.dspace.identifier.DOI; import org.dspace.storage.rdbms.DatabaseConfigVO; import org.hibernate.FlushMode; import org.hibernate.Hibernate; @@ -280,6 +281,11 @@ public void uncacheEntity(E entity) throws SQLExcep } } + if (entity instanceof DOI) { + DOI doi = (DOI) entity; + uncacheEntity(doi.getDSpaceObject()); + } + // ITEM if (entity instanceof Item) { Item item = (Item) entity; diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java b/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java index 99643db33fa..2e726234a28 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIServiceImpl.java @@ -161,7 +161,13 @@ public String formatIdentifier(String identifier) throws DOIIdentifierException @Override public List getDOIsByStatus(Context context, List statuses) throws SQLException { - return doiDAO.findByStatus(context, statuses); + return doiDAO.findByStatus(context, statuses, -1, -1); + } + + @Override + public List getDOIsByStatus(Context context, List statuses, int offset, int limit) + throws SQLException { + return doiDAO.findByStatus(context, statuses, offset, limit); } @Override diff --git a/dspace-api/src/main/java/org/dspace/identifier/dao/DOIDAO.java b/dspace-api/src/main/java/org/dspace/identifier/dao/DOIDAO.java index 8085c6599b6..df75ced157a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/dao/DOIDAO.java +++ b/dspace-api/src/main/java/org/dspace/identifier/dao/DOIDAO.java @@ -31,7 +31,7 @@ public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso, List findSimilarNotInState(Context context, String doi, List statuses, boolean dsoNotNull) throws SQLException; - public List findByStatus(Context context, List statuses) throws SQLException; + public List findByStatus(Context context, List statuses, int offset, int limit) throws SQLException; public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/identifier/dao/impl/DOIDAOImpl.java b/dspace-api/src/main/java/org/dspace/identifier/dao/impl/DOIDAOImpl.java index 784fec1d889..0811a7adfb6 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/dao/impl/DOIDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/dao/impl/DOIDAOImpl.java @@ -70,7 +70,7 @@ public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso, List findByStatus(Context context, List statuses) throws SQLException { + public List findByStatus(Context context, List statuses, int offset, int limit) throws SQLException { CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, DOI.class); Root doiRoot = criteriaQuery.from(DOI.class); @@ -80,7 +80,7 @@ public List findByStatus(Context context, List statuses) throws SQ orPredicates.add(criteriaBuilder.equal(doiRoot.get(DOI_.status), status)); } criteriaQuery.where(criteriaBuilder.or(orPredicates.toArray(new Predicate[] {}))); - return list(context, criteriaQuery, false, DOI.class, -1, -1); + return list(context, criteriaQuery, false, DOI.class, limit, offset); } @Override diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index ad885314bb2..a90ccc0980b 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -132,6 +132,8 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args "Perform online deletion for all identifiers queued for deletion."); options.addOption("q", "quiet", false, "Turn the command line output off."); + options.addOption("o", "offset", true, "The records offset"); + options.addOption("li", "limit", true, "The records limit"); Option filterDoi = Option.builder().optionalArg(true).longOpt("filter").hasArg().argName("filterName") .desc("Use the specified filter name instead of the provider's filter. Defaults to a special " + @@ -212,6 +214,17 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args organiser.list("deletion", null, null, DOIIdentifierProvider.TO_BE_DELETED); } + int limit = -1; + int offset = -1; + + if (line.hasOption("li")) { + limit = Integer.valueOf(line.getOptionValue("li")); + } + + if (line.hasOption("o")) { + offset = Integer.valueOf(line.getOptionValue("o")); + } + DOIService doiService = IdentifierServiceFactory.getInstance().getDOIService(); // Do we get a filter? if (line.hasOption("filter")) { @@ -224,7 +237,7 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args if (line.hasOption('s')) { try { List dois = doiService - .getDOIsByStatus(context, Arrays.asList(DOIIdentifierProvider.TO_BE_RESERVED)); + .getDOIsByStatus(context, Arrays.asList(DOIIdentifierProvider.TO_BE_RESERVED), offset, limit); if (dois.isEmpty()) { System.err.println("There are no objects in the database " + "that could be reserved."); @@ -243,7 +256,7 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args if (line.hasOption('r')) { try { List dois = doiService - .getDOIsByStatus(context, Arrays.asList(DOIIdentifierProvider.TO_BE_REGISTERED)); + .getDOIsByStatus(context, Arrays.asList(DOIIdentifierProvider.TO_BE_REGISTERED), offset, limit); if (dois.isEmpty()) { System.err.println("There are no objects in the database " + "that could be registered."); @@ -265,7 +278,7 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args List dois = doiService.getDOIsByStatus(context, Arrays.asList( DOIIdentifierProvider.UPDATE_BEFORE_REGISTRATION, DOIIdentifierProvider.UPDATE_RESERVED, - DOIIdentifierProvider.UPDATE_REGISTERED)); + DOIIdentifierProvider.UPDATE_REGISTERED), offset, limit); if (dois.isEmpty()) { System.err.println("There are no objects in the database " + "whose metadata needs an update."); @@ -284,7 +297,7 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args if (line.hasOption('d')) { try { List dois = doiService - .getDOIsByStatus(context, Arrays.asList(DOIIdentifierProvider.TO_BE_DELETED)); + .getDOIsByStatus(context, Arrays.asList(DOIIdentifierProvider.TO_BE_DELETED), offset, limit); if (dois.isEmpty()) { System.err.println("There are no objects in the database " + "that could be deleted."); diff --git a/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java b/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java index 5bd68a90615..e8236a013f7 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java +++ b/dspace-api/src/main/java/org/dspace/identifier/service/DOIService.java @@ -124,6 +124,18 @@ public String formatIdentifier(String identifier) */ public List getDOIsByStatus(Context context, List statuses) throws SQLException; + /** + * Find all DOIs that have one of a given set of statuses. + * @param context current DSpace session. + * @param statuses desired statuses. + * @param offset offset value + * @param limit limited number of items + * @return all DOIs having any of the given statuses. + * @throws SQLException passed through. + */ + public List getDOIsByStatus(Context context, List statuses, int offset, int limit) + throws SQLException; + /** * Find all DOIs that are similar to the specified pattern and not in the * specified states. From b815bed1120fb066274ba353c9548cc0d0c55b0a Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Tue, 9 Jan 2024 19:39:39 +0100 Subject: [PATCH 05/67] CST-13236 Improve performance of ItemEnhancer working in an incremental way at the database level --- .../content/enhancer/script/ItemEnhancerScript.java | 13 ++++++------- .../enhancer/service/ItemEnhancerService.java | 13 +++++++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java index 2c4d1f20346..a98e423cd26 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java @@ -57,7 +57,7 @@ public void internalRun() throws Exception { context.turnOffAuthorisationSystem(); try { - enhanceItems(); + enhanceItems(context); context.complete(); handler.logInfo("Enhancement completed with success"); } catch (Exception e) { @@ -68,7 +68,7 @@ public void internalRun() throws Exception { } } - private void enhanceItems() { + private void enhanceItems(Context context) { findItemsToEnhance().forEachRemaining(this::enhanceItem); } @@ -87,14 +87,13 @@ private void enhanceItem(Item item) { } else { itemEnhancerService.enhance(context, item); } - - uncacheItem(item); - + storeChangesAndFreeResources(context); } - private void uncacheItem(Item item) { + private void storeChangesAndFreeResources(Context context) { try { - context.uncacheEntity(item); + context.commit(); + context.clear(); } catch (SQLException e) { throw new SQLRuntimeException(e); } diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java b/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java index 5b3b419bfa8..62349b06bdf 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/service/ItemEnhancerService.java @@ -20,7 +20,15 @@ public interface ItemEnhancerService { /** * Enhances the given item with all the item enhancers defined adding virtual - * metadata fields on it. + * metadata fields on it. ItemEnhancer will use the information stored in the + * source metadata to decide if virtual metadata must be calculated. This could + * lead to stale information if the given item is linked to the same related items + * than before but in the mean time the related items have been changed in a way + * that could affect the generated virtual metadata (for example a publication + * listing 3 authors assuming that we are flatting on the publication the information + * about the author current affiliation would not update the virtual affiliation + * if this method is invoked on the item without touching the author list - in this + * scenario you need to use the {@link #forceEnhancement(Context, Item)} method * * @param context the DSpace Context * @param item the item to enhance @@ -29,7 +37,8 @@ public interface ItemEnhancerService { /** * Remove all the already calculated virtual metadata fields from the given item - * and perform a new enhancement. + * and perform a new enhancement. This remove the risk of stale data related to + * unforeseen update on the related items * * @param context the DSpace Context * @param item the item to enhance From 5da479524b4375471b2e02d1613b641171c49617 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Wed, 10 Jan 2024 22:27:25 +0100 Subject: [PATCH 06/67] CST-13236 iterate over all the items in a paginated way committing after each page --- .../enhancer/script/ItemEnhancerScript.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java index a98e423cd26..d67063210c6 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/script/ItemEnhancerScript.java @@ -31,6 +31,7 @@ * */ public class ItemEnhancerScript extends DSpaceRunnable> { + private final int PAGE_SIZE = 20; private ItemService itemService; @@ -69,34 +70,32 @@ public void internalRun() throws Exception { } private void enhanceItems(Context context) { - findItemsToEnhance().forEachRemaining(this::enhanceItem); + try { + int total = itemService.countArchivedItems(context); + for (int offset = 0; offset < total; offset += PAGE_SIZE) { + findItemsToEnhance(offset).forEachRemaining(this::enhanceItem); + } + context.commit(); + context.clear(); + } catch (SQLException e) { + throw new SQLRuntimeException(e); + } } - private Iterator findItemsToEnhance() { + private Iterator findItemsToEnhance(int offset) { try { - return itemService.findAll(context); + return itemService.findAll(context, PAGE_SIZE, offset); } catch (SQLException e) { throw new SQLRuntimeException(e); } } private void enhanceItem(Item item) { - if (force) { itemEnhancerService.forceEnhancement(context, item); } else { itemEnhancerService.enhance(context, item); } - storeChangesAndFreeResources(context); - } - - private void storeChangesAndFreeResources(Context context) { - try { - context.commit(); - context.clear(); - } catch (SQLException e) { - throw new SQLRuntimeException(e); - } } private void assignCurrentUserInContext() throws SQLException { From a847dd14ac31292a1d376c7b82162c4138cad96a Mon Sep 17 00:00:00 2001 From: Nikita Krivonosov Date: Tue, 30 Jan 2024 20:19:30 +0100 Subject: [PATCH 07/67] [DSC-1473] - ItemEnhancer: improve the performance of the script --- dspace-api/src/main/java/org/dspace/core/Context.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 4d3079240c3..ad82ee83a84 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.authorize.ResourcePolicy; import org.dspace.content.DSpaceObject; +import org.dspace.core.exception.SQLRuntimeException; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; @@ -33,6 +34,7 @@ import org.dspace.storage.rdbms.DatabaseConfigVO; import org.dspace.storage.rdbms.DatabaseUtils; import org.dspace.utils.DSpace; +import org.hibernate.Session; import org.springframework.util.CollectionUtils; /** @@ -451,6 +453,14 @@ public void commit() throws SQLException { } } + public void clear() { + try { + ((Session) dbConnection.getSession()).clear(); + reloadContextBoundEntities(); + } catch (SQLException e) { + throw new SQLRuntimeException(e); + } + } /** * Dispatch any events (cached in current Context) to configured EventListeners (consumers) From 0a7f0393fbe679ae5dbce7b3928fc992a96a638a Mon Sep 17 00:00:00 2001 From: eskander Date: Thu, 15 Feb 2024 19:03:30 +0200 Subject: [PATCH 08/67] [DSC-1256] Avoid NPE checking bitstream content when metadata are missing on cris layout --- .../org/dspace/content/ItemServiceImpl.java | 54 ++++++++++--------- .../impl/CrisLayoutBoxServiceImpl.java | 10 ++-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java index 6b3ef003edc..c801b8cac94 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -216,28 +216,6 @@ public Thumbnail getThumbnail(Context context, Item item) throws SQLException { if (thumbnail != null) { return thumbnail; } - // If no thumbnail is retrieved by the first strategy - // then use the fallback strategy - Bitstream thumbBitstream = null; - List originalBundles = getBundles(item, "ORIGINAL"); - Bitstream primaryBitstream = null; - if (CollectionUtils.isNotEmpty(originalBundles)) { - primaryBitstream = originalBundles.get(0).getPrimaryBitstream(); - } - if (primaryBitstream == null) { - primaryBitstream = bitstreamService.getFirstBitstream(item, "ORIGINAL"); - } - if (primaryBitstream != null) { - thumbBitstream = bitstreamService.getThumbnail(context, primaryBitstream); - if (thumbBitstream == null) { - thumbBitstream = bitstreamService.getFirstBitstream(item, "THUMBNAIL"); - } - } - - if (thumbBitstream != null) { - return new Thumbnail(thumbBitstream, primaryBitstream); - } - return null; } @@ -252,7 +230,27 @@ private Thumbnail thumbnailLayoutTabConfigurationStrategy(Context context, Item List thumbFields = getThumbnailFields(crisLayoutTabs); if (CollectionUtils.isEmpty(thumbFields)) { - return null; + // If no thumbnail is retrieved by the first strategy + // then use the fallback strategy + Bitstream thumbBitstream = null; + List originalBundles = getBundles(item, "ORIGINAL"); + Bitstream primaryBitstream = null; + if (CollectionUtils.isNotEmpty(originalBundles)) { + primaryBitstream = originalBundles.get(0).getPrimaryBitstream(); + } + if (primaryBitstream == null) { + primaryBitstream = bitstreamService.getFirstBitstream(item, "ORIGINAL"); + } + if (primaryBitstream != null) { + thumbBitstream = bitstreamService.getThumbnail(context, primaryBitstream); + if (thumbBitstream == null) { + thumbBitstream = bitstreamService.getFirstBitstream(item, "THUMBNAIL"); + } + } + + if (thumbBitstream != null) { + return new Thumbnail(thumbBitstream, primaryBitstream); + } } return retrieveThumbnailFromFields(context, item, thumbFields); } @@ -312,9 +310,13 @@ private Thumbnail retrieveThumbnail(Context context, Item item, String bundle, if (CollectionUtils.isNotEmpty(bundles)) { Optional primaryBitstream = bundles.get(0).getBitstreams().stream().filter(bitstream -> { return bitstream.getMetadata().stream().anyMatch(metadataValue -> { - return metadataValue.getMetadataField().getID() == metadataField.getID() - && metadataValue.getValue() != null - && metadataValue.getValue().equalsIgnoreCase(value); + if (metadataField != null) { + return metadataValue.getMetadataField().getID() == metadataField.getID() + && metadataValue.getValue() != null + && metadataValue.getValue().equalsIgnoreCase(value); + } else { + return true; + } }); }).findFirst(); if (primaryBitstream.isEmpty()) { diff --git a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java index acd5d38c0b4..c15a2f792bc 100644 --- a/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/layout/service/impl/CrisLayoutBoxServiceImpl.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -208,13 +209,10 @@ private boolean isMetadataFieldPresent(DSpaceObject item, MetadataField metadata } private boolean isBitstreamPresent(Context context, Item item, CrisLayoutFieldBitstream field) { - - Map filters = Map.of(); - - if (field.getMetadataField() != null) { - filters = Map.of(field.getMetadataField().toString('.'), field.getMetadataValue()); + Map filters = new HashMap<>(); + if (field.getMetadataField() != null && StringUtils.isNotBlank(field.getMetadataValue())) { + filters.put(field.getMetadataField().toString('.'), field.getMetadataValue()); } - try { return bitstreamService.findShowableByItem(context, item.getID(), field.getBundle(), filters).size() > 0; } catch (SQLException e) { From 0b64e6d6b348c58d22043b90530e1948e2fa9ef2 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Mon, 4 Mar 2024 17:27:53 +0100 Subject: [PATCH 09/67] DSC-1459 Allows multiple entity types for RelatedEntityItemEnhancer, provide a bridge between the ReferCrosswalk virtual and the ItemEnhancer --- .../impl/RelatedEntityItemEnhancer.java | 73 ++++--------------- .../impl/RelatedEntityItemEnhancerUtils.java | 72 ++++++++++++++++++ .../VirtualFieldToEnhancedMetadata.java | 62 ++++++++++++++++ .../api/extra-metadata-enhancers-for-test.xml | 6 +- dspace/config/spring/api/crosswalks.xml | 4 +- .../config/spring/api/metadata-enhancers.xml | 36 ++++----- 6 files changed, 170 insertions(+), 83 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancerUtils.java create mode 100644 dspace-api/src/main/java/org/dspace/content/integration/crosswalks/virtualfields/VirtualFieldToEnhancedMetadata.java diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java index 7a6f2927092..4041df194ce 100644 --- a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancer.java @@ -49,10 +49,13 @@ public class RelatedEntityItemEnhancer extends AbstractItemEnhancer { @Autowired private ItemService itemService; + @Autowired + private RelatedEntityItemEnhancerUtils relatedEntityItemEnhancerUtils; + /** - * the entity that can be extended by this enhancer, i.e. Publication + * the entities that can be extended by this enhancer, i.e. Publication */ - private String sourceEntityType; + private List sourceEntityTypes; /** * the metadata used to navigate the relation, i.e. dc.contributor.author @@ -66,7 +69,7 @@ public class RelatedEntityItemEnhancer extends AbstractItemEnhancer { @Override public boolean canEnhance(Context context, Item item) { - return sourceEntityType == null || sourceEntityType.equals(itemService.getEntityTypeLabel(item)); + return sourceEntityTypes == null || sourceEntityTypes.contains(itemService.getEntityTypeLabel(item)); } @Override @@ -81,7 +84,8 @@ public boolean enhance(Context context, Item item, boolean deepMode) { throw new SQLRuntimeException(e); } } else { - Map> currMetadataValues = getCurrentVirtualsMap(item); + Map> currMetadataValues = relatedEntityItemEnhancerUtils + .getCurrentVirtualsMap(item, getVirtualQualifier()); Map> toBeMetadataValues = getToBeVirtualMetadata(context, item); if (!equivalent(currMetadataValues, toBeMetadataValues)) { try { @@ -212,7 +216,8 @@ private boolean cleanObsoleteVirtualFields(Context context, Item item) throws SQ private List getObsoleteVirtualFields(Item item) { List obsoleteVirtualFields = new ArrayList<>(); - Map> currentVirtualsMap = getCurrentVirtualsMap(item); + Map> currentVirtualsMap = relatedEntityItemEnhancerUtils + .getCurrentVirtualsMap(item, getVirtualQualifier()); Set virtualSources = getVirtualSources(item); for (String authority : currentVirtualsMap.keySet()) { if (!virtualSources.contains(authority)) { @@ -235,41 +240,6 @@ private Set getVirtualSources(Item item) { .collect(Collectors.toSet()); } - private Map> getCurrentVirtualsMap(Item item) { - Map> currentVirtualsMap = new HashMap>(); - List sources = itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, - VIRTUAL_SOURCE_METADATA_ELEMENT, getVirtualQualifier(), Item.ANY); - List generated = itemService.getMetadata(item, VIRTUAL_METADATA_SCHEMA, VIRTUAL_METADATA_ELEMENT, - getVirtualQualifier(), Item.ANY); - - if (sources.size() != generated.size()) { - LOGGER.error( - "inconsistent virtual metadata for the item {} got {} sources and {} generated virtual metadata", - item.getID().toString(), sources.size(), generated.size()); - } - - for (int i = 0; i < Integer.max(sources.size(), generated.size()); i++) { - String authority; - if (i < sources.size()) { - authority = sources.get(i).getValue(); - } else { - // we have less source than virtual metadata let's generate a random uuid to - // associate with these extra metadata so that they will be managed as obsolete - // value - authority = UUID.randomUUID().toString(); - } - List mvalues = currentVirtualsMap.get(authority); - if (mvalues == null) { - mvalues = new ArrayList(); - } - if (i < generated.size()) { - mvalues.add(generated.get(i)); - } - currentVirtualsMap.put(authority, mvalues); - } - return currentVirtualsMap; - } - private Optional getRelatedVirtualField(Item item, int pos) { return getVirtualFields(item).stream() .skip(pos) @@ -278,7 +248,8 @@ private Optional getRelatedVirtualField(Item item, int pos) { private boolean performEnhancement(Context context, Item item) throws SQLException { boolean result = false; - Map> currentVirtualsMap = getCurrentVirtualsMap(item); + Map> currentVirtualsMap = relatedEntityItemEnhancerUtils + .getCurrentVirtualsMap(item, getVirtualQualifier()); Set virtualSources = getVirtualSources(item); for (String authority : virtualSources) { boolean foundAtLeastOne = false; @@ -345,24 +316,8 @@ private void addVirtualSourceField(Context context, Item item, String sourceValu getVirtualQualifier(), null, sourceValueAuthority); } - public void setSourceEntityType(String sourceEntityType) { - this.sourceEntityType = sourceEntityType; - } - - @Deprecated - public void setSourceItemMetadataField(String sourceItemMetadataField) { - LOGGER.warn( - "RelatedEntityItemEnhancer configured using the old single source item metadata field, " - + "please update the configuration to use the list"); - this.sourceItemMetadataFields = List.of(sourceItemMetadataField); - } - - @Deprecated - public void setRelatedItemMetadataField(String relatedItemMetadataField) { - LOGGER.warn( - "RelatedEntityItemEnhancer configured using the old single related item metadata field, " - + "please update the configuration to use the list"); - this.relatedItemMetadataFields = List.of(relatedItemMetadataField); + public void setSourceEntityTypes(List sourceEntityTypes) { + this.sourceEntityTypes = sourceEntityTypes; } public void setRelatedItemMetadataFields(List relatedItemMetadataFields) { diff --git a/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancerUtils.java b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancerUtils.java new file mode 100644 index 00000000000..064223b5642 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/enhancer/impl/RelatedEntityItemEnhancerUtils.java @@ -0,0 +1,72 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.enhancer.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.service.ItemService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Utility methods used by {@link RelatedEntityItemEnhancer} + * + * @author Andrea Bollini (andrea.bollini at 4science.com) + * + */ +public class RelatedEntityItemEnhancerUtils { + + @Autowired + private ItemService itemService; + + private static final Logger LOGGER = LoggerFactory.getLogger(RelatedEntityItemEnhancerUtils.class); + + public Map> getCurrentVirtualsMap(Item item, String virtualQualifier) { + Map> currentVirtualsMap = new HashMap>(); + List sources = itemService.getMetadata(item, RelatedEntityItemEnhancer.VIRTUAL_METADATA_SCHEMA, + RelatedEntityItemEnhancer.VIRTUAL_SOURCE_METADATA_ELEMENT, virtualQualifier, Item.ANY); + List generated = itemService.getMetadata(item, + RelatedEntityItemEnhancer.VIRTUAL_METADATA_SCHEMA, RelatedEntityItemEnhancer.VIRTUAL_METADATA_ELEMENT, + virtualQualifier, Item.ANY); + + if (sources.size() != generated.size()) { + LOGGER.error( + "inconsistent virtual metadata for the item {} got {} sources and {} generated virtual metadata", + item.getID().toString(), sources.size(), generated.size()); + } + + for (int i = 0; i < Integer.max(sources.size(), generated.size()); i++) { + String authority; + if (i < sources.size()) { + authority = sources.get(i).getValue(); + } else { + // we have less source than virtual metadata let's generate a random uuid to + // associate with these extra metadata so that they will be managed as obsolete + // value + authority = UUID.randomUUID().toString(); + } + List mvalues = currentVirtualsMap.get(authority); + if (mvalues == null) { + mvalues = new ArrayList(); + } + if (i < generated.size()) { + mvalues.add(generated.get(i)); + } + currentVirtualsMap.put(authority, mvalues); + } + return currentVirtualsMap; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/virtualfields/VirtualFieldToEnhancedMetadata.java b/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/virtualfields/VirtualFieldToEnhancedMetadata.java new file mode 100644 index 00000000000..2ea11f6c4ea --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/integration/crosswalks/virtualfields/VirtualFieldToEnhancedMetadata.java @@ -0,0 +1,62 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.integration.crosswalks.virtualfields; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.dspace.content.Item; +import org.dspace.content.MetadataValue; +import org.dspace.content.enhancer.impl.RelatedEntityItemEnhancerUtils; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.core.CrisConstants; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Implementation of {@link VirtualField} that returns the values from the a + * cris.virtual. metadata using the provided in the form of + * -- as source metadata. + * Source metadata that are not found in the cris.virtualsource. leads to a PLACEHOLDER + * + * @author Andrea Bollini at 4science.comm + * + */ +public class VirtualFieldToEnhancedMetadata implements VirtualField { + + @Autowired + private ItemService itemService; + + @Autowired + private RelatedEntityItemEnhancerUtils relatedEntityItemEnhancerUtils; + + @Override + public String[] getMetadata(Context context, Item item, String fieldName) { + ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + String[] fieldBits = fieldName.split("\\."); + if (fieldBits.length != 3) { + throw new IllegalArgumentException( + "VirtualFieldToEnhancedMetadata must be used specifying the EnhancedMetadata qualifier as " + + "element and the source metadata as qualifier, i.e. virtual.department.dc-contributor-author"); + } + String virtualQualifier = fieldBits[1]; + String metadata = fieldBits[2].replaceAll("-", "."); + Map> map = relatedEntityItemEnhancerUtils.getCurrentVirtualsMap(item, + virtualQualifier); + List values = itemService.getMetadataByMetadataString(item, metadata).stream() + .map(mv -> mv.getAuthority() != null && map.containsKey(mv.getAuthority()) + ? map.get(mv.getAuthority()).get(0).getValue() + : CrisConstants.PLACEHOLDER_PARENT_METADATA_VALUE) + .collect(Collectors.toList()); + String[] resultValues = values.toArray(new String[0]); + return resultValues; + } + +} diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml index 0311d8a26aa..a713d3acbcc 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/extra-metadata-enhancers-for-test.xml @@ -21,7 +21,11 @@ - + + + TestEntity + + dc.contributor.author diff --git a/dspace/config/spring/api/crosswalks.xml b/dspace/config/spring/api/crosswalks.xml index 9184a56482d..8d7bb2ae910 100644 --- a/dspace/config/spring/api/crosswalks.xml +++ b/dspace/config/spring/api/crosswalks.xml @@ -554,7 +554,9 @@ - + +