How to receive and reply on Spring - spring

I'm trying to deploy a RPC (request/reply pattern) and I'm using RabbitMQ and Spring in the server side because I need dynamic consumers. I can configurate dynamic consumers with SimpleMessageListenerContainer but i don't know how to reply my message.
Here is my class configuration:
#Configuration
public class dynamicConsumerConfig {
private static Properties prop = new Properties();
public static void setPropValues() throws IOException {
File configFile = new File("src/main/resources/config.properties");
InputStream inStream = new FileInputStream(configFile.getAbsolutePath());
prop.load(inStream);
}
#Bean
public Queue slowQueue() {
return new Queue("slowQueue");
}
#Bean
public Queue fastQueue() {
return new Queue("fastQueue");
}
#Bean
public DirectExchange exchange1() {
return new DirectExchange("pdfqueues");
}
#Bean
public Binding slowBind(DirectExchange exchange, Queue slowQueue) {
return BindingBuilder.bind(slowQueue)
.to(exchange)
.with("slow");
}
#Bean
public Binding fastBind(DirectExchange exchange, Queue fastQueue) {
return BindingBuilder.bind(fastQueue)
.to(exchange)
.with("fast");
}
#Bean
public ConnectionFactory connect() throws IOException {
setPropValues();
CachingConnectionFactory connection = new CachingConnectionFactory();
connection.setHost(prop.getProperty("HOST"));
connection.setUsername(prop.getProperty("USER"));
connection.setPassword(prop.getProperty("PASS"));
connection.setPort(Integer.parseInt(prop.getProperty("PORT")));
return connection;
}
#Bean
public SimpleMessageListenerContainer container1(ConnectionFactory connection) throws IOException {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
setPropValues();
container.setConnectionFactory(connection);
container.setQueueNames("slowQueue");
container.setMessageListener(firstListener());
container.setMaxConcurrentConsumers(8);
container.setConcurrentConsumers(1);
container.setConsecutiveActiveTrigger(1);
container.setConsecutiveIdleTrigger(1);
container.setTxSize(1);
container.setPrefetchCount(1);
return container;
}
#Bean
public MessageListener firstListener()
{
return new MessageListener() {
#Override
public void onMessage(Message message) {
PdfBoxService pdfboxservice = new PdfBoxService(prop.getProperty("tmpPath"),prop.getProperty("imagicPath"),prop.getProperty("resources"),
prop.getProperty("tessdata"),prop.getProperty("languages"));
String picture = new String(message.getBody(), StandardCharsets.UTF_8);
List<ImagePair> lip = null;
try {
lip = new ArrayList<ImagePair>();
lip.add(new ImagePair("JPG", picture));
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
ByteArrayOutputStream output= pdfboxservice.ImgToPdf(lip, false, false, false, 1, 1);
} catch (IOException | InterruptedException | TransformerException | BadFieldValueException
| TesseractException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}
In the fuction firstListener() i get the message. In this case is a picture. The picture is converted from JPG to PDF. The PDF is stored in outputvariable.
I need to reply this output in other queue but i don't have tools for do it.
I think that my code is a bad pattern but I don't know how to do a RPC pattern with dynamic consumers using SimpleMessageListenerContainer.

Use a MessageListenerAdapter with a POJO method that returns a result instead of implementing MessageListener yourself.
Starting with version 2.0, a convenient FunctionalInterface has been provided:
#FunctionalInterface
public interface ReplyingMessageListener<T, R> {
R handleMessage(T t);
}
This facilitates convenient configuration of the adapter using Java 8 lamdas:
new MessageListenerAdapter((ReplyingMessageListener<String, String>) data -> {
...
return result;
}));

Related

Messages not rolling back on K8s pod restarts when using Spring JMS Listener with Client Ack

We have Spring JMS application ( deployed on K8s) which processes about 100 - 400 messages/sec. The application consumes messages from IBM MQ and processes them. Off late we have started noticing messages getting dropped whenever K8s pod restarts or deployments are done even though we have message ack in place. I am looking for a solution here to resolve this issue.
Software
Version
Spring Boot
2.1.7.RELEASE
IBM MQ Client
9.1.0.5
JMS
2.0.1
Java
11
#Configuration
#EnableJms
public class MqConfiguration {
#Bean
public MQConnectionFactory mqConnectionFactory(Servers configProperties) {
MQConnectionFactory mqConnectionFactory = new MQConnectionFactory();
try {
mqConnectionFactory.setHostName(configProperties.getHost());
mqConnectionFactory.setQueueManager(configProperties.getQueueManager());
mqConnectionFactory.setPort(Integer.valueOf(configProperties.getPort()));
mqConnectionFactory.setChannel(configProperties.getChannel());
mqConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
mqConnectionFactory.setCCSID(1208);
mqConnectionFactory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
} catch (Exception e) {
logger.logError(mqConnectionFactory, ,
"Failed to create MQ ConnectionFactory", String.valueOf(HttpStatus.SC_BAD_REQUEST), e);
}
return mqConnectionFactory;
}
#Bean(name = "messageListenerContainerFactory")
public DefaultJmsListenerContainerFactory provideJmsListenerContainerFactory(
MQConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
factory.setErrorHandler(new ErrorHandler() {
#Override
public void handleError(Throwable t) {
ServiceMetrics metrics = new ServiceMetrics();
metrics.setCorrelationId(UUID.getUUID());
logger.logError(factory, "Exception occured at JMS Factory Container Listener", String.valueOf(HttpStatus.SC_BAD_REQUEST), t);
}
});
return factory;
}
#Bean(name = "jmsQueueTemplate")
public JmsTemplate provideJmsQueueTemplate(MQConnectionFactory connectionFactory) {
return new JmsTemplate(connectionFactory);
}
}
#Configuration
public class AsyncConfiguration {
#Autowired
private Servers configProperties;
#Bean(name = "asyncTaskExecutor")
public ExecutorService getAsyncTaskExecutor() {
String THREAD_POOL = "th-pool-";
return getExecutor(THREAD_POOL, 70,true);
}
private ExecutorService getExecutor(String threadName, int maxPoolSize, boolean cached) {
final ThreadFactory threadFactory = new CustomizableThreadFactory(threadName);
if (cached) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, maxPoolSize,
60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
threadPoolExecutor.setRejectedExecutionHandler((r, executor) -> {
if (!executor.isShutdown()) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
throw new RejectedExecutionException(e);
}
}
});
return threadPoolExecutor;
} else {
return new ThreadPoolExecutor(maxPoolSize, maxPoolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
threadFactory);
}
}
#Component
public class InputQueueListener {
#Autowired
private ExecutorService asyncTaskExecutor;
#JmsListener(destination = "${mqserver.queue}", containerFactory = "messageListenerContainerFactory", concurrency = "1-16")
public void processXMLMessage(Message message) {
CompletableFuture.runAsync(() -> processMessage(message), asyncTaskExecutor);
}
private void processMessage(Message message) {
String inputXmlMessage = null;
boolean isSuccess = false;
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
inputXmlMessage = textMessage.getText();
} else if (message instanceof BytesMessage) {
BytesMessage byteMessage = (BytesMessage) message;
inputXmlMessage = CommonHelperUtil.getMessageFromBytes(byteMessage);
} else {
logger.logError(null, "Invalid message type received while converting Message to XML", String.valueOf(HttpStatus.SC_BAD_REQUEST));
errorQueuePublisher.publishErrorMessage(message);
try {
message.acknowledge();
} catch (JMSException jmsException) {
logger.logError(null, null, "Failed to Acknowledge XML message.",
String.valueOf(HttpStatus.SC_BAD_REQUEST), jmsException);
}
}
-
-
if (isSuccessProcessed) {
message.acknowledge();
} else {
message.acknowledge();
// Publishing back to the same queue
publishForRetry.publishMessageForRetry(message);
}
} catch (Exception e) {
if (StringUtils.isBlank(serviceMetrics.getCorrelationId())) {
serviceMetrics.setCorrelationId(UUID.getUUID());
}
logger.logError(null, null, "Exception while Converting Processing Message. Retrying to publish.",
String.valueOf(HttpStatus.SC_BAD_REQUEST), e);
// Publishing back to the same queue
publishForRetry.publishMessageForRetry(message);
try {
message.acknowledge();
} catch (JMSException jmsException) {
logger.logError(null, null,
"Failed to Acknowledge the Message when publishing" + "to Error Queue",
String.valueOf(HttpStatus.SC_BAD_REQUEST), jmsException);
}
}
}
}
}

Imitate behaviour of OutputStreamWriter with TcpSendingMessageHandler

We used the below piece of code for a Tcp server:
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true) {
Socket socket = serverSocket.accept();
new ServerThread(socket).start();
}
}
and ServerThread:
private class ServerThread extends Thread {
private final Socket socket;
private ServerThread(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
try (final InputStreamReader reader = new InputStreamReader(socket.getInputStream());
final OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream())) {
int bytesRead;
char[] buffer = new char[1024];
while ((bytesRead = reader.read(buffer)) != -1){
System.out.println(new String(buffer, 0, bytesRead));
writer.write(/* some char[] */);
writer.flush();
}
// ...
socket.close();
} catch (IOException exception){
// ...
socket.close();
}
}
}
We recently switched to using Spring integration:
#Bean
public TcpNetServerConnectionFactory connectionFactory() {
TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(9000);
ByteArrayRawSerializer serializer = new ByteArrayRawSerializer();
factory.setDeserializer(serializer);
factory.setSerializer(serializer);
return factory;
}
#Bean
public TcpReceivingChannelAdapter channelAdapter(AbstractServerConnectionFactory connectionFactory) {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(connectionFactory);
adapter.setOutputChannel(messageChannel());
return adapter;
}
#Bean
#ServiceActivator(inputChannel = "outputChannel")
public TcpSendingMessageHandler messageHandler(AbstractServerConnectionFactory connectionFactory){
final TcpSendingMessageHandler handler = new TcpSendingMessageHandler();
handler.setConnectionFactory(connectionFactory());
return handler;
}
#Bean
public MessageChannel messageChannel() {
return new DirectChannel();
}
#Bean
#Transformer(inputChannel = "messageChannel", outputChannel = "loggingChannel")
public ObjectToStringTransformer loggingTransformer() {
return new ObjectToStringTransformer();
}
#Bean
#ServiceActivator(inputChannel = "loggingChannel")
public void log(String message) {
System.out.println(message);
}
with above configuration; we successfully receive messages and print them. When I access the Spring application with a Telnet client, I do get the messages on terminal, however, a client application (written with C++ with WinSock2.h) gets a timeout. What is different with the application and how do I imitate the behaviour of OutputStreamWriters
writer.write(/* some char[] */);
writer.flush();
with TcpSendingMessageHandler for the WinSock2.h application?
Did you read ByteArrayRawSerializer JavaDocs? Do you know that it doesn't close a Socket by itself as you do in your custom code after reading and writing?
you probably need to think about closing a Connection after using it.
See this option on the ConnectionFactory:
/**
* If true, sockets created by this factory will be used once.
* #param singleUse The singleUse to set.
*/
public void setSingleUse(boolean singleUse) {
Then TcpSendingMessageHandler will close it after sending a message into that OutputStreamWriter.

How to build a nonblocking Consumer when using AsyncRabbitTemplate with Request/Reply Pattern

I'm new to rabbitmq and currently trying to implement a nonblocking producer with a nonblocking consumer. I've build some test producer where I played around with typereference:
#Service
public class Producer {
#Autowired
private AsyncRabbitTemplate asyncRabbitTemplate;
public <T extends RequestEvent<S>, S> RabbitConverterFuture<S> asyncSendEventAndReceive(final T event) {
return asyncRabbitTemplate.convertSendAndReceiveAsType(QueueConfig.EXCHANGE_NAME, event.getRoutingKey(), event, event.getResponseTypeReference());
}
}
And in some other place the test function that gets called in a RestController
#Autowired
Producer producer;
public void test() throws InterruptedException, ExecutionException {
TestEvent requestEvent = new TestEvent("SOMEDATA");
RabbitConverterFuture<TestResponse> reply = producer.asyncSendEventAndReceive(requestEvent);
log.info("Hello! The Reply is: {}", reply.get());
}
This so far was pretty straightforward, where I'm stuck now is how to create a consumer which is non-blocking too. My current listener:
#RabbitListener(queues = QueueConfig.QUEUENAME)
public TestResponse onReceive(TestEvent event) {
Future<TestResponse> replyLater = proccessDataLater(event.getSomeData())
return replyLater.get();
}
As far as I'm aware, when using #RabbitListener this listener runs in its own thread. And I could configure the MessageListener to use more then one thread for the active listeners. Because of that, blocking the listener thread with future.get() is not blocking the application itself. Still there might be the case where all threads are blocking now and new events are stuck in the queue, when they maybe dont need to. What I would like to do is to just receive the event without the need to instantly return the result. Which is probably not possible with #RabbitListener. Something like:
#RabbitListener(queues = QueueConfig.QUEUENAME)
public void onReceive(TestEvent event) {
/*
* Some fictional RabbitMQ API call where i get a ReplyContainer which contains
* the CorrelationID for the event. I can call replyContainer.reply(testResponse) later
* in the code without blocking the listener thread
*/
ReplyContainer replyContainer = AsyncRabbitTemplate.getReplyContainer()
// ProcessDataLater calls reply on the container when done with its action
proccessDataLater(event.getSomeData(), replyContainer);
}
What is the best way to implement such behaviour with rabbitmq in spring?
EDIT Config Class:
#Configuration
#EnableRabbit
public class RabbitMQConfig implements RabbitListenerConfigurer {
public static final String topicExchangeName = "exchange";
#Bean
TopicExchange exchange() {
return new TopicExchange(topicExchangeName);
}
#Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost("localhost");
return connectionFactory;
}
#Bean
public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
return new MappingJackson2MessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
return rabbitTemplate;
}
#Bean
public AsyncRabbitTemplate asyncRabbitTemplate() {
return new AsyncRabbitTemplate(rabbitTemplate());
}
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
#Bean
Queue queue() {
return new Queue("test", false);
}
#Bean
Binding binding() {
return BindingBuilder.bind(queue()).to(exchange()).with("foo.#");
}
#Bean
public SimpleRabbitListenerContainerFactory myRabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setMaxConcurrentConsumers(5);
factory.setMessageConverter(producerJackson2MessageConverter());
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return factory;
}
#Override
public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar) {
registrar.setContainerFactory(myRabbitListenerContainerFactory());
}
}
I don't have time to test it right now, but something like this should work; presumably you don't want to lose messages so you need to set the ackMode to MANUAL and do the acks yourself (as shown).
UPDATE
#SpringBootApplication
public class So52173111Application {
private final ExecutorService exec = Executors.newCachedThreadPool();
#Autowired
private RabbitTemplate template;
#Bean
public ApplicationRunner runner(AsyncRabbitTemplate asyncTemplate) {
return args -> {
RabbitConverterFuture<Object> future = asyncTemplate.convertSendAndReceive("foo", "test");
future.addCallback(r -> {
System.out.println("Reply: " + r);
}, t -> {
t.printStackTrace();
});
};
}
#Bean
public AsyncRabbitTemplate asyncTemplate(RabbitTemplate template) {
return new AsyncRabbitTemplate(template);
}
#RabbitListener(queues = "foo")
public void listen(String in, Channel channel, #Header(AmqpHeaders.DELIVERY_TAG) long tag,
#Header(AmqpHeaders.CORRELATION_ID) String correlationId,
#Header(AmqpHeaders.REPLY_TO) String replyTo) {
ListenableFuture<String> future = handleInput(in);
future.addCallback(result -> {
Address address = new Address(replyTo);
this.template.convertAndSend(address.getExchangeName(), address.getRoutingKey(), result, m -> {
m.getMessageProperties().setCorrelationId(correlationId);
return m;
});
try {
channel.basicAck(tag, false);
}
catch (IOException e) {
e.printStackTrace();
}
}, t -> {
t.printStackTrace();
});
}
private ListenableFuture<String> handleInput(String in) {
SettableListenableFuture<String> future = new SettableListenableFuture<String>();
exec.execute(() -> {
try {
Thread.sleep(2000);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
future.set(in.toUpperCase());
});
return future;
}
public static void main(String[] args) {
SpringApplication.run(So52173111Application.class, args);
}
}

How to Bind Publisher Message to Custom class in my Receiver with out #Payload

In my application i am publishing Message from one of FileProcess service(which will process CSV file and convert that to CSVPojo and publish to queue by using RabbitTemplate.
rabbitTemplate.convertAndSend("spring-boot-rabbitmq-BulkSolve.async_BulkSolve_Msg", "BulkSolve_GeneralrequestQueue", pojo);
I have another service BusinessProcess service that have to Listen to this queue and get messages and do some business process on those messages.To do this we intended to do this using SpringBatch, so i created a job which will listen queue and process. The trigger point for the job is as below.
#EnableRabbit
public class Eventscheduler {
#Autowired
Job csvJob;
#Autowired
private JobLauncher jobLauncher;
//#Scheduled(cron="0 */2 * ? * *")
#RabbitListener(queues ="BulkSolve_GeneralrequestQueue")
public void trigger(){
Reader.batchstatus=false;
Map<String,JobParameter> maps= new HashMap<String,JobParameter>();
maps.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(maps);
JobExecution execution=null;
try {
//JobLauncher jobLauncher = new JobLauncher();
execution=jobLauncher.run(csvJob, jobParameters);
} catch (JobExecutionAlreadyRunningException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobRestartException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobParametersInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("JOB Executed:" + execution.getStatus());
}
}
so my job will trigger when there is a msg published to this Queue. And after my job triggered in my job iam getting exception in my reader. In reader i am getting below exception.
org.springframework.amqp.support.converter.MessageConversionException: failed to resolve class name [com.comcast.FileProcess.Pojo.CSVPojo]
Below is my Reader class which i used to read message as receiver.
#Component
public class Reader extends AmqpItemReader<List<RequestPojo>>{
#Autowired
#Qualifier("rabbitTemplate")
private RabbitTemplate rabbitTemplate;
public static boolean batchstatus;
private List<RequestPojo> reqList = new ArrayList<RequestPojo>();
/* #Autowired
private SimpleMessageListenerContainer messagelistener;*/
public Reader(AmqpTemplate rabbitTemplate) {
super(rabbitTemplate);
// TODO Auto-generated constructor stub
}
List<RequestPojo> msgList = new ArrayList<RequestPojo>();
#Override
#SuppressWarnings("unchecked")
public List<RequestPojo> read() {
if(!batchstatus){
RequestPojo msg=(RequestPojo)rabbitTemplate.receiveAndConvert("BulkSolve_GeneralrequestQueue");
//return (List<RequestPojo>) rabbitTemplate.receive();
System.out.println("I am inside Reader" );
msgList.add((RequestPojo) msg);
//Object result = rabbitTemplate.receiveAndConvert();
batchstatus=true;
return msgList;
}
return null;
}
}
Here Consumer is Getting the Pojo class with its pacakge name from publisher.
I am able to consume Messages by using #Payload below is my code using which successfully consumed messages(below is that code) but i want to consume messages by using RabbitTemplate.receiveAndConvert("QueueName") Which i showed in my Reader class.
/*Below code sucesfully consumed messages from receiver side using #Payload*/
#RabbitHandler
#RabbitListener(containerFactory = "simpleMessageListenerContainerFactory", queues ="BulkSolve_GeneralrequestQueue")
public void subscribeToRequestQueue(#Payload RequestPojo sampleRequestMessage, Message message) throws InterruptedException {
System.out.println(sampleRequestMessage.toString());
}
Can any one help on this to resolve my error to consume published messages from Receiver using RabbitTemplate.receiveAndConvert("QueueName")
As per your suggestion i have made some configuration changes as below for Jackson2JsonMessageConverter to bind the message to my custom class RequestPojo as per below but it still not bind the message to my custom class. Can you please suggest me what i am doing wrong here and suggest me what to do to make it work.
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public MessageConverter jsonMessageConverter() {
return jsonCustomMessageConverter();
}
#Bean
public Jackson2JsonMessageConverter jsonCustomMessageConverter() {
Jackson2JsonMessageConverter jsonConverter = new Jackson2JsonMessageConverter();
jsonConverter.setClassMapper(classMapper());
return jsonConverter;
}
#Bean
public DefaultClassMapper classMapper() {
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put("RequestPojo", RequestPojo.class);
// idClassMapping.put("bar", Bar.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
Changed as per your suggestion but getting below error .
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
Caused by: org.springframework.amqp.support.converter.MessageConversionException: Cannot handle message
... 15 common frames omitted
Caused by: org.springframework.messaging.converter.MessageConversionException: Cannot convert from [[B] to [com.comcast.BusinessProcess.Pojos.RequestPojo] for GenericMessage [payload=byte[230], headers={amqp_receivedDeliveryMode=PERSISTENT, amqp_receivedRoutingKey=BulkSolve_SummaryrequestQueue, amqp_contentEncoding=UTF-8, amqp_receivedExchange=spring-boot-rabbitmq-BulkSolve_summary.async_BulkSolve_Msg, amqp_deliveryTag=1, amqp_consumerQueue=BulkSolve_SummaryrequestQueue, amqp_redelivered=false, id=d79db57c-3cd4-d104-a343-9373215400b8, amqp_consumerTag=amq.ctag-sYwuWA5pmN07gnEUTO-p6A, contentType=application/json, __TypeId__=com.comcast.FileProcess.Pojo.CSVPojo, timestamp=1535661077865}]
at org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver.resolveArgument(PayloadArgumentResolver.java:142) ~[spring-messaging-4.3.11.RELEASE.jar!/:4.3.11.RELEASE]
at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:112) ~[spring-messaging-4.3.11.RELEASE.jar!/:4.3.11.RELEASE]
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:135) ~[spring-messaging-4.3.11.RELEASE.jar!/:4.3.11.RELEASE]
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:107) ~[spring-messaging-4.3.11.RELEASE.jar!/:4.3.11.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:49) ~[spring-rabbit-1.7.4.RELEASE.jar!/:na]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:126) ~[spring-rabbit-1.7.4.RELEASE.jar!/:na]
... 14 common frames omitted
Below is my RabbitConfiguration class.
#Configuration("asyncRPCConfig")
#EnableScheduling
#EnableRabbit
public class RabbitMqConfiguration {
public static String replyQueue;
public static String directExchange;
public static String requestRoutingKey;
public static String replyRoutingKey;
//public static final int threads=3;
/*#Bean
public ExecutorService executorService(){
return Executors.newFixedThreadPool(threads);
}*/
/*#Bean
public CsvPublisher csvPublisher(){
return new CsvPublisher();
}
#Bean
public ExcelPublisher excelPublisher(){
return new ExcelPublisher();
}*/
/*#Bean
public GeneralQueuePublisher generalQueuePublisher(){
return new GeneralQueuePublisher();
}
*/
/*#Bean
public SummaryQueuePublisher summaryQueuePublisher(){
return new SummaryQueuePublisher();
}*/
/*#Bean
public Subscriber subscriber(){
return new Subscriber();
}*/
/*#Bean
public Subscriber1 subscriber1(){
return new Subscriber1();
}
#Bean
public Subscriber2 subscriber2(){
return new Subscriber2();
}
#Bean
public RestClient restClient(){
return new RestClient();
}*/
/*#Bean
public SubscriberGeneralQueue1 SubscriberGeneralQueue1(){
return new SubscriberGeneralQueue1();
}*/
/*#Bean
public SubscriberSummaryQueue1 SubscriberSummaryQueue1(){
return new SubscriberSummaryQueue1();
}*/
#Bean
public Eventscheduler Eventscheduler(){
return new Eventscheduler();
}
#Bean
public Executor taskExecutor() {
return Executors.newCachedThreadPool();
}
#Bean
public SimpleRabbitListenerContainerFactory simpleMessageListenerContainerFactory(ConnectionFactory connectionFactory,
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setTaskExecutor(taskExecutor());
configurer.configure(factory, connectionFactory);
return factory;
}
/* #Bean
public SimpleRabbitListenerContainerFactory simpleMessageListenerContainerFactory_Summary(ConnectionFactory connectionFactory,
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setTaskExecutor(taskExecutor());
configurer.configure(factory, connectionFactory);
return factory;
}*/
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public MessageConverter jsonMessageConverter() {
return jsonCustomMessageConverter();
}
#Bean
public Jackson2JsonMessageConverter jsonCustomMessageConverter() {
Jackson2JsonMessageConverter jsonConverter = new Jackson2JsonMessageConverter();
jsonConverter.setClassMapper(classMapper());
return jsonConverter;
}
#Bean
public DefaultClassMapper classMapper() {
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put("com.comcast.FileProcess.Pojo.CSVPojo", RequestPojo.class);
// idClassMapping.put("bar", Bar.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
#Bean
public Queue replyQueueRPC() {
return new Queue("BulkSolve_GeneralreplyQueue");
}
#Bean
public Queue requestQueueRPC() {
return new Queue("BulkSolve_GeneralrequestQueue");
}
/*below are the newly added method for two other queues*/
#Bean
public Queue summaryreplyQueueRPC() {
return new Queue("BulkSolve_SummaryreplyQueue");
}
#Bean
public Queue summaryrequestQueueRPC() {
return new Queue("BulkSolve_SummaryrequestQueue");
}
#Bean
public SimpleMessageListenerContainer rpcGeneralReplyMessageListenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
simpleMessageListenerContainer.setQueues(replyQueueRPC());
simpleMessageListenerContainer.setTaskExecutor(taskExecutor());
//simpleMessageListenerContainer.setMessageListener(listenerAdapter1);
return simpleMessageListenerContainer;
}
#Bean
public SimpleMessageListenerContainer rpcSummaryReplyMessageListenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
simpleMessageListenerContainer.setQueues(summaryreplyQueueRPC());
//simpleMessageListenerContainer.setMessageListener(listenerAdapter2);
simpleMessageListenerContainer.setTaskExecutor(taskExecutor());
return simpleMessageListenerContainer;
}
/* #Bean
#Qualifier("listenerAdapter1")
MessageListenerAdapter listenerAdapter1(SubscriberGeneralQueue1 generalReceiver) {
return new MessageListenerAdapter(generalReceiver, "receivegeneralQueueMessage");
}*/
/* #Bean
#Qualifier("listenerAdapter2")
MessageListenerAdapter listenerAdapter2(SubscriberSummaryQueue1 summaryReceiver) {
return new MessageListenerAdapter(summaryReceiver, "receivesummaryQueueMessage");
}*/
#Bean
public RequestPojo requestPojo(){
return new RequestPojo();
}
/* #Bean
#Qualifier("asyncGeneralRabbitTemplate")
public AsyncRabbitTemplate asyncGeneralRabbitTemplate(ConnectionFactory connectionFactory) {
AsyncRabbitTemplate asyncGeneralRabbitTemplate = new AsyncRabbitTemplate(rabbitTemplate(connectionFactory),
rpcGeneralReplyMessageListenerContainer(connectionFactory),
"spring-boot-rabbitmq-BulkSolve.async_BulkSolve_Msg" + "/" + "BulkSolve_GeneralreplyQueue");
AsyncRabbitTemplate at = new AsyncRabbitTemplate(connectionFactory, "spring-boot-rabbitmq-examples.async_rpc", "rpc_request", "replyQueueRPC","replyQueueRPC");
return asyncGeneralRabbitTemplate;
}
template defined for other 2 queues
#Bean
#Qualifier("asyncSummaryRabbitTemplate")
public AsyncRabbitTemplate asyncSummaryRabbitTemplate(ConnectionFactory connectionFactory) {
AsyncRabbitTemplate asyncSummaryRabbitTemplate = new AsyncRabbitTemplate(rabbitTemplate(connectionFactory),
rpcSummaryReplyMessageListenerContainer(connectionFactory),
"spring-boot-rabbitmq-BulkSolve_summary.async_BulkSolve_Msg" + "/" + "BulkSolve_SummaryreplyQueue");
AsyncRabbitTemplate at = new AsyncRabbitTemplate(connectionFactory, "spring-boot-rabbitmq-examples.async_rpc", "rpc_request", "replyQueueRPC","replyQueueRPC");
return asyncSummaryRabbitTemplate;
}*/
#Bean
public DirectExchange directExchange() {
return new DirectExchange("spring-boot-rabbitmq-BulkSolve.async_BulkSolve_Msg");
}
//Added new exchange
#Bean
public DirectExchange directExchange1() {
return new DirectExchange("spring-boot-rabbitmq-BulkSolve_summary.async_BulkSolve_Msg");
}
#Bean
public List<Binding> bindings() {
return Arrays.asList(
BindingBuilder.bind(requestQueueRPC()).to(directExchange()).with("BulkSolve_GeneralrequestQueue"),
BindingBuilder.bind(replyQueueRPC()).to(directExchange()).with("BulkSolve_GeneralreplyQueue"),
BindingBuilder.bind(summaryrequestQueueRPC()).to(directExchange1()).with("BulkSolve_SummaryrequestQueue"),
BindingBuilder.bind(summaryreplyQueueRPC()).to(directExchange1()).with("BulkSolve_SummaryreplyQueue")
);
}
}
//Below is my Reader class
#Component
public class Reader extends AmqpItemReader<List<RequestPojo>>{
#Autowired
#Qualifier("rabbitTemplate")
private RabbitTemplate rabbitTemplate;
public static boolean batchstatus;
private List<RequestPojo> reqList = new ArrayList<RequestPojo>();
/* #Autowired
private SimpleMessageListenerContainer messagelistener;*/
public Reader(AmqpTemplate rabbitTemplate) {
super(rabbitTemplate);
// TODO Auto-generated constructor stub
}
List<RequestPojo> msgList = new ArrayList<RequestPojo>();
#Override
#SuppressWarnings("unchecked")
public List<RequestPojo> read() {
if(!batchstatus){
RequestPojo msg=(RequestPojo)rabbitTemplate.receiveAndConvert("BulkSolve_GeneralrequestQueue" );
//rabbitTemplate.receiveandco
//return (List<RequestPojo>) rabbitTemplate.receive();
System.out.println("I am inside Reader" + msg);
msgList.add(msg);
//Object result = rabbitTemplate.receiveAndConvert();
batchstatus=true;
return msgList;
}
return null;
}
}
Below is my Trigger point code which trigger Job when message is there in queue.
#EnableRabbit
public class Eventscheduler {
#Autowired
Job csvJob;
#Autowired
private JobLauncher jobLauncher;
//#Scheduled(cron="0 */2 * ? * *")
#RabbitListener(queues ="BulkSolve_GeneralrequestQueue")
public void trigger(){
Reader.batchstatus=false;
Map<String,JobParameter> maps= new HashMap<String,JobParameter>();
maps.put("time", new JobParameter(System.currentTimeMillis()));
JobParameters jobParameters = new JobParameters(maps);
JobExecution execution=null;
try {
//JobLauncher jobLauncher = new JobLauncher();
execution=jobLauncher.run(csvJob, jobParameters);
} catch (JobExecutionAlreadyRunningException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobRestartException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobParametersInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("JOB Executed:" + execution.getStatus());
}
}
Thanks.
As you noticed there is slight difference between subscribeToRequestQueue(#Payload RequestPojo sampleRequestMessage) and rabbitTemplate.receiveAndConvert("BulkSolve_GeneralrequestQueue"). And right, that one is exactly that #Payload RequestPojo which is missed in the raw receiveAndConvert(). Therefore when this method is performed there is no target type to consult for the expected conversion. This way we just fallback to whatever we have in the incoming message. In your case that is a __TypeId__ header with the source type from the producer com.comcast.FileProcess.Pojo.CSVPojo.
If you really would like to the enforce the conversion on the consumer side into the RequestPojo, you need to consider to use an overloaded receiveAndConvert variant:
/**
* Receive a message if there is one from a specific queue and convert it to a Java
* object. Returns immediately, possibly with a null value. Requires a
* {#link org.springframework.amqp.support.converter.SmartMessageConverter}.
*
* #param queueName the name of the queue to poll
* #param type the type to convert to.
* #param <T> the type.
* #return a message or null if there is none waiting
* #throws AmqpException if there is a problem
* #since 2.0
*/
<T> T receiveAndConvert(String queueName, ParameterizedTypeReference<T> type) throws AmqpException;

How can I add information to a JAXBElement SOAP request?

I have a class generated with JAXB2 form a WSDL. The elements defined in the WSDL are NOT declared as XmlRootElement.
#Service
public class ProblemService extends WebServiceGatewaySupport {
public ProblemResponse addProblem(final Problem problem, final String aNumber) {
final String namespacePrefix = "soapenv";
final String action = "Problem";
final ObjectFactory factory = new ObjectFactory();
final JAXBElement<Problem> request = factory.createProblem(problem);
try {
StringResult result = new StringResult();
getMarshaller().marshal(request, result);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace(System.err);
}
final WebServiceTemplate wst = this.getWebServiceTemplate();
#SuppressWarnings("unchecked")
final JAXBElement<ProblemResponse> response = (JAXBElement<ProblemResponse>) wst
.marshalSendAndReceive(abcConfiguration.getEndpoint(), request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(final WebServiceMessage message) {
try {
prepareSoapHeader(message, namespacePrefix, action);
final SaajSoapMessage ssMessage = (SaajSoapMessage) message;
final SOAPEnvelope envelope = ssMessage.getSaajMessage().getSOAPPart().getEnvelope();
envelope.getBody().setPrefix(namespacePrefix);
final NodeList nl = ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().getChildNodes();
ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody().removeChild(nl.item(0));
final SOAPElement se = ssMessage.getSaajMessage().getSOAPPart().getEnvelope().getBody()
.addBodyElement(new QName(action));
se.setPrefix(NAMESPACE_PREFIX_V2);
addUserAuthentification(se);
try {
StringResult result = new StringResult();
getAbcConfiguration().marshaller().marshal(request, result);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace(System.err);
}
System.out.println();
} catch (SoapFaultClientException e) {
logger.error("Error on client side during marshalling of the SOAP request for {}.", action, e);
} catch (SOAPException e) {
logger.error("Error during marshalling of the SOAP request for {}.", action, e);
}
}
});
return response.getValue();
}
}
The generated StringResult looks quiet good but I need to replace some parts in the resulting XML (for instance the prefix) and I need to add some stuff into the SoapBody which are not part of the base class (Problem) before sending the SOAP request to the remote service.
Furthermore I want to modify the header part of the envelope...
How can I achieve this? My application is a SpringBoot application and in the configuration class being used in my service the un-/marshaller are defined this way:
#Bean
public Jaxb2Marshaller marshaller() {
final Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
//setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setContextPath(contextPath);
//marshaller.afterPropertiesSet();
marshaller.setMarshallerProperties(new HashMap<String, Object>() {{
put(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, true);
}});
return marshaller;
}
#Bean
public ProblemService problemService(final Jaxb2Marshaller marshaller) throws Exception {
final ProblemService client = new ProblemService();
client.setDefaultUri(this.endpoint);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
final HttpsUrlConnectionMessageSender msgSender = new HttpsUrlConnectionMessageSender();
client.setMessageSenders(new WebServiceMessageSender[] {msgSender, httpComponentsMessageSender()});
//client.setMessageSender(msgSender);
return client;
}
With this little piece of code I was able to add information to the SoapBody as demanded:
try {
getKpmConfiguration().marshaller().marshal(request, ssMessage.getPayloadResult());
ssMessage. writeTo(System.out);
} catch (/*JAXB*/Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

Resources