You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2022/01/06 13:04:57 UTC
[sling-org-apache-sling-feature-launcher] branch issues/SLING-11042 updated: Register feature as a service and update metadata
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch issues/SLING-11042
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-launcher.git
The following commit(s) were added to refs/heads/issues/SLING-11042 by this push:
new f091e29 Register feature as a service and update metadata
f091e29 is described below
commit f091e29a5294b745034680a3bedda6839fda2fab
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Thu Jan 6 14:04:50 2022 +0100
Register feature as a service and update metadata
---
.../feature/launcher/impl/FeatureProcessor.java | 2 +
.../launcher/impl/launchers/AbstractRunner.java | 74 +++++++++++++++++++++-
.../launcher/impl/launchers/FrameworkLauncher.java | 63 ++++++++++++++++++
.../launcher/impl/launchers/FrameworkRunner.java | 16 +++--
4 files changed, 148 insertions(+), 7 deletions(-)
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 ac1815b..9b7ab0d 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
@@ -187,6 +187,8 @@ public class FeatureProcessor {
for(final Artifact a : entry.getValue()) {
final URL artifactFile = ctx.getArtifactFile(a.getId());
+ // add URL to feature metadata
+ a.getMetadata().put(URL.class.getName(), artifactFile.toString());
config.getInstallation().addBundle(entry.getKey(), artifactFile);
}
}
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
index d5f952b..0c56e27 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
@@ -18,6 +18,7 @@ package org.apache.sling.feature.launcher.impl.launchers;
import java.io.IOException;
import java.io.InputStream;
+import java.io.StringReader;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -27,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -38,16 +40,20 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.PrototypeServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.BundleWiring;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
@@ -81,12 +87,24 @@ public abstract class AbstractRunner implements Callable<Integer> {
protected final Logger logger;
+ private Supplier<String> featureSupplier;
+
+ private BiConsumer<URL, Map<String, String>> bundleReporter;
+
public AbstractRunner(final List<Object[]> configurations, final List<URL> installables) {
this.configurations = new ArrayList<>(configurations);
this.installables = installables;
this.logger = LoggerFactory.getLogger("launcher");
}
+ public void setFeatureSupplier(final Supplier<String> supplier) {
+ this.featureSupplier = supplier;
+ }
+
+ public void setBundleReporter(final BiConsumer<URL, Map<String, String>> reporter) {
+ this.bundleReporter = reporter;
+ }
+
protected void setupFramework(final Framework framework, final Map<Integer, List<URL>> bundlesMap) throws BundleException {
// check for Apache Felix CM persistence manager config
final String pm = framework.getBundleContext().getProperty(CM_CONFIG_PM);
@@ -313,7 +331,7 @@ public abstract class AbstractRunner implements Callable<Integer> {
if (file.getProtocol().equals("file")) {
location = "reference:";
}
- location = location + file.toString();
+ location = location.concat(file.toString());
final Bundle bundle = bc.installBundle(location, null);
@@ -324,10 +342,64 @@ public abstract class AbstractRunner implements Callable<Integer> {
}
bundle.start();
}
+
+ if ( this.bundleReporter != null ) {
+ final Map<String, String> params = new HashMap<>();
+ params.put(Constants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
+ params.put(Constants.BUNDLE_VERSION, bundle.getVersion().toString());
+ params.put("Bundle-Id", String.valueOf(bundle.getBundleId()));
+
+ this.bundleReporter.accept(file, params);
+ }
+
}
}
}
+ protected void finishStartup(final Framework framework) {
+ Bundle featureBundle = null;
+ for(final Bundle bundle : framework.getBundleContext().getBundles()) {
+ if ( featureSupplier != null && "org.apache.sling.feature".equals(bundle.getSymbolicName()) ) {
+ featureBundle = bundle;
+ }
+ }
+ if ( featureBundle != null ) {
+ final Bundle bundle = featureBundle;
+ // the feature is registered as a prototype to give each client a copy as feature models are mutable
+ final Dictionary<String, Object> properties = new Hashtable<>();
+ properties.put("name", "org.apache.sling.feature.launcher");
+ featureBundle.getBundleContext().registerService(new String[] {"org.apache.sling.feature.Feature"},
+ new PrototypeServiceFactory<Object>() {
+
+ @Override
+ public Object getService(final Bundle client, final ServiceRegistration<Object> registration) {
+ final ClassLoader cl = bundle.adapt(BundleWiring.class).getClassLoader();
+ final ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(cl);
+ try {
+ final Class<?> readerClass = cl.loadClass("org.apache.sling.feature.io.json.FeatureJSONReader");
+ final Method readMethod = readerClass.getDeclaredMethod("read", java.io.Reader.class, String.class);
+ try( final StringReader reader = new StringReader(featureSupplier.get())) {
+ return readMethod.invoke(null, reader, null);
+ }
+ } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
+ // ignore
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldTCCL);
+ }
+ return null;
+ }
+
+ @Override
+ public void ungetService(final Bundle client, final ServiceRegistration<Object> registration,
+ final Object service) {
+ // nothing to do
+ }
+
+ }, properties);
+ }
+ }
+
/**
* Sort the start levels in the ascending order. The only exception is the start level
* "0", which should be put at the position configured in {@code felix.startlevel.bundle}.
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
index e88a566..11a3128 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
@@ -16,19 +16,29 @@
*/
package org.apache.sling.feature.launcher.impl.launchers;
+import java.io.IOException;
+import java.io.StringWriter;
import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.io.json.FeatureJSONWriter;
import org.apache.sling.feature.launcher.impl.VariableSubstitutor;
import org.apache.sling.feature.launcher.spi.Launcher;
import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
import org.apache.sling.feature.launcher.spi.LauncherRunContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
import aQute.bnd.annotation.spi.ServiceProvider;
@@ -38,12 +48,14 @@ import aQute.bnd.annotation.spi.ServiceProvider;
@ServiceProvider(value = Launcher.class)
public class FrameworkLauncher implements Launcher {
+ private Feature feature;
@Override
public void prepare(final LauncherPrepareContext context,
final ArtifactId frameworkId,
final Feature app) throws Exception {
context.addAppJar(context.getArtifactFile(frameworkId));
+ this.feature = app;
}
/**
@@ -89,6 +101,37 @@ public class FrameworkLauncher implements Launcher {
Callable<Integer> restart = (Callable<Integer>) constructor.newInstance(properties, context.getBundleMap(),
context.getConfigurations(), context.getInstallableArtifacts());
+ setOptionalSupplier(restart, "setFeatureSupplier", new Supplier<Object>() {
+
+ @Override
+ public Object get() {
+ try ( final StringWriter writer = new StringWriter()) {
+ FeatureJSONWriter.write(writer, feature);
+ writer.flush();
+ return writer.toString();
+ } catch ( final IOException ignore) {
+ // ignore
+ }
+ return null;
+ }
+
+ });
+
+ setOptionalBiConsumer(restart, "setBundleReporter", new BiConsumer<URL, Map<String, String>>() {
+ @Override
+ public void accept(final URL url, final Map<String, String> values) {
+ final String urlString = url.toString();
+ for(final Artifact a : feature.getBundles()) {
+ if ( urlString.equals(a.getMetadata().get(URL.class.getName()))) {
+ for(final Map.Entry<String, String> entry : values.entrySet()) {
+ a.getMetadata().put(entry.getKey(), entry.getValue());
+ }
+ break;
+ }
+ }
+ }
+
+ });
return restart.call();
// nothing else to do, constructor starts everything
}
@@ -96,4 +139,24 @@ public class FrameworkLauncher implements Launcher {
protected String getFrameworkRunnerClass() {
return FrameworkRunner.class.getName();
}
+
+ private void setOptionalSupplier(final Object restart, final String name, final Supplier<Object> supplier) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ try {
+ final Method setSupplier = restart.getClass().getMethod(name, Supplier.class);
+ setSupplier.setAccessible(true);
+ setSupplier.invoke(restart, supplier);
+ } catch ( final NoSuchMethodException nsme) {
+ // ignore
+ }
+ }
+
+ private void setOptionalBiConsumer(final Object restart, final String name, final BiConsumer consumer) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ try {
+ final Method setMethod = restart.getClass().getMethod(name, BiConsumer.class);
+ setMethod.setAccessible(true);
+ setMethod.invoke(restart, consumer);
+ } catch ( final NoSuchMethodException nsme) {
+ // ignore
+ }
+ }
}
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java
index f31a8a2..fd77c01 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java
@@ -39,12 +39,20 @@ public class FrameworkRunner extends AbstractRunner {
private volatile int type = -1;
+ private final Map<String, String> frameworkProperties;
+
+ private final Map<Integer, List<URL>> bundlesMap;
+
public FrameworkRunner(final Map<String, String> frameworkProperties,
final Map<Integer, List<URL>> bundlesMap,
final List<Object[]> configurations,
final List<URL> installables) throws Exception {
super(configurations, installables);
-
+ this.frameworkProperties = frameworkProperties;
+ this.bundlesMap = bundlesMap;
+ }
+
+ public Integer call() throws Exception {
final ServiceLoader<FrameworkFactory> loader = ServiceLoader.load(FrameworkFactory.class);
FrameworkFactory factory = null;
for(FrameworkFactory f : loader) {
@@ -81,7 +89,6 @@ public class FrameworkRunner extends AbstractRunner {
this.setupFramework(framework, bundlesMap);
-
long time = System.currentTimeMillis();
long startTimeout = Long.parseLong(frameworkProperties.getOrDefault(START_TIMEOUT, String.valueOf(10 * 60)));
@@ -89,6 +96,7 @@ public class FrameworkRunner extends AbstractRunner {
if (!this.startFramework(framework, startTimeout, TimeUnit.SECONDS)) {
throw new TimeoutException("Waited for more than " + startTimeout + " seconds to startup framework.");
}
+ this.finishStartup(framework);
logger.info("Framework started");
logger.debug("Startup took: " + (System.currentTimeMillis() - time));
@@ -101,10 +109,6 @@ public class FrameworkRunner extends AbstractRunner {
}
logger.debug("Restart took: " + (System.currentTimeMillis() - time));
}
- }
-
- @Override
- public Integer call() {
return type;
}
}