[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