Tutorial-JmsRemotingPage edited by Claus IbsenTutorial on Spring Remoting with JMS
PrefaceThis tutorial aims to guide the reader through the stages of creating a project which uses Camel to facilitate the routing of messages from a JMS queue to a Spring service. The route works in a synchronous fashion returning a response to the client.
PrerequisitesThis tutorial uses Maven to setup the Camel project and for dependencies for artifacts. DistributionThis sample is distributed with the Camel distribution as examples/camel-example-spring-jms. AboutThis tutorial is a simple example that demonstrates more the fact how well Camel is seamless integrated with Spring to leverage the best of both worlds. This sample is client server solution using JMS messaging as the transport. The sample has two flavors of servers and also for clients demonstrating different techniques for easy communication. The Server is a JMS message broker that routes incoming messages to a business service that does computations on the received message and returns a response.
We use the following Camel components:
Create the Camel Project
mvn archetype:create -DgroupId=org.example -DartifactId=CamelWithJmsAndSpring
AOP Enabled ServerThe example has an enhanced Server example that uses fullblown AspejctJ AOP for doing a audit tracking of invocations of the business service. We leverage Spring AOP support in the {{camel-server-aop.xml} configuration file. First we must declare the correct XML schema's to use: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:camel="http://camel.apache.org/schema/spring" xmlns:context="http://www.springframework.org/schema/context" xmlns:broker="http://activemq.apache.org/schema/core" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"> Then we include all the existing configuration from the normal server example: <!-- let Spring do its IoC stuff in this package --> <context:component-scan base-package="org.apache.camel.example.server"/> <!-- lets configure the ActiveMQ JMS broker server to listen on TCP 61610 --> <broker:broker useJmx="false" persistent="false" brokerName="localhost"> <broker:transportConnectors> <broker:transportConnector name="tcp" uri="tcp://localhost:61610"/> </broker:transportConnectors> </broker:broker> <!-- lets configure the Camel JMS consumer to use the ActiveMQ broker declared above --> <bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory"> <bean class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61610"/> </bean> </property> </bean> Then we enable the AspejctJ AOP auto proxy feature of Spring that will scan for classes annotated with the @Aspect annotation: <!-- turn on AspejctJ AOP to weave all @Aspects beans declared in this spring xml file --> <aop:aspectj-autoproxy/> Then we define our Audit tracker bean that does the actual audit logging. It's also the class that is annotated with the @Aspect so Spring will pick this up, as the aspect. <!-- Aspect that tracks all the invocations of the business service --> <bean id="AuditTracker" class="org.apache.camel.example.server.AuditTracker"> <!-- define what store to use for audit backup --> <property name="store" ref="AuditStore"/> </bean> And the gem is that we inject the AuditTracker aspect bean with a Camel endpoint that defines where the audit should be stored. Noticed how easy it is to setup as we have just defined an endpoint URI that is file based, meaning that we stored the audit tracks as files. We can change this tore to any Camel components as we wish. To store it on a JMS queue simply change the URI to jms:queue:audit. <!-- declare a camel context that scans for classes that is RouteBuilder in the package org.apache.camel.example.server --> <camel:camelContext id="camel"> <camel:package>org.apache.camel.example.server</camel:package> <!-- enable JMX connector so we can connect to the server and browse mbeans --> <!-- Camel will log at INFO level the service URI to use for connecting with jconsole --> <camel:jmxAgent id="agent" createConnector="true"/> <!-- the audit store endpoint is configued as file based. In Camel 2.0 the endpoint should be defined in camel context --> <camel:endpoint id="AuditStore" uri="file://target/store"/> </camel:camelContext> And the full blown Aspejct for the audit tracker java code: /** * For audit tracking of all incoming invocations of our business (Multiplier) */ @Aspect public class AuditTracker { // endpoint we use for backup store of audit tracks private Endpoint store; @Required public void setStore(Endpoint store) { this.store = store; } @Before("execution(int org.apache.camel.example.server.Multiplier.multiply(int)) && args(originalNumber)") public void audit(int originalNumber) throws Exception { String msg = "Someone called us with this number " + originalNumber; System.out.println(msg); // now send the message to the backup store using the Camel Message Endpoint pattern Exchange exchange = store.createExchange(); exchange.getIn().setBody(msg); store.createProducer().process(exchange); } } Run the ServerThe Server is started using the org.apache.camel.spring.Main class that can start camel-spring application out-of-the-box. The Server can be started in several flavors:
In this sample as there are two servers (with and without AOP) we have prepared some profiles in maven to start the Server of your choice. Or for the AOP enabled Server example: Writing The ClientsThis sample has three clients demonstrating different Camel techniques for communication
Client Using The ProducerTemplateWe will initially create a client by directly using ProducerTemplate. We will later create a client which uses Spring remoting to hide the fact that messaging is being used. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> <camel:camelContext id="camel"> <camel:template id="camelTemplate"/> </camel:camelContext> <!-- Camel JMSProducer to be able to send messages to a remote Active MQ server --> <bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent"> <property name="brokerURL" value="tcp://localhost:61610"/> </bean> The client will not use the Camel Maven Plugin so the Spring XML has been placed in src/main/resources to not conflict with the server configs.
And the CamelClient source code: public static void main(final String[] args) throws Exception { System.out.println("Notice this client requires that the CamelServer is already running!"); ApplicationContext context = new ClassPathXmlApplicationContext("camel-client.xml"); // get the camel template for Spring template style sending of messages (= producer) ProducerTemplate camelTemplate = (ProducerTemplate) context.getBean("camelTemplate"); System.out.println("Invoking the multiply with 22"); // as opposed to the CamelClientRemoting example we need to define the service URI in this java code int response = (Integer)camelTemplate.sendBody("jms:queue:numbers", ExchangePattern.InOut, 22); System.out.println("... the result is: " + response); System.exit(0); } The ProducerTemplate is retrieved from a Spring ApplicationContext and used to manually place a message on the "numbers" JMS queue. The exchange pattern (ExchangePattern.InOut) states that the call should be synchronous, and that we will receive a response. Before running the client be sure that both the ActiveMQ broker and the CamelServer are running. Client Using Spring RemotingSpring Remoting "eases the development of remote-enabled services". It does this by allowing you to invoke remote services through your regular Java interface, masking that a remote service is being called. <!-- Camel proxy for a given service, in this case the JMS queue In Camel 2.0 , the proxy should be defined in camelContext. --> <camel:proxy id="multiplierProxy" serviceInterface="org.apache.camel.example.server.Multiplier" serviceUrl="jms:queue:numbers"/> The snippet above only illustrates the different and how Camel easily can setup and use Spring Remoting in one line configurations. The proxy will create a proxy service bean for you to use to make the remote invocations. The serviceInterface property details which Java interface is to be implemented by the proxy. serviceUrl defines where messages sent to this proxy bean will be directed. Here we define the JMS endpoint with the "numbers" queue we used when working with Camel template directly. The value of the id property is the name that will be the given to the bean when it is exposed through the Spring ApplicationContext. We will use this name to retrieve the service in our client. I have named the bean multiplierProxy simply to highlight that it is not the same multiplier bean as is being used by CamelServer. They are in completely independent contexts and have no knowledge of each other. As you are trying to mask the fact that remoting is being used in a real application you would generally not include proxy in the name. And the Java client source code: public static void main(final String[] args) { System.out.println("Notice this client requires that the CamelServer is already running!"); ApplicationContext context = new ClassPathXmlApplicationContext("camel-client-remoting.xml"); // just get the proxy to the service and we as the client can use the "proxy" as it was // a local object we are invoking. Camel will under the covers do the remote communication // to the remote ActiveMQ server and fetch the response. Multiplier multiplier = (Multiplier)context.getBean("multiplierProxy"); System.out.println("Invoking the multiply with 33"); int response = multiplier.multiply(33); System.out.println("... the result is: " + response); System.exit(0); } Again, the client is similar to the original client, but with some important differences.
Client Using Message Endpoint EIP PatternThis client uses the Message Endpoint EIP pattern to hide the complexity to communicate to the Server. The Client uses the same simple API to get hold of the endpoint, create an exchange that holds the message, set the payload and create a producer that does the send and receive. All done using the same neutral Camel API for all the components in Camel. So if the communication was socket TCP based you just get hold of a different endpoint and all the java code stays the same. That is really powerful. Okay enough talk, show me the code! public static void main(final String[] args) throws Exception { System.out.println("Notice this client requires that the CamelServer is already running!"); ApplicationContext context = new ClassPathXmlApplicationContext("camel-client.xml"); CamelContext camel = (CamelContext) context.getBean("camel"); // get the endpoint from the camel context Endpoint endpoint = camel.getEndpoint("jms:queue:numbers"); // create the exchange used for the communication // we use the in out pattern for a synchronized exchange where we expect a response Exchange exchange = endpoint.createExchange(ExchangePattern.InOut); // set the input on the in body // must you correct type to match the expected type of an Integer object exchange.getIn().setBody(11); // to send the exchange we need an producer to do it for us Producer producer = endpoint.createProducer(); // start the producer so it can operate producer.start(); // let the producer process the exchange where it does all the work in this oneline of code System.out.println("Invoking the multiply with 11"); producer.process(exchange); // get the response from the out body and cast it to an integer int response = exchange.getOut().getBody(Integer.class); System.out.println("... the result is: " + response); // stop and exit the client producer.stop(); System.exit(0); } Switching to a different component is just a matter of using the correct endpoint. So if we had defined a TCP endpoint as: "mina:tcp://localhost:61610" then its just a matter of getting hold of this endpoint instead of the JMS and all the rest of the java code is exactly the same. Run the ClientsThe Clients is started using their main class respectively.
In this sample we start the clients using maven: Also see the Maven pom.xml file how the profiles for the clients is defined. Using the Camel Maven PluginThe Camel Maven Plugin allows you to run your Camel routes directly from Maven. This negates the need to create a host application, as we did with Camel server, simply to start up the container. This can be very useful during development to get Camel routes running quickly. pom.xml <build> <plugins> <plugin> <groupId>org.apache.camel</groupId> <artifactId>camel-maven-plugin</artifactId> </plugin> </plugins> </build> All that is required is a new plugin definition in your Maven POM. As we have already placed our Camel config in the default location (camel-server.xml has been placed in META-INF/spring/) we do not need to tell the plugin where the route definitions are located. Simply run mvn camel:run. Using Camel JMXCamel has extensive support for JMX and allows us to inspect the Camel Server at runtime. As we have enabled the JMXAgent in our tutorial we can fire up the jconsole and connect to the following service URI: service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi/camel. Notice that Camel will log at INFO level the JMX Connector URI:
...
DefaultInstrumentationAgent INFO JMX connector thread started on service:jmx:rmi:///jndi/rmi://claus-acer:1099/jmxrmi/camel
...
In the screenshot below we can see the route and its performance metrics: See Also
Change Notification Preferences
View Online
|
View Change
|
Add Comment
|