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

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/master by this push:
     new f8862de  Type safe component injection
f8862de is described below

commit f8862de6e48ee41eddc5d3dec84d72b61e2f6895
Author: lburgazzoli <lburgazz...@gmail.com>
AuthorDate: Thu Jun 18 12:17:06 2020 +0200

    Type safe component injection
    
    This allows to:
    
    ```java
    @ApplicationScoped
    public static class Configurer {
        @Inject
        LogComponent log;
    
        @PostConstruct
        void setUpLogComponent() {
            log.setBasicPropertyBinding(true);
            log.setExchangeFormatter(new MyExchangeFormatter());
        }
    
        public LogComponent getLog() {
            return log;
        }
    }
    
    @ApplicationScoped
    public static class Holder {
        private LogComponent log;
    
        @Inject
        public Holder(LogComponent log) {
            this.log = log;
        }
    
        public LogComponent getLog() {
            return log;
        }
    }
    ```
---
 .../core/deployment/InjectionPointsProcessor.java  | 124 +++++++++++++++++++++
 .../core/runtime/CamelInjectionPointTest.java      |  91 +++++++++++++++
 .../quarkus/core/InjectionPointsRecorder.java      |  47 ++++++++
 3 files changed, 262 insertions(+)

diff --git 
a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/InjectionPointsProcessor.java
 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/InjectionPointsProcessor.java
new file mode 100644
index 0000000..79f7b20
--- /dev/null
+++ 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/InjectionPointsProcessor.java
@@ -0,0 +1,124 @@
+/*
+ * 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.quarkus.core.deployment;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem;
+import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
+import io.quarkus.arc.processor.BuildExtension;
+import io.quarkus.arc.processor.InjectionPointInfo;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
+import org.apache.camel.Component;
+import org.apache.camel.quarkus.core.InjectionPointsRecorder;
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.AnnotationTarget;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.FieldInfo;
+import org.jboss.jandex.MethodInfo;
+import org.jboss.jandex.Type;
+import org.jboss.logging.Logger;
+
+public class InjectionPointsProcessor {
+    private static final Logger LOGGER = 
Logger.getLogger(InjectionPointsProcessor.class);
+
+    private static final DotName ANNOTATION_NAME_NAMED = DotName.createSimple(
+            Named.class.getName());
+    private static final DotName INTERFACE_NAME_COMPONENT = 
DotName.createSimple(
+            Component.class.getName());
+
+    private static SyntheticBeanBuildItem syntheticBean(DotName name, 
Supplier<?> creator) {
+        return SyntheticBeanBuildItem.configure(name)
+                .supplier(creator)
+                .scope(Singleton.class)
+                .unremovable()
+                .setRuntimeInit()
+                .done();
+    }
+
+    @Record(ExecutionTime.RUNTIME_INIT)
+    @BuildStep
+    BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem 
injectedComponents(
+            CombinedIndexBuildItem index,
+            InjectionPointsRecorder recorder,
+            BeanRegistrationPhaseBuildItem beanRegistrationPhase,
+            BuildProducer<SyntheticBeanBuildItem> syntheticBeans) {
+
+        final Collection<ClassInfo> components = 
index.getIndex().getAllKnownImplementors(INTERFACE_NAME_COMPONENT);
+        final Set<String> created = new HashSet<>();
+
+        for (InjectionPointInfo injectionPoint : 
beanRegistrationPhase.getContext().get(BuildExtension.Key.INJECTION_POINTS)) {
+            if (injectionPoint.getTarget().kind() == 
AnnotationTarget.Kind.FIELD) {
+                FieldInfo target = injectionPoint.getTarget().asField();
+
+                if (!created.add(target.type().name().toString())) {
+                    continue;
+                }
+
+                if (components.stream().anyMatch(ci -> 
Objects.equals(ci.name(), target.type().name()))) {
+                    final AnnotationInstance named = 
target.annotation(ANNOTATION_NAME_NAMED);
+                    final String componentName = named == null ? target.name() 
: named.value().asString();
+                    final Supplier<?> creator = 
recorder.componentSupplier(componentName, target.type().toString());
+
+                    LOGGER.debugf("Creating synthetic component bean [name=%s, 
type=%s]", componentName, target.type().name());
+
+                    syntheticBeans.produce(syntheticBean(target.type().name(), 
creator));
+                }
+            }
+
+            if (injectionPoint.getTarget().kind() == 
AnnotationTarget.Kind.METHOD) {
+                final MethodInfo target = 
injectionPoint.getTarget().asMethod();
+                final List<Type> types = target.parameters();
+
+                for (int i = 0; i < types.size(); i++) {
+                    Type type = types.get(0);
+
+                    if (!created.add(type.name().toString())) {
+                        continue;
+                    }
+
+                    if (components.stream().anyMatch(ci -> 
Objects.equals(ci.name(), type.name()))) {
+                        final AnnotationInstance named = 
target.annotation(ANNOTATION_NAME_NAMED);
+                        final String componentName = named == null ? 
target.parameterName(i) : named.value().asString();
+                        final Supplier<?> creator = 
recorder.componentSupplier(componentName, type.toString());
+
+                        LOGGER.debugf("Creating synthetic component bean 
[name=%s, type=%s]", componentName, type.name());
+
+                        syntheticBeans.produce(syntheticBean(type.name(), 
creator));
+                    }
+                }
+            }
+        }
+
+        // method using BeanRegistrationPhaseBuildItem should return a 
BeanConfiguratorBuildItem
+        // otherwise the build step may be processed at the wrong time.
+        return new BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem();
+    }
+}
diff --git 
a/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelInjectionPointTest.java
 
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelInjectionPointTest.java
new file mode 100644
index 0000000..597c24f
--- /dev/null
+++ 
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/runtime/CamelInjectionPointTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.quarkus.core.runtime;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.log.LogComponent;
+import org.apache.camel.spi.ExchangeFormatter;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CamelInjectionPointTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+                    .addClasses(Configurer.class, MyExchangeFormatter.class));
+
+    @Inject
+    Configurer configurer;
+    @Inject
+    Holder holder;
+
+    @Test
+    public void testConfigurer() {
+        assertThat(configurer.getLog()).isNotNull();
+        assertThat(configurer.getLog().isBasicPropertyBinding()).isTrue();
+        
assertThat(configurer.getLog().getExchangeFormatter()).isInstanceOf(MyExchangeFormatter.class);
+        assertThat(holder.getLog()).isNotNull();
+        assertThat(holder.getLog().isBasicPropertyBinding()).isTrue();
+        
assertThat(holder.getLog().getExchangeFormatter()).isInstanceOf(MyExchangeFormatter.class);
+    }
+
+    @ApplicationScoped
+    public static class Configurer {
+        @Inject
+        LogComponent log;
+
+        @PostConstruct
+        void setUpLogComponent() {
+            log.setBasicPropertyBinding(true);
+            log.setExchangeFormatter(new MyExchangeFormatter());
+        }
+
+        public LogComponent getLog() {
+            return log;
+        }
+    }
+
+    @ApplicationScoped
+    public static class Holder {
+        private LogComponent log;
+
+        @Inject
+        public Holder(LogComponent log) {
+            this.log = log;
+        }
+
+        public LogComponent getLog() {
+            return log;
+        }
+    }
+
+    public static class MyExchangeFormatter implements ExchangeFormatter {
+        @Override
+        public String format(Exchange exchange) {
+            return exchange.toString();
+        }
+    }
+}
diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/InjectionPointsRecorder.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/InjectionPointsRecorder.java
new file mode 100644
index 0000000..c281fad
--- /dev/null
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/InjectionPointsRecorder.java
@@ -0,0 +1,47 @@
+/*
+ * 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.quarkus.core;
+
+import java.util.function.Supplier;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.runtime.annotations.Recorder;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+
+@Recorder
+public class InjectionPointsRecorder {
+    public Supplier<? extends Component> componentSupplier(String 
componentName, String componentType) {
+        return new Supplier<Component>() {
+            @Override
+            public Component get() {
+                // We can't inject the CamelContext from the BuildStep as it 
will create a
+                // dependency cycle as the BuildStep that creates the 
CamelContext requires
+                // BeanManger instance. As this is a fairly trivial job, we 
can safely keep
+                // the context lookup-at runtime.
+                final CamelContext camelContext = 
Arc.container().instance(CamelContext.class).get();
+                if (camelContext == null) {
+                    throw new IllegalStateException("No CamelContext found");
+                }
+
+                return camelContext.getComponent(
+                        componentName,
+                        
camelContext.getClassResolver().resolveClass(componentType, Component.class));
+            }
+        };
+    }
+}

Reply via email to