Camel and Activemq setup with Spring Boot - java

I have noticed in several examples, the common way to configure activemq with camel is with the following beans. I would like to know if Spring Boot already configures any of these beans by default. I know that if the activemq jars are on the class path a default connection factory is created, but what about everything below?
<bean id="jmsConnectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<bean id="pooledConnectionFactory"
class="org.apache.activemq.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="maxConnections" value="8"/>
<property name="connectionFactory" ref="jmsConnectionFactory"/>
</bean>
<bean id="jmsConfig"
class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledConnectionFactory"/>
<property name="concurrentConsumers" value="10"/>
</bean>
<bean id="jms"
class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig"/>
<property name="transacted" value="true"/>
<property name="cacheLevelName" value="CACHE_CONSUMER"/>
</bean>
or
#Bean
public ActiveMQConnectionFactory getConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(brokerURL);
return connectionFactory;
}
#Bean(initMethod = "start", destroyMethod = "stop")
public PooledConnectionFactory getPooledConnectionFactory() {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
pooledConnectionFactory.setMaxConnections(maxConnections);
pooledConnectionFactory.setConnectionFactory(getConnectionFactory());
return pooledConnectionFactory;
}
#Bean
public JmsConfiguration getJmsConfiguration() {
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConnectionFactory(getPooledConnectionFactory());
return jmsConfiguration;
}
#Bean
public JmsConfiguration getJmsHighPriorityConfiguration() {
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConnectionFactory(getPooledConnectionFactory());
jmsConfiguration.setPriority(8);
return jmsConfiguration;
}
#Override
protected void setupCamelContext(CamelContext camelContext) throws Exception {
ActiveMQComponent activeMQComponent = new ActiveMQComponent();
activeMQComponent.setConfiguration(getJmsConfiguration());
camelContext.addComponent("activemq", activeMQComponent);
ActiveMQComponent activeMQHighPriorityComponent = new ActiveMQComponent();
activeMQHighPriorityComponent.setConfiguration(getJmsHighPriorityConfiguration());
camelContext.addComponent("activemq-high-priority", activeMQHighPriorityComponent);
}

Meanwhile there are some spring-boot-starters which can be used to have ActiveMQ and Camel running within Spring Boot.
ActiveMQ
Start with spring-boot-starter-activemq in your pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
Configuration
Have a look what's configurable through this at all - its documented in Appendix A. Common application properties (search for 'activemq' and 'jms').
Alternative approach: from my point of view, its best to determine what's configurable in Sprint Boot and what not is having a look at their auto-configuration mechanism:
ActiveMQAutoconfiguration which shows how ActiveMQ and JMS Configuration relate to each other
ActiveMQProperties determining ActiveMQ-specific properties which can be set
Camel
Apache Camel provides its own Spring Boot integration. Basically you also have to add camel-spring-boot-starter:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>2.17.3</version>
</dependency>
Configuration
I haven't found a good example configuration file, so again, have a look at configuration is exposed through CamelConfigurationProperties class.
In general - like you mentioned - you might end up in registering some of your beans manually if you don't find all properties exposed via this configuration.

Related

How can I add my custom type converters to SpringBoot

Config my custom type converters by using (Spring 4.x)XML properties like this.
<mvc:annotation-driven conversion-service="factoryBean" />
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="factoryBean" >
<property name="converters">
<list>
<bean class="com.mvc.convertor.MyConvertor" />
</list>
</property>
</bean>
MyConvertor implements org.springframework.core.convert.converter.Converter.
And how can I config my custom type converters by using SpringBoot.I have tried many methods but failed.Hope any one can help me to resolve.Thanks!
As Stephane Nicoll pointed out spring boot should automatically pick up any converters registered as bean configuration in your application.
#Configuration
#EnableWebMvc
public class MyConfiguration {
#Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = ...
HttpMessageConverter<?> another = ...
return new HttpMessageConverters(additional, another);
}
}

JMX with spring without XML configuration, 100% annotation based?

I am using JMX in Spring application with XML configuration:
<bean id="jmxExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=bean1" value-ref="bean1"/>
<entry key="bean:name=bean2" value-ref="bean2"/>
<entry key="bean:name=bean3" value-ref="bean3"/>
</map>
</property>
<property name="notificationListenerMappings">
<map>
<entry key="*">
<bean class="com.test.listener"/>
</entry>
</map>
</property>
</bean>
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="1099" />
</bean>
<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean">
<property name="objectName" value="connector:name=rmi" />
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi" />
</bean>
I understand from various documents like instead of this XML configuration we could annotate it with #EnableMBeanExport and with #ManagedResource for the beans.
But i doubt how ConnectorServerFactoryBean gets configured with these annotations. Or is there any annotation available to configure RMI and connectorServerFactoryBean?
Also i need to know how to annotate, notificationListenerMappings configured?
P.S:
I have the code working for publisher and listener under XML configuration. I am planning to move it completely on annotation based as i do not want to disturb XML configuration already in PROD.
Edited
Found the following piece of code: planning to try it:
#Bean
public RmiRegistryFactoryBean registry() {
return new RmiRegistryFactoryBean();
}
#Bean
#DependsOn("registry")
public ConnectorServerFactoryBean connectorServer() throws MalformedObjectNameException {
ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
connectorServerFactoryBean.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/connector");
return connectorServerFactoryBean;
}
Edit 2:
I am proceeding on above mentioned approach and I am able to configure MBeans and able to publish notifications. But unfortunately I am stuck up with configuring NotificationListener through Annotation.
I tried adding the following:
#Bean
#DependsOn("registry")
public ConnectorServerFactoryBean connectorServer() throws MalformedObjectNameException {
ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
connectorServerFactoryBean.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/connector");
//TestListener is my NotificationListener class
ObjectName objectName = new ObjectName("bean:name=bean1");
connectorServerFactoryBean.getServer().addNotificationListener(objectName,
new TestListener(), null,null);
return connectorServerFactoryBean;
}
I am getting instanceNotFoundException stating bean:name=bean1 is not found. But I have configured like, #ManagedResource(objectName="bean:name=bean1") on my bean1.
Any help please on what i am missing?
#EnableMBeanExport has a server property, which reference the bean name of a server object.
see for example the test of this component, which use this server property : https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java

change datasource url at runtime in spring

I have spring application configured via annotations. Here is part of my config
#Configuration
#EnableTransactionManagement
public class JpaSpringConfiguration {
#Bean(destroyMethod = "close")
#Lazy
#Primary
public BasicDataSource dataSource(#Value("${statistics.hostname}") String statisticsHostname) {
final BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
String url = String.format("jdbc:postgresql://%s:5432/statistics-db", statisticsHostname);
dataSource.setUrl(url);
....
return dataSource;
}
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
final PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer();
placeholderConfigurer.setSystemPropertiesMode(SYSTEM_PROPERTIES_MODE_OVERRIDE);
Properties properties = new Properties();
properties.setProperty("statistics.hostname", "localhost");
placeholderConfigurer.setProperties(properties);
return placeholderConfigurer;
}
Until recently we had xml configuration
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="properties">
<props>
<prop key="statistics.hostname">localhost</prop>
</props>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" lazy-init="true" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://${statistics.hostname}:5432/statistics-db" />
<property name="username" value="user" />
<property name="password" value="password" />
</bean>
When user selected different server to connect to we set system property and closed application context and refreshed
System.setProperty("statistics.hostname", hostname)
applicationContext.close()
applicationContext.refresh()
This does not work when I use annotation configuration.
My questions are:
why it does not work now?
how to get rid of setting hostname via system property altogether?
EDIT: I just found out that I forgot ${} around the name of the parameter in method dataSource(). So it works now but question 2 still remains.
not sure why it doesnt work, but you may try to do couple more things:
Is closing context before refresh really needed? Try to only refresh it.
You can mark your bean as #RefreshScope ( but it requires spring cloud ) and refresh it using /refresh endpoint. That would require another endpoint to actually update your host on a bean before calling refresh.
"how to get rid of setting hostname via system property altogether?"
pass that to property file which is the way it is normally configured. If you are using spring boot, then you only have to configure:
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
...
properties. Datasource bean would be created using those values for you.

Spring #Transactional in an Aspect (AOP)

I've created an Aspect which contains an #Transactional annotation. My advice is being invoked as expected, but the new entity AuditRecord is never saved to the database, it looks like my #Transactional annotation is not working.
#Aspect
#Order(100)
public class ServiceAuditTrail {
private AppService appService;
private FooRecordRepository fooRecordRepository;
#AfterReturning("execution(* *.app.services.*.*(..))")
public void logAuditTrail(JoinPoint jp){
Object[] signatureArgs = jp.getArgs();
String methodName = jp.getSignature().getName();
List<String> args = new ArrayList<String>();
for(Object arg : signatureArgs){
args.add(arg.toString());
}
createRecord(methodName, args);
}
#Transactional
private void createRecord(String methodName, List<String> args){
AuditRecord auditRecord = new AuditRecord();
auditRecord.setDate(new Date());
auditRecord.setAction(methodName);
auditRecord.setDetails(StringUtils.join(args, ";"));
auditRecord.setUser(appService.getUser());
fooRecordRepository.addAuditRecord(auditRecord);
}
public void setAppService(AppService appService) {
this.appService = appService;
}
public void setFooRecordRepository(FooRecordRepository fooRecordRepository) {
this.fooRecordRepository= fooRecordRepository;
}
}
The bean context is as follows:
<tx:annotation-driven transaction-manager="txManager.main" order="200"/>
<aop:aspectj-autoproxy />
<bean id="app.aspect.auditTrail" class="kernel.audit.ServiceAuditTrail">
<property name="appService" ref="app.service.generic" />
<property name="fooRecordRepository" ref="domain.repository.auditRecord" />
</bean>
My pointcut is intercepting only interfaces (service interfaces). The service methods may or may not be transactional. If the service method is transactional, I would like that transaction to be rolled back if the Advice fails for some reason.
My question: Why is the transactional annotation being ignored? This is my first time building an AOP service with Spring, I would also welcome any architectural or implementation improvements.
Thanks!
In Spring, #Transactional works by creating a proxy of your class (either a Java or cglib proxy) and intercepting the annotated method. This means that #Transactional doesn't work if you are calling the annotated method from another method of the same class.
Just move the createRecord method to a new class (don't forget to make it a Spring bean too) and it will work.
A very good question. If you have to rollback/commit the transactions you can directly configure the spring transactional as mentioned here.
Doing this method does not require you to add #Transactional on each of the class/method manually.
Spring configuration is below(copied from the reference link ). Instead of writing custom advisors/pointcuts just add configuration in spring-application context.xml file with your advisors/pointcuts.
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!-- don't forget the DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
ref:
Spring transactional : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

Java config to XML config - Spring Social

I'm new to Java Config & am stuggling to convert a piece of Java based config to XML based config.
Can anyone kindly guide me with the conversion. Many thanks!
#Bean
#Scope(value="singleton", proxyMode=ScopedProxyMode.INTERFACES)
public UsersConnectionRepository usersConnectionRepository(AccountRepository accountRepository) {
JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(
dataSource, connectionFactoryLocator(), Encryptors.noOpText());
repository.setConnectionSignUp(new AccountConnectionSignUp(accountRepository));
return repository;
}
Ok, found out myself:
<bean id="usersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
<constructor-arg ref="jpaDataSource" />
<constructor-arg ref="connectionFactoryLocator" />
<constructor-arg ref="textEncryptor" />
<property name="connectionSignUp" ref="accountConnectionSignUp" />
</bean>
<bean id="accountConnectionSignUp" class="path to AccountConnectionSignUp" />
This is for anyone's help. Thanks!

Categories

Resources