Apache Camel load XML routes and beans from file into CamelContext - java

So, now I am attempting to import routes from an XML file into the Java DSL.
I've been attempting to start with this link but since it's such a simple example, it doesn't really help me and doesn't point me to a more complicated example.
My problem is that my Camel routes use beans. Beans for the PropertiesComponent and FileIdempotentRepository and others are defined within the XML file for use by the routes in the XML file.
My original Spring configuration looked something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="bean1" class="class1" />
<bean id="bean2" class="class2" />
<bean id="bean3" class="FileIdempotentRepository"> [...] </bean>
<bean id="properties" class="PropertiesComponent"> [...] </bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="{{someplace}}&filter=#bean1" />
<setHeader headerName="FileRepoKey">
<simple>${file:name}-${file:modified}</simple>
</setHeader>
<idempotentConsumer messageIdRepositoryRef="bean3">
<header>FileRepoKey</header>
<process ref="bean2" />
<to uri="{{otherplace}}"/>
</idempotentConsumer>
</route>
</camelContext>
</beans>
So how do I convert this mess into something usable by the Java DSL to import routes from?
I understand from looking at that link that I need to do something like convert <camelContext> to <routes>. But leaving in the beans gives me an error along the lines of:
Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.springframework.org/schema/beans", local:"beans"). Expected elements are [...]
What do I need to change? Or can I not have beans in the XML file in order for it to be imported by the Java used in the link?

I guess I should've asked this a different way and maybe someone would have thought of this way.
It may give you all nightmares, I'm not sure. Be warned.
So since the concept is "have things potentially run from an XML file alongside Java" the following end result came about:
public static void main(String[] args) throws Exception {
Main main = new Main();
//the XML file has a CamelContext in it.
main.setApplicationContextUri("myRoutes.xml");
main.start();//instantiates the CamelContext so we can use it in Java
List<CamelContext> camelContexts = main.getCamelContexts(); //should only have 1 item in the list
CamelContext context = camelContexts.get(0);
//in order to add a component to the registry the following is needed for set up
// afterwards, should just be able to add anything to the registry with registry.put("name", object)
final SimpleRegistry registry = new SimpleRegistry();
final CompositeRegistry compositeRegistry = new CompositeRegistry();
compositeRegistry.addRegistry(context.getRegistry());
compositeRegistry.addRegistry(registry);
((DefaultCamelContext) context).setRegistry(compositeRegistry);
final FileIdempotentRepository myFileStore = new FileIdempotentRepository();
File myFile = new File("idempotentRepoFiles/myFileStore.txt");
final TimeStampFileFilter<?> myFileFilter = new TimeStampFileFilter<Object>(0L);
registry.put("myFileFilter", myFileFilter);
//512MB
myFileStore.setMaxFileStoreSize(536870912L);
myFileStore.setFileStore(myFile);
myFileStore.setCacheSize(100000);
//add a route to the CamelContext that was initially created in the XML file
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
onException(myException.class)
.handled(true);
onException(GenericFileOperationFailedException.class)
.onException(SocketException.class)
.maximumRedeliveries(2)
.redeliveryDelay(5000L)
;
Processor myProcessor = new myProcessor();
from("{{myStart}}&filter=#myFileFilter")
.setHeader("myFileRepoKey", simple("${file:name}-${file:modified}"))
.idempotentConsumer(header("myFileRepoKey"), myFileStore)
.process(myProcessor)
.to("{{myEnd}}")
;
}
});
context.start();
main.run();
}
Basically: create a CamelContext in the Spring XML file, initialize it, grab it, modify it to include routes built in Java.

Route definition in Camel can be XML based (Spring DSL or Blueprint DSL) or Java based (Java DSL). A route definition can be expressed equally in both languages.
In a Spring application you can define your beans in a file and your routes in other files which you import. Routes defined in external files can refer to beans defined in your main file.
spring-main.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="bean1" class="class1" />
<bean id="bean2" class="class2" />
<bean id="bean3" class="FileIdempotentRepository"> [...] </bean>
<bean id="properties" class="PropertiesComponent"> [...] </bean>
<import resource="camel-routes.xml"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<routeContextRef ref="ExternalRoutes"/>
</camelContext>
</beans>
camel-routes.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<routeContext id="ExternalRoutes" xmlns="http://camel.apache.org/schema/spring">
<route id="ARoute">
<from uri="direct:startHere" />
<to uri="bean:bean3" />
</route>
</routeContext>
</beans>
You can import more than one external file, of course. Just name each RouteContext differently.
If you modify one of the RouteContexts you must then restart your application. If you need a more dynamic application, try using an OSGi container to run your Camel routes, so you can easily modularize your application and add/remove features at runtime.

Related

How to configure route tracing in Apache Camel >= 3?

I'm trying to migrate from Camel 2.X to 3.X and have run in to a question about logging the routing trace.
Previously I have configured it like this in my application context xml-file:
<bean id="camelTracer" class="org.apache.camel.processor.interceptor.Tracer">
<property name="traceExceptions" value="false" />
<property name="traceInterceptors" value="true" />
<property name="logLevel" value="DEBUG" />
<property name="logName" value="com.mycompany.routing.trace" />
</bean>
<bean id="traceFormatter" class="org.apache.camel.processor.interceptor.DefaultTraceFormatter">
<property name="showBody" value="true" />
<property name="maxChars" value="0" />
</bean>
But that obviously does not work anymore.
From the migration guide on the Camel website:
"A new tracer has been implemented and the old tracer has been removed. The new tracer logs messages at the org.apache.camel.Tracing logger name which is hardcoded. The format of the output is also updated to make it better. The tracer can be customized."
If I set .tracing() at the start of my routes it does log the trace. The name is hardcoded which is fine, but I would like to change the level from INFO to DEBUG among other things.
Does anyone know where to find information on how to configure this "new" tracer (preferrably in an applicationContext.xml file)? Or anywhere else, maybe in the Java DSL route? Or if it is even possible?
Thanks!
Logging level of DefaultTracer cannot be changed by configuration. You need to implement customized Tracer and bind this implementation to registry.
Tracer:
public class TracerCustom extends DefaultTracer {
private static final Logger LOG = LoggerFactory.getLogger("com.stackoverflow.camel.TracerCustom");
#Override
protected void dumpTrace(String out) {
LOG.debug(out);
}
// Customize other methods if needed
}
Spring context:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean class="com.stackoverflow.camel.TracerCustom" />
<camelContext id="tracerCamelContext" xmlns="http://camel.apache.org/schema/spring">
<route trace="true">
<from uri="timer:test"/>
<to uri="log:test"/>
</route>
</camelContext>
</beans>

How do I implement a route in camel to receive messages from a JMS queue?

I've referred to the JMS page of the Camel documentation and many related SO questions such as this one, but I'm unable to find a comprehensive list on the implementation.
I'm using Spring XML along with Camel and Weblogic for the server. I've made a test queue with the following names:
Server: TestJMSServer, Module: TestJMSModule, Queue: TestJMSQueue, CF: TestConnectionFactory.
According to the Camel documentation, my route should look something like this:
<camel:route id="test">
<camel:from uri="jms:TestJMSQueue" />
<camel:to uri="file:/Users/...." />
</camel:route>
This gives me an error saying "connectionFactory must be specified". So exactly what else do I need to add to my applicationContext.xml in order to listen to this queue?
You need to tell Camel's jms-component which JMS connection factory to use. Most likely you'll get that from jndi if you're using WebLogic.
In the example below i am looking up the connection factory using spring's jee:jndi-lookup (i believe that might even be a name you can use in WebLogic). The looked up factory is then made available as a spring bean with id myConnectionFactory.
This connection factory bean is then used for the connectionFactory property for camel's JmsComponent. Notice the id attribute: jms. This defines the camel endpoint uri scheme to be used in your routes.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<jee:jndi-lookup id="myConnectionFactory" jndi-name="jms/connectionFactory"/>
<route id="test" xmlns="http://camel.apache.org/schema/spring">
<from uri="jms:TestJMSQueue"/>
<to uri="file:/Users/...."/>
</route>
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="myConnectionFactory"/>
<!-- more configuration required based on your requirements -->
</bean>
<!--
example uses invm amq broker:
<bean id="anothercnf" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://mybroker"/>
</bean>
-->
</beans>
Important Note: You will need to tune this further (setup transactions, setup concurrent consumers, possible configure a spring jms connection pool)

Use Autowire to get sftpChannel configured in spring xml

I am using the following spring configuration to transfer a file from local folder to remote SFTP server.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:sftp="http://www.springframework.org/schema/integration/sftp"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/sftp
http://www.springframework.org/schema/integration/sftp/spring-integration-sftp-2.2.xsd">
<bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="xxxxxxx" />
<property name="knownHosts" value = "C:\knownhosts"/>
<property name="user" value="wildfly" />
<property name="password" value="w!ldfly" />
<property name="port" value="22" />
</bean>
<int:channel id="sftpChannel" />
<sftp:outbound-channel-adapter id="triggerFtpOutBound" channel="sftpChannel"
session-factory="sftpSessionFactory" remote-directory="/home/wildfly">
</sftp:outbound-channel-adapter>
I am using the following code to send file.
#Autowired
private MessageChannel sftpChannel;
Function()
{
File f = new File("c:/test.txt");
Message<File> message = MessageBuilder.withPayload(f).build();
sftpChannel.send(message);
}
I am getting null pointer exception at sftpChannel.send(message). How can i autowire sftpChannel in my code?
The following code works. But, i want to Autowire sftpChannel.
ApplicationContext context = new ClassPathXmlApplicationContext("spring/config/spring-sftp.xml");
MessageChannel sftpChannel = context.getBean("sftpChannel", MessageChannel.class);
File f = new File("c:/test.txt");
Message<File> message = MessageBuilder.withPayload(f).build();
sftpChannel.send(message);
In order to use autowired you need to include
<context:annotation-config />
to your configuration file
<beans
//...
xmlns:context="http://www.springframework.org/schema/context"
//...
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
//...
<context:annotation-config />
//...
</beans>
Here, there is a full example
http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/
Autowiring works the same as any Spring application; the bean that gets the autowired channel must be declared as a bean in the context.
You can always debug Spring bean wiring by enabling DEBUG logging for org.springframework.
The framework puts out lots of information while wiring classes.
I'm not sure in your code, but that looks like you are going to use an #Autowired property from the constructor.
For this purpose you have to use the constructor injection.
Even if your Function is a valid Spring bean, it is a bad idea to send any message from the constructor.
Please, revise your design and read more books about Spring.

ActiveMQ and Java routes

I'm new to ActiveMQ (version 5.9.1) and Apache Camel. I'm experimenting about a couple of routes. I wrote them in Java, then packed in a .jar and deployed on %ACTIVEMQ_HOME%/lib. In my custom foo-activemq.xml I added a camelContext tag with a package child tag. Here is my relevant .xml
...
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<package>edu.foo.amq.camel</package>
</camelContext>
...
And here are my two routes:
package edu.foo.amq.camel;
import org.apache.camel.builder.RouteBuilder;
public class NumberRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("jms:queue:number.queue")
.marshal("UTF-8")
.choice()
.when(header("readyToGo").isNotEqualTo(true))
.to("jms:queue:big.number.queue");
}
}
and
package edu.foo.amq.camel;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
public class BigNumberRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("jms:queue:big.number.queue")
.marshal("UTF-8")
.split(body().tokenize("\n")).streaming()
.process(new Processor() {
#Override
public void process(Exchange arg0) throws Exception {
arg0.getIn().setHeader("readyToGo", true);
System.out.println(arg0.getIn().getBody(
String.class));
}
})
.to("jms:queue:number.queue");
}
}
When I start my ActiveMQ broker I can see ... Total 0 routes, of wich 0 is started.. If I look at the ActiveMQ web console I can se the queues number.queue and big.number.queue up and running but my java routes don't. What am I missing?
UPDATE:
I remove the camelContext tag from my foo-activemq.xml configuration file and add the import tag instead:
...
<import resource="jetty.xml"/>
<import resource="foo-camel.xml"/>
...
Here is my foo-camle.xml configuration file, I largely use the %ACTIVEMQ_HOME%/examples/conf/camel.xml file. I put this file into %ACTIVEMQ_HOME%/conf:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<package>edu.foo.amq.camel</package>
</camelContext>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent" >
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://amq-broker?create=false"/>
<property name="userName" value="${activemq.username}"/>
<property name="password" value="${activemq.password}"/>
</bean>
</property>
</bean>
but the result is the same: ActiveMQ says no routes
The Camel configuration XML file must be added to the activemq.xml configuration file:
<import resource="foo-activemq.xml" />
EDIT:
What else?
Rename foo-activemq.xml to the default activemq.xml
Verify again if your jar is really in the lib directory (I am sure you have done that already)
Add a simple test route to foo-camel-xml and check in the log file if this route is loaded, e.g.
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring" >
<package>edu.foo.amq.camel</package>
<route>
<from uri="direct:start" />
<log message="${body}" />
</route>
</camelContext>

Mbean JMX Spring Framework

I have a web application which has more than 40 Mbean. I used Spring Framework.
I am doing good and its working well. But i have 40 Mbean, so want to generalize the thing.
#Component
#ManagedResource(objectName="ProjectCache:name=XMBean", log=true, logFile="jmx.log")
public class XMBean extends AbstractCacheMBean<String, XCO, XCache> {
#ManagedOperation(description ="ProjectCache XCO key")
#Override
public List<String> showAllKeys(){
return super.getKey();
}
#ManagedOperation(description ="ProjectCache XCO")
public List<String> showAllElements(){
return super.findAll();
}
#Override
public XCache getCache() {
return getFacadeCache().getXCache();
}
#ManagedOperation(description ="ProjectCache XCO by key)
#Override
public String ShowbyKey(String key) {
return super.findbyKey(key);
}
}
Now i have Same way Class YMbean, AMBean and so.
I configured the Spring in application mbean.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/beans"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd">
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="server" ref="mbeanServer"/>
<property name="assembler" ref="assembler" />
<property name="namingStrategy" ref="namingStrategy" />
</bean>
<bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
<!-- will create management interface using annotation metadata -->
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<!-- will pick up the ObjectName from the annotation -->
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource" />
</bean>
<bean id="xMBean"
class="in.projet.business.mbean.XMBean">
<property name="memoryCache" ref="repository" />
</bean>
And same way i am going to preapre YMbean Class and in xml going to initialise.
What should i do that not require modification in XML Whatsoever or number of class i create ,dont require to update XML.
property is same in all Mbean which i am going to use.
All ideas or input are welcome.
Thanks
Remove all of your configuration and replace with the use of the namespace and only once. Also your MBeans are #Components so you can simply scan for them. Which only would leave you with the following lines of xml
<context:component-scan base-package="in.projet.business.mbean" />
<context:mbean-export/>
Or if you want to keep your current configuration instead of the namespace replace it at least with the following and remove all other beans. This enables autodetection of MBeans in your application context (this is basically the same as the <context:mbean-export /> does.
For more information I strongly suggest the JMX chapter of the reference guide.

Categories

Resources