You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gn...@apache.org on 2019/06/05 21:01:20 UTC

[camel] 01/02: First pass at a generated endpoint DSL

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch endpoint-dsl
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 5c684ea3774a5c2a0cc8388bb19a4d83ae180977
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Jun 5 10:50:26 2019 +0200

    First pass at a generated endpoint DSL
---
 components/camel-blueprint/pom.xml                 |   1 +
 components/camel-spring/pom.xml                    |   1 +
 .../camel/component/web3j/Web3jConfiguration.java  |   2 +-
 components/pom.xml                                 |   1 +
 .../model/endpoint/EndpointConfiguration.java      |   5 +
 .../camel/maven/packaging/EndpointDslMojo.java     | 631 +++++++++++++++
 .../camel/maven/packaging/generics/ClassUtil.java  | 218 +++++
 .../maven/packaging/generics/GenericsUtil.java     | 880 +++++++++++++++++++++
 .../generics/OwbGenericArrayTypeImpl.java          |  64 ++
 .../generics/OwbParametrizedTypeImpl.java          | 125 +++
 .../packaging/generics/OwbTypeVariableImpl.java    | 182 +++++
 .../packaging/generics/OwbWildcardTypeImpl.java    |  82 ++
 .../maven/packaging/model/EndpointOptionModel.java |   9 +
 .../camel/maven/packaging/srcgen/GenericType.java  |   2 +-
 .../camel/maven/packaging/srcgen/JavaClass.java    |  39 +-
 15 files changed, 2237 insertions(+), 5 deletions(-)

diff --git a/components/camel-blueprint/pom.xml b/components/camel-blueprint/pom.xml
index 3179463..cd197ec 100644
--- a/components/camel-blueprint/pom.xml
+++ b/components/camel-blueprint/pom.xml
@@ -357,6 +357,7 @@
                                                 <include name="org/apache/camel/LoggingLevel.java"/>
                                                 <include name="org/apache/camel/ManagementStatisticsLevel.java"/>
                                                 <include name="**/package-info.java"/>
+                                                <exclude name="org/apache/camel/model/endpoint/*.java"/>
                                             </fileset>
                                             <fileset dir="${basedir}/../../core/camel-util/src/main/java">
                                                 <include
diff --git a/components/camel-spring/pom.xml b/components/camel-spring/pom.xml
index 2a873b7..8ba2a98 100644
--- a/components/camel-spring/pom.xml
+++ b/components/camel-spring/pom.xml
@@ -386,6 +386,7 @@
                                         <include name="org/apache/camel/ShutdownRunningTask.java"/>
                                         <include name="org/apache/camel/WaitForTaskToComplete.java"/>
                                         <include name="org/apache/camel/package-info.java"/>
+                                        <exclude name="org/apache/camel/model/endpoint/*.java"/>
                                     </fileset>
                                     <fileset dir="${basedir}/../../core/camel-util/src/main/java">
                                         <include name="org/apache/camel/concurrent/ThreadPoolRejectedPolicy.java"/>
diff --git a/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java b/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java
index 70db1be..d6a901e 100644
--- a/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java
+++ b/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java
@@ -48,7 +48,7 @@ public class Web3jConfiguration implements Cloneable {
     @UriParam(label = "producer")
     private String address;
 
-    @UriParam(label = "common", javaType = "String")
+    @UriParam(label = "common", javaType = "java.lang.String")
     private List<String> topics;
 
     @UriParam(label = "producer")
diff --git a/components/pom.xml b/components/pom.xml
index ded9f83..97da906 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -368,6 +368,7 @@
                             <goal>prepare-spring-boot-starter</goal>
                             <goal>prepare-spring-boot-auto-configuration</goal>
                             <goal>generate-legal</goal>
+                            <goal>generate-endpoint-dsl</goal>
                         </goals>
                         <phase>prepare-package</phase>
                     </execution>
diff --git a/core/camel-core/src/main/java/org/apache/camel/model/endpoint/EndpointConfiguration.java b/core/camel-core/src/main/java/org/apache/camel/model/endpoint/EndpointConfiguration.java
new file mode 100644
index 0000000..a8b85bf
--- /dev/null
+++ b/core/camel-core/src/main/java/org/apache/camel/model/endpoint/EndpointConfiguration.java
@@ -0,0 +1,5 @@
+package org.apache.camel.model.endpoint;
+
+public class EndpointConfiguration {
+
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
new file mode 100644
index 0000000..c6730e1
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
@@ -0,0 +1,631 @@
+/*
+ * 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.camel.maven.packaging;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import javax.annotation.Generated;
+
+import org.apache.camel.maven.packaging.generics.GenericsUtil;
+import org.apache.camel.maven.packaging.model.ComponentModel;
+import org.apache.camel.maven.packaging.model.ComponentOptionModel;
+import org.apache.camel.maven.packaging.model.EndpointOptionModel;
+import org.apache.camel.maven.packaging.srcgen.GenericType;
+import org.apache.camel.maven.packaging.srcgen.GenericType.BoundType;
+import org.apache.camel.maven.packaging.srcgen.JavaClass;
+import org.apache.camel.maven.packaging.srcgen.Property;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.jboss.forge.roaster.model.util.Strings;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.springframework.boot.liquibase.SpringPackageScanClassResolver;
+
+import static org.apache.camel.maven.packaging.AbstractGeneratorMojo.updateResource;
+import static org.apache.camel.maven.packaging.JSonSchemaHelper.getSafeValue;
+import static org.apache.camel.maven.packaging.PackageHelper.findCamelCoreDirectory;
+import static org.apache.camel.maven.packaging.PackageHelper.loadText;
+
+/**
+ * Generate Spring Boot auto configuration files for Camel components and data
+ * formats.
+ */
+@Mojo(name = "generate-endpoint-dsl", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, defaultPhase = LifecyclePhase.PROCESS_CLASSES)
+public class EndpointDslMojo extends AbstractMojo {
+
+    private static final Map<String, String> PRIMITIVEMAP;
+    private static final Map<String, Class> PRIMITIVE_CLASSES = new HashMap<>();
+
+    static {
+        PRIMITIVEMAP = new HashMap<>();
+        PRIMITIVEMAP.put("boolean", "java.lang.Boolean");
+        PRIMITIVEMAP.put("char", "java.lang.Character");
+        PRIMITIVEMAP.put("long", "java.lang.Long");
+        PRIMITIVEMAP.put("int", "java.lang.Integer");
+        PRIMITIVEMAP.put("integer", "java.lang.Integer");
+        PRIMITIVEMAP.put("byte", "java.lang.Byte");
+        PRIMITIVEMAP.put("short", "java.lang.Short");
+        PRIMITIVEMAP.put("double", "java.lang.Double");
+        PRIMITIVEMAP.put("float", "java.lang.Float");
+
+        PRIMITIVE_CLASSES.put("int", int.class);
+        PRIMITIVE_CLASSES.put("short", short.class);
+        PRIMITIVE_CLASSES.put("long", long.class);
+        PRIMITIVE_CLASSES.put("byte", byte.class);
+        PRIMITIVE_CLASSES.put("char", char.class);
+        PRIMITIVE_CLASSES.put("float", float.class);
+        PRIMITIVE_CLASSES.put("double", double.class);
+        PRIMITIVE_CLASSES.put("boolean", boolean.class);
+        PRIMITIVE_CLASSES.put("void", void.class);
+    }
+
+    private static final String[] IGNORE_MODULES = {/* Non-standard -> */ };
+
+    /**
+     * The maven project.
+     */
+    @Parameter(property = "project", required = true, readonly = true)
+    protected MavenProject project;
+
+    /**
+     * The project build directory
+     */
+    @Parameter(defaultValue = "${project.build.directory}")
+    protected File buildDir;
+
+    /**
+     * The base directory
+     */
+    @Parameter(defaultValue = "${basedir}")
+    protected File baseDir;
+
+    DynamicClassLoader projectClassLoader;
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        // Do not generate code for ignored module
+        if (Arrays.asList(IGNORE_MODULES).contains(project.getArtifactId())) {
+            getLog().info("Component auto-configuration will not be created: component contained in the ignore list");
+            return;
+        }
+
+        executeAll();
+    }
+
+    private void executeAll() throws MojoExecutionException, MojoFailureException {
+        Map<File, Supplier<String>> files = PackageHelper.findJsonFiles(buildDir, p -> p.isDirectory() || p.getName().endsWith(".json")).stream()
+            .collect(Collectors.toMap(Function.identity(), s -> cache(() -> loadJson(s))));
+
+        executeComponent(files);
+    }
+
+    private static String loadJson(File file) {
+        try (InputStream is = new FileInputStream(file)) {
+            return loadText(is);
+        } catch (IOException e) {
+            throw new IOError(e);
+        }
+    }
+
+    private static <T> Supplier<T> cache(Supplier<T> supplier) {
+        return new Supplier<T>() {
+            T value;
+
+            @Override
+            public T get() {
+                if (value == null) {
+                    value = supplier.get();
+                }
+                return value;
+            }
+        };
+    }
+
+    private void executeComponent(Map<File, Supplier<String>> jsonFiles) throws MojoExecutionException, MojoFailureException {
+        // find the component names
+        Set<String> componentNames = new TreeSet<>();
+        findComponentNames(buildDir, componentNames);
+
+        // create auto configuration for the components
+        if (!componentNames.isEmpty()) {
+            getLog().debug("Found " + componentNames.size() + " components");
+
+            List<ComponentModel> allModels = new LinkedList<>();
+            for (String componentName : componentNames) {
+                String json = loadComponentJson(jsonFiles, componentName);
+                if (json != null) {
+                    ComponentModel model = generateComponentModel(componentName, json);
+                    allModels.add(model);
+                }
+            }
+
+            // Group the models by implementing classes
+            Map<String, List<ComponentModel>> grModels = allModels.stream().collect(Collectors.groupingBy(ComponentModel::getJavaType));
+            for (String componentClass : grModels.keySet()) {
+                List<ComponentModel> compModels = grModels.get(componentClass);
+                ComponentModel model = compModels.get(0); // They should be
+                                                          // equivalent
+                List<String> aliases = compModels.stream().map(ComponentModel::getScheme).sorted().collect(Collectors.toList());
+
+                 String pkg = "org.apache.camel.model.endpoint";
+
+                String overrideComponentName = null;
+                if (aliases.size() > 1) {
+                    // determine component name when there are multiple ones
+                    overrideComponentName = model.getArtifactId().replace("camel-", "");
+                }
+
+                createEndpointDsl(pkg, model, overrideComponentName);
+            }
+        }
+    }
+
+    private void createEndpointDsl(String packageName, ComponentModel model, String overrideComponentName) throws MojoFailureException {
+        int pos = model.getJavaType().lastIndexOf(".");
+        String name = model.getJavaType().substring(pos + 1);
+        name = name.replace("Component", "Endpoint");
+
+        Class<?> realComponentClass = loadClass(model.getJavaType());
+        Class<?> realEndpointClass = findEndpointClass(realComponentClass);
+
+        final JavaClass javaClass = new JavaClass(getProjectClassLoader());
+        javaClass.setPackage(packageName);
+        javaClass.setName(name);
+
+        Map<String, JavaClass> enumClasses = new HashMap<>();
+
+        JavaClass commonClass = javaClass.addNestedType().setPublic().setStatic(true);
+        commonClass.setName(name.replace("Endpoint", "Common"));
+        commonClass.extendSuperType("EndpointConfiguration");
+
+        JavaClass consumerClass = javaClass.addNestedType().setPublic().setStatic(true);
+        consumerClass.setName(name.replace("Endpoint", "Consumer"));
+        consumerClass.extendSuperType(name.replace("Endpoint", "Common"));
+
+        JavaClass producerClass = javaClass.addNestedType().setPublic().setStatic(true);
+        producerClass.setName(name.replace("Endpoint", "Producer"));
+        producerClass.extendSuperType(name.replace("Endpoint", "Common"));
+
+        String doc = "Generated by camel-package-maven-plugin - do not edit this file!";
+        if (!Strings.isBlank(model.getDescription())) {
+            doc = model.getDescription() + "\n\n" + doc;
+        }
+        javaClass.getJavaDoc().setText(doc);
+
+        javaClass.addAnnotation(Generated.class.getName())
+                .setStringValue("value", EndpointDslMojo.class.getName());
+
+        for (EndpointOptionModel option : model.getEndpointOptions()) {
+
+            JavaClass target = commonClass;
+            if (option.getLabel() != null) {
+                if (option.getLabel().contains("producer")) {
+                    target = producerClass;
+                } else if (option.getLabel().contains("consumer")) {
+                    target = consumerClass;
+                }
+            }
+
+            GenericType gtype;
+            try {
+                Field field = findField(realComponentClass, realEndpointClass, option);
+                gtype = new GenericType(GenericsUtil.resolveType(realEndpointClass, field));
+                gtype = getType(javaClass, enumClasses, option.getEnums(), gtype.toString());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+
+            Property prop = target.addProperty(gtype, option.getName());
+            if ("true".equals(option.getDeprecated())) {
+                prop.getField().addAnnotation(Deprecated.class);
+                prop.getAccessor().addAnnotation(Deprecated.class);
+                prop.getMutator().addAnnotation(Deprecated.class);
+            }
+            if (!Strings.isBlank(option.getDescription())) {
+                String desc = option.getDescription();
+                if (!desc.endsWith(".")) {
+                    desc = desc + ".";
+                }
+                desc = desc + " The option is a " + option.getJavaType() + " type.";
+                prop.getField().getJavaDoc().setFullText(desc);
+            }
+        }
+
+        String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java";
+        writeSourceIfChanged(javaClass, fileName, false);
+    }
+
+    private Class<?> findEndpointClass(Class<?> componentClass) {
+        String endpointName = componentClass.getCanonicalName().replaceFirst("Component", "Endpoint");
+        if ("org.apache.camel.component.disruptor.vm.DisruptorVmEndpoint".equals(endpointName)) {
+            endpointName = "org.apache.camel.component.disruptor.DisruptorEndpoint";
+        } else if ("org.apache.camel.component.etcd.EtcdEndpoint".equals(endpointName)) {
+            endpointName = "org.apache.camel.component.etcd.AbstractEtcdPollingEndpoint";
+        } else if ("org.apache.camel.websocket.jsr356.JSR356WebSocketEndpoint".equals(endpointName)) {
+            endpointName = "org.apache.camel.websocket.jsr356.JSR356Endpoint";
+        }
+        return loadClass(endpointName);
+    }
+
+    private Field findField(Class<?> realComponentClass, Class<?> realEndpointClass, EndpointOptionModel option) throws NoSuchFieldException {
+        Field field = null;
+        List<Class<?>> classes = new ArrayList<>();
+        classes.add(realComponentClass);
+        classes.add(realEndpointClass);
+        while (!classes.isEmpty()) {
+            Class cl = classes.remove(0);
+            for (Field f : cl.getDeclaredFields()) {
+                String n = f.getName();
+                UriPath path = f.getAnnotation(UriPath.class);
+                if (path != null && !Strings.isBlank(path.name())) {
+                    n = path.name();
+                }
+                UriParam param = f.getAnnotation(UriParam.class);
+                if (param != null && !Strings.isBlank(param.name())) {
+                    n = param.name();
+                }
+                if (n.equals(option.getName())) {
+                    field = f;
+                    break;
+                }
+                if (f.getType().isAnnotationPresent(UriParams.class)) {
+                    classes.add(f.getType());
+                }
+            }
+            if (field != null) {
+                break;
+            }
+            cl = cl.getSuperclass();
+            if (cl != null) {
+                classes.add(cl);
+            }
+        }
+        if (field == null) {
+            throw new NoSuchFieldException("Could not find field for option " + option.getName());
+        }
+        return field;
+    }
+
+    static boolean isPrimitive(String type) {
+        return PRIMITIVE_CLASSES.containsKey(type);
+    }
+
+    private Class<?> loadClass(String loadClassName) {
+        Class<?> optionClass;
+        String org = loadClassName;
+        while (true) {
+            try {
+                optionClass = getProjectClassLoader().loadClass(loadClassName);
+                break;
+            } catch (ClassNotFoundException e) {
+                int dotIndex = loadClassName.lastIndexOf('.');
+                if (dotIndex == -1) {
+                    throw new IllegalArgumentException(org);
+                } else {
+                    loadClassName = loadClassName.substring(0, dotIndex) + "$" + loadClassName.substring(dotIndex + 1);
+                }
+            }
+        }
+        return optionClass;
+    }
+
+
+    private GenericType getType(JavaClass javaClass, Map<String, JavaClass> enumClasses, String enums, String type) {
+        type = type.trim();
+        // Check if this is an array
+        if (type.endsWith("[]")) {
+            GenericType t = getType(javaClass, enumClasses, enums, type.substring(0, type.length() - 2));
+            return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t);
+        }
+        // Check if this is a generic
+        int genericIndex = type.indexOf('<');
+        if (genericIndex > 0) {
+            if (!type.endsWith(">")) {
+                throw new IllegalArgumentException("Can not load type: " + type);
+            }
+            GenericType base = getType(javaClass, enumClasses, enums, type.substring(0, genericIndex));
+            if (base.getRawClass() == Object.class) {
+                return base;
+            }
+            String[] params = splitParams(type.substring(genericIndex + 1, type.length() - 1));
+            GenericType[] types = new GenericType[params.length];
+            for (int i = 0; i < params.length; i++) {
+                types[i] = getType(javaClass, enumClasses, enums, params[i]);
+            }
+            return new GenericType(base.getRawClass(), types);
+        }
+        // Primitive
+        if (isPrimitive(type)) {
+            return new GenericType(PRIMITIVE_CLASSES.get(type));
+        }
+        // Extends
+        if (type.startsWith("? extends ")) {
+            String raw = type.substring("? extends ".length());
+            return new GenericType(loadClass(raw), BoundType.Extends);
+        }
+        // Super
+        if (type.startsWith("? super ")) {
+            String raw = type.substring("? extends ".length());
+            return new GenericType(loadClass(raw), BoundType.Super);
+        }
+        // Wildcard
+        if (type.equals("?")) {
+            return new GenericType(Object.class, BoundType.Extends);
+        }
+        if (loadClass(type).isEnum() && !Strings.isBlank(enums) && !isCamelCoreType(type)) {
+            String enumClassName = type.substring(type.lastIndexOf('.') + 1);
+            if (enumClassName.contains("$")) {
+                enumClassName = enumClassName.substring(enumClassName.indexOf('$') + 1);
+            }
+            JavaClass enumClass = enumClasses.get(enumClassName);
+            if (enumClass == null) {
+                enumClass = javaClass.addNestedType().setPublic().setStatic(true)
+                                .setName(enumClassName).setEnum(true);
+                enumClasses.put(enumClassName, enumClass);
+                for (String value : enums.split(",")) {
+                    enumClass.addValue(value
+                            .replace('.', '_')
+                            .replace('-', '_'));
+                }
+            }
+            type = javaClass.getPackage() + "." + javaClass.getName() + "$" + enumClassName;
+            return new GenericType(generateDummyClass(type));
+        }
+        if (!isCamelCoreType(type)) {
+            getLog().debug("Substituting java.lang.Object to " + type);
+            return new GenericType(Object.class);
+        }
+        return new GenericType(loadClass(type));
+    }
+
+    private String[] splitParams(String string) {
+        List<String> params = new ArrayList<>();
+        int cur = 0;
+        int start = 0;
+        int opened = 0;
+        while (true) {
+            int nextComma = string.indexOf(',', cur);
+            int nextOpen = string.indexOf('<', cur);
+            int nextClose = string.indexOf('>', cur);
+            if (nextComma < 0) {
+                params.add(string.substring(start));
+                return params.toArray(new String[0]);
+            } else if ((nextOpen < 0 || nextComma < nextOpen)
+                    && (nextClose < 0 || nextComma < nextClose)
+                    && opened == 0) {
+                params.add(string.substring(start, nextComma));
+                start = cur = nextComma + 1;
+            } else if (nextOpen < 0) {
+                if (--opened < 0) {
+                    throw new IllegalStateException();
+                }
+                cur = nextClose + 1;
+            } else if (nextClose < 0 || nextOpen < nextClose) {
+                ++opened;
+                cur = nextOpen + 1;
+            } else {
+                if (--opened < 0) {
+                    throw new IllegalStateException();
+                }
+                cur = nextClose + 1;
+            }
+        }
+    }
+
+    private boolean isCamelCoreType(String type) {
+        return type.startsWith("java.")
+                || type.matches("org\\.apache\\.camel\\.(spi\\.)?([A-Za-z]+)");
+    }
+
+    protected DynamicClassLoader getProjectClassLoader() {
+        if (projectClassLoader == null) {
+            final List<?> classpathElements;
+            try {
+                classpathElements = project.getTestClasspathElements();
+            } catch (org.apache.maven.artifact.DependencyResolutionRequiredException e) {
+                throw new RuntimeException(e.getMessage(), e);
+            }
+            final URL[] urls = new URL[classpathElements.size()];
+            int i = 0;
+            for (Iterator<?> it = classpathElements.iterator(); it.hasNext(); i++) {
+                try {
+                    urls[i] = new File((String)it.next()).toURI().toURL();
+                } catch (MalformedURLException e) {
+                    throw new RuntimeException(e.getMessage(), e);
+                }
+            }
+            final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+            projectClassLoader = new DynamicClassLoader(urls, tccl != null ? tccl : getClass().getClassLoader());
+        }
+        return projectClassLoader;
+    }
+
+    static class DynamicClassLoader extends URLClassLoader {
+        public DynamicClassLoader(URL[] urls, ClassLoader parent) {
+            super(urls, parent);
+        }
+
+        public Class defineClass(String name, byte[] data) {
+            return super.defineClass(name, data, 0, data.length);
+        }
+    }
+
+    private Class generateDummyClass(String clazzName) {
+        try {
+            return getProjectClassLoader().loadClass(clazzName);
+        } catch (ClassNotFoundException e) {
+            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+            cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, clazzName.replace('.', '/'), null, "java/lang/Object", null);
+            cw.visitEnd();
+            return getProjectClassLoader().defineClass(clazzName, cw.toByteArray());
+        }
+    }
+
+    private static String loadComponentJson(Map<File, Supplier<String>> jsonFiles, String componentName) {
+        return loadJsonOfType(jsonFiles, componentName, "component");
+    }
+
+    private static String loadJsonOfType(Map<File, Supplier<String>> jsonFiles, String modelName, String type) {
+        for (Map.Entry<File, Supplier<String>> entry : jsonFiles.entrySet()) {
+            if (entry.getKey().getName().equals(modelName + ".json")) {
+                String json = entry.getValue().get();
+                if (json.contains("\"kind\": \"" + type + "\"")) {
+                    return json;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static ComponentModel generateComponentModel(String componentName, String json) {
+        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
+
+        ComponentModel component = new ComponentModel(true);
+        component.setScheme(getSafeValue("scheme", rows));
+        component.setSyntax(getSafeValue("syntax", rows));
+        component.setAlternativeSyntax(getSafeValue("alternativeSyntax", rows));
+        component.setTitle(getSafeValue("title", rows));
+        component.setDescription(getSafeValue("description", rows));
+        component.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
+        component.setLabel(getSafeValue("label", rows));
+        component.setDeprecated(getSafeValue("deprecated", rows));
+        component.setDeprecationNote(getSafeValue("deprecationNote", rows));
+        component.setConsumerOnly(getSafeValue("consumerOnly", rows));
+        component.setProducerOnly(getSafeValue("producerOnly", rows));
+        component.setJavaType(getSafeValue("javaType", rows));
+        component.setGroupId(getSafeValue("groupId", rows));
+        component.setArtifactId(getSafeValue("artifactId", rows));
+        component.setVersion(getSafeValue("version", rows));
+
+        rows = JSonSchemaHelper.parseJsonSchema("componentProperties", json, true);
+        for (Map<String, String> row : rows) {
+            ComponentOptionModel option = new ComponentOptionModel();
+            option.setName(getSafeValue("name", row));
+            option.setDisplayName(getSafeValue("displayName", row));
+            option.setKind(getSafeValue("kind", row));
+            option.setType(getSafeValue("type", row));
+            option.setJavaType(getSafeValue("javaType", row));
+            option.setDeprecated(getSafeValue("deprecated", row));
+            option.setDeprecationNote(getSafeValue("deprecationNote", row));
+            option.setDescription(getSafeValue("description", row));
+            option.setDefaultValue(getSafeValue("defaultValue", row));
+            option.setEnums(getSafeValue("enum", row));
+            component.addComponentOption(option);
+        }
+
+        rows = JSonSchemaHelper.parseJsonSchema("properties", json, true);
+        for (Map<String, String> row : rows) {
+            EndpointOptionModel option = new EndpointOptionModel();
+            option.setName(getSafeValue("name", row));
+            option.setDisplayName(getSafeValue("displayName", row));
+            option.setKind(getSafeValue("kind", row));
+            option.setGroup(getSafeValue("group", row));
+            option.setLabel(getSafeValue("label", row));
+            option.setRequired(getSafeValue("required", row));
+            option.setType(getSafeValue("type", row));
+            option.setJavaType(getSafeValue("javaType", row));
+            option.setEnums(getSafeValue("enum", row));
+            option.setPrefix(getSafeValue("prefix", row));
+            option.setMultiValue(getSafeValue("multiValue", row));
+            option.setDeprecated(getSafeValue("deprecated", row));
+            option.setDeprecationNote(getSafeValue("deprecationNote", row));
+            option.setDefaultValue(getSafeValue("defaultValue", row));
+            option.setDescription(getSafeValue("description", row));
+            option.setEnumValues(getSafeValue("enum", row));
+            component.addEndpointOption(option);
+        }
+
+        return component;
+    }
+
+    private void findComponentNames(File dir, Set<String> componentNames) {
+        File f = new File(dir, "classes/META-INF/services/org/apache/camel/component");
+
+        if (f.exists() && f.isDirectory()) {
+            File[] files = f.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    // skip directories as there may be a sub .resolver
+                    // directory
+                    if (file.isDirectory()) {
+                        continue;
+                    }
+                    String name = file.getName();
+                    if (name.charAt(0) != '.') {
+                        componentNames.add(name);
+                    }
+                }
+            }
+        }
+    }
+
+    private void writeSourceIfChanged(JavaClass source, String fileName, boolean innerClassesLast) throws MojoFailureException {
+        writeSourceIfChanged(source.printClass(innerClassesLast), fileName);
+    }
+
+    private void writeSourceIfChanged(String source, String fileName) throws MojoFailureException {
+        File core = findCamelCoreDirectory(project.getBasedir());
+
+        File target = new File(new File(core, "src/main/java"), fileName);
+
+        try {
+            String header;
+            try (InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt")) {
+                header = loadText(is);
+            }
+            String code = header + source;
+            getLog().debug("Source code generated:\n" + code);
+
+            updateResource(null, target.toPath(), code);
+        } catch (Exception e) {
+            throw new MojoFailureException("IOError with file " + target, e);
+        }
+    }
+
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
new file mode 100644
index 0000000..7c1d0ea
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
@@ -0,0 +1,218 @@
+/*
+ * 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.camel.maven.packaging.generics;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Utility classes with respect to the class operations.
+ *
+ * @author <a href="mailto:gurkanerdogdu@yahoo.com">Gurkan Erdogdu</a>
+ * @since 1.0
+ */
+public final class ClassUtil {
+    public static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPERS_MAP;
+
+    static {
+        Map<Class<?>, Class<?>> primitiveToWrappersMap = new HashMap<Class<?>, Class<?>>();
+        primitiveToWrappersMap.put(Integer.TYPE, Integer.class);
+        primitiveToWrappersMap.put(Float.TYPE, Float.class);
+        primitiveToWrappersMap.put(Double.TYPE, Double.class);
+        primitiveToWrappersMap.put(Character.TYPE, Character.class);
+        primitiveToWrappersMap.put(Long.TYPE, Long.class);
+        primitiveToWrappersMap.put(Byte.TYPE, Byte.class);
+        primitiveToWrappersMap.put(Short.TYPE, Short.class);
+        primitiveToWrappersMap.put(Boolean.TYPE, Boolean.class);
+        primitiveToWrappersMap.put(Void.TYPE, Void.class);
+        PRIMITIVE_TO_WRAPPERS_MAP = Collections.unmodifiableMap(primitiveToWrappersMap);
+    }
+
+    public static final Type[] NO_TYPES = new Type[0];
+
+    /*
+     * Private constructor
+     */
+    private ClassUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static boolean isSame(Type type1, Type type2) {
+        if ((type1 instanceof Class) && ((Class<?>) type1).isPrimitive()) {
+            type1 = PRIMITIVE_TO_WRAPPERS_MAP.get(type1);
+        }
+        if ((type2 instanceof Class) && ((Class<?>) type2).isPrimitive()) {
+            type2 = PRIMITIVE_TO_WRAPPERS_MAP.get(type2);
+        }
+        return type1 == type2;
+    }
+
+    public static Class<?> getPrimitiveWrapper(Class<?> clazz) {
+        return PRIMITIVE_TO_WRAPPERS_MAP.get(clazz);
+
+    }
+
+    /**
+     * Gets the class of the given type arguments.
+     * <p>
+     * If the given type {@link Type} parameters is an instance of the
+     * {@link ParameterizedType}, it returns the raw type otherwise it return
+     * the casted {@link Class} of the type argument.
+     * </p>
+     * 
+     * @param type class or parametrized type
+     * @return
+     */
+    public static Class<?> getClass(Type type)
+    {
+        return getClazz(type);
+    }
+
+
+    /**
+     * Returns true if type is an instance of <code>ParameterizedType</code>
+     * else otherwise.
+     * 
+     * @param type type of the artifact
+     * @return true if type is an instance of <code>ParameterizedType</code>
+     */
+    public static boolean isParametrizedType(Type type)
+    {
+        return type instanceof ParameterizedType;
+    }
+    
+    /**
+     * Returns true if type is an instance of <code>WildcardType</code>
+     * else otherwise.
+     * 
+     * @param type type of the artifact
+     * @return true if type is an instance of <code>WildcardType</code>
+     */    
+    public static boolean isWildCardType(Type type)
+    {
+        return type instanceof WildcardType;
+    }
+
+
+    /**
+     * Returns true if rhs is assignable type
+     * to the lhs, false otherwise.
+     * 
+     * @param lhs left hand side class
+     * @param rhs right hand side class
+     * @return true if rhs is assignable to lhs
+     */
+    public static boolean isClassAssignableFrom(Class<?> lhs, Class<?> rhs)
+    {
+        if(lhs.isPrimitive())
+        {
+            lhs = getPrimitiveWrapper(lhs);
+        }
+        
+        if(rhs.isPrimitive())
+        {
+            rhs = getPrimitiveWrapper(rhs);
+        }
+
+        if (lhs.isAssignableFrom(rhs))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Return raw class type for given type.
+     *
+     * @param type base type instance
+     * @return class type for given type
+     */
+    public static Class<?> getClazz(Type type) {
+        if (type instanceof ParameterizedType) {
+            ParameterizedType pt = (ParameterizedType) type;
+            return (Class<?>) pt.getRawType();
+        } else if (type instanceof Class) {
+            return (Class<?>) type;
+        } else if (type instanceof GenericArrayType) {
+            GenericArrayType arrayType = (GenericArrayType) type;
+            return Array.newInstance(getClazz(arrayType.getGenericComponentType()), 0).getClass();
+        } else if (type instanceof WildcardType) {
+            WildcardType wildcardType = (WildcardType) type;
+            Type[] bounds = wildcardType.getUpperBounds();
+            if (bounds.length > 1) {
+                throw new IllegalArgumentException("Illegal use of wild card type with more than one upper bound: " + wildcardType);
+            } else if (bounds.length == 0) {
+                return Object.class;
+            } else {
+                return getClass(bounds[0]);
+            }
+        } else if (type instanceof TypeVariable) {
+            TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+            if (typeVariable.getBounds().length > 1) {
+                throw new IllegalArgumentException("Illegal use of type variable with more than one bound: " + typeVariable);
+            } else {
+                Type[] bounds = typeVariable.getBounds();
+                if (bounds.length == 0) {
+                    return Object.class;
+                } else {
+                    return getClass(bounds[0]);
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + type);
+        }
+    }
+
+
+    public static boolean isRawClassEquals(Type ipType, Type apiType) {
+        Class ipClass = getRawPrimitiveType(ipType);
+        Class apiClass = getRawPrimitiveType(apiType);
+
+        if (ipClass == null || apiClass == null) {
+            // we found some illegal generics
+            return false;
+        }
+
+        return ipClass.equals(apiClass);
+    }
+
+    private static Class getRawPrimitiveType(Type type) {
+        if (type instanceof Class) {
+            if (((Class) type).isPrimitive()) {
+                return getPrimitiveWrapper((Class) type);
+            }
+            return (Class) type;
+        }
+
+        if (type instanceof ParameterizedType) {
+            return getRawPrimitiveType(((ParameterizedType) type).getRawType());
+        }
+
+        return null;
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/GenericsUtil.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/GenericsUtil.java
new file mode 100644
index 0000000..ec68bfb
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/GenericsUtil.java
@@ -0,0 +1,880 @@
+/*
+ * 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.camel.maven.packaging.generics;
+
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility classes for generic type operations.
+ */
+public final class GenericsUtil {
+    public static boolean satisfiesDependency(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType) {
+        if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType) {
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+        } else {
+            Type injectionPointRawType = injectionPointType instanceof ParameterizedType ? ((ParameterizedType) injectionPointType).getRawType() : injectionPointType;
+            Type beanRawType = beanType instanceof ParameterizedType ? ((ParameterizedType) beanType).getRawType() : beanType;
+
+            if (ClassUtil.isSame(injectionPointRawType, beanRawType)) {
+                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+            }
+        }
+
+        return false;
+    }
+
+    public static boolean satisfiesDependencyRaw(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType) {
+        if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType) {
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+        } else {
+            Type injectionPointRawType = injectionPointType instanceof ParameterizedType ? ((ParameterizedType) injectionPointType).getRawType() : injectionPointType;
+            Type beanRawType = beanType instanceof ParameterizedType ? ((ParameterizedType) beanType).getRawType() : beanType;
+
+            if (ClassUtil.isSame(injectionPointRawType, beanRawType)) {
+                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointRawType, beanRawType);
+            } else {
+                Class bean = (Class) beanType;
+                if (bean.getSuperclass() != null && ClassUtil.isRawClassEquals(injectionPointType, bean.getSuperclass())) {
+                    return true;
+                }
+
+                Class<?>[] interfaces = bean.getInterfaces();
+                if (interfaces == null || interfaces.length == 0) {
+                    return false;
+                }
+
+                for (Class<?> clazz : interfaces) {
+                    if (ClassUtil.isRawClassEquals(injectionPointType, clazz)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 5.2.3 and 5.2.4
+     */
+    public static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, Type requiredType, Type beanType) {
+        if (requiredType instanceof Class) {
+            return isAssignableFrom(isDelegateOrEvent, (Class<?>) requiredType, beanType);
+        } else if (requiredType instanceof ParameterizedType) {
+            return isAssignableFrom(isDelegateOrEvent, isProducer, (ParameterizedType) requiredType, beanType);
+        } else if (requiredType instanceof TypeVariable) {
+            return isAssignableFrom(isDelegateOrEvent, (TypeVariable<?>) requiredType, beanType);
+        } else if (requiredType instanceof GenericArrayType) {
+            return Class.class.isInstance(beanType) && Class.class.cast(beanType).isArray()
+                    && isAssignableFrom(isDelegateOrEvent, (GenericArrayType) requiredType, beanType);
+        } else if (requiredType instanceof WildcardType) {
+            return isAssignableFrom(isDelegateOrEvent, (WildcardType) requiredType, beanType);
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
+        }
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, Type beanType) {
+        if (beanType instanceof Class) {
+            return isAssignableFrom(injectionPointType, (Class<?>) beanType);
+        } else if (beanType instanceof TypeVariable) {
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>) beanType);
+        } else if (beanType instanceof ParameterizedType) {
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType) beanType);
+        } else if (beanType instanceof GenericArrayType) {
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (GenericArrayType) beanType);
+        } else if (beanType instanceof WildcardType) {
+            return isAssignableFrom(isDelegateOrEvent, (Type) injectionPointType, (WildcardType) beanType);
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass());
+        }
+    }
+
+    private static boolean isAssignableFrom(Class<?> injectionPointType, Class<?> beanType) {
+        return ClassUtil.isClassAssignableFrom(injectionPointType, beanType);
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, TypeVariable<?> beanType) {
+        for (Type bounds : beanType.getBounds()) {
+            if (isAssignableFrom(isDelegateOrEvent, injectionPointType, bounds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * CDI Spec. 5.2.4: "A parameterized bean type is considered assignable to a raw required type
+     * if the raw generics are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object."
+     */
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, ParameterizedType beanType) {
+        if (beanType.getRawType() != injectionPointType) {
+            return false; //raw generics don't match
+        }
+
+        if (isDelegateOrEvent) {
+            // for delegate and events we match 'in reverse' kind off
+            // @Observes ProcessInjectionPoint<?, Instance> does also match Instance<SomeBean>
+            return isAssignableFrom(true, injectionPointType, beanType.getRawType());
+        }
+
+        for (Type typeArgument : beanType.getActualTypeArguments()) {
+            if (typeArgument == Object.class) {
+                continue;
+            }
+            if (!(typeArgument instanceof TypeVariable)) {
+                return false; //neither object nor type variable
+            }
+            TypeVariable<?> typeVariable = (TypeVariable<?>) typeArgument;
+            for (Type bounds : typeVariable.getBounds()) {
+                if (bounds != Object.class) {
+                    return false; //bound type variable
+                }
+            }
+        }
+        return true;
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, GenericArrayType beanType) {
+        return injectionPointType.isArray() && isAssignableFrom(isDelegateOrEvent, injectionPointType.getComponentType(), beanType.getGenericComponentType());
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Type injectionPointType, WildcardType beanType) {
+        for (Type bounds : beanType.getLowerBounds()) {
+            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, injectionPointType)) {
+                return false;
+            }
+        }
+        for (Type bounds : beanType.getUpperBounds()) {
+            if (isAssignableFrom(isDelegateOrEvent, false, injectionPointType, bounds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Type beanType) {
+        if (beanType instanceof Class) {
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (Class<?>) beanType);
+        } else if (beanType instanceof TypeVariable) {
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (TypeVariable<?>) beanType);
+        } else if (beanType instanceof ParameterizedType) {
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType) beanType);
+        } else if (beanType instanceof WildcardType) {
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (WildcardType) beanType);
+        } else if (beanType instanceof GenericArrayType) {
+            return false;
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + beanType.getClass());
+        }
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Class<?> beanType) {
+        Class<?> rawInjectionPointType = getRawType(injectionPointType);
+        if (rawInjectionPointType.equals(beanType)) {
+            if (isProducer) {
+                for (final Type t : injectionPointType.getActualTypeArguments()) {
+                    if (!TypeVariable.class.isInstance(t) || !isNotBound(TypeVariable.class.cast(t).getBounds())) {
+                        if (!Class.class.isInstance(t) || Object.class != t) {
+                            return false;
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+        if (!rawInjectionPointType.isAssignableFrom(beanType)) {
+            return false;
+        }
+        if (beanType.getSuperclass() != null && isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType.getGenericSuperclass())) {
+            return true;
+        }
+        for (Type genericInterface : beanType.getGenericInterfaces()) {
+            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, genericInterface)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, TypeVariable<?> beanType) {
+        final Type[] types = beanType.getBounds();
+        if (isNotBound(types)) {
+            return true;
+        }
+        for (final Type bounds : types) {
+            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, bounds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * CDI Spec. 5.2.4
+     */
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, ParameterizedType injectionPointType, ParameterizedType beanType) {
+        if (injectionPointType.getRawType() != beanType.getRawType()) {
+            return false;
+        }
+        boolean swapParams = !isDelegateOrEvent;
+        Type[] injectionPointTypeArguments = injectionPointType.getActualTypeArguments();
+        Type[] beanTypeArguments = beanType.getActualTypeArguments();
+        for (int i = 0; i < injectionPointTypeArguments.length; i++) {
+            Type injectionPointTypeArgument = injectionPointTypeArguments[i];
+            Type beanTypeArgument = beanTypeArguments[i];
+
+            // for this special case it's actually an 'assignable to', thus we swap the params, see CDI-389
+            // but this special rule does not apply to Delegate injection points...
+            if (swapParams &&
+                    (injectionPointTypeArgument instanceof Class || injectionPointTypeArgument instanceof TypeVariable) &&
+                    beanTypeArgument instanceof TypeVariable) {
+                final Type[] bounds = ((TypeVariable<?>) beanTypeArgument).getBounds();
+                final boolean isNotBound = isNotBound(bounds);
+                if (!isNotBound) {
+                    for (final Type upperBound : bounds) {
+                        if (!isAssignableFrom(true, false, upperBound, injectionPointTypeArgument)) {
+                            return false;
+                        }
+                    }
+                }
+            } else if (swapParams && injectionPointTypeArgument instanceof TypeVariable) {
+                return false;
+            } else if (isDelegateOrEvent && injectionPointTypeArgument instanceof Class && beanTypeArgument instanceof Class) {
+                // if no wildcard type was given then we require a real exact match.
+                return injectionPointTypeArgument.equals(beanTypeArgument);
+
+            } else if (!isAssignableFrom(isDelegateOrEvent, false, injectionPointTypeArgument, beanTypeArgument)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isNotBound(final Type... bounds) {
+        return bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class == bounds[0]);
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, TypeVariable<?> injectionPointType, Type beanType) {
+        for (Type bounds : injectionPointType.getBounds()) {
+            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, beanType)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // rules are a bit different when in an array so we handle ParameterizedType manually (not reusing isAssignableFrom)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, GenericArrayType injectionPointType, Type beanType) {
+        final Type genericComponentType = injectionPointType.getGenericComponentType();
+        final Class componentType = Class.class.cast(beanType).getComponentType();
+        if (Class.class.isInstance(genericComponentType)) {
+            return Class.class.cast(genericComponentType).isAssignableFrom(componentType);
+        }
+        if (ParameterizedType.class.isInstance(genericComponentType)) {
+            return isAssignableFrom(isDelegateOrEvent, false, ParameterizedType.class.cast(genericComponentType).getRawType(), componentType);
+        }
+        return isAssignableFrom(isDelegateOrEvent, false, genericComponentType, componentType);
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, Type beanType) {
+        if (beanType instanceof TypeVariable) {
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>) beanType);
+        }
+        for (Type bounds : injectionPointType.getLowerBounds()) {
+            if (!isAssignableFrom(isDelegateOrEvent, false, beanType, bounds)) {
+                return false;
+            }
+        }
+        for (Type bounds : injectionPointType.getUpperBounds()) {
+            Set<Type> beanTypeClosure = getTypeClosure(beanType);
+            boolean isAssignable = false;
+            for (Type beanSupertype : beanTypeClosure) {
+                if (isAssignableFrom(isDelegateOrEvent, false, bounds, beanSupertype)
+                        || (Class.class.isInstance(bounds)
+                        && ParameterizedType.class.isInstance(beanSupertype)
+                        && bounds == ParameterizedType.class.cast(beanSupertype).getRawType())) {
+                    isAssignable = true;
+                    break;
+                }
+            }
+            if (!isAssignable) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * CDI 1.1 Spec. 5.2.4, third bullet point
+     */
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, TypeVariable<?> beanType) {
+        for (Type upperBound : injectionPointType.getUpperBounds()) {
+            for (Type bound : beanType.getBounds()) {
+                if (!isAssignableFrom(isDelegateOrEvent, false, upperBound, bound) && !isAssignableFrom(isDelegateOrEvent, false, bound, upperBound)) {
+                    return false;
+                }
+            }
+        }
+        for (Type lowerBound : injectionPointType.getLowerBounds()) {
+            for (Type bound : beanType.getBounds()) {
+                if (!isAssignableFrom(isDelegateOrEvent, false, bound, lowerBound)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @return <tt>true</tt>, if the specified type declaration contains an unresolved type variable.
+     */
+    public static boolean containsTypeVariable(Type type) {
+        if (type instanceof Class) {
+            return false;
+        } else if (type instanceof TypeVariable) {
+            return true;
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+            return containTypeVariable(parameterizedType.getActualTypeArguments());
+        } else if (type instanceof WildcardType) {
+            WildcardType wildcardType = (WildcardType) type;
+            return containTypeVariable(wildcardType.getUpperBounds()) || containTypeVariable(wildcardType.getLowerBounds());
+        } else if (type instanceof GenericArrayType) {
+            GenericArrayType arrayType = (GenericArrayType) type;
+            return containsTypeVariable(arrayType.getGenericComponentType());
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+        }
+
+    }
+
+    public static boolean containTypeVariable(Collection<? extends Type> types) {
+        return containTypeVariable(types.toArray(new Type[types.size()]));
+    }
+
+    public static boolean containTypeVariable(Type[] types) {
+        for (Type type : types) {
+            if (containsTypeVariable(type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param type to check
+     * @return {@code true} if the given type contains a {@link WildcardType}
+     * {@code false} otherwise
+     */
+    public static boolean containsWildcardType(Type type) {
+        if (!(type instanceof ParameterizedType)) {
+            return false;
+        }
+
+        for (Type typeArgument : getParameterizedType(type).getActualTypeArguments()) {
+            if (ClassUtil.isParametrizedType(typeArgument)) {
+                if (containsWildcardType(typeArgument)) {
+                    return true;
+                }
+            } else {
+                if (ClassUtil.isWildCardType(typeArgument)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Resolves the actual type of the specified field for the type hierarchy specified by the given subclass
+     */
+    public static Type resolveType(Class<?> subclass, Field field) {
+        return resolveType(field.getGenericType(), subclass, newSeenList());
+    }
+
+    /**
+     * Resolves the actual return type of the specified method for the type hierarchy specified by the given subclass
+     */
+    public static Type resolveReturnType(Class<?> subclass, Method method) {
+        return resolveType(method.getGenericReturnType(), subclass, newSeenList());
+    }
+
+    /**
+     * Resolves the actual parameter generics of the specified constructor for the type hierarchy specified by the given subclass
+     */
+    public static Type[] resolveParameterTypes(Class<?> subclass, Constructor<?> constructor) {
+        return resolveTypes(constructor.getGenericParameterTypes(), subclass);
+    }
+
+    /**
+     * Resolves the actual parameter generics of the specified method for the type hierarchy specified by the given subclass
+     */
+    public static Type[] resolveParameterTypes(Class<?> subclass, Method method) {
+        return resolveTypes(method.getGenericParameterTypes(), subclass);
+    }
+
+    /**
+     * Resolves the actual type of the specified type for the type hierarchy specified by the given subclass
+     */
+    public static Type resolveType(Type type, Class<?> subclass, Member member) {
+        return resolveType(type, subclass, newSeenList());
+    }
+
+    public static Type resolveType(Type type, Class<?> subclass, Member member, Collection<TypeVariable<?>> seen) {
+        return resolveType(type, subclass, seen);
+    }
+
+    public static Type resolveType(Type type, Type actualType, Collection<TypeVariable<?>> seen) {
+        if (type instanceof Class) {
+            return type;
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+
+            Type[] resolvedTypeArguments;
+            if (Enum.class.equals(parameterizedType.getRawType())) {
+                // Enums derive from themselves, which would create an infinite loop
+                // we directly escape the loop if we detect this.
+                resolvedTypeArguments = new Type[]{new OwbWildcardTypeImpl(new Type[]{Enum.class}, ClassUtil.NO_TYPES)};
+            } else {
+                resolvedTypeArguments = resolveTypes(parameterizedType.getActualTypeArguments(), actualType, seen);
+
+            }
+
+            return new OwbParametrizedTypeImpl(parameterizedType.getOwnerType(), parameterizedType.getRawType(), resolvedTypeArguments);
+        } else if (type instanceof TypeVariable) {
+            TypeVariable<?> variable = (TypeVariable<?>) type;
+            return resolveTypeVariable(variable, actualType, seen);
+        } else if (type instanceof WildcardType) {
+            WildcardType wildcardType = (WildcardType) type;
+            Type[] upperBounds = resolveTypes(wildcardType.getUpperBounds(), actualType, seen);
+            Type[] lowerBounds = resolveTypes(wildcardType.getLowerBounds(), actualType, seen);
+            return new OwbWildcardTypeImpl(upperBounds, lowerBounds);
+        } else if (type instanceof GenericArrayType) {
+            GenericArrayType arrayType = (GenericArrayType) type;
+            return createArrayType(resolveType(arrayType.getGenericComponentType(), actualType, seen));
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+        }
+    }
+
+    public static Type[] resolveTypes(Type[] types, Type actualType, Collection<TypeVariable<?>> seen) {
+        Type[] resolvedTypeArguments = new Type[types.length];
+        for (int i = 0; i < types.length; i++) {
+            final Type type = resolveType(types[i], actualType, seen);
+            if (type != null) // means a stackoverflow was avoided, just keep what we have
+            {
+                resolvedTypeArguments[i] = type;
+            }
+        }
+        return resolvedTypeArguments;
+    }
+
+    public static Type[] resolveTypes(Type[] types, Type actualType) {
+        Type[] resolvedTypeArguments = new Type[types.length];
+        for (int i = 0; i < types.length; i++) {
+            resolvedTypeArguments[i] = resolveType(types[i], actualType, newSeenList());
+        }
+        return resolvedTypeArguments;
+    }
+
+    public static Set<Type> getTypeClosure(Class<?> type) {
+        return getTypeClosure(type, type);
+    }
+
+    public static Set<Type> getTypeClosure(Type actualType) {
+        return getTypeClosure(actualType, actualType);
+    }
+
+    /**
+     * Returns the type closure for the specified parameters.
+     * <h3>Example 1:</h3>
+     * <p>
+     * Take the following classes:
+     * </p>
+     * <code>
+     * public class Foo<T> {
+     * private T t;
+     * }
+     * public class Bar extends Foo<Number> {
+     * }
+     * </code>
+     * <p>
+     * To get the type closure of T in the context of Bar (which is {Number.class, Object.class}), you have to call this method like
+     * </p>
+     * <code>
+     * GenericUtil.getTypeClosure(Foo.class.getDeclaredField("t").getType(), Bar.class, Foo.class);
+     * </code>
+     * <h3>Example 2:</h3>
+     * <p>
+     * Take the following classes:
+     * </p>
+     * <code>
+     * public class Foo<T> {
+     * private T t;
+     * }
+     * public class Bar<T> extends Foo<T> {
+     * }
+     * </code>
+     * <p>
+     * To get the type closure of Bar<T> in the context of Foo<Number> (which are besides Object.class the <tt>ParameterizedType</tt>s Bar<Number> and Foo<Number>),
+     * you have to call this method like
+     * </p>
+     * <code>
+     * GenericUtil.getTypeClosure(Foo.class, new TypeLiteral<Foo<Number>>() {}.getType(), Bar.class);
+     * </code>
+     *
+     * @param type       the type to get the closure for
+     * @param actualType the context to bind type variables
+     * @return the type closure
+     */
+    public static Set<Type> getTypeClosure(Type type, Type actualType) {
+        Class<?> rawType = getRawType(type);
+        Class<?> actualRawType = getRawType(actualType);
+        if (rawType.isAssignableFrom(actualRawType) && rawType != actualRawType) {
+            return getTypeClosure(actualType, type);
+        }
+        if (hasTypeParameters(type)) {
+            type = getParameterizedType(type);
+        }
+        return getDirectTypeClosure(type, actualType);
+    }
+
+    public static Set<Type> getDirectTypeClosure(final Type type, final Type actualType) {
+        Set<Type> typeClosure = new HashSet<Type>();
+        typeClosure.add(Object.class);
+        fillTypeHierarchy(typeClosure, type, actualType);
+        return typeClosure;
+    }
+
+    private static void fillTypeHierarchy(Set<Type> set, Type type, Type actualType) {
+        if (type == null) {
+            return;
+        }
+        Type resolvedType = GenericsUtil.resolveType(type, actualType, newSeenList());
+        set.add(resolvedType);
+        Class<?> resolvedClass = GenericsUtil.getRawType(resolvedType, actualType);
+        if (resolvedClass.getSuperclass() != null) {
+            fillTypeHierarchy(set, resolvedClass.getGenericSuperclass(), resolvedType);
+        }
+        for (Type interfaceType : resolvedClass.getGenericInterfaces()) {
+            fillTypeHierarchy(set, interfaceType, resolvedType);
+        }
+    }
+
+    private static Collection<TypeVariable<?>> newSeenList() {
+        return new ArrayList<TypeVariable<?>>();
+    }
+
+    public static boolean hasTypeParameters(Type type) {
+        if (type instanceof Class) {
+            Class<?> classType = (Class<?>) type;
+            return classType.getTypeParameters().length > 0;
+        }
+        return false;
+    }
+
+    public static ParameterizedType getParameterizedType(Type type) {
+        if (type instanceof ParameterizedType) {
+            return (ParameterizedType) type;
+        } else if (type instanceof Class) {
+            Class<?> classType = (Class<?>) type;
+            return new OwbParametrizedTypeImpl(classType.getDeclaringClass(), classType, classType.getTypeParameters());
+        } else {
+            throw new IllegalArgumentException(type.getClass().getSimpleName() + " is not supported");
+        }
+    }
+
+    public static <T> Class<T> getRawType(Type type) {
+        return getRawType(type, null);
+    }
+
+    static <T> Class<T> getRawType(Type type, Type actualType) {
+        if (type instanceof Class) {
+            return (Class<T>) type;
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+            return getRawType(parameterizedType.getRawType(), actualType);
+        } else if (type instanceof TypeVariable) {
+            TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+            Type mostSpecificType = getMostSpecificType(getRawTypes(typeVariable.getBounds(), actualType), typeVariable.getBounds());
+            return getRawType(mostSpecificType, actualType);
+        } else if (type instanceof WildcardType) {
+            WildcardType wildcardType = (WildcardType) type;
+            Type mostSpecificType = getMostSpecificType(getRawTypes(wildcardType.getUpperBounds(), actualType), wildcardType.getUpperBounds());
+            return getRawType(mostSpecificType, actualType);
+        } else if (type instanceof GenericArrayType) {
+            GenericArrayType arrayType = (GenericArrayType) type;
+            return getRawType(createArrayType(getRawType(arrayType.getGenericComponentType(), actualType)), actualType);
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+        }
+    }
+
+    private static <T> Class<T>[] getRawTypes(Type[] types) {
+        return getRawTypes(types, null);
+    }
+
+    private static <T> Class<T>[] getRawTypes(Type[] types, Type actualType) {
+        Class<T>[] rawTypes = new Class[types.length];
+        for (int i = 0; i < types.length; i++) {
+            rawTypes[i] = getRawType(types[i], actualType);
+        }
+        return rawTypes;
+    }
+
+    private static Type getMostSpecificType(Class<?>[] types, Type[] genericTypes) {
+        Class<?> mostSpecificType = types[0];
+        int mostSpecificIndex = 0;
+        for (int i = 0; i < types.length; i++) {
+            if (mostSpecificType.isAssignableFrom(types[i])) {
+                mostSpecificType = types[i];
+                mostSpecificIndex = i;
+            }
+        }
+        return genericTypes[mostSpecificIndex];
+    }
+
+    private static Class<?>[] getClassTypes(Class<?>[] rawTypes) {
+        List<Class<?>> classTypes = new ArrayList<Class<?>>();
+        for (Class<?> rawType : rawTypes) {
+            if (!rawType.isInterface()) {
+                classTypes.add(rawType);
+            }
+        }
+        return classTypes.toArray(new Class[classTypes.size()]);
+    }
+
+    private static Type resolveTypeVariable(TypeVariable<?> variable, Type actualType, Collection<TypeVariable<?>> seen) {
+        if (actualType == null) {
+            return variable;
+        }
+        Class<?> declaringClass = getDeclaringClass(variable.getGenericDeclaration());
+        Class<?> actualClass = getRawType(actualType);
+        if (actualClass == declaringClass) {
+            return resolveTypeVariable(variable, variable.getGenericDeclaration(), getParameterizedType(actualType), seen);
+        } else if (actualClass.isAssignableFrom(declaringClass)) {
+            Class<?> directSubclass = getDirectSubclass(declaringClass, actualClass);
+            Type[] typeArguments = resolveTypeArguments(directSubclass, actualType);
+            Type directSubtype = new OwbParametrizedTypeImpl(directSubclass.getDeclaringClass(), directSubclass, typeArguments);
+            return resolveTypeVariable(variable, directSubtype, seen);
+        } else // if (declaringClass.isAssignableFrom(actualClass))
+        {
+            Type genericSuperclass = getGenericSuperclass(actualClass, declaringClass);
+            if (genericSuperclass == null) {
+                return variable;
+            } else if (genericSuperclass instanceof Class) {
+                // special handling for type erasure
+                Class<?> superclass = (Class<?>) genericSuperclass;
+                genericSuperclass = new OwbParametrizedTypeImpl(superclass.getDeclaringClass(), superclass, getRawTypes(superclass.getTypeParameters()));
+            } else {
+                ParameterizedType genericSupertype = getParameterizedType(genericSuperclass);
+                Type[] typeArguments = resolveTypeArguments(getParameterizedType(actualType), genericSupertype);
+                genericSuperclass = new OwbParametrizedTypeImpl(genericSupertype.getOwnerType(), genericSupertype.getRawType(), typeArguments);
+            }
+            Type resolvedType = resolveTypeVariable(variable, genericSuperclass, seen);
+            if (resolvedType instanceof TypeVariable) {
+                TypeVariable<?> resolvedTypeVariable = (TypeVariable<?>) resolvedType;
+                TypeVariable<?>[] typeParameters = actualClass.getTypeParameters();
+                for (int i = 0; i < typeParameters.length; i++) {
+                    if (typeParameters[i].getName().equals(resolvedTypeVariable.getName())) {
+                        resolvedType = getParameterizedType(actualType).getActualTypeArguments()[i];
+                        break;
+                    }
+                }
+            }
+            return resolvedType;
+        }
+    }
+
+    private static Class<?> getDeclaringClass(GenericDeclaration declaration) {
+        if (declaration instanceof Class) {
+            return (Class<?>) declaration;
+        } else if (declaration instanceof Member) {
+            return ((Member) declaration).getDeclaringClass();
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + declaration.getClass());
+        }
+    }
+
+    private static Type resolveTypeVariable(TypeVariable<?> variable, GenericDeclaration declaration, ParameterizedType type,
+                                            Collection<TypeVariable<?>> seen) {
+        int index = getIndex(declaration, variable);
+        if (declaration instanceof Class) {
+            if (index >= 0) {
+                return type.getActualTypeArguments()[index];
+            } else {
+                index = getIndex(type, variable);
+                if (index >= 0) {
+                    return declaration.getTypeParameters()[index];
+                }
+            }
+        } else {
+            if (seen.contains(variable)) {
+                return null;
+            }
+            seen.add(variable);
+
+            Type[] resolvedBounds = resolveTypes(declaration.getTypeParameters()[index].getBounds(), type, seen);
+            return OwbTypeVariableImpl.createTypeVariable(variable, resolvedBounds);
+        }
+        return variable;
+    }
+
+    private static int getIndex(GenericDeclaration declaration, TypeVariable<?> variable) {
+        Type[] typeParameters = declaration.getTypeParameters();
+        for (int i = 0; i < typeParameters.length; i++) {
+            if (typeParameters[i] instanceof TypeVariable) {
+                TypeVariable<?> variableArgument = (TypeVariable<?>) typeParameters[i];
+                if (variableArgument.getName().equals(variable.getName())) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    private static int getIndex(ParameterizedType type, TypeVariable<?> variable) {
+        Type[] actualTypeArguments = type.getActualTypeArguments();
+        for (int i = 0; i < actualTypeArguments.length; i++) {
+            if (actualTypeArguments[i] instanceof TypeVariable) {
+                TypeVariable<?> variableArgument = (TypeVariable<?>) actualTypeArguments[i];
+                if (variableArgument.getName().equals(variable.getName())) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    private static Class<?> getDirectSubclass(Class<?> declaringClass, Class<?> actualClass) {
+        if (actualClass.isInterface()) {
+            Class<?> subclass = declaringClass;
+            for (Class<?> iface : declaringClass.getInterfaces()) {
+                if (iface == actualClass) {
+                    return subclass;
+                }
+                if (actualClass.isAssignableFrom(iface)) {
+                    subclass = iface;
+                } else {
+                    subclass = declaringClass.getSuperclass();
+                }
+            }
+            return getDirectSubclass(subclass, actualClass);
+        } else {
+            Class<?> directSubclass = declaringClass;
+            while (directSubclass.getSuperclass() != actualClass) {
+                directSubclass = directSubclass.getSuperclass();
+            }
+            return directSubclass;
+        }
+    }
+
+    private static Type getGenericSuperclass(Class<?> subclass, Class<?> superclass) {
+        if (!superclass.isInterface()) {
+            return subclass.getGenericSuperclass();
+        } else {
+            for (Type genericInterface : subclass.getGenericInterfaces()) {
+                if (getRawType(genericInterface) == superclass) {
+                    return genericInterface;
+                }
+            }
+        }
+        return superclass;
+    }
+
+    private static Type[] resolveTypeArguments(Class<?> subclass, Type supertype) {
+        if (supertype instanceof ParameterizedType) {
+            ParameterizedType parameterizedSupertype = (ParameterizedType) supertype;
+            return resolveTypeArguments(subclass, parameterizedSupertype);
+        } else {
+            return subclass.getTypeParameters();
+        }
+    }
+
+    private static Type[] resolveTypeArguments(Class<?> subclass, ParameterizedType parameterizedSupertype) {
+        Type genericSuperclass = getGenericSuperclass(subclass, getRawType(parameterizedSupertype));
+        if (!(genericSuperclass instanceof ParameterizedType)) {
+            return subclass.getTypeParameters();
+        }
+        ParameterizedType parameterizedSuperclass = (ParameterizedType) genericSuperclass;
+        Type[] typeParameters = subclass.getTypeParameters();
+        Type[] actualTypeArguments = parameterizedSupertype.getActualTypeArguments();
+        return resolveTypeArguments(parameterizedSuperclass, typeParameters, actualTypeArguments);
+    }
+
+    private static Type[] resolveTypeArguments(ParameterizedType subtype, ParameterizedType parameterizedSupertype) {
+        return resolveTypeArguments(getParameterizedType(getRawType(subtype)), parameterizedSupertype.getActualTypeArguments(), subtype.getActualTypeArguments());
+    }
+
+    private static Type[] resolveTypeArguments(ParameterizedType parameterizedType, Type[] typeParameters, Type[] actualTypeArguments) {
+        Type[] resolvedTypeArguments = new Type[typeParameters.length];
+        for (int i = 0; i < typeParameters.length; i++) {
+            resolvedTypeArguments[i] = resolveTypeArgument(parameterizedType, typeParameters[i], actualTypeArguments);
+        }
+        return resolvedTypeArguments;
+    }
+
+    private static Type resolveTypeArgument(ParameterizedType parameterizedType, Type typeParameter, Type[] actualTypeArguments) {
+        if (typeParameter instanceof TypeVariable) {
+            TypeVariable<?> variable = (TypeVariable<?>) typeParameter;
+            int index = getIndex(parameterizedType, variable);
+            if (index == -1) {
+                return typeParameter;
+            } else {
+                return actualTypeArguments[index];
+            }
+        } else if (typeParameter instanceof GenericArrayType) {
+            GenericArrayType array = (GenericArrayType) typeParameter;
+            return createArrayType(resolveTypeArgument(parameterizedType, array.getGenericComponentType(), actualTypeArguments));
+        } else {
+            return typeParameter;
+        }
+    }
+
+    private static Type createArrayType(Type componentType) {
+        if (componentType instanceof Class) {
+            return Array.newInstance((Class<?>) componentType, 0).getClass();
+        } else {
+            return new OwbGenericArrayTypeImpl(componentType);
+        }
+    }
+
+    public static Type resolveType(ParameterizedType parameterizedType, Type metadataType) {
+        return resolveType(parameterizedType, metadataType, newSeenList());
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbGenericArrayTypeImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbGenericArrayTypeImpl.java
new file mode 100644
index 0000000..2b3722b
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbGenericArrayTypeImpl.java
@@ -0,0 +1,64 @@
+/*
+ * 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.camel.maven.packaging.generics;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+
+public class OwbGenericArrayTypeImpl implements GenericArrayType {
+
+    private Type componentType;
+
+    public OwbGenericArrayTypeImpl(Type componentType) {
+        this.componentType = componentType;
+    }
+
+    @Override
+    public Type getGenericComponentType() {
+        return componentType;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return componentType.hashCode();
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        } else if (obj instanceof GenericArrayType) {
+            return ((GenericArrayType) obj).getGenericComponentType().equals(componentType);
+        } else {
+            return false;
+        }
+
+    }
+
+    public String toString() {
+        return componentType + "[]";
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbParametrizedTypeImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbParametrizedTypeImpl.java
new file mode 100644
index 0000000..2cf3a95
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbParametrizedTypeImpl.java
@@ -0,0 +1,125 @@
+/*
+ * 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.camel.maven.packaging.generics;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+/**
+ * Custom parametrized type implementation.
+ *
+ * @version $Rev: 1621935 $ $Date: 2014-09-02 09:07:32 +0200 (Tue, 02 Sep 2014) $
+ */
+public class OwbParametrizedTypeImpl implements ParameterizedType {
+    /**
+     * Owner type
+     */
+    private final Type owner;
+
+    /**
+     * Raw type
+     */
+    private final Type rawType;
+
+    /**
+     * Actual type arguments
+     */
+    private final Type[] types;
+
+    /**
+     * New instance.
+     *
+     * @param owner owner
+     * @param raw   raw
+     */
+    public OwbParametrizedTypeImpl(Type owner, Type raw, Type... types) {
+        this.owner = owner;
+        rawType = raw;
+        this.types = types;
+    }
+
+    @Override
+    public Type[] getActualTypeArguments() {
+        return types.clone();
+    }
+
+    @Override
+    public Type getOwnerType() {
+        return owner;
+    }
+
+    @Override
+    public Type getRawType() {
+        return rawType;
+    }
+
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(types) ^ (owner == null ? 0 : owner.hashCode()) ^ (rawType == null ? 0 : rawType.hashCode());
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        } else if (obj instanceof ParameterizedType) {
+            ParameterizedType that = (ParameterizedType) obj;
+            Type thatOwnerType = that.getOwnerType();
+            Type thatRawType = that.getRawType();
+            return (owner == null ? thatOwnerType == null : owner.equals(thatOwnerType))
+                    && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))
+                    && Arrays.equals(types, that.getActualTypeArguments());
+        } else {
+            return false;
+        }
+
+    }
+
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(((Class<?>) rawType).getName());
+        Type[] actualTypes = getActualTypeArguments();
+        if (actualTypes.length > 0) {
+            buffer.append("<");
+            int length = actualTypes.length;
+            for (int i = 0; i < length; i++) {
+                if (actualTypes[i] instanceof Class) {
+                    buffer.append(((Class<?>) actualTypes[i]).getSimpleName());
+                } else {
+                    buffer.append(actualTypes[i].toString());
+                }
+                if (i != actualTypes.length - 1) {
+                    buffer.append(", ");
+                }
+            }
+
+            buffer.append(">");
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbTypeVariableImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbTypeVariableImpl.java
new file mode 100644
index 0000000..7abd222
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbTypeVariableImpl.java
@@ -0,0 +1,182 @@
+/*
+ * 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.camel.maven.packaging.generics;
+
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+
+
+public class OwbTypeVariableImpl
+{
+    private static final Class<?>[] TYPE_VARIABLE_TYPES = new Class<?>[]{TypeVariable.class};
+
+    /**
+     * Java TypeVariable is different in various JDK versions. Thus it is not possible to e.g.
+     * write a custom TypeVariable which works in either Java7 and Java8 as they introduced
+     * new methods in Java8 which have return generics which only exist in Java8 :(
+     *
+     * As workaround we dynamically crate a proxy to wrap this and do the delegation manually.
+     * This is of course slower, but as we do not use it often it might not have much impact.
+     *
+     * @param typeVariable
+     * @param bounds
+     * @return the typeVariable with the defined bounds.
+     */
+    public static TypeVariable createTypeVariable(TypeVariable typeVariable, Type... bounds)
+    {
+        TypeVariable tv = (TypeVariable) Proxy.newProxyInstance(OwbTypeVariableImpl.class.getClassLoader(), TYPE_VARIABLE_TYPES,
+                new OwbTypeVariableInvocationHandler(typeVariable, bounds));
+
+        return tv;
+    }
+
+
+
+    public static class OwbTypeVariableInvocationHandler implements InvocationHandler
+    {
+
+        private String name;
+        private GenericDeclaration genericDeclaration;
+        private Type[] bounds;
+
+
+        public OwbTypeVariableInvocationHandler(TypeVariable typeVariable, Type... bounds)
+        {
+            name = typeVariable.getName();
+            genericDeclaration = typeVariable.getGenericDeclaration();
+            if (bounds == null || bounds.length == 0)
+            {
+                this.bounds = typeVariable.getBounds();
+            }
+            else
+            {
+                this.bounds = bounds;
+            }
+        }
+
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+        {
+            String methodName = method.getName();
+            if ("equals".equals(methodName))
+            {
+                return typeVariableEquals(args[0]);
+            }
+            else if ("hashCode".equals(methodName))
+            {
+                return typeVariableHashCode();
+            }
+            else if ("toString".equals(methodName))
+            {
+                return typeVariableToString();
+            }
+            else if ("getName".equals(methodName))
+            {
+                return getName();
+            }
+            else if ("getGenericDeclaration".equals(methodName))
+            {
+                return getGenericDeclaration();
+            }
+            else if ("getBounds".equals(methodName))
+            {
+                return getBounds();
+            }
+
+
+            // new method from java8...
+            return null;
+        }
+
+        /** method from TypeVariable */
+        public String getName()
+        {
+            return name;
+        }
+
+        /** method from TypeVariable */
+        public GenericDeclaration getGenericDeclaration()
+        {
+            return genericDeclaration;
+        }
+
+        /** method from TypeVariable */
+        public Type[] getBounds()
+        {
+            return bounds.clone();
+        }
+
+        /** method from TypeVariable */
+        public int typeVariableHashCode()
+        {
+            return Arrays.hashCode(bounds) ^ name.hashCode() ^ genericDeclaration.hashCode();
+        }
+
+        /** method from TypeVariable */
+        public boolean typeVariableEquals(Object object)
+        {
+            if (this == object)
+            {
+                return true;
+            }
+            else if (object instanceof TypeVariable)
+            {
+                TypeVariable<?> that = (TypeVariable<?>)object;
+                return name.equals(that.getName()) && genericDeclaration.equals(that.getGenericDeclaration()) && Arrays.equals(bounds, that.getBounds());
+            }
+            else
+            {
+                return false;
+            }
+
+        }
+
+        /** method from TypeVariable */
+        public String typeVariableToString()
+        {
+            StringBuilder buffer = new StringBuilder();
+            buffer.append(name);
+            if (bounds.length > 0)
+            {
+                buffer.append(" extends ");
+                boolean first = true;
+                for (Type bound: bounds)
+                {
+                    if (first)
+                    {
+                        first = false;
+                    }
+                    else
+                    {
+                        buffer.append(',');
+                    }
+                    buffer.append(' ').append(bound);
+                }
+            }
+            return buffer.toString();
+        }
+
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbWildcardTypeImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbWildcardTypeImpl.java
new file mode 100644
index 0000000..aebcfd0
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbWildcardTypeImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.camel.maven.packaging.generics;
+
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+
+public class OwbWildcardTypeImpl implements WildcardType {
+
+    private Type[] upperBounds;
+    private Type[] lowerBounds;
+
+    public OwbWildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
+        this.upperBounds = upperBounds.clone();
+        this.lowerBounds = lowerBounds.clone();
+    }
+
+    @Override
+    public Type[] getUpperBounds() {
+        return upperBounds.clone();
+    }
+
+    @Override
+    public Type[] getLowerBounds() {
+        return lowerBounds.clone();
+    }
+
+    public String toString() {
+        StringBuilder buffer = new StringBuilder("?");
+        if (upperBounds.length > 0) {
+            buffer.append(" extends");
+            boolean first = true;
+            for (Type upperBound : upperBounds) {
+                if (first) {
+                    first = false;
+                } else {
+                    buffer.append(',');
+                }
+                buffer.append(' ');
+                if (upperBound instanceof Class) {
+                    buffer.append(((Class<?>) upperBound).getSimpleName());
+                } else {
+                    buffer.append(upperBound);
+                }
+            }
+        }
+        if (lowerBounds.length > 0) {
+            buffer.append(" super");
+            boolean first = true;
+            for (Type lowerBound : lowerBounds) {
+                if (first) {
+                    first = false;
+                } else {
+                    buffer.append(',');
+                }
+                buffer.append(' ');
+                if (lowerBound instanceof Class) {
+                    buffer.append(((Class<?>) lowerBound).getSimpleName());
+                } else {
+                    buffer.append(lowerBound);
+                }
+            }
+        }
+        return buffer.toString();
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
index 87d8df8..3416df5 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
@@ -26,6 +26,7 @@ public class EndpointOptionModel {
     private String displayName;
     private String kind;
     private String group;
+    private String label;
     private String required;
     private String type;
     private String javaType;
@@ -74,6 +75,14 @@ public class EndpointOptionModel {
         this.group = group;
     }
 
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
     public String getRequired() {
         return required;
     }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java
index 3004605..8baa0ac 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java
@@ -35,7 +35,7 @@ public class GenericType {
 
     private static final Map<String, Class> PRIMITIVE_CLASSES = new HashMap<>();
 
-    enum BoundType {
+    public enum BoundType {
         Exact, Extends, Super
     }
     
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java
index 22ae6ca..0be7f1d 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java
@@ -25,6 +25,8 @@ import java.util.Set;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
 
+import org.jboss.forge.roaster.model.util.Strings;
+
 public class JavaClass {
 
     ClassLoader classLoader;
@@ -38,6 +40,7 @@ public class JavaClass {
     List<Field> fields = new ArrayList<>();
     List<Method> methods = new ArrayList<>();
     List<JavaClass> nested = new ArrayList<>();
+    List<String> values = new ArrayList<>();
     Javadoc javadoc = new Javadoc();
     boolean isStatic;
     boolean isPublic = true;
@@ -57,6 +60,14 @@ public class JavaClass {
         this.parent = parent;
     }
 
+    protected ClassLoader getClassLoader() {
+        if (classLoader == null && parent != null) {
+            return parent.getClassLoader();
+        } else {
+            return classLoader;
+        }
+    }
+
     public JavaClass setStatic(boolean aStatic) {
         isStatic = aStatic;
         return this;
@@ -72,6 +83,9 @@ public class JavaClass {
         return this;
     }
 
+    public String getPackage() {
+        return packageName;
+    }
     public JavaClass setPackage(String packageName) {
         this.packageName = packageName;
         return this;
@@ -121,7 +135,7 @@ public class JavaClass {
 
     public Annotation addAnnotation(String type) {
         try {
-            Class<?> cl = classLoader.loadClass(type);
+            Class<?> cl = getClassLoader().loadClass(type);
             return addAnnotation(cl);
         } catch (ClassNotFoundException e) {
             throw new IllegalArgumentException("Unable to parse type", e);
@@ -139,9 +153,9 @@ public class JavaClass {
 
     public Property addProperty(String type, String name) {
         try {
-            return addProperty(GenericType.parse(type, classLoader), name);
+            return addProperty(GenericType.parse(type, getClassLoader()), name);
         } catch (ClassNotFoundException e) {
-            throw new IllegalArgumentException("Unable to parse type", e);
+            throw new IllegalArgumentException("Unable to parse type " + type + " for property " + name, e);
         }
     }
 
@@ -173,6 +187,10 @@ public class JavaClass {
         return clazz;
     }
 
+    public void addValue(String value) {
+        values.add(value);
+    }
+
     public boolean isClass() {
         return isClass;
     }
@@ -236,6 +254,21 @@ public class JavaClass {
         printJavadoc(sb, indent, javadoc);
         printAnnotations(sb, indent, annotations);
 
+        if (isEnum) {
+            sb.append(indent)
+                .append(isPublic ? "public " : "")
+                .append(isStatic ? "static " : "")
+                .append("enum ").append(name).append(" {\n")
+                .append(indent)
+                .append("    ")
+                .append(Strings.join(values, ", "))
+                .append(";\n")
+                .append(indent)
+                .append("}");
+            return;
+
+        }
+
         StringBuilder sb2 = new StringBuilder();
         sb2.append(indent);
         if (isPublic) {