From 43df3dc2f0595b1851e69fd43639f204a7274485 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:11:14 +0100 Subject: [PATCH 01/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-mailsender/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/celements-mailsender/pom.xml b/celements-mailsender/pom.xml index 5dc5a4fc..05c8ec10 100644 --- a/celements-mailsender/pom.xml +++ b/celements-mailsender/pom.xml @@ -4,10 +4,10 @@ com.celements celements - 6.2 + 7.0-SNAPSHOT celements-mailsender - 6.1-SNAPSHOT + 7.0-SNAPSHOT Celements Mail Sender From 492aa7415630baf6693d4cad03b17baeb0e7bbc0 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:11:56 +0100 Subject: [PATCH 02/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-rights/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/celements-rights/pom.xml b/celements-rights/pom.xml index 1f019c75..1a9e4845 100644 --- a/celements-rights/pom.xml +++ b/celements-rights/pom.xml @@ -24,17 +24,17 @@ com.celements celements - 6.0 + 7.0-SNAPSHOT 4.0.0 celements-rights - 6.1-SNAPSHOT + 7.0-SNAPSHOT Celements Rights com.celements celements-model - 6.0 + 7.0-SNAPSHOT provided From 1f3954268a77f90d4a04e65631fbde45077bf43c Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:12:10 +0100 Subject: [PATCH 03/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-scheduler/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/celements-scheduler/pom.xml b/celements-scheduler/pom.xml index 5e80a760..37a6a40c 100644 --- a/celements-scheduler/pom.xml +++ b/celements-scheduler/pom.xml @@ -25,16 +25,16 @@ com.celements celements - 6.6 + 7.0-SNAPSHOT celements-scheduler - 6.3-SNAPSHOT + 7.0-SNAPSHOT Celements Scheduler com.celements celements-model - 6.7 + 7.0-SNAPSHOT provided From 1a4d33ae371295d292b7f7028690598e3ddfbc92 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:12:41 +0100 Subject: [PATCH 04/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-captcha/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/celements-captcha/pom.xml b/celements-captcha/pom.xml index 765a0f84..ee409588 100644 --- a/celements-captcha/pom.xml +++ b/celements-captcha/pom.xml @@ -26,23 +26,23 @@ com.celements celements - 6.0 + 7.0-SNAPSHOT 4.0.0 celements-captcha - 6.1-SNAPSHOT + 7.0-SNAPSHOT Celements captcha implementations com.celements celements-model - 6.0 + 7.0-SNAPSHOT provided com.celements celements-core - 6.0 + 7.0-SNAPSHOT provided From c3a30213b8b5e3d5a944968a6f9e5ff25e57b045 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:12:55 +0100 Subject: [PATCH 05/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-contact/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/celements-contact/pom.xml b/celements-contact/pom.xml index 07cfa823..415c0262 100644 --- a/celements-contact/pom.xml +++ b/celements-contact/pom.xml @@ -26,23 +26,23 @@ com.celements celements - 6.0 + 7.0-SNAPSHOT 4.0.0 celements-contact - 6.1-SNAPSHOT + 7.0-SNAPSHOT Celements Core com.celements celements-model - 6.0 + 7.0-SNAPSHOT provided com.celements celements-core - 6.0 + 7.0-SNAPSHOT provided From 35badde2287e46f243f6183def4444f852f7ab8e Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:13:38 +0100 Subject: [PATCH 06/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-css/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/celements-css/pom.xml b/celements-css/pom.xml index 8c14684c..5138e208 100644 --- a/celements-css/pom.xml +++ b/celements-css/pom.xml @@ -3,23 +3,23 @@ com.celements celements - 6.0 + 7.0-SNAPSHOT 4.0.0 celements-css - 6.1-SNAPSHOT + 7.0-SNAPSHOT Webtools for css handling like inliner, minifier com.celements celements-model - 6.0 + 7.0-SNAPSHOT provided com.celements celements-core - 6.0 + 7.0-SNAPSHOT provided From 12ff142d51fce79cba92f44a3b75fac1569a0309 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:13:52 +0100 Subject: [PATCH 07/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-cleverreach/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/celements-cleverreach/pom.xml b/celements-cleverreach/pom.xml index cc4c579d..5bf1c5a9 100644 --- a/celements-cleverreach/pom.xml +++ b/celements-cleverreach/pom.xml @@ -24,35 +24,35 @@ com.celements celements - 6.3 + 7.0-SNAPSHOT 4.0.0 celements-cleverreach - 6.2-SNAPSHOT + 7.0-SNAPSHOT Celements connector to Clever Reach API com.celements celements-model - 6.4 + 7.0-SNAPSHOT provided com.celements celements-core - 6.13 + 7.0-SNAPSHOT provided com.celements celements-css - 6.0 + 7.0-SNAPSHOT provided com.celements celements-mailsender - 6.0 + 7.0-SNAPSHOT provided From d5ec7471c626c7d0567224753c90b00c8bb28918 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:15:01 +0100 Subject: [PATCH 08/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-search-lucene/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/celements-search-lucene/pom.xml b/celements-search-lucene/pom.xml index 1d4fbffc..2170da2a 100644 --- a/celements-search-lucene/pom.xml +++ b/celements-search-lucene/pom.xml @@ -25,22 +25,22 @@ com.celements celements - 6.6 + 7.0-SNAPSHOT celements-search-lucene - 6.4-SNAPSHOT + 7.0-SNAPSHOT Celements Search Lucene com.celements celements-model - 6.6 + 7.0-SNAPSHOT provided com.celements celements-core - 6.14 + 7.0-SNAPSHOT provided From 4172d209a120209ca915fba85e9f8ae91e0c3922 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:58:17 +0100 Subject: [PATCH 09/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-search/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/celements-search/pom.xml b/celements-search/pom.xml index e41e4839..5189df93 100644 --- a/celements-search/pom.xml +++ b/celements-search/pom.xml @@ -24,23 +24,23 @@ com.celements celements - 6.6 + 7.0-SNAPSHOT 4.0.0 celements-search - 6.5-SNAPSHOT + 7.0-SNAPSHOT Celements Search com.celements celements-model - 6.7 + 7.0-SNAPSHOT provided com.celements celements-core - 6.15 + 7.0-SNAPSHOT provided @@ -51,12 +51,12 @@ com.celements celements-search-lucene - 6.3 + 7.0-SNAPSHOT com.celements celements-scheduler - 6.2 + 7.0-SNAPSHOT provided From 546e42fe47ee62315d0d503bad9f8c39a0c3a18c Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 14:59:04 +0100 Subject: [PATCH 10/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-calendar-gdata/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/celements-calendar-gdata/pom.xml b/celements-calendar-gdata/pom.xml index 5514ab5b..bbe0b40c 100644 --- a/celements-calendar-gdata/pom.xml +++ b/celements-calendar-gdata/pom.xml @@ -26,11 +26,11 @@ com.celements celements - 6.0-SNAPSHOT + 7.0-SNAPSHOT 4.0.0 celements-calendar-gdata - 6.0-SNAPSHOT + 7.0-SNAPSHOT Celements Calendar @@ -42,7 +42,7 @@ com.celements celements-calendar - 6.0-SNAPSHOT + 7.0-SNAPSHOT com.google.gdata From 2827092663e093815805ff11bbda7e9a377e0cf7 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 15:11:37 +0100 Subject: [PATCH 11/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-webdav/pom.xml | 16 +++++++++++----- .../com/celements/webdav/SardineAdapter.java | 8 +++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/celements-webdav/pom.xml b/celements-webdav/pom.xml index 8ab93571..08b14a86 100644 --- a/celements-webdav/pom.xml +++ b/celements-webdav/pom.xml @@ -24,30 +24,36 @@ com.celements celements - 6.0 + 7.0-SNAPSHOT 4.0.0 celements-webdav - 6.1-SNAPSHOT + 7.0-SNAPSHOT Celements WebDAV Client com.celements celements-xwiki-core - 6.0 + 7.0-SNAPSHOT provided com.celements celements-model - 6.0 + 7.0-SNAPSHOT provided com.celements celements-core - 6.0 + 7.0-SNAPSHOT + provided + + + com.celements + celements-user-manager + 6.3 provided diff --git a/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java b/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java index 0a9d5388..7326d556 100644 --- a/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java +++ b/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java @@ -18,8 +18,6 @@ import javax.annotation.Nullable; import javax.net.ssl.SSLContext; import javax.validation.constraints.NotNull; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriBuilderException; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.io.IOUtils; @@ -55,6 +53,9 @@ import com.github.sardine.impl.SardineException; import com.github.sardine.impl.SardineImpl; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriBuilderException; + @Component(SardineAdapter.NAME) public class SardineAdapter implements WebDavService, Initializable { @@ -190,7 +191,8 @@ URL buildCompleteUrl(Path path) { if (checkNotNull(path).isAbsolute()) { path = Paths.get("/", baseUrl.getPath()).relativize(path); } - return UriBuilder.fromUri(baseUrl.toURI()).path(path.normalize().toString()).build().toURL(); + return UriBuilder.fromUri(baseUrl.toURI()).path(path.normalize().toString()).build() + .toURL(); } catch (URISyntaxException | UriBuilderException | MalformedURLException exc) { // this shouldn't happen since baseUrl and path are already well defined objects throw new IllegalArgumentException(MessageFormat.format("unable to build url with " From 5c23c95bba24719deb17782ed8e6c90739659a96 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 15:33:18 +0100 Subject: [PATCH 12/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-layout/component/pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/celements-layout/component/pom.xml b/celements-layout/component/pom.xml index 7d3218c5..0ee8c618 100644 --- a/celements-layout/component/pom.xml +++ b/celements-layout/component/pom.xml @@ -3,29 +3,29 @@ com.celements celements - 6.6 + 7.0-SNAPSHOT 4.0.0 celements-layout - 6.5-SNAPSHOT + 7.0-SNAPSHOT Celements Layout com.celements celements-core - 6.14 + 7.0-SNAPSHOT provided com.celements celements-model - 6.6 + 7.0-SNAPSHOT provided com.celements celements-spring-security - 6.0 + 7.0-SNAPSHOT provided From 6c924ff771cefc64234a690a3639a0a626254e0d Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 15:34:37 +0100 Subject: [PATCH 13/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-tag/pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/celements-tag/pom.xml b/celements-tag/pom.xml index 57b02b0b..8f756d15 100644 --- a/celements-tag/pom.xml +++ b/celements-tag/pom.xml @@ -24,41 +24,41 @@ com.celements celements - 6.6 + 7.0-SNAPSHOT 4.0.0 celements-tag - 6.6-SNAPSHOT + 7.0-SNAPSHOT Celements Tags com.celements celements-xwiki-core - 6.9 + 7.0-SNAPSHOT provided com.celements celements-model - 6.6 + 7.0-SNAPSHOT provided com.celements celements-core - 6.14 + 7.0-SNAPSHOT provided com.celements celements-search - 6.3 + 7.0-SNAPSHOT provided com.celements celements-spring-security - 6.0 + 7.0-SNAPSHOT provided From 72a3b6d25f355184a3774464c0797d406123f496 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 15:35:25 +0100 Subject: [PATCH 14/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-user-manager/component/pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/celements-user-manager/component/pom.xml b/celements-user-manager/component/pom.xml index 9da4003d..b3bf6e7a 100644 --- a/celements-user-manager/component/pom.xml +++ b/celements-user-manager/component/pom.xml @@ -24,41 +24,41 @@ com.celements celements - 6.6 + 7.0-SNAPSHOT 4.0.0 celements-user-manager - 6.4-SNAPSHOT + 7.0-SNAPSHOT Celements User Manager com.celements celements-model - 6.6 + 7.0-SNAPSHOT provided com.celements celements-wiki-manager - 6.1 + 7.0-SNAPSHOT provided com.celements celements-core - 6.14 + 7.0-SNAPSHOT provided com.celements celements-subsystem-migration-manager - 6.0 + 7.0-SNAPSHOT provided com.celements celements-mailsender - 6.0 + 7.0-SNAPSHOT provided From b5c2635ad7a6b1d01a06f4c66ccb2aa5361249cc Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 16:34:02 +0100 Subject: [PATCH 15/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-webdav/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/celements-webdav/pom.xml b/celements-webdav/pom.xml index 08b14a86..41f45076 100644 --- a/celements-webdav/pom.xml +++ b/celements-webdav/pom.xml @@ -53,7 +53,7 @@ com.celements celements-user-manager - 6.3 + 7.0-SNAPSHOT provided From a61c96ffa0af56ed690c5261158801182683e835 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 16:50:43 +0100 Subject: [PATCH 16/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-layout/web-module/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/celements-layout/web-module/pom.xml b/celements-layout/web-module/pom.xml index b97f541a..523aba2b 100644 --- a/celements-layout/web-module/pom.xml +++ b/celements-layout/web-module/pom.xml @@ -26,18 +26,18 @@ com.celements celementsweb - 6.2 + 7.0-SNAPSHOT 4.0.0 celements-layout-web - 6.5-SNAPSHOT + 7.0-SNAPSHOT war Celements Layout Web com.celements celements-layout - 6.4 + 7.0-SNAPSHOT From 7419afbb49ea552d19a8229be51353dc7e98316a Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 16:52:44 +0100 Subject: [PATCH 17/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-user-manager/web-module/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/celements-user-manager/web-module/pom.xml b/celements-user-manager/web-module/pom.xml index 7eed29f6..348ccdab 100644 --- a/celements-user-manager/web-module/pom.xml +++ b/celements-user-manager/web-module/pom.xml @@ -26,13 +26,13 @@ com.celements celementsweb - 6.2 + 7.0-SNAPSHOT 4.0.0 celements-user-manager-web war User Manager Web-Module - 6.4-SNAPSHOT + 7.0-SNAPSHOT @@ -46,7 +46,7 @@ com.celements celements-user-manager - 6.3 + 7.0-SNAPSHOT From f1fef1eda9f7e7696b8cb839371a52182dc8f590 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 16:58:00 +0100 Subject: [PATCH 18/34] fix PaymentService --- .../com/celements/payment/PaymentService.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/celements-payment/src/main/java/com/celements/payment/PaymentService.java b/celements-payment/src/main/java/com/celements/payment/PaymentService.java index 3e095bb3..3622d06b 100644 --- a/celements-payment/src/main/java/com/celements/payment/PaymentService.java +++ b/celements-payment/src/main/java/com/celements/payment/PaymentService.java @@ -25,8 +25,6 @@ import java.util.List; import java.util.Map; -import org.hibernate.HibernateException; -import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.component.annotation.Component; @@ -36,7 +34,7 @@ import com.celements.model.context.ModelContext; import com.celements.payment.exception.PaymentException; import com.celements.payment.raw.PaymentRawObject; -import com.celements.web.plugin.api.CelementsWebPluginApi; +import com.celements.web.plugin.CelementsWebPlugin; import com.google.common.base.Functions; import com.google.common.collect.Lists; import com.xpn.xwiki.XWikiContext; @@ -69,9 +67,9 @@ public void executePaymentAction(Map data) { XWikiDocument actionXdoc = getXWikiContext().getWiki().getDocument(callbackDocRef, getXWikiContext()); Document actionDoc = actionXdoc.newDocument(getXWikiContext()); - CelementsWebPluginApi celementsweb = (CelementsWebPluginApi) getXWikiContext().getWiki().getPluginApi( - "celementsweb", getXWikiContext()); - celementsweb.getPlugin().executeAction(actionDoc, data, actionXdoc, getXWikiContext()); + CelementsWebPlugin celementsweb = (CelementsWebPlugin) getXWikiContext().getWiki() + .getPlugin("celementsweb", getXWikiContext()); + celementsweb.executeAction(actionDoc, data, actionXdoc, getXWikiContext()); } else { LOGGER.warn("Failed to execute payment action because Payment.CallbackActions" + " does not exist in [" + getXWikiContext().getDatabase() + "]."); @@ -84,13 +82,9 @@ public void executePaymentAction(Map data) { @Override public void storePaymentObject(final PaymentRawObject paymentObj) throws PaymentException { try { - getHibStore().executeWrite(context.getXWikiContext(), true, new HibernateCallback() { - - @Override - public Void doInHibernate(Session session) throws HibernateException { - session.saveOrUpdate(paymentObj); - return null; - } + getHibStore().executeWrite(context.getXWikiContext(), true, (HibernateCallback) session -> { + session.saveOrUpdate(paymentObj); + return null; }); LOGGER.info("storePaymentObject: '{}'", paymentObj); } catch (XWikiException xwe) { From 57bb5962f685ea652be782d0cfcd05abb3a29bdd Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 16:58:21 +0100 Subject: [PATCH 19/34] [prepare-major] update to major version 7.0-SNAPSHOT --- celements-payment/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/celements-payment/pom.xml b/celements-payment/pom.xml index 5d33ecba..9fe9c909 100644 --- a/celements-payment/pom.xml +++ b/celements-payment/pom.xml @@ -26,23 +26,23 @@ com.celements celements - 6.0 + 7.0-SNAPSHOT 4.0.0 payment - 6.1-SNAPSHOT + 7.0-SNAPSHOT Celements Payment com.celements celements-model - 6.0 + 7.0-SNAPSHOT provided com.celements celements-core - 6.0 + 7.0-SNAPSHOT provided From 3259cd52d95c6faadb47a6e471f1bb8742de3b01 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 19:26:20 +0100 Subject: [PATCH 20/34] add wiki-manager --- celements-webdav/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/celements-webdav/pom.xml b/celements-webdav/pom.xml index 41f45076..32ef2155 100644 --- a/celements-webdav/pom.xml +++ b/celements-webdav/pom.xml @@ -50,6 +50,12 @@ 7.0-SNAPSHOT provided + + com.celements + celements-wiki-manager + 7.0-SNAPSHOT + provided + com.celements celements-user-manager From 0b5dd4a8172d9b1fff2f9d7de16c1a0a48af1d5a Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 1 Jan 2026 19:33:12 +0100 Subject: [PATCH 21/34] add javax.ws.rs --- celements-webdav/pom.xml | 6 ++++++ .../src/main/java/com/celements/webdav/SardineAdapter.java | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/celements-webdav/pom.xml b/celements-webdav/pom.xml index 32ef2155..80396bbe 100644 --- a/celements-webdav/pom.xml +++ b/celements-webdav/pom.xml @@ -63,6 +63,12 @@ provided + + javax.ws.rs + javax.ws.rs-api + provided + + diff --git a/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java b/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java index 7326d556..1855c09f 100644 --- a/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java +++ b/celements-webdav/src/main/java/com/celements/webdav/SardineAdapter.java @@ -18,6 +18,8 @@ import javax.annotation.Nullable; import javax.net.ssl.SSLContext; import javax.validation.constraints.NotNull; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriBuilderException; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.io.IOUtils; @@ -53,9 +55,6 @@ import com.github.sardine.impl.SardineException; import com.github.sardine.impl.SardineImpl; -import jakarta.ws.rs.core.UriBuilder; -import jakarta.ws.rs.core.UriBuilderException; - @Component(SardineAdapter.NAME) public class SardineAdapter implements WebDavService, Initializable { From 6f02a825a1488ca81111b279393e3e6d5cc5eea0 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Sat, 10 Jan 2026 13:07:35 +0100 Subject: [PATCH 22/34] merge celements-search-lucene into celements-search --- .gitignore | 1 - celements-search-lucene/pom.xml | 103 ------------------ .../main/resources/META-INF/components.txt | 5 - celements-search/pom.xml | 56 ++++++++-- .../search/lucene/LuceneDocType.java | 0 .../lucene/index/analysis/CelAnalyzer.java | 0 .../analysis/CelementsSimpleAnalyzer.java | 0 .../index/queue/IndexQueuePriority.java | 0 .../rebuild/LuceneIndexRebuildService.java | 0 .../plugin/lucene/AbstractDocumentData.java | 0 .../plugin/lucene/AbstractIndexData.java | 0 .../xwiki/plugin/lucene/AttachmentData.java | 0 .../xpn/xwiki/plugin/lucene/DeleteData.java | 0 .../xpn/xwiki/plugin/lucene/DocumentData.java | 0 .../xpn/xwiki/plugin/lucene/IndexFields.java | 0 .../xwiki/plugin/lucene/IndexRebuilder.java | 0 .../xpn/xwiki/plugin/lucene/IndexUpdater.java | 0 .../xpn/xwiki/plugin/lucene/LucenePlugin.java | 0 .../xwiki/plugin/lucene/LucenePluginApi.java | 0 .../xpn/xwiki/plugin/lucene/SearchResult.java | 0 .../xwiki/plugin/lucene/SearchResults.java | 0 .../com/xpn/xwiki/plugin/lucene/WikiData.java | 0 .../plugin/lucene/XWikiDocumentQueue.java | 0 .../indexExtension/ILuceneIndexExtender.java | 0 .../ILuceneIndexExtensionServiceRole.java | 0 .../indexExtension/IndexExtensionField.java | 0 .../LuceneIndexExtensionService.java | 0 .../event/LuceneDocumentDeletedEvent.java | 0 .../event/LuceneDocumentDeletingEvent.java | 0 .../event/LuceneDocumentIndexedEvent.java | 0 .../event/LuceneDocumentIndexingEvent.java | 0 .../ActionExecutionEventListener.java | 0 .../ISearcherProviderRole.java | 0 .../SearchProviderDebugScriptService.java | 0 .../searcherProvider/SearcherProvider.java | 0 .../SearcherProviderManager.java | 0 .../src/main/resources/META-INF/MANIFEST.MF | 6 +- .../main/resources/META-INF/components.txt | 5 + .../plugin/lucene/AttachmentDataTest.java | 0 .../plugin/lucene/TestXWikiDocument.java | 0 .../IndexExtensionFieldTest.java | 0 .../LuceneIndexExtensionServiceTest.java | 0 .../ActionExecutionEventListenerTest.java | 0 .../SearcherProviderManagerTest.java | 0 .../SearcherProviderTest.java | 0 .../src/test/resources/html.html | 0 .../src/test/resources/msoffice97.doc | Bin .../src/test/resources/opendocument.odt | Bin .../src/test/resources/openxml.docx | Bin .../src/test/resources/pdf.pdf | Bin .../src/test/resources/txt.txt | 0 .../src/test/resources/zip.zip | Bin 52 files changed, 57 insertions(+), 119 deletions(-) delete mode 100644 celements-search-lucene/pom.xml delete mode 100644 celements-search-lucene/src/main/resources/META-INF/components.txt rename {celements-search-lucene => celements-search}/src/main/java/com/celements/search/lucene/LuceneDocType.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/celements/search/lucene/index/analysis/CelAnalyzer.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/celements/search/lucene/index/analysis/CelementsSimpleAnalyzer.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/celements/search/lucene/index/queue/IndexQueuePriority.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/celements/search/lucene/index/rebuild/LuceneIndexRebuildService.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractDocumentData.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractIndexData.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/AttachmentData.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/DeleteData.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/DocumentData.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/IndexFields.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/IndexRebuilder.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/IndexUpdater.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePlugin.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePluginApi.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResult.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResults.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/WikiData.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/XWikiDocumentQueue.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtender.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtensionServiceRole.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionField.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionService.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletedEvent.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletingEvent.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexedEvent.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexingEvent.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListener.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ISearcherProviderRole.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearchProviderDebugScriptService.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProvider.java (100%) rename {celements-search-lucene => celements-search}/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManager.java (100%) rename {celements-search-lucene => celements-search}/src/main/resources/META-INF/MANIFEST.MF (92%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/AttachmentDataTest.java (100%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/TestXWikiDocument.java (100%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionFieldTest.java (100%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionServiceTest.java (100%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListenerTest.java (100%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManagerTest.java (100%) rename {celements-search-lucene => celements-search}/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderTest.java (100%) rename {celements-search-lucene => celements-search}/src/test/resources/html.html (100%) rename {celements-search-lucene => celements-search}/src/test/resources/msoffice97.doc (100%) rename {celements-search-lucene => celements-search}/src/test/resources/opendocument.odt (100%) rename {celements-search-lucene => celements-search}/src/test/resources/openxml.docx (100%) rename {celements-search-lucene => celements-search}/src/test/resources/pdf.pdf (100%) rename {celements-search-lucene => celements-search}/src/test/resources/txt.txt (100%) rename {celements-search-lucene => celements-search}/src/test/resources/zip.zip (100%) diff --git a/.gitignore b/.gitignore index cd5d2338..0c3075cc 100644 --- a/.gitignore +++ b/.gitignore @@ -85,7 +85,6 @@ local.properties *.war *.nar *.ear -*.zip *.tar.gz *.rar diff --git a/celements-search-lucene/pom.xml b/celements-search-lucene/pom.xml deleted file mode 100644 index 2170da2a..00000000 --- a/celements-search-lucene/pom.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - 4.0.0 - - com.celements - celements - 7.0-SNAPSHOT - - celements-search-lucene - 7.0-SNAPSHOT - Celements Search Lucene - - - com.celements - celements-model - 7.0-SNAPSHOT - provided - - - com.celements - celements-core - 7.0-SNAPSHOT - provided - - - org.apache.lucene - lucene-core - 3.4.0 - - - org.apache.lucene - lucene-analyzers - 3.4.0 - - - org.apache.lucene - lucene-queryparser - 3.4.0 - - - commons-collections - commons-collections - - - org.apache.tika - tika-parsers - - - org.apache.pdfbox - jbig2-imageio - - - com.github.jai-imageio - jai-imageio-jpeg2000 - - - org.xerial - sqlite-jdbc - - - - - javax.servlet - javax.servlet-api - test - - - - org.apache.xmlgraphics - batik-ext - 1.7 - test - - - - - scm:git:git@github.com:celements/celements-features.git - scm:git:git@github.com:celements/celements-features.git - https://github.com/celements/celements-features/celements-search-lucene - HEAD - - diff --git a/celements-search-lucene/src/main/resources/META-INF/components.txt b/celements-search-lucene/src/main/resources/META-INF/components.txt deleted file mode 100644 index bde1c1d0..00000000 --- a/celements-search-lucene/src/main/resources/META-INF/components.txt +++ /dev/null @@ -1,5 +0,0 @@ -com.xpn.xwiki.plugin.lucene.searcherProvider.SearcherProviderManager -com.xpn.xwiki.plugin.lucene.searcherProvider.ActionExecutionEventListener -com.xpn.xwiki.plugin.lucene.indexExtension.LuceneIndexExtensionService -com.xpn.xwiki.plugin.lucene.searcherProvider.SearchProviderDebugScriptService -com.xpn.xwiki.plugin.lucene.IndexRebuilder diff --git a/celements-search/pom.xml b/celements-search/pom.xml index 5189df93..32ff04f9 100644 --- a/celements-search/pom.xml +++ b/celements-search/pom.xml @@ -43,22 +43,64 @@ 7.0-SNAPSHOT provided + + com.celements + celements-scheduler + 7.0-SNAPSHOT + provided + + + + + org.apache.lucene + lucene-core + 3.4.0 + + + org.apache.lucene + lucene-analyzers + 3.4.0 + org.apache.lucene lucene-queryparser 3.4.0 - com.celements - celements-search-lucene - 7.0-SNAPSHOT + commons-collections + commons-collections - com.celements - celements-scheduler - 7.0-SNAPSHOT - provided + org.apache.tika + tika-parsers + + + org.apache.pdfbox + jbig2-imageio + + + com.github.jai-imageio + jai-imageio-jpeg2000 + + + org.xerial + sqlite-jdbc + + + + + javax.servlet + javax.servlet-api + test + + + + org.apache.xmlgraphics + batik-ext + 1.7 + test + scm:git:git@github.com:celements/celements-features.git diff --git a/celements-search-lucene/src/main/java/com/celements/search/lucene/LuceneDocType.java b/celements-search/src/main/java/com/celements/search/lucene/LuceneDocType.java similarity index 100% rename from celements-search-lucene/src/main/java/com/celements/search/lucene/LuceneDocType.java rename to celements-search/src/main/java/com/celements/search/lucene/LuceneDocType.java diff --git a/celements-search-lucene/src/main/java/com/celements/search/lucene/index/analysis/CelAnalyzer.java b/celements-search/src/main/java/com/celements/search/lucene/index/analysis/CelAnalyzer.java similarity index 100% rename from celements-search-lucene/src/main/java/com/celements/search/lucene/index/analysis/CelAnalyzer.java rename to celements-search/src/main/java/com/celements/search/lucene/index/analysis/CelAnalyzer.java diff --git a/celements-search-lucene/src/main/java/com/celements/search/lucene/index/analysis/CelementsSimpleAnalyzer.java b/celements-search/src/main/java/com/celements/search/lucene/index/analysis/CelementsSimpleAnalyzer.java similarity index 100% rename from celements-search-lucene/src/main/java/com/celements/search/lucene/index/analysis/CelementsSimpleAnalyzer.java rename to celements-search/src/main/java/com/celements/search/lucene/index/analysis/CelementsSimpleAnalyzer.java diff --git a/celements-search-lucene/src/main/java/com/celements/search/lucene/index/queue/IndexQueuePriority.java b/celements-search/src/main/java/com/celements/search/lucene/index/queue/IndexQueuePriority.java similarity index 100% rename from celements-search-lucene/src/main/java/com/celements/search/lucene/index/queue/IndexQueuePriority.java rename to celements-search/src/main/java/com/celements/search/lucene/index/queue/IndexQueuePriority.java diff --git a/celements-search-lucene/src/main/java/com/celements/search/lucene/index/rebuild/LuceneIndexRebuildService.java b/celements-search/src/main/java/com/celements/search/lucene/index/rebuild/LuceneIndexRebuildService.java similarity index 100% rename from celements-search-lucene/src/main/java/com/celements/search/lucene/index/rebuild/LuceneIndexRebuildService.java rename to celements-search/src/main/java/com/celements/search/lucene/index/rebuild/LuceneIndexRebuildService.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractDocumentData.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractDocumentData.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractDocumentData.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractDocumentData.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractIndexData.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractIndexData.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractIndexData.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/AbstractIndexData.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/AttachmentData.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/AttachmentData.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/AttachmentData.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/AttachmentData.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/DeleteData.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/DeleteData.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/DeleteData.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/DeleteData.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/DocumentData.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/DocumentData.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/DocumentData.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/DocumentData.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/IndexFields.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/IndexFields.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/IndexFields.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/IndexFields.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/IndexRebuilder.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/IndexRebuilder.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/IndexRebuilder.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/IndexRebuilder.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/IndexUpdater.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/IndexUpdater.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/IndexUpdater.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/IndexUpdater.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePlugin.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePlugin.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePlugin.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePlugin.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePluginApi.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePluginApi.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePluginApi.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/LucenePluginApi.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResult.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResult.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResult.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResult.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResults.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResults.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResults.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/SearchResults.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/WikiData.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/WikiData.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/WikiData.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/WikiData.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/XWikiDocumentQueue.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/XWikiDocumentQueue.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/XWikiDocumentQueue.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/XWikiDocumentQueue.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtender.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtender.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtender.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtender.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtensionServiceRole.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtensionServiceRole.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtensionServiceRole.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/ILuceneIndexExtensionServiceRole.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionField.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionField.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionField.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionField.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionService.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionService.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionService.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionService.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletedEvent.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletedEvent.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletedEvent.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletedEvent.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletingEvent.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletingEvent.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletingEvent.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentDeletingEvent.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexedEvent.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexedEvent.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexedEvent.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexedEvent.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexingEvent.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexingEvent.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexingEvent.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/observation/event/LuceneDocumentIndexingEvent.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListener.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListener.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListener.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListener.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ISearcherProviderRole.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ISearcherProviderRole.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ISearcherProviderRole.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ISearcherProviderRole.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearchProviderDebugScriptService.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearchProviderDebugScriptService.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearchProviderDebugScriptService.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearchProviderDebugScriptService.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProvider.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProvider.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProvider.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProvider.java diff --git a/celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManager.java b/celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManager.java similarity index 100% rename from celements-search-lucene/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManager.java rename to celements-search/src/main/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManager.java diff --git a/celements-search-lucene/src/main/resources/META-INF/MANIFEST.MF b/celements-search/src/main/resources/META-INF/MANIFEST.MF similarity index 92% rename from celements-search-lucene/src/main/resources/META-INF/MANIFEST.MF rename to celements-search/src/main/resources/META-INF/MANIFEST.MF index 5e949512..254272e1 100644 --- a/celements-search-lucene/src/main/resources/META-INF/MANIFEST.MF +++ b/celements-search/src/main/resources/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ -Manifest-Version: 1.0 -Class-Path: - +Manifest-Version: 1.0 +Class-Path: + diff --git a/celements-search/src/main/resources/META-INF/components.txt b/celements-search/src/main/resources/META-INF/components.txt index 4fdbaf4a..66e37763 100644 --- a/celements-search/src/main/resources/META-INF/components.txt +++ b/celements-search/src/main/resources/META-INF/components.txt @@ -1,3 +1,8 @@ +com.xpn.xwiki.plugin.lucene.searcherProvider.SearcherProviderManager +com.xpn.xwiki.plugin.lucene.searcherProvider.ActionExecutionEventListener +com.xpn.xwiki.plugin.lucene.indexExtension.LuceneIndexExtensionService +com.xpn.xwiki.plugin.lucene.searcherProvider.SearchProviderDebugScriptService +com.xpn.xwiki.plugin.lucene.IndexRebuilder com.celements.search.lucene.LuceneSearchService com.celements.search.lucene.LuceneSearchScriptService com.celements.search.web.classes.WebSearchClassConfig diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/AttachmentDataTest.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/AttachmentDataTest.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/AttachmentDataTest.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/AttachmentDataTest.java diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/TestXWikiDocument.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/TestXWikiDocument.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/TestXWikiDocument.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/TestXWikiDocument.java diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionFieldTest.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionFieldTest.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionFieldTest.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/IndexExtensionFieldTest.java diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionServiceTest.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionServiceTest.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionServiceTest.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/indexExtension/LuceneIndexExtensionServiceTest.java diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListenerTest.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListenerTest.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListenerTest.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/ActionExecutionEventListenerTest.java diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManagerTest.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManagerTest.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManagerTest.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderManagerTest.java diff --git a/celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderTest.java b/celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderTest.java similarity index 100% rename from celements-search-lucene/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderTest.java rename to celements-search/src/test/java/com/xpn/xwiki/plugin/lucene/searcherProvider/SearcherProviderTest.java diff --git a/celements-search-lucene/src/test/resources/html.html b/celements-search/src/test/resources/html.html similarity index 100% rename from celements-search-lucene/src/test/resources/html.html rename to celements-search/src/test/resources/html.html diff --git a/celements-search-lucene/src/test/resources/msoffice97.doc b/celements-search/src/test/resources/msoffice97.doc similarity index 100% rename from celements-search-lucene/src/test/resources/msoffice97.doc rename to celements-search/src/test/resources/msoffice97.doc diff --git a/celements-search-lucene/src/test/resources/opendocument.odt b/celements-search/src/test/resources/opendocument.odt similarity index 100% rename from celements-search-lucene/src/test/resources/opendocument.odt rename to celements-search/src/test/resources/opendocument.odt diff --git a/celements-search-lucene/src/test/resources/openxml.docx b/celements-search/src/test/resources/openxml.docx similarity index 100% rename from celements-search-lucene/src/test/resources/openxml.docx rename to celements-search/src/test/resources/openxml.docx diff --git a/celements-search-lucene/src/test/resources/pdf.pdf b/celements-search/src/test/resources/pdf.pdf similarity index 100% rename from celements-search-lucene/src/test/resources/pdf.pdf rename to celements-search/src/test/resources/pdf.pdf diff --git a/celements-search-lucene/src/test/resources/txt.txt b/celements-search/src/test/resources/txt.txt similarity index 100% rename from celements-search-lucene/src/test/resources/txt.txt rename to celements-search/src/test/resources/txt.txt diff --git a/celements-search-lucene/src/test/resources/zip.zip b/celements-search/src/test/resources/zip.zip similarity index 100% rename from celements-search-lucene/src/test/resources/zip.zip rename to celements-search/src/test/resources/zip.zip From 130d854d2c63ae174f6c2dc27ba29b8c1e26a116 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 15 Jan 2026 12:39:17 +0100 Subject: [PATCH 23/34] S3AttachmentContentStore --- celements-s3/pom.xml | 45 +++++++ .../store/s3/S3AttachmentContentStore.java | 114 ++++++++++++++++++ .../java/com/celements/store/s3/S3Config.java | 98 +++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 celements-s3/pom.xml create mode 100644 celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java create mode 100644 celements-s3/src/main/java/com/celements/store/s3/S3Config.java diff --git a/celements-s3/pom.xml b/celements-s3/pom.xml new file mode 100644 index 00000000..f6d1df17 --- /dev/null +++ b/celements-s3/pom.xml @@ -0,0 +1,45 @@ + + + com.celements + celements + 7.0-SNAPSHOT + + 4.0.0 + celements-s3 + 7.0-SNAPSHOT + Celements S3 Integration + + + com.celements + celements-config-source + 7.0-SNAPSHOT + provided + + + com.celements + celements-servlet + 7.0-SNAPSHOT + provided + + + com.celements + celements-model + 7.0-SNAPSHOT + provided + + + software.amazon.awssdk + s3 + 2.41.5 + + + + scm:git:git@github.com:celements/celements-base.git + scm:git:git@github.com:celements/celements-base.git + https://github.com/celements/celements-base/tree/dev/celements-s3 + HEAD + + diff --git a/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java new file mode 100644 index 00000000..235b92f3 --- /dev/null +++ b/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java @@ -0,0 +1,114 @@ +package com.celements.store.s3; + +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import com.celements.servlet.NodeConfig.NodeIdentity; +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiAttachmentContent; +import com.xpn.xwiki.store.AttachmentContentStore; + +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.NoSuchKeyException; + +@Component +@Named(S3AttachmentContentStore.STORE_NAME) +@Lazy +public class S3AttachmentContentStore implements AttachmentContentStore { + + public static final String STORE_NAME = "store.attachment.content.s3"; + + private final NodeIdentity nodeIdentity; + private final S3Client s3Client; + private final String s3BucketFilebase; + + @Inject + public S3AttachmentContentStore( + NodeIdentity nodeIdentity, + Optional s3Client, + @Named("s3BucketFilebase") Optional s3BucketFilebase) { + this.nodeIdentity = nodeIdentity; + this.s3Client = s3Client.orElseThrow(IllegalStateException::new); + this.s3BucketFilebase = s3BucketFilebase.orElseThrow(IllegalStateException::new); + } + + @Override + public String getStoreName() { + return STORE_NAME; + } + + public String buildS3Key(XWikiAttachment attachment) { + var doc = attachment.getDoc(); + var wiki = doc.getDocumentReference().getWikiReference(); + return String.join("/", + "attcontent", "v1", // base path + "app", nodeIdentity.appName(), // allow bucket multi-tenancy by app name + "wiki", wiki.getName(), // identify wiki + "doc", Long.toString(doc.getId()), // identify document + "att", Long.toString(attachment.getId()), // identify attachment + "ver", attachment.getVersion()); // identify attachment version + } + + public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentStoreException { + try { + s3Client.headObject(builder -> builder + .bucket(s3BucketFilebase) + .key(buildS3Key(attachment))); + return true; + } catch (NoSuchKeyException e) { + return false; + } catch (Exception e) { + throw new AttachmentContentStoreException("Failed checking attachment", e); + } + } + + @Override + public void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + try { + var attachment = content.getAttachment(); + try (var data = content.getContentInputStream()) { + s3Client.putObject(builder -> builder + .bucket(s3BucketFilebase) + .key(buildS3Key(attachment)) + .contentLength(content.getSize()) + .contentType(attachment.getMimeType()), + RequestBody.fromInputStream(data, content.getSize())); + } + } catch (Exception e) { + throw new AttachmentContentStoreException("Failed saving attachment", e); + } + } + + @Override + public void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + try { + var attachment = content.getAttachment(); + try (var data = s3Client.getObject(builder -> builder + .bucket(s3BucketFilebase) + .key(buildS3Key(attachment)))) { + content.setContent(data); + } + } catch (Exception e) { + throw new AttachmentContentStoreException("Failed loading attachment", e); + } + } + + @Override + public void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + try { + var attachment = content.getAttachment(); + s3Client.deleteObject(builder -> builder + .bucket(s3BucketFilebase) + .key(buildS3Key(attachment))); + } catch (Exception e) { + throw new AttachmentContentStoreException("Failed deleting attachment", e); + } + } + +} diff --git a/celements-s3/src/main/java/com/celements/store/s3/S3Config.java b/celements-s3/src/main/java/com/celements/store/s3/S3Config.java new file mode 100644 index 00000000..36b8e4ab --- /dev/null +++ b/celements-s3/src/main/java/com/celements/store/s3/S3Config.java @@ -0,0 +1,98 @@ +package com.celements.store.s3; + +import java.net.URI; +import java.util.Optional; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.xwiki.configuration.ConfigurationSource; + +import com.celements.configuration.CelementsAllPropertiesConfigurationSource; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; +import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +@Configuration +public class S3Config { + + private static final Logger LOGGER = LoggerFactory.getLogger(S3Config.class); + + private final ConfigurationSource cfgSrc; + + @Inject + public S3Config(CelementsAllPropertiesConfigurationSource cfgSrc) { + this.cfgSrc = cfgSrc; + } + + @Bean(destroyMethod = "close") + @Nullable + public S3Client s3Client() { + var endpoint = cfgSrc.getProperty("celements.s3.endpoint", "").trim(); + var region = cfgSrc.getProperty("celements.s3.region", "eu-central").trim(); + if (endpoint.isEmpty()) { + LOGGER.info("S3 endpoint not configured"); + return null; + } + var client = S3Client.builder() + .endpointOverride(URI.create(endpoint)) + .region(Region.of(region)) + .credentialsProvider(StaticCredentialsProvider.create(buildCredentials())) + .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED) + .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED) + .build(); + testClient(client); + LOGGER.info("S3 configured: {}", endpoint); + return client; + } + + private void testClient(S3Client client) { + try { + client.listBuckets(); + } catch (Exception exc) { + client.close(); + throw exc; + } + } + + private AwsCredentials buildCredentials() { + var accessKey = cfgSrc.getProperty("celements.s3.accessKey", "").trim(); + var secretKey = cfgSrc.getProperty("celements.s3.secretKey", "").trim(); + if (accessKey.isEmpty() || secretKey.isEmpty()) { + throw new IllegalArgumentException("s3.accessKey/secretKey missing"); + } + return AwsBasicCredentials.builder() + .accessKeyId(accessKey) + .secretAccessKey(secretKey) + .build(); + } + + @Bean(name = "s3BucketFilebase") + @Nullable + public String s3BucketFilebase(Optional s3Client) { + var bucket = cfgSrc.getProperty("celements.s3.bucket.filebase", "").trim(); + if (bucket.isEmpty()) { + LOGGER.info("S3 filebase bucket not configured"); + return null; + } + testBucket(s3Client, bucket); + LOGGER.info("S3 filebase bucket configured: {}", bucket); + return bucket; + } + + private void testBucket(Optional s3Client, String bucket) { + s3Client + .orElseThrow(() -> new IllegalStateException("S3 client not configured")) + .headBucket(builder -> builder.bucket(bucket)); + } + +} From 9a693621fa4f2eff742952e1a441507011aa2735 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 15 Jan 2026 13:13:44 +0100 Subject: [PATCH 24/34] address PR comments --- .../store/s3/S3AttachmentContentStore.java | 49 ++++++++++++++----- .../java/com/celements/store/s3/S3Config.java | 2 +- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java index 235b92f3..ad4a52ef 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java @@ -16,6 +16,7 @@ import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.NoSuchKeyException; +import software.amazon.awssdk.services.s3.model.S3Exception; @Component @Named(S3AttachmentContentStore.STORE_NAME) @@ -34,8 +35,10 @@ public S3AttachmentContentStore( Optional s3Client, @Named("s3BucketFilebase") Optional s3BucketFilebase) { this.nodeIdentity = nodeIdentity; - this.s3Client = s3Client.orElseThrow(IllegalStateException::new); - this.s3BucketFilebase = s3BucketFilebase.orElseThrow(IllegalStateException::new); + this.s3Client = s3Client + .orElseThrow(() -> new IllegalStateException("S3Client missing")); + this.s3BucketFilebase = s3BucketFilebase + .orElseThrow(() -> new IllegalStateException("s3BucketFilebase missing")); } @Override @@ -43,6 +46,10 @@ public String getStoreName() { return STORE_NAME; } + /** + * Builds the S3 key for the given attachment. The key structure is as follows: + * attcontent/v1/app/{appName}/wiki/{wikiName}/doc/{docId}/att/{attachmentId}/rev/{version} + */ public String buildS3Key(XWikiAttachment attachment) { var doc = attachment.getDoc(); var wiki = doc.getDocumentReference().getWikiReference(); @@ -52,17 +59,20 @@ public String buildS3Key(XWikiAttachment attachment) { "wiki", wiki.getName(), // identify wiki "doc", Long.toString(doc.getId()), // identify document "att", Long.toString(attachment.getId()), // identify attachment - "ver", attachment.getVersion()); // identify attachment version + "rev", attachment.getVersion()); // identify attachment version } public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentStoreException { + var s3Key = buildS3Key(attachment); try { s3Client.headObject(builder -> builder .bucket(s3BucketFilebase) - .key(buildS3Key(attachment))); + .key(s3Key)); return true; } catch (NoSuchKeyException e) { return false; + } catch (S3Exception e) { + throw new AttachmentContentStoreException(buildS3ErrorMessage(s3Key, e), e); } catch (Exception e) { throw new AttachmentContentStoreException("Failed checking attachment", e); } @@ -70,16 +80,18 @@ public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentSt @Override public void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + var s3Key = buildS3Key(content.getAttachment()); try { - var attachment = content.getAttachment(); try (var data = content.getContentInputStream()) { s3Client.putObject(builder -> builder .bucket(s3BucketFilebase) - .key(buildS3Key(attachment)) - .contentLength(content.getSize()) - .contentType(attachment.getMimeType()), + .key(s3Key) + .contentLength((long) content.getSize()) + .contentType(content.getAttachment().getMimeType()), RequestBody.fromInputStream(data, content.getSize())); } + } catch (S3Exception e) { + throw new AttachmentContentStoreException(buildS3ErrorMessage(s3Key, e), e); } catch (Exception e) { throw new AttachmentContentStoreException("Failed saving attachment", e); } @@ -87,13 +99,17 @@ public void saveContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + var s3Key = buildS3Key(content.getAttachment()); try { - var attachment = content.getAttachment(); try (var data = s3Client.getObject(builder -> builder .bucket(s3BucketFilebase) - .key(buildS3Key(attachment)))) { + .key(s3Key))) { content.setContent(data); } + } catch (NoSuchKeyException e) { + throw new AttachmentContentStoreException("Attachment content not found in S3: " + s3Key, e); + } catch (S3Exception e) { + throw new AttachmentContentStoreException(buildS3ErrorMessage(s3Key, e), e); } catch (Exception e) { throw new AttachmentContentStoreException("Failed loading attachment", e); } @@ -101,14 +117,23 @@ public void loadContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { + var s3Key = buildS3Key(content.getAttachment()); try { - var attachment = content.getAttachment(); s3Client.deleteObject(builder -> builder .bucket(s3BucketFilebase) - .key(buildS3Key(attachment))); + .key(s3Key)); + } catch (S3Exception e) { + throw new AttachmentContentStoreException(buildS3ErrorMessage(s3Key, e), e); } catch (Exception e) { throw new AttachmentContentStoreException("Failed deleting attachment", e); } } + private static String buildS3ErrorMessage(String s3Key, S3Exception e) { + return String.format("S3 error for attachment (key=%s, status=%d, code=%s)", + s3Key, + e.statusCode(), + e.awsErrorDetails() != null ? e.awsErrorDetails().errorCode() : "n/a"); + } + } diff --git a/celements-s3/src/main/java/com/celements/store/s3/S3Config.java b/celements-s3/src/main/java/com/celements/store/s3/S3Config.java index 36b8e4ab..681353bc 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/S3Config.java +++ b/celements-s3/src/main/java/com/celements/store/s3/S3Config.java @@ -68,7 +68,7 @@ private AwsCredentials buildCredentials() { var accessKey = cfgSrc.getProperty("celements.s3.accessKey", "").trim(); var secretKey = cfgSrc.getProperty("celements.s3.secretKey", "").trim(); if (accessKey.isEmpty() || secretKey.isEmpty()) { - throw new IllegalArgumentException("s3.accessKey/secretKey missing"); + throw new IllegalArgumentException("celements.s3.accessKey/secretKey missing"); } return AwsBasicCredentials.builder() .accessKeyId(accessKey) From da81d07ae09e6c2bd5f401e289f01d0cb122e581 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Fri, 16 Jan 2026 14:02:27 +0100 Subject: [PATCH 25/34] move s3 store package --- .../celements/store/s3/{ => att}/S3AttachmentContentStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename celements-s3/src/main/java/com/celements/store/s3/{ => att}/S3AttachmentContentStore.java (99%) diff --git a/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java similarity index 99% rename from celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java rename to celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java index ad4a52ef..9c2e92e6 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java @@ -1,4 +1,4 @@ -package com.celements.store.s3; +package com.celements.store.s3.att; import java.util.Optional; From 4f769490342bc6ebcc7305e36770e41673080891 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Fri, 16 Jan 2026 14:04:14 +0100 Subject: [PATCH 26/34] add logging --- .../celements/store/s3/att/S3AttachmentContentStore.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java index 9c2e92e6..65786802 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java @@ -5,6 +5,8 @@ import javax.inject.Inject; import javax.inject.Named; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -23,6 +25,9 @@ @Lazy public class S3AttachmentContentStore implements AttachmentContentStore { + private static final Logger LOGGER = LoggerFactory + .getLogger(S3AttachmentContentStore.class); + public static final String STORE_NAME = "store.attachment.content.s3"; private final NodeIdentity nodeIdentity; @@ -64,6 +69,7 @@ public String buildS3Key(XWikiAttachment attachment) { public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentStoreException { var s3Key = buildS3Key(attachment); + LOGGER.info("hasContent - {} in {}", attachment, s3Key); try { s3Client.headObject(builder -> builder .bucket(s3BucketFilebase) @@ -81,6 +87,7 @@ public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentSt @Override public void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { var s3Key = buildS3Key(content.getAttachment()); + LOGGER.info("saveContent - {} to {}", content.getAttachment(), s3Key); try { try (var data = content.getContentInputStream()) { s3Client.putObject(builder -> builder @@ -100,6 +107,7 @@ public void saveContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { var s3Key = buildS3Key(content.getAttachment()); + LOGGER.info("loadContent - {} from {}", content.getAttachment(), s3Key); try { try (var data = s3Client.getObject(builder -> builder .bucket(s3BucketFilebase) @@ -118,6 +126,7 @@ public void loadContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { var s3Key = buildS3Key(content.getAttachment()); + LOGGER.info("deleteContent - {} from {}", content.getAttachment(), s3Key); try { s3Client.deleteObject(builder -> builder .bucket(s3BucketFilebase) From eb954265b88092ececbd7eac0006ff15c2962b5c Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 22 Jan 2026 23:27:33 +0100 Subject: [PATCH 27/34] deleteContent for all attachment contents --- .../s3/att/S3AttachmentContentStore.java | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java index 65786802..3b556e6c 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java @@ -1,5 +1,6 @@ package com.celements.store.s3.att; +import java.util.List; import java.util.Optional; import javax.inject.Inject; @@ -18,6 +19,7 @@ import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.NoSuchKeyException; +import software.amazon.awssdk.services.s3.model.ObjectIdentifier; import software.amazon.awssdk.services.s3.model.S3Exception; @Component @@ -53,22 +55,31 @@ public String getStoreName() { /** * Builds the S3 key for the given attachment. The key structure is as follows: - * attcontent/v1/app/{appName}/wiki/{wikiName}/doc/{docId}/att/{attachmentId}/rev/{version} + * attachment/{appName}/{wikiName}/{docId}/{attachmentId} */ - public String buildS3Key(XWikiAttachment attachment) { + public String buildS3AttachmentKey(XWikiAttachment attachment) { var doc = attachment.getDoc(); var wiki = doc.getDocumentReference().getWikiReference(); return String.join("/", - "attcontent", "v1", // base path - "app", nodeIdentity.appName(), // allow bucket multi-tenancy by app name - "wiki", wiki.getName(), // identify wiki - "doc", Long.toString(doc.getId()), // identify document - "att", Long.toString(attachment.getId()), // identify attachment - "rev", attachment.getVersion()); // identify attachment version + "attachment", // base path + nodeIdentity.appName(), // allow bucket multi-tenancy by app name + wiki.getName(), // identify wiki + Long.toString(doc.getId()), // identify document + Long.toString(attachment.getId())); // identify attachment + } + + /** + * Builds the S3 key for the given attachment. The key structure is as follows: + * attachment/{appName}/{wikiName}/{docId}/{attachmentId}/{version} + */ + public String buildS3AttachmentVersionKey(XWikiAttachment attachment) { + return String.join("/", + buildS3AttachmentKey(attachment), + attachment.getVersion()); // identify attachment version } public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentStoreException { - var s3Key = buildS3Key(attachment); + var s3Key = buildS3AttachmentVersionKey(attachment); LOGGER.info("hasContent - {} in {}", attachment, s3Key); try { s3Client.headObject(builder -> builder @@ -86,7 +97,7 @@ public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentSt @Override public void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { - var s3Key = buildS3Key(content.getAttachment()); + var s3Key = buildS3AttachmentVersionKey(content.getAttachment()); LOGGER.info("saveContent - {} to {}", content.getAttachment(), s3Key); try { try (var data = content.getContentInputStream()) { @@ -106,7 +117,7 @@ public void saveContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { - var s3Key = buildS3Key(content.getAttachment()); + var s3Key = buildS3AttachmentVersionKey(content.getAttachment()); LOGGER.info("loadContent - {} from {}", content.getAttachment(), s3Key); try { try (var data = s3Client.getObject(builder -> builder @@ -123,9 +134,31 @@ public void loadContent(XWikiAttachmentContent content) throws AttachmentContent } } + @Override + public void deleteContent(XWikiAttachment attachment) throws AttachmentContentStoreException { + var s3Prefix = buildS3AttachmentKey(attachment) + "/"; + LOGGER.info("deleteContent - {} from {}", attachment, s3Prefix); + List batch = s3Client.listObjectsV2(builder -> builder + .bucket(s3BucketFilebase) + .prefix(s3Prefix)) + .contents() + .stream() + .map(s3Object -> ObjectIdentifier.builder().key(s3Object.key()).build()) + .toList(); + if (batch.isEmpty()) { + return; + } else if (batch.size() >= 1000) { + throw new AttachmentContentStoreException( + "Too many objects to delete in S3 for attachment: " + attachment, null); + } + s3Client.deleteObjects(builder -> builder + .bucket(s3BucketFilebase) + .delete(deleteBuilder -> deleteBuilder.objects(batch))); + } + @Override public void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { - var s3Key = buildS3Key(content.getAttachment()); + var s3Key = buildS3AttachmentVersionKey(content.getAttachment()); LOGGER.info("deleteContent - {} from {}", content.getAttachment(), s3Key); try { s3Client.deleteObject(builder -> builder From a11b50d7ae0ea8e2bad3fe39681d6cf21da0406f Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Fri, 23 Jan 2026 00:35:59 +0100 Subject: [PATCH 28/34] improve s3 name --- .../com/celements/store/s3/att/S3AttachmentContentStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java index 3b556e6c..af679638 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java @@ -61,8 +61,8 @@ public String buildS3AttachmentKey(XWikiAttachment attachment) { var doc = attachment.getDoc(); var wiki = doc.getDocumentReference().getWikiReference(); return String.join("/", - "attachment", // base path - nodeIdentity.appName(), // allow bucket multi-tenancy by app name + nodeIdentity.clusterName(), // allow bucket multi-tenancy by cluster name + "attachments", // subbucket for attachments wiki.getName(), // identify wiki Long.toString(doc.getId()), // identify document Long.toString(attachment.getId())); // identify attachment From 4414d3f58e1b7be07688abfb2da3fb97135b3115 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Fri, 23 Jan 2026 00:43:16 +0100 Subject: [PATCH 29/34] S3AttachmentContentMigrationService --- .../S3AttachmentContentMigrationService.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java new file mode 100644 index 00000000..d6290be9 --- /dev/null +++ b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java @@ -0,0 +1,96 @@ +package com.celements.store.s3.att.migration; + +import static com.celements.logging.LogUtils.*; + +import java.util.List; + +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.WikiReference; + +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.util.ModelUtils; +import com.celements.query.IQueryExecutionServiceRole; +import com.celements.store.s3.att.S3AttachmentContentStore; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.store.AttachmentContentStore.AttachmentContentStoreException; + +import one.util.streamex.StreamEx; + +@Service +public class S3AttachmentContentMigrationService { + + static final Logger LOGGER = LoggerFactory.getLogger(S3AttachmentContentMigrationService.class); + + private final IQueryExecutionServiceRole queryExecutor; + private final S3AttachmentContentStore s3AttStore; + private final IModelAccessFacade modelAccess; + private final ModelUtils modelUtils; + + @Inject + public S3AttachmentContentMigrationService( + IQueryExecutionServiceRole queryExecutor, + S3AttachmentContentStore s3AttStore, + IModelAccessFacade modelAccess, + ModelUtils modelUtils) { + this.queryExecutor = queryExecutor; + this.s3AttStore = s3AttStore; + this.modelAccess = modelAccess; + this.modelUtils = modelUtils; + } + + private static String getSqlAttachmentsWithContent() { + return "SELECT d.XWD_FULLNAME " + + "FROM xwikidoc d " + + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " + + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; + } + + public void migrate(WikiReference wiki) throws XWikiException { + var result = queryExecutor.executeReadSql(getSqlAttachmentsWithContent()); + LOGGER.info("[{}] migrating {} attachments to S3 store", wiki.getName(), result.size()); + for (List row : result) { + var docRef = modelUtils.resolveRef(row.get(0), DocumentReference.class, wiki); + try { + migrate(docRef); + } catch (Exception exc) { + LOGGER.warn("[{}] failed migrating document: {}", wiki.getName(), docRef, exc); + } + } + } + + public void migrate(DocumentReference docRef) { + for (var att : modelAccess.getOrCreateDocument(docRef).getAttachmentList()) { + try { + migrate(att); + } catch (XWikiException | AttachmentContentStoreException exc) { + LOGGER.warn("[{}] failed migrating: {}", + att.getDoc().getDocumentReference().getWikiReference().getName(), att, exc); + } + } + } + + public void migrate(XWikiAttachment att) + throws XWikiException, AttachmentContentStoreException { + var archive = att.loadArchive(); + var versions = StreamEx.of(archive.getVersions()).append(att.getRCSVersion()); + for (var v : versions.distinct()) { + pushToS3(archive.getRevision(v)); + } + } + + private void pushToS3(XWikiAttachment att) + throws XWikiException, AttachmentContentStoreException { + if (!s3AttStore.hasContent(att)) { + LOGGER.debug("[{}] pushToS3 {}", + defer(() -> att.getDoc().getDocumentReference().getWikiReference().getName()), + defer(() -> s3AttStore.buildS3AttachmentVersionKey(att))); + s3AttStore.saveContent(att.loadContent()); + } + } +} From 293557b80b3a590b2402fbbbf5ccbafe63e3b023 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Fri, 23 Jan 2026 21:47:23 +0100 Subject: [PATCH 30/34] S3AttachmentContentMigrationService improve logging --- .../S3AttachmentContentMigrationService.java | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java index d6290be9..b2b4f25f 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java @@ -8,8 +8,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.WikiReference; import com.celements.model.access.IModelAccessFacade; @@ -23,6 +25,7 @@ import one.util.streamex.StreamEx; @Service +@Lazy public class S3AttachmentContentMigrationService { static final Logger LOGGER = LoggerFactory.getLogger(S3AttachmentContentMigrationService.class); @@ -45,52 +48,74 @@ public S3AttachmentContentMigrationService( } private static String getSqlAttachmentsWithContent() { - return "SELECT d.XWD_FULLNAME " + return "SELECT DISTINCT d.XWD_FULLNAME, a.XWA_FILENAME " + "FROM xwikidoc d " + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; } - public void migrate(WikiReference wiki) throws XWikiException { + public void migrate(WikiReference wiki) throws XWikiException, AttachmentContentStoreException { var result = queryExecutor.executeReadSql(getSqlAttachmentsWithContent()); LOGGER.info("[{}] migrating {} attachments to S3 store", wiki.getName(), result.size()); + var count = 0; + var countContents = 0; + var processed = 0; for (List row : result) { var docRef = modelUtils.resolveRef(row.get(0), DocumentReference.class, wiki); - try { - migrate(docRef); - } catch (Exception exc) { - LOGGER.warn("[{}] failed migrating document: {}", wiki.getName(), docRef, exc); + var fileName = row.get(1); + var att = modelAccess.getOrCreateDocument(docRef).getAttachment(fileName); + if (att != null) { + try { + countContents += migrate(att); + count++; + } catch (XWikiException exc) { + LOGGER.warn("[{}] failed migrating attachment: {}", + wiki.getName(), serialize(docRef) + "@" + fileName, exc); + } } - } - } - - public void migrate(DocumentReference docRef) { - for (var att : modelAccess.getOrCreateDocument(docRef).getAttachmentList()) { - try { - migrate(att); - } catch (XWikiException | AttachmentContentStoreException exc) { - LOGGER.warn("[{}] failed migrating: {}", - att.getDoc().getDocumentReference().getWikiReference().getName(), att, exc); + processed++; + if ((processed % 100) == 0) { + LOGGER.debug("[{}] processed {}/{} attachments", wiki.getName(), processed, result.size()); } } + LOGGER.info("[{}] migrated {} attachments with {} contents to S3 store", + wiki.getName(), count, countContents); } - public void migrate(XWikiAttachment att) - throws XWikiException, AttachmentContentStoreException { + public int migrate(XWikiAttachment att) throws XWikiException, AttachmentContentStoreException { var archive = att.loadArchive(); var versions = StreamEx.of(archive.getVersions()).append(att.getRCSVersion()); + var count = 0; for (var v : versions.distinct()) { - pushToS3(archive.getRevision(v)); + try { + if (pushToS3(archive.getRevision(v))) { + count++; + } + } catch (XWikiException exc) { + throw new XWikiException(0, 0, "Failed migrating " + + serialize(att.getAttachmentReference()) + "@" + v, exc); + } } + LOGGER.trace("[{}] migrated {} with {} contents", + defer(() -> att.getWikiReference().getName()), + defer(() -> serialize(att.getAttachmentReference())), + count); + return count; } - private void pushToS3(XWikiAttachment att) + private boolean pushToS3(XWikiAttachment att) throws XWikiException, AttachmentContentStoreException { if (!s3AttStore.hasContent(att)) { - LOGGER.debug("[{}] pushToS3 {}", - defer(() -> att.getDoc().getDocumentReference().getWikiReference().getName()), + LOGGER.trace("[{}] pushToS3 {}", + defer(() -> att.getWikiReference().getName()), defer(() -> s3AttStore.buildS3AttachmentVersionKey(att))); s3AttStore.saveContent(att.loadContent()); + return true; } + return false; + } + + private String serialize(EntityReference ref) { + return modelUtils.serializeRefLocal(ref); } } From ef69d60e45209b0a0f976297429dd98effc38eab Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Mon, 2 Feb 2026 22:30:43 +0100 Subject: [PATCH 31/34] rebuildArchive --- .../S3AttachmentContentMigrationService.java | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java index b2b4f25f..01b6b874 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java @@ -14,13 +14,15 @@ import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.WikiReference; +import com.celements.init.XWikiProvider; import com.celements.model.access.IModelAccessFacade; import com.celements.model.util.ModelUtils; -import com.celements.query.IQueryExecutionServiceRole; +import com.celements.query.QueryExecutionService; import com.celements.store.s3.att.S3AttachmentContentStore; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.store.AttachmentContentStore.AttachmentContentStoreException; +import com.xpn.xwiki.store.AttachmentVersioningStore; import one.util.streamex.StreamEx; @@ -30,32 +32,34 @@ public class S3AttachmentContentMigrationService { static final Logger LOGGER = LoggerFactory.getLogger(S3AttachmentContentMigrationService.class); - private final IQueryExecutionServiceRole queryExecutor; + private final QueryExecutionService queryExecutor; private final S3AttachmentContentStore s3AttStore; private final IModelAccessFacade modelAccess; private final ModelUtils modelUtils; + private final XWikiProvider xwikiProvider; @Inject public S3AttachmentContentMigrationService( - IQueryExecutionServiceRole queryExecutor, + QueryExecutionService queryExecutor, S3AttachmentContentStore s3AttStore, IModelAccessFacade modelAccess, - ModelUtils modelUtils) { + ModelUtils modelUtils, + XWikiProvider xwikiProvider) { this.queryExecutor = queryExecutor; this.s3AttStore = s3AttStore; this.modelAccess = modelAccess; this.modelUtils = modelUtils; + this.xwikiProvider = xwikiProvider; } - private static String getSqlAttachmentsWithContent() { - return "SELECT DISTINCT d.XWD_FULLNAME, a.XWA_FILENAME " - + "FROM xwikidoc d " - + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " - + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; + public void migrate(WikiReference wiki) throws XWikiException, AttachmentContentStoreException { + migrateArchive(wiki); + // migrateRecycleBin(wiki); // TODO implement xwikiattrecyclebin migration } - public void migrate(WikiReference wiki) throws XWikiException, AttachmentContentStoreException { - var result = queryExecutor.executeReadSql(getSqlAttachmentsWithContent()); + public void migrateArchive(WikiReference wiki) + throws XWikiException, AttachmentContentStoreException { + var result = queryExecutor.executeReadSql(wiki, String.class, getSqlAttachmentsWithContent()); LOGGER.info("[{}] migrating {} attachments to S3 store", wiki.getName(), result.size()); var count = 0; var countContents = 0; @@ -82,11 +86,28 @@ public void migrate(WikiReference wiki) throws XWikiException, AttachmentContent wiki.getName(), count, countContents); } + private static String getSqlAttachmentsWithContent() { + return "SELECT DISTINCT d.XWD_FULLNAME, a.XWA_FILENAME " + + "FROM xwikidoc d " + + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " + + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; + } + public int migrate(XWikiAttachment att) throws XWikiException, AttachmentContentStoreException { - var archive = att.loadArchive(); - var versions = StreamEx.of(archive.getVersions()).append(att.getRCSVersion()); + var count = migrateArchive(att); + if (count > 0) { // content moved to s3, let's rebuild the archive without content blobs + var archive = att.loadArchive(); + archive.rebuildArchive(false); + getAttachmentVersioningStore().saveArchive(archive, false); + } + return count; + } + + public int migrateArchive(XWikiAttachment att) + throws XWikiException, AttachmentContentStoreException { var count = 0; - for (var v : versions.distinct()) { + var archive = att.loadArchive(); + for (var v : StreamEx.of(archive.getVersions()).append(att.getRCSVersion()).distinct()) { try { if (pushToS3(archive.getRevision(v))) { count++; @@ -118,4 +139,10 @@ private boolean pushToS3(XWikiAttachment att) private String serialize(EntityReference ref) { return modelUtils.serializeRefLocal(ref); } + + private AttachmentVersioningStore getAttachmentVersioningStore() { + return xwikiProvider.get().orElseThrow(IllegalStateException::new) + .getAttachmentStore() + .getVersioningStore(); + } } From eca6a614e1a688c3007fbccdeb565df8e10733fc Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Wed, 4 Feb 2026 19:36:05 +0100 Subject: [PATCH 32/34] saveArchive with transaction --- .../com/celements/store/s3/att/S3AttachmentContentStore.java | 5 +++-- .../att/migration/S3AttachmentContentMigrationService.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java index af679638..14fec7bc 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java @@ -80,7 +80,7 @@ public String buildS3AttachmentVersionKey(XWikiAttachment attachment) { public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentStoreException { var s3Key = buildS3AttachmentVersionKey(attachment); - LOGGER.info("hasContent - {} in {}", attachment, s3Key); + LOGGER.debug("hasContent - {} in {}", attachment, s3Key); try { s3Client.headObject(builder -> builder .bucket(s3BucketFilebase) @@ -118,14 +118,15 @@ public void saveContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void loadContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { var s3Key = buildS3AttachmentVersionKey(content.getAttachment()); - LOGGER.info("loadContent - {} from {}", content.getAttachment(), s3Key); try { try (var data = s3Client.getObject(builder -> builder .bucket(s3BucketFilebase) .key(s3Key))) { content.setContent(data); } + LOGGER.debug("loadContent - {} from {}", content.getAttachment(), s3Key); } catch (NoSuchKeyException e) { + LOGGER.info("loadContent - {} not found in {}", content.getAttachment(), s3Key); throw new AttachmentContentStoreException("Attachment content not found in S3: " + s3Key, e); } catch (S3Exception e) { throw new AttachmentContentStoreException(buildS3ErrorMessage(s3Key, e), e); diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java index 01b6b874..ccce8b7b 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java @@ -98,7 +98,7 @@ public int migrate(XWikiAttachment att) throws XWikiException, AttachmentContent if (count > 0) { // content moved to s3, let's rebuild the archive without content blobs var archive = att.loadArchive(); archive.rebuildArchive(false); - getAttachmentVersioningStore().saveArchive(archive, false); + getAttachmentVersioningStore().saveArchive(archive, true); } return count; } From a24e02b929051bbc81a657c3199d9f0f854c7fd5 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Wed, 4 Feb 2026 22:56:21 +0100 Subject: [PATCH 33/34] cleanup improvements --- .../S3AttachmentContentMigrationService.java | 99 +++++++++++-------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java index ccce8b7b..44677cda 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java @@ -23,6 +23,7 @@ import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.store.AttachmentContentStore.AttachmentContentStoreException; import com.xpn.xwiki.store.AttachmentVersioningStore; +import com.xpn.xwiki.store.hibernate.HibernateAttachmentContentStore; import one.util.streamex.StreamEx; @@ -33,7 +34,8 @@ public class S3AttachmentContentMigrationService { static final Logger LOGGER = LoggerFactory.getLogger(S3AttachmentContentMigrationService.class); private final QueryExecutionService queryExecutor; - private final S3AttachmentContentStore s3AttStore; + private final S3AttachmentContentStore s3AttContentStore; + private final HibernateAttachmentContentStore hibAttContentStore; private final IModelAccessFacade modelAccess; private final ModelUtils modelUtils; private final XWikiProvider xwikiProvider; @@ -41,69 +43,67 @@ public class S3AttachmentContentMigrationService { @Inject public S3AttachmentContentMigrationService( QueryExecutionService queryExecutor, - S3AttachmentContentStore s3AttStore, + S3AttachmentContentStore s3AttContentStore, + HibernateAttachmentContentStore hibAttContentStore, IModelAccessFacade modelAccess, ModelUtils modelUtils, XWikiProvider xwikiProvider) { this.queryExecutor = queryExecutor; - this.s3AttStore = s3AttStore; + this.s3AttContentStore = s3AttContentStore; + this.hibAttContentStore = hibAttContentStore; this.modelAccess = modelAccess; this.modelUtils = modelUtils; this.xwikiProvider = xwikiProvider; } - public void migrate(WikiReference wiki) throws XWikiException, AttachmentContentStoreException { - migrateArchive(wiki); + public void migrate(WikiReference wiki, boolean cleanup) + throws XWikiException, AttachmentContentStoreException { + migrateArchive(wiki, cleanup); // migrateRecycleBin(wiki); // TODO implement xwikiattrecyclebin migration } - public void migrateArchive(WikiReference wiki) + private static final String SQL_ATTACHMENTS_WITH_CONTENT = "" + + "SELECT DISTINCT d.XWD_FULLNAME, a.XWA_FILENAME " + + "FROM xwikidoc d " + + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " + + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; + + public void migrateArchive(WikiReference wiki, boolean cleanup) throws XWikiException, AttachmentContentStoreException { - var result = queryExecutor.executeReadSql(wiki, String.class, getSqlAttachmentsWithContent()); + var result = queryExecutor.executeReadSql(wiki, String.class, SQL_ATTACHMENTS_WITH_CONTENT); LOGGER.info("[{}] migrating {} attachments to S3 store", wiki.getName(), result.size()); - var count = 0; - var countContents = 0; - var processed = 0; + var countPushed = 0; + var countProcessed = 0; + var countCleaned = 0; + var countError = 0; for (List row : result) { var docRef = modelUtils.resolveRef(row.get(0), DocumentReference.class, wiki); var fileName = row.get(1); var att = modelAccess.getOrCreateDocument(docRef).getAttachment(fileName); if (att != null) { try { - countContents += migrate(att); - count++; + countPushed += migrate(att); + if (cleanup) { + cleanup(att); // content moved to s3, cleanup content in db + countCleaned++; + } } catch (XWikiException exc) { - LOGGER.warn("[{}] failed migrating attachment: {}", - wiki.getName(), serialize(docRef) + "@" + fileName, exc); + countError++; + LOGGER.error("[{}] failed migrating {}", wiki.getName(), + serialize(att.getAttachmentReference()), exc); } } - processed++; - if ((processed % 100) == 0) { - LOGGER.debug("[{}] processed {}/{} attachments", wiki.getName(), processed, result.size()); + countProcessed++; + if ((countProcessed % 100) == 0) { + LOGGER.info("[{}] processed {}/{} attachments", + wiki.getName(), countProcessed, result.size()); } } - LOGGER.info("[{}] migrated {} attachments with {} contents to S3 store", - wiki.getName(), count, countContents); + LOGGER.info("[{}] migration finished: {} processed, {} failed, {} cleaned, {} pushed contents", + wiki.getName(), countProcessed, countError, countPushed, countCleaned); } - private static String getSqlAttachmentsWithContent() { - return "SELECT DISTINCT d.XWD_FULLNAME, a.XWA_FILENAME " - + "FROM xwikidoc d " - + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " - + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; - } - - public int migrate(XWikiAttachment att) throws XWikiException, AttachmentContentStoreException { - var count = migrateArchive(att); - if (count > 0) { // content moved to s3, let's rebuild the archive without content blobs - var archive = att.loadArchive(); - archive.rebuildArchive(false); - getAttachmentVersioningStore().saveArchive(archive, true); - } - return count; - } - - public int migrateArchive(XWikiAttachment att) + public int migrate(XWikiAttachment att) throws XWikiException, AttachmentContentStoreException { var count = 0; var archive = att.loadArchive(); @@ -126,16 +126,35 @@ public int migrateArchive(XWikiAttachment att) private boolean pushToS3(XWikiAttachment att) throws XWikiException, AttachmentContentStoreException { - if (!s3AttStore.hasContent(att)) { + if (!s3AttContentStore.hasContent(att)) { LOGGER.trace("[{}] pushToS3 {}", defer(() -> att.getWikiReference().getName()), - defer(() -> s3AttStore.buildS3AttachmentVersionKey(att))); - s3AttStore.saveContent(att.loadContent()); + defer(() -> s3AttContentStore.buildS3AttachmentVersionKey(att))); + s3AttContentStore.saveContent(att.loadContent()); return true; } return false; } + // let's rebuild the archive without content blobs and delete the content + public void cleanup(XWikiAttachment att) throws XWikiException { + LOGGER.trace("[{}] cleanup {}", + defer(() -> att.getWikiReference().getName()), + defer(() -> serialize(att.getAttachmentReference()))); + hibAttContentStore.executeWrite(att.getWikiReference(), true, session -> { + try { + var archive = att.loadArchive(); + archive.rebuildArchive(false); + getAttachmentVersioningStore().saveArchive(archive, false); + hibAttContentStore.deleteContent(att); + } catch (AttachmentContentStoreException e) { + throw new XWikiException(0, 0, "Failed deleting content from fallback store for " + + serialize(att.getAttachmentReference()), e); + } + return null; + }); + } + private String serialize(EntityReference ref) { return modelUtils.serializeRefLocal(ref); } From 31a5e3e4c4ff8c769120f62c209f51cdb5351f23 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Thu, 5 Feb 2026 01:00:00 +0100 Subject: [PATCH 34/34] avoid multiple doc loads --- .../store/s3/att/S3AttachmentContentStore.java | 12 ++++++------ .../S3AttachmentContentMigrationService.java | 13 +++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java index 14fec7bc..14acd387 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/S3AttachmentContentStore.java @@ -80,7 +80,7 @@ public String buildS3AttachmentVersionKey(XWikiAttachment attachment) { public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentStoreException { var s3Key = buildS3AttachmentVersionKey(attachment); - LOGGER.debug("hasContent - {} in {}", attachment, s3Key); + LOGGER.debug("hasContent - {} : {}", s3Key, attachment); try { s3Client.headObject(builder -> builder .bucket(s3BucketFilebase) @@ -98,7 +98,7 @@ public boolean hasContent(XWikiAttachment attachment) throws AttachmentContentSt @Override public void saveContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { var s3Key = buildS3AttachmentVersionKey(content.getAttachment()); - LOGGER.info("saveContent - {} to {}", content.getAttachment(), s3Key); + LOGGER.info("saveContent - {} : {}", s3Key, content.getAttachment()); try { try (var data = content.getContentInputStream()) { s3Client.putObject(builder -> builder @@ -124,9 +124,9 @@ public void loadContent(XWikiAttachmentContent content) throws AttachmentContent .key(s3Key))) { content.setContent(data); } - LOGGER.debug("loadContent - {} from {}", content.getAttachment(), s3Key); + LOGGER.debug("loadContent - {} : {}", s3Key, content.getAttachment()); } catch (NoSuchKeyException e) { - LOGGER.info("loadContent - {} not found in {}", content.getAttachment(), s3Key); + LOGGER.debug("loadContent - {} not found : {}", s3Key, content.getAttachment()); throw new AttachmentContentStoreException("Attachment content not found in S3: " + s3Key, e); } catch (S3Exception e) { throw new AttachmentContentStoreException(buildS3ErrorMessage(s3Key, e), e); @@ -138,7 +138,7 @@ public void loadContent(XWikiAttachmentContent content) throws AttachmentContent @Override public void deleteContent(XWikiAttachment attachment) throws AttachmentContentStoreException { var s3Prefix = buildS3AttachmentKey(attachment) + "/"; - LOGGER.info("deleteContent - {} from {}", attachment, s3Prefix); + LOGGER.info("deleteContent - {} : {}", s3Prefix, attachment); List batch = s3Client.listObjectsV2(builder -> builder .bucket(s3BucketFilebase) .prefix(s3Prefix)) @@ -160,7 +160,7 @@ public void deleteContent(XWikiAttachment attachment) throws AttachmentContentSt @Override public void deleteContent(XWikiAttachmentContent content) throws AttachmentContentStoreException { var s3Key = buildS3AttachmentVersionKey(content.getAttachment()); - LOGGER.info("deleteContent - {} from {}", content.getAttachment(), s3Key); + LOGGER.info("deleteContent - {} : {}", s3Key, content.getAttachment()); try { s3Client.deleteObject(builder -> builder .bucket(s3BucketFilebase) diff --git a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java index 44677cda..9a966f9e 100644 --- a/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java +++ b/celements-s3/src/main/java/com/celements/store/s3/att/migration/S3AttachmentContentMigrationService.java @@ -21,6 +21,7 @@ import com.celements.store.s3.att.S3AttachmentContentStore; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.store.AttachmentContentStore.AttachmentContentStoreException; import com.xpn.xwiki.store.AttachmentVersioningStore; import com.xpn.xwiki.store.hibernate.HibernateAttachmentContentStore; @@ -66,7 +67,8 @@ public void migrate(WikiReference wiki, boolean cleanup) + "SELECT DISTINCT d.XWD_FULLNAME, a.XWA_FILENAME " + "FROM xwikidoc d " + "JOIN xwikiattachment a ON d.XWD_ID = a.XWA_DOC_ID " - + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID"; + + "JOIN xwikiattachment_content c ON a.XWA_ID = c.XWA_ID " + + "ORDER BY d.XWD_FULLNAME, a.XWA_FILENAME"; public void migrateArchive(WikiReference wiki, boolean cleanup) throws XWikiException, AttachmentContentStoreException { @@ -76,10 +78,13 @@ public void migrateArchive(WikiReference wiki, boolean cleanup) var countProcessed = 0; var countCleaned = 0; var countError = 0; + XWikiDocument doc = null; for (List row : result) { var docRef = modelUtils.resolveRef(row.get(0), DocumentReference.class, wiki); - var fileName = row.get(1); - var att = modelAccess.getOrCreateDocument(docRef).getAttachment(fileName); + if ((doc == null) || !doc.getDocumentReference().equals(docRef)) { + doc = modelAccess.getOrCreateDocument(docRef); + } + var att = doc.getAttachment(row.get(1)); if (att != null) { try { countPushed += migrate(att); @@ -100,7 +105,7 @@ public void migrateArchive(WikiReference wiki, boolean cleanup) } } LOGGER.info("[{}] migration finished: {} processed, {} failed, {} cleaned, {} pushed contents", - wiki.getName(), countProcessed, countError, countPushed, countCleaned); + wiki.getName(), countProcessed, countError, countCleaned, countPushed); } public int migrate(XWikiAttachment att)