I am using Spring Boot to initiate a camel route that uses Camel-sql to query MySQL DB and call a REST service.
application.properties
db.driver=com.mysql.jdbc.Driver
db.url=mysql://IP:PORT/abc
db.username=abc
db.password=pwd
Application.java
public static void main(String[] args) {
SpringApplication.run(WlEventNotificationBatchApplication.class, args);
}
DataSourceConfig.java
public class DataSourceConfig {
#Value("${db.driver}")
public String dbDriver;
#Value("${db.url}")
public String dbUrl;
#Value("${db.username}")
public String dbUserName;
#Value("${db.password}")
public String dbPassword;
#Bean("dataSource")
public DataSource getConfig() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dbDriver);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUserName);
dataSource.setPassword(dbPassword);
return dataSource;
}
}
WLRouteBuilder.java
#Component
public class WLRouteBuilder extends RouteBuilder {
#Autowired
private NotificationConfig notificationConfig;
#Autowired
private DataSource dataSource;
#Override
public void configure() throws Exception {
from("direct:eventNotification")
.to("sql:"+notificationConfig.getSqlQuery()+"?dataSource="+dataSource)
.process(new RowMapper())
.log("${body}");
}
}
I see the below error when I run, found out that Camel is unable to find DataSource bean in registry. I am quite not sure how to inject "DataSource" to Registry in Spring Boot using Java DSL.
?dataSource=org.springframework.jdbc.datasource.DriverManagerDataSource%40765367 due to: No bean could be found in the registry for: org.springframework.jdbc.datasource.DriverManagerDataSource#765367 of type: javax.sql.DataSource
Its the name of the bean that Camel uses in the uri, where you refer to it using the # syntax as documented here: http://camel.apache.org/how-do-i-configure-endpoints.html (referring beans)
So something alike
.to("sql:"+notificationConfig.getSqlQuery()+"?dataSource=#dataSource"
Where dataSource is the name of the bean that creates the DataSource, which you can give another name eg
#Bean("myDataSource")
And then the Camel SQL endpoint is
.to("sql:"+notificationConfig.getSqlQuery()+"?dataSource=#myDataSource"
Related
I'd like to know if it is possible to use Project Reactor with Apache Camel, so applications be fully reactive and non-blocking IO. I'd like to know how does the Project Reactor support works when integrating other Apache Camel's components.
Can I read for example from S3 reactively (therefore I'll need to use the Async S3 client behind the scenes)? Or will I block when reading from S3 and then just create a Flux out of what has been returned?
Where reactiveness is needed, you should use the relevant spring and reactor libraries. there are pseudo camel code also u can db call in camel bean or processors etc
#RestController
#RequestMapping(value = "/api/books")
#RequiredArgsContructor
public class HomeController {
private final BookRepository bookRepository;
private final ProducerTemplate template
#GetMapping("")
public Flux<Book> getHome() {
List <Book> books=bookRepository.findAll();
X ret = template.requestBody("direct:something", books, X.class);
}
}
#Component
public class SomeRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:something")
.process(e-> {
List<Book> books = e.getIn.getBody(List.class)
// some logic
e.getIn.setBody(new X( ))
})
}
What is the proper way to use endpoint DSL and then reference the endpoint with ProducerTemplate? When creating a route and using endpoint DSL, it seems that Camel is creating a different uri for the endpoint. My EndpointRouteBuilder class:
#Component
public class MyRoutes extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(seda("STATUS_ENDPOINT"))
.routeId("stateChangeRoute")
.to(activemq("topic:statusTopic"))
}
}
and then injecting the endpoint to ProducerTemplate
#Component
public class StateChangePublisher {
#EndpointInject(value="seda:STATUS_ENDPOINT")
private ProducerTemplate producer;
public void publish(String str) {
try {
producer.sendBody(str);
} catch(CamelExecutionException e) {
e.printStackTrace();
}
}
}
When camel starts, I see two entries in the log:
o.a.camel.component.seda.SedaEndpoint : Endpoint seda:STATUS_ENDPOINT is using shared queue: seda:STATUS_ENDPOINT with size: 1000
o.a.camel.component.seda.SedaEndpoint : Endpoint seda://STATUS_ENDPOINT is using shared queue: seda://STATUS_ENDPOINT with size: 1000
The queue eventually fills up and nothing gets delivered to the "to" endpoint.
If I define the route without using the endpoint DSL method "seda()"
from("seda:STATUS_ENDPOINT")
then it works.
Is this a bug or am I doing something wrong?
I'm using camel 3.2.0 and
This was a bug in the endpoint dsl. Try upgrading to camel 3.3.0. I think it was fixed in the new release.
https://issues.apache.org/jira/browse/CAMEL-14859
Note: Question is about standalone (using Main class) behaviour of the unreleased Camel 3.0.0-M2 version which has many enhancements for standalone mode over Camel 2.x - the code below is not intended to work on Camel 2.x
Problem: I noticed that modifications on the properties component as described in [1] do not affect config properties injected into beans. In my case I want to set the pc's environmentVariableMode to override (fallback is the default).
While the override was effective on the route, the bean got injected with the original property value.
The property "hi" in application.properties is set to "Hello" and the environment variable "hi" is set to "Huhu" which should override the former when environmentVariableMode is set to override (2).
When run:
System env var hi=Huhu
14:34:02.282 [Camel (camel-1) thread #2 - timer://foo] INFO route1 - Huhu from route
14:34:02.297 [Camel (camel-1) thread #2 - timer://foo] INFO route1 - Hello from bean
Code:
public class MyApplication {
private MyApplication() {
}
public static void main(String[] args) throws Exception {
Main main = new Main();
main.addConfigurationClass(MyConfiguration.class);
main.addRouteBuilder(MyRouteBuilder.class);
main.run(args);
}
}
public class MyConfiguration {
#BindToRegistry
public MyBean myBean(#PropertyInject("hi") String hi) {
return new MyBean(hi);
}
}
public class MyBean {
private String hi;
public MyBean(String hi) {
this.hi = hi;
}
public String hello() {
return hi + " from bean";
}
}
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
CamelContext context = getContext();
PropertiesComponent pc = context.getComponent("properties", PropertiesComponent.class);
pc.setEnvironmentVariableMode(PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_OVERRIDE); //default is FALLBACK
System.out.println("System env var hi=" + System.getenv("hi"));
from("timer:foo?repeatCount=1")
.log("${properties:hi} from route")
.bean("myBean")
.log("${body}");
}
}
application.properties:
hi = Hello
The only way I could get it to work was to override Main#postProcessCamelContext -- is this really the way it is intended to use? Or is there a more idiomatic way?
public class MyApplication extends Main {
private MyApplication(String[] args) throws Exception {
addConfigurationClass(MyConfiguration.class);
addRouteBuilder(MyRouteBuilder.class);
run(args);
}
#Override
protected void postProcessCamelContext(CamelContext camelContext) throws Exception {
PropertiesComponent pc = camelContext.getComponent("properties", PropertiesComponent.class);
pc.setEnvironmentVariableMode(PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_OVERRIDE);
super.postProcessCamelContext(camelContext);
}
public static void main(String[] args) throws Exception {
new MyApplication(args);
}
}
A suggestion to Camel development: Wouldn't it make more sense to set environmentVariableMode to override by default instead of fallback, especially when thinking about container deployments: Environment variables take precedence over system properties which take precedence over application configuration (e.g. application.properties)?
[1] https://github.com/apache/camel/blob/master/components/camel-properties/src/main/docs/properties-component.adoc
Yeah its better to have ENV override, you are welcome to log a JIRA and work on a github PR. We love contributions
http://camel.apache.org/support.html
I logged a ticket: https://issues.apache.org/jira/browse/CAMEL-13502
Okay this has now been implement to be the default mode, and you can also configure this from the application.properties file etc: https://github.com/apache/camel/blob/master/examples/camel-example-main/src/main/resources/application.properties#L23
And the issue with #PropertyInject has also been fixed, in Camel v3.0M3
I have camel processor which reads a file from directory using ConsumerTemplate as shown below
#Component
public class FileReaderProcessor implements Processor {
#Consume
private ConsumerTemplate consumerTemplate;
/**
* {#inheritDoc}
*/
#Override
public void process(Exchange exchange) throws Exception {
final byte[] filecontent = consumerTemplate.receiveBody(SOURCE_FILE_COMPONENT,
byte[].class);
}
}
However when i tried below code it works
final byte[] filecontent = exchange.getContext().createConsumerTemplate().receiveBody(SOURCE_FILE_COMPONENT,
byte[].class);
The problem is i need to annotate the ConsumerTemplate and use it my code. kindly help me to use ConsumerTemplate with annotation
#Consume
private ConsumerTemplate consumerTemplate;
If you check ConsumerTemplate class you will notice that it extends Service. That means it can be Autowired as any other component in Spring Boot, which I am guessing you are using based on #Component annotation used in your code. So the solution is pretty simple:
#Autowired
private ConsumerTemplate consumerTemplate;
I am learning about Spring AOP at first time.
I am reading about in this sites:
Site2 and
Site1
Following this I have made the next classes
Main class:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();
MessagePrinter printer = context.getBean(MessagePrinter.class);
System.out.println(printer.getMessage());
}
}
App config class:
#Configuration
#ComponentScan("com.pjcom.springaop")
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig {
#PostConstruct
public void doAlert() {
System.out.println("Application done.");
}
}
Aspect class:
#Component
#Aspect
public class AspectMonitor {
#Before("execution(* com.pjcom.springaop.message.impl.MessagePrinter.getMessage(..))")
public void beforeMessagePointCut(JoinPoint joinPoint) {
System.out.println("Monitorizando Mensaje.");
}
}
And others...
Just like that app work nice, but if I put proxyTargetClass to false. Then I get the error below.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.pjcom.springaop.message.impl.MessagePrinter] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:318)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
at com.pjcom.springaop.App.main(App.java:18)
Why?
#EnableAspectJAutoProxy(proxyTargetClass=false)
Indicates that JDK dynamic proxy will be created to support aspect execution on the object. And therefore as this type of proxy requires a class to implement an interface your MessagePrinter must implement some interface which declares method getMessage.
#EnableAspectJAutoProxy(proxyTargetClass=true)
On the opposite instruct to use CGLIB proxy which is able to create proxy for a class without an interface.
1> Message Printer has to be defined as a component i.e :
`
package com.pjcom.springaop.message.impl;
#Component
public class MessagePrinter{
public void getMessage(){
System.out.println("getMessage() called");
}
}`
in the same package as configuration java file if no #ComponentScan is not defined for some other packages.
2> If same type of bean class has many other dependencies then to resolve dependencies in spring Config use #Qualifier annotation.