publisher failed to recieve first message on rabbitmq - rabbitmq

im trying to implement an integration test on RabbitMQ, so i have a setup of a published and basic consumer, to send and receive messages across a queue.
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setHost("localhost");
factory.setPort(5672);
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(RPC_QUEUE_NAME_KEY, DURABLE, EXCLUSIVE, AUTO_DELETE, null);
channel.exchangeDeclare(RPC_EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, RPC_EXCHANGE_NAME, RPC_QUEUE_NAME_KEY);
channel.basicQos(1);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, false, consumer);
System.out.println(" [x] Awaiting RPC requests");
while(true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
BasicProperties props = delivery.getProperties();
BasicProperties replyProps = new BasicProperties.Builder().correlationId(props.getCorrelationId()).build();
try
{
RetrieveUnencryptedCardsResponse response = null;
JAXBContext jaxbContext = JAXBContext.newInstance(RetrieveUnencryptedCardsRequest.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(new String(delivery.getBody()));
JAXBElement<RetrieveUnencryptedCardsRequest> message = jaxbUnmarshaller.unmarshal(new StreamSource(reader), RetrieveUnencryptedCardsRequest.class);
response = retrieveUnencryptedCardsResponse(message);
JAXBContext jaxbMarshallingContext = JAXBContext.newInstance(RetrieveUnencryptedCardsResponse.class);
Marshaller jaxbMarshaller = jaxbMarshallingContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
ByteArrayOutputStream xmlStream = new ByteArrayOutputStream();
jaxbMarshaller.marshal(new JAXBElement<RetrieveUnencryptedCardsResponse>(_RetrieveUnencryptedCardsResponse_QNAME, RetrieveUnencryptedCardsResponse.class, null,
response), xmlStream);
data = xmlStream.toByteArray();
xmlStream.close();
}
catch(Exception e)
{
logger.error(e);
}
finally
{
channel.basicPublish("", props.getReplyTo(), replyProps, data);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(connection != null)
{
try
{
connection.close();
}
catch(Exception ignore)
{
}
}
}
}
The problem occurs when I try to receive the messages from the queue. i cant receive the first message, but i can from the second one.
i don't know if this might help:
<rabbit:queue id="account.amqp.default.reply.queue"
name="${vendor.account.amqp.default.reply.queue}" durable="${connector.fixed.queue.durable}"
auto-delete="false" exclusive="false">
<rabbit:queue-arguments>
<entry key="x-message-ttl">
<value type="java.lang.Long">${vendor.account.amqp.default.reply.ttl}</value>
</entry>
<entry key="x-ha-policy" value="${vendor.account.default.queue.ha.policy}" />
<entry key="x-ha-policy-params"
value="#{ T(org.springframework.util.CollectionUtils).arrayToList((T(org.springframework.util.StringUtils).commaDelimitedListToSet('${vendor.account.default.queue.ha.nodes}')).toArray())}" />
</rabbit:queue-arguments>
</rabbit:queue>
<rabbit:template id="pamRabbitTemplate"
connection-factory="connectionFactory" message-converter="accountMarshallingMessageConverter"
reply-timeout="${gateway.reply.timeout}" reply-queue="${vendor.account.amqp.default.reply.queue}">
<rabbit:reply-listener
concurrency="${vendor.account.amqp.default.reply.queue.consumers}" />
</rabbit:template>
<bean id="accountMarshallingMessageConverter"
class="org.springframework.amqp.support.converter.MarshallingMessageConverter">
<property name="marshaller" ref="fuelNotificationWsMarshaller" />
<property name="unmarshaller" ref="fuelNotificationWsMarshaller" />
</bean>
and in pamRabbitTemplate:
<int:channel id="channel.accounts.request" />
<int:channel id="channel.accounts.reply" />
<int:channel id="channel.accounts.outbound.request" />
<int:channel id="channel.accounts.outbound.reply" />
<int:channel id="channel.accounts.error" />
<int-amqp:outbound-gateway request-channel="channel.accounts.outbound.request"
reply-channel="channel.accounts.outbound.reply" amqp-template="pamRabbitTemplate"
exchange-name="${pam.exchange.service.account}" routing-key="${pam.routingkey.service.account}"
requires-reply="false" />
<int:gateway id="gateway.account"
service-interface="notification.fuelnotification.infrastructure.integration.RetrieveAccountsGateway"
error-channel="channel.error" default-reply-timeout="${gateway.reply.timeout}">
<int:method name="retrieveUnencryptedCards"
request-channel="channel.accounts.request" reply-channel="channel.accounts.reply" />
</int:gateway>
<int:transformer id="accountsReqTf" method="transform"
input-channel="channel.accounts.request" output-channel="channel.accounts.outbound.request">
<bean
class="notification.fuelnotification.infrastructure.transformer.common.AccountsRequestTransformer" />
</int:transformer>
<int:transformer id="accountResTf" method="transform"
input-channel="channel.accounts.outbound.reply" output-channel="channel.accounts.reply">
<bean
class="notification.fuelnotification.infrastructure.transformer.common.UnencryptedCardsResponseTransformer" />
</int:transformer>
<int:transformer id="errorLoggerTransformer"
method="logDetails" input-channel="channel.error">
<bean
class="notification.fuelnotification.infrastructure.config.amqp.ErrorChannelDetailLogger" />
</int:transformer>

Related

Apache TomEE external ActiveMQ resource not rolling back in distributed transaction

I am trying to achieve a distributed transaction in Apache TomEE. In words, the flow is:
A message reader (i.e. a message driven bean) reads from a queue (1) and processes one message triggering:
one or more inserts/updates on a database (2)
sending a message to another queue (3)
Operations 1, 2, & 3 are all part of the same XA transaction controlled by TomEE. Therefore, under any circumstances they either all fail or all succeed.
tomee.xml
<?xml version="1.0" encoding="UTF-8"?>
<tomee>
this resource adapter is just necessary to tell tomee to not start internal ActiveMq instance
<Resource id="MyAdapter" type="ActiveMQResourceAdapter">
BrokerXmlConfig
ServerUrl tcp://fakehost:666
</Resource>
<Resource id="jms/MyIncomingConnFactory" type="javax.jms.ConnectionFactory" class-name="org.apache.activemq.ActiveMQXAConnectionFactory">
BrokerURL tcp://externalhost:61616?jms.redeliveryPolicy.maximumRedeliveries=0
</Resource>
<Resource id="jms/MyOutgoingConnFactory" type="javax.jms.ConnectionFactory" class-name="org.apache.activemq.ActiveMQXAConnectionFactory">
BrokerURL tcp://externalhost:61616?jms.redeliveryPolicy.maximumRedeliveries=0
</Resource>
<Resource id="jms/MyOutgoingQueue" class-name="org.apache.activemq.command.ActiveMQQueue">
PhysicalName MY_OUTGOING_QUEUE
</Resource>
<Resource id="jms/MyIncomingQueue" class-name="org.apache.activemq.command.ActiveMQQueue">
PhysicalName MY_INCOMING_QUEUE
</Resource>
<Resource id="jdbc/myDBXAPooled" type="DataSource">
XaDataSource myDBXA
DataSourceCreator dbcp
JtaManaged true
UserName TestUser
Password TestPassword
MaxWait 2000
ValidationQuery SELECT 1
MaxActive 15
</Resource>
<Resource id="myDBXA" type="XADataSource" class-name="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource">
Url jdbc:mysql://localhost:3306/test
User TestUser
Password TestPassword
</Resource>
</tomee>
Springconfig.xml:
<?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:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd">
<!-- <jee:jndi-lookup jndi-name="myDBXAPooled" id="myDatasource" resource-ref="true" /> -->
<jee:jndi-lookup jndi-name="jms/MyOutgoingConnFactory" id="myOutgoingConnFactory" resource-ref="true" />
<jee:jndi-lookup jndi-name="jms/MyIncomingConnFactory" id="myIncomingConnFactory" resource-ref="true" />
<jee:jndi-lookup jndi-name="jms/MyOutgoingQueue" id="myOutgoingQueue" resource-ref="true" />
<jee:jndi-lookup jndi-name="jms/MyIncomingQueue" id="myIncomingQueue" resource-ref="true" />
<jee:jndi-lookup jndi-name="jdbc/myDBXAPooled" id="myDatasource" resource-ref="true" />
<tx:jta-transaction-manager/>
<!-- <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> -->
<!-- the previous two ways of getting the transactionManager seems equivalent and both get Geronimo -->
</beans>
SpringConfig.xml
<?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:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd">
<bean id="messageListener" class="com.test.MyListener">
<property name="connectionFactory" ref="myIncomingConnFactory" />
<property name="destination" ref="myIncomingQueue" />
<!-- <property name="sessionTransacted" value="true" /> -->
<property name="concurrentConsumers" value="1" />
<property name="maxConcurrentConsumers" value="6" />
<property name="messageListener" ref="myMessageProcessor" />
<property name="transactionManager" ref="transactionManager" />
<property name="taskExecutor" ref="msgListenersTaskExecutor" />
</bean>
<bean id="myMessageProcessor" class="com.test.MyMessageReceiver">
<property name="forwardConnectionFactory" ref="myOutgoingConnFactory" />
<property name="forwardQueue" ref="myOutgoingQueue" />
<property name="datasource" ref="myDatasource" />
</bean>
<bean id="msgListenersTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"/>
</beans>
MyMessageReceiver.java:
package com.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class MyMessageReceiver implements MessageListener {
static Logger log = Logger.getLogger(MyMessageReceiver.class);
private ConnectionFactory forwardConnectionFactory;
private Queue forwardQueue;
private DataSource datasource;
public void setForwardConnectionFactory(ConnectionFactory connFactory) {
forwardConnectionFactory=connFactory;
}
public void setforwardQueue(Queue queue) {
forwardQueue=queue;
}
public void setDatasource(DataSource ds) {
datasource=ds;
}
#Override
#Transactional(propagation=Propagation.REQUIRED)
public void onMessage(Message message) {
log.info("************************************");
MyListener listener = (MyListener)SpringContext.getBean("messageListener");
listener.printInfo();
log.info("************************************");
TextMessage msg = (TextMessage) message;
String text = null;
try {
text = msg.getText();
if (text != null) log.info("MESSAGE RECEIVED: "+ text);
updateDB(text); // function call to update DB
sendMsg(text); // function call to publish messages to queue
System.out.println("****************Rollback");
// Throwing exception to rollback DB, Message should not be
// published and consumed message sent to a DLQ
//(Broker side DLQ configuration already done)
throw new RuntimeException();
//if (text!=null && text.indexOf("rollback")!=-1) throw new RuntimeException("Message content includes the word rollback");
} catch (Exception e) {
log.error("Rolling back the entire XA transaction");
log.error(e.getMessage());
throw new RuntimeException("Rolled back because of "+e.getMessage());
}
}
private void updateDB(String text) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
System.out.println("*******datasource "+datasource);
conn = datasource.getConnection();
System.out.println("*******conn "+conn.getMetaData().getUserName());
if (conn!=null) {
System.out.println("*******conn "+conn.getMetaData().getUserName());
ps = conn.prepareStatement("INSERT INTO MY_TABLE (name) VALUES(?)");
ps.setString(1, text);
ps.executeUpdate();
}
} catch (Exception e) {
throw e;
} finally {
if (ps!=null) {
try {
ps.close();
} catch (SQLException e) {
log.error(e.getMessage());
// do nothing
}
}
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
log.error(e.getMessage());
// do nothing
}
}
}
}
private void sendMsg(String msgToBeSent) throws Exception {
javax.jms.Connection conn = null;
Session session = null;
try {
System.out.println("*************forwardConnectionFactory"+forwardConnectionFactory);
conn = forwardConnectionFactory.createConnection();
session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(forwardQueue);
TextMessage msg = session.createTextMessage(msgToBeSent);
messageProducer.send(msg);
} catch (Exception e) {
throw e;
} finally {
if (session != null) {
try {
session.close();
} catch (JMSException e) {
// do nothing
}
}
if (conn != null) {
try {
conn.close();
} catch (JMSException e) {
// do nothing
}
}
}
}
}
MyListener.java:
package com.test;
import javax.transaction.Status;
import javax.transaction.SystemException;
import org.apache.log4j.Logger;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.transaction.jta.JtaTransactionManager;
public class MyListener extends DefaultMessageListenerContainer {
static Logger log = Logger.getLogger(MyListener.class);
public void printInfo() {
try {
log.info("trans manager="+((JtaTransactionManager)this.getTransactionManager()).getTransactionManager()+","+((JtaTransactionManager)this.getTransactionManager()).getTransactionManager().getStatus()+", this.isSessionTransacted()="+this.isSessionTransacted());
log.info("STATUS_ACTIVE="+Status.STATUS_ACTIVE);
log.info("STATUS_COMMITTEDE="+Status.STATUS_COMMITTED);
log.info("STATUS_COMMITTING="+Status.STATUS_COMMITTING);
log.info("STATUS_MARKED_ROLLBACK="+Status.STATUS_MARKED_ROLLBACK);
log.info("STATUS_NO_TRANSACTION="+Status.STATUS_NO_TRANSACTION);
log.info("STATUS_PREPARED="+Status.STATUS_PREPARED);
log.info("STATUS_PREPARING="+Status.STATUS_PREPARING);
log.info("STATUS_ROLLEDBACK="+Status.STATUS_ROLLEDBACK);
log.info("STATUS_ROLLING_BACK="+Status.STATUS_ROLLING_BACK);
log.info("STATUS_UNKNOWN="+Status.STATUS_UNKNOWN);
} catch (SystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void forceRollback() {
try {
((JtaTransactionManager)this.getTransactionManager()).getTransactionManager().setRollbackOnly();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
After updating the database and sending the message to the outgoing queue I am purposefully throwing a RuntimeException just to test the transaction rollback of both the database and the message broker.
All three operations are committed in case of success, but it only rolls back the database operation in case of failure, while the two JMS operations are committed anyway.
It could either be:
wrong settings in my tomee.xml (especially queue connection factories) or somewhere else
a bug??
I already spent quite much time fighting with the thing and searching for possible solutions.
It would be great to hear your opinion on this and, once again, advanced apologies if it turns out to be a mistake on my side.
I believe you need to use the ActiveMQ JCA resource adapter to ensure that connections are automatically enlisted into the XA transaction. Try this:
<tomee>
<Resource id="MyJmsResourceAdapter" type="ActiveMQResourceAdapter">
# Do not start the embedded ActiveMQ broker
BrokerXmlConfig =
ServerUrl = tcp://externalhost:61616?jms.redeliveryPolicy.maximumRedeliveries=0
</Resource>
<Resource id="jms/MyIncomingConnFactory" type="javax.jms.ConnectionFactory">
resourceAdapter = MyJmsResourceAdapter
transactionSupport = xa
</Resource>
<Resource id="jms/MyOutgoingConnFactory" type="javax.jms.ConnectionFactory">
resourceAdapter = MyJmsResourceAdapter
transactionSupport = xa
</Resource>
<Resource id="jms/MyOutgoingQueue" type="javax.jms.Queue"/>
<Resource id="jms/MyIncomingQueue" type="javax.jms.Queue"/>
<Resource id="jdbc/myDBXAPooled" type="DataSource">
XaDataSource myDBXA
DataSourceCreator dbcp
JtaManaged true
UserName TestUser
Password TestPassword
MaxWait 2000
ValidationQuery SELECT 1
MaxActive 15
</Resource>
<Resource id="myDBXA" type="XADataSource" class-name="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource">
Url jdbc:mysql://localhost:3306/test
User TestUser
Password TestPassword
</Resource>
</tomee>

Routing to virtual destinations inside ActiveMQ broker

I have an activemq configuration wherein I have a virtual destination and a normal topic
I want to route all the JMS messages to the destination(VirtualTopic.Notifications) to 2 queues(VirtualTopic.SMS, VirtualTopic.EMAIL) based on their JMSType in the message header.
And I want the normal Topic(VirtualTopic.gps) to work as usual.
This is my configuration of activemq.xml. Here Consumer.SMS.VirtualTopic and Consumer.EMAIL.VirtualTopic is created.
<destinations>
<queue physicalName="Consumer.SMS.VirtualTopic" />
<queue physicalName="Consumer.EMAIL.VirtualTopic" />
</destinations>
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="VirtualTopic.Notifications" forwardOnly="false">
<forwardTo>
<filteredDestination selector="JMSType = 'SMS'" queue="Consumer.SMS.VirtualTopic"/>
<filteredDestination selector="JMSType = 'EMAIL'" queue="Consumer.EMAIL.VirtualTopic"/>
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
While the consumer and topic (VirtualTopic.gps) is created from the server side code.
private static MessageProducer getTopicProducer(String topicName) throws JMSException {
MessageProducer producer = topicProducers.get(topicName);
if (producer == null) {
logger.info("Creating message producer for Topic : {}", topicName);
Destination destination = session.createTopic(topicName);
List<String> queueNames = PropertyReader
.getPropertyStringList("jms.topic.consumer.list", JMSProducer.properties);
if (queueNames != null) {
for (String queueName : queueNames) {
Queue virtualQueue = session.createQueue(queueName);
MessageConsumer con = session.createConsumer(virtualQueue);
con.close();
}
}
producer = session.createProducer(destination);
topicProducers.put(topicName, producer);
}
return producer;
}
All the messages to the VirtualTopic.Notifications are routed to 2 different queues and consumers can pick up messages from respective queues
But the issue is all the messages which are being sent to the VirtualTopic.gps are filtered and the consumers cant consume the gps messages.
Thank you so much Hassen..
Adding this line <virtualTopic name=">" selectorAware="false" />
to the activemq.xml did the trick.
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="VirtualTopic.Notifications"
forwardOnly="false">
<forwardTo>
<filteredDestination selector="JMSType = 'SMS'"
queue="Consumer.SMS.VirtualTopic" />
<filteredDestination selector="JMSType ='EMAIL'"
queue="Consumer.EMAIL.VirtualTopic" />
</forwardTo>
</compositeQueue>
<virtualTopic name=">" selectorAware="false" />
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
The following example shows how to set up a element
in the XML configuration so that when a message is sent to MY.QUEUE
then it is really forwarded to the physical queue FOO and the topic
BAR.
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="MY.QUEUE">
<forwardTo>
<queue physicalName="FOO" />
<topic physicalName="BAR" />
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
By default, subscribers cannot consume messages directly from a
composite queue or topic - it is a logical construct only. Given the
configuration above, subscribers can only consume messages from FOO
and BAR; but not MY.QUEUE. This behaviour can be altered to implement
use cases such as watching a queue by sending the same messages to a
notification topic (wire tapping), by setting the optionally set
forwardOnly attribute to false.
<compositeQueue name="IncomingOrders" forwardOnly="false">
<forwardTo>
<topic physicalName="Notifications" />
</forwardTo>
</compositeQueue>
Messages sent to IncomingOrders will all be copied and forwarded to
Notifications, before being placed on the physical IncomingOrders
queue for consumption by subscribers.
take a look here http://activemq.apache.org/virtual-destinations.html
with your actual config you can consume only from queue's SMS & EMAIL, if you want to consume from Notifications you need to set forwardOnly="false"
UPDATE :
Try this code :
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQMessageConsumer;
import org.apache.activemq.ActiveMQSession;
import org.apache.activemq.command.ActiveMQTextMessage;
public class SimpleSenderConsumerVirtualTopic {
public static void main(String[] args) throws JMSException {
Connection conn = null;
try {
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
conn = cf.createConnection( );
ActiveMQSession session = (ActiveMQSession) conn.createSession(false,
ActiveMQSession.AUTO_ACKNOWLEDGE);
ActiveMQMessageConsumer consumer = (ActiveMQMessageConsumer) session
.createConsumer(session.createQueue("Consumer.A.VirtualTopic.gps"));
MessageProducer producer = session.createProducer(session.createTopic("VirtualTopic.gps"));
conn.start();
ActiveMQTextMessage msg = (ActiveMQTextMessage) session.createTextMessage("VirtualTopic.gps test");
producer.send(msg);
msg = null;
while ((msg = (ActiveMQTextMessage) consumer.receive(5000)) != null) {
System.out.println("Received message is: " + msg.getText());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
}
}
}
}
}
AND add this :
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="VirtualTopic.Notifications" forwardOnly="false">
<forwardTo>
<filteredDestination selector="JMSType = 'SMS'" queue="Consumer.SMS.VirtualTopic"/>
<filteredDestination selector="JMSType = 'EMAIL'" queue="Consumer.EMAIL.VirtualTopic"/>
</forwardTo>
</compositeQueue>
<virtualTopic name=">" selectorAware="false" />
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>

Logging Of 400 Error Mssage

In Production Environment,When I make Request for my web page.The Request show in apache's access logs,but not in configured my configured web logs.
My logback.xml is
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<jmxConfigurator />
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } --- [%15.15t{14}] %-40.40logger{0} : %m%n" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/var/log/company_name/payments.log</file>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE" />
</root>
And My controller is
#CrossOrigin
#RestController
#RequestMapping("/my/v1")
public class PaymentApi {
#RequestMapping(value = RestURIConstants.VOID_TRANSACTION,
method = RequestMethod.POST )
public #ResponseBody ResponseEntity<?> voidPayment(#RequestParam("request") String voidRequestJson,
HttpServletRequest httpServletRequest) {
//Code here.
}
#ExceptionHandler
void handleException(Exception e, HttpServletResponse response) throws IOException {
logger.error("Error in Request : " + new ObjectMapper().writeValueAsString(e));
if (e instanceof IllegalArgumentException) {
response.sendError(HttpStatus.BAD_REQUEST.value(), e.getMessage());
return;
}
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), "an error occured while processing request");
}
}
I am making web Request using below code.
public static <T extends Response,S extends Request> T doPostRequest(S request,
Class<T> responseClass,
String urlString) throws ClientProtocolException, IOException{
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
logger.info("Sending Request " + objectMapper.writeValueAsString(request));
HttpEntity<String> entity = new HttpEntity<String>("request=" + objectMapper.writeValueAsString(request), headers);
ResponseEntity<String> response = null ;
response = restTemplate.exchange(urlString, HttpMethod.POST, entity, String.class);
logger.info("Response recieved " + response.toString());
if (response.getStatusCode() == HttpStatus.OK ||
response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
return objectMapper.readValue(response.getBody(), responseClass) ;
// gson.fromJson(response.getBody(), responseClass) ;
}else{
return objectMapper.readValue(response.getBody(), responseClass) ;
}
}
Same code is working for my local ,But in production it throws 400 Error and also there are no logs in payments.log file.
The Java version in production on my payment server is on 1.8.0_25 and server which is making these request are on 1.8.0_91.
I am unable to identify the reasons as there are no logs are present for this web request in payments.log
their is limit on header size on production.Because of that i am getting this issue and there are no logs in payments.log.

Spring integration with Rabbit AMQP for "Client Sends Message -> Server Receives & returns msg on return queue --> Client get correlated msg"

I am able to write a java program using Rabbit Java API's doing the following:
Client sends message over Rabbit MQ exchange/queue with correlation Id (Say UUID - "348a07f5-8342-45ed-b40b-d44bfd9c4dde").
Server receives the message.
Server sends response message over Rabbit MQ exchange/queue with the same correlation Id - "348a07f5-8342-45ed-b40b-d44bfd9c4dde".
Client received the correlated message only in the same thread as 1.
Below is the Send.java and Recv.java using Rabbit APIs. I need help to convert this sample to use Spring AMQP integration especially receiving part on step 4. I am looking for something like receive method which can filter message using correlation Id.
Send.java:
import java.util.UUID;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
public class Send {
private final static String REQUEST_QUEUE = "REQUEST.QUEUE";
private final static String RESPONSE_QUEUE = "RESPONSE.QUEUE";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(REQUEST_QUEUE, false, false, false, null);
String message = "Hello World!";
String cslTransactionId = UUID.randomUUID().toString();
BasicProperties properties = (new BasicProperties.Builder())
.correlationId(cslTransactionId)
.replyTo(RESPONSE_QUEUE).build();
channel.basicPublish("", REQUEST_QUEUE, properties, message.getBytes());
System.out.println("Client Sent '" + message + "'");
Channel responseChannel = connection.createChannel();
responseChannel.queueDeclare(RESPONSE_QUEUE, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
responseChannel.basicConsume(RESPONSE_QUEUE, false, consumer);
String correlationId = null;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String responseMessage = new String(delivery.getBody());
correlationId = delivery.getProperties().getCorrelationId();
System.out.println("Correlation Id:" + correlationId);
if (correlationId.equals(cslTransactionId)) {
System.out.println("Client Received '" + responseMessage + "'");
responseChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), true);
break;
}
}
channel.close();
connection.close();
}
}
Recv.java
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv {
private final static String REQUEST_QUEUE = "REQUEST.QUEUE";
private final static String RESPONSE_QUEUE = "RESPONSE.QUEUE";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(REQUEST_QUEUE, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(REQUEST_QUEUE, true, consumer);
String correlationId = null;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
correlationId = delivery.getProperties().getCorrelationId();
System.out.println("Correlation Id:" + correlationId);
System.out.println("Server Received '" + message + "'");
if (correlationId != null)
break;
}
String responseMsg = "Response Message";
Channel responseChannel = connection.createChannel();
responseChannel.queueDeclare(RESPONSE_QUEUE, false, false, false, null);
BasicProperties properties = (new BasicProperties.Builder())
.correlationId(correlationId).build();
channel.basicPublish("", RESPONSE_QUEUE, properties,responseMsg.getBytes());
System.out.println("Server Sent '" + responseMsg + "'");
channel.close();
connection.close();
}
}
After running the Java configuration provided by gary, I am trying to move the configuration to XML format for server side adding listener. Below is the XML configuration:
server.xml
<?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:int-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xsi:schemaLocation="http://www.springframework.org/schema/integration/amqp http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean
id="serviceListenerContainer"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="queues" ref="requestQueue"/>
<property name="messageListener" ref="messageListenerAdaptor"/>
</bean>
<bean id="messageListenerAdaptor"
class="org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="pojoListener" />
</bean>
<bean
id="pojoListener"
class="PojoListener"/>
<bean
id="replyListenerContainer"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="queues" ref="replyQueue"/>
<property name="messageListener" ref="fixedReplyQRabbitTemplate"/>
</bean>
<!-- Infrastructure -->
<rabbit:connection-factory
id="connectionFactory"
host="localhost"
username="guest"
password="guest"
cache-mode="CHANNEL"
channel-cache-size="5"/>
<rabbit:template
id="fixedReplyQRabbitTemplate"
connection-factory="connectionFactory"
exchange="fdms.exchange"
routing-key="response.key"
reply-queue="RESPONSE.QUEUE">
<rabbit:reply-listener/>
</rabbit:template>
<rabbit:admin connection-factory="connectionFactory"/>
<rabbit:queue id="requestQueue" name="REQUEST.QUEUE" />
<rabbit:queue id="replyQueue" name="RESPONSE.QUEUE" />
<rabbit:direct-exchange name="fdms.exchange" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="RESPONSE.QUEUE" key="response.key" />
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>
SpringReceive.java
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringReceive {
/**
* #param args
*/
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("cslclient.xml");
SimpleMessageListenerContainer serviceListenerContainer = context.getBean("serviceListenerContainer", SimpleMessageListenerContainer.class);
serviceListenerContainer.start();
}
}
You can use RabbitTemplate.sendAndReceive() (or convertSendAndReceive()) with a reply listener container (Docs here); the template will take care of the correlation for you.
If you are using Spring Integration, use an outbound gateway with an appropriately configured rabbit template.

Lucene Index: Missing documents

We have a pretty basic Lucene set up. We recently noticed that some documents aren't written to the index.
This is how we create the document:
private void addToDirectory(SpecialDomainObject specialDomainObject) throws IOException {
Document document = new Document();
document.add(new TextField("id", String.valueOf(specialDomainObject.getId()), Field.Store.YES));
document.add(new TextField("name", specialDomainObject.getName(), Field.Store.YES));
document.add(new TextField("tags", joinTags(specialDomainObject.getTags()), Field.Store.YES));
document.add(new TextField("contents", getContents(specialDomainObject), Field.Store.YES));
for (Language language : getAllAssociatedLanguages(specialDomainObject)) {
document.add(new IntField("languageId", language.getId(), Field.Store.YES));
}
specialDomainObjectIndexWriter.updateDocument(new Term("id", document.getField("id").stringValue()), document);
specialDomainObjectIndexWriter.commit();
}
This is how we create the analyzer and the index writer:
<bean id="luceneVersion" class="org.apache.lucene.util.Version" factory-method="valueOf">
<constructor-arg value="LUCENE_46"/>
</bean>
<bean id="analyzer" class="org.apache.lucene.analysis.standard.StandardAnalyzer">
<constructor-arg ref="luceneVersion"/>
</bean>
<bean id="specialDomainObjectIndexWriter" class="org.apache.lucene.index.IndexWriter">
<constructor-arg ref="specialDomainObjectDirectory" />
<constructor-arg>
<bean class="org.apache.lucene.index.IndexWriterConfig">
<constructor-arg ref="luceneVersion"/>
<constructor-arg ref="analyzer" />
<property name="openMode" value="CREATE_OR_APPEND"/>
</bean>
</constructor-arg>
</bean>
Indexing is done with a scheduled task:
#Component
public class ScheduledSpecialDomainObjectIndexCreationTask implements ScheduledIndexCreationTask {
private static final Logger logger = LoggerFactory.getLogger(ScheduledSpecialDomainObjectIndexCreationTask.class);
#Autowired
private IndexOperator specialDomainObjectIndexOperator;
#Scheduled(fixedDelay = 3600 * 1000)
#Override
public void createIndex() {
Date indexCreationStartDate = new Date();
try {
logger.info("Updating complete special domain object index...");
specialDomainObjectIndexOperator.createIndex();
if (logger.isDebugEnabled()) {
Date indexCreationEndDate = new Date();
logger.debug("Index creation duration: {} ms", indexCreationEndDate.getTime() - indexCreationStartDate.getTime());
}
} catch (IOException e) {
logger.error("Could update complete special domain object index.", e);
}
}
}
createIndex() is implemented as follows:
#Override
public void createIndex() throws IOException {
logger.trace("Preparing for index generation...");
IndexWriter indexWriter = getIndexWriter();
Date start = new Date();
logger.trace("Deleting all documents from index...");
indexWriter.deleteAll();
logger.trace("Starting index generation...");
long numberOfProcessedObjects = fillIndex();
logger.debug("Index written in " + (new Date().getTime() - start.getTime()) + " milliseconds.");
logger.debug("Number of processed objects: {}", numberOfProcessedObjects);
logger.debug("Number of documents in index: {}", indexWriter.numDocs());
indexWriter.commit();
indexWriter.forceMerge(1);
}
#Override
protected long fillIndex() throws IOException {
Page<SpecialDomainObject> specialDomainObjectsPage = specialDomainObjectRepository.findAll(new PageRequest(0, MAXIMUM_PAGE_ELEMENTS));
while (true) {
addToDirectory(specialDomainObjectsPage);
if (specialDomainObjectsPage.hasNextPage()) {
specialDomainObjectsPage =
specialDomainObjectRepository.findAll(new PageRequest(specialDomainObjectsPage.getNumber() + 1, specialDomainObjectsPage.getSize()));
} else {
break;
}
}
return specialDomainObjectsPage.getTotalElements();
}
There are about 2000 specialDomainObject instances and about 80 aren't written to the index (we checked this with Luke).
Is there anything that could cause the missing documents?
We found the problem: The default encoding of the operating system was not set to UTF-8.