From eaad5942e1f314285d253c9f2f2291e575453a08 Mon Sep 17 00:00:00 2001 From: MatusBeke Date: Fri, 14 Aug 2026 16:56:39 +0200 Subject: [PATCH 1/2] feat(docker): run Handle server as a separate service (not embedded) Adds docker/docker-compose-handle.yml, an overlay that: * overrides the backend entrypoint to STOP starting the embedded Handle server (drops the /dspace/bin/start-handle-server line), * enables the backend remote-resolver endpoints (handle.remote-resolver.enabled = true), * runs a standalone dspace-handle-server service (image from dataquest-dev/docker-handle-server) that resolves via the backend REST API and waits for the backend before starting. The Handle server no longer runs inside the backend and has no direct DB access. Include this file LAST so its entrypoint override wins. Co-Authored-By: Claude Opus 4.8 --- docker/docker-compose-handle.yml | 87 ++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docker/docker-compose-handle.yml diff --git a/docker/docker-compose-handle.yml b/docker/docker-compose-handle.yml new file mode 100644 index 00000000000..83a9aacf7d3 --- /dev/null +++ b/docker/docker-compose-handle.yml @@ -0,0 +1,87 @@ +# +# The contents of this file are subject to the license and copyright +# detailed in the LICENSE and NOTICE files at the root of the source +# tree and available online at +# +# http://www.dspace.org/license/ +# + +# Overlay that runs the Handle server as a SEPARATE service instead of the one +# embedded in the backend. The backend stops starting its own Handle server and +# the standalone service resolves via the backend's remote-resolver REST API: +# +# client -> dspace-handle-server(:8000) --HTTP /server/resolve--> dspace (backend) -> dspacedb +# +# The Handle server never connects to the database itself. +# +# This overlay MUST be listed LAST so its backend `entrypoint` override wins over +# docker-compose-rest.yml (entrypoint is replaced, not merged). The network ipam +# from docker-compose-rest.yml is preserved (this file only references dspacenet): +# +# docker compose -p d7 \ +# -f docker/docker-compose.yml \ +# -f docker/docker-compose-rest.yml \ +# -f docker/docker-compose-handle.yml up -d +# +networks: + dspacenet: + +services: + # Backend: enable the remote-resolver endpoints and STOP starting the embedded + # Handle server. This entrypoint is identical to docker-compose-rest.yml with + # the `/dspace/bin/start-handle-server` line removed. + dspace: + environment: + handle__P__remote__D__resolver__P__enabled: 'true' + entrypoint: + - /bin/bash + - '-c' + - | + while (! /dev/null 2>&1; do sleep 1; done; + pushd /usr/local/tomcat/webapps && (unlink server || true) && (ln -s /dspace/webapps/server/ `echo -n "${DSPACE_REST_NAMESPACE}" | sed -e 's#^/##' -e 'sx/x#x'` || true) && popd + /dspace/bin/dspace database migrate force + ./custom_run.sh + + # Standalone CNRI Handle server (image built from dataquest-dev/docker-handle-server). + dspace-handle-server: + image: ${DSPACE_HANDLE_IMAGE:-ghcr.io/dataquest-dev/docker-handle-server:latest} + container_name: dspace-handle-server${INSTANCE} + restart: unless-stopped + depends_on: + - dspace + networks: + - dspacenet + environment: + # Backend REST base the resolver plugin talks to. + DSPACE_HANDLE_ENDPOINT: ${DSPACE_HANDLE_ENDPOINT:-http://dspace:8080/server} + ports: + - published: ${HANDLE_HTTP_PORT:-8000} + target: 8000 + host_ip: ${HOST_IP:-127.0.0.1} + - published: ${HANDLE_NATIVE_PORT:-2641} + target: 2641 + protocol: tcp + host_ip: ${HOST_IP:-127.0.0.1} + - published: ${HANDLE_NATIVE_PORT:-2641} + target: 2641 + protocol: udp + host_ip: ${HOST_IP:-127.0.0.1} + # Configure the plugin at runtime (works with the current published image): + # 1) switch config.dct to the remote-resolver storage plugin (if not already), + # 2) point the plugin at the backend, + # 3) wait for the backend (the plugin loads the prefix list on startup and + # aborts if the backend is unreachable), then start the Handle server. + entrypoint: + - /bin/sh + - -c + - | + grep -q MultiRemoteDSpaceRepositoryHandlePlugin /app/config/config.dct || \ + sed -i 's#"server_config" = {#"server_config" = {\n "storage_type" = "CUSTOM"\n "storage_class" = "org.dspace.handle.MultiRemoteDSpaceRepositoryHandlePlugin"#' /app/config/config.dct + echo "dspace.handle.endpoint1 = $$DSPACE_HANDLE_ENDPOINT" > /app/config/handle-dspace-plugin.cfg + echo "Backend endpoint: $$DSPACE_HANDLE_ENDPOINT" + until wget -q -O- "$$DSPACE_HANDLE_ENDPOINT/listprefixes" >/dev/null 2>&1; do + echo " waiting for backend $$DSPACE_HANDLE_ENDPOINT ..."; + sleep 5; + done + echo "Backend is up. Starting Handle server." + exec /app/hs/bin/hdl-server /app/config/ From b24c795af8f0e8065b68186e2e67e30f03776865 Mon Sep 17 00:00:00 2001 From: MatusBeke Date: Mon, 17 Aug 2026 09:20:32 +0200 Subject: [PATCH 2/2] fix(docker): drop backend Handle ports and avoid port clash (Copilot review) - Use `ports: !override` on the backend so the now-unused embedded Handle ports (native 264${INSTANCE}, http 801${INSTANCE}) are removed instead of append-merged, preventing a 'port is already allocated' clash. - Default the standalone HTTP port to 8010 (was 8000, which collides with the backend debug port 800${INSTANCE}). - Remove the redundant top-level 'networks: dspacenet:' block; the file only references the network, so the ipam from docker-compose-rest.yml is preserved. Co-Authored-By: Claude Opus 4.8 --- docker/docker-compose-handle.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docker/docker-compose-handle.yml b/docker/docker-compose-handle.yml index 83a9aacf7d3..70373269f2a 100644 --- a/docker/docker-compose-handle.yml +++ b/docker/docker-compose-handle.yml @@ -23,9 +23,6 @@ # -f docker/docker-compose-rest.yml \ # -f docker/docker-compose-handle.yml up -d # -networks: - dspacenet: - services: # Backend: enable the remote-resolver endpoints and STOP starting the embedded # Handle server. This entrypoint is identical to docker-compose-rest.yml with @@ -33,6 +30,18 @@ services: dspace: environment: handle__P__remote__D__resolver__P__enabled: 'true' + # The backend no longer runs a Handle server, so drop the (now unused) Handle + # ports it publishes in docker-compose-rest.yml (native 264${INSTANCE} and + # HTTP 801${INSTANCE}). `!override` REPLACES the ports list instead of the + # default append-merge, which is what lets us remove them; this also avoids a + # "port is already allocated" clash with dspace-handle-server. Keep REST + debug. + ports: !override + - published: 808${INSTANCE} + target: 8080 + host_ip: ${HOST_IP:-127.0.0.1} + - published: 800${INSTANCE} + target: 8000 + host_ip: ${HOST_IP:-127.0.0.1} entrypoint: - /bin/bash - '-c' @@ -55,7 +64,9 @@ services: # Backend REST base the resolver plugin talks to. DSPACE_HANDLE_ENDPOINT: ${DSPACE_HANDLE_ENDPOINT:-http://dspace:8080/server} ports: - - published: ${HANDLE_HTTP_PORT:-8000} + # HTTP resolver. Default 8010 matches the Handle HTTP port the embedded + # server used to expose, and avoids the backend debug port (800${INSTANCE}). + - published: ${HANDLE_HTTP_PORT:-8010} target: 8000 host_ip: ${HOST_IP:-127.0.0.1} - published: ${HANDLE_NATIVE_PORT:-2641}