You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by da...@apache.org on 2019/03/29 13:25:33 UTC
[sling-org-apache-sling-feature-launcher] branch master updated:
SLING-8334 Get Launcher Extension API ready for 1.0 release
This is an automated email from the ASF dual-hosted git repository.
davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-launcher.git
The following commit(s) were added to refs/heads/master by this push:
new 4103794 SLING-8334 Get Launcher Extension API ready for 1.0 release
new 9eaf372 Merge pull request #8 from bosschaert/SLING-8334
4103794 is described below
commit 4103794287784f51997866715d17466effe9e291
Author: David Bosschaert <bo...@adobe.com>
AuthorDate: Thu Mar 28 15:53:13 2019 +0000
SLING-8334 Get Launcher Extension API ready for 1.0 release
The extensions now get provided with a single ExtensionContext object
that combines the functionality that was previously spread over the
LauncherPrepareContext and ExtensionInstallationContext.
Additionally, a lookup is provided for a Feature Model based on Artifact
ID. This can be used by extension plugins to read feature models
referenced in an extension.
---
.../launcher/impl/ExtensionContextImpl.java | 107 +++++++++++++++++++++
.../feature/launcher/impl/FeatureProcessor.java | 18 ++--
.../sling/feature/launcher/impl/Installation.java | 8 +-
.../apache/sling/feature/launcher/impl/Main.java | 14 ++-
.../extensions/handlers/ContentPackageHandler.java | 7 +-
.../impl/extensions/handlers/RepoInitHandler.java | 7 +-
...tallationContext.java => ExtensionContext.java} | 39 ++++++--
.../launcher/spi/extensions/ExtensionHandler.java | 3 +-
.../launcher/impl/ExtensionContextImplTest.java | 52 ++++++++++
src/test/resources/test-feature.json | 3 +
10 files changed, 223 insertions(+), 35 deletions(-)
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/ExtensionContextImpl.java b/src/main/java/org/apache/sling/feature/launcher/impl/ExtensionContextImpl.java
new file mode 100644
index 0000000..8e543b6
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/ExtensionContextImpl.java
@@ -0,0 +1,107 @@
+/*
+ * 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.feature.launcher.impl;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.io.json.FeatureJSONReader;
+import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
+import org.apache.sling.feature.launcher.spi.extensions.ExtensionContext;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+
+class ExtensionContextImpl implements ExtensionContext {
+ private final Installation installation;
+ private final LauncherPrepareContext prepareContext;
+ private final Map<ArtifactId, Feature> loadedFeatures;
+
+ ExtensionContextImpl(LauncherPrepareContext lpc, Installation inst, Map<ArtifactId, Feature> featureMap) {
+ prepareContext = lpc;
+ installation = inst;
+ loadedFeatures = featureMap;
+ }
+
+ @Override
+ public void addBundle(Integer startLevel, File file) {
+ installation.addBundle(startLevel, file);
+ }
+
+ @Override
+ public void addInstallableArtifact(File file) {
+ installation.addInstallableArtifact(file);
+ }
+
+ @Override
+ public void addConfiguration(String pid, String factoryPid, Dictionary<String, Object> properties) {
+ installation.addConfiguration(pid, factoryPid, properties);
+ }
+
+ @Override
+ public void addFrameworkProperty(String key, String value) {
+ installation.addFrameworkProperty(key, value);
+ }
+
+ @Override
+ public Map<String, String> getFrameworkProperties() {
+ return installation.getFrameworkProperties();
+ }
+
+ @Override
+ public Map<Integer, List<File>> getBundleMap() {
+ return installation.getBundleMap();
+ }
+
+ @Override
+ public List<Object[]> getConfigurations() {
+ return installation.getConfigurations();
+ }
+
+ @Override
+ public List<File> getInstallableArtifacts() {
+ return installation.getInstallableArtifacts();
+ }
+
+ @Override
+ public void addAppJar(File jar) {
+ prepareContext.addAppJar(jar);
+ }
+
+ @Override
+ public File getArtifactFile(ArtifactId artifact) throws IOException {
+ return prepareContext.getArtifactFile(artifact);
+ }
+
+ @Override
+ public Feature getFeature(ArtifactId artifact) throws IOException {
+ Feature f = loadedFeatures.get(artifact);
+ if (f != null)
+ return f;
+
+ File file = getArtifactFile(artifact);
+ if (file == null)
+ return null;
+
+ try (FileReader r = new FileReader(file)) {
+ return FeatureJSONReader.read(r, artifact.toMvnUrl());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java b/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
index e63abfa..3f3ad26 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
@@ -19,8 +19,6 @@ package org.apache.sling.feature.launcher.impl;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -52,11 +50,12 @@ public class FeatureProcessor {
* Read the features and prepare the application
* @param config The current configuration
* @param artifactManager The artifact manager
+ * @param loadedFeatures This map will be populated with features that were loaded as part of this process
* @return The merged feature representing the application
* @throws IOException when an IO exception occurs during application creation
*/
public static Feature createApplication(final LauncherConfig config,
- final ArtifactManager artifactManager) throws IOException
+ final ArtifactManager artifactManager, final Map<ArtifactId, Feature> loadedFeatures) throws IOException
{
final BuilderContext builderContext = new BuilderContext(id -> {
try {
@@ -89,21 +88,20 @@ public class FeatureProcessor {
ServiceLoader.load(PostProcessHandler.class).iterator(), Spliterator.ORDERED), false)
.toArray(PostProcessHandler[]::new));
- List<Feature> features = new ArrayList<>();
-
for (final String initFile : config.getFeatureFiles()) {
Main.LOG().debug("Reading feature file {}", initFile);
final ArtifactHandler featureArtifact = artifactManager.getArtifactHandler(initFile);
try (final FileReader r = new FileReader(featureArtifact.getFile())) {
final Feature f = FeatureJSONReader.read(r, featureArtifact.getUrl());
- features.add(f);
+ loadedFeatures.put(f.getId(), f);
} catch (Exception ex) {
throw new IOException("Error reading feature: " + initFile, ex);
}
}
// TODO make feature id configurable
- final Feature app = FeatureBuilder.assemble(ArtifactId.fromMvnId("group:assembled:1.0.0"), builderContext, features.toArray(new Feature[0]));
+ final Feature app = FeatureBuilder.assemble(ArtifactId.fromMvnId("group:assembled:1.0.0"), builderContext, loadedFeatures.values().toArray(new Feature[0]));
+ loadedFeatures.put(app.getId(), app);
// TODO: this sucks
for (Artifact bundle : app.getBundles()) {
@@ -126,10 +124,12 @@ public class FeatureProcessor {
* @param ctx The launcher prepare context
* @param config The launcher configuration
* @param app The merged feature to launch
+ * @param loadedFeatures The features previously loaded by the launcher, this includes features that
+ * were passed in via file:// URLs from the commandline
* @throws Exception when something goes wrong
*/
public static void prepareLauncher(final LauncherPrepareContext ctx, final LauncherConfig config,
- final Feature app) throws Exception {
+ final Feature app, Map<ArtifactId, Feature> loadedFeatures) throws Exception {
for(final Map.Entry<Integer, List<Artifact>> entry : app.getBundles().getBundlesByStartOrder().entrySet()) {
for(final Artifact a : entry.getValue()) {
final File artifactFile = ctx.getArtifactFile(a.getId());
@@ -156,7 +156,7 @@ public class FeatureProcessor {
extensions: for(final Extension ext : app.getExtensions()) {
for (ExtensionHandler handler : ServiceLoader.load(ExtensionHandler.class, FeatureProcessor.class.getClassLoader()))
{
- if (handler.handle(ext, ctx, config.getInstallation())) {
+ if (handler.handle(new ExtensionContextImpl(ctx, config.getInstallation(), loadedFeatures), ext)) {
continue extensions;
}
}
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java b/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java
index dbc17a6..40c8851 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java
@@ -16,6 +16,8 @@
*/
package org.apache.sling.feature.launcher.impl;
+import org.apache.sling.feature.launcher.spi.LauncherRunContext;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Dictionary;
@@ -23,13 +25,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.apache.sling.feature.launcher.spi.LauncherRunContext;
-import org.apache.sling.feature.launcher.spi.extensions.ExtensionInstallationContext;
-
/**
* This class holds the configuration of the launcher.
*/
-public class Installation implements LauncherRunContext, ExtensionInstallationContext {
+public class Installation implements LauncherRunContext {
/** The map with the framework properties. */
private final Map<String, String> fwkProperties = new HashMap<>();
@@ -102,7 +101,6 @@ public class Installation implements LauncherRunContext, ExtensionInstallationCo
return this.fwkProperties;
}
- @Override
public void addFrameworkProperty(String key, String value)
{
this.fwkProperties.put(key, value);
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/Main.java b/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
index aff2330..aad8b63 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
@@ -23,7 +23,9 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
@@ -224,7 +226,8 @@ public class Main {
try {
boolean restart = launcherConfig.getFeatureFiles().length == 0;
- final Feature app = assemble(launcherConfig, artifactManager);
+ Map<ArtifactId, Feature> loadedFeatures = new HashMap<>();
+ final Feature app = assemble(launcherConfig, artifactManager, loadedFeatures);
Main.LOG().info("");
Main.LOG().info("Assembling launcher...");
@@ -247,7 +250,7 @@ public class Main {
launcher.prepare(ctx, getFelixFrameworkId(m_frameworkVersion), app);
- FeatureProcessor.prepareLauncher(ctx, launcherConfig, app);
+ FeatureProcessor.prepareLauncher(ctx, launcherConfig, app, loadedFeatures);
Main.LOG().info("Using {} local artifacts, {} cached artifacts, and {} downloaded artifacts",
launcherConfig.getLocalArtifacts(), launcherConfig.getCachedArtifacts(), launcherConfig.getDownloadedArtifacts());
@@ -275,7 +278,8 @@ public class Main {
}
}
- private static Feature assemble(final LauncherConfig launcherConfig, final ArtifactManager artifactManager) throws IOException
+ private static Feature assemble(final LauncherConfig launcherConfig, final ArtifactManager artifactManager,
+ Map<ArtifactId, Feature> loadedFeatures) throws IOException
{
if (launcherConfig.getFeatureFiles().length == 0) {
File application = getApplicationFeatureFile(launcherConfig);
@@ -285,11 +289,11 @@ public class Main {
else {
throw new IllegalStateException("No feature(s) to launch found and none where specified");
}
- return FeatureProcessor.createApplication(launcherConfig, artifactManager);
+ return FeatureProcessor.createApplication(launcherConfig, artifactManager, loadedFeatures);
}
else
{
- final Feature app = FeatureProcessor.createApplication(launcherConfig, artifactManager);
+ final Feature app = FeatureProcessor.createApplication(launcherConfig, artifactManager, loadedFeatures);
// write application back
final File file = getApplicationFeatureFile(launcherConfig);
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/ContentPackageHandler.java b/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/ContentPackageHandler.java
index dfc0db4..a08061c 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/ContentPackageHandler.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/ContentPackageHandler.java
@@ -21,19 +21,18 @@ import java.io.IOException;
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.Extension;
import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
+import org.apache.sling.feature.launcher.spi.extensions.ExtensionContext;
import org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler;
-import org.apache.sling.feature.launcher.spi.extensions.ExtensionInstallationContext;
public class ContentPackageHandler implements ExtensionHandler
{
@Override
- public boolean handle(Extension extension, LauncherPrepareContext prepareContext, ExtensionInstallationContext installationContext) throws IOException
+ public boolean handle(ExtensionContext context, Extension extension) throws IOException
{
if (extension.getType() == ExtensionType.ARTIFACTS
&& extension.getName().equals(Extension.EXTENSION_NAME_CONTENT_PACKAGES)) {
for(final Artifact a : extension.getArtifacts() ) {
- installationContext.addInstallableArtifact(prepareContext.getArtifactFile(a.getId()));
+ context.addInstallableArtifact(context.getArtifactFile(a.getId()));
}
return true;
}
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/RepoInitHandler.java b/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/RepoInitHandler.java
index c639104..34ac652 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/RepoInitHandler.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/extensions/handlers/RepoInitHandler.java
@@ -21,16 +21,15 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sling.feature.Configuration;
import org.apache.sling.feature.Extension;
import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
+import org.apache.sling.feature.launcher.spi.extensions.ExtensionContext;
import org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler;
-import org.apache.sling.feature.launcher.spi.extensions.ExtensionInstallationContext;
public class RepoInitHandler implements ExtensionHandler
{
private static final AtomicInteger index = new AtomicInteger(1);
@Override
- public boolean handle(Extension extension, LauncherPrepareContext prepareContext, ExtensionInstallationContext installationContext) throws Exception
+ public boolean handle(ExtensionContext context, Extension extension) throws Exception
{
if (extension.getName().equals(Extension.EXTENSION_NAME_REPOINIT)) {
if ( extension.getType() != ExtensionType.TEXT ) {
@@ -39,7 +38,7 @@ public class RepoInitHandler implements ExtensionHandler
final Configuration cfg = new Configuration("org.apache.sling.jcr.repoinit.RepositoryInitializer~repoinit"
+ String.valueOf(index.getAndIncrement()));
cfg.getProperties().put("scripts", extension.getText());
- installationContext.addConfiguration(Configuration.getName(cfg.getPid()),
+ context.addConfiguration(Configuration.getName(cfg.getPid()),
Configuration.getFactoryPid(cfg.getPid()), cfg.getConfigurationProperties());
return true;
}
diff --git a/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionInstallationContext.java b/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionContext.java
similarity index 52%
rename from src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionInstallationContext.java
rename to src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionContext.java
index 5efd7b7..21e38ca 100644
--- a/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionInstallationContext.java
+++ b/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionContext.java
@@ -16,28 +16,55 @@
*/
package org.apache.sling.feature.launcher.spi.extensions;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
+import org.apache.sling.feature.launcher.spi.LauncherRunContext;
+
import java.io.File;
+import java.io.IOException;
import java.util.Dictionary;
-import org.apache.sling.feature.launcher.spi.LauncherRunContext;
-
-public interface ExtensionInstallationContext extends LauncherRunContext
-{
+/**
+ * This context object is provided to launcher extensions.
+ */
+public interface ExtensionContext extends LauncherPrepareContext, LauncherRunContext {
+ /**
+ * Add a bundle to be installed by the launcher.
+ * @param startLevel The start level for the bundle.
+ * @param file The file with the bundle.
+ */
public void addBundle(final Integer startLevel, final File file);
/**
- * Add an artifact to be installed by the installer
+ * Add an artifact to be installed by the launcher
* @param file The file
*/
public void addInstallableArtifact(final File file);
/**
- * Add a configuration
+ * Add a configuration to be installed by the launcher
* @param pid The pid
* @param factoryPid The factory pid
* @param properties The propertis
*/
public void addConfiguration(final String pid, final String factoryPid, final Dictionary<String, Object> properties);
+ /**
+ * Add a framework property to be set by the launcher.
+ * @param key The key for the property.
+ * @param value The value for the property.
+ */
public void addFrameworkProperty(final String key, final String value);
+
+ /**
+ * Return the feature object for a given Artifact ID. It looks for the requested feature
+ * in the list of features provided from the launcher commandline as well as in the configured
+ * repositories.
+ * @param artifact The artifact ID for the feature.
+ * @return The Feature Model or null if the artifact cannot be found.
+ * @throws IOException If the artifact can be found, but creating a Feature
+ * Model out of it causes an exception.
+ */
+ Feature getFeature(ArtifactId artifact) throws IOException;
}
diff --git a/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionHandler.java b/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionHandler.java
index d09298e..acb8347 100644
--- a/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionHandler.java
+++ b/src/main/java/org/apache/sling/feature/launcher/spi/extensions/ExtensionHandler.java
@@ -17,9 +17,8 @@
package org.apache.sling.feature.launcher.spi.extensions;
import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
public interface ExtensionHandler
{
- public boolean handle(Extension extension, LauncherPrepareContext prepareContext, ExtensionInstallationContext installationContext) throws Exception;
+ public boolean handle(ExtensionContext context, Extension extension) throws Exception;
}
diff --git a/src/test/java/org/apache/sling/feature/launcher/impl/ExtensionContextImplTest.java b/src/test/java/org/apache/sling/feature/launcher/impl/ExtensionContextImplTest.java
new file mode 100644
index 0000000..e03b871
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/launcher/impl/ExtensionContextImplTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.feature.launcher.impl;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class ExtensionContextImplTest {
+ @Test
+ public void testGetFeature() throws Exception {
+ File testFeatureFile = new File(getClass().getResource("/test-feature.json").getFile());
+
+ ArtifactId aid2 = ArtifactId.fromMvnId("g:a:2");
+ LauncherPrepareContext lpc = Mockito.mock(LauncherPrepareContext.class);
+ Mockito.when(lpc.getArtifactFile(aid2)).thenReturn(testFeatureFile);
+
+ ArtifactId aid1 = ArtifactId.fromMvnId("g:a:1");
+ Feature f1 = new Feature(aid1);
+ Map<ArtifactId, Feature> loaded = new HashMap<>();
+ loaded.put(aid1, f1);
+
+ ExtensionContextImpl c = new ExtensionContextImpl(lpc, null, loaded);
+ assertEquals(f1, c.getFeature(aid1));
+ assertNotNull(c.getFeature(aid2));
+ assertNull(c.getFeature(ArtifactId.fromMvnId("g:a:3")));
+ }
+}
diff --git a/src/test/resources/test-feature.json b/src/test/resources/test-feature.json
new file mode 100644
index 0000000..8bac79d
--- /dev/null
+++ b/src/test/resources/test-feature.json
@@ -0,0 +1,3 @@
+{
+ "id" : "org.apache.sling/a-test-feature/1"
+}
\ No newline at end of file