You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2025/05/09 08:25:11 UTC
(sling-org-apache-sling-feature-extension-content) branch master updated: SLING-12774 - Content feature launcher extension should reinstall SNAPSHOT content packages (#25)
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-extension-content.git
The following commit(s) were added to refs/heads/master by this push:
new f55bec7 SLING-12774 - Content feature launcher extension should reinstall SNAPSHOT content packages (#25)
f55bec7 is described below
commit f55bec716a2c9c39bf66f85da20cd239a12d9eff
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Fri May 9 10:25:06 2025 +0200
SLING-12774 - Content feature launcher extension should reinstall SNAPSHOT content packages (#25)
Enable reinstallation of snapshots only when the org.apache.sling.feature.extension.content.useStrictMode
system property is set to true.
---
README.md | 6 +-
.../feature/extension/content/ContentHandler.java | 61 +++++--
.../extension/content/ContentHandlerTest.java | 176 +++++++++++++++++----
3 files changed, 202 insertions(+), 41 deletions(-)
diff --git a/README.md b/README.md
index 8fea7fc..6a98008 100644
--- a/README.md
+++ b/README.md
@@ -20,4 +20,8 @@ The default start order is the maximum integer value (i.e. the package is proces
All content-packages from the model are registered (externally) in a [`FSPackageRegistry`](https://jackrabbit.apache.org/filevault/apidocs/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistry.html) and scheduled for installation with execution plans (given via OSGi configuration) which automatically get executed during repository start by [ExecutionPlanRepoInitializer](https://github.com/apache/sling-org-apache-sling-jcr-packageinit/blob/master/src/main/java/org/apache/s [...]
-Strict package mode installation can be enabled for all packages by setting the `org.apache.sling.feature.extension.content.useStrictMode` system property to true.
\ No newline at end of file
+Strict package mode installation can be enabled for all packages by setting the `org.apache.sling.feature.extension.content.useStrictMode` system property to true.
+
+## Reinstallation of SNAPSHOT content packages
+
+By default SNAPSHOT content packages are not reprocessed. If reinstallation is desired the `org.apache.sling.feature.extension.content.reinstallSnapshots` system property must be set to `true`. This requires at runtime a version 1.0.6 or newer of the `org.apache.sling.jcr.packageinit` bundle.
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/feature/extension/content/ContentHandler.java b/src/main/java/org/apache/sling/feature/extension/content/ContentHandler.java
index 3575272..64c6ecd 100644
--- a/src/main/java/org/apache/sling/feature/extension/content/ContentHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/content/ContentHandler.java
@@ -49,21 +49,26 @@ import org.apache.sling.feature.launcher.spi.extensions.ExtensionContext;
import org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler;
public class ContentHandler implements ExtensionHandler {
+ private static final String SYS_PROP_USE_STRICT_MODE = ContentHandler.class.getPackageName()+ ".useStrictMode";
+
+ private static final String SYS_PROP_REINSTALL_SNAPSHOTS = ContentHandler.class.getPackageName()+ ".reinstallSnapshots";
+
public static final String PACKAGEREGISTRY_HOME = "packageregistry.home";
private static final String REPOSITORY_HOME = "repository.home";
private static final String REGISTRY_FOLDER = "packageregistry";
- private static ExecutionPlanBuilder buildExecutionPlan(Collection<Artifact> artifacts, Set<PackageId> satisfiedPackages, LauncherPrepareContext prepareContext, File registryHome, boolean useStrictMode) throws Exception {
+ private static ExecutionPlanBuilderWithDetails buildExecutionPlan(Collection<Artifact> artifacts, Set<PackageId> satisfiedPackages, LauncherPrepareContext prepareContext, File registryHome,
+ boolean useStrictMode, boolean reinstallSnapshots) throws Exception {
- List<File> packageReferences = new ArrayList<>();
+ List<PackageReference> packageReferences = new ArrayList<>();
for (final Artifact a : artifacts) {
final URL file = prepareContext.getArtifactFile(a.getId());
File tmp = IOUtils.getFileFromURL(file, true, null);
if (tmp != null && tmp.length() > 0) {
- packageReferences.add(tmp);
+ packageReferences.add(new PackageReference(tmp, a));
}
}
@@ -75,10 +80,11 @@ public class ContentHandler implements ExtensionHandler {
ExecutionPlanBuilder builder = registry.createExecutionPlan();
builder.with(satisfiedPackages);
-
- for (File pkgFile : packageReferences) {
+ boolean hasAnySnapshot = false;
+ for (PackageReference pkgRef : packageReferences) {
+ hasAnySnapshot |= pkgRef.isSnapshot;
try {
- PackageId pid = registry.registerExternal(pkgFile, false);
+ PackageId pid = registry.registerExternal(pkgRef.file, pkgRef.isSnapshot && reinstallSnapshots);
ImportOptions importOptions = new ImportOptions();
importOptions.setStrict(useStrictMode);
PackageTaskOptions options = new ImportOptionsPackageTaskOption(importOptions);
@@ -89,8 +95,8 @@ public class ContentHandler implements ExtensionHandler {
}
builder.validate();
satisfiedPackages.addAll(builder.preview());
- return builder;
-
+
+ return new ExecutionPlanBuilderWithDetails(builder, hasAnySnapshot);
}
@Override
@@ -99,7 +105,8 @@ public class ContentHandler implements ExtensionHandler {
if (extension.getType() == ExtensionType.ARTIFACTS
&& extension.getName().equals(Extension.EXTENSION_NAME_CONTENT_PACKAGES)) {
- boolean useStrictMode = Boolean.getBoolean(getClass().getPackageName()+ ".useStrictMode");
+ boolean useStrictMode = Boolean.getBoolean(SYS_PROP_USE_STRICT_MODE);
+ boolean reinstallSnapshots = Boolean.getBoolean(SYS_PROP_REINSTALL_SNAPSHOTS);
Map<Integer, Collection<Artifact>> orderedArtifacts = new TreeMap<>();
for (final Artifact a : extension.getArtifacts()) {
@@ -114,16 +121,28 @@ public class ContentHandler implements ExtensionHandler {
}
List<String> executionPlans = new ArrayList<>();
Set<PackageId> satisfiedPackages = new HashSet<>();
+ boolean anyPlanIncludesSnapshots = false;
for (Collection<Artifact> artifacts : orderedArtifacts.values()) {
- ExecutionPlanBuilder builder = buildExecutionPlan(artifacts, satisfiedPackages, context, registryHome, useStrictMode);
+ ExecutionPlanBuilderWithDetails builderWithDetails = buildExecutionPlan(artifacts, satisfiedPackages, context, registryHome, useStrictMode, reinstallSnapshots);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- builder.save(baos);
+ builderWithDetails.builder.save(baos);
executionPlans.add(baos.toString(StandardCharsets.UTF_8));
+ anyPlanIncludesSnapshots |= builderWithDetails.includesSnapshots;
}
+
+ if ( anyPlanIncludesSnapshots ) {
+ if ( reinstallSnapshots ) {
+ context.getLogger().info("Found at least one SNAPSHOT package - configuring ExecutionPlanRepoInitializer to reprocess all plans that contain SNAPSHOT packages.");
+ } else {
+ context.getLogger().info("Found at least one SNAPSHOT package but ExecutionPlanRepoInitializer is not configured by default to reinstall SNAPSHOTS. Set system property {} to true to enable.", SYS_PROP_REINSTALL_SNAPSHOTS);
+ }
+ }
+
// Workaround for too bold relocation mechanism - corresponding details at https://issues.apache.org/jira/browse/MSHADE-156
final Configuration initcfg = new Configuration("org.apache.sling.jcr.packageinit.impl.ExecutionPlanRepoInitializer");
initcfg.getProperties().put("executionplans", executionPlans.toArray(new String[executionPlans.size()]));
initcfg.getProperties().put("statusfilepath", registryHome.getAbsolutePath() + File.separator + "executedplans.file");
+ initcfg.getProperties().put("reinstallSnapshots", anyPlanIncludesSnapshots && reinstallSnapshots);
context.addConfiguration(initcfg.getPid(), null, initcfg.getProperties());
// Workaround for too bold relocation mechanism - corresponding details at https://issues.apache.org/jira/browse/MSHADE-156
final Configuration registrycfg = new Configuration("org.UNSHADE.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry");
@@ -158,4 +177,24 @@ public class ContentHandler implements ExtensionHandler {
}
return registryHome;
}
+
+ static class PackageReference {
+ private File file;
+ private boolean isSnapshot;
+
+ public PackageReference(File file, Artifact artifact) {
+ this.file = file;
+ this.isSnapshot = artifact.getId().getVersion().endsWith("-SNAPSHOT");
+ }
+ }
+
+ static class ExecutionPlanBuilderWithDetails {
+ private ExecutionPlanBuilder builder;
+ private boolean includesSnapshots;
+
+ public ExecutionPlanBuilderWithDetails(ExecutionPlanBuilder builder, boolean includesSnapshots) {
+ this.builder = builder;
+ this.includesSnapshots = includesSnapshots;
+ }
+ }
}
diff --git a/src/test/java/org/apache/sling/feature/extension/content/ContentHandlerTest.java b/src/test/java/org/apache/sling/feature/extension/content/ContentHandlerTest.java
index 589f90d..f3f23ed 100644
--- a/src/test/java/org/apache/sling/feature/extension/content/ContentHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/content/ContentHandlerTest.java
@@ -17,7 +17,6 @@
package org.apache.sling.feature.extension.content;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -28,6 +27,9 @@ import java.net.URL;
import java.util.Dictionary;
import java.util.Iterator;
+import org.apache.jackrabbit.vault.packaging.registry.impl.AbstractPackageRegistry.SecurityConfig;
+import org.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry;
+import org.apache.jackrabbit.vault.packaging.registry.impl.InstallationScope;
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Extension;
@@ -42,6 +44,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import org.slf4j.LoggerFactory;
@RunWith(MockitoJUnitRunner.class)
public class ContentHandlerTest {
@@ -73,43 +76,48 @@ public class ContentHandlerTest {
private static final String COORDINATES_TEST_PACKAGE_C_10 = "my_packages:test_c:1.0";
private static String TEST_PACKAGE_C_10 = "testpackages/test_c-1.0.zip";
private static ArtifactId TEST_PACKAGE_AID_C_10 = ArtifactId.fromMvnId(COORDINATES_TEST_PACKAGE_C_10);
+ private static ArtifactId TEST_PACKAGE_AID_C_10_SNAPSHOT = TEST_PACKAGE_AID_C_10.changeVersion("1.0-SNAPSHOT");
@Before
public void setUp() throws Exception {
- URL test_a = this.getClass().getResource(TEST_PACKAGE_A_10);
- when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_A_10)).thenReturn(test_a);
- URL test_b = this.getClass().getResource(TEST_PACKAGE_B_10);
- when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_B_10)).thenReturn(test_b);
- URL test_c = this.getClass().getResource(TEST_PACKAGE_C_10);
- when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_C_10)).thenReturn(test_c);
+ URL testA = this.getClass().getResource(TEST_PACKAGE_A_10);
+ when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_A_10)).thenReturn(testA);
+ URL testB = this.getClass().getResource(TEST_PACKAGE_B_10);
+ when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_B_10)).thenReturn(testB);
+ URL testC = this.getClass().getResource(TEST_PACKAGE_C_10);
+ when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_C_10)).thenReturn(testC);
+ when(extensionContext.getArtifactFile(TEST_PACKAGE_AID_C_10_SNAPSHOT)).thenReturn(testC);
+
+ when(extensionContext.getLogger()).thenReturn(LoggerFactory.getLogger(getClass().getName()));
+
+ System.setProperty(ContentHandler.PACKAGEREGISTRY_HOME, testFolder.getRoot().toString());
+
+ System.clearProperty(ContentHandler.class.getPackageName()+".reinstallSnapshots");
}
+ /**
+ * Validates that the content handler creates the execution plan correctly
+ *
+ * @throws Exception
+ */
@Test
public void testMultipleStartOrders() throws Exception {
ContentHandler ch = new ContentHandler();
- System.setProperty(ContentHandler.PACKAGEREGISTRY_HOME, testFolder.getRoot().toString());
+
Extension ext = new Extension(ExtensionType.ARTIFACTS, "content-packages", ExtensionState.OPTIONAL);
- Artifact artifact_a = new Artifact(TEST_PACKAGE_AID_A_10);
- Artifact artifact_b = new Artifact(TEST_PACKAGE_AID_B_10);
- Artifact artifact_c = new Artifact(TEST_PACKAGE_AID_C_10);
- artifact_a.getMetadata().put("start-order", "2");
- artifact_b.getMetadata().put("start-order", "1");
- artifact_c.getMetadata().put("start-order", "1");
- ext.getArtifacts().add(artifact_a);
- ext.getArtifacts().add(artifact_b);
- ext.getArtifacts().add(artifact_c);
- @SuppressWarnings("unchecked")
- ArgumentCaptor<Dictionary<String, Object>> executionPlanCaptor = ArgumentCaptor.forClass(Dictionary.class);
+
+ Artifact artifactA = new Artifact(TEST_PACKAGE_AID_A_10);
+ Artifact artifactB = new Artifact(TEST_PACKAGE_AID_B_10);
+ Artifact artifactC = new Artifact(TEST_PACKAGE_AID_C_10);
+ artifactA.getMetadata().put("start-order", "2");
+ artifactB.getMetadata().put("start-order", "1");
+ artifactC.getMetadata().put("start-order", "1");
+ ext.getArtifacts().add(artifactA);
+ ext.getArtifacts().add(artifactB);
+ ext.getArtifacts().add(artifactC);
+
+ final String[] executionplans = execute(ch, ext, false);
- ch.handle(extensionContext, ext);
- verify(extensionContext).addConfiguration(eq("org.apache.sling.jcr.packageinit.impl.ExecutionPlanRepoInitializer"), any(), executionPlanCaptor.capture());
- verify(extensionContext).addConfiguration(eq("org.UNSHADE.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry"), any(), any());
- Iterator<Dictionary<String, Object>> dictIt = executionPlanCaptor.getAllValues().iterator();
- Dictionary<String, Object> dict = dictIt.next();
- final String[] executionplans = (String[]) dict.get("executionplans");
- final String statusFileHome = (String)dict.get("statusfilepath");
- File executedPlansFile = new File(testFolder.getRoot(), "executedplans.file");
- assertEquals(executedPlansFile.getAbsolutePath(), statusFileHome);
final String expected_0 =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<executionPlan version=\"1.0\">\n" +
@@ -146,6 +154,116 @@ public class ContentHandlerTest {
"</executionPlan>\n";
assertEquals(expected_1, executionplans[1]);
- assertFalse(dictIt.hasNext());
+ assertEquals(2, executionplans.length);
+ }
+
+ /**
+ * Validates that a package that is already registered is skipped from the execution plan.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void duplicatePackage() throws Exception {
+
+ FSPackageRegistry registry = new FSPackageRegistry(testFolder.getRoot(), InstallationScope.UNSCOPED, new SecurityConfig(null, null), true);
+ registry.register(getClass().getResourceAsStream(TEST_PACKAGE_C_10), false);
+
+ ContentHandler ch = new ContentHandler();
+ Extension ext = new Extension(ExtensionType.ARTIFACTS, "content-packages", ExtensionState.OPTIONAL);
+ Artifact artifactC = new Artifact(TEST_PACKAGE_AID_C_10);
+ artifactC.getMetadata().put("start-order", "1");
+ ext.getArtifacts().add(artifactC);
+ final String[] executionplans = execute(ch, ext, false);
+
+ final String expected_0 =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<executionPlan version=\"1.0\"/>\n";
+
+ assertEquals(expected_0, executionplans[0]);
+ assertEquals(1, executionplans.length);
+
+ }
+
+ /**
+ * Validates that a SNAPSHOT package that is already registered is included in the execution plan when the corresponding system property is set.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void duplicateSnapshotPackage_reinstall() throws Exception {
+
+ System.setProperty(ContentHandler.class.getPackageName()+".reinstallSnapshots", "true");
+
+ FSPackageRegistry registry = new FSPackageRegistry(testFolder.getRoot(), InstallationScope.UNSCOPED, new SecurityConfig(null, null), true);
+ registry.register(getClass().getResourceAsStream(TEST_PACKAGE_C_10), false);
+
+ ContentHandler ch = new ContentHandler();
+ Extension ext = new Extension(ExtensionType.ARTIFACTS, "content-packages", ExtensionState.OPTIONAL);
+ Artifact artifactC = new Artifact(TEST_PACKAGE_AID_C_10_SNAPSHOT);
+ artifactC.getMetadata().put("start-order", "1");
+ ext.getArtifacts().add(artifactC);
+
+ final String[] executionplans = execute(ch, ext, true);
+
+ final String expected_0 =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<executionPlan version=\"1.0\">\n" +
+ " <task cmd=\"extract\" packageId=\"my_packages:test_c:1.0\">\n" +
+ " <options type=\"ImportOptions\">\n" +
+ " <isStrict>false</isStrict>\n" +
+ " <autoSaveThreshold>-1</autoSaveThreshold>\n" +
+ " <nonRecursive>false</nonRecursive>\n" +
+ " <dryRun>false</dryRun>\n" +
+ " </options>\n" +
+ " </task>\n" +
+ "</executionPlan>\n";
+
+ assertEquals(expected_0, executionplans[0]);
+ assertEquals(1, executionplans.length);
+ }
+
+ /**
+ * Validates that a SNAPSHOT package that is already registered is included in the execution plan when the corresponding system property is not set.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void duplicateSnapshotPackage_skip() throws Exception {
+
+ FSPackageRegistry registry = new FSPackageRegistry(testFolder.getRoot(), InstallationScope.UNSCOPED, new SecurityConfig(null, null), true);
+ registry.register(getClass().getResourceAsStream(TEST_PACKAGE_C_10), false);
+
+ ContentHandler ch = new ContentHandler();
+ Extension ext = new Extension(ExtensionType.ARTIFACTS, "content-packages", ExtensionState.OPTIONAL);
+ Artifact artifactC = new Artifact(TEST_PACKAGE_AID_C_10_SNAPSHOT);
+ artifactC.getMetadata().put("start-order", "1");
+ ext.getArtifacts().add(artifactC);
+
+ final String[] executionplans = execute(ch, ext, false);
+
+ final String expected_0 =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<executionPlan version=\"1.0\"/>\n";
+
+ assertEquals(expected_0, executionplans[0]);
+ assertEquals(1, executionplans.length);
+ }
+
+ private String[] execute(ContentHandler ch, Extension ext, boolean expectedReinstallSnapshots) throws Exception {
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<Dictionary<String, Object>> executionPlanCaptor = ArgumentCaptor.forClass(Dictionary.class);
+
+ ch.handle(extensionContext, ext);
+ verify(extensionContext).addConfiguration(eq("org.apache.sling.jcr.packageinit.impl.ExecutionPlanRepoInitializer"), any(), executionPlanCaptor.capture());
+ verify(extensionContext).addConfiguration(eq("org.UNSHADE.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry"), any(), any());
+ Iterator<Dictionary<String, Object>> dictIt = executionPlanCaptor.getAllValues().iterator();
+ Dictionary<String, Object> dict = dictIt.next();
+ final String[] executionplans = (String[]) dict.get("executionplans");
+ final String statusFileHome = (String)dict.get("statusfilepath");
+ final Boolean reinstallSnapshots = (Boolean)dict.get("reinstallSnapshots");
+ assertEquals("reinstallSnapshots configuration property", expectedReinstallSnapshots, reinstallSnapshots);
+ File executedPlansFile = new File(testFolder.getRoot(), "executedplans.file");
+ assertEquals(executedPlansFile.getAbsolutePath(), statusFileHome);
+ return executionplans;
}
}