GRANITE-26607 - Add package extraction logic
diff --git a/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/ContentPackageExtractor.java b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/ContentPackageExtractor.java
new file mode 100644
index 0000000..571fae5
--- /dev/null
+++ b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/ContentPackageExtractor.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.distribution.journal.impl.subscriber;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.packaging.JcrPackage;
+import org.apache.jackrabbit.vault.packaging.JcrPackageManager;
+import org.apache.jackrabbit.vault.packaging.Packaging;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Hook that can be added to a DistributionPackageBuilder.
+ * Each distribution package is inspected for possible content packages in /etc/packages.
+ * Such content packages are installed via the Packaging service.
+ */
+public class ContentPackageExtractor {
+ private static final String PACKAGE_BASE_PATH = "/etc/packages/";
+
+ private Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private Packaging packageService;
+ private PackageHandling packageHandling;
+
+ public ContentPackageExtractor(Packaging packageService, PackageHandling packageHandling) {
+ this.packageService = packageService;
+ this.packageHandling = packageHandling;
+ }
+
+ public void handle(ResourceResolver resourceResolver, List<String> paths) {
+ requireNonNull(resourceResolver, "Must provide resourceResolver");
+ if (packageHandling == PackageHandling.Off) {
+ return;
+ }
+ log.info("Scanning imported nodes for packages to install.");
+ for (String path : paths) {
+ try {
+ Resource resource = resourceResolver.getResource(path);
+ if (resource != null) {
+ Node node = resource.adaptTo(Node.class);
+ if (isContentPackage(path, node)) {
+ installPackage(path, node);
+ }
+ }
+ } catch (RepositoryException e) {
+ log.warn("Error trying check if {} contains a content package to extract.", path, e);
+ }
+ }
+ }
+
+ private boolean isContentPackage(String path, Node node) throws RepositoryException {
+ return path.startsWith(PACKAGE_BASE_PATH) && node.isNodeType(NodeType.NT_FILE);
+ }
+
+ private void installPackage(String path, Node node) {
+ try {
+ log.info("Content package received at {}. Starting import.\n", path);
+ Session session = node.getSession();
+ JcrPackageManager packMgr = packageService.getPackageManager(session);
+ JcrPackage pack = packMgr.open(node);
+ ImportOptions opts = new ImportOptions();
+ if (packageHandling == PackageHandling.Extract) {
+ pack.extract(opts);
+ } else {
+ pack.install(opts);
+ }
+ } catch (Exception e) {
+ log.warn("Error trying to extracting content package on path {}.", path, e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/DistributionSubscriber.java b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/DistributionSubscriber.java
index aad12aa..3a63f20 100644
--- a/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/DistributionSubscriber.java
+++ b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/DistributionSubscriber.java
@@ -68,6 +68,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.commons.jackrabbit.SimpleReferenceBinary;
+import org.apache.jackrabbit.vault.packaging.Packaging;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
@@ -164,6 +165,9 @@
@Reference
private ServiceUserMapped mappedUser;
+ @Reference
+ private Packaging packaging;
+
private ServiceRegistration<DistributionAgent> componentReg;
private final PackageRetries packageRetries = new PackageRetries();
@@ -206,6 +210,8 @@
private volatile Thread queueProcessor;
+ private ContentPackageExtractor extractor;
+
@Activate
public void activate(SubscriberConfiguration config, BundleContext context, Map<String, Object> properties) {
@@ -312,6 +318,7 @@
LOG.info(msg);
Dictionary<String, Object> props = createServiceProps(config);
componentReg = context.registerService(DistributionAgent.class, this, props);
+ extractor = new ContentPackageExtractor(packaging, config.packageHandling());
}
private Set<String> getNotEmpty(String[] agentNames) {
@@ -639,6 +646,7 @@
try {
pkgStream = pkgStream(resolver, pkgMsg);
packageBuilder.installPackage(resolver, pkgStream);
+ extractor.handle(resolver, pkgMsg.getPathsList());
} finally {
IOUtils.closeQuietly(pkgStream);
}
diff --git a/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/PackageHandling.java b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/PackageHandling.java
new file mode 100644
index 0000000..0be7ca8
--- /dev/null
+++ b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/PackageHandling.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.distribution.journal.impl.subscriber;
+
+public enum PackageHandling {
+ Off, Extract, Install;
+}
diff --git a/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/SubscriberConfiguration.java b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/SubscriberConfiguration.java
index c52ba3f..41c9e58 100644
--- a/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/SubscriberConfiguration.java
+++ b/src/main/java/org/apache/sling/distribution/journal/impl/subscriber/SubscriberConfiguration.java
@@ -49,4 +49,7 @@
@AttributeDefinition(name = "maxRetries", description = "The max number of attempts to import a package before moving the package to an error queue. If set to a negative value, the number of attempts is infinite. Default is -1 (infinite attempts).")
int maxRetries() default -1;
+ @AttributeDefinition(name = "packageHandling", description = "Defines if content packages in /etc/packages should be processed (Extract, Install, Off).")
+ PackageHandling packageHandling() default PackageHandling.Off;
+
}