Spring integration : replace xml configured bean property dynamically? - spring

I'm trying to do a ftp poller with the help of Spring integration and the poller works great with the xml configuration. Now I would like to be able to dynamically set some properties of the poller like the cron-expression or the polling rate to make it configurable by java code and link it to a web interface.
I have seen a lot of topics around the subject but nothing really clear to do that.
Is there a classic way of doing that ?
Can it be done with SpeL ?
My bean poller declaration in XML is as follows :
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel" session-factory="ftpClientFactory"
filename-regex=".*\.tmp$" auto-create-local-directory="true"
delete-remote-files="false" remote-directory="/cft-polling" local-directory="file:target/ftp-output" >
<int:poller fixed-rate="1000" />
</int-ftp:inbound-channel-adapter>
<int:channel id="ftpChannel">
<int:queue />
</int:channel>

I'm not sure there is enough here for a solid answer, but assuming that the ftp poller is defined and managed in the spring container, and assuming there are proper accessores to modify it's properties...that you will be able to change it's setting just like you would any other object.
First you would have to get a reference of the spring managed object, you can do this by having one of your classes implement ApplicationContextAware thereby exposing the Spring context.
Then it's just a matter of getting the bean from the context and updating it's property.
public class MyManagedClass implements ApplicationContextAware {
private ApplicationContext springContext;
public void changeBeansProperty(){
MyFtpPoller poller = (MyFtpPoller) springContext.getBean("ftpInbound");
poller.setCronExpress("12 12 * * * *");
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.springContext = applicationContext;
}
}

Related

How to use java Java configuration class in spring integration xml file related to apache kafka?

we are using xml file to implement apache kafka using spring-integration. for now producer and consumer factories are called using #bean in xml file. but I want to read configurations from java class and use that class in the xml file. How do I do that for producer and consumer?
current version for producer:
<int:chain input-channel="kafka-output-channel">
<int:object-to-json-transformer/>
<int-kafka:outbound-channel-adapter id="kafkaOutboundChannelAdapter"
kafka-template="template"
topic="${topic.name}" />
</int:chain>
<bean id="pf" class="org.springframework.kafka.core.DefaultKafkaProducerFactory">
<constructor-arg>
<map>
<entry key="bootstrap.servers" value="${broker.list}" />
<entry key="key.serializer" value="org.apache.kafka.common.serialization.StringSerializer"/>
<entry key="value.serializer" value="org.apache.kafka.common.serialization.StringSerializer"/>
</map>
</constructor-arg>
</bean>
<!-- Kafka Template -->
<bean id="template" class="org.springframework.kafka.core.KafkaTemplate">
<constructor-arg ref="pf"/>
</bean>
But I want to create class like below and use beans below in the xml file,
#Configuration
public class KafkaProducerConfig {
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, appConfig.getBrokersList());
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(props);
}
}
same thing I want to do for consumer part!
could someone please help?
Nothing is changed here. The XML and Java & Annotation configurations contributes to the same ApplicationContext and bean declared in one place can be references in the other, and wise versa. So, to use that kafkaTemplate bean in the XML, you just need to use exactly this name instead of that template and remove those bean definitions in the XML.
Only the problem that annotation configuration has to be primary: use an AnnotationConfigApplicationContext. Although I see that you use Spring Boot, so everything is OK. Your XML config has to be used on some #Configuration class as a #ImportResource. On the other hand if you have already enough experience with annotations configuration, there might not be a reason to stick with XML config even for Spring Integration. Consider to investigate Java DSL which smoothly replaces whatever you can have in XML: https://docs.spring.io/spring-integration/docs/current/reference/html/dsl.html#java-dsl
Please, also pay attention that there is an auto-configuration for Apache Kafka in Spring Boot. So you might not need to configure your template and produce/consumer factories: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#messaging.kafka

How to implement SmartLifecycleRoleController in Spring Integration application

I am writing client-server application on base of int-ip:tcp endpoints. Code written in Kotlin.
Application contains two tcp clienst which should make connection to server in sertain sequence,
one after each other, when first cleint already establised connection to server and made some initialisation.
As a solution for this synchronisation I suppose to use a SmartLifecycleRoleController to start group of endpoints of a dependent tcp client.
To that end in depended (second) client I add role="rcCluster" and auto-startup="false" attributes to tcp-outbound-channel-adapter and tcp-inbound-channel-adapter
<int-ip:tcp-connection-factory id="rcClientConnectionFactory"
type="client"
host="${tdirelay.host}"
port="${tdirelay.rcPort}"
single-use="false"
so-timeout="10000"
so-keep-alive="false"
serializer="rawSerializerDeserializer"
deserializer="rawSerializerDeserializer"
ssl-context-support="sslContext"/>
<int-ip:tcp-outbound-channel-adapter id="rcOutBoundAdapter"
channel="rcPacketQueueChannel"
phase="5000"
connection-factory="rcClientConnectionFactory"
role="rcCluster"
auto-startup="false"
/>
<int-ip:tcp-inbound-channel-adapter id="rcInboundAdapter"
channel="rcFromServer"
client-mode="true"
retry-interval="5000"
connection-factory="rcClientConnectionFactory"
role="rcCluster"
auto-startup="false"
/>
The leading (first) tcp client uses interceptor endpoint to make a prologue exhange with server in accordance with the protocol:
<bean id="dcInterceptorFactoryChain"
class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
<property name="interceptors">
<array>
<bean class="com.tcpclient.DirectCannel.DCConnectionInterceptorFactory">
</bean>
</array>
</property>
</bean>
<int-ip:tcp-connection-factory id="dcClientConnectionFactory"
type="client"
host="${tdirelay.host}"
port="${tdirelay.dcPort}"
single-use="false"
so-timeout="10000"
so-keep-alive="false"
interceptor-factory-chain="dcInterceptorFactoryChain"
serializer="rawSerializerDeserializer"
deserializer="rawSerializerDeserializer"
ssl-context-support="sslContext"
/>
I am planning to call startLifecyclesInRole(String role) and stopLifecyclesInRole(String role) methods of the SmartLifecycleRoleController inside my Interceptor class.
So, I added #Autowired private val roleController: SmartLifecycleRoleController to InterceptorFactory as explained in spring-integration/docs
My InterceptorFactory is:
class DCConnectionInterceptorFactory() : TcpConnectionInterceptorFactory, ApplicationEventPublisherAware {
#Autowired
private val roleController: SmartLifecycleRoleController? = null
#Volatile
private var applicationEventPublisher: ApplicationEventPublisher? = null
override fun setApplicationEventPublisher(applicationEventPublisher: ApplicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher
}
override fun getInterceptor(): TcpConnectionInterceptorSupport {
return DCConnectionInterceptor(this.applicationEventPublisher!!, roleController!!)
}
}
IntelliJ IDEA gives warning:
Could not autowire. No beans of 'SmartLifecycleRoleController' type found
And building gives error:
[task-scheduler-2] ERROR org.springframework.integration.ip.tcp.connection.ClientModeConnectionManager - Could not establish connection using dcClientConnectionFactory, host=localhost, port=9001
kotlin.KotlinNullPointerException
at com.tcpclient.DirectCannel.DCConnectionInterceptorFactory.getInterceptor(DCConnectionInterceptorFactory.kt:25)
I suppose that I need to define SmartLifecycleRoleController type bean in xml configuration file
(that is not mentioned in the docs: https://docs.spring.io/spring-integration/docs/4.3.4.RELEASE/reference/html/messaging-endpoints-chapter.html#endpoint-roles).
The constructor of this class has arguments:
public SmartLifecycleRoleController(List roles, List lifecycles)
which I do not know how to fill in my case in xml file:
If you know how to do this, please provide a live example of using bean of the SmartLifecycleRoleController class in the xml configuration file.
First of all it would be better do not use #Autowired there at all, since it isn't clear if an annotation configuration is enabled in your application context or not. Just because you show only XML config for Spring.
Therefore your DCConnectionInterceptorFactory should have a setter for that roleController property instead. Or what is the proper way to declare Java bean properties in Kotlin...
Then you need to use something like this in your XML config:
<bean class="com.tcpclient.DirectCannel.DCConnectionInterceptorFactory">
<property name="roleController" ref="integrationLifecycleRoleController"/>
</bean>
The Framework creates for your a SmartLifecycleRoleController automatically and registers it with the mentioned IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER bean name.
Please, raise a JIRA on the matter to improve documentation to make since much cleaner.
Your approach, by the way, is correct.

Camel OSGI: how do I get the spring application context?

Below is a snippet from the camle-osgi example. I can obtain the camel context but how do I get the original spring application context? Or how am I supposed to get a reference to "mybean"?
MyRouteBuilder.java:
public class MyRouteBuilder extends RouteBuilder {
public static void main(String[] args) throws Exception{
new Main().run(args);
}
public void configure() {
// set up the transform bean
ModelCamelContext mc = getContext();//ok, I can get the camel context fine
//but how to get the spring context to get to "mybean"???
//set up routes
}
}
beans.xml:
<beans>
<camel:camelContext xmlns="http://camel.apache.org/schema/spring">
<package>org.apache.servicemix.examples.camel</package>
-----
</route>
</camel:camelContext>
<bean id="mybean" class="myclass"/>
</beans>
You should not use the spring context directly. Instead use the spring features to inject the beans you need.
Define the routebuilder as a bean in the spring context and use a setter and to inject myBean.
Keep the auto discovery and use annotations like #Autowired in the routebuilder to inject mybean.
A third option is to use getContext().getRegistry(). The registry allows to access all beans of the spring context by name or by type.
It would not be camel if we are already out of options :-)
So another options is to use the bean component of camel to access beans from the spring context: http://camel.apache.org/bean.html

Spring AOP and apache shiro configuration.Annotations not been scanned

I've been struggling with a configuration which requires a knowledge in AOP.
i must admit that AOP is that part i'm trying to get for a while without success.
It seems that my shiro annotations are not scanned and thus are ignored.
i've tried using shiro 1.1.0+ maven3+spring 3.0.5.RELEASE, hibernate 3.6.1.Final with ZK 5.0.6.
i got my hibernaterealm working , talking to database, i got the authentication working, i successfully(i believe) get the roles and permission loaded.
so to test the authorization side i have somewhere in my code this :
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isPermitted("businessaccount:list")) {
throw new AuthorizationException("User not authorized");
}
and it works fine.
So i know my permissions were loaded.i'll be convenient for me using annotations to i've put it in implementation class, because i didn't plan on using interface at first place with my controller classes which are extending ZK GenericForwardController.
i've seen this bug and i've decided to do a try with one interface with the #RequiresPersmissions on methods.
apparently it's still not working as in it's giving access to unauthorized subject.there is no error in my log.Maybe i'm doing something wrong here are snippet of the codes:
#Component("layouteventhandler")
public class LayoutEventHandlerImpl extends GenericForwardComposer implements LayoutEventHandler {
Logger logger = Logger.getLogger(LayoutEventHandlerImpl.class);
Menuitem logout;
//...
#Override
public void onClick$pAccounts() {
try {
execution.sendRedirect("/accounts/personal/list");
} catch (Exception ex) {
logger.info("Error redirecting to personal accounts", ex);
}
}
#Override
public void onClick$bAccounts() {
try {
execution.sendRedirect("/accounts/business/list");
} catch (Exception ex) {
logger.info("Error redirecting to business accounts", ex);
}
}
//.....
}
its interface it :
public interface LayoutEventHandler {
#RequiresPermissions(value="personalaccount:list")
public void onClick$pAccounts();
#RequiresPermissions(value="businessaccount:list")
public void onClick$bAccounts();
//.....
}
here is my shiro applicationcontext
<bean id="hibernateRealm" class="com.personal.project.admin.webapp.security.DatabaseRealm" />
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="hibernateRealm" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<!-- <property name="proxyTargetClass" value="true" />-->
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations can be associated
with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- ... -->
is it in there something that i should do? thanks for reading and helping out
I don't know Shiro, but I'm guessing that you've put annotations on your bean classes which implement interfaces and then you're proxying them for security, transactions, and/or something else. When that happens, the object that's returned is a JDK dynamic proxy, which isn't an instance of your bean's concrete class, only of the interface it implements. Therefore any annotation scanning that depends on annotations in the concrete class won't find them.
To expand on Ryan Stewart's answer, you need to add
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
to the implementing class (not the interface) and move the Shiro annotations to it.
I encountered a similar problem when I was running two spring contexts. There is a parent root context that defined Database, Service, Security and non-SpringMVC web beans and a child web context for a Spring MVC REST api which contained the Controllers I want to proxy. The Configuration for each context was class path scanning separate packages.
In this case make sure that the DefaultAdvisorAutoProxyCreator and the AuthorizationAttributeSourceAdvisor beans that are requied are defined in the child web context (i.e. where the Rest Controllers are class path scanned) as defining them in the parent context does not work (the documentation on the DefaultAdvisorAutoProxyCreate is quite clear about this in hindsight!).
Posting this in case someone else encounters the same issue.

Another question on Spring 3, servlet, #autowired

I think I've read every question and answer on Spring and autowiring a servlet, both here and at springsource.org, and I still can't get it working.
All I want to do is have the datasource automatically set in my servlets. I understand that the container creates the servlet and not Spring.
Here is code from my test servlet:
package mypackage.servlets;
imports go here...
#Service
public class TestServlet extends HttpServlet
{
private JdbcTemplate _jt;
#Autowired
public void setDataSource(DataSource dataSource)
{
_jt = new JdbcTemplate(dataSource);
}
etc etc
In my applicationContext.xml I have:
<context:annotation-config />
<context:component-scan base-package="mypackage.servlets />
<import resource="datasource.xml" />
and in my datasource.xml:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
If I can't get this working I'll just use WebApplicationContextUtils in the servlet's init method but I'd really like to make this work after all the reading I've been doing.
I'm using Spring 3, Java 1.6.
Thanks,
Paul
You need to replace your Servlets by Spring MVC contollers. Because Spring will not inject anything the classes (servlets) created by someone else then Spring itselfe (except #Configurable).
(To get an very simple example, take a look at the STS Spring Template Project: MVC).
What I wanted to do was get a DataSource reference in my Servlet for free, i.e. not calling a static getDatasource method on some class.
Here's what I learned and how I got it working:
Servlets cannot be configured or autowired by Spring. Servlets are created before Spring's app context is loaded. See issue SPR-7801: https://jira.springsource.org/browse/SPR-7801
What I did was create a DataSource in my applicationContext.xml and export that as a property:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="myDatasource">
<ref bean="dataSource"/>
</entry>
</map>
</property>
</bean>
In my servlet's init method I read the property:
public void init(ServletConfig config)
{
Object obj = config.getServletContext().getAttribute("myDatasource");
setDataSource((DataSource)obj);
}
public void setDataSource(DataSource datasource)
{
// do something here with datasource, like
// store it or make a JdbcTemplate out of it
}
If I'd been using DAOs instead of hitting the database from the servlets it would have been easy to wire them up for #Autowired by marking them #Configurable, and also be able to use #Transactional and other Spring goodies.

Resources