I have a standard route with a ftp uri as a consumer endpoint with a pollStrategy defined and added to the registry. However, I am getting the following error:
Caused by: java.lang.IllegalArgumentException: Could not find a suitable setter for property: pollStrategy as there isn't a setter method with same type: java.lang.String nor type conversion possible: No type converter available to convert from type: java.lang.String to the required type: org.apache.camel.spi.PollingConsumerPollStrategy with value #pollingStrategy
at org.apache.camel.util.IntrospectionSupport.setProperty(IntrospectionSupport.java:588)
at org.apache.camel.util.IntrospectionSupport.setProperty(IntrospectionSupport.java:616)
at org.apache.camel.util.IntrospectionSupport.setProperties(IntrospectionSupport.java:473)
at org.apache.camel.util.IntrospectionSupport.setProperties(IntrospectionSupport.java:483)
at org.apache.camel.util.EndpointHelper.setProperties(EndpointHelper.java:255)
at org.apache.camel.impl.DefaultComponent.setProperties(DefaultComponent.java:257)
at org.apache.camel.component.file.GenericFileComponent.createEndpoint(GenericFileComponent.java:67)
at org.apache.camel.component.file.GenericFileComponent.createEndpoint(GenericFileComponent.java:37)
at org.apache.camel.impl.DefaultComponent.createEndpoint(DefaultComponent.java:114)
at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:568)
I have tried different combinations but always end up with this error. Can anyone spot what I am missing? My code seems fairly similar to the Camel unit tests I looked at. The route looks like this:
import org.apache.camel.*;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultPollingConsumerPollStrategy;
import org.apache.camel.spi.PollingConsumerPollStrategy;
import org.apache.camel.util.ServiceHelper;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import static org.apache.camel.builder.ProcessorBuilder.setBody;
public class Test extends RouteBuilder {
final CamelContext camelContext = getContext();
final org.apache.camel.impl.SimpleRegistry registry = new org.apache.camel.impl.SimpleRegistry();
final org.apache.camel.impl.CompositeRegistry compositeRegistry = new org.apache.camel.impl.CompositeRegistry();
private final CountDownLatch latch = new CountDownLatch(1);
#Override
public void configure() throws Exception {
ExceptionBuilder.setup(this);
compositeRegistry.addRegistry(camelContext.getRegistry());
compositeRegistry.addRegistry(registry);
((org.apache.camel.impl.DefaultCamelContext) camelContext).setRegistry(compositeRegistry);
registry.put("pollingStrategy", new MyPollStrategy());
from("ftp://user#localhost/receive/in?password=1234&autoCreate=false&startingDirectoryMustExist=true&pollStrategy=#pollingStrategy&fileName=test.csv&consumer.delay=10m")
.convertBodyTo(String.class)
.log(LoggingLevel.INFO, "TEST", "${body} : ${headers}");
}
private class MyPollStrategy implements PollingConsumerPollStrategy {
int maxPolls=3;
public boolean begin(Consumer consumer, Endpoint endpoint) {
return true;
}
public void commit(Consumer consumer, Endpoint endpoint, int polledMessages) {
if (polledMessages > maxPolls) {
maxPolls = polledMessages;
}
latch.countDown();
}
public boolean rollback(Consumer consumer, Endpoint endpoint, int retryCounter, Exception cause) throws Exception {
return false;
}
}
}
Note, if I remove the pollStrategy reference in the uri then everything works.
Ok found the solution..must have had one too many beers when working on this..a bit too obvious.
final CamelContext camelContext = getContext();
final org.apache.camel.impl.SimpleRegistry registry = new org.apache.camel.impl.SimpleRegistry();
final org.apache.camel.impl.CompositeRegistry compositeRegistry = new org.apache.camel.impl.CompositeRegistry();
That part should be in the configure method and not in the class variable declaration part.
Related
Is it possible to call an object with the Simple language directly within the route and without side effects? The 2 approaches i've tried are;
.toD("${header.exchangeHelper.inc1()}") //works but fails if there is a return type from the called method
.bean(new Simple("${header.exchangeHelper.inc1()}")) //works but sets the body to false
Neither of which give the ideal solution.
You can store the result to exchange property or header instead. This way you'll keep the original body and get the result from your method in case you need it later. Alternatively you can just call the method using a processor.
These are generally better approaches with Java-DSL for something like this than using simple-language since they benefit from IDE's refactoring tools, error highlighting and many forms of linting.
package com.example;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.camel.Exchange;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class ExampleTest extends CamelTestSupport {
ExchangeHelper exchangeHelper = mock(ExchangeHelper.class);
#Test
public void useSetPropertyTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBodyAndHeader("direct:useSetProperty", "Hello",
"exchangeHelper", exchangeHelper);
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Test
public void justUseProcessorTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBody("direct:justUseProcessor", "Hello");
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Test
public void useHeaderFromProcessorTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBodyAndHeader("direct:useHeaderFromProcessor", "Hello",
"exchangeHelper", exchangeHelper);
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:useSetProperty")
.setProperty("result")
.simple("${header.exchangeHelper.inc1()}")
.log("Body: ${body} Result header: ${exchangeProperty.result}")
.to("mock:result")
.removeProperty("result");
from("direct:justUseProcessor")
.process( ex -> { exchangeHelper.inc1(); })
.log("Body: ${body}")
.to("mock:result");
from("direct:useHeaderFromProcessor")
.process( ex -> {
ex.getMessage()
.getHeader("exchangeHelper", ExchangeHelper.class)
.inc1();
})
.log("Body: ${body}")
.to("mock:result");
}
};
}
interface ExchangeHelper {
public boolean inc1();
}
}
Not tried, but why not using the wiretap EIP to issue an extra (and separated!) request to your requestHelper method ?
Something like:
from("direct:demo")
.wireTap("bean:${header.exchangeHelper.inc1()}")
.to("direct:doSomething");
You can simply use Camel Script, something like that:
from("direct:exampleScript")
.script().simple("${header.exchangeHelper.inc1()}")
.log("End")
;
What is the best way to make a small modification to some data in a Camel route?
I'm pulling in a BSON document from Mongo. I need to use a timestamp from it in an http call, but I need to convert it from milliseconds to seconds.
I tried setting a header.
.setHeader("test").jsonpath("$.startTime")
Which lets me add the timestamp to the URL with a Simple expression.
.toD("https://test.com/api/markets?resolution=60&start_time=${headers.test}")
But I can't find a way to modify the value of the header.
I also tried using a process
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
DocumentContext message = JsonPath.parse(exchange.getMessage().getBody());
String time = message.read("$.startTime").toString();
time = "111100000";
// do something with the payload and/or exchange here
//exchange.getIn().setBody("Changed body");
}
})
But here the exchange isn't passed back out. I based this on how I used an enrich EIP, with an aggregation strategy that returned an Exchange with the changes I made. This Process doesn't seem to work that way.
You can modify body, header or property using Lambda, processor or a bean. With processor you need to use Message.setHeader method to modify the value of the header at least for value types and strings. Bean methods receive body value by default so if you want to pass in header you'll need to specify it using simple language.
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class SetHeaderTest extends CamelTestSupport {
#Test
public void testGreeting() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(3);
template.sendBodyAndHeader("direct:modifyGreetingLambda",
null, "greeting", "Hello");
template.sendBodyAndHeader("direct:modifyGreetingProcessor",
null, "greeting", "Hello");
template.sendBodyAndHeader("direct:modifyGreetingBean",
null, "greeting", "Hello");
resultMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:modifyGreetingLambda")
.routeId("modifyGreetingLambda")
.setHeader("greeting").exchange(exchange -> {
String modifiedGreeting = (String)exchange.getMessage().getHeader("greeting");
modifiedGreeting += " world!";
return modifiedGreeting;
})
.log("${headers.greeting}")
.to("mock:result");
from("direct:modifyGreetingProcessor")
.routeId("modifyGreetingProcessor")
.process(new Processor(){
#Override
public void process(Exchange exchange) throws Exception {
String modifiedGreeting = (String)exchange.getMessage().getHeader("greeting");
modifiedGreeting += " world!";
exchange.getMessage().setHeader("greeting", modifiedGreeting);
}
})
.log("${headers.greeting}")
.to("mock:result");
from("direct:modifyGreetingBean")
.routeId("modifyGreetingBean")
.setHeader("greeting").method(new ModifyGreetingBean(),
"modifyGreeting('${headers.greeting}')")
.log("${headers.greeting}")
.to("mock:result");
}
};
}
public class ModifyGreetingBean {
public String modifyGreeting(String greeting) {
return greeting + " world!";
}
}
}
Aside from these you can also use expression languages like simple or groovy.
In the route you can set the header with the milliseconds value .setHeader("test").jsonpath("$.startTime").
Then in a processor you can retrieve this value:
String milliSecondsValue = (String) exchange.getIn().getHeader("test");
Then you transform the milliSecondsValue to the value you want and you set it back on the exchange:
exchange.getIn().setHeader("test", secondsValue);
After that call .toD("https://test.com/api/markets?resolution=60&start_time=${header.test}") and it will use the seconds value
I am trying the connect to Corda using that component and sending data to Apache ActiveMQ again using Apache Camel's Corda component.
Corda is running properly. Particularly, cardapp-example is running, and Notary- PartyA - PartyB and PartyC are alive. I can query using their terminal.
ActiveMQ is working properly, I test it with another input source.
I've also tried to connect difeerent localhost ports of all four nodes, and also the example one showed in the Camel's corda component webpage.
public class CordaConnector {
public void ConnectToCorda() throws Exception {
CamelContext context = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
context.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("corda://localhost:10004?username=user1&password=test&operation=VAULT_TRACK&contractStateClass=#contractStateClass").
}
});
while(true) {
context.start();
}
}
}
I got the following error message:
Exception in thread "main" org.apache.camel.FailedToCreateRouteException: Failed to create route route1: Route(route1)[From[corda://localhost:10004?username=user1&pa... because of Failed to resolve endpoint: corda://localhost:10004?contractStateClass=%23contractStateClass&operation=VAULT_TRACK&password=test&username=user1 due to: Error binding property (contractStateClass=#contractStateClass) with name: contractStateClass on bean: org.apache.camel.component.corda.CordaConfiguration#1de76cc7 with value: #contractStateClass
...
So when tested seperately, corda works properly, ActiveMQ works properly (with different output), and I ave tried different ports to query information. I have alos tried different commands to query, such as:
from("corda://localhost:10000?username=user1&password=test&operation=NETWORK_MAP_FEED").
to("activemq:queue:try");
I've checked this question Failed to create route route1, but was no help.
I would appreciate any help on what might be the reason.
In your route from uri, you are setting the contractStateClass property using value #contractStateClass : this references a bean named contractStateClass in the Camel registry. But since you did not bind any bean with this name in the context registry, Camel fail to resolve this value: Error binding property (contractStateClass=#contractStateClass) with name: contractStateClass on bean: org.apache.camel.component.corda.CordaConfiguration#1de76cc7 with value: #contractStateClass
You simply need to configure a bean of type Class and provide it to the camel registry. Something like that should work ( camel version 2.24.x )
import net.corda.core.contracts.OwnableState;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.SimpleRegistry;
public class CordaConnector {
public static void main(String[] args) {
try {
SimpleRegistry registry = new SimpleRegistry();
registry.put("contractStateClass", OwnableState.class);
CamelContext camelContext = new DefaultCamelContext(registry);
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from("corda://localhost:10004?username=user1&password=test&operation=VAULT_TRACK&contractStateClass=#contractStateClass")
.log("got message");
}
});
camelContext.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
EDIT for Camel v3.x :
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.support.SimpleRegistry;
public class CordaConnector {
public static void main(String[] args) {
try {
SimpleRegistry registry = new SimpleRegistry();
registry.bind("contractStateClass", MyContractClass.class);
CamelContext camelContext = new DefaultCamelContext(registry);
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from("corda://localhost:10004?username=user1&password=test&operation=VAULT_TRACK&contractStateClass=#contractStateClass")
.log("got message");
}
});
camelContext.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Suppose I have the following camel route:
.from("direct:start")
.log("received ${body} message")
.to("mock:end");
How would you test that the message "received Camel rocks!" message is logged when you send a "Camel rocks!" message to the direct:start endpoint
I would read the written file. Or add a custom appender to the logging system and assert that it received the message.
Or check Camel's internal unit tests.
But what exactly are you trying to achieve?
You are supposed to test your application and not the frameworks you are using.
I would not test the actual logging part, but why not save the data you are interested in a property or header, and then in your unit test assert that the value of that property or header must be such and such?
Here is a way to test this which I admit is a bit too invasive. It would have been much easier if AdviceWithBuilder would have been added something like replaceWith(ProcessDefinition replacement).
Here is the working example:
package com.my.org.some.pkg;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.testng.CamelTestSupport;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.springframework.test.util.ReflectionTestUtils;
import org.testng.annotations.Test;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CamelLoggingTest extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").id("abc")
.log("received ${body} message")
.to("mock:stop");
}
};
}
#Test
public void shouldLogExpectedMessage() throws Exception {
Logger logger = Mockito.mock(Logger.class);
context.getRouteDefinition("abc").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
ReflectionTestUtils.setField(context.getRouteDefinition("abc")
.getOutputs().get(0), "logger", logger);
}
});
when(logger.isInfoEnabled()).thenReturn(true);
sendBody("direct:start", "Camel rocks!");
verify(logger).info("received Camel rocks! message");
}
}
I have a simple Camel route test class that I have expanded to use a placeholder for an endpoint.
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.component.properties.PropertiesComponent;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
import java.util.Properties;
public class SimpleTestRoute extends CamelTestSupport {
#EndpointInject(uri = "{{test.route.out}}")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Test
public void test() throws Exception {
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").to("{{test.route.out}}");
}
};
}
#Override
protected CamelContext createCamelContext() throws Exception {
Properties props = new Properties();
props.setProperty("test.route.out", "mock:result");
CamelContext context = super.createCamelContext();
PropertiesComponent pc = context.getComponent("properties", PropertiesComponent.class);
pc.setOverrideProperties(props);
return context;
}
}
However, when I run I get
org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: {{test.route.out}} due to: Property with key [test.route.out] not found in properties from text: {{test.route.out}}
at ...
Caused by: java.lang.IllegalArgumentException: Property with key [test.route.out] not found in properties from text: {{test.route.out}}
at ...
I have tried various combinations of brace locations without progress. Now I suspect the problem is that the override is not being set but I cannot see what I am doing wrong or how to resolve it.
FWIW, I am running camel 2.13.1