Chapter 5. Jarkarta EE Integration
This chapter provides the necessary information about the integration points with Jarkarta EE.
5.1. CDI
The Camel CDI component provides an auto-configuration for Apache Camel, using CDI as dependency injection framework. However, it is based on convention-over-configuration. It implements the standard camel bean integration so that you can use the Camel annotations easily in CDI beans.
For more information about CDI, refer to the cdi documentation.
The following example describes how you can consume and assosciate the Camel Context with a route.
@Startup @ApplicationScoped @ContextName("cdi-context") public class MyRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { from("direct:start").transform(body().prepend("Hi")); } }
@Inject @ContextName("cdi-context") private CamelContext camelctx;
5.1.1. Importing XML DSL configuration
Camel CDI integration enables you to import existing XML DSL files via the @ImportResource
annotation:
@ImportResource("camel-context.xml") class MyBean { }
The location of the imported file must be present on the deployment classpath. Placing the file into locations such as WEB-INF
will not work. However, WEB-INF/classes
will work fine.
5.2. EJB
Management support is provided through the ejb component which integrates with the EJB3 subsystem.
CamelContext camelctx = new DefaultCamelContext(); camelctx.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("ejb:java:module/HelloBean"); } });
5.3. JAXB
JAXB support is provided through the Camel JAXB data format.
Camel supports unmarshalling XML data to JAXB annotated classes and marshalling from classes to XML. The following demonstrates a simple Camel route for marshalling and unmarshalling with the Camel JAXB data format class.
5.3.1. JAXB Annotated class
@XmlRootElement(name = "customer") @XmlAccessorType(XmlAccessType.FIELD) public class Customer implements Serializable { private String firstName; private String lastName; public Customer() { } public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
5.3.2. JAXB Class XML representation
<customer xmlns="http://org/wildfly/test/jaxb/model/Customer"> <firstName>John</firstName> <lastName>Doe</lastName> </customer>
5.3.3. Camel JAXB Unmarshalling
WildFlyCamelContext camelctx = contextFactory.createCamelContext(getClass().getClassLoader()); final JaxbDataFormat jaxb = new JaxbDataFormat(); jaxb.setContextPath("org.wildfly.camel.test.jaxb.model"); camelctx.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start") .unmarshal(jaxb); } }); camelctx.start(); ProducerTemplate producer = camelctx.createProducerTemplate(); // Send an XML representation of the customer to the direct:start endpoint Customer customer = producer.requestBody("direct:start", readCustomerXml(), Customer.class); Assert.assertEquals("John", customer.getFirstName()); Assert.assertEquals("Doe", customer.getLastName());
5.3.4. Camel JAXB Marshalling
WildFlyCamelContext camelctx = contextFactory.createCamelContext(); final JaxbDataFormat jaxb = new JaxbDataFormat(); jaxb.setContextPath("org.wildfly.camel.test.jaxb.model"); camelctx.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start") .marshal(jaxb); } }); camelctx.start(); ProducerTemplate producer = camelctx.createProducerTemplate(); Customer customer = new Customer("John", "Doe"); String customerXML = producer.requestBody("direct:start", customer, String.class); Assert.assertEquals(readCustomerXml(), customerXML);
5.4. JAX-RS
JAX-RS support is provided by Camel CXF-RS.
5.4.1. CXF-RS Producer
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://camel.apache.org/schema/cxf" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <cxf:rsClient id="cxfProducer" address="http://localhost:8080/rest" serviceClass="org.wildfly.camel.examples.cxf.jaxrs.GreetingService" /> <camelContext id="cxfrs-camel-context" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start" /> <setHeader headerName="operationName"> <simple>greet</simple> </setHeader> <setHeader headerName="CamelCxfRsUsingHttpAPI"> <constant>false</constant> </setHeader> <to uri="cxfrs:bean:cxfProducer" /> </route> </camelContext> </beans>
5.4.2. CXF-RS Consumer
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://camel.apache.org/schema/cxf" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <cxf:rsServer id="cxfConsumer" address="http://localhost:8080/rest" serviceClass="org.wildfly.camel.examples.cxf.jaxrs.GreetingService" /> <camelContext id="cxfrs-camel-context" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="cxfrs:bean:cxfConsumer" /> <setBody> <constant>Hello world</constant> </setBody> </route> </camelContext> </beans>
5.4.3. JAX-RS Consumer with the Camel REST DSL
The Camel REST DSL gives the capability to write Camel routes that act as JAX-RS consumers. The following RouteBuilder class demonstrates this.
@Startup @ApplicationScoped @ContextName("rest-camel-context") public class RestConsumerRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { // Use the camel-undertow component to provide REST integration restConfiguration().component("undertow") .contextPath("/rest").port(8080).bindingMode(RestBindingMode.json); rest("/customer") // GET /rest/customer .get() .produces(MediaType.APPLICATION_JSON) .to("direct:getCustomers") // GET /rest/customer/1 .get("/{id}") .produces(MediaType.APPLICATION_JSON) .to("direct:getCustomer") // POST /rest/customer .post() .type(Customer.class) .to("direct:createCustomer"); // PUT /rest/customer .put() .type(Customer.class) .to("direct:updateCustomer"); // DELETE /rest/customer/1 .delete("/{id}") .to("direct:deleteCustomer"); } }
By setting the binding mode, Camel can marshal and unmarshal JSON data either by specifying a 'produces()' or 'type()' configuration step.
-
The REST DSL configuration starts with
restConfiguration().component("undertow")
. - The Camel on EAP Subsystem only supports the camel-servlet and camel-undertow components for use with the REST DSL. However, it does not work if you configure the other components.
5.4.4. Security
Refer to the JAX-RS security section.
5.4.5. Quickstart examples in Fuse on EAP
A quickstart example is available in your Fuse on EAP installation at quickstarts/camel/camel-cxf-jaxrs
directory.
5.5. JAX-WS
WebService support is provided through the CXF component which integrates with the JBoss EAP WebServices subsystem that also uses Apache CXF.
5.5.1. JAX-WS CXF Producer
The following code example uses CXF to consume a web service which has been deployed by the WildFly web services subsystem.
5.5.1.1. JAX-WS web service
The following simple web service has a simple 'greet' method which will concatenate two string arguments together and return them.
When the JBoss EAP web service subsystem detects classes containing JAX-WS annotations, it bootstraps a CXF endpoint. In this example the service endpoint will be located at http://hostname:port/context-root/greeting.
// Service interface @WebService(name = "greeting") public interface GreetingService { @WebMethod(operationName = "greet", action = "urn:greet") String greet(@WebParam(name = "message") String message, @WebParam(name = "name") String name); } // Service implementation public class GreetingServiceImpl implements GreetingService{ public String greet(String message, String name) { return message + " " + name ; } }
5.5.1.2. Camel route configuration
This RouteBuilder configures a CXF producer endpoint which will consume the 'greeting' web service defined above. CDI in conjunction with the camel-cdi component is used to bootstrap the RouteBuilder and CamelContext.
@Startup @ApplicationScoped @ContextName("cxf-camel-context") public class CxfRouteBuilder extends RouteBuilder { @Override public void configure() throws Exception { from("direct:start") .to("cxf://http://localhost:8080/example-camel-cxf/greeting?serviceClass=" + GreetingService.class.getName()); } }
The greeting web service 'greet' requires two parameters. These can be supplied to the above route by way of a ProducerTemplate
. The web service method argument values are configured by constructing an object array which is passed as the exchange body.
String message = "Hello" String name = "Kermit" ProducerTemplate producer = camelContext.createProducerTemplate(); Object[] serviceParams = new Object[] {message, name}; String result = producer.requestBody("direct:start", serviceParams, String.class);
5.5.2. Camel CXF JAX-WS Consumer
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://camel.apache.org/schema/cxf" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <cxf:cxfEndpoint id="cxfConsumer" address="http://localhost:8080/webservices/greeting" serviceClass="org.wildfly.camel.examples.cxf.jaxws.GreetingService" /> <camelContext id="cxfws-camel-context" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="cxf:bean:cxfConsumer" /> <to uri="log:ws" /> </route> </camelContext> </beans>
5.5.3. Security
Refer to the JAX-WS security section.
5.5.4. Quickstart examples in Fuse on EAP
A quickstart example is available in your Fuse on EAP installation at quickstarts/camel/camel-cxf-jaxws
directory.
5.6. JMS
Messaging support is provided through the JMS component which integrates with the JBoss EAP Messaging (ActiveMQ Artemis) subsystem.
Integration with other JMS implementations is possible through configuration of vendor specific resource adapters, or if not available, by using the JBoss Generic JMS resource adapter.
5.6.1. JBoss EAP JMS configuration
You can configure the JBoss EAP messaging subsystem through the standard JBoss EAP XML configuration files. For example, standalone.xml or domain.xml.
For the examples, that follow you use the embedded ActiveMQ Artemis in memory instance. You first configure a new JMS queue on the messaging subsystem by adding the following XML configuration to the jms-destinations section.
<jms-queue name="WildFlyCamelQueue"> <entry name="java:/jms/queue/WildFlyCamelQueue"/> </jms-queue>
Alternatively you could use a CLI script to add the queue.
$ jms-queue add --queue-address=WildFlyCamelQueue --entries=queue/WildFlyCamelQueue,java:/jms/queue/WildFlyCamelQueue
Also, you can create a messaging-deployment
configuration within a custom jms.xml deployment descriptor. See section 'Deployment of -jms.xml files' within the JBoss EAP messaging subsystem documentation for more information.
5.6.2. Camel route configuration
The following JMS producer and consumer examples make use of JBoss EAP’s embedded ActiveMQ Artemis server to publish and consume messages to and from destinations.
The examples also use CDI in conjunction with the camel-cdi component. JMS ConnectionFactory instances are injected into the Camel RouteBuilder through JNDI lookups.
5.6.2.1. JMS Producer
The DefaultJMSConnectionFactory
connection factory is injected into the RouteBuilder from JNDI. Under the JBoss EAP XML configuration, you can find the connection factory, within the messaging subsystem.
Next a timer endpoint runs every 10 seconds to send an XML payload to the WildFlyCamelQueue destination that has been configured earlier.
@Startup @ApplicationScoped @ContextName("jms-camel-context") public class JmsRouteBuilder extends RouteBuilder { @Resource(mappedName = "java:jboss/DefaultJMSConnectionFactory") private ConnectionFactory connectionFactory; @Override public void configure() throws Exception { JmsComponent component = new JmsComponent(); component.setConnectionFactory(connectionFactory); getContext().addComponent("jms", component); from("timer://sendJMSMessage?fixedRate=true&period=10000") .transform(constant("<?xml version='1.0><message><greeting>hello world</greeting></message>")) .to("jms:queue:WildFlyCamelQueue") .log("JMS Message sent"); } }
A log message will be output to the console each time a JMS message is added to the WildFlyCamelQueue destination. To verify that the messages really are being placed onto the queue, you can use the JBoss EAP administration console.
5.6.2.2. JMS Consumer
To consume JMS messages the Camel RouteBuilder implementation is similar to the producer example.
As before, the connection factory is discovered from JNDI, injected and set on the JMSComponent instance.
When the JMS endpoint consumes messages from the WildFlyCamelQueue destination, the content is logged to the console.
@Override public void configure() throws Exception { JmsComponent component = new JmsComponent(); component.setConnectionFactory(connectionFactory); getContext().addComponent("jms", component); from("jms:queue:WildFlyCamelQueue") .to("log:jms?showAll=true"); }
5.6.2.3. JMS Transactions
To enable Camel JMS routes to participate in JMS transactions, some additional configuration is required. Since camel-jms is built around spring-jms, you need to configure some Spring classes to enable them to work with JBoss EAP’s transaction manager and connection factory. The following code example demonstrates how to use CDI to configure a transactional JMS Camel route.
The camel-jms component requires a transaction manager of type org.springframework.transaction.PlatformTransactionManager
. Therefore, you start by creating a bean extending JtaTransactionManager
. Note that the bean is annotated with @Named
to allow the bean to be registered within the Camel bean registry. Also note that the JBoss EAP transaction manager and user transaction instances are injected using CDI.
@Named("transactionManager") public class CdiTransactionManager extends JtaTransactionManager { @Resource(mappedName = "java:/TransactionManager") private TransactionManager transactionManager; @Resource private UserTransaction userTransaction; @PostConstruct public void initTransactionManager() { setTransactionManager(transactionManager); setUserTransaction(userTransaction); } }
Next, you need to declare the transaction policy that you want to use. Again, use the @Named
annotation to make the bean available to Camel. The transaction manager is also injected so that a TransactionTemplate can be created with the desired transaction policy. PROPAGATION_REQUIRED in this instance.
@Named("PROPAGATION_REQUIRED") public class CdiRequiredPolicy extends SpringTransactionPolicy { @Inject public CdiRequiredPolicy(CdiTransactionManager cdiTransactionManager) { super(new TransactionTemplate(cdiTransactionManager, new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED))); } }
Now you can configure our Camel RouteBuilder class and inject the dependencies needed for the Camel JMS component. The JBoss EAP XA connection factory is injected together with the transaction manager that has been configured earlier.
In this example RouteBuilder, whenever any messages are consumed from queue1, they are routed to another JMS queue named queue2. Messages consumed from queue2 result in JMS transaction being rolled back using the rollback() DSL method. This results in the original message being placed onto the dead letter queue(DLQ).
@Startup @ApplicationScoped @ContextName("jms-camel-context") public class JMSRouteBuilder extends RouteBuilder { @Resource(mappedName = "java:/JmsXA") private ConnectionFactory connectionFactory; @Inject CdiTransactionManager transactionManager; @Override public void configure() throws Exception { // Creates a JMS component which supports transactions JmsComponent jmsComponent = JmsComponent.jmsComponentTransacted(connectionFactory, transactionManager); getContext().addComponent("jms", jmsComponent); from("jms:queue:queue1") .transacted("PROPAGATION_REQUIRED") .to("jms:queue:queue2"); // Force the transaction to roll back. The message will end up on the Wildfly 'DLQ' message queue from("jms:queue:queue2") .to("log:end") .rollback(); }
5.6.2.4. Remote JMS destinations
It is possible for one JBoss EAP instance to send messages to ActiveMQ Artemis destinations configured on another JBoss EAP instance through remote JNDI.
Some additional JBoss EAP configuration is required to achieve this. First an exported JMS queue is configured.
Only JNDI names bound in the java:jboss/exported
namespace are considered as candidates for remote clients, so the queue is named appropriately.
You must configure the queue on the JBoss EAP client application server andJBoss EAP remote server.
<jms-queue name="RemoteQueue"> <entry name="java:jboss/exported/jms/queues/RemoteQueue"/> </jms-queue>
Before the client can connect to the remote server, user access credentials need to be configured. On the remote server run the add user utility to create a new application user within the 'guest' group. This example has a user with the name 'admin' and a password of 'secret'.
The RouteBuilder implementation is different to the previous examples. Instead of injecting the connection factory, you need to configure an InitialContext and retrieve it from JNDI ourselves.
The configureInitialContext
method creates this InitialContext. Notice that you need to set a provider URL which should reference your remote JBoss EAP instance host name and port number. This example uses the JBoss EAP JMS http-connector, but there are alternatives documented here.
Finally the route is configured to send an XML payload every 10 seconds to the remote destination configured earlier - 'RemoteQueue'.
@Override public void configure() throws Exception { Context initialContext = configureInitialContext(); ConnectionFactory connectionFactory = (ConnectionFactory) initialContext.lookup("java:jms/RemoteConnectionFactory"); JmsComponent component = new JmsComponent(); component.setConnectionFactory(connectionFactory); getContext().addComponent("jms", component); from("timer://foo?fixedRate=true&period=10000") .transform(constant("<?xml version='1.0><message><greeting>hello world</greeting></message>")) .to("jms:queue:RemoteQueue?username=admin&password=secret") .to("log:jms?showAll=true"); } private Context configureInitialContext() throws NamingException { final Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory"); env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL, "http-remoting://my-remote-host:8080")); env.put(Context.SECURITY_PRINCIPAL, System.getProperty("username", "admin")); env.put(Context.SECURITY_CREDENTIALS, System.getProperty("password", "secret")); return new InitialContext(env); }
5.6.3. Security
Refer to the JMS security section.
5.6.4. Quickstart examples in Fuse on EAP
A quickstart example is available in your Fuse on EAP installation at quickstarts/camel/camel-jms
directory.
5.7. JMX
You can provide management support through the JMX component which integrates with the JBoss EAP JMX subsystem.
CamelContext camelctx = contextFactory.createWildflyCamelContext(getClass().getClassLoader()); camelctx.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { String host = InetAddress.getLocalHost().getHostName(); from("jmx:platform?format=raw&objectDomain=org.apache.camel&key.context=" + host + "/system-context-1&key.type=routes&key.name=\"route1\"" + "&monitorType=counter&observedAttribute=ExchangesTotal&granularityPeriod=500"). to("direct:end"); } }); camelctx.start(); ConsumerTemplate consumer = camelctx.createConsumerTemplate(); MonitorNotification notifcation = consumer.receiveBody("direct:end", MonitorNotification.class); Assert.assertEquals("ExchangesTotal", notifcation.getObservedAttribute());
5.8. JNDI
JNDI integration is provided by a JBoss EAP specific CamelContext as shown below:
InitialContext inictx = new InitialContext(); CamelContextFactory factory = inictx.lookup("java:jboss/camel/CamelContextFactory"); WildFlyCamelContext camelctx = factory.createCamelContext();
From a WildFlyCamelContext
you can obtain a preconfigured Naming Context
Context context = camelctx.getNamingContext(); context.bind("helloBean", new HelloBean());
which can then be referenced from Camel routes.
camelctx.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").beanRef("helloBean"); } }); camelctx.start();
5.9. JPA
JPA integration is provided by the Camel JPA component. You can develop Camel JPA applications by providing a persistence.xml configuration file together with some JPA annotated classes.
5.9.1. Example persistence.xml
In the following example, you can use the JBoss EAP in-memory ExampleDS datasource which is configured within the JBoss EAP standalone.xml configuration file.
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="camel"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <class>org.wildfly.camel.test.jpa.model.Customer</class> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.show_sql" value="true"/> </properties> </persistence-unit> </persistence>
5.9.2. Example JPA entitiy
@Entity @Table(name = "customer") public class Customer implements Serializable { @Id @GeneratedValue private Long id; private String firstName; private String lastName; public Customer() { } public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public Long getId() { return id; } public void setId(final Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
5.9.3. Camel JPA endpoint / route configuration
Having configured JPA, you can make use of CDI to inject an EntityManager and UserTransaction instance into your RouteBuilder class or test case:
@PersistenceContext EntityManager em; @Inject UserTransaction userTransaction;
Now to configure the Camel routes and JPA endpoint:
WildFlyCamelContext camelctx = contextFactory.createCamelContext(getClass().getClassLoader()); EntityManagerFactory entityManagerFactory = em.getEntityManagerFactory(); // Configure a transaction manager JtaTransactionManager transactionManager = new JtaTransactionManager(); transactionManager.setUserTransaction(userTransaction); transactionManager.afterPropertiesSet(); // Configure the JPA endpoint to use the correct EntityManagerFactory and JtaTransactionManager final JpaEndpoint jpaEndpoint = new JpaEndpoint(); jpaEndpoint.setCamelContext(camelctx); jpaEndpoint.setEntityType(Customer.class); jpaEndpoint.setEntityManagerFactory(entityManagerFactory); jpaEndpoint.setTransactionManager(transactionManager); camelctx.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start") .to(jpaEndpoint); } }); camelctx.start();
Finally, you can send a 'Customer' entity to the 'direct:start' endpoint and then query the ExampleDS datasource to verify that a record was saved.
Customer customer = new Customer("John", "Doe"); ProducerTemplate producer = camelctx.createProducerTemplate(); producer.sendBody("direct:start", customer); // Query the in memory database customer table to verify that a record was saved CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class); query.select(criteriaBuilder.count(query.from(Customer.class))); long recordCount = em.createQuery(query).getSingleResult(); Assert.assertEquals(1L, recordCount);