Merge pull request #26 from rmannibucau/rmannibucau/KARAF-6899
[KARAF-6899] add LifecycleCallbacks API
diff --git a/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml b/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml
index e44545b..e72018e 100644
--- a/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml
+++ b/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml
@@ -79,6 +79,7 @@
<groupId>org.apache.aries.spifly</groupId>
<artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
<version>1.3.2</version>
+ <optional>true</optional> <!-- not really needed -->
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java b/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java
index 50c0918..c03e8f9 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java
@@ -13,6 +13,7 @@
*/
package org.apache.winegrower;
+import org.apache.winegrower.api.LifecycleCallbacks;
import org.apache.winegrower.deployer.OSGiBundleLifecycle;
import org.apache.winegrower.scanner.StandaloneScanner;
import org.apache.winegrower.scanner.manifest.HeaderManifestContributor;
@@ -60,6 +61,8 @@
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.stream.Stream;
@@ -67,6 +70,7 @@
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
+import static java.util.Comparator.comparing;
import static java.util.Locale.ROOT;
import static java.util.Optional.ofNullable;
import static java.util.function.Function.identity;
@@ -141,6 +145,29 @@
"org.apache.aries.cdi");
private List<String> defaultConfigurationAdminPids;
+ private List<LifecycleCallbacks> lifecycleCallbacks;
+
+ /**
+ * Only for SPI ones, the epxlicit ones in {@link #lifecycleCallbacks} are always used.
+ */
+ private boolean useLifecycleCallbacks = true;
+
+ public boolean isUseLifecycleCallbacks() {
+ return useLifecycleCallbacks;
+ }
+
+ public void setUseLifecycleCallbacks(final boolean useLifecycleCallbacks) {
+ this.useLifecycleCallbacks = useLifecycleCallbacks;
+ }
+
+ public List<LifecycleCallbacks> getLifecycleCallbacks() {
+ return lifecycleCallbacks;
+ }
+
+ public void setLifecycleCallbacks(final List<LifecycleCallbacks> lifecycleCallbacks) {
+ this.lifecycleCallbacks = lifecycleCallbacks;
+ }
+
public List<String> getDefaultConfigurationAdminPids() {
return defaultConfigurationAdminPids;
}
@@ -293,13 +320,31 @@
public Impl(final Configuration configuration) {
this.configuration = configuration;
+ final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ if (configuration.isUseLifecycleCallbacks()) {
+ final List<LifecycleCallbacks> callbacks = StreamSupport.stream(
+ ServiceLoader.load(LifecycleCallbacks.class, contextClassLoader).spliterator(), false)
+ .sorted(comparing(LifecycleCallbacks::order))
+ .collect(toList());
+ if (configuration.getLifecycleCallbacks() == null) {
+ configuration.setLifecycleCallbacks(callbacks);
+ } else {
+ configuration.setLifecycleCallbacks(Stream.concat(
+ configuration.getLifecycleCallbacks().stream(),
+ callbacks.stream()).sorted(comparing(LifecycleCallbacks::order))
+ .collect(toList()));
+ }
+ } else if (configuration.getLifecycleCallbacks() == null) {
+ configuration.setLifecycleCallbacks(emptyList());
+ }
+ runCallbacks(LifecycleCallbacks::processConfiguration, configuration);
+
final Collection<ConfigurationListener> configurationListeners = new ArrayList<>();
final Collection<DefaultEventAdmin.EventHandlerInstance> eventListeners = new ArrayList<>();
this.services = new OSGiServices(this, configurationListeners, eventListeners);
this.registry = new BundleRegistry(services, configuration);
- try (final InputStream stream = Thread.currentThread().getContextClassLoader()
- .getResourceAsStream("winegrower.properties")) {
+ try (final InputStream stream = contextClassLoader.getResourceAsStream("winegrower.properties")) {
loadConfiguration(stream);
} catch (final IOException e) {
LOGGER.warn(e.getMessage());
@@ -322,6 +367,10 @@
this.services.registerService(new String[]{type.getName()}, impl, props, this.registry.getBundles().get(0L).getBundle());
}
+ private <A> void runCallbacks(final BiConsumer<LifecycleCallbacks, A> action, final A arg) {
+ configuration.getLifecycleCallbacks().forEach(c -> action.accept(c, arg));
+ }
+
private ConfigurationAdmin loadConfigurationAdmin(final Collection<ConfigurationListener> configurationListeners) {
final Iterator<ConfigurationAdmin> configurationAdminIterator = ServiceLoader.load(ConfigurationAdmin.class).iterator();
if (configurationAdminIterator.hasNext()) {
@@ -450,28 +499,33 @@
@Override
public synchronized Ripener start() {
- startTime = System.currentTimeMillis();
- LOGGER.info("Starting Apache Winegrower application on {}",
- LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault()));
- if (configuration.isLazyInstall()) {
- return this;
+ runCallbacks(LifecycleCallbacks::beforeStart, this);
+ try {
+ startTime = System.currentTimeMillis();
+ LOGGER.info("Starting Apache Winegrower application on {}",
+ LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault()));
+ if (configuration.isLazyInstall()) {
+ return this;
+ }
+ final StandaloneScanner scanner = getScanner();
+ final AtomicLong bundleIdGenerator = new AtomicLong(1);
+ Stream.concat(Stream.concat(
+ scanner.findOSGiBundles().stream(),
+ scanner.findPotentialOSGiBundles().stream()),
+ scanner.findEmbeddedClasses().stream())
+ .sorted(this::compareBundles)
+ .map(it -> new OSGiBundleLifecycle(
+ it.getManifest(), it.getJar(),
+ services, registry, configuration,
+ bundleIdGenerator.getAndIncrement(),
+ it.getFiles()))
+ .peek(OSGiBundleLifecycle::start)
+ .peek(it -> registry.getBundles().put(it.getBundle().getBundleId(), it))
+ .forEach(bundle -> LOGGER.debug("Bundle {}", bundle));
+ this.scanner = null; // we don't need it anymore since we don't support runtime install so make it gc friendly
+ } finally {
+ runCallbacks(LifecycleCallbacks::afterStart, this);
}
- final StandaloneScanner scanner = getScanner();
- final AtomicLong bundleIdGenerator = new AtomicLong(1);
- Stream.concat(Stream.concat(
- scanner.findOSGiBundles().stream(),
- scanner.findPotentialOSGiBundles().stream()),
- scanner.findEmbeddedClasses().stream())
- .sorted(this::compareBundles)
- .map(it -> new OSGiBundleLifecycle(
- it.getManifest(), it.getJar(),
- services, registry, configuration,
- bundleIdGenerator.getAndIncrement(),
- it.getFiles()))
- .peek(OSGiBundleLifecycle::start)
- .peek(it -> registry.getBundles().put(it.getBundle().getBundleId(), it))
- .forEach(bundle -> LOGGER.debug("Bundle {}", bundle));
- this.scanner = null; // we don't need it anymore since we don't support runtime install so make it gc friendly
return this;
}
@@ -481,33 +535,38 @@
@Override
public synchronized void stop() {
- LOGGER.info("Stopping Apache Winegrower application on {}", LocalDateTime.now());
- final Map<Long, OSGiBundleLifecycle> bundles = registry.getBundles();
- bundles.values().stream()
- .sorted((o1, o2) -> (int) (o2.getBundle().getBundleId() - o1.getBundle().getBundleId()))
- .forEach(OSGiBundleLifecycle::stop);
- bundles.clear();
- if (configuration.getWorkDir().exists()) {
- try {
- Files.walkFileTree(configuration.getWorkDir().toPath(), new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
- Files.delete(file);
- return super.visitFile(file, attrs);
- }
+ runCallbacks(LifecycleCallbacks::beforeStop, this);
+ try {
+ LOGGER.info("Stopping Apache Winegrower application on {}", LocalDateTime.now());
+ final Map<Long, OSGiBundleLifecycle> bundles = registry.getBundles();
+ bundles.values().stream()
+ .sorted((o1, o2) -> (int) (o2.getBundle().getBundleId() - o1.getBundle().getBundleId()))
+ .forEach(OSGiBundleLifecycle::stop);
+ bundles.clear();
+ if (configuration.getWorkDir().exists()) {
+ try {
+ Files.walkFileTree(configuration.getWorkDir().toPath(), new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return super.visitFile(file, attrs);
+ }
- @Override
- public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
- Files.delete(dir);
- return super.postVisitDirectory(dir, exc);
- }
- });
- } catch (final IOException e) {
- LOGGER.warn("Can't delete work directory", e);
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ Files.delete(dir);
+ return super.postVisitDirectory(dir, exc);
+ }
+ });
+ } catch (final IOException e) {
+ LOGGER.warn("Can't delete work directory", e);
+ }
}
- }
- if (DefaultEventAdmin.class.isInstance(eventAdmin)) {
- DefaultEventAdmin.class.cast(eventAdmin).close();
+ if (DefaultEventAdmin.class.isInstance(eventAdmin)) {
+ DefaultEventAdmin.class.cast(eventAdmin).close();
+ }
+ } finally {
+ runCallbacks(LifecycleCallbacks::afterStop, this);
}
}
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/api/LifecycleCallbacks.java b/winegrower-core/src/main/java/org/apache/winegrower/api/LifecycleCallbacks.java
new file mode 100644
index 0000000..31ee1b6
--- /dev/null
+++ b/winegrower-core/src/main/java/org/apache/winegrower/api/LifecycleCallbacks.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.api;
+
+import org.apache.winegrower.Ripener;
+
+/**
+ * Enables to interact with Ripener before/after it is active.
+ * Very convenient to register custom built in services for example.
+ * It is registered as a plain java SPI (META-INF/services/org.apache.winegrower.api.LifecycleCallbacks).
+ */
+public interface LifecycleCallbacks {
+ /**
+ * @return callbacks are sorted thanks to this order (natural int order).
+ */
+ default int order() {
+ return 1000;
+ }
+
+ // called before ripener is setup
+ default void processConfiguration(final Ripener.Configuration configuration) {
+ // no-op
+ }
+
+ default void beforeStart(final Ripener ripener) {
+ // no-op
+ }
+
+ default void afterStart(final Ripener ripener) {
+ // no-op
+ }
+
+ default void beforeStop(final Ripener ripener) {
+ // no-op
+ }
+
+ default void afterStop(final Ripener ripener) {
+ // no-op
+ }
+}
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/api/LifecycleCallbacksTest.java b/winegrower-core/src/test/java/org/apache/winegrower/api/LifecycleCallbacksTest.java
new file mode 100644
index 0000000..70e6242
--- /dev/null
+++ b/winegrower-core/src/test/java/org/apache/winegrower/api/LifecycleCallbacksTest.java
@@ -0,0 +1,58 @@
+package org.apache.winegrower.api;
+
+import org.apache.winegrower.Ripener;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class LifecycleCallbacksTest {
+ @Test
+ void lifecycle() {
+ final MyCallback callback = new MyCallback();
+ final Ripener.Configuration configuration = new Ripener.Configuration();
+ configuration.setLifecycleCallbacks(singletonList(callback));
+ System.setProperty("winegrower.scanner.standalone.skipUrlsScanning", "true");
+ try (final Ripener ripener = Ripener.create(configuration).start()) {
+ // no-op
+ } finally {
+ System.clearProperty("winegrower.scanner.standalone.skipUrlsScanning");
+ }
+ assertEquals(
+ asList("processConfiguration=true", "beforeStart=true", "afterStart=true", "beforeStop=true", "afterStop=true"),
+ callback.events);
+ }
+
+ public static class MyCallback implements LifecycleCallbacks {
+ private final List<String> events = new ArrayList<>();
+
+ @Override
+ public void processConfiguration(final Ripener.Configuration configuration) {
+ events.add("processConfiguration=" + (configuration != null));
+ }
+
+ @Override
+ public void beforeStart(final Ripener ripener) {
+ events.add("beforeStart=" + (ripener != null));
+ }
+
+ @Override
+ public void afterStart(final Ripener ripener) {
+ events.add("afterStart=" + (ripener != null));
+ }
+
+ @Override
+ public void beforeStop(final Ripener ripener) {
+ events.add("beforeStop=" + (ripener != null));
+ }
+
+ @Override
+ public void afterStop(final Ripener ripener) {
+ events.add("afterStop=" + (ripener != null));
+ }
+ }
+}
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/test/WithRipener.java b/winegrower-core/src/test/java/org/apache/winegrower/test/WithRipener.java
index 51046ea..ea1baf1 100644
--- a/winegrower-core/src/test/java/org/apache/winegrower/test/WithRipener.java
+++ b/winegrower-core/src/test/java/org/apache/winegrower/test/WithRipener.java
@@ -20,12 +20,14 @@
import static java.util.Collections.singletonList;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@@ -45,6 +47,7 @@
import java.util.stream.Stream;
import org.apache.winegrower.Ripener;
+import org.apache.winegrower.api.LifecycleCallbacks;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -81,6 +84,8 @@
Entry[] includeResources() default {};
+ boolean addLifecycleCallbackSpy() default false;
+
class Extension implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
private static final String CLASSES_BASE = System.getProperty(Extension.class.getName() + ".classesBase",
@@ -103,7 +108,7 @@
final Ripener.Configuration configuration = new Ripener.Configuration();
configuration.setScanningExcludes(asList("common-java5-" /* surefire, yes... */, "test-classes"));
- setConfiguration(configuration, config);
+ setConfiguration(configuration, config, extensionContext.getTestClass().orElseThrow(IllegalStateException::new));
final Ripener ripener = new Ripener.Impl(configuration).start();
store.put(Ripener.class, ripener);
@@ -126,7 +131,8 @@
ofNullable(store.get(Ripener.class, Ripener.class)).ifPresent(Ripener::stop);
}
- private void setConfiguration(final Ripener.Configuration configuration, final WithRipener config) {
+ private void setConfiguration(final Ripener.Configuration configuration, final WithRipener config,
+ final Class<?> test) {
final Collection<String> includes = asList(config.includes());
if (!includes.isEmpty()) {
configuration.setJarFilter(it -> includes.stream().anyMatch(e -> e.startsWith(it)));
@@ -136,6 +142,19 @@
if (!workDir.isEmpty()) {
configuration.setWorkDir(new File(workDir));
}
+
+ if (config.addLifecycleCallbackSpy()) {
+ configuration.setLifecycleCallbacks(Stream.of(test.getClasses())
+ .filter(LifecycleCallbacks.class::isAssignableFrom)
+ .map(it -> {
+ try {
+ return it.asSubclass(LifecycleCallbacks.class).getConstructor().newInstance();
+ } catch (final InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ }
+ })
+ .collect(toList()));
+ }
}
private URL[] createUrls(final WithRipener config, final ExtensionContext context) {
diff --git a/winegrower-extension/winegrower-agent/src/main/java/org/apache/winegrower/extension/agent/WinegrowerAgent.java b/winegrower-extension/winegrower-agent/src/main/java/org/apache/winegrower/extension/agent/WinegrowerAgent.java
index d198746..d53f0c6 100644
--- a/winegrower-extension/winegrower-agent/src/main/java/org/apache/winegrower/extension/agent/WinegrowerAgent.java
+++ b/winegrower-extension/winegrower-agent/src/main/java/org/apache/winegrower/extension/agent/WinegrowerAgent.java
@@ -170,10 +170,43 @@
private static Object createConfiguration(final Class<?> configType, final String agentArgs) throws Throwable {
final Object configuration = configType.getConstructor().newInstance();
+ final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
ofNullable(extractConfig(agentArgs,"workDir="))
.map(String::valueOf)
.map(File::new)
.ifPresent(value -> doCall(configuration, "setWorkDir", new Class<?>[]{File.class}, new Object[]{value}));
+ ofNullable(extractConfig(agentArgs,"useLifecycleCallbacks="))
+ .map(String::valueOf)
+ .map(Boolean::parseBoolean)
+ .ifPresent(value -> doCall(configuration, "setUseLifecycleCallbacks", new Class<?>[]{boolean.class}, new Object[]{value}));
+ ofNullable(extractConfig(agentArgs,"lifecycleCallbacks="))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .map(it -> asList(it.split(",")))
+ .map(callbacks -> {
+ try {
+ final Class<?> type = contextClassLoader.loadClass("org.apache.winegrower.api.LifecycleCallbacks");
+ return callbacks.stream()
+ .map(clazz -> {
+ try {
+ return contextClassLoader
+ .loadClass(clazz)
+ .getConstructor()
+ .newInstance();
+ } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalArgumentException(
+ e.getTargetException());
+ }
+ })
+ .map(type::cast)
+ .collect(toList());
+ } catch (final ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ })
+ .ifPresent(value -> doCall(configuration, "setLifecycleCallbacks", new Class<?>[]{List.class}, new Object[]{value}));
ofNullable(extractConfig(agentArgs,"prioritizedBundles="))
.map(String::valueOf)
.filter(it -> !it.isEmpty())
@@ -200,13 +233,12 @@
.map(it -> asList(it.split(",")))
.ifPresent(contributors -> {
try {
- final Class<?> type = Thread.currentThread().getContextClassLoader().loadClass(
+ final Class<?> type = contextClassLoader.loadClass(
"org.apache.winegrower.scanner.manifest.ManifestContributor");
final Collection<?> value = contributors.stream()
.map(clazz -> {
try {
- return Thread.currentThread()
- .getContextClassLoader()
+ return contextClassLoader
.loadClass(clazz)
.getConstructor()
.newInstance();
@@ -229,8 +261,7 @@
.filter(it -> !it.isEmpty())
.ifPresent(filter -> {
try {
- final Predicate<String> predicate = (Predicate<String>) Thread.currentThread()
- .getContextClassLoader().loadClass(filter).getConstructor().newInstance();
+ final Predicate<String> predicate = (Predicate<String>) contextClassLoader.loadClass(filter).getConstructor().newInstance();
doCall(configuration, "setJarFilter", new Class<?>[]{Predicate.class}, new Object[]{predicate});
} catch (final InstantiationException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
throw new IllegalArgumentException(e);
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/PourMojo.java b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/PourMojo.java
index 182b86e..d91eb46 100644
--- a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/PourMojo.java
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/PourMojo.java
@@ -18,6 +18,7 @@
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
+import org.apache.winegrower.api.LifecycleCallbacks;
import org.apache.xbean.finder.util.Files;
import java.io.File;
@@ -65,6 +66,12 @@
@Parameter(property = "winegrower.prioritizedBundles")
private List<String> prioritizedBundles;
+ @Parameter(property = "winegrower.lifecycleCallbacks")
+ private List<Class<?>> lifecycleCallbacks;
+
+ @Parameter(property = "winegrower.useLifecycleCallbacks", defaultValue = "true")
+ private boolean useLifecycleCallbacks;
+
@Parameter(property = "winegrower.systemVariables")
private Map<String, String> systemVariables;
@@ -158,6 +165,22 @@
private Object createConfiguration(final Class<?> configClass)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
final Object configuration = configClass.getConstructor().newInstance();
+ doCall(configuration, "setUseLifecycleCallbacks", new Class<?>[]{boolean.class}, new Object[]{useLifecycleCallbacks});
+ ofNullable(lifecycleCallbacks).map(it -> it.stream()
+ .map(clazz -> {
+ try {
+ return clazz
+ .getConstructor()
+ .newInstance();
+ } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalArgumentException(
+ e.getTargetException());
+ }
+ })
+ .collect(toList()))
+ .ifPresent(value -> doCall(configuration, "setLifecycleCallbacks", new Class<?>[]{List.class}, new Object[]{value}));
ofNullable(workDir)
.ifPresent(value -> doCall(configuration, "setWorkDir", new Class<?>[]{File.class}, new Object[]{value}));
ofNullable(prioritizedBundles)
diff --git a/winegrower-extension/winegrower-servlet/src/main/java/org/apache/winegrower/servlet/service/ServletHttpServiceDeployer.java b/winegrower-extension/winegrower-servlet/src/main/java/org/apache/winegrower/servlet/service/ServletHttpServiceDeployer.java
index f57f5ab..1507235 100644
--- a/winegrower-extension/winegrower-servlet/src/main/java/org/apache/winegrower/servlet/service/ServletHttpServiceDeployer.java
+++ b/winegrower-extension/winegrower-servlet/src/main/java/org/apache/winegrower/servlet/service/ServletHttpServiceDeployer.java
@@ -14,6 +14,7 @@
package org.apache.winegrower.servlet.service;
import org.apache.winegrower.Ripener;
+import org.apache.winegrower.api.LifecycleCallbacks;
import org.apache.winegrower.scanner.manifest.ManifestContributor;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@@ -181,6 +182,10 @@
private Ripener.Configuration createConfiguration(final ServletContext servletContext) {
final Ripener.Configuration configuration = new Ripener.Configuration();
+ ofNullable(servletContext.getInitParameter("winegrower.servlet.ripener.configuration.useLifecycleCallbacks"))
+ .map(String::valueOf)
+ .map(Boolean::parseBoolean)
+ .ifPresent(configuration::setUseLifecycleCallbacks);
ofNullable(servletContext.getInitParameter("winegrower.servlet.ripener.configuration.workdir")).map(String::valueOf)
.map(File::new).ifPresent(configuration::setWorkDir);
ofNullable(servletContext.getInitParameter("winegrower.servlet.ripener.configuration.prioritizedBundles"))
@@ -208,6 +213,19 @@
}
}).map(ManifestContributor.class::cast).collect(toList()));
});
+ ofNullable(servletContext.getInitParameter("winegrower.servlet.ripener.configuration.lifecycleCallbacks"))
+ .map(String::valueOf).filter(it -> !it.isEmpty()).map(it -> asList(it.split(","))).ifPresent(lifecycleCallbacks -> {
+ configuration.setLifecycleCallbacks(lifecycleCallbacks.stream().map(clazz -> {
+ try {
+ return Thread.currentThread().getContextClassLoader().loadClass(clazz).getConstructor().newInstance();
+ } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException
+ | ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalArgumentException(e.getTargetException());
+ }
+ }).map(LifecycleCallbacks.class::cast).collect(toList()));
+ });
ofNullable(servletContext.getInitParameter("winegrower.servlet.ripener.configuration.jarFilter")).map(String::valueOf)
.filter(it -> !it.isEmpty()).ifPresent(filter -> {
try {
diff --git a/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/Winegrower.java b/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/Winegrower.java
index 216aba0..fa676ef 100644
--- a/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/Winegrower.java
+++ b/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/Winegrower.java
@@ -20,6 +20,7 @@
import java.lang.annotation.Target;
import java.util.function.Predicate;
+import org.apache.winegrower.api.LifecycleCallbacks;
import org.apache.winegrower.extension.testing.junit5.internal.WinegrowerExtension;
import org.apache.winegrower.scanner.manifest.ManifestContributor;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -38,6 +39,8 @@
String[] ignoredBundles() default {};
String[] scanningExcludes() default {};
String[] scanningIncludes() default {};
+ Class<? extends LifecycleCallbacks>[] lifecycleCallbacks() default {};
+ boolean useLifecycleCallbacks() default true;
interface JarFilter extends Predicate<String> {
}
diff --git a/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/internal/WinegrowerExtension.java b/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/internal/WinegrowerExtension.java
index 7006097..8b78b3c 100644
--- a/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/internal/WinegrowerExtension.java
+++ b/winegrower-extension/winegrower-testing/winegrower-testing-junit5/src/main/java/org/apache/winegrower/extension/testing/junit5/internal/WinegrowerExtension.java
@@ -48,6 +48,8 @@
private Ripener.Configuration createConfiguration(final Winegrower winegrower) {
final Ripener.Configuration configuration = new Ripener.Configuration();
+ of(winegrower.useLifecycleCallbacks())
+ .ifPresent(configuration::setUseLifecycleCallbacks);
of(winegrower.workDir())
.filter(it -> !it.isEmpty())
.ifPresent(wd -> configuration.setWorkDir(new File(wd)));
@@ -85,6 +87,17 @@
throw new IllegalArgumentException(e.getTargetException());
}
}).collect(toList())));
+ of(winegrower.lifecycleCallbacks())
+ .filter(it -> it.length > 0)
+ .ifPresent(value -> configuration.setLifecycleCallbacks(Stream.of(value).map(it -> {
+ try {
+ return it.getConstructor().newInstance();
+ } catch (final InstantiationException | IllegalAccessException | NoSuchMethodException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalArgumentException(e.getTargetException());
+ }
+ }).collect(toList())));
return configuration;
}