I have a MessageDriven bean which consume messages and send it to somewhere else by REST API. Like this:
#MessageDriven(name = "n", activationConfig = {...})
public class SubmitMessageBean implements MessageListener {
#Resource
private MessageDrivenContext context;
#Override
public void onMessage(final Message message) {
try {
// Submit data to somewhere with REST API
} catch (IOException ex) {
this.context.getRollbackOnly();
} catch (JMSException ex) {
e.printStackTrace();
}
}
}
When error happens I want to retry sending in a minute. It there anyway to do that?
OK, I figured it out. You can also easily add delay to give the remote system more time to resolve the error.
#MessageDriven(name = "n", activationConfig = {...})
public class SubmitMessageBean implements MessageListener {
#Inject
JMSContext context;
#Resource(mappedName = "...")
Queue queue;
#Override
public void onMessage(Message message) {
Integer retry = 0;
try {
// track how many time we tried before
retry = message.getIntProperty("retry");
// Submit data to somewhere with REST API
} catch (IOException ex) {
// Put it back in queue again
// You can limit number of retry by keeping retry variable
if (retry < 5) {
JMSProducer producer = this.context.createProducer();
producer.setProperty("retry", r);
// Add some delay to start it again after a minute
producer.setDeliveryDelay(60000);
// Send it again with send()
}
} catch (JMSException ex) {
e.printStackTrace();
}
}
}
Of course you need to fill {...} with your own right values.
Related
I have an application that queues and deques messages from Oracle AQ using the JMS interface. When the application is running items get queued and dequeued and I can see queued items in the queue table. However, one the application shuts down the queue table is cleared and the application cannot access the previously queued items. Any idea what might cause that behavior?
The Oracle AQ is created using this code:
BEGIN
dbms_aqadm.create_queue_table(
queue_table => 'schema.my_queuetable',
sort_list =>'priority,enq_time',
comment => 'Queue table to hold my data',
multiple_consumers => FALSE, -- THis is necessary so that a message is only processed by a single consumer
queue_payload_type => 'SYS.AQ$_JMS_OBJECT_MESSAGE',
compatible => '10.0.0',
storage_clause => 'TABLESPACE LGQUEUE_IRM01');
END;
/
BEGIN
dbms_aqadm.create_queue (
queue_name => 'schema.my_queue',
queue_table => 'schema.my_queuetable');
END;
/
BEGIN
dbms_aqadm.start_queue(queue_name=>'schema.my_queue');
END;
/
I also have a Java class for connecting to the queue, queueing items and processing dequeued items like this:
public class MyOperationsQueueImpl implements MyOperationsQueue {
private static final Log LOGGER = LogFactory.getLog(MyOperationsQueueImpl.class);
private final QueueConnection queueConnection;
private final QueueSession producerQueueSession;
private final QueueSession consumerQueueSession;
private final String queueName;
private final QueueSender queueSender;
private final QueueReceiver queueReceiver;
private MyOperationsQueue.MyOperationEventReceiver eventReceiver;
public MyOperationsQueueImpl(DBUtils dbUtils, String queueName) throws MyException {
this.eventReceiver = null;
this.queueName = queueName;
try {
DataSource ds = dbUtils.getDataSource();
QueueConnectionFactory connectionFactory = AQjmsFactory.getQueueConnectionFactory(ds);
this.queueConnection = connectionFactory.createQueueConnection();
// We create separate producer and consumer sessions because that is what is recommended by the docs
// See: https://docs.oracle.com/javaee/6/api/javax/jms/Session.html
this.producerQueueSession = this.queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
this.consumerQueueSession = this.queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
this.queueSender = this.producerQueueSession.createSender(this.producerQueueSession.createQueue(this.queueName));
this.queueReceiver = this.consumerQueueSession.createReceiver(this.consumerQueueSession.createQueue(this.queueName));
this.queueConnection.start();
} catch (JMSException| NamingException exception) {
throw new MyOperationException("Failed to create MyOperationsQueue", exception);
}
}
#Override
protected void finalize() throws Throwable {
this.queueReceiver.close();
this.queueSender.close();
this.consumerQueueSession.close();
this.producerQueueSession.close();
this.queueConnection.close();
super.finalize();
}
#Override
public void submitMyOperation(MyOperationParameters myParameters) throws MyOperationException {
try {
ObjectMessage message = this.producerQueueSession.createObjectMessage(myParameters);
this.queueSender.send(message);
synchronized (this) {
if(this.eventReceiver != null) {
this.eventReceiver.onOperationSubmitted(message.getJMSMessageID(), myParameters);
}
}
} catch (JMSException exc) {
throw new MyOperationException("Failed to submit my operation", exc);
}
}
#Override
public void setMyOperationEventReceiver(MyOperationEventReceiver operationReceiver) throws MyOperationException {
LOGGER.debug("Setting my operation event receiver");
synchronized (this) {
if(this.eventReceiver != null) {
throw new IllegalStateException("Cannot set an operation event receiver if it is already set");
}
this.eventReceiver = operationReceiver;
try {
this.queueReceiver.setMessageListener(message -> {
LOGGER.debug("New message received from queue receiver");
try {
ObjectMessage objectMessage = (ObjectMessage) message;
eventReceiver.onOperationReady(message.getJMSMessageID(), (MyOperationParameters) objectMessage.getObject());
} catch (Exception exception) {
try {
eventReceiver.onOperationRetrievalFailed(message.getJMSMessageID(), exception);
} catch (JMSException innerException) {
LOGGER.error("Failed to get message ID for JMS Message: "+message, innerException);
}
}
});
} catch (JMSException exc) {
throw new MyOperationException("Failed to set My message listener", exc);
}
}
}
}
I'm trying to aply Redisson features for my project as message broker and I have a question. Is it possible to push Redisson to precceding recieved messages asynchronously? I have created a small example, sent 4 messages from different URL's. I expected, that Redisson proceeded them asynchronously, but it did it one by one.
Here the implementation:
public class RedisListenerServiceImpl implements MessageListener<String> {
private static final Logger log = LoggerFactory.getLogger(RedisListenerServiceImpl.class);
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public void onMessage(CharSequence channel, String stringMsg) {
log.info("Message received: {}", stringMsg);
MessageDto msg;
try {
msg = objectMapper.readValue(stringMsg, MessageDto.class);
} catch (final IOException e) {
log.error("Unable to deserialize message: {}", e.getMessage(), e);
return;
}
try {
//Do my stuff
} catch (Exception e) {
log.error("Unable to get service from factory: {}", e.getMessage(), e);
}
}
}
And the config:
#Configuration
public class RedisListenerConfig {
#Autowired
public RedisListenerConfig(RedissonClient redisClient,
MessageListener redisListenerService,
#Value("${redis.sub.key}") String redisSubKey) {
RTopic subscribeTopic = redisClient.getTopic(redisSubKey);
subscribeTopic.addListenerAsync(String.class, redisListenerService);
}
}
It's expected behavior. If you want your messages to be processed concurrently when the Listener onMessage() method is triggered, just use a thread pool to process it.
Since Redisson doesn't know how many threads you want to consume the triggered events, it leaves the implementation detail to you.
public class RedisListenerServiceImpl implements MessageListener<String> {
private static final Logger log = LoggerFactory.getLogger(RedisListenerServiceImpl.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
#Override
public void onMessage(CharSequence channel, String stringMsg) {
log.info("Message received: {}", stringMsg);
MessageDto msg;
try {
msg = objectMapper.readValue(stringMsg, MessageDto.class);
executorService.submit(()->{
System.out.println("do something with message: "+msg);
});
} catch (final IOException e) {
log.error("Unable to deserialize message: {}", e.getMessage(), e);
return;
}
try {
//Do my stuff
} catch (Exception e) {
log.error("Unable to get service from factory: {}", e.getMessage(), e);
}
}
I have implemented a Camel Service , but when i try to shutdown my route , it's impossible .... I have to kill the process. What i have missed ?
First I create a class which implements camel.Service :
#Service("myService")
public class MyService implements org.apache.camel.Service {
...
public WebSocket ws = null;
private Boolean isRunning=true;
public void mainCall() {
try {
.....
ws = connect();
while(isRunning) {
.....
}
} catch (IOException e) {
e.printStackTrace();
} catch (WebSocketException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void start() throws Exception {
isRunning = true;
mainCall();
}
#Override
public void stop() throws Exception {
isRunning = false;
ws.disconnect();
}
I add my service on my Camel context like below :
#Autowired
private MyService myService;
#Autowired
private CamelContext context;
#PostConstruct
public void setupCamelContext() throws Exception {
....
context.addService(myService);
}
At the end i start my route :
from("timer://runOnce?repeatCount=1&delay=5000")
.serviceCall("myService");
use HAWTIO for CAMEL if you want to stop/start routes manually.
Here's the link: http://hawtio.github.io/hawtio/plugins/camel/
I resolve my problem by splitting my Service in two :
One who implement org.apache.camel.Service
Second who implement start fucntion but with an #Async anotation
The main problem in my case was my infinite loop block stuck the start function, Asunc method resolve the problem
I have a Java service which is getting messages from an Oracle Advanced Queue. I can create the connection and listen and get messages OK. I can see that you can stop and start listening for messages, so I have implemented controls for that. However, I would like to be able to report on the current status of the listener. I can see if it's there, but how can I tell if it's stopped or started?
I have a container class along the lines of (Listener is my own class (implementing both MessageListener and ExceptionListener) which actually does something with the message)
public class QueueContainer {
private static final String QUEUE_NAME = "foo";
private final Connection dbConnection;
private final QueueConnection queueConnection;
private final QueueSession queueSession;
private final Queue queue;
private final MessageConsumer consumer;
private final Listener listener;
public QueueContainer(final Connection dbConnection ) {
try {
this.dbConnection = dbConnection;
queueConnection = AQjmsQueueConnectionFactory.createQueueConnection(dbConnection);
queueSession = queueConnection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
queue = ((AQjmsSession) queueSession).getQueue(context.getEnvironment(), QUEUE_NAME);
consumer = queueSession.createConsumer(queue);
listener = new Listener(QUEUE_NAME);
consumer.setMessageListener(listener);
queueConnection.setExceptionListener(listener);
} catch (JMSException | SQLException e) {
throw new RunTimeException("Queue Exception", e);
}
}
public void startListening() {
try {
queueConnection.start();
} catch (JMSException e) {
throw new RunTimeException("Failed to start listening to queue", e);
}
}
public void stopListening() {
try {
queueConnection.stop();
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
public void close() {
if (queueConnection != null) {
try {
queueConnection.close();
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
}
public boolean isRunning() {
try {
// This doesn't work - I can't distinguish between started and stopped
return queueConnection.getClientID() != null;
} catch (JMSException e) {
LOGGER.warn("Failed to get queue client ID", e);
return false;
}
}
I can't see what to put in isRunning that could distinguish between a stopped and started listener
The JMS API assumes you know yourself what you did. So why not add a boolean flag and keep track of this ?
private volatile boolean isListening = false;
...
public void startListening() {
try {
queueConnection.start();
isListening = true;
} catch (JMSException e) {
throw new RunTimeException("Failed to start listening to queue", e);
}
}
public void stopListening() {
try {
queueConnection.stop();
isListening = false;
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
public void close() {
if (queueConnection != null) {
try {
queueConnection.close();
isListening = false;
} catch (JMSException e) {
throw new RunTimeException("Failed to stop listening to queue", e);
}
}
}
public boolean isRunning() {
return isListening;
}
There is no JMS API call to determine whether or not a javax.jms.Connection is started.
To be clear, the queue itself is not the entity that is started or stopped. The connection is started or stopped.
You may be able to get this information from the Oracle Advanced Queue implementation object, but I'm not familiar with that implementation so I can't say. Obviously any solution using an implementation object rather than the standard API will not be portable.
I have two different types of messages and I am handling these message by two different listener.
I am adding listener in one class
public DrawingXmppServices(SurfaceViewCanvas surfaceView) {
chat = chatManager.createChat(BUDDY, new MyChatListener());
chat.addMessageListener(new MyChatListener());
receiver = surfaceView;
}
public void sendDrawingData(String drawingData, String buddyId) {
try {
chat.sendMessage(drawingData);
} catch (XMPPException e) {
e.printStackTrace();
}
}
And On other class
public InstantMessageXmpp(CanvasActivity canvasActivity) {
receiver = (MessageReceiver) canvasActivity;
chat = chatManager.createChat(BUDDY, this);
chat.addMessageListener(this);
}
public void sendIM(String message, String buddyID) {
try {
chat.sendMessage(message);
} catch (XMPPException e) {
e.printStackTrace();
}
}
When I call InstantMessageXmpp to send its packet but the listener of DrawingXmppServices is called. What mistake is I am doing?
Please guide me. Any suggestion can help me. Thank you