I have a code that reads from the IBM MQ queue manager but I want to be read from IBM MQ without removing the message from the queue, only after I send acknowledge to IBM MQ I want to remove the message
this is my IBM reader code :
public class IBMReaderStub extends AbstractReader {
private JMSContext context = null;
JMSConsumer consumer;
Destination destination;
public IBMReaderStub(String queueName) {
this(queueName, new IBMListener());
}
public IBMReaderStub(String queueName, IBMListener onMessage) {
super(ConfigurationManager.getString(HOST), ConfigurationManager.getInt(PORT, DEFAULT_IBM_PORT), queueName, new QueueWithThreadPool(), onMessage);
}
#Override
protected void initializeConsumer() {
try {
JmsConnectionFactory jmsConnectionFactory = createJmsConnectionFactory();
context = jmsConnectionFactory.createContext();
destination = context.createQueue("queue:///" + getQueueName()); // Set the producer and consumer destination to be the same... not true in general
consumer = context.createConsumer(destination);
} catch (Exception e) {
System.out.println(e);
}
listen();
}
#Override
public void listen() {
consumer.setMessageListener(getOnMessage());
}
private JmsConnectionFactory createJmsConnectionFactory() throws Exception {
JmsFactoryFactory jmsFactory = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory jmsConnectionFactory = jmsFactory.createConnectionFactory();
jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_HOST_NAME, this.getHost());
jmsConnectionFactory.setIntProperty(WMQConstants.WMQ_PORT, getPort());
jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_CHANNEL, ConfigurationManager.getString(CHANNEL_NAME));
jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, ConfigurationManager.getString(QUEUE_MANAGER_NAME));
jmsConnectionFactory.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, ConfigurationManager.getString(APPLICATION_NAME));
jmsConnectionFactory.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
return jmsConnectionFactory;
}
public static void main(String[] args) {
try {
IBMReaderStub reader = new IBMReaderStub("hey");
IBMReaderStub reader2 = new IBMReaderStub("hey");
reader.listen();
reader2.listen();
} catch (Exception e) {
System.out.println(e);
}
}
}
IBM MQ provides transactional access to messages, so you need to create a transacted session, and then you can commit or roll back message gets or puts as needed.
https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.1.0/com.ibm.mq.dev.doc/q032210_.html
https://www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.dev.doc/q032220_.htm
Related
I am trying to find a bug is some RabbitMQ client code that was developed six or seven years ago. The code was modified to allow for delayed messages. It seems that connections are created to the RabbitMQ server and then never destroyed. Each exists in a separate thread so I end up with 1000's of threads. I am sure the problem is very obvious / simple - but I am having trouble seeing it. I have been looking at the exchangeDeclare method (the commented out version is from the original code which seemed to work), but I have been unable to find the default values for autoDelete and durable which are being set in the modified code. The method below in within a Spring service class. Any help, advice, guidance and pointing out huge obvious errors appreciated!
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "fanout");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-delay", 10000); //delay in miliseconds i.e 10secs
AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
Connection connection = null;
Channel channel = null;
try {
connection = myConnection.getConnection();
}
catch(Exception e) {
log.error("AMQP send method Exception. Unable to get connection.");
e.printStackTrace();
return;
}
try {
if (connection != null) {
log.debug(" [CORE: AMQP] Sending message with key {} : {}",routingKey, message);
channel = connection.createChannel();
// channel.exchangeDeclare(exchange, exchangeType);
channel.exchangeDeclare(exchange, "x-delayed-message", true, false, args);
// channel.basicPublish(exchange, routingKey, null, message.getBytes());
channel.basicPublish(exchange, routingKey, props.build(), message.getBytes());
}
else {
log.error("Total AMQP melt down. This should never happen!");
}
}
catch(Exception e) {
log.error("AMQP send method Exception. Unable to get send.");
e.printStackTrace();
}
finally {
channel.close();
}
}
This is the connection class
#Service
public class PersistentConnection {
private static final Logger log = LoggerFactory.getLogger(PersistentConnection.class);
private static Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
log.error("Unable to close AMQP Connection.");
e.printStackTrace();
}
}
public Connection getConnection( ) {
if (myConnection == null) {
start();
}
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String user = applicationConfiguration.getAMQPUser();
String password = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
log.error("Unable to initialise AMQP Connection.");
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
log.warn("Message Server has blocked. It may be resource limitted.");
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
log.warn("Message server is unblocked.");
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
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);
}
}
}
}
Please, this is the first time that I write a flink job and I need help. The goal of the job is to calculate the average of different fields of an avro object. The avro schema that I use to parse json messages that come from an ActiveMQ queue is the following:
[
{
"type":"record",
"name":"SensorDataAnnotation",
"namespace":"zzz",
"fields":[
{"name":"meas","type":["null","string"]},
{"name":"prefix","type":["null","string"]}
]
},
{
"namespace":"zzz",
"name":"SensorDataList",
"type":"record",
"fields":[
{"name":"SensorDataListContainer",
"type":{"name":"SensorDataListContainer","type":"array","namespace":"zzz",
"items":{"type":"record","name":"SensorData","namespace":"zzz",
"fields":[
{"name":"prkey","type":"int"},
{"name":"prkeyannotation","type":["null","SensorDataAnnotation"]},
{"name":"value1","type":["null","double"]},
{"name":"value1annotation","type":["null","SensorDataAnnotation"]},
{"name":"value2","type":["null","double"]},
{"name":"value2annotation","type":["null","SensorDataAnnotation"]},
{"name":"value3","type":["null","int"]},
{"name":"value3annotation","type":["null","SensorDataAnnotation"]}
}
]
This is the flink job that I tried to write:
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<SensorData> messages = env.addSource(new StreamSource());
DataStream<Double> counts = messages
.map(new MapFunction<SensorData, Double>() {
#Override
public Double map(SensorData arg0) throws Exception {
return arg0.getValue1();
}
})
.timeWindowAll(Time.seconds(10), Time.seconds(5))
.apply(new Avg());
counts.print();
env.execute("ActiveMQ Streaming Job");
with the StreamSource and Avg classes:
StreamSource
class StreamSource extends RichSourceFunction<SensorData> {
private static final long serialVersionUID = 1L;
private static final Logger LOG = Logger.getLogger(StreamSource.class);
private transient volatile boolean running;
private transient MessageConsumer consumer;
private transient Connection connection;
private void init() throws JMSException {
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
// Create a Connection
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Topic or Queue)
Destination destination = session.createQueue("input");
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session.createConsumer(destination);
}
#Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
running = true;
init();
}
#Override
public void run(SourceContext<SensorData> ctx) {
// this source never completes
while (running) {
try {
// Wait for a message
Message message = consumer.receive(1000);
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
String text = textMessage.getText();
try {
byte[] avroDesObj = jsonToAvro(text, SensorData.SCHEMA$.toString());
DatumReader<SensorData> reader = new SpecificDatumReader<SensorData>(SensorData.SCHEMA$);
Decoder decoder = DecoderFactory.get().binaryDecoder(avroDesObj, null);
SensorData data = reader.read(null, decoder);
ctx.collect(data);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
LOG.error("Don't know what to do .. or no message");
}
} catch (JMSException e) {
LOG.error(e.getLocalizedMessage());
running = false;
}
}
try {
close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
#Override
public void cancel() {
running = false;
}
#Override
public void close() throws Exception {
LOG.info("Closing");
try {
connection.close();
} catch (JMSException e) {
throw new RuntimeException("Error while closing ActiveMQ connection ", e);
}
}
}
Avg
public static class Avg implements AllWindowFunction<Double,Double, TimeWindow> {
#Override
public void apply(TimeWindow window, Iterable<Double> values, Collector<Double> out) throws Exception {
double sum = 0.0;
int count = 0;
for(Double value : values) {
sum += value.doubleValue();
count++;
}
Double avg = values.iterator().next();
avg = sum / count;
out.collect(avg);
}
}
When I launch the exported jar of this job in the flink dashboard, it does not start and I don't know what I am doing wrong.
Thank You.
My application needs to monitor multiple JMS queue's.
How should this be done?
Start 2 threads?
Can 2 queues be monitored at the same time?
Sample code for one queue:
...
queue1 = session.createQueue("queue-1");
consumer = session.createConsumer(queue1);
connection.start();
while (true) {
Message m = consumer.receive(10000);
if (m == null) {
...nothing...
} else {
...do something with the message...
}
}
...
How should I watch queue-1 and queue-2?
You could use quartz scheduler Quartz Scheduler for this. Implement one (or more) quartz job(s) like this:
public class MessageReaderJob1 implements Job {
private QueueReader1 qr;
#Override
public synchronized void execute(JobExecutionContext arg0) throws JobExecutionException {
qr = QueueReader1.getInstance();
try {
Message message = qr.getConsumer().receiveNoWait();
....
}
}
Then you will need a scheduler that you will run from your application (main method or servlet), note that you can implement a different trigger for the second queue also:
public class TestCasesSchedule {
private Scheduler scheduler;
public void createSchedule() {
JobDetail job1 = JobBuilder.newJob(MessageReaderJob1.class)
.withIdentity("jobname", Scheduler.DEFAULT_GROUP)
.build();
JobDetail job2 = JobBuilder.newJob(MessageReaderJob2.class)
.withIdentity("jobname", Scheduler.DEFAULT_GROUP)
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("minutestrigger", "triggergroup")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever())
.build();
try {
SchedulerFactory sf = new StdSchedulerFactory();
scheduler = sf.getScheduler();
scheduler.start();
scheduler.scheduleJob(job1, trigger);
scheduler.scheduleJob(job2, trigger);
} catch (SchedulerException se) {
System.err.println(se.getMessage())
}
}
QueueReader for one of your queue's would look like this:
public class QueueReader1 {
private MessageConsumer consumer = null;
private Context jndiContext = null;
private QueueConnectionFactory queueConnectionFactory = null;
private QueueConnection queueConnection = null;
private QueueSession queueSession = null;
private Queue queue = null;
private static final QueueReader instance = new QueueReader();
public synchronized static QueueReader getInstance() {
return instance;
}
private QueueReader() {
/*
* Create a JNDI API InitialContext object if none exists
* yet.
*/
try {
jndiContext = new InitialContext();
} catch (NamingException e) {
System.err.println(e.getMessage())
System.exit(1);
}
/*
* Look up connection factory and queue. If either does
* not exist, exit.
*/
try {
queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("connection_factory_name");
queue = (Queue) jndiContext.lookup("queue_name");
queueConnection =
queueConnectionFactory.createQueueConnection();
queueSession =
queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
consumer = queueSession.createConsumer(queue);
queueConnection.start();
} catch (JMSException ex) {
System.err.println(ex.getMessage());
} catch (NamingException e) {
System.err.println(e.getMessage());
}
}
}
This is my solution. It works. Any extra advise is welcome!
Main class:
public class Notifier {
public static void main(String[] args) throws Exception {
// Start a thread for each JMQ queue to monitor.
DestinationThread destination1 = new DestinationThread("queue1");
DestinationThread destination2 = new DestinationThread("queue2");
destination1.start();
destination2.start();
}
}
The Thread:
public class DestinationThread extends Thread {
private String destinationQueue;
private static ActiveMQConnectionFactory connectionFactory = null;
private static Connection connection = null;
private static Session session = null;
private static Destination destination = null;
private static MessageConsumer consumer = null;
public DestinationThread(String destinationQueue) {
this.destinationQueue = destinationQueue;
}
#Override
public void run() {
try {
initializeThread(destinationQueue);
startThread(destinationQueue);
} catch (Exception e) {
//TODO
}
}
private void initializeThread(String destinationQueue) {
boolean connectionMade = false;
while (!connectionMade) {
try {
connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue(destinationQueue);
consumer = session.createConsumer(destination);
connectionMade = true;
} catch (JMSException e) {
//TODO
try {
Thread.sleep(30000);
} catch (InterruptedException ie) {
}
}
}
}
private void startThreadOther(String destinationQueue) throws Exception {
while (true) {
try {
Message message = consumer.receive(300000);
if (message == null) {
//No message received for 5 minutes - Re-initializing the connection
initializeThread(destinationQueue);
} else if (message instanceof TextMessage) {
if (destinationQueue.equals("queue1") {
//Message received from queue1 - do something with it
} else if (destinationQueue.equals("queue2") {
//Message received from queue2 - do something with it
} else {
//nothing
}
} else {
//nothing
}
} catch (Exception e) {
//TODO
}
}
}
}
I have a Glassfish 3.1.2 server running on a remote machine (JDK 1.6.0_30). The following code is the stand-alone client running in a Java SE environment, connecting to the JMS using a JNDI lookup. The client is publisher and subscriber at the same time.
I created the JMS connection pool and topic as follows:
./asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/TopicConnectionFactory
./asadmin create-jms-resource --restype javax.jms.Topic jms/TopicUpdate
I start two instances of this client. The messages seem to be delivered - no errors - but the messages do not arrive at the subscribers ...
What I am doing wrong ?
Any help appreciated - many thanks in advance!
public class JMS implements MessageListener {
private TopicConnectionFactory factory;
private TopicConnection connection;
private Topic topic;
private void subscribe() {
try {
System.setProperty("org.omg.CORBA.ORBInitialHost", "192.168.1.6");
System.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
InitialContext ctx = new InitialContext();
factory = (TopicConnectionFactory)ctx.lookup("jms/TopicConnectionFactory");
topic = (Topic)ctx.lookup("jms/TopicUpdate");
connection = factory.createTopicConnection();
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber = session.createSubscriber(topic);
subscriber.setMessageListener(this);
connection.start();
while(true) {
Thread.sleep(5000);
sendMessage();
}
} catch (InterruptedException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
} catch (NamingException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
} catch (JMSException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void sendMessage() {
try {
TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
TopicPublisher publisher = session.createPublisher(topic);
TextMessage message = session.createTextMessage();
message.setText("Message from client.");
publisher.send(message);
session.close();
System.out.println("Message sent.");
} catch (JMSException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void onMessage(Message msg) {
System.out.println("Message received.");
}
public JMS() {
subscribe();
}
public static void main(String[] args) {
new JMS();
}
}
When you use true as the first argument when creating a session, the acknowledge mode is ignored and you're assumed to be transacted. try it with the first argument as false.
Just so it's clear, modify this line of code:
TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
to be :
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
In your send message method.
It's good idea to have publisher and subscriber different.I
Here is code how to subscribe using Spring JMS template.
public class MsgReader implements
SessionAwareMessageListener<Message> {
#Override
public void onMessage(Message message, Session session) throws JMSException {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TextMessage");
}
}
}
Spring Bean file.
Finally load beans.
public class SpringJMSTest {
/**
* #param args
*/
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext(new String[]{"/resource/consumerBean.xml"});
}
}
Now you will start receiving messages in console.