[GERONIMO-6796] better OWB integration with extension, annotation (dynamic proxy) and producer support
diff --git a/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/ConfigurationGenerator.java b/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/ConfigurationGenerator.java
index a51903a..84796bc 100644
--- a/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/ConfigurationGenerator.java
+++ b/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/ConfigurationGenerator.java
@@ -35,6 +35,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -47,8 +48,8 @@
import static java.util.Comparator.comparing;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
@Slf4j
@RequiredArgsConstructor
@@ -141,7 +142,7 @@
if (!context.getDynamicProxyModels().isEmpty()) {
final Set<Collection<String>> proxies = context.getDynamicProxyModels().stream()
.map(DynamicProxyModel::getClasses)
- .collect(toSet());
+ .collect(toCollection(LinkedHashSet::new));
ensureWorkingDirectoryExists();
final Path json = workingDirectory.resolve("dynamicproxies.arthur.json");
@@ -169,7 +170,7 @@
throw new IllegalStateException(e);
}
});
- log.info("Dumped generated classes in '{}'", dynamicClassesDir);
+ log.info("Dumped {} generated classes in '{}'", context.getDynamicClasses().size(), dynamicClassesDir);
if (configuration.getClasspath() == null) {
configuration.setClasspath(singletonList(dynamicClassesDir.toString()));
} else {
diff --git a/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/DefautContext.java b/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/DefautContext.java
index 8e7b44c..6cdbca0 100644
--- a/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/DefautContext.java
+++ b/arthur-impl/src/main/java/org/apache/geronimo/arthur/impl/nativeimage/generator/DefautContext.java
@@ -16,9 +16,13 @@
*/
package org.apache.geronimo.arthur.impl.nativeimage.generator;
-import static java.util.Optional.of;
-import static java.util.Optional.ofNullable;
-import static java.util.stream.Collectors.toList;
+import lombok.Data;
+import org.apache.geronimo.arthur.impl.nativeimage.ArthurNativeImageConfiguration;
+import org.apache.geronimo.arthur.spi.ArthurExtension;
+import org.apache.geronimo.arthur.spi.model.ClassReflectionModel;
+import org.apache.geronimo.arthur.spi.model.DynamicProxyModel;
+import org.apache.geronimo.arthur.spi.model.ResourceBundleModel;
+import org.apache.geronimo.arthur.spi.model.ResourceModel;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
@@ -35,14 +39,9 @@
import java.util.function.Predicate;
import java.util.stream.Stream;
-import org.apache.geronimo.arthur.impl.nativeimage.ArthurNativeImageConfiguration;
-import org.apache.geronimo.arthur.spi.ArthurExtension;
-import org.apache.geronimo.arthur.spi.model.ClassReflectionModel;
-import org.apache.geronimo.arthur.spi.model.DynamicProxyModel;
-import org.apache.geronimo.arthur.spi.model.ResourceBundleModel;
-import org.apache.geronimo.arthur.spi.model.ResourceModel;
-
-import lombok.Data;
+import static java.util.Optional.of;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
@Data
public class DefautContext implements ArthurExtension.Context {
@@ -122,6 +121,14 @@
}
@Override
+ public void includeResourceBundle(final String name) {
+ if (configuration.getIncludeResourceBundles() == null) {
+ configuration.setIncludeResourceBundles(new ArrayList<>());
+ }
+ configuration.getIncludeResourceBundles().add(name);
+ }
+
+ @Override
public void initializeAtBuildTime(final String... classes) {
if (configuration.getInitializeAtBuildTime() == null) {
configuration.setInitializeAtBuildTime(new ArrayList<>());
@@ -132,6 +139,16 @@
}
@Override
+ public void initializeAtRunTime(final String... classes) {
+ if (configuration.getInitializeAtRunTime() == null) {
+ configuration.setInitializeAtRunTime(new ArrayList<>());
+ }
+ configuration.getInitializeAtRunTime().addAll(Stream.of(classes)
+ .filter(it -> !configuration.getInitializeAtBuildTime().contains(it))
+ .collect(toList()));
+ }
+
+ @Override
public String getProperty(final String key) {
return extensionProperties == null ? null : extensionProperties.get(key);
}
@@ -167,11 +184,11 @@
public Optional<Predicate<String>> createPredicate(final String property, final ArthurExtension.PredicateType type) {
return ofNullable(getProperty(property))
.flatMap(ex -> Stream.of(ex.split(","))
- .map(String::trim)
- .filter(it -> !it.isEmpty())
- .map(it -> of((Predicate<String>) n -> type.test(it, n)))
- .reduce(Optional.<Predicate<String>>empty(),
- (opt, p) -> opt.map(e -> of(e.or(p.orElseThrow(IllegalArgumentException::new)))).orElse(p)));
+ .map(String::trim)
+ .filter(it -> !it.isEmpty())
+ .map(it -> of((Predicate<String>) n -> type.test(it, n)))
+ .reduce(Optional.<Predicate<String>>empty(),
+ (opt, p) -> opt.map(e -> of(e.or(p.orElseThrow(IllegalArgumentException::new)))).orElse(p)));
}
@Override
@@ -179,17 +196,22 @@
final Optional<Predicate<String>> includes = createPredicate(propertyBase + "includes", type);
final Optional<Predicate<String>> excludes = createPredicate(propertyBase + "excludes", type);
return n -> {
- if (includes.isPresent()) {
+ final boolean hasInclude = includes.isPresent();
+ if (hasInclude) {
if (includes.orElseThrow(IllegalStateException::new).test(n)) {
return true;
}
}
- if (excludes.isPresent()) {
+ final boolean hasExclude = excludes.isPresent();
+ if (hasExclude) {
if (excludes.orElseThrow(IllegalStateException::new).test(n)) {
return false;
}
}
- return !excludes.isPresent() && !includes.isPresent();
+ if (hasExclude && !hasInclude) {
+ return true;
+ }
+ return !hasExclude && !hasInclude;
};
}
diff --git a/arthur-maven-plugin/src/main/java/org/apache/geronimo/arthur/maven/mojo/NativeImageMojo.java b/arthur-maven-plugin/src/main/java/org/apache/geronimo/arthur/maven/mojo/NativeImageMojo.java
index 30dc0ee..1f365be 100644
--- a/arthur-maven-plugin/src/main/java/org/apache/geronimo/arthur/maven/mojo/NativeImageMojo.java
+++ b/arthur-maven-plugin/src/main/java/org/apache/geronimo/arthur/maven/mojo/NativeImageMojo.java
@@ -16,43 +16,7 @@
*/
package org.apache.geronimo.arthur.maven.mojo;
-import static java.lang.ClassLoader.getSystemClassLoader;
-import static java.util.Optional.ofNullable;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
-import static java.util.stream.Collectors.toSet;
-import static lombok.AccessLevel.PROTECTED;
-import static org.apache.maven.plugins.annotations.LifecyclePhase.PACKAGE;
-import static org.apache.maven.plugins.annotations.ResolutionScope.TEST;
-import static org.apache.xbean.finder.archive.ClasspathArchive.archive;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import javax.json.bind.Jsonb;
-import javax.json.bind.JsonbBuilder;
-import javax.json.bind.JsonbConfig;
-import javax.json.bind.config.PropertyOrderStrategy;
-
+import lombok.Getter;
import org.apache.geronimo.arthur.impl.nativeimage.ArthurNativeImageConfiguration;
import org.apache.geronimo.arthur.impl.nativeimage.ArthurNativeImageExecutor;
import org.apache.geronimo.arthur.impl.nativeimage.generator.extension.AnnotationExtension;
@@ -77,11 +41,50 @@
import org.apache.maven.project.ProjectDependenciesResolver;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.archive.Archive;
import org.apache.xbean.finder.archive.CompositeArchive;
+import org.apache.xbean.finder.archive.FilteredArchive;
+import org.apache.xbean.finder.filter.Filter;
+import org.apache.xbean.finder.filter.Filters;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.graph.DependencyVisitor;
-import lombok.Getter;
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.config.PropertyOrderStrategy;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import static java.lang.ClassLoader.getSystemClassLoader;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toSet;
+import static lombok.AccessLevel.PROTECTED;
+import static org.apache.maven.plugins.annotations.LifecyclePhase.PACKAGE;
+import static org.apache.maven.plugins.annotations.ResolutionScope.TEST;
+import static org.apache.xbean.finder.archive.ClasspathArchive.archive;
/**
* Generates a native binary from current project.
@@ -280,6 +283,12 @@
private List<String> excludedArtifacts;
/**
+ * Classes or packages (startsWith is used to test entries).
+ */
+ @Parameter(property = "arthur.scanningClassesOrPackagesExcludes")
+ private List<String> scanningClassesOrPackagesExcludes;
+
+ /**
* groupId:artifactId list of ignored artifact during the scanning phase.
* Compared to `excludedArtifacts`, it keeps the jar in the scanning/extension classloader
* but it does not enable to find any annotation in it.
@@ -421,7 +430,8 @@
.withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL))) {
thread.setContextClassLoader(loader);
final Predicate<Artifact> scanningFilter = createScanningFilter();
- final AnnotationFinder finder = new AnnotationFinder(new CompositeArchive(classpathEntries.entrySet().stream()
+ final Function<Archive, Archive> archiveProcessor = createArchiveFilter();
+ final AnnotationFinder finder = new AnnotationFinder(archiveProcessor.apply(new CompositeArchive(classpathEntries.entrySet().stream()
.filter(e -> scanningFilter.test(e.getKey()))
.map(Map.Entry::getValue)
.map(path -> {
@@ -431,7 +441,7 @@
throw new IllegalStateException(e);
}
})
- .collect(toList())));
+ .collect(toList()))));
final AtomicBoolean finderLinked = new AtomicBoolean();
MavenArthurExtension.with(
reflections, resources, bundles, dynamicProxies,
@@ -439,6 +449,7 @@
ArthurNativeImageExecutor.ExecutorConfiguration.builder()
.jsonSerializer(jsonb::toJson)
.annotatedClassFinder(finder::findAnnotatedClasses)
+ .annotatedFieldFinder(finder::findAnnotatedFields)
.annotatedMethodFinder(finder::findAnnotatedMethods)
.extensionProperties(getExtensionProperties())
.implementationFinder(p -> {
@@ -490,6 +501,19 @@
}
}
+ private Function<Archive, Archive> createArchiveFilter() {
+ if (scanningClassesOrPackagesExcludes == null || scanningClassesOrPackagesExcludes.isEmpty()) {
+ return Function.identity();
+ }
+ final Filter filter = Filters.invert(Filters.prefixes(
+ scanningClassesOrPackagesExcludes.stream()
+ .map(String::trim)
+ .filter(it -> !it.isEmpty())
+ .distinct()
+ .toArray(String[]::new)));
+ return a -> new FilteredArchive(a, filter);
+ }
+
private Map<String, String> getExtensionProperties() {
final Map<String, String> props = extensionProperties == null ? new HashMap<>() : new HashMap<>(extensionProperties);
props.putIfAbsent("classes", project.getBuild().getOutputDirectory());
diff --git a/arthur-spi/src/main/java/org/apache/geronimo/arthur/spi/ArthurExtension.java b/arthur-spi/src/main/java/org/apache/geronimo/arthur/spi/ArthurExtension.java
index 5a7eb78..249d6cd 100644
--- a/arthur-spi/src/main/java/org/apache/geronimo/arthur/spi/ArthurExtension.java
+++ b/arthur-spi/src/main/java/org/apache/geronimo/arthur/spi/ArthurExtension.java
@@ -119,12 +119,23 @@
void enableAllCharsets();
/**
+ * Includes resource bundle (directly from the CLI since graal has some bugs on that as of today).
+ */
+ void includeResourceBundle(String name);
+
+ /**
* Forces classes to be initialized during the build and not at run time.
* @param classes classes to initialize.
*/
void initializeAtBuildTime(String... classes);
/**
+ * Forces classes to be initialized during the run only, not the build.
+ * @param classes classes to initialize.
+ */
+ void initializeAtRunTime(String... classes);
+
+ /**
* Retrieve a context property, used to configured an extension.
* @param key the key to read.
* @return the value or null if missing.
diff --git a/documentation/src/content/knights.adoc b/documentation/src/content/knights.adoc
index e276d79..7e42e78 100644
--- a/documentation/src/content/knights.adoc
+++ b/documentation/src/content/knights.adoc
@@ -24,6 +24,7 @@
- link:jsch-knight.html[JSch]: it contains some end user API integrated with built-in extensions to simplify application graal-ification,
- link:winegrower-knight.html[Winegrower]: Apache winegrower (Cloud OSGi runtime) support.
- link:openwebbeans-knight.html[OpenWebBeans]: Apache OpenWebBeans (CDI SE runtime) support.
+- link:slf4j-knight.html[SLF4J]: SLF4J (JUL) integration.
== Configure a Knight in Arthur Maven plugin
diff --git a/documentation/src/content/openwebbeans-knight.adoc b/documentation/src/content/openwebbeans-knight.adoc
index 222dace..e1983ea 100644
--- a/documentation/src/content/openwebbeans-knight.adoc
+++ b/documentation/src/content/openwebbeans-knight.adoc
@@ -91,7 +91,7 @@
<plugin>
<groupId>org.apache.geronimo.arthur</groupId>
<artifactId>arthur-maven-plugin</artifactId>
- <version>1.0.0-SNAPSHOT</version>
+ <version>${arthur.version}</version>
<configuration>
<main>org.company.cdi.Main</main>
<graalExtensions>
@@ -114,6 +114,7 @@
a|`extension.openwebbeans.container.se.properties`|Properties|CDI SE properties.
a|`extension.openwebbeans.container.se.services`|Properties|OpenWebBeans services as properties (SPI=IMPL).
a|`extension.openwebbeans.container.se.classes`|String list|Bean classes to register.
+a|`extension.openwebbeans.extension.excludes`|String list|Extension fully qualified name prefixes to exclude undesired extensions.
|===
---
diff --git a/documentation/src/content/slf4j-knight.adoc b/documentation/src/content/slf4j-knight.adoc
new file mode 100644
index 0000000..bef4607
--- /dev/null
+++ b/documentation/src/content/slf4j-knight.adoc
@@ -0,0 +1,57 @@
+////
+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.
+////
+= Arthur SLF4J Knights
+
+Arthur Slf4j knight is responsible to force some SLF4j and SLF4J-JUL class initialization at build time.
+
+== Coordinates
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.geronimo.arthur.knights</groupId>
+ <artifactId>slf4j-knight</artifactId>
+ <version>${arthur.version}</version>
+</dependency>
+----
+
+== Usage
+
+The `slf4j-knight` handles some build time initialization to make `slf4j-jdk14` working with graalvm compilation.
+
+
+You must register the slf4j knight in `arthur-maven-plugin`:
+
+
+[source,xml]
+----
+<plugin>
+ <groupId>org.apache.geronimo.arthur</groupId>
+ <artifactId>arthur-maven-plugin</artifactId>
+ <version>${arthur.version}</version>
+ <configuration>
+ <main>org.company.Main</main>
+ <graalExtensions>
+ <graalExtension>slf4j</graalExtension>
+ </graalExtensions>
+ </configuration>
+</plugin>
+----
+
+---
+
+Previous: link:knights.html[Knights]
diff --git a/documentation/src/content/winegrower-knight.adoc b/documentation/src/content/winegrower-knight.adoc
index 82bea1a..ddd5aed 100644
--- a/documentation/src/content/winegrower-knight.adoc
+++ b/documentation/src/content/winegrower-knight.adoc
@@ -149,7 +149,7 @@
<plugin>
<groupId>org.apache.geronimo.arthur</groupId>
<artifactId>arthur-maven-plugin</artifactId>
- <version>1.0.0-SNAPSHOT</version>
+ <version>${arthur.version}</version>
<configuration>
<main>org.company.osgi.ScrMain</main>
<graalExtensions>
diff --git a/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/OpenWebBeansExtension.java b/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/OpenWebBeansExtension.java
index 68484bb..a1cc15b 100644
--- a/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/OpenWebBeansExtension.java
+++ b/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/OpenWebBeansExtension.java
@@ -19,9 +19,11 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.geronimo.arthur.spi.ArthurExtension;
import org.apache.geronimo.arthur.spi.model.ClassReflectionModel;
+import org.apache.geronimo.arthur.spi.model.DynamicProxyModel;
import org.apache.geronimo.arthur.spi.model.ResourceModel;
import org.apache.openwebbeans.se.CDISeScannerService;
import org.apache.openwebbeans.se.PreScannedCDISeScannerService;
+import org.apache.webbeans.component.AbstractProducerBean;
import org.apache.webbeans.component.InjectionTargetBean;
import org.apache.webbeans.component.ManagedBean;
import org.apache.webbeans.config.OpenWebBeansConfiguration;
@@ -39,6 +41,10 @@
import org.apache.webbeans.intercept.SessionScopedBeanInterceptorHandler;
import org.apache.webbeans.lifecycle.StandaloneLifeCycle;
import org.apache.webbeans.logger.WebBeansLoggerFacade;
+import org.apache.webbeans.portable.BaseProducerProducer;
+import org.apache.webbeans.portable.ProducerFieldProducer;
+import org.apache.webbeans.portable.ProducerMethodProducer;
+import org.apache.webbeans.portable.events.ExtensionLoader;
import org.apache.webbeans.service.ClassLoaderProxyService;
import org.apache.webbeans.service.DefaultLoaderService;
import org.apache.webbeans.spi.ApplicationBoundaryService;
@@ -68,6 +74,7 @@
import javax.enterprise.inject.Default;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
+import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.Bean;
import javax.inject.Qualifier;
import javax.interceptor.InterceptorBinding;
@@ -75,11 +82,15 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
+import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
@@ -88,9 +99,11 @@
import java.util.regex.Pattern;
import java.util.stream.Stream;
+import static java.util.Collections.singleton;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
@Slf4j
public class OpenWebBeansExtension implements ArthurExtension {
@@ -102,13 +115,15 @@
final WebBeansContext webBeansContext = WebBeansContext.currentInstance();
final BeanManagerImpl beanManager = webBeansContext.getBeanManagerImpl();
final Set<Bean<?>> beans = beanManager.getBeans();
+ final Predicate<String> classFilter = context.createIncludesExcludes(
+ "extension.openwebbeans.classes.filter.", PredicateType.STARTS_WITH);
// 1. capture all proxies
- dumpProxies(context, webBeansContext, beans);
+ dumpProxies(context, webBeansContext, beans, classFilter);
// 2. register all classes which will require reflection + proxies
- final String beanClassesList = registerBeansForReflection(context, beans);
- getProxies(webBeansContext).keySet().forEach(name -> {
+ final String beanClassesList = registerBeansForReflection(context, beans, classFilter);
+ getProxies(webBeansContext).keySet().stream().sorted().forEach(name -> {
final ClassReflectionModel model = new ClassReflectionModel();
model.setName(name);
model.setAllDeclaredConstructors(true);
@@ -148,36 +163,144 @@
model.setAllDeclaredConstructors(true);
context.register(model);
});
- // 4.3 annotations
+ // 5 annotations
+ final Collection<Class<?>> customAnnotations = Stream.concat(
+ context.findAnnotatedClasses(Qualifier.class).stream(),
+ context.findAnnotatedClasses(NormalScope.class).stream())
+ .collect(toList());
Stream.concat(Stream.concat(Stream.of(
Initialized.class, Destroyed.class, NormalScope.class, ApplicationScoped.class, Default.class,
Dependent.class, ConversationScoped.class, RequestScoped.class, Observes.class, ObservesAsync.class,
Qualifier.class, InterceptorBinding.class),
beanManager.getAdditionalQualifiers().stream()),
- Stream.concat(
- context.findAnnotatedClasses(Qualifier.class).stream(),
- context.findAnnotatedClasses(NormalScope.class).stream()))
+ customAnnotations.stream())
.distinct()
+ .map(Class::getName)
+ .sorted()
.forEach(clazz -> {
final ClassReflectionModel model = new ClassReflectionModel();
- model.setName(clazz.getName());
+ model.setName(clazz);
+ model.setAllDeclaredMethods(true);
+ context.register(model);
+ });
+ customAnnotations.stream() // DefaultAnnotation.of
+ .filter(it -> !it.getName().startsWith("javax."))
+ .map(Class::getName)
+ .sorted()
+ .map(it -> {
+ final DynamicProxyModel proxyModel = new DynamicProxyModel();
+ proxyModel.setClasses(singleton(it));
+ return proxyModel;
+ })
+ .forEach(context::register);
+
+ // 6 extensions - normally taken by graalvm service loader but we need a bit more reflection
+ final ExtensionLoader extensionLoader = webBeansContext.getExtensionLoader();
+ try {
+ final Field extensionClasses = ExtensionLoader.class.getDeclaredField("extensionClasses");
+ if (!extensionClasses.isAccessible()) {
+ extensionClasses.setAccessible(true);
+ }
+ final Predicate<String> extensionFilter = context.createPredicate("extension.openwebbeans.extension.excludes", PredicateType.STARTS_WITH)
+ .map(Predicate::negate)
+ .orElseGet(() -> n -> true);
+ final Set<Class<?>> classes = (Set<Class<?>>) extensionClasses.get(extensionLoader);
+ classes.stream()
+ .filter(it -> extensionFilter.test(it.getName()))
+ .flatMap(this::hierarchy)
+ .distinct()
+ .map(Class::getName)
+ .filter(classFilter)
+ .sorted()
+ .forEach(clazz -> {
+ final ClassReflectionModel model = new ClassReflectionModel();
+ model.setName(clazz);
+ model.setAllDeclaredConstructors(true);
+ model.setAllDeclaredMethods(true);
+ model.setAllDeclaredMethods(true);
+ context.register(model);
+ });
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new IllegalStateException("Incompatible OpenWebBeans version", e);
+ }
+
+ // 7. producer types must be reflection friendly
+ findProducedClasses(beans)
+ .map(Class::getName)
+ .sorted()
+ .forEach(name -> {
+ final ClassReflectionModel model = new ClassReflectionModel();
+ model.setName(name);
+ model.setAllDeclaredConstructors(true);
+ model.setAllDeclaredFields(true);
model.setAllDeclaredMethods(true);
context.register(model);
});
- // enforce some build time init for annotations and some specific classes
+ // 8. enforce some build time init for annotations and some specific classes
context.initializeAtBuildTime(
Reception.class.getName(),
TransactionPhase.class.getName(),
DefaultSingletonService.class.getName(),
WebBeansLoggerFacade.class.getName());
+ try { // openwebbeans-slf4j is an optional module
+ final Class<?> logger = context.loadClass("org.apache.openwebbeans.slf4j.Slf4jLogger");
+ context.initializeAtBuildTime(logger.getName());
+ } catch (final RuntimeException e) {
+ // ignore, not there
+ }
- // we add the resource bundle in the feature not here
+ // 9. we add the resource bundle + the bundle as resource for some extensions (thank you JUL)
+ context.includeResourceBundle("openwebbeans/Messages");
+ final ResourceModel resourceModel = new ResourceModel();
+ resourceModel.setPattern("openwebbeans/Messages\\.properties");
+ context.register(resourceModel);
+
+ // 10. OWB creates proxies on TypeVariable (generics checks) so enable it
+ final DynamicProxyModel typeVariableProxyModel = new DynamicProxyModel();
+ typeVariableProxyModel.setClasses(singleton(TypeVariable.class.getName()));
+ context.register(typeVariableProxyModel);
} finally {
System.setProperties(original);
}
}
+ private Stream<Class<?>> findProducedClasses(final Set<Bean<?>> beans) {
+ return beans.stream()
+ .filter(it -> AbstractProducerBean.class.isInstance(it) && BaseProducerProducer.class.isInstance(AbstractProducerBean.class.cast(it).getProducer()))
+ .flatMap(it -> {
+ final BaseProducerProducer bpp = BaseProducerProducer.class.cast(AbstractProducerBean.class.cast(it).getProducer());
+ final Collection<Type> types = it.getTypes();
+ if (ProducerMethodProducer.class.isInstance(bpp)) {
+ return concat(types, get(bpp, "producerMethod", Method.class).getReturnType());
+ }
+ if (ProducerFieldProducer.class.isInstance(bpp)) {
+ return concat(types, get(bpp, "producerField", AnnotatedField.class).getJavaMember().getType());
+ }
+ return null;
+ })
+ .filter(Objects::nonNull);
+ }
+
+ private Stream<Class<?>> concat(final Collection<Type> types, final Class<?> type) {
+ return Stream.concat(Stream.of(type), types.stream().filter(Class.class::isInstance).map(Class.class::cast))
+ .distinct() // if types includes type, avoids to do twice the hierarchy
+ .flatMap(this::hierarchy)
+ .distinct();
+ }
+
+ private <T> T get(final BaseProducerProducer p, final String field, final Class<T> type) {
+ try {
+ final Field declaredField = p.getClass().getDeclaredField(field);
+ if (!declaredField.isAccessible()) {
+ declaredField.setAccessible(true);
+ }
+ return type.cast(declaredField.get(p));
+ } catch (final Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
private Stream<? extends Class<?>> findServices(final Properties properties) {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
return properties.stringPropertyNames().stream()
@@ -196,8 +319,7 @@
.filter(Objects::nonNull);
}
- private String registerBeansForReflection(final Context context, final Set<Bean<?>> beans) {
- final Predicate<String> classFilter = context.createIncludesExcludes("extension.openwebbeans.classes.filter.", PredicateType.STARTS_WITH);
+ private String registerBeansForReflection(final Context context, final Set<Bean<?>> beans, final Predicate<String> classFilter) {
final boolean includeClassResources = Boolean.parseBoolean(context.getProperty("extension.openwebbeans.classes.includeAsResources"));
return beans.stream()
.filter(ManagedBean.class::isInstance)
@@ -224,7 +346,8 @@
.collect(joining(","));
}
- private void dumpProxies(final Context context, final WebBeansContext webBeansContext, final Set<Bean<?>> beans) {
+ private void dumpProxies(final Context context, final WebBeansContext webBeansContext, final Set<Bean<?>> beans,
+ final Predicate<String> classFilter) {
// interceptors/decorators
beans.stream()
.filter(InjectionTargetBean.class::isInstance)
@@ -240,10 +363,13 @@
if (proxies.isEmpty()) {
log.info("No proxy found for this application");
} else {
- proxies.forEach((className, bytes) -> {
- context.registerGeneratedClass(className, bytes);
- log.info("Registered proxy '{}'", className);
- });
+ proxies.entrySet().stream()
+ .filter(it -> classFilter.test(it.getKey()))
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(e -> {
+ context.registerGeneratedClass(e.getKey(), e.getValue());
+ log.info("Registered proxy '{}'", e.getKey());
+ });
}
}
@@ -254,7 +380,9 @@
private Stream<Class<?>> hierarchy(final Class<?> it) {
return it == null || it == Object.class ?
Stream.empty() :
- Stream.concat(Stream.of(it), hierarchy(it.getSuperclass()));
+ Stream.concat(
+ Stream.concat(Stream.of(it), hierarchy(it.getSuperclass())),
+ Stream.of(it.getInterfaces()).flatMap(this::hierarchy));
}
private Properties initProperties(final Context context, final OpenWebBeansConfiguration configuration,
@@ -294,6 +422,10 @@
properties.setProperty("org.apache.webbeans.spi.DefiningClassService", runtime ?
"org.apache.webbeans.service.ClassLoaderProxyService$LoadFirst" :
"org.apache.webbeans.service.ClassLoaderProxyService$Spy");
+ if (runtime) {
+ properties.setProperty(
+ properties.getProperty("org.apache.webbeans.spi.DefiningClassService") + ".skipPackages", "true");
+ }
properties.setProperty("org.apache.webbeans.spi.ApplicationBoundaryService",
"org.apache.webbeans.corespi.se.SimpleApplicationBoundaryService");
}
diff --git a/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/feature/OpenWebBeansFeature.java b/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/feature/OpenWebBeansFeature.java
index d15a1ef..5e09559 100644
--- a/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/feature/OpenWebBeansFeature.java
+++ b/knights/openwebbeans-knight/src/main/java/org/apache/geronimo/arthur/knight/openwebbeans/feature/OpenWebBeansFeature.java
@@ -17,14 +17,12 @@
package org.apache.geronimo.arthur.knight.openwebbeans.feature;
import com.oracle.svm.core.annotate.AutomaticFeature;
-import com.oracle.svm.core.jdk.LocalizationFeature;
import com.oracle.svm.core.jdk.Resources;
import com.oracle.svm.core.option.HostedOptionKey;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionDescriptor;
import org.graalvm.compiler.options.OptionDescriptors;
import org.graalvm.compiler.options.OptionType;
-import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import java.io.IOException;
@@ -68,7 +66,6 @@
if (Options.OpenWebBeansProperties.hasBeenSet()) {
register(Options.OpenWebBeansProperties.getValue(), "META-INF/openwebbeans/openwebbeans.properties");
}
- ImageSingletons.lookup(LocalizationFeature.class).addBundleToCache("openwebbeans/Messages");
}
private void register(final String path, final String resource) {
diff --git a/knights/pom.xml b/knights/pom.xml
index 89e31c1..1264fb1 100644
--- a/knights/pom.xml
+++ b/knights/pom.xml
@@ -32,6 +32,7 @@
<module>jsch-knight</module>
<module>winegrower-knight</module>
<module>openwebbeans-knight</module>
+ <module>slf4j-knight</module>
</modules>
<dependencies>
diff --git a/knights/slf4j-knight/pom.xml b/knights/slf4j-knight/pom.xml
new file mode 100644
index 0000000..0153e34
--- /dev/null
+++ b/knights/slf4j-knight/pom.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>knights</artifactId>
+ <groupId>org.apache.geronimo.arthur.knights</groupId>
+ <version>1.0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>slf4j-knight</artifactId>
+ <name>Arthur :: Knights :: Slf4j</name>
+
+ <properties>
+ <geronimo-arthur.shortname>knight.slf4j</geronimo-arthur.shortname>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.arthur</groupId>
+ <artifactId>arthur-spi</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/knights/slf4j-knight/src/main/java/org/apache/geronimo/arthur/knight/slf4j/Slf4jExtension.java b/knights/slf4j-knight/src/main/java/org/apache/geronimo/arthur/knight/slf4j/Slf4jExtension.java
new file mode 100644
index 0000000..0534542
--- /dev/null
+++ b/knights/slf4j-knight/src/main/java/org/apache/geronimo/arthur/knight/slf4j/Slf4jExtension.java
@@ -0,0 +1,35 @@
+/*
+ * 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.geronimo.arthur.knight.slf4j;
+
+import org.apache.geronimo.arthur.spi.ArthurExtension;
+
+public class Slf4jExtension implements ArthurExtension {
+ @Override
+ public void execute(final Context context) {
+ // use strings cause maven uses slf4j and we don't want NoClassDefFoundError
+ context.initializeAtBuildTime(
+ "org.slf4j.impl.JDK14LoggerAdapter",
+ "org.slf4j.impl.StaticLoggerBinder",
+ "org.slf4j.LoggerFactory");
+ }
+
+ @Override
+ public int order() {
+ return 10;
+ }
+}
diff --git a/knights/slf4j-knight/src/main/resources/META-INF/services/org.apache.geronimo.arthur.spi.ArthurExtension b/knights/slf4j-knight/src/main/resources/META-INF/services/org.apache.geronimo.arthur.spi.ArthurExtension
new file mode 100644
index 0000000..73c79b2
--- /dev/null
+++ b/knights/slf4j-knight/src/main/resources/META-INF/services/org.apache.geronimo.arthur.spi.ArthurExtension
@@ -0,0 +1 @@
+org.apache.geronimo.arthur.knight.slf4j.Slf4jExtension