Global onException and interceptor definitions outside Camel context? - apache-camel

I have multiple Camel applications (written in Spring DSL) and they all share the same onException and a number of interceptors defined globally in the camel context. I'm wondering if it's possible to have onException and interceptors defined in a separate XML file somewhere in the class path and include or inject it into camel context when the app starts. Java DSL has adviceWith, but is there such thin in Spring DSL as well?

This is not possible currently. However in Camel 3.12 onwards we are adding such feature which is called route configuration
You can read about it here: https://camel.apache.org/manual/latest/route-configuration.html

My temporary "solution" was to store the route configuration XML files in some pre-defined location, e.g. "/routeConfigurations" and I added the following code to my SpringBoot application class:
#Bean
public void initRouteConfigurations() throws Exception {
SpringCamelContext springCamelContext = (SpringCamelContext) camelContext;
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] routeConfigurations = resolver.getResources("/routeConfigurations/*");
for (Resource r : routeConfigurations) {
InputStream is = r.getURL().openStream();
ModelParser parser = new ModelParser(is, "http://camel.apache.org/schema/spring");
Optional<RouteConfigurationsDefinition> definitionsOptional = parser.parseRouteConfigurationsDefinition();
RouteConfigurationsDefinition routeConfigurationsDefinition = definitionsOptional.get();
springCamelContext.addRouteConfigurations(routeConfigurationsDefinition.getRouteConfigurations());
}
}

Related

FailedToStartRouteException exception while using camel-spring-boot, amqp and kafka starters with SpringBoot, unable to find connectionFactory bean

I am creating an application using Apache Camel to transfer messages from AMQP to Kafka. Code can also be seen here - https://github.com/prashantbhardwaj/qpid-to-kafka-using-camel
I thought of creating it as standalone SpringBoot app using spring, amqp and kafka starters. Created a route like
#Component
public class QpidToKafkaRoute extends RouteBuilder {
public void configure() throws Exception {
from("amqp:queue:destinationName")
.to("kafka:topic");
}
}
And SpringBoot application configuration is
#SpringBootApplication
public class CamelSpringJmsKafkaApplication {
public static void main(String[] args) {
SpringApplication.run(CamelSpringJmsKafkaApplication.class, args);
}
#Bean
public JmsConnectionFactory jmsConnectionFactory(#Value("${qpidUser}") String qpidUser, #Value("${qpidPassword}") String qpidPassword, #Value("${qpidBrokerUrl}") String qpidBrokerUrl) {
JmsConnectionFactory jmsConnectionFactory = new JmsConnectionFactory(qpidPassword, qpidPassword, qpidBrokerUrl);
return jmsConnectionFactory;
}
#Bean
#Primary
public CachingConnectionFactory jmsCachingConnectionFactory(JmsConnectionFactory jmsConnectionFactory) {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(jmsConnectionFactory);
return cachingConnectionFactory;
}
jmsConnectionFactory bean which is created using Spring Bean annotation should be picked by amqp starter and should be injected into the route. But it is not happening. When I started this application, I got following exception -
org.apache.camel.FailedToStartRouteException: Failed to start route route1 because of Route(route1)[From[amqp:queue:destinationName] -> [To[kafka:.
Caused by: java.lang.IllegalArgumentException: connectionFactory must be specified
If I am not wrong connectionFactory should be created automatically if I pass right properties in application.properties file.
My application.properties file looks like :
camel.springboot.main-run-controller = true
camel.component.amqp.enabled = true
camel.component.amqp.connection-factory = jmsCachingConnectionFactory
camel.component.amqp.async-consumer = true
camel.component.amqp.concurrent-consumers = 1
camel.component.amqp.map-jms-message = true
camel.component.amqp.test-connection-on-startup = true
camel.component.kafka.brokers = localhost:9092
qpidBrokerUrl = amqp://localhost:5672?jms.username=guest&jms.password=guest&jms.clientID=clientid2&amqp.vhost=default
qpidUser = guest
qpidPassword = guest
Could you please help suggest why during autoconfiguring connectionFactory object is not being used? When I debug this code, I can clearly see that connectionFactory bean is getting created.
I can even see one more log line -
CamelContext has only been running for less than a second. If you intend to run Camel for a longer time then you can set the property camel.springboot.main-run-controller=true in application.properties or add spring-boot-starter-web JAR to the classpath.
however if you see my application.properties file, required property is present at the very first line.
One more log line, I can see at the beginning of application startup -
[main] trationDelegate$BeanPostProcessorChecker : Bean 'org.apache.camel.spring.boot.CamelAutoConfiguration' of type [org.apache.camel.spring.boot.CamelAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Is this log line suggesting anything?
Note - One interesting fact that exactly same code was running fine last night, just restarted my desktop and there is not even a single word changed and now it is throwing exception.
This just refers to an interface
camel.component.amqp.connection-factory = javax.jms.ConnectionFactory
Instead it should refer to an existing factory instance, such as
camel.component.amqp.connection-factory = #myFactory
Which you can setup via spring boot #Bean annotation style.

Camel IgniteMessagingComponent

Hello I am trying use camel ignite messaging component to produce and consume the messages across the ignite cluster.
Please find the sample code :
IgniteMessagingComponent igniteMessagingComponent = IgniteMessagingComponent.fromConfiguration(igniteConfiguration);
igniteMessagingComponent.setCamelContext(camelContext);
igniteMessagingComponent.setIgniteConfiguration(igniteConfiguration);
igniteMessagingComponent.setIgnite(ignite);
igniteMessagingComponent.setIgniteConfiguration(igniteConfiguration);
Endpoint enp = igniteMessagingComponent.createEndpoint("ignite-messaging:topic?Topic=testtopicname&SendMode=ORDERED&Timeout=30000");
producerTemplate.sendBody(enp,"Test");
I am getting below error : java.lang.IllegalArgumentException: CamelContext must be specified on: Message[]
Please suggest what I have missed.
Got resolved it was configuration issue the way I have started my camel ignite-messaging subscriber's endpoint. After setting CamelContext explicitly to the endpoint It work successfully.
Sample code :
**IgniteMessagingComponent igniteMessagingComponent=
IgniteMessagingComponent.fromConfiguration(igniteConfiguration);
igniteMessagingComponent.setIgnite(ignite);
Map<String,Object> parameters = new HashMap();
IgniteMessagingEndpoint psenp = new IgniteMessagingEndpoint("ignite-messaging:", "topic1", parameters,igniteMessagingComponent);
psenp.setCamelContext(camelContext);
from(psenp).process(processor).end();**

Using httpclientBuilder in Apache Camel 2.21.5 Olingo4 component

Spring Boot 1.5.14 app using apache camel 2.21.5 and camel-olingo4 component. It seems to default to using the httpAsyncClientBuilder even though I explicitly set the httpClientBuilder in the configuration.
Setting the httpClientBuilder in the Olingo4Configuration explicitly in configuration class, but olingo4 component still wants to use the async httpclient.
looking closer at the Olingo4Component class.. seems the code ignores any configured httpClientBuilder and forces use of http async client builder.
private Olingo4AppWrapper createOlingo4App(Olingo4Configuration configuration) {
Object clientBuilder = configuration.getHttpAsyncClientBuilder();
if (clientBuilder == null) {
HttpAsyncClientBuilder asyncClientBuilder = HttpAsyncClientBuilder.create();
In my spring config class, i setup the Olingo configuration as bean:
#Bean
public Olingo4Configuration olingo4Configuration(HttpClientConfigurationProperties httpClientConfigProps,
MSDynamicsConfigurationProperties dynamicsConfigProps) {
Olingo4Configuration config = new Olingo4Configuration();
config.setHttpClientBuilder(httpClientBuilder(httpClientConfigProps, dynamicsConfigProps));
config.setServiceUri(dynamicsConfigProps.getServiceRoot());
return config;
}
I'm expecting Olingo comp. to use the http client builder i define above, but the http wire debug is showing it's using the aysnc client instead.
http-outgoing-0 >> "User-Agent: Apache-HttpAsyncClient/4.1.3 (Java/1.8.0_191)
Thoughts?

Application REST Client on Karaf

I'am writing a simple . application deploying on Karaf 4.1.0. It's role is sending a rest request to REST API. When I start my bundle I have an error:
javax.ws.rs.ProcessingException: org.apache.cxf.interceptor.Fault: No message body writer has been found for class package.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1149)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1094)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)
at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:428)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1631)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1626)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.post(WebClient.java:1566)
at org.apache.cxf.jaxrs.client.spec.InvocationBuilderImpl.post(InvocationBuilderImpl.java:145)
at package.worker.service.implementation.ConnectionServiceImpl.postCheckRequest(ConnectionServiceImpl.java:114)
at package.worker.service.implementation.ConnectionServiceImpl.sendCheck(ConnectionServiceImpl.java:103)
at package.worker.module.QueueSharedListener.run(QueueSharedListener.java:37)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.cxf.interceptor.Fault: No message body writer has been found for class package.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1222)
at org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handleMessage(AbstractClient.java:1091)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:649)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1093)
... 11 more
Caused by: javax.ws.rs.ProcessingException: No message body writer has been found for class com.emot.dto.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.java:780)
at org.apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.java:494)
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1217)
... 15 more
Initialization WebTarget:
private ConnectionServiceImpl() {
client = ClientBuilder.newClient();
client.property(
ClientProperties.CONNECT_TIMEOUT,
snifferProperties.getProperty(SnifferProperties.PARAM_REST_API_CONNECTION_TIMEOUT));
client.property(
ClientProperties.READ_TIMEOUT,
snifferProperties.getProperty(SnifferProperties.PARAM_REST_API_READ_TIMEOUT));
System.out.println(2);
webTarget = client.target(buildUrl());
}
Send requests :
private synchronized boolean postCheckRequest(String path, Object content) {
boolean result = true;
try {
Response response = webTarget
.path("check")
.path("add/one")
.request(MediaType.APPLICATION_JSON)
.post(Entity.json(content));
result = (response.getStatus() == 200);
} catch (Exception e) {
System.out.println("Error but working");
e.printStackTrace();
result = false;
}
return result;
}
I have always the problems with Karaf... i dont understand why it . couldn't working correctly...
The issue you are facing is mostly not a Karaf issue, but a typical issue you may face while working with some JAX-RS implementation in non-JavaEE environment.
Exception literally says that your implementation misses message body writer. Message body writer is the class which implements class javax.ws.rs.ext.MessageBodyWriter and is responsible for serializing your data objects to some format (like JSON). There is another class named javax.ws.rs.ext.MessageBodyReader, which does the opposite thing. All these classes are registered to JAX-RS framework as providers, extending its capabilities. Details are here: https://jersey.java.net/documentation/latest/message-body-workers.html
So, generally you must decide what you use for serializing/deserializing between your data objects and HTTP MediaType and register a proper JAX-RS provider.
With Jackson, for example, your problem can be easily solved by using one of its standard implementation: either com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, if you use JAXB annotations, or com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider, if you prefer Jackson annotations. Add this class in providers section of your Blueprint descriptor:
<jaxrs:server id="restServer" address="/rest">
<jaxrs:serviceBeans>
....
</jaxrs:serviceBeans>
<jaxrs:providers>
....
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
....
</jaxrs:providers>
</jaxrs:server>

Is there a way to skip "waiting for dependencies" for Camel blueprint testing?

I have some Camel blueprint unit tests running against a camel route. The route is a simple camel route that pulls messages from an activemq queue and then sends to another queue. I'm using an osgi service to expose the amq component I'm sending and receiving from.
<reference id="activemq-in" filter="(osgi.jndi.service.name=amq/in)" interface="org.apache.camel.Component" />
<camelContext>
<route>
<from uri="activemq-in:queue:some.queue" />
...
<to uri="activemq-in:queue:some.other.queue" />
</route>
</camelContext>
In my unit test, I'm stubbing out the amq component with something else however whenever I run my unit tests they always hang on waiting for the activemq dependencies for about 30 seconds before it gives up and the unit test runs successfully.
INFO BlueprintContainerImpl - Bundle UnitTest/1.0.0 is waiting for dependencies [(&(osgi.jndi.service.name=amq/in)(objectClass=org.apache.camel.Component))]
Is there any way I can have Camel blueprint testing skip the waiting for dependencies stage?
EDIT:
Sample blank unit test that will just load the blueprint, wait for 30 seconds for the osgi service that doesn't exist, then give up and pass:
public class CamelTest extends CamelBlueprintTestSupport {
// Loads the blueprint for the unit test
#Override
protected String getBlueprintDescriptor() {
return "OSGI-INF/blueprint/blueprint.xml";
}
// configures the osgi service to use the embedded amq broker instead of the osgi resource
#Override
protected BundleContext createBundleContext() throws Exception {
BundleContext bundleContext = super.createBundleContext();
ActiveMQComponent activeMQComponent = new ActiveMQComponent();
activeMQComponent.setBrokerURL("vm://amq");
Properties inboundProperties = new Properties();
inboundProperties.setProperty("osgi.jndi.service.name", "amq/in");
bundleContext.registerService("org.apache.camel.Component", activeMQComponent, (Dictionary) inboundProperties);
return bundleContext;
}
#Test
public void blankTest() {
}
}
If the service is not required for the unit test, then, you can create a fake blueprint/spring.xml file for your tests.. Except in this one, you don't start the service on start up. You have to refer to it in your test and can also choose not to start the normal bundle:
> #Override protected String getBundleFilter() {
> // don't want the normal marc21import bundle to start as it causes conflict to our test
> return "(!(Bundle-SymbolicName=<bundle_name>))"; }
>
> #Override protected String getBlueprintDescriptor() {
> return "blueprint/fake_blueprint.xml"; }
Yes, there is a way to handle this issue( I have gone through this annoying waiting for dependency stuff with blueprint unit testing). How I handle this issue ? Is by organising the bean definitions in its own files.
For example,
I keep bean declarations for CXF/JPA in its own bean-context.xml like , jpa-context.xml / cxf-context.xml.
And obviously, in my unit test case blueprint file I won't load these jpa/cxf bean contexts.
In this approach the routes also needs to be organised, You need to design the routes in such a way that, routes with these external integrations are kept in a separate light weight gateway routes. Keep all these gateway routes in a separate route context files. And don't load them in your blueprint unit test cases.
Lucky that we don't need to explicitly import files in blueprint. !
Why this approach ?
I don't use unit test cases for testing external integrations(You will anyway do integration testing), I keep it mainly for testing my routing logic, transformations & business rules.
You don't have to maintain 2 different beans-context files for testing & main.
Guess it helps !
Sample file structure:
bean-context.xml(in scope for unit testing)
cxf-context.xml(not required to include in unit testing)
jpa-context.xml(not required to include in unit testing)
gateway-routes.xml(not required to include in unit testing)
business-routes.xml(in scope for unit testing)
This way you won't see 'is waiting for dependencies' for cxf
Note: Also make sure to remove the unused xml namespace declarations in each of the xml files.

Resources