How to set the Exchange of a bean to ProducerTemplate - java

i m running a test with CamelTestSupport,
public class TestIntegrationBeanCtrlContrat extends CamelTestSupport {
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
this.from("direct:start")
.bean(MyClassA.class, "methodOfMyClassA").to("mock:result");
}
};
}
#Test
public void test_ControleBean_Integration() {
this.template.sendBody(....);
}
I m trying to put the body of another bean to the producer template , for exemple :
template.sendBody( bean(MyClassB.class, "methodOfMyClassB") )
Is it possible to do that ?
In general How can i do to setup the input in the produceTemplace.

I’m not sure I understand your needs but if you want to inject the result of some bean in the route process you should use Camel Mock to inject the bean process (MyClassB.methodOfMyClassB() in your example):
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").bean("BeanA", "methodA").to("mock:beanB").to("mock:result");
}
};
}
#Test
public void test() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:beanB");
mock.whenAnyExchangeReceived(new Processor() {
public void process(Exchange exchange) throws Exception {
// call the method of your class here
exchange.getIn().setBody(MyClassB.methodOfMyClassB());
}
});
template.sendBody("Your message body...");
// check some results
mock.assertIsSatisfied();
}

Related

How to test Apache Camel onCompletion().onFailureOnly() logic

Suppose I have Camel route like this:
#Component
public class MyRoute extends RouteBuilder {
private final BrokenBean brokenBean;
public MyRoute(BrokenBean brokenBean) {
this.brokenBean = brokenBean;
}
#Override
public void configure() throws Exception {
from("{{rabbitmq.inbound}}")
.errorHandler(defaultErrorHandler()
.maximumRedeliveries(1)
.redeliveryDelay(1000))
.end()
.onCompletion()
.onCompleteOnly()
.to("direct:success")
.end()
.onCompletion()
.onFailureOnly()
.to("direct:failure").id("failure")
.end()
.routeId("my_route")
.bean(brokenBean, "hello")
.to("direct:success").id("success");
from("direct:success")
.log("Success received");
from("direct:failure")
.log("Failed received");
}
And here is bean logic which is called from this route.
This is just an example.
#Component
public class BrokenBean {
public void hello() {
System.out.println("Hello called");
}
}
As route logic reveals and I tested it manually, if we got the exception
from BrokeBean the message would be routed to direct:failure and it does in runtime.
But in test below:
#RunWith(CamelSpringBootRunner.class)
#ContextConfiguration(classes = MyRouteTest.TestConfig.class)
public class MyRouteTest {
#Produce("direct:inbound")
protected ProducerTemplate directInbound;
#Autowired
SpringCamelContext context;
#MockBean
BrokenBean brokenBeanMock;
#Before
public void setUp() throws Exception {
AdviceWith.adviceWith(context.getRouteDefinition("my_route"),
context,
new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
onException(RuntimeException.class)
.continued(true);
weaveById("failure").replace().to("mock:direct:failure");
weaveById("success").replace().to("mock:direct:success");
}
});
}
#Test
public void testMyRouteFailedSuccessExpected() throws Exception {
MockEndpoint mockEndpoint = context.getEndpoint("mock:direct:failure",
MockEndpoint.class);
doThrow(new RuntimeException("Failed call")).when(brokenBeanMock).hello();
directInbound.sendBody("hello there");
mockEndpoint.expectedMessageCount(1);
mockEndpoint.assertIsSatisfied();
}
#Test
public void testMyRouteSuccessFailedExpected() throws Exception {
MockEndpoint mockEndpoint = context.getEndpoint("mock:direct:success",
MockEndpoint.class);
doThrow(new RuntimeException("Failed call")).when(brokenBeanMock).hello();
directInbound.sendBody("hello there");
mockEndpoint.expectedMessageCount(1);
mockEndpoint.assertIsSatisfied();
}
#Configuration
#Import({MyRoute.class})
public static class TestConfig extends CamelConfiguration {
#Bean
public BridgePropertyPlaceholderConfigurer bridgePropertyPlaceholderConfigurer() {
final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
final BridgePropertyPlaceholderConfigurer configurer = new BridgePropertyPlaceholderConfigurer();
yaml.setResources(new ClassPathResource("application.yml"));
configurer.setOrder(1);
configurer.setIgnoreUnresolvablePlaceholders(true);
configurer.setProperties(yaml.getObject());
return configurer;
}
}
}
I have the opposite result: testMyRouteSuccessFailedExpected succeeded and testMyRouteFailedSuccessExpected failed.
Which is not what I expected. Actually I tried a lot with adviceWith setup but with no luck.
It seems a simple case to check but behaviour look strange to me.
Any help appreciated. Thanks.
You can essentially only use 1 onCompletion in a route. You have 2. However we don't validate this on startup and report a problem.
Camel cannot understand that your 2 on completions would not overlap as one is for success and another for failure. So you need to only use 1 in your route.

Camel : DirectConsumerNotAvailable exception during test

I have the following RouteBuilder:
MyRouteBuilder.java
#Component
public class MyRouteBuilder extends SpringRouteBuilder {
#Autowired
private MQConnectionProperties mqConnectionProperties;
#Override
public void configure() {
setupExceptionHandler();
setupTransformerReceiveChannel();
setupOrchestrationChannel();
}
private void setupExceptionHandler() {
onException(Exception.class).handled(true).to("direct:error");
}
private void setupTransformerReceiveChannel() {
from(mqConnectionProperties.getTransformerReceiveQueue())
.routeId(TransformerConstants.TRANSFORM_CONSUME_ROUTE)
.log("Processing transform request.")
.setHeader(TransformerConstants.TRANSFORM_HEADER_STATUS, simple("SUCCESS"))
.to("direct:cosTransform")
.end();
}
private void setupOrchestrationChannel() {
from("direct:cosTransform")
.routeId(TransformerConstants.TRANSFORM_XSLT_ROUTE)
.process(new XSLTConfigurationProcessor())
.log("Executing an xsl transform for Market=${header.market} and LOB=${header.lineOfBusiness}")
.choice().id("transformBranch")
.when(header("market").isEqualTo("NI"))
.process(new TransformerNIProcessor()).id("NITransform")
.endChoice()
.otherwise()
.recipientList(simple("xslt:./xsl/${header.market}/${header.lineOfBusiness}.xsl?saxon=true&contentCache=false")).id("BITransform")
.end();
}
}
Note: Exception handler is defined as a separate routeBuilder class. I have the following unit test for MyRouteBuilder.
MyRouteBuilderTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyRouteTestConfiguration.class)
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class TransformerRouteBuilderUnitTest extends CamelTestSupport{
#Autowired
MQConnectionProperties mqConnectionProperties;
#Autowired
MyRouteBuilder myRouteBuilder;
MockEndpoint mockOutput;
MockEndpoint mockError;
MockEndpoint mockProcessor;
#Override
public boolean isUseAdviceWith() {
return true;
}
#Override
protected RoutesBuilder createRouteBuilder() {
return myRouteBuilder;
}
#Before
public void setup() throws Exception {
super.setUp();
}
#Override
public String isMockEndpoints() {
return "direct:cosTransform";
}
#Test
public void test_transformerReceiveChannel_happyPath_transformStatusHeaderSet() throws Exception {
startCamelContext();
mockOutput = getMockEndpoint("mock:direct:cosTransform", false);
mockOutput.expectedHeaderReceived("TransformStatus", "fail");
template.requestBody(mqConnectionProperties.getTransformerReceiveQueue(), new DefaultExchange(context));
MockEndpoint.assertIsSatisfied(context);
stopCamelContext();
}
stack trace
org.apache.camel.CamelExecutionException: Exception occurred during execution on the exchange: Exchange[ID-LIBP03P-QK70A9V-57085-1490361963523-1-2]
at org.apache.camel.util.ObjectHelper.wrapCamelExecutionException(ObjectHelper.java:1706)
at org.apache.camel.util.ExchangeHelper.extractResultBody(ExchangeHelper.java:660)
at org.apache.camel.impl.DefaultProducerTemplate.extractResultBody(DefaultProducerTemplate.java:471)
at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:133)
at org.apache.camel.impl.DefaultProducerTemplate.sendBody(DefaultProducerTemplate.java:149)
at org.apache.camel.impl.DefaultProducerTemplate.requestBody(DefaultProducerTemplate.java:301)
at com.transformer.routing.MyRouteBuilderUnitTest.test_transformerReceiveChannel_happyPath_transformStatusHeaderSet(TransformerRouteBuilderUnitTest.java:89)
.....
Caused by: org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://error]. Exchange[ID-LIBP03P-QK70A9V-57085-1490361963523-1-4]
....
I've read a few solutions to this and most answers revolve around failing to start the camel context, or having a second context that is being used rather than the context with the modified route etc., but I don't think that's the case here; debugging shows only 1 context which definately gets started, which only contains a route definition for MyRouteBuilder(as opposed to the route + the exception handling route).

Autowired in CustomInterceptor getting null(Spring Boot) [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 7 years ago.
I want to make a check in database when i receive a request. So i did a Interceptor like below,
CustomInterceptor.java
#Component
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Autowired
private DatabaseService databaseService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//Set Request Attribute(TODO)
LogService.info(this.getClass().getName(), "New Request URI is:" + request.getRequestURI());
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String authToken = request.getHeader("AuthToken");
boolean isValidRequest = databaseService.checkIfTokenIsValid(authToken);
}
}
Application.class:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
// protected Properties props = new Properties();
//
// public Application() {
// props.setProperty("error.path", "/error");
//// props.setProperty("error.whitelabel.enabled", "false");
//// props.setProperty("org.springframework.web", "DEBUG");
// }
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// application.properties(props);
return application.sources(Application.class);
}
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
LogService.info(Application.class.getName(), "Loading Service...");
super.onStartup(servletContext);
LogService.info(Application.class.getName(), "Service Started");
}
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
}
DatabasService.java
#Service
public class DatabaseService {
#Autowired
private ApplicationProperties properties;
private final JdbcTemplate defaultJdbcTemplate;
#Autowired
public DatabaseService(
#Qualifier("dataSource") DataSource dataSource) {
defaultJdbcTemplate = new JdbcTemplate(dataSource);
}
public boolean checkIfTokenIsValid() {
//Perform Check
}
}
CustomWebConfiguration.java
#Configuration
#EnableWebMvc
public class CustomWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor())
.addPathPatterns("/**");
}
}
But i get NullPointer At: boolean isValidRequest = databaseService.checkIfTokenIsValid(authToken);
What is wrong here, why cannot spring Autowire the Databaseservice in Interceptor?
Note: Autowire works fine everywhere else, but not in the interceptor.
Solution (Thanks to M. Deinum)
Change the CustomWebConfiguration.java like below;
#Configuration
#EnableWebMvc
public class CustomWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Bean
public CustomInterceptor customInterceptor() {
return new CustomInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customInterceptor())
.addPathPatterns("/**");
}
}
Spring will only autowire beans it knows about, you are creating the instance yourself outside the control of Spring.
Either inject a CustomInterceptor into your configuration class or add a #Bean method to make it a Spring managed instance. Then use that instance to add it to the list of interceptors.
It is OK to use #SpringBootApplication on your main class, however don't you miss #EnableAutoConfiguration and #ComponentScan on the same class to tell Spring Boot to look for other components and services automatically?

Mock input queue in apache camel

Hi I have application using apache camel and input queue that is a start point of the processing. I'm trying to find a nice way to mock somehow this input queue so :
I reuse the production routing file, I don't want to copy and paste the contents and make just one change for the routing of the queue
I can send the message to this 'mocked' queue and processing is done as in production
This is probably about changing 'queue:' into 'direct:' routing, but I couldn't find any other way than specifying another xml.
You can use Camels AdviceWith method to intercept messages during testing:
public class MySuperTest extends CamelTestSupport {
public void testAdvised() throws Exception {
// advice the first route using the inlined route builder
context.getRouteDefinitions().get(0).adviceWith(context, new RouteBuilder() {
#Override
public void configure() throws Exception {
// intercept sending to mock:foo and do something else
interceptSendToEndpoint("mock:foo")
.skipSendToOriginalEndpoint()
.to("log:foo")
.to("mock:advised");
}
});
getMockEndpoint("mock:foo").expectedMessageCount(0);
getMockEndpoint("mock:advised").expectedMessageCount(1);
getMockEndpoint("mock:result").expectedMessageCount(1);
template.sendBody("direct:start", "Hello World");
assertMockEndpointsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
//TODO build your route here
from("direct:start").process(...).to("mock:result");
}
};
}
}
You can advise a route to replace your from-component, e.g. replace the amq endpoint with a direct endpoint. You can then use a producer template to trigger the route in your test.
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/META-INF/spring/your-context.xml" })
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#MockEndpoints("none")
#UseAdviceWith
public class ReplaceFromTest {
#Autowired
protected CamelContext context;
#Produce(context = "your-camel-context-id")
protected ProducerTemplate template;
#Before
public void setUp() throws Exception {
AdviceWithRouteBuilder mockAmq = new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:amq-mock");
}
};
((ModelCamelContext) context).getRouteDefinition("route_to_advise").adviceWith((ModelCamelContext) context, mockAmq);
context.start();
}
#After
public void tearDown() throws Exception {
context.stop();
}
#DirtiesContext
#Test
public void sendMessageTest() {
Map<String, Object> myHeaders = new HashMap<>();
String myBody = "Some content";
template.sendBodyAndHeaders("direct://amq-mock", myBody, myHeaders);
// Verify the results
}
}
HTH.
I have created class like this
import org.apache.camel.CamelContext;
public class JmsToSedaComponent {
private CamelContext camelContext;
public JmsToSedaComponent(CamelContext camelContext) {
this.camelContext = camelContext;
}
public void init() {
camelContext.removeComponent("jms");
camelContext.addComponent("jms", camelContext.getComponent("seda"));
}
}
and then in the spring xml file:
<bean class="com.lmig.ci.baods.dial.integration.JmsToSedaComponent" init-method="init">
<constructor-arg ref="camelContext"/>
</bean>
This replaces all JMS components to SEDA.

Am I testing correctly?

I have a Camel app that is integrated with spring and i would like to write tests for it. Here's my app:
camel-config.xml
<camelContext xmlns="http://camel.apache.org/schema/spring">
<routeBuilder ref="converter" />
</camelContext>
<bean id="converter" class="Converter"/>
class to be tested:
#Component
public class Converter extends SpringRouteBuilder {
#Override
public void configure() throws Exception {
final XmlJsonDataFormat xmlJsonFormat = new XmlJsonDataFormat();
xmlJsonFormat.setTypeHints(String.valueOf("YES"));
from("ftp://Mike#localhost?" +
"noop=true&binary=true&consumer.delay=5s&include=.*xml")
.idempotentConsumer(header("CamelFileName"), FileIdempotentRepository.fileIdempotentRepository(new File("data", "repo.dat")))
.marshal(xmlJsonFormat).to("file://data").process(
new Processor() {
//System.out.println();
}
});
}
}
And here's my testing class:
public class RouteTest extends CamelTestSupport {
#Override
protected CamelContext createCamelContext() throws Exception {
CamelContext context = super.createCamelContext();
context.addComponent("ftp", context.getComponent("seda"));
return context;
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("ftp://Mike#localhost").to("mock:quote");
}
};
}
#Test
public void testSameMessageArrived() throws Exception {
MockEndpoint quote = getMockEndpoint("mock:quote");
FileReader fl = new FileReader("D:\\test\\asdasd.txt");
quote.expectedBodiesReceived(fl);
template.sendBody("ftp://Mike#localhost", fl);
quote.assertIsSatisfied();
}
}
This test passes, but I'm not sure that it's the right way to test this particular program.
Could you please tell me if I'm doing it right, or I should test it other way?
A better way would be to not rewrite the route. Use your actual route instead.
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new Converter();
}
And then use camel-mock, this let you intercept existing endpoint like this:
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock all endpoints
mockEndpoints();
}
});
getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World");
Or with patterns:
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock only log endpoints
mockEndpoints("log*");
}
});
// now we can refer to log:foo as a mock and set our expectations
getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
If you want more info on testing with Camel, I seriously advice you to read the "Camel in Action" book.
EDIT: here is a response of Claus Ibsen to a similar question (stack).
You can do integration testing with a dedicated or embedded FTP server or you can use mocks for unit testing, depending on what you want to test. You could also do both.

Categories

Resources