From 702eac1e9d25477d2171105ea2ae4fd895b0803a Mon Sep 17 00:00:00 2001
From: Homada Boumedane <homada.boumedane@unige.ch>
Date: Mon, 17 Feb 2025 15:34:50 +0100
Subject: [PATCH 1/3] refactor: dissemination package

use a checksum on the list of subsetItems to discriminate and reuse them if we ask for the same dip.

Closes: DLCM-2830
---
 .../java/ch/dlcm/business/OrderService.java   |  53 ++++++++-
 .../controller/access/SearchController.java   | 108 ++++++++++++------
 .../DisseminationInfoPackageRepository.java   |   3 +
 .../ch/dlcm/repository/OrderRepository.java   |  14 +++
 .../dissemination/BasicDipBuilderService.java |  14 ++-
 .../dissemination/DipBuilderService.java      |  20 +++-
 .../DisseminationPolicyProvider.java          |   3 +
 .../HederaDipBuilderService.java              |   7 +-
 .../dissemination/IiifDipBuilderService.java  |   7 +-
 .../dissemination/OaisDipBuilderService.java  |   7 +-
 .../main/java/ch/dlcm/model/access/Order.java |  12 ++
 .../main/resources/scripts/upgrade22to30.sql  |   6 +-
 12 files changed, 207 insertions(+), 47 deletions(-)

diff --git a/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java b/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
index 16702c5150..101887cc48 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
@@ -26,9 +26,14 @@ package ch.dlcm.business;
 import static ch.dlcm.model.access.Order.OrderStatus.SUBMITTED;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
 
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.xml.transform.TransformerFactoryConfigurationError;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,9 +50,11 @@ import org.springframework.validation.FieldError;
 
 import jakarta.transaction.Transactional;
 
+import ch.unige.solidify.ChecksumAlgorithm;
 import ch.unige.solidify.SolidifyConstants;
 import ch.unige.solidify.config.SolidifyEventPublisher;
 import ch.unige.solidify.exception.SolidifyFileDeleteException;
+import ch.unige.solidify.exception.SolidifyProcessingException;
 import ch.unige.solidify.exception.SolidifyRuntimeException;
 import ch.unige.solidify.rest.ActionName;
 import ch.unige.solidify.rest.RestCollection;
@@ -55,6 +62,7 @@ import ch.unige.solidify.rest.RestCollectionPage;
 import ch.unige.solidify.rest.Result;
 import ch.unige.solidify.service.ResourceService;
 import ch.unige.solidify.util.FileTool;
+import ch.unige.solidify.util.HashTool;
 import ch.unige.solidify.util.StringTool;
 
 import ch.dlcm.config.DLCMProperties;
@@ -84,6 +92,7 @@ public class OrderService extends ResourceService<Order> {
   private final DataSize fileSizeLimit;
   private final String orderLocation;
   private final SearchMgmt searchMgmt;
+  private final ChecksumAlgorithm defaultChecksumAlgorithm;
   private final ArchivalInfoPackageService aipService;
   private final OrderSubsetItemRepository orderSubsetItemRepository;
   private final TrustedDisseminationPolicyRemoteResourceService disseminationPolicyRemoteResourceService;
@@ -99,6 +108,7 @@ public class OrderService extends ResourceService<Order> {
     this.disseminationPolicyRemoteResourceService = disseminationPolicyRemoteResourceService;
     this.fileSizeLimit = dlcmProperties.getParameters().getFileSizeLimit();
     this.orderLocation = dlcmProperties.getOrderLocation();
+    this.defaultChecksumAlgorithm = ChecksumAlgorithm.valueOf(dlcmProperties.getParameters().getDefaultChecksum());
   }
 
   public Order saveOrder(Order order) {
@@ -179,19 +189,27 @@ public class OrderService extends ResourceService<Order> {
 
   public Order createOrderForAIP(String aipId, DisseminationPolicyDto disseminationPolicy) {
     final Order order = new Order();
+    List<OrderSubsetItem> subsetItemList = disseminationPolicy.getSubsetItemList();
     order.setName(aipId);
     order.setQueryType(QueryType.DIRECT);
     order.setQuery(aipId);
     order.setDisseminationPolicyId(disseminationPolicy.getDisseminationPolicyId());
     order.setOrganizationalUnitDisseminationPolicyId(disseminationPolicy.getOrganizationalUnitDisseminationPolicyId());
+    this.calculateChecksum(order, subsetItemList); //calculate and set checksum
     final Order savedOrder = this.save(order);
-    List<OrderSubsetItem> subsetItemList = disseminationPolicy.getSubsetItemList();
+
+    if (subsetItemList == null) { // if no subsetItemList is provided, set it to an empty list
+      subsetItemList = List.of();
+    }
+
     subsetItemList.forEach(subsetItem -> {
       subsetItem.setOrder(savedOrder);
       subsetItem.setCreatedBy(savedOrder.getCreatedBy());
       subsetItem.setUpdatedBy(savedOrder.getUpdatedBy());
     });
+
     this.orderSubsetItemRepository.saveAll(subsetItemList);
+
     return savedOrder;
   }
 
@@ -212,10 +230,15 @@ public class OrderService extends ResourceService<Order> {
     this.submitOrder(order1);
   }
 
-  public List<Order> findFullDirectOrderByAipAndDisseminationPolicy(String aipId, String disseminationPolicyId) {
+  public List<Order> findDirectOrderByAipAndDisseminationPolicy(String aipId, String disseminationPolicyId) {
     return ((OrderRepository) this.itemRepository).findFullDirectOrderByAipAndDisseminationPolicyId(aipId, disseminationPolicyId);
   }
 
+  public List<Order> findDirectOrderByDisseminationPolicyAndSubsetChecksum(String aipId, String disseminationPolicyId, String checksum) {
+    return ((OrderRepository) this.itemRepository).findDirectOrderByDisseminationPolicyIdAndAipIdAndChecksum(aipId, disseminationPolicyId,
+            checksum);
+  }
+
   public List<Order> findByAip(String aipId) {
     return ((OrderRepository) this.itemRepository).findByAip(aipId);
   }
@@ -298,6 +321,32 @@ public class OrderService extends ResourceService<Order> {
     return ((OrderRepository) this.itemRepository).getSize(resId);
   }
 
+  public Optional<Order> isThereAnyOrderWithSameChecksum(Order order) {
+    return ((OrderRepository) this.itemRepository).findOrdersByChecksum(order.getChecksum(), order.getResId())
+            .stream()
+            .findFirst();
+  }
+
+  private void calculateChecksum(Order order, List<OrderSubsetItem> subsetItems) {
+    if (subsetItems != null && !subsetItems.isEmpty()) {
+      // Create a string combining all paths, sorted by path to ensure consistent checksum
+      String content = subsetItems.stream()
+              .map(OrderSubsetItem::getItemPath)
+              .sorted()
+              .collect(Collectors.joining("|"));
+      if (content.isEmpty()) {
+        order.setChecksum(null);
+        return;
+      }
+      try {
+        String hash = HashTool.hash(this.defaultChecksumAlgorithm.toString(), content.getBytes(StandardCharsets.UTF_8));
+        order.setChecksum(hash);
+      } catch (TransformerFactoryConfigurationError | NoSuchAlgorithmException e) {
+        throw new SolidifyProcessingException(e.getMessage(), e);
+      }
+    }
+  }
+
   @Override
   public OrderSpecification getSpecification(Order resource) {
     return new OrderSpecification(resource);
diff --git a/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java b/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java
index f1a8ba0e72..5afdcaf598 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java
@@ -23,13 +23,18 @@
 
 package ch.dlcm.controller.access;
 
+import static ch.dlcm.DLCMConstants.DISSEMINATION_POLICY_BASIC_ID;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
 
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+import javax.xml.transform.TransformerFactoryConfigurationError;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,10 +55,12 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import ch.unige.solidify.ChecksumAlgorithm;
 import ch.unige.solidify.SolidifyConstants;
 import ch.unige.solidify.auth.model.AuthApplicationRole;
 import ch.unige.solidify.auth.service.ApplicationRoleListService;
 import ch.unige.solidify.config.SolidifyEventPublisher;
+import ch.unige.solidify.exception.SolidifyProcessingException;
 import ch.unige.solidify.exception.SolidifyRestException;
 import ch.unige.solidify.exception.SolidifyRuntimeException;
 import ch.unige.solidify.model.dto.CitationDto;
@@ -65,6 +72,7 @@ import ch.unige.solidify.security.EveryonePermissions;
 import ch.unige.solidify.service.DownloadTokenService;
 import ch.unige.solidify.service.rest.abstractservice.IndexMetadataRemoteResourceService;
 import ch.unige.solidify.util.FileTool;
+import ch.unige.solidify.util.HashTool;
 import ch.unige.solidify.util.StringTool;
 
 import ch.dlcm.DLCMConstants;
@@ -76,6 +84,7 @@ import ch.dlcm.message.ArchiveStatisticMessage.StatisticType;
 import ch.dlcm.model.MetadataVisibility;
 import ch.dlcm.model.access.Order;
 import ch.dlcm.model.access.Order.OrderStatus;
+import ch.dlcm.model.access.OrderSubsetItem;
 import ch.dlcm.model.dto.DisseminationPolicyDto;
 import ch.dlcm.model.index.ArchiveMetadata;
 import ch.dlcm.model.oais.DisseminationInfoPackage;
@@ -100,6 +109,7 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
   private final DownloadTokenService downloadTokenService;
   private final TrustedUserRemoteResourceService trustedUserRemoteResourceService;
   private final DisseminationPolicyProvider disseminationPolicyProvider;
+  private final ChecksumAlgorithm defaultChecksumAlgorithm;
 
   protected SearchController(
           IndexMetadataRemoteResourceService<ArchiveMetadata> indexResourceRemoteService,
@@ -119,6 +129,7 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
     this.downloadTokenService = downloadTokenService;
     this.trustedUserRemoteResourceService = trustedUserRemoteResourceService;
     this.disseminationPolicyProvider = disseminationPolicyProvider;
+    this.defaultChecksumAlgorithm = ChecksumAlgorithm.valueOf(dlcmProperties.getParameters().getDefaultChecksum());
   }
 
   @Override
@@ -165,8 +176,8 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
     DisseminationPolicy disseminationPolicy;
     if (orderId == null) {
       //If order is not specified, check a direct order with the aipId as dipName and disseminationPolicyPublic and not itemList selected.
-      List<Order> orderList = this.orderService.findFullDirectOrderByAipAndDisseminationPolicy(dipName,
-              DLCMConstants.DISSEMINATION_POLICY_BASIC_ID);
+      List<Order> orderList = this.orderService.findDirectOrderByAipAndDisseminationPolicy(dipName,
+              DISSEMINATION_POLICY_BASIC_ID);
       if (orderList.isEmpty()) {
         throw new IllegalStateException("Order for DIP " + dipName + " is missing");
       }
@@ -221,61 +232,77 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
           + "|| @downloadWithAclPermissionService.isAllowed(#aipId)")
   public HttpEntity<String> prepareDownload(@PathVariable String aipId,
           @RequestBody(required = false) DisseminationPolicyDto disseminationPolicyDto) {
+
     if (disseminationPolicyDto == null) {
       disseminationPolicyDto = new DisseminationPolicyDto();
     }
+
     if (disseminationPolicyDto.getDisseminationPolicyId() == null) {
-      disseminationPolicyDto.setDisseminationPolicyId(DLCMConstants.DISSEMINATION_POLICY_BASIC_ID);
+      disseminationPolicyDto.setDisseminationPolicyId(DISSEMINATION_POLICY_BASIC_ID);
     }
-    List<DisseminationInfoPackage> dipList = List.of();
-    if (disseminationPolicyDto.getSubsetItemList() == null) {
+
+    List<DisseminationInfoPackage> dipList;
+    boolean isBasicPolicy = disseminationPolicyDto.getDisseminationPolicyId().equals(DISSEMINATION_POLICY_BASIC_ID);
+    if (isBasicPolicy && disseminationPolicyDto.getSubsetItemList() != null) {
+        // Basic policy with subset items - find specific DIP
+        String hash = this.calculateChecksum(disseminationPolicyDto.getSubsetItemList());
+      dipList = this.dipRepository.findByDisseminationPolicyIdAndAipIdAndSubsetItems(disseminationPolicyDto.getDisseminationPolicyId(), aipId,
+              hash);
+    } else {
+      // Non-basic policy - find DIP by policy and AIP
       dipList = this.dipRepository.findByFullDisseminationPolicyIdAndAipId(disseminationPolicyDto.getDisseminationPolicyId(), aipId);
-      disseminationPolicyDto.setSubsetItemList(List.of());
     }
 
+    List<Order> directOrders;
     if (dipList.size() > 1) {
       log.error("AIP download: too many DIPs");
       return new ResponseEntity<>("Too many DIPs found", HttpStatus.INTERNAL_SERVER_ERROR);
-    } else if (dipList.isEmpty()) {
-      /*
-       * DIP does not exist yet -> create a DIRECT order for the AIP that will create a DIP with only one
-       * AIP inside
-       */
-
-      // Create order to download AIP and create DIP
-      List<Order> orderList = List.of();
-      if (disseminationPolicyDto.getSubsetItemList().isEmpty()) {
-        orderList = this.orderService.findFullDirectOrderByAipAndDisseminationPolicy(aipId, disseminationPolicyDto.getDisseminationPolicyId());
+    }
+
+    if (dipList.isEmpty()) { // No DIP found
+      // Check if the DTO has asked for  subset items
+      if (disseminationPolicyDto.getSubsetItemList() != null) {
+        // calculate checksum of subset items
+        String hash = this.calculateChecksum(disseminationPolicyDto.getSubsetItemList());
+        directOrders = isBasicPolicy
+                ?
+                orderService.findDirectOrderByDisseminationPolicyAndSubsetChecksum(aipId, disseminationPolicyDto.getDisseminationPolicyId(),
+                        hash)
+                :
+                orderService.findDirectOrderByAipAndDisseminationPolicy(aipId, disseminationPolicyDto.getDisseminationPolicyId());
+      } else {
+        directOrders = this.orderService.findDirectOrderByAipAndDisseminationPolicy(aipId, disseminationPolicyDto.getDisseminationPolicyId());
       }
-      final Order order;
-      if (orderList.isEmpty()) {
-        order = this.orderService.createOrderForAIP(aipId, disseminationPolicyDto);
+
+      if (directOrders.isEmpty()) {
+        // create a new order
+        Order order = this.orderService.createOrderForAIP(aipId, disseminationPolicyDto);
         this.orderService.submitOrder(order);
-      } else if (orderList.size() == 1) {
-        order = orderList.get(0);
-        if (order.getStatus() == OrderStatus.IN_PROGRESS) {
-          this.orderService.submitOrder(order);
-        } else if (order.getStatus() == OrderStatus.IN_ERROR) {
-          log.error("AIP download: order in error");
-          return new ResponseEntity<>("Order is in error", HttpStatus.INTERNAL_SERVER_ERROR);
+        return new ResponseEntity<>(order.getResId(), HttpStatus.ACCEPTED);
+      } else if (directOrders.size() == 1) {
+        Order order = directOrders.get(0);
+        switch (order.getStatus()) {
+          case IN_PROGRESS:
+            orderService.submitOrder(order);
+            break;
+          case IN_ERROR:
+            log.error("AIP download: order in error");
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Order is in error");
+          default:
+            // do nothing
         }
       } else {
         log.error("AIP download: too many orders");
         return new ResponseEntity<>("Too many orders found", HttpStatus.INTERNAL_SERVER_ERROR);
       }
 
-      return new ResponseEntity<>(order.getResId(), HttpStatus.ACCEPTED);
-
-    } else {
-      /*
-       * DIP already exists
-       */
-      final DisseminationInfoPackage dip = dipList.get(0);
+    }
+    log.debug("DIP already exists");
+    final DisseminationInfoPackage dip = dipList.get(0);
       if (dip.getOrders().size() != 1) {
         return new ResponseEntity<>("Exactly one order should be associated to the DIP " + dip.getResId(), HttpStatus.INTERNAL_SERVER_ERROR);
       }
       return new ResponseEntity<>(dip.getOrders().get(0).getResId(), HttpStatus.ACCEPTED);
-    }
   }
 
   @Override
@@ -371,4 +398,17 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
     final ArchiveMetadata archive = this.getIndexResourceRemoteService().getIndexMetadata(dipName);
     return this.disseminationPolicyProvider.calculateFinalFileName(disseminationPolicy, archive.getTitle(), dipName);
   }
+
+  private String calculateChecksum(List<OrderSubsetItem> subsetItems) {
+    // Create a string combining all paths, sorted by path to ensure consistent checksum
+    String content = subsetItems.stream()
+            .map(OrderSubsetItem::getItemPath)
+            .sorted()
+            .collect(Collectors.joining("|"));
+    try {
+      return HashTool.hash(this.defaultChecksumAlgorithm.toString(), content.getBytes(StandardCharsets.UTF_8));
+    } catch (TransformerFactoryConfigurationError | NoSuchAlgorithmException e) {
+      throw new SolidifyProcessingException(e.getMessage(), e);
+    }
+  }
 }
diff --git a/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java b/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java
index 1ff12dd331..596ba28aba 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java
@@ -76,4 +76,7 @@ public interface DisseminationInfoPackageRepository extends SolidifyRepository<D
           + "AND si IS NULL "
           + "AND order.queryType = 'DIRECT'")
   List<DisseminationInfoPackage> findByFullDisseminationPolicyIdAndAipId(String disseminationPolicyId, String aipId);
+
+  @Query("SELECT dip FROM DisseminationInfoPackage dip JOIN dip.orders order JOIN dip.aips aip WHERE aip.resId = :aipId AND order.disseminationPolicyId = :disseminationPolicyId AND order.checksum = :checksum")
+  List<DisseminationInfoPackage> findByDisseminationPolicyIdAndAipIdAndSubsetItems(String disseminationPolicyId, String aipId, String checksum);
 }
diff --git a/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java b/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java
index 09771e5e8a..b23b1e2e90 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java
@@ -54,8 +54,22 @@ public interface OrderRepository extends SolidifyRepository<Order> {
   @Query("SELECT DISTINCT o FROM Order o JOIN o.aipPackages aip LEFT JOIN o.subsetItems si WHERE aip.resId=:aipId and o.queryType = 'DIRECT' and o.disseminationPolicyId=:disseminationPolicyId and si IS NULL")
   List<Order> findFullDirectOrderByAipAndDisseminationPolicyId(String aipId, String disseminationPolicyId);
 
+  @Query("SELECT DISTINCT o FROM Order o JOIN o.aipPackages aip WHERE aip.resId=:aipId and o.queryType = 'DIRECT' and o.disseminationPolicyId=:disseminationPolicyId and o.checksum =:checksum")
+  List<Order> findDirectOrderByDisseminationPolicyIdAndAipIdAndChecksum(String aipId, String disseminationPolicyId, String checksum);
+
   List<Order> findByStatusIn(List<OrderStatus> statuses);
 
   @Query("SELECT COALESCE(SUM(df.fileSize), 0) FROM Order o JOIN o.aipPackages aip JOIN aip.dataFiles df WHERE o.resId = :orderId")
   long getSize(String orderId);
+
+  /**
+   * Find orders that have the same checksum
+   *
+   * @param checksum the checksum to search for
+   * @param orderId  the order ID to exclude from search
+   * @return list of orders with matching checksums
+   */
+  @Query("SELECT o FROM Order o WHERE o.checksum = :checksum AND o.resId != :orderId ORDER BY o.creation.when DESC")
+  List<Order> findOrdersByChecksum(String checksum, String orderId);
+
 }
diff --git a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/BasicDipBuilderService.java b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/BasicDipBuilderService.java
index 6c38ce505e..1868ebce6b 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/BasicDipBuilderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/BasicDipBuilderService.java
@@ -30,6 +30,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.stereotype.Service;
 
 import ch.unige.solidify.exception.SolidifyRuntimeException;
@@ -37,22 +38,31 @@ import ch.unige.solidify.service.MessageService;
 import ch.unige.solidify.util.FileTool;
 
 import ch.dlcm.DLCMConstants;
+import ch.dlcm.business.OrderService;
 import ch.dlcm.config.DLCMProperties;
 import ch.dlcm.config.DLCMRepositoryDescription;
+import ch.dlcm.controller.AccessController;
 import ch.dlcm.model.access.Order;
 import ch.dlcm.model.oais.ArchivalInfoPackage;
 import ch.dlcm.service.MetadataService;
 
 @Service
+@ConditionalOnBean(AccessController.class)
 public class BasicDipBuilderService extends DipBuilderService {
 
-  public BasicDipBuilderService(DLCMProperties dlcmProperties, MessageService messageService, MetadataService metadataService,
+  public BasicDipBuilderService(DLCMProperties dlcmProperties,
+          MessageService messageService,
+          MetadataService metadataService,
+          OrderService orderService,
           DLCMRepositoryDescription repository) {
-    super(dlcmProperties, messageService, metadataService, repository);
+    super(dlcmProperties, messageService, metadataService, orderService, repository);
   }
 
   @Override
   public void buildDisseminationPolicyDip(Path workingFolder, ArchivalInfoPackage aip, Order order) {
+    if (this.orderService.isThereAnyOrderWithSameChecksum(order).isPresent()) {
+      return; // Do not build DIP if there is already an order with the same checksum
+    }
     // Extract datacite part from dlcm.xml to a new file and delete it
     this.extractDataciteMetadata(workingFolder, aip.getMetadataVersion());
     this.deleteUpdatedMetadataFiles(workingFolder);
diff --git a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DipBuilderService.java b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DipBuilderService.java
index 94cf9494fc..8c38a64ea5 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DipBuilderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DipBuilderService.java
@@ -51,6 +51,7 @@ import ch.unige.solidify.util.ZipTool;
 
 import ch.dlcm.DLCMConstants;
 import ch.dlcm.DLCMMetadataVersion;
+import ch.dlcm.business.OrderService;
 import ch.dlcm.config.DLCMProperties;
 import ch.dlcm.config.DLCMRepositoryDescription;
 import ch.dlcm.model.Access;
@@ -66,13 +67,16 @@ public abstract class DipBuilderService extends DLCMService {
   private static final Logger log = LoggerFactory.getLogger(DipBuilderService.class);
 
   protected final MetadataService metadataService;
+  protected final OrderService orderService;
   protected final String repositoryPrefix;
 
   protected DipBuilderService(DLCMProperties dlcmProperties, MessageService messageService,
           MetadataService metadataService,
+          OrderService orderService,
           DLCMRepositoryDescription repository) {
     super(messageService, dlcmProperties);
     this.metadataService = metadataService;
+    this.orderService = orderService;
     this.repositoryPrefix = repository.getName();
   }
 
@@ -85,6 +89,10 @@ public abstract class DipBuilderService extends DLCMService {
   public boolean createDipArchive(String orderLocation, Order order, String dipId, String orgUnitId, Access accessLevel, Path accessPath,
           Path dipFolder) throws IOException, JAXBException {
     final ZipTool zipTool;
+    if (this.orderService.isThereAnyOrderWithSameChecksum(order).isPresent()) {
+      return false; // Do not build DIP if there is already an order with the same checksum
+    }
+
     // Create order archive (i.e. DIP) & Purge working files for each orgUnit
     zipTool = new ZipTool(this.getDipArchiveUri(orderLocation, order.getResId(), orgUnitId, accessLevel, dipId));
     if (!zipTool.zipFiles(this.getBasePathToCreateDipZip(accessPath, order))) {
@@ -107,11 +115,13 @@ public abstract class DipBuilderService extends DLCMService {
       }
 
       if (order.getSubsetItems() != null && !order.getSubsetItems().isEmpty() && !aip.isCollection()) {
-        this.buildSubsetDip(rootFolder.resolve(aip.getResId()), order.getSubsetItems());
-        // Check that there is at least one data file apart from the internal folder
-        if (!isThereAtLeastOneFileInArchiveExcludingMetadataFolder(rootFolder.resolve(aip.getResId()))) {
-          log.error("IOException when checking if there is at least one file in the archive");
-          throw new SolidifyRuntimeException("IOException when checking if there is at least one file in the archive");
+        if (this.orderService.isThereAnyOrderWithSameChecksum(order).isEmpty()) {
+          this.buildSubsetDip(rootFolder.resolve(aip.getResId()), order.getSubsetItems());
+          // Check that there is at least one data file apart from the internal folder
+          if (!isThereAtLeastOneFileInArchiveExcludingMetadataFolder(rootFolder.resolve(aip.getResId()))) {
+            log.error("IOException when checking if there is at least one file in the archive");
+            throw new SolidifyRuntimeException("IOException when checking if there is at least one file in the archive");
+          }
         }
       }
       this.buildDisseminationPolicyDip(rootFolder.resolve(aip.getResId()), aip, order);
diff --git a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DisseminationPolicyProvider.java b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DisseminationPolicyProvider.java
index 381174f125..c9226303a0 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DisseminationPolicyProvider.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/DisseminationPolicyProvider.java
@@ -23,13 +23,16 @@
 
 package ch.dlcm.service.dissemination;
 
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.stereotype.Service;
 
+import ch.dlcm.controller.AccessController;
 import ch.dlcm.model.policies.DisseminationPolicy;
 import ch.dlcm.model.tool.CleanTool;
 import ch.dlcm.service.rest.trusted.TrustedDisseminationPolicyRemoteResourceService;
 
 @Service
+@ConditionalOnBean(AccessController.class)
 public class DisseminationPolicyProvider {
 
   private final TrustedDisseminationPolicyRemoteResourceService disseminationPolicyRemoteResourceService;
diff --git a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/HederaDipBuilderService.java b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/HederaDipBuilderService.java
index 0a261f827d..c3747e19f3 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/HederaDipBuilderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/HederaDipBuilderService.java
@@ -25,23 +25,28 @@ package ch.dlcm.service.dissemination;
 
 import java.nio.file.Path;
 
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.stereotype.Service;
 
 import ch.unige.solidify.service.MessageService;
 
+import ch.dlcm.business.OrderService;
 import ch.dlcm.config.DLCMProperties;
 import ch.dlcm.config.DLCMRepositoryDescription;
+import ch.dlcm.controller.AccessController;
 import ch.dlcm.model.access.Order;
 import ch.dlcm.model.oais.ArchivalInfoPackage;
 import ch.dlcm.service.MetadataService;
 
 @Service
+@ConditionalOnBean(AccessController.class)
 public class HederaDipBuilderService extends DipBuilderService {
   public HederaDipBuilderService(DLCMProperties dlcmProperties,
           MessageService messageService,
           MetadataService metadataService,
+          OrderService orderService,
           DLCMRepositoryDescription repository) {
-    super(dlcmProperties, messageService, metadataService, repository);
+    super(dlcmProperties, messageService, metadataService, orderService, repository);
   }
 
   @Override
diff --git a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/IiifDipBuilderService.java b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/IiifDipBuilderService.java
index e3b9b6f22c..71704e33ac 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/IiifDipBuilderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/IiifDipBuilderService.java
@@ -33,6 +33,7 @@ import java.util.stream.Stream;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.stereotype.Service;
 import org.springframework.util.AntPathMatcher;
 
@@ -41,8 +42,10 @@ import ch.unige.solidify.service.MessageService;
 import ch.unige.solidify.util.FileTool;
 
 import ch.dlcm.DLCMConstants;
+import ch.dlcm.business.OrderService;
 import ch.dlcm.config.DLCMProperties;
 import ch.dlcm.config.DLCMRepositoryDescription;
+import ch.dlcm.controller.AccessController;
 import ch.dlcm.model.access.Order;
 import ch.dlcm.model.display.OrganizationalUnitDisseminationPolicyDTO;
 import ch.dlcm.model.oais.ArchivalInfoPackage;
@@ -51,6 +54,7 @@ import ch.dlcm.service.rest.abstractservice.OrganizationalDisseminationPolicyRem
 import ch.dlcm.service.rest.trusted.TrustedOrganizationnalUnitDisseminationPolicyRemoteResourceService;
 
 @Service
+@ConditionalOnBean(AccessController.class)
 public class IiifDipBuilderService extends DipBuilderService {
   private static final Logger log = LoggerFactory.getLogger(IiifDipBuilderService.class);
 
@@ -62,9 +66,10 @@ public class IiifDipBuilderService extends DipBuilderService {
   public IiifDipBuilderService(DLCMProperties dlcmProperties,
           MessageService messageService,
           MetadataService metadataService,
+          OrderService orderService,
           DLCMRepositoryDescription repository,
           TrustedOrganizationnalUnitDisseminationPolicyRemoteResourceService trustedOrgUnitDisseminationPolicyRemoteService) {
-    super(dlcmProperties, messageService, metadataService, repository);
+    super(dlcmProperties, messageService, metadataService, orderService, repository);
     this.trustedOrgUnitDisseminationPolicyRemoteService = trustedOrgUnitDisseminationPolicyRemoteService;
   }
 
diff --git a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/OaisDipBuilderService.java b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/OaisDipBuilderService.java
index b925e04b91..3d0771f70b 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/OaisDipBuilderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/service/dissemination/OaisDipBuilderService.java
@@ -26,6 +26,7 @@ package ch.dlcm.service.dissemination;
 import java.io.IOException;
 import java.nio.file.Path;
 
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.stereotype.Service;
 
 import jakarta.xml.bind.JAXBException;
@@ -35,21 +36,25 @@ import ch.unige.solidify.util.FileTool;
 import ch.unige.solidify.util.ZipTool;
 
 import ch.dlcm.DLCMConstants;
+import ch.dlcm.business.OrderService;
 import ch.dlcm.config.DLCMProperties;
 import ch.dlcm.config.DLCMRepositoryDescription;
+import ch.dlcm.controller.AccessController;
 import ch.dlcm.model.Access;
 import ch.dlcm.model.access.Order;
 import ch.dlcm.model.oais.ArchivalInfoPackage;
 import ch.dlcm.service.MetadataService;
 
 @Service
+@ConditionalOnBean(AccessController.class)
 public class OaisDipBuilderService extends DipBuilderService {
 
   public OaisDipBuilderService(DLCMProperties dlcmProperties,
           MessageService messageService,
           MetadataService metadataService,
+          OrderService orderService,
           DLCMRepositoryDescription repository) {
-    super(dlcmProperties, messageService, metadataService, repository);
+    super(dlcmProperties, messageService, metadataService, orderService, repository);
   }
 
   @Override
diff --git a/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java b/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java
index 94787e9f81..dc548ec0ad 100644
--- a/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java
+++ b/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java
@@ -144,6 +144,10 @@ public class Order extends ResourceNormalized implements RemoteResourceContainer
   @Column(name = DLCMConstants.ORGANIZATIONAL_DISSEMINATION_POLICY_ID_FIELD, length = DB_ID_LENGTH)
   private String organizationalUnitDisseminationPolicyId;
 
+  @Schema(description = "The checksum of the order subitems list.")
+  @Column(length = SolidifyConstants.DB_LONG_STRING_LENGTH)
+  private String checksum;
+
   @Override
   public <T> boolean addItem(T t) {
     if (t instanceof DisseminationInfoPackage dip) {
@@ -249,6 +253,10 @@ public class Order extends ResourceNormalized implements RemoteResourceContainer
     return this.organizationalUnitDisseminationPolicyId;
   }
 
+  public String getChecksum() {
+    return this.checksum;
+  } 
+
   @JsonIgnore
   public boolean hasAipInError() {
     if (this.getAipPackages().isEmpty()) {
@@ -409,6 +417,10 @@ public class Order extends ResourceNormalized implements RemoteResourceContainer
     this.organizationalUnitDisseminationPolicyId = id;
   }
 
+  public void setChecksum(String checksum) {
+    this.checksum = checksum;
+  }
+
   private boolean addAip(ArchivalInfoPackage aip) {
     if (!this.exist(this.aipPackages, aip)) {
       return this.aipPackages.add(aip);
diff --git a/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql b/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql
index d602318ca7..039247d34d 100755
--- a/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql
+++ b/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql
@@ -376,4 +376,8 @@ WHERE type IN ('IIIF')
   AND parameters IS NOT NULL;
 
 
-update notification set sent_time = NOW() where sent_time is null;
\ No newline at end of file
+update notification set sent_time = NOW() where sent_time is null;
+
+-- add new column for order_queery to store checksum
+ALTER TABLE order_query
+    ADD COLUMN checksum varchar(1024) null;
\ No newline at end of file
-- 
GitLab


From 8d66a6b61ec26fa50c3e5bd9fb8ba514d828730b Mon Sep 17 00:00:00 2001
From: Homada Boumedane <homada.boumedane@unige.ch>
Date: Wed, 19 Feb 2025 16:28:20 +0100
Subject: [PATCH 2/3] refactor: dissemination package

fix code review.

Closes: DLCM-2830
---
 .../java/ch/dlcm/business/OrderService.java   | 24 +++++---------
 .../controller/access/SearchController.java   | 33 ++++---------------
 .../DisseminationInfoPackageRepository.java   |  2 +-
 .../ch/dlcm/repository/OrderRepository.java   |  6 ++--
 .../main/java/ch/dlcm/model/access/Order.java | 11 +++----
 .../main/resources/scripts/upgrade22to30.sql  |  4 +--
 6 files changed, 27 insertions(+), 53 deletions(-)

diff --git a/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java b/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
index 101887cc48..24b100e40e 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
@@ -35,6 +35,7 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import javax.xml.transform.TransformerFactoryConfigurationError;
 
+import org.apache.logging.log4j.util.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -195,19 +196,16 @@ public class OrderService extends ResourceService<Order> {
     order.setQuery(aipId);
     order.setDisseminationPolicyId(disseminationPolicy.getDisseminationPolicyId());
     order.setOrganizationalUnitDisseminationPolicyId(disseminationPolicy.getOrganizationalUnitDisseminationPolicyId());
-    this.calculateChecksum(order, subsetItemList); //calculate and set checksum
-    final Order savedOrder = this.save(order);
-
     if (subsetItemList == null) { // if no subsetItemList is provided, set it to an empty list
       subsetItemList = List.of();
     }
-
+    order.setSubitemsChecksum(this.calculateChecksum(subsetItemList)); //calculate and set checksum
+    final Order savedOrder = this.save(order);
     subsetItemList.forEach(subsetItem -> {
       subsetItem.setOrder(savedOrder);
       subsetItem.setCreatedBy(savedOrder.getCreatedBy());
       subsetItem.setUpdatedBy(savedOrder.getUpdatedBy());
     });
-
     this.orderSubsetItemRepository.saveAll(subsetItemList);
 
     return savedOrder;
@@ -230,7 +228,7 @@ public class OrderService extends ResourceService<Order> {
     this.submitOrder(order1);
   }
 
-  public List<Order> findDirectOrderByAipAndDisseminationPolicy(String aipId, String disseminationPolicyId) {
+  public List<Order> findFullDirectOrderByAipAndDisseminationPolicy(String aipId, String disseminationPolicyId) {
     return ((OrderRepository) this.itemRepository).findFullDirectOrderByAipAndDisseminationPolicyId(aipId, disseminationPolicyId);
   }
 
@@ -322,29 +320,25 @@ public class OrderService extends ResourceService<Order> {
   }
 
   public Optional<Order> isThereAnyOrderWithSameChecksum(Order order) {
-    return ((OrderRepository) this.itemRepository).findOrdersByChecksum(order.getChecksum(), order.getResId())
+    return ((OrderRepository) this.itemRepository).findDifferentOrdersWithSameChecksum(order.getSubitemsChecksum(), order.getResId())
             .stream()
             .findFirst();
   }
 
-  private void calculateChecksum(Order order, List<OrderSubsetItem> subsetItems) {
+  public String calculateChecksum(List<OrderSubsetItem> subsetItems) {
     if (subsetItems != null && !subsetItems.isEmpty()) {
       // Create a string combining all paths, sorted by path to ensure consistent checksum
       String content = subsetItems.stream()
               .map(OrderSubsetItem::getItemPath)
               .sorted()
-              .collect(Collectors.joining("|"));
-      if (content.isEmpty()) {
-        order.setChecksum(null);
-        return;
-      }
+              .collect(Collectors.joining(""));
       try {
-        String hash = HashTool.hash(this.defaultChecksumAlgorithm.toString(), content.getBytes(StandardCharsets.UTF_8));
-        order.setChecksum(hash);
+        return HashTool.hash(this.defaultChecksumAlgorithm.toString(), content.getBytes(StandardCharsets.UTF_8));
       } catch (TransformerFactoryConfigurationError | NoSuchAlgorithmException e) {
         throw new SolidifyProcessingException(e.getMessage(), e);
       }
     }
+    return Strings.EMPTY;
   }
 
   @Override
diff --git a/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java b/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java
index 5afdcaf598..55cebaa31b 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/controller/access/SearchController.java
@@ -28,13 +28,9 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
 import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
 
 import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.stream.Collectors;
-import javax.xml.transform.TransformerFactoryConfigurationError;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,7 +56,6 @@ import ch.unige.solidify.SolidifyConstants;
 import ch.unige.solidify.auth.model.AuthApplicationRole;
 import ch.unige.solidify.auth.service.ApplicationRoleListService;
 import ch.unige.solidify.config.SolidifyEventPublisher;
-import ch.unige.solidify.exception.SolidifyProcessingException;
 import ch.unige.solidify.exception.SolidifyRestException;
 import ch.unige.solidify.exception.SolidifyRuntimeException;
 import ch.unige.solidify.model.dto.CitationDto;
@@ -72,7 +67,6 @@ import ch.unige.solidify.security.EveryonePermissions;
 import ch.unige.solidify.service.DownloadTokenService;
 import ch.unige.solidify.service.rest.abstractservice.IndexMetadataRemoteResourceService;
 import ch.unige.solidify.util.FileTool;
-import ch.unige.solidify.util.HashTool;
 import ch.unige.solidify.util.StringTool;
 
 import ch.dlcm.DLCMConstants;
@@ -84,7 +78,6 @@ import ch.dlcm.message.ArchiveStatisticMessage.StatisticType;
 import ch.dlcm.model.MetadataVisibility;
 import ch.dlcm.model.access.Order;
 import ch.dlcm.model.access.Order.OrderStatus;
-import ch.dlcm.model.access.OrderSubsetItem;
 import ch.dlcm.model.dto.DisseminationPolicyDto;
 import ch.dlcm.model.index.ArchiveMetadata;
 import ch.dlcm.model.oais.DisseminationInfoPackage;
@@ -176,7 +169,7 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
     DisseminationPolicy disseminationPolicy;
     if (orderId == null) {
       //If order is not specified, check a direct order with the aipId as dipName and disseminationPolicyPublic and not itemList selected.
-      List<Order> orderList = this.orderService.findDirectOrderByAipAndDisseminationPolicy(dipName,
+      List<Order> orderList = this.orderService.findFullDirectOrderByAipAndDisseminationPolicy(dipName,
               DISSEMINATION_POLICY_BASIC_ID);
       if (orderList.isEmpty()) {
         throw new IllegalStateException("Order for DIP " + dipName + " is missing");
@@ -245,7 +238,7 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
     boolean isBasicPolicy = disseminationPolicyDto.getDisseminationPolicyId().equals(DISSEMINATION_POLICY_BASIC_ID);
     if (isBasicPolicy && disseminationPolicyDto.getSubsetItemList() != null) {
         // Basic policy with subset items - find specific DIP
-        String hash = this.calculateChecksum(disseminationPolicyDto.getSubsetItemList());
+      String hash = this.orderService.calculateChecksum(disseminationPolicyDto.getSubsetItemList());
       dipList = this.dipRepository.findByDisseminationPolicyIdAndAipIdAndSubsetItems(disseminationPolicyDto.getDisseminationPolicyId(), aipId,
               hash);
     } else {
@@ -261,17 +254,18 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
 
     if (dipList.isEmpty()) { // No DIP found
       // Check if the DTO has asked for  subset items
-      if (disseminationPolicyDto.getSubsetItemList() != null) {
+      if (disseminationPolicyDto.getSubsetItemList() != null || disseminationPolicyDto.getSubsetItemList().isEmpty()) {
         // calculate checksum of subset items
-        String hash = this.calculateChecksum(disseminationPolicyDto.getSubsetItemList());
+        String hash = this.orderService.calculateChecksum(disseminationPolicyDto.getSubsetItemList());
         directOrders = isBasicPolicy
                 ?
                 orderService.findDirectOrderByDisseminationPolicyAndSubsetChecksum(aipId, disseminationPolicyDto.getDisseminationPolicyId(),
                         hash)
                 :
-                orderService.findDirectOrderByAipAndDisseminationPolicy(aipId, disseminationPolicyDto.getDisseminationPolicyId());
+                orderService.findFullDirectOrderByAipAndDisseminationPolicy(aipId, disseminationPolicyDto.getDisseminationPolicyId());
       } else {
-        directOrders = this.orderService.findDirectOrderByAipAndDisseminationPolicy(aipId, disseminationPolicyDto.getDisseminationPolicyId());
+        directOrders = this.orderService.findFullDirectOrderByAipAndDisseminationPolicy(aipId,
+                disseminationPolicyDto.getDisseminationPolicyId());
       }
 
       if (directOrders.isEmpty()) {
@@ -398,17 +392,4 @@ public abstract class SearchController extends AccessIndexResourceReadOnlyContro
     final ArchiveMetadata archive = this.getIndexResourceRemoteService().getIndexMetadata(dipName);
     return this.disseminationPolicyProvider.calculateFinalFileName(disseminationPolicy, archive.getTitle(), dipName);
   }
-
-  private String calculateChecksum(List<OrderSubsetItem> subsetItems) {
-    // Create a string combining all paths, sorted by path to ensure consistent checksum
-    String content = subsetItems.stream()
-            .map(OrderSubsetItem::getItemPath)
-            .sorted()
-            .collect(Collectors.joining("|"));
-    try {
-      return HashTool.hash(this.defaultChecksumAlgorithm.toString(), content.getBytes(StandardCharsets.UTF_8));
-    } catch (TransformerFactoryConfigurationError | NoSuchAlgorithmException e) {
-      throw new SolidifyProcessingException(e.getMessage(), e);
-    }
-  }
 }
diff --git a/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java b/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java
index 596ba28aba..c2af5cd4c2 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/repository/DisseminationInfoPackageRepository.java
@@ -77,6 +77,6 @@ public interface DisseminationInfoPackageRepository extends SolidifyRepository<D
           + "AND order.queryType = 'DIRECT'")
   List<DisseminationInfoPackage> findByFullDisseminationPolicyIdAndAipId(String disseminationPolicyId, String aipId);
 
-  @Query("SELECT dip FROM DisseminationInfoPackage dip JOIN dip.orders order JOIN dip.aips aip WHERE aip.resId = :aipId AND order.disseminationPolicyId = :disseminationPolicyId AND order.checksum = :checksum")
+  @Query("SELECT dip FROM DisseminationInfoPackage dip JOIN dip.orders order JOIN dip.aips aip WHERE aip.resId = :aipId AND order.disseminationPolicyId = :disseminationPolicyId AND order.subitemsChecksum = :checksum")
   List<DisseminationInfoPackage> findByDisseminationPolicyIdAndAipIdAndSubsetItems(String disseminationPolicyId, String aipId, String checksum);
 }
diff --git a/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java b/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java
index b23b1e2e90..d7c7c260ae 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/repository/OrderRepository.java
@@ -54,7 +54,7 @@ public interface OrderRepository extends SolidifyRepository<Order> {
   @Query("SELECT DISTINCT o FROM Order o JOIN o.aipPackages aip LEFT JOIN o.subsetItems si WHERE aip.resId=:aipId and o.queryType = 'DIRECT' and o.disseminationPolicyId=:disseminationPolicyId and si IS NULL")
   List<Order> findFullDirectOrderByAipAndDisseminationPolicyId(String aipId, String disseminationPolicyId);
 
-  @Query("SELECT DISTINCT o FROM Order o JOIN o.aipPackages aip WHERE aip.resId=:aipId and o.queryType = 'DIRECT' and o.disseminationPolicyId=:disseminationPolicyId and o.checksum =:checksum")
+  @Query("SELECT DISTINCT o FROM Order o JOIN o.aipPackages aip WHERE aip.resId=:aipId and o.queryType = 'DIRECT' and o.disseminationPolicyId=:disseminationPolicyId and o.subitemsChecksum =:checksum")
   List<Order> findDirectOrderByDisseminationPolicyIdAndAipIdAndChecksum(String aipId, String disseminationPolicyId, String checksum);
 
   List<Order> findByStatusIn(List<OrderStatus> statuses);
@@ -69,7 +69,7 @@ public interface OrderRepository extends SolidifyRepository<Order> {
    * @param orderId  the order ID to exclude from search
    * @return list of orders with matching checksums
    */
-  @Query("SELECT o FROM Order o WHERE o.checksum = :checksum AND o.resId != :orderId ORDER BY o.creation.when DESC")
-  List<Order> findOrdersByChecksum(String checksum, String orderId);
+  @Query("SELECT o FROM Order o WHERE o.subitemsChecksum = :checksum AND o.resId != :orderId ORDER BY o.creation.when DESC")
+  List<Order> findDifferentOrdersWithSameChecksum(String checksum, String orderId);
 
 }
diff --git a/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java b/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java
index dc548ec0ad..17dbe5a463 100644
--- a/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java
+++ b/DLCM-Model/src/main/java/ch/dlcm/model/access/Order.java
@@ -145,8 +145,7 @@ public class Order extends ResourceNormalized implements RemoteResourceContainer
   private String organizationalUnitDisseminationPolicyId;
 
   @Schema(description = "The checksum of the order subitems list.")
-  @Column(length = SolidifyConstants.DB_LONG_STRING_LENGTH)
-  private String checksum;
+  private String subitemsChecksum;
 
   @Override
   public <T> boolean addItem(T t) {
@@ -253,8 +252,8 @@ public class Order extends ResourceNormalized implements RemoteResourceContainer
     return this.organizationalUnitDisseminationPolicyId;
   }
 
-  public String getChecksum() {
-    return this.checksum;
+  public String getSubitemsChecksum() {
+    return this.subitemsChecksum;
   } 
 
   @JsonIgnore
@@ -417,8 +416,8 @@ public class Order extends ResourceNormalized implements RemoteResourceContainer
     this.organizationalUnitDisseminationPolicyId = id;
   }
 
-  public void setChecksum(String checksum) {
-    this.checksum = checksum;
+  public void setSubitemsChecksum(String checksum) {
+    this.subitemsChecksum = checksum;
   }
 
   private boolean addAip(ArchivalInfoPackage aip) {
diff --git a/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql b/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql
index 039247d34d..d00fb89bb6 100755
--- a/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql
+++ b/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql
@@ -378,6 +378,6 @@ WHERE type IN ('IIIF')
 
 update notification set sent_time = NOW() where sent_time is null;
 
--- add new column for order_queery to store checksum
+-- add new column for order_query to store checksum
 ALTER TABLE order_query
-    ADD COLUMN checksum varchar(1024) null;
\ No newline at end of file
+    ADD COLUMN checksum varchar(255) null;
\ No newline at end of file
-- 
GitLab


From 59d0941c4f5fc4ec8f9ae10e3486ec321287aa23 Mon Sep 17 00:00:00 2001
From: Homada Boumedane <homada.boumedane@unige.ch>
Date: Wed, 19 Feb 2025 17:08:56 +0100
Subject: [PATCH 3/3] refactor: dissemination package

fix code review.

Closes: DLCM-2830
---
 DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java b/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
index 24b100e40e..bff2783dc2 100644
--- a/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
+++ b/DLCM-Access/src/main/java/ch/dlcm/business/OrderService.java
@@ -35,7 +35,6 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import javax.xml.transform.TransformerFactoryConfigurationError;
 
-import org.apache.logging.log4j.util.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -338,7 +337,7 @@ public class OrderService extends ResourceService<Order> {
         throw new SolidifyProcessingException(e.getMessage(), e);
       }
     }
-    return Strings.EMPTY;
+    return "";
   }
 
   @Override
-- 
GitLab