How to pass parameters to Apache Camel through command line? - apache-camel

I use Apache Camel’s Spring Main to boot my Camel application. I need my application to read the command line arguments to set some parameters. So, I cannot use property files.
At the moment, I can pass arguments via the JVM system properties, and it works well:
Application.java
public class Application extends org.apache.camel.spring.Main {
public static void main(String[] args) throws Exception {
Application app = new Application();
instance = app;
app.run(args);
}
}
camel-context.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.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="shutdownBean" class="com.example.ShutdownBean" />
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file:{{inputFile}}?noop=true"/>
<to uri="bean:shutdownBean" />
</route>
</camelContext>
</beans>
I run the app with java com.example.Application -DinputFile=C:/absolute/path/to/watch and everything works fine:
…
FileEndpoint INFO Using default memory based idempotent repository with cache max size: 1000
InternalRouteStartupManager INFO Route: route1 started and consuming from: file://C:/absolute/path/to/watch
AbstractCamelContext INFO Total 1 routes, of which 1 are started
…
But I would like to have some input validation and make the app easier to use because -D could be confusing for a non Java user. So I change Application.java:
public class Application extends org.apache.camel.spring.Main {
private File inputFile;
public static void main(String[] args) throws Exception {
Application app = new Application();
instance = app;
app.run(args);
}
public Application() {
addOption(new ParameterOption("i", "inputFile", "The input file", "inputFile") {
#Override
protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) {
File file = FileUtils.getFile(parameter);
// some business validation
setInputFile(file);
}
});
}
private void setInputFile(File inputFile) {
this.inputFile = inputFile;
}
}
Then, I could use the following command to run the application: java com.example.Application -inputFile C:/absolute/path/to/watch
How can I use my inputFile field into my Camel route?

Call addProperty(String key, String value) in your doProcess method. Then it will be accessible throught {{key}} notation.
MyApplication:
public final class MyApplication extends Main {
private MyApplication() {
super();
addCliOption("g", "greeting", "Greeting");
addCliOption("n", "name", "Who to greet");
}
public static void main(String[] args) throws Exception {
MyApplication app = new MyApplication();
app.configure().addRoutesBuilder(MyRouteBuilder.class);
app.run(args);
}
private void addCliOption(String abbrevation, String parameterName, String description) {
addOption(new ParameterOption(abbrevation, parameterName, description, parameterName) {
protected void doProcess(String arg, String parameter, LinkedList<String> remainingArgs) {
addProperty("console." + parameterName, parameter);
}
});
}
}
MyRouteBuilder:
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("quartz:foo")
.log("{{console.greeting}} {{console.name}}");
}
}
java org.apache.camel.example.MyApplication -greeting Hello -name Morgan
23:10:25.862 [DefaultQuartzScheduler-MyCoolCamel_Worker-1] INFO route1 - Hello Morgan
23:10:26.832 [DefaultQuartzScheduler-MyCoolCamel_Worker-2] INFO route1 - Hello Morgan
23:10:27.829 [DefaultQuartzScheduler-MyCoolCamel_Worker-3] INFO route1 - Hello Morgan

Related

Mock the body of an intermediate route executed in Camel Tests

Available examples of the usage of the Camel Test component show how to test the expectations of a route:
However what I need to do is mock the body (manually setting it) of an intermediate route, e.g.:
<?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.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="exampleBean" class="xxx.ExampleBean"/>
<routeContext id="routesTest" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:route1" />
<to uri="direct:route2" />
<log message="${body}"/>
</route>
<route>
<from uri="direct:route2"/>
<to uri="bean:exampleBean"/>
<to uri="direct:route3" />
</route>
<route>
<from uri="direct:route3"/>
<log message="${body}"/>
</route>
</routeContext>
</beans>
In this scenario I want to completely avoid the actual execution of bean:exampleBean, mocking the result of its execution.
My test class:
public class MyTests extends CamelSpringTestSupport {
#Produce(uri = "direct:route1")
protected ProducerTemplate inputProducerTemplate;
#EndpointInject(uri = "mock:bean:exampleBean")
protected MockEndpoint mockBeanExampleBean;
#Test
public void testRoute() throws Exception {
CompletableFuture<Object> future = inputProducerTemplate.asyncSendBody("direct:route1", "Some message");
Object o = future.get();
}
#Override
public String isMockEndpoints() {
return "bean:exampleBean";
}
#Override
protected AbstractApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("spring/gesti-test-application-context.xml");
}
}
public class ExampleBean {
public String enhance(String message) {
System.out.println(message);
//Here I would call a REST API
return "MY API RESULT";
}
}
When using mockBeanExampleBean.whenAnyExchangeReceived(exchange -> exchange.getMessage().setBody("My message")); it allows to override the input to exampleBean, but doesn't avoid its execution.
In the context of your unit test, route2 might be a "mock" component instead. A clean way of achieving that is to declare the route(s) in the properties file. The legibility of the routes gets harder, though.
Then, you could:
#EndpointInject("mock://route2")
MockEndpoint mockSecondStep;
mockSecondStep.whenExchangeReceived(1, e -> {
List whatever = new ArrayList<>();
e.getMessage().setBody(whatever);
});
I solved it using an InterceptStrategy:
public class MyTests extends CamelSpringTestSupport {
#Test
public void testRoute() throws Exception {
CompletableFuture<Object> future = template.asyncSendBody("direct:route1", "Some message");
Object o = future.get();
assertEquals("INTERCEPTED!", o);
}
#Override
protected AbstractApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("spring/gesti-test-application-context.xml");
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
context.getProcessorDefinition("bean:exampleBean").addInterceptStrategy(
(context, definition, target, nextTarget) -> exchange -> exchange.getOut().setBody("INTERCEPTED!"));
}
};
}
}
public class ExampleBean {
public String enhance(String message) {
System.out.println(message);
//Here I would call a REST API
return "MY API RESULT";
}
}

How to perform Arquillian Testing with Apache Camel Routes

I'm new to Apache Camel. I have written simple program to place a file to another Location using camel routes. And I have written Junit and Mock Tests for that.
This is my simpleCamelRoute.java
#Component
public class SimpleCamelRoute extends RouteBuilder {
#Autowired
Environment environment;
#Override
public void configure() throws Exception {
from("{{startRoute}}").log("Timer Invoked and the body" + environment.getProperty("message"))
.pollEnrich("{{fromRoute}}").to("{{toRoute1}}");
}
}
this is SimpleCamelRouteTest.java
#ActiveProfiles("dev")
#RunWith(CamelSpringBootRunner.class)
#DirtiesContext(classMode =
DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#SpringBootTest
public class SimpleCamelRouteTest {
#Autowired
ProducerTemplate producerTemplate;
#Autowired
Environment environment;
#BeforeClass
public static void startCleanUp() throws IOException {
FileUtils.cleanDirectory(new File("data/input"));
FileUtils.deleteDirectory(new File("data/output"));
}
#Test
public void testMoveFile() throws InterruptedException {
String message = "type,sku#,itemdescription,price\n" + "ADD,100,Samsung TV,500\n" + "ADD,101,LG TV,500";
String fileName = "fileTest.txt";
producerTemplate.sendBodyAndHeader(environment.getProperty("fromRoute"), message, Exchange.FILE_NAME, fileName);
Thread.sleep(3000);
File outFile = new File("data/output/" + fileName);
// File outFile = new File("data/output/"+"abc.txt");
assertTrue(outFile.exists());
}
}
this is my application.yml file
spring:
profiles:
active: dev
---
spring:
profiles: mock
startRoute: direct:input
fromRoute : file:data/input?delete=true&readLock=none
toRoute1: mock:output
message: MOCK Environment
---
spring:
profiles: dev
startRoute: timer:hello?period=10s
fromRoute : file:data/input?delete=true&readLock=none
toRoute1: file:data/output
message: DEV Environment
---
Like the same way I tried with Mock Test through MockEndPoints.
I have Gone through Apache Camel official site: https://camel.apache.org/cdi-testing.html but I didn't understand the flow to test through Camel Arquillian Integration test.
How can I test my project through Arquillian.

Solr throwing rg.apache.solr.client.solrj.impl.HttpSolrServer$RemoteSolrException: Expected mime type application/octet-stream but got text/html

I am using Solr + Spring Data Solr for Solr Indexing.
I am getting following error when I am trying save the solr document.
HTTP Status 500 - Request processing failed; nested exception is org.springframework.data.solr.UncategorizedSolrException: Expected mime type application/octet-stream but got text/html.
Here is my code:
Config:
<?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:solr="http://www.springframework.org/schema/data/solr"
xsi:schemaLocation="http://www.springframework.org/schema/data/solr http://www.springframework.org/schema/data/solr/spring-solr.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<solr:repositories base-package="my.solr.repo"
multicore-support="true" />
<solr:solr-server id="solrServer" url="http://localhost:8983/solr" />
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg index="0" ref="solrServer" />
</bean>
</beans>
#Repository
public class SolrMyRepository extends
SimpleSolrRepository<SolrmyModel, Serializable> {
#Autowired
public SolrMyRepository(final SolrTemplate solrTemplate) {
super(solrTemplate);
}
}
#SolrDocument(solrCoreName = "my")
public class SolrMyModel {
#Id
#Indexed
private Long id;
#Indexed
private String myTitle;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMyTitle() {
return myTitle;
}
public void setMyTitle(String myTitle) {
this.myTitle = myTitle;
}
}
#Service
public class SolrMyService {
#Autowired
private SolrMyRepository solrMyRepository;
public void test() {
final SolrMyModel model = new SolrMyModel();
model.setId((long) 1);
model.setTitle("My title");
System.out.println(model);
solrMyRepository.save(model);
}
}
I am not sure what else I am missing but constantly I am getting this error.
Solution:
I got over the above problem as I have not create a solr core by running following command.
./solr create -c my
As now its working, how can I created core programatically?
I got over the above problem as I have not create a solr core by running following command.
./solr create -c my

Apache-Camel Accessing header values within string-template

my problem is, I don't know how I can access exchange's header values inside a string-template declaration. I would like to have internationalized mail templates. The test code below ...
public class StringTemplateTest extends CamelTestSupport {
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Test
public void testTemplating() throws Exception {
resultEndpoint.expectedBodiesReceived("test");
template.sendBodyAndHeader("test", "lang", "de");
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").to("string-template:mailTemplate_$simple{in.header.lang}.tm").to("mock:result");
}
};
}
}
ends in a ...
java.io.FileNotFoundException: Cannot find resource: mailTemplate_$simple{in.header.lang}.tm in classpath for URI: mailTemplate_$simple{in.header.lang}.tm
I would expect, the string-template is lookig for mailTemplate_de.tm.
Thank you for help in advance!
Your problem is that .to("component:xyz") endpoints are evaluated at the time the route is built - they are not dynamic and won't pick up ${} properties.
Instead you need to use recipientList, like this:
from("direct:start")
.recipientList(simple("string_template:mailTemplate_${in.header.lang}.tm"))
.to("mock:result")

Bootstrap camel in spring web application

I'm creating a application with Spring-jersey-camel. I wanted to expose my jersey layer and internally invoke camel routes to invoke resources.
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
applicationContext.xml
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
<packageScan>
<package>com.company.myapp.camel</package>
<excludes>**.*</excludes>
<includes>*Routes.java</includes>
</packageScan>
</camelContext>
MyRoutes.java
#Component
public final class MyRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:getOrdersData").validate(body().isNotNull())
.log("Camel to get orders")
.to("restlet:http://localhost:8081/ordersapp/rest/order/123");
}
}
OrderResourceImpl.java
#Component
#Path("/orderLookup")
public class ReservationResources {
#org.apache.camel.produce
ProducerTemplate producer;
public void setProducer(ProducerTemplate producer) throws Exception {
this.producer = producer;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{orderId}")
public Response orderLookup(#PathParam("orderId") final long orderrId){
Response r = Response.noContent().build();
//Producer is null. throws nullPointerException
String order= producer.requestBody("direct:getOrdersData", orderId, String.class);
r = Response.ok().entity(reservation).build();
return r;
}
}
Any idea what I'm doing wrong? or how to inject myRoute/ProducerTemplate im my orderResourceImpl.java. Thanks in advance
Two Options,
If ReservationResources is a spring bean then, Inject the Camel Context into it and create a ProducerTemplate from that
ProducerTemplate template = camelContext.createProducerTemplate();
If ReservationResources is not a spring bean then get the Camel Context via a static method https://stackoverflow.com/a/13633109/3696510 and then create the ProducerTemplate.
ProducerTemplate template = StaticSpringApplicationContext.getBean("camelContext").createProducerTemplate()
Also if you do use that StaticSpringApplicationContext mentioned in the link, I would add this method to it.
public static <T> T getBean(String beanName, Class<T> clazz) {
return (T) CONTEXT.getBean(beanName,clazz);
}

Resources