Service Activator is not getting invoked - spring-boot

I am trying this use case:
Poll for a message in a queue->transform the message->Call a method with the transformed message.
Here is my code
<jms:message-driven-channel-adapter id="jmsIn"
destination-name="test"
channel="jmsInChannel"/>
<channel id="jmsInChannel"/>
<channel id="consoleOut"/>
<int:transformer input-channel="jmsInChannel" ref="xmlMsgToVORPojoTransformer" output-channel="consoleOut">
</int:transformer>
<beans:bean id="xmlMsgToVORPojoTransformer" class="com.order.jmspublisher.ValidateOrderMessageTransformer">
<beans:property name="unmarshaller" ref="marshaller" />
</beans:bean>
<beans:bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<beans:property name="classesToBeBound">
<beans:list>
<beans:value>com.order.jmspublisher.ValidateOrderResponseType</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<logging-channel-adapter id="consoleOutloggerChannel" channel="consoleOut" log-full-message="true" level="DEBUG"/>
<int:service-activator id="sa" input-channel="consoleOut" output-channel="someChannel" method="handleVOR">
<beans:bean id="vorActivator" class="com.order.jmspublisher.VORServiceActivator"/>
</int:service-activator>
My Transformer code is as follows:
#SuppressWarnings("rawtypes")
public Message transform(String message)
{
try {
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(new StringReader(message)));
xsr.nextTag(); // Advance to Envelope tag
while(xsr.hasNext() ) {
int next = xsr.next();
if(next == XMLStreamReader.START_ELEMENT)
if(xsr.getLocalName().equals("ValidateOrderResponse"))
{
break;
}
}
ValidateOrderResponseType vor = (ValidateOrderResponseType)marshaller.unmarshal(new StAXSource(xsr));
return MessageBuilder.withPayload(vor).build();
} catch (XmlMappingException e) {
return MessageBuilder.withPayload(e).build();
} catch(Exception e){
return MessageBuilder.withPayload(e).build();
}
}
========================================================================
My service activator method code is as follows:
public class VORServiceActivator {
private static final Logger logger = LoggerFactory.getLogger(VORServiceActivator.class);
#ServiceActivator
public String handleVOR(ValidateOrderResponseType vor)
{
logger.info("vor information received and invoke some services here....\r\n"+vor);
return vor.toString();
}
}
=========================================================================
My service activator method is not getting called. But my transform is getting called and I can see the same in logs.
Please help me to know where I am going wrong.
Thanks in advance.

<channel id="consoleOut"/> is a DirectChannel; you have 2 consumers subscribed to the channel (logging adapter and service activator).
The default dispatcher will distribute messages to these competing consumers in round-robin fashion; so each will get alternate messages.
You need to change consoleOut to a <publish-subscribe-channel /> if you want both consumers to get all messages.
You can read about channel types here.

Related

JMS Topic Subscriber in Spring using JMS template/ Message Subscriber

I have a simple Spring application for JMS Producer/Subscriber using ActiveMQ with below configuration :
Application Context xml :
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="userName" value="user" />
<property name="password" value="password" />
</bean>
<bean id="messageDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="messageQueue1" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE">
</property>
</bean>
<bean id="springJmsProducer" class="SpringJmsProducer">
<property name="destination" ref="messageDestination" />
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
<property name="destination" ref="messageDestination" />
<property name="jmsTemplate" ref="jmsTemplate" />
</bean>
and Below is Spring producer
public class SpringJmsProducer {
private JmsTemplate jmsTemplate;
private Destination destination;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public void sendMessage(final String msg) {
jmsTemplate.send(destination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(msg);
}});
}
}
below is Spring Consumer:
public class SpringJmsConsumer {
private JmsTemplate jmsTemplate;
private Destination destination;
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
public String receiveMessage() throws JMSException {
TextMessage textMessage =(TextMessage) jmsTemplate.receive(destination);
return textMessage.getText();
}
}
Issue : When i start producer and post messages, and then i start consumer, Consumer is not reading old messages but only reading messages posted after consumer was started. Could anyone please help me how to make this durable subscriber so that messages in queue which are not acknowledged should be read by consumer and also i need to implement Synchronous Consumer not Asynchronous.
I have tried all possible solution but none is working. Any help is highly appreciated
if you want consumer receive messages sent to the topic before he starts you have 2 choice :
1. Use Activemq Retroactive Consumer
Background A retroactive consumer is just a regular JMS Topic consumer
who indicates that at the start of a subscription every attempt should
be used to go back in time and send any old messages (or the last
message sent on that topic) that the consumer may have missed.
See the Subscription Recovery Policy for more detail.
You mark a Consumer as being retroactive as follows:
topic = new ActiveMQTopic("TEST.Topic?consumer.retroactive=true");
http://activemq.apache.org/retroactive-consumer.html
2. Use Durable Subscribers :
Note that the Durable Subscriber receive messages sent to the topic before he starts at the 2nd run
http://activemq.apache.org/manage-durable-subscribers.html
This is possible with DefaultMessageListenerContainer Asynchronously
<bean id="jmsContainer" destroy-method="shutdown"
class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="messageDestination" />
<property name="messageListener" ref="messageListenerAdapter" />
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
<property name="subscriptionDurable" value="true" />
<property name="clientId" value="UniqueClientId" />
</bean>
<bean id="messageListenerAdapter"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="springJmsConsumer" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
</bean>
AND Update your consumer :
public class SpringJmsConsumer implements javax.jms.MessageListener {
public void onMessage(javax.jms.Message message) {
// treat message;
message.acknowledge();
}
}
UPDATE to use
if you want a Synchronous Durable Subscriber, an example
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
public class SpringJmsConsumer {
private Connection conn;
private TopicSubscriber topicSubscriber;
public SpringJmsConsumer(ConnectionFactory connectionFactory, Topic destination ) {
conn = connectionFactory.createConnection("user", "password");
Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
topicSubscriber = session.createDurableSubscriber(destination, "UniqueClientId");
conn.start();
}
public String receiveMessage() throws JMSException {
TextMessage textMessage = (TextMessage) topicSubscriber.receive();
return textMessage.getText();
}
}
And update springJmsConsumer
<bean id="springJmsConsumer" class="SpringJmsConsumer">
<constructor-arg ref="connectionFactory" />
<constructor-arg ref="messageDestination" />
</bean>
Note that connection failures are not managed by this code.

How to Over ride BindAuthenticator handleBindException for Spring LDAP Authentication setup in Spring Boot

For Spring security setup in Spring Boot. The LDAP Authentication provider is configured by default to use BindAuthenticator class.
This Class contains method
/**
* Allows subclasses to inspect the exception thrown by an attempt to bind with a
* particular DN. The default implementation just reports the failure to the debug
* logger.
*/
protected void handleBindException(String userDn, String username, Throwable cause) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to bind as " + userDn + ": " + cause);
}
}
This Method is to handle the authentication related Exceptions like invalid credentials.
I want to over-ride this method so i can handle this issue and return proper error message on the basis of error codes returned by LDAP. like invalid password or the account is locked.
Current LDAP implementation always returns "Bad Credentials" that does not give the right picture that why my credentials are invalid. i want to cover the cases
where the account is Locked
password is expired so i can redirect to change password
account locked due to number of invalid password retries
Please help
The issue i fixed by defining the LDAP context instead of using the Spring Boot LDAPAuthenticationProviderConfigurer.
Then created the FilterBasedLdapUserSearch and Over-written the BindAuthentication with my ConnectBindAuthenticator.
i created a separate LDAPConfiguration class for spring boot configuration and registered all these custom objects as Beans.
From the above Objects i created LDAPAuthenticationProvider by passing my Custom Objects to constructor
The Config is as below
#Bean
public DefaultSpringSecurityContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(env.getProperty("ldap.url"));
contextSource.setBase(env.getProperty("ldap.base"));
contextSource.setUserDn(env.getProperty("ldap.managerDn"));
contextSource.setPassword(env.getProperty("ldap.managerPassword"));
return contextSource;
}
#Bean
public ConnectBindAuthenticator bindAuthenticator() {
ConnectBindAuthenticator connectBindAuthenticator = new ConnectBindAuthenticator(contextSource());
connectBindAuthenticator.setUserSearch(ldapUserSearch());
connectBindAuthenticator.setUserDnPatterns(new String[]{env.getProperty("ldap.managerDn")});
return connectBindAuthenticator;
}
#Bean
public LdapUserSearch ldapUserSearch() {
return new FilterBasedLdapUserSearch("", env.getProperty("ldap.userSearchFilter"), contextSource());
}
You have to change your spring security configuration to add your extension of BindAuthenticator:
CustomBindAuthenticator.java
public class CustomBindAuthenticator extends BindAuthenticator {
public CustomBindAuthenticator(BaseLdapPathContextSource contextSource) {
super(contextSource);
}
#Override
protected void handleBindException(String userDn, String username, Throwable cause) {
// TODO: Include here the logic of your custom BindAuthenticator
if (somethingHappens()) {
throw new MyCustomException("Custom error message");
}
super.handleBindException(userDn, username, cause);
}
}
spring-security.xml
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="LDAP_URL" />
<beans:property name="userDn" value="USER_DN" />
<beans:property name="password" value="PASSWORD" />
</beans:bean>
<beans:bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0" value="USER_SEARCH_BASE" />
<beans:constructor-arg index="1" value="USER_SEARCH_FILTER" />
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean class="com.your.project.CustomBindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch" ref="userSearch" />
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="ldapAuthProvider" />
</security:authentication-manager>
Hope it's helpful.

Activemq degrade response time

I`m using activemq 5.10 with spring 4.1.1.
I find a degrade problem with the response time of messages. After 4 days, the response time starts to grow up from 15ms to 200ms and more.
The app works fine with approximately 1000 messages per second and then all run slower.
Here is part of the xml beans:
<amq:systemUsage>
<amq:memoryUsage>
<amq:memoryUsage limit="512 mb">
</amq:memoryUsage>
</amq:memoryUsage>
<amq:storeUsage>
<amq:storeUsage limit="50 mb"></amq:storeUsage>
</amq:storeUsage>
<amq:tempUsage>
<amq:tempUsage limit="50 mb"></amq:tempUsage>
</amq:tempUsage>
</amq:systemUsage>
<amq:broker brokerName="myBroker" id="broker"
persistent="false" deleteAllMessagesOnStartup="true" enableStatistics="false"
useLoggingForShutdownErrors="true">
<amq:transportConnectors>
<amq:transportConnector
uri="nio://${Ip}:${Port}?jms.useAsyncSend=true?jms.useCompression=true"
disableAsyncDispatch="false" />
</amq:transportConnectors>
<amq:destinationPolicy>
<amq:policyMap>
<amq:policyEntries>
<amq:policyEntry queue=">" optimizedDispatch="true" />
</amq:policyEntries>
</amq:policyMap>
</amq:destinationPolicy>
</amq:broker>
<!-- A JMS connection factory for ActiveMQ -->
<bean id="ConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="nio://${Ip}:${Port}" />
<bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
destroy-method="stop">
<property name="connectionFactory" ref="ConnectionFactory" />
<property name="maxConnections" value="10" />
</bean>
<bean id="Listener" class="xx.com.xxx.MessageListener" />
<jms:listener-container container-type="default"
connection-factory="ConnectionFactory" acknowledge="auto">
<jms:listener destination="${QueueName}"
ref="sisBusMessageListener" method="onMessage" />
</jms:listener-container>
And here is the Java code:
public class MessageListener extends GenericMessageListener {
public void onMessage(Message request) {
MyExecutor mythread = new MyExecutor(request, new DateTime());
executor.execute(mythread );
}
}
public class MyExecutor {
public void init() {
try {
connectionFactory = ApplicationHelper.getBean("ConnectionFactory");
connectionFactory.setAlwaysSessionAsync(false);
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
} catch (JMSException e) {
logs...
}
}
public void returnMessage(Message request, Object responseFromExternalSystem) throws JMSException {
MapMessage response = session.createMapMessage();
response.setJMSCorrelationID(request.getJMSCorrelationID());
//code that set info on map message is here
replyProducer = session.createProducer(request.getJMSReplyTo());
replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
replyProducer.send(response);
}
}
The solution was re-use the connections and sessions creating only one instanace and creating an other instance in some cases(errors, desconnections...)

Spring splitter/aggregator handling exceptions

Version : spring-integration-core - 2.2.3
Here is the simplified version of my splitter/aggregator setup.
<task:executor id="taskExecutor" pool-size="${pool.size}"
queue-capacity="${queue.capacity}"
rejection-policy="CALLER_RUNS" keep-alive="120"/>
<int:channel id="service-requests"/>
<int:channel id="service-request"/>
<int:channel id="channel-1">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:channel id="channel-2">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:gateway id="myServiceRequestor" default-reply-timeout="${reply.timeout}"
default-reply-channel="service-aggregated-reply"
default-request-channel="service-request"
service-interface="com.blah.blah.MyServiceRequestor"/>
<int:splitter input-channel="service-request"
ref="serviceSplitter" output-channel="service-requests"/>
<!-- To split the request and return a java.util.Collection of Type1 and Type2 -->
<bean id="serviceSplitter" class="com.blah.blah.ServiceSplitter"/>
<int:payload-type-router input-channel="service-requests" resolution-required="true">
<int:mapping
type="com.blah.blah.Type1"
channel="channel-1"/>
<int:mapping
type="com.blah.blah.Type2"
channel="channel-2"/>
</int:payload-type-router>
<!-- myService is a bean where processType1 & processType2 method is there to process the payload -->
<int:service-activator input-channel="channel-1"
method="processType1" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:service-activator input-channel="channel-2"
method="processType2" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:publish-subscribe-channel id="service-reply" task-executor="taskExecutor"/>
<!-- myServiceAggregator has a aggregate method which takes a Collection as argument(aggregated response from myService) -->
<int:aggregator input-channel="service-reply"
method="aggregate" ref="myServiceAggregator"
output-channel="service-aggregated-reply"
send-partial-result-on-expiry="false"
message-store="myResultMessageStore"
expire-groups-upon-completion="true"/>
<bean id="myResultMessageStore" class="org.springframework.integration.store.SimpleMessageStore" />
<bean id="myResultMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
<property name="messageGroupStore" ref="myResultMessageStore" />
<property name="timeout" value="2000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myResultMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>
If the processType1/processType2 method in mySevice throws a RuntimeException, then it tries to send the message to an error channel(i believe spring does it by default) and the message payload in error channel stays on in heap and not getting garbage collected.
Updated More Info:
For my comment on error channel. I debugged the code and found that ErrorHandlingTaskExecutor is trying to use a MessagePublishingErrorHandler which inturn sending the message to the channel returned by MessagePublishingErrorHandler.resolveErrorChannel method.
Code snippet from ErrorHandlingTaskExecutor.java
public void execute(final Runnable task) {
this.executor.execute(new Runnable() {
public void run() {
try {
task.run();
}
catch (Throwable t) {
errorHandler.handleError(t); /// This is the part which sends the message in to error channel.
}
}
});
}
Code snipper from MessagePublishingErrorHandler.java
public final void handleError(Throwable t) {
MessageChannel errorChannel = this.resolveErrorChannel(t);
boolean sent = false;
if (errorChannel != null) {
try {
if (this.sendTimeout >= 0) {
sent = errorChannel.send(new ErrorMessage(t), this.sendTimeout);
.....
When i take a heap dump, I always see the reference to the payload message(which i believe is maintained in the above channel) and not getting GC'ed.
Would like to know what is the correct way to handle this case or if i'm missing any in my config?
Also is it possible to tell spring to discard the payload(instead of sending it to error channel) in case of any exception thrown by the service activator method?
Looking forward for your inputs.
Thanks.
You don't have an error-channel defined on your gateway so we won't send it there, we'll just throw an exception to the caller.
However, the partial group is sitting in the aggregator and will never complete. You need to configure a MessageGroupStoreReaper as shown in the reference manual (or set a group-timeout in Spring Integration 4.0.x) to discard the partial group.

Spring 4 WebSockets subscribe to a topic

Is it possible to map a method in a spring managed bean to a topic subscription using spring messaging?
I've looked at the examples here: http://assets.spring.io/wp/WebSocketBlogPost.html
including the example stock application but it looks like all topic subscription is on the client side. Is it possible to also subscribe to topics on the server side?
You can use JMS to subscribe to the topic.
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener" />
</bean>
More here: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/jms.html

Resources