Spring integration - less boilerplate? - java

I have started using Spring-integration for listening on JMS-queues, and it's working satisfactory, but I have somewhat of an issue with the amount of xml-configuration required in order to set up a listener. Most of this is boilerplate, what changes is really just the jms-instance and the name of the queue to listen to, and the class and method to invoke upon receiving a message. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<bean id="jms.queue.myQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${myApplication.jms.queue.myQueue}" />
</bean>
<bean id="jms.container.myJMSListender"
class="org.springframework.jms.listener.DefaultMessageListenerContainer"
destroy-method="destroy">
<property name="connectionFactory"
ref="jmsConnectionFactory" /> <!-- Defined elsewhere -->
<property name="destination"
ref="jms.queue.myQueue" />
<property name="sessionTransacted"
value="true" />
</bean>
<int:publish-subscribe-channel id="channel.myQueue" />
<jms:message-driven-channel-adapter id="channelAdapter.myQueue"
container="jms.container.myQueue"
channel="channel.myQueue"
acknowledge="transacted"/>
<int:service-activator id="serviceActivator.myQueue"
input-channel="channgel.myQueue"
ref="myQueueJMSListener"
method="handleMessage" />
</beans>
As you can see, a lot of config for something fairly simple, and with quite a lot of JMS-listeners this becomes tedious, both to read and write.
Is there a way to configure listening to a queue with Spring-integration that requires less boilerplate? I've looked into creating my own XML-tag, but I'm kinda hoping that there is a simpler solution.

You only need an external container if you need to configure properties that are not exposed on the namespace.
<jms:message-driven-channel-adapter id="channelAdapter.myQueue"
destination="jms.queue.myQueue"
connection-factory="jmsConnectionFactory"
channel="channel.myQueue"
acknowledge="transacted"/>

Found that Spring integration has annotation support.
Requires some setup, but this can be used for multiple listeners:
<amq:connectionFactory id="jmsConnectionFactory" brokerURL="${jms.host}">
<amq:redeliveryPolicy>
<amq:redeliveryPolicy maximumRedeliveries="0" />
</amq:redeliveryPolicy>
</amq:connectionFactory>
<bean id="cachingJmsConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnectionFactory" />
</bean>
<jms:annotation-driven />
<bean id="jmsListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="cachingJmsConnectionFactory" />
</bean>
Then, listening to a certain queue is as simple as:
#JmsListener(destination = "${myQueueName}")
public void listen(#Payload final String payload) {

Related

How to map the marshalled object values(reader) to hibernate entity object(writer) in spring batch

I am new to Spring Batch, even after an extensive search also I didn't get any clue on how to map the XML marshalled object value to hibernate entity object. To be specific, I think it follows the same approach as FieldSetMapper but not sure how to get the MarshallObject instead of FlatFieldSet. Please help.
FYI: I should use Hibernate only and not JdbcTemplate or preparedstatementsetter etc.
The batch config file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<import resource="//context.xml" />
<!-- <context:component-scan base-package="com.politico.batchhandle.marshallbatch" /> -->
<!-- parallel job processing -->
<job id="feedprocesser" xmlns="http://www.springframework.org/schema/batch">
<!-- <split id="split1"> <flow> -->
<step id="stepfeedprocess">
<!-- <tasklet task-executor="taskExecutor" throttle-limit="10"> -->
<tasklet>
<chunk reader="xmlmultiReader" writer="dataWriter" processor="dataProcessor"
commit-interval="10" />
</tasklet>
</step>
<!-- </flow> </split> -->
</job>
<!-- <bean id="testReader" class="com.politico.batchhandle.marshallbatch.DataReader"
/> -->
<bean id="dataProcessor" class="com.politico.batchhandle.marshallbatch.DataProcessor" />
<bean id="dataWriter" class="com.politico.batchhandle.marshallbatch.DataWriter" />
<!-- <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"
/> -->
<bean id="xmlmultiReader" class="org.springframework.batch.item.xml.StaxEventItemReader">
<property name="fragmentRootElementName" value="crs-bill-summary" />
<property name="resource" value="classpath:xmlfiles\\Bill_Digest.xml" />
<property name="unmarshaller" ref="reportUnmarshaller" />
<property name="fieldSetMapper" ref="dataFieldSetMapper" />
</bean>
<bean id="reportUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.politico.jaxbconvert.readerTO.CrsBillSummary</value>
</list>
</property>
<property name="mappingLocation" value="classpath:/userMapping.xml" />
</bean>
<bean id="dataFieldSetMapper" class="com.politico.batchhandle.dao.DataFieldSetMapper">
</bean>
</beans>
I assumed xml marshall object also can be mapped using FieldSetMapper, here is the corresponding code:
public class DataFieldSetMapper implements FieldSetMapper<CrsBillSummary>{
#Override
public CrsBillSummary mapFieldSet(FieldSet fieldset) throws BindException {
// TODO Auto-generated method stub
return null;
}
}
I am sure I am using the wrong interface for fieldsetmapping , so please help me with the right approach..
Please add this one to your XML
<bean id="reportUnmarshaller"
class="org.springframework.oxm.xstream.XStreamMarshaller">
<property name="aliases">
<util:map id="aliases">
<entry key="trade"
value="XXX.CrsBillSummary" />
<entry key="property1" value="java.math.BigDecimal" />
<entry key="property2" value="java.lang.String" />
</util:map>
</property>
</bean>
Note: CrsBillSummary in your case, I hope you should use Pojo class not Entity one. And then you can convert to Entity at Writer layer then persist it to Database. It is best practice I recommend use it to avoid mistakes which unattended objects persist to Database.

ActiveMQ Concurrency Issue - Multiple Consumers Consuming the Same Message From Queue

I'm using Spring JMS and ActiveMQ where I have a client that pushes messages to a Queue and I have multiple consumer threads that are listening and removing messages from the Queue. Some of the time the same messages gets dequeued from the Queue by two consumers. I don't want this behavior and want to ensure the only one messages is processed by only one consumer thread. Any ideas on where I've gone wrong?
Spring 3.2.2 Config:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.myapp" />
<!-- JMS ConnectionFactory config Starts -->
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>${brokerURL}</value>
</property>
<property name="userName" value="${username}" />
<property name="password" value="${password}" />
</bean>
<bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<!-- JMS ConnectionFactory config Ends -->
<!-- JMS Template config Starts -->
<bean id="myQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="${activemq.consumer.destinationName}" />
</bean>
<bean id="myQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="pooledJmsConnectionFactory" />
</bean>
<!-- JMS Template config Ends -->
<!-- JMS Listener config starts -->
<bean id="simpleMessageConverter"
class="org.springframework.jms.support.converter.SimpleMessageConverter" />
<bean id="myContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="${threadcount}" />
<property name="connectionFactory" ref="pooledJmsConnectionFactory" />
<property name="destination" ref="myQueue" />
<property name="messageListener" ref="myListener" />
<property name="messageSelector" value="JMSType = 'New'" />
</bean>
<bean id="myListener"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="myapp.MessageListener" />
</constructor-arg>
<property name="defaultListenerMethod" value="receive" />
<property name="messageConverter" ref="simpleMessageConverter" />
</bean>
<!-- JMS Listener config Ends -->
<!-- enable the configuration of transactional behavior based on annotations -->
<bean id="myJMSMessageSender" class="myapp.JMSMessageSender">
<property name="jmsTemplate" ref="myQueueTemplate" />
<property name="jmsQueue" ref="myQueue" />
<property name="messageConverter" ref="simpleMessageConverter" />
</bean>
<bean id="myQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="pooledJmsConnectionFactory" />
</bean>
</beans>
ActiveMQ 5.9.1 config:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="instance8161" dataDirectory="${activemq.data}" persistent="false">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic=">">
<!-- The constantPendingMessageLimitStrategy is used to prevent
slow topic consumers to block producers and affect other consumers
by limiting the number of messages that are retained
For more information, see:
http://activemq.apache.org/slow-consumer-handling.html
-->
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="1000"/>
</pendingMessageLimitStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
... <!-- rest is default ActiveMQ Config -->
</broker>
Most likely, your myapp.MessageListener (or one of its dependencies) is not thread-safe and you are seeing cross-talk across the consumer threads.
Best practice is to craft your listener as stateless (no mutated fields in the class). If that's not possible, you need to protect shared variables with locks.

JMX Integration with Apache CXF

I need CXF server monitoring with JMX and followed CXF Documentation. I use tomcat and following is the content of my cxf.xml file located at /home/kalpa/applications/apache-tomcat-7.0.54/webapps/java_first_jaxws/WEB-INF/classes
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="org.apache.cxf.management.InstrumentationManager" class="org.apache.cxf.management.jmx.InstrumentationManagerImpl">
<property name="bus" ref="cxf" />
<property name="enabled" value="true" />
<property name="JMXServiceURL " value="service:jmx:rmi:///jndi/rmi://localhost:9914/jmxrmi" />
</bean>
</beans>
But I cannot monitor the data with jconsole. There is no local entry for the connection.
What could be the issue here?
I don't know if your question is still an ongoing problem but this is my configuration :
<cxf:bus bus="cxf">
<cxf:properties>
<entry key="bus.jmx.enabled" value="true"/>
<entry key="bus.jmx.JMXServiceURL" value="service:jmx:rmi:///jndi/rmi://localhost:9914/jmxrmi"/>
</cxf:properties>
</cxf:bus>
Instead of yours :
<bean id="org.apache.cxf.management.InstrumentationManager" class="org.apache.cxf.management.jmx.InstrumentationManagerImpl">
<property name="bus" ref="cxf" />
<property name="enabled" value="true" />
<property name="JMXServiceURL " value="service:jmx:rmi:///jndi/rmi://localhost:9914/jmxrmi" />
</bean>
I'm working with CXF 3.0.2, and a jconsole in JRE 1.6.0_27 ...
This is working fine :
jconsole connection string

Get BeanCreationException when try to add Jackson Library

I have a simple Hello World example that passes a Map to Camel and displays the values to the console via Log4J. I want to expand this example to render this map in JSON by adding the Jackson library to my Camel applicationContext.xml
First I tried adding the following XML tags to my applicationContext.xml (as specified at
http://camel.apache.org/json.html under "Using JSON in Spring DSL")
<camel:dataFormats>
<camel:json id="jack" library="Jackson"/>
</camel:dataFormats>
But when I add this to my applicationContext.xml, and run my Java code I get the following XmlBeanDefinitionStoreException message:
cvc-complex-type.2.4.a: Invalid content was found starting with element 'dataFormats'. One of '{"http://camel.apache.org/schema/
spring":route}' is expected.
Moving these tags inside or outside of my camelContext yields the same error (just a longer list of URLs when inside the camelContext).
Is there something else I need to specify in my ApplicationContext.xml?
Here is my current applicationContext.xml:
UPDATED: The following xml now works. Had to move the location of the dataFormats XML tags.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean
class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<context:component-scan base-package="sample" />
<context:annotation-config />
<camel:camelContext id="HelloWorldContext">
<camel:dataFormats>
<camel:json id="jack" library="Jackson"/>
</camel:dataFormats>
<camel:route>
<camel:from
uri="timer://hello.world.request.timer?fixedRate=true&period=10000" />
<camel:to uri="log:hello.world.request?level=INFO?showAll=true" />
<camel:bean ref="helloWorld" />
<camel:to uri="log:hello.world.response?level=INFO?showAll=true" />
</camel:route>
</camel:camelContext>
<bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="transacted" value="false" />
<property name="concurrentConsumers" value="1" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost" />
<property name="redeliveryPolicy" ref="redeliveryPolicy" />
<property name="prefetchPolicy" ref="prefetchPolicy" />
</bean>
<bean id="prefetchPolicy" class="org.apache.activemq.ActiveMQPrefetchPolicy">
<property name="queuePrefetch" value="5" />
</bean>
<bean id="redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<property name="maximumRedeliveries" value="1" />
<property name="backOffMultiplier" value="2" />
<property name="initialRedeliveryDelay" value="2000" />
<property name="useExponentialBackOff" value="true" />
</bean>
</beans>
The dateFormats and json elements are part of the camel namespace. You need to specify that
<camel:dataFormats>
<camel:json id="jack" library="Jackson"/>
</camel:dataFormats>

How to use default-servlet-handler

I want to configure Spring MVC to serve dynamic files mixed with static files, like this (URL => File):
/iAmDynamic.html => /WEB-INF/views/iAmDynamic.html.ftl
/iAmAlsoDynamic.js => /WEB-INF/views/iAmAlsoDynamic.js.ftl
/iAmStatiHtml => /iAmStatic.html
The DispatchServlet is mapped to /, annotation-based MVC configuration is enabled and I have a view controller like this (Simplified):
#Controller
public class ViewController
{
#RequestMapping("*.html")
public String handleHtml(final HttpServletRequest request)
{
return request.getServletPath();
}
#RequestMapping("*.js")
public String handleJavaScript(final HttpServletRequest request)
{
return request.getServletPath();
}
}
The spring config looks like this:
<context:component-scan base-package="myPackage" />
<mvc:default-servlet-handler />
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/views/" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
</bean>
Unfortunately it doesn't work. When this <mvc:default-servlet-handler /> is active then I can only access the iAmStatic.html file. When I disable the default-servlet-handler then only the dynamic stuff works. But I want both at once and that's exactly what this default-servlet-handler should do, or not? Where is the error here?
I had similar problem, none of the requests were getting mapped to the Spring Controllers:
I discovered I was missing this in spring config xml:
<mvc:annotation-driven/>
It seems with , this is necessary. From documentation, the purpose of doing this is:
Configures the annotation-driven Spring MVC Controller programming model
I will also let DefaultServlet handle static content requests.
So your spring config should look like:
<context:component-scan base-package="myPackage" />
<!-- Define location and mapping of static content -->
<mvc:resources location="/static/" mapping="/static/**"/>
<mvc:default-servlet-handler />
<mvc:annotation-driven/>
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/views/" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
</bean>
Hope this helps!
You need to define two important configurations
<mvc:annotation-driven/>
<mvc:default-servlet-handler />
<mvc:annotation-driven/> will enable your default infrastructure beans where as <mvc:default-servlet-handler /> will configure a handler for serving static resources by forwarding to the Servlet container's default Servlet.
Also don't forget the mvc name space i.e xmlns:mvc="http://www.springframework.org/schema/mvc"
My complete config file (using TilesViewResolver) looks like below
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven/>
<!--
Configures a handler for serving static resources by forwarding to the
Servlet container's default Servlet.
-->
<mvc:default-servlet-handler />
<mvc:view-controller path="/" view-name="welcome"/>
<mvc:view-controller path="/home" view-name="welcome"/>
<bean class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.tiles3.TilesViewResolver">
<property name="order" value="1"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="2"/>
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
Also if you have multiple HandlerMapping considering ordering them. For one for which you don't provide order explicitly Spring treats it with lowest precedence.
I think that the view name you are returning from the ViewController is invalid. I expect that request.getServletPath() returns a blank string for all URLs, because the path to your servlet is probably / and the Java documentation says that getServletPath() returns a blank string for that path. Therefore the FreeMarker view resolver is probably ignoring the view name because it wouldn't know what to show.
However using a controller class with #RequestMapping is probably not the ideal way to go about this task anyway. Spring includes a ContentNegotiatingViewResolver which automatically determines the correct view depending on the content type. This overview of ContentNegotiatingViewResolver explains how to set it up.

Categories