Skip to content

Commit

Permalink
Register CDI event bridges only when required
Browse files Browse the repository at this point in the history
Fixes #3149
  • Loading branch information
jamesnetherton committed Oct 6, 2021
1 parent a765054 commit f8604b5
Show file tree
Hide file tree
Showing 21 changed files with 802 additions and 67 deletions.
8 changes: 8 additions & 0 deletions docs/modules/ROOT/pages/reference/extensions/core.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ What to do if it is not possible to extract CSimple expressions from a route def
| `org.apache.camel.quarkus.core.CamelConfig.FailureRemedy`
| `warn`

|icon:lock[title=Fixed at build time] [[quarkus.camel.event-bridge.enabled]]`link:#quarkus.camel.event-bridge.enabled[quarkus.camel.event-bridge.enabled]`

Whether to enable the bridging of Camel events to CDI events.
This allows CDI observers to be configured for Camel events. E.g. those belonging to the `org.apache.camel.quarkus.core.events`, `org.apache.camel.quarkus.main.events` & `org.apache.camel.impl.event` packages.
Note that this configuration item only has any effect when observers configured for Camel events are present in the application.
| `boolean`
| `true`

|icon:lock[title=Fixed at build time] [[quarkus.camel.main.enabled]]`link:#quarkus.camel.main.enabled[quarkus.camel.main.enabled]`

If `true` all `camel-main` features are enabled; otherwise no `camel-main` features are enabled. See described the xref:user-guide/configuration.adoc[Configuration] section of Camel Quarkus documentation for more details.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
*/
package org.apache.camel.quarkus.core.deployment;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;

import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem;
import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
Expand Down Expand Up @@ -163,4 +168,54 @@ public CamelRuntimeBuildItem runtime(
recorder.createRuntime(beanContainer.getValue(), context.getCamelContext()),
config.bootstrap.enabled);
}

/**
* Registers Camel CDI event bridges if quarkus.camel.event-bridge.enabled=true and if
* the relevant events have CDI observers configured for them.
*
* @param beanDiscovery build item containing the results of bean discovery
* @param context build item containing the CamelContext instance
* @param recorder the CamelContext recorder instance
*/
@Record(ExecutionTime.STATIC_INIT)
@BuildStep(onlyIf = EventBridgeEnabled.class)
public void registerCamelEventBridges(
BeanDiscoveryFinishedBuildItem beanDiscovery,
CamelContextBuildItem context,
CamelContextRecorder recorder) {

Set<String> observedLifecycleEvents = beanDiscovery.getObservers()
.stream()
.map(observerInfo -> observerInfo.getObservedType().name().toString())
.filter(observedType -> observedType.startsWith("org.apache.camel.quarkus.core.events"))
.collect(Collectors.collectingAndThen(Collectors.toUnmodifiableSet(), HashSet::new));

// For management events the event class simple name is collected as users can
// observe events on either the Camel event interface or the concrete event class, and
// these are located in different packages
Set<String> observedManagementEvents = beanDiscovery.getObservers()
.stream()
.map(observerInfo -> observerInfo.getObservedType().name().toString())
.filter(className -> className.matches("org.apache.camel(?!.quarkus).*Event$"))
.map(className -> CamelSupport.loadClass(className, Thread.currentThread().getContextClassLoader()))
.map(observedEventClass -> observedEventClass.getSimpleName())
.collect(Collectors.collectingAndThen(Collectors.toUnmodifiableSet(), HashSet::new));

if (!observedLifecycleEvents.isEmpty()) {
recorder.registerLifecycleEventBridge(context.getCamelContext(), observedLifecycleEvents);
}

if (!observedManagementEvents.isEmpty()) {
recorder.registerManagementEventBridge(context.getCamelContext(), observedManagementEvents);
}
}

public static final class EventBridgeEnabled implements BooleanSupplier {
CamelConfig config;

@Override
public boolean getAsBoolean() {
return config.eventBridge.enabled;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
*/
package org.apache.camel.quarkus.core.deployment.main;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem;
import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -36,6 +39,7 @@
import org.apache.camel.quarkus.core.CamelConfig;
import org.apache.camel.quarkus.core.CamelRecorder;
import org.apache.camel.quarkus.core.CamelRuntime;
import org.apache.camel.quarkus.core.deployment.CamelContextProcessor.EventBridgeEnabled;
import org.apache.camel.quarkus.core.deployment.main.spi.CamelMainBuildItem;
import org.apache.camel.quarkus.core.deployment.main.spi.CamelMainEnabled;
import org.apache.camel.quarkus.core.deployment.main.spi.CamelMainListenerBuildItem;
Expand Down Expand Up @@ -191,6 +195,32 @@ CamelRuntimeBuildItem runtime(
index.getIndex().getAnnotations(DotName.createSimple(QuarkusMain.class.getName())).isEmpty());
}

/**
* Registers Camel Main CDI event bridges if quarkus.camel.event-bridge.enabled=true and if
* the relevant events have CDI observers configured for them.
*
* @param beanDiscovery build item containing the results of bean discovery
* @param main build item containing the CamelMain instance
* @param recorder the CamelContext recorder instance
*/
@Record(ExecutionTime.STATIC_INIT)
@BuildStep(onlyIf = { CamelMainEnabled.class, EventBridgeEnabled.class })
public void registerCamelMainEventBridge(
BeanDiscoveryFinishedBuildItem beanDiscovery,
CamelMainBuildItem main,
CamelMainRecorder recorder) {

Set<String> observedMainEvents = beanDiscovery.getObservers()
.stream()
.map(observerInfo -> observerInfo.getObservedType().name().toString())
.filter(observedType -> observedType.startsWith("org.apache.camel.quarkus.main.events"))
.collect(Collectors.collectingAndThen(Collectors.toUnmodifiableSet(), HashSet::new));

if (!observedMainEvents.isEmpty()) {
recorder.registerCamelMainEventBridge(main.getInstance(), observedMainEvents);
}
}

@BuildStep(onlyIf = CamelMainEnabled.class)
AdditionalIndexedClassesBuildItem indexCamelMainApplication() {
// Required for launching CamelMain based applications from the IDE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* 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.main;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import io.quarkus.test.QuarkusUnitTest;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.quarkus.main.CamelMain;
import org.apache.camel.quarkus.main.CamelMainEventBridge;
import org.apache.camel.quarkus.main.events.AfterConfigure;
import org.apache.camel.quarkus.main.events.AfterStart;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.Asset;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class CamelMainEventBridgeDisabledConfigTest {
@RegisterExtension
static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addAsResource(applicationProperties(), "application.properties"));

@Inject
CamelMain main;

@Inject
EventHandler handler;

@Test
public void testObservers() {
assertFalse(main.getMainListeners().stream().anyMatch(mainListener -> mainListener
.getClass()
.equals(CamelMainEventBridge.class)));
assertTrue(handler.builders().isEmpty());
assertTrue(handler.routes().isEmpty());
}

public static Asset applicationProperties() {
Writer writer = new StringWriter();

Properties props = new Properties();
props.setProperty("quarkus.banner.enabled", "false");
props.setProperty("quarkus.camel.event-bridge.enabled", "false");

try {
props.store(writer, "");
} catch (IOException e) {
throw new RuntimeException(e);
}

return new StringAsset(writer.toString());
}

@ApplicationScoped
public static class EventHandler {
private final Set<String> builders = new CopyOnWriteArraySet<>();
private final Set<String> routes = new CopyOnWriteArraySet<>();

public void afterConfigure(@Observes AfterConfigure event) {
event.getMain().configure().getRoutesBuilders().forEach(
builder -> builders.add(builder.getClass().getName()));
}

public void afterStart(@Observes AfterStart event) {
event.getCamelContext(ModelCamelContext.class).getRoutes().forEach(
route -> routes.add(route.getRouteId()));
}

public Set<String> builders() {
return builders;
}

public Set<String> routes() {
return routes;
}
}

public static class MyRoutes extends RouteBuilder {
public static String ROUTE_ID = "myRoute";
public static String FROM_ENDPOINT = "direct://start";

@Override
public void configure() throws Exception {
from(FROM_ENDPOINT)
.routeId(ROUTE_ID)
.log("${body}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.main;

import javax.inject.Inject;

import io.quarkus.test.QuarkusUnitTest;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.quarkus.main.CamelMain;
import org.apache.camel.quarkus.main.CamelMainEventBridge;
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.junit.jupiter.api.Assertions.assertFalse;

public class CamelMainEventBridgeDisabledTest {
@RegisterExtension
static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));

@Inject
CamelMain main;

@Test
public void camelMainEventBridgeNotConfigured() {
// Since no CDI observers for main events are configured the event bridge should not be present
assertFalse(main.getMainListeners().stream().anyMatch(mainListener -> mainListener
.getClass()
.equals(CamelMainEventBridge.class)));
}

public static class MyRoutes extends RouteBuilder {
public static String ROUTE_ID = "myRoute";
public static String FROM_ENDPOINT = "direct://start";

@Override
public void configure() throws Exception {
from(FROM_ENDPOINT)
.routeId(ROUTE_ID)
.log("${body}");
}
}
}
Loading

0 comments on commit f8604b5

Please sign in to comment.