org.jboss.aerogear.unifiedpush.message.util.JmsClient Maven / Gradle / Ivy
/**
* JBoss, Home of Professional Open Source
* Copyright Red Hat, Inc., and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.aerogear.unifiedpush.message.util;
import org.jboss.aerogear.unifiedpush.message.exception.MessageDeliveryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Utility class for sending and receiving JMS messages
*/
@Stateless
public class JmsClient {
private static final Logger logger = LoggerFactory.getLogger(JmsClient.class);
@Resource(mappedName = "java:/ConnectionFactory")
private ConnectionFactory connectionFactory;
@Resource(mappedName = "java:/JmsXA")
private ConnectionFactory xaConnectionFactory;
/**
* Creates {@link JmsSender} utility that allows to specify how should be message sent and into which destination
*
* @param message the message to be send out
* @return the new sender object
*/
public JmsSender send(Serializable message) {
return new JmsSender(message);
}
/**
* Creates {@link JmsReceiver} utility that allows to specify how should be message received and from which destination
*
* @return the new receiver object
*/
public JmsReceiver receive() {
return new JmsReceiver();
}
/**
* Utility that allows to specify how should be message sent and into which destination
*/
public class JmsReceiver {
private boolean transacted;
private String selector;
private Wait wait = new WaitIndefinitely();
private int acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
private boolean autoClose = true;
private Connection connection;
public JmsReceiver() {
}
/**
* Receives the message in transaction (i.e. use JmsXA connection factory).
*
* @return this receiver object
*/
public JmsReceiver inTransaction() {
this.transacted = true;
return this;
}
/**
* Specifies a selector used to query messages from a destination.
* The selector can be formatted with arguments as in {@link String#format(String, Object...)}.
*
* @param selector specifies to query messages from a destination.
* @param args argument for the selector
* @return this receiver object
*
* @see String#format(String, Object...)
*/
public JmsReceiver withSelector(String selector, Object... args) {
this.selector = String.format(selector, args);
return this;
}
/**
* Don't block and returns the message what is in the queue, if there is none queued, then returns null immediately.
*
* @return this receiver object
*/
public JmsReceiver noWait() {
this.wait = new NoWait();
return this;
}
/**
* Waits specific number of milliseconds for a message to eventually appear in the queue, or returns null if there was no message queued in given interval.
*
* @param timeout wait until
* @return this receiver object
*/
public JmsReceiver withTimeout(long timeout) {
this.wait = new WaitSpecificTime(timeout);
return this;
}
/**
* Sets the message acknowledgement mode.
*
* @param acknowledgeMode JMS acknowledge mode as in {@link Session}
* @return this receiver object
*/
public JmsReceiver withAcknowledgeMode(int acknowledgeMode) {
this.acknowledgeMode = acknowledgeMode;
return this;
}
/**
* Won't close the connection automatically upon completion, allowing to reuse given connection.
*
* @return this receiver object
*/
public JmsReceiver noAutoClose() {
this.autoClose = false;
return this;
}
/**
* Closes the connection.
*/
public void close() {
try {
connection.close();
} catch (JMSException e) {
throw new java.lang.IllegalStateException(e);
}
}
/**
* Receives message from the given destination.
*
* @param destination where to receive from
* @return dequeued {@link ObjectMessage}
*/
public ObjectMessage from(Destination destination) {
try {
if (transacted) {
connection = xaConnectionFactory.createConnection();
} else {
connection = connectionFactory.createConnection();
}
Session session = connection.createSession(transacted, acknowledgeMode);
MessageConsumer messageConsumer;
if (selector != null) {
messageConsumer = session.createConsumer(destination, selector);
} else {
messageConsumer = session.createConsumer(destination);
}
connection.start();
ObjectMessage objectMessage;
if (wait instanceof WaitIndefinitely) {
objectMessage = (ObjectMessage) messageConsumer.receive();
} else if (wait instanceof NoWait) {
objectMessage = (ObjectMessage) messageConsumer.receiveNoWait();
} else if (wait instanceof WaitSpecificTime) {
objectMessage = (ObjectMessage) messageConsumer.receive(((WaitSpecificTime) wait).getTime());
} else {
throw new IllegalStateException("Unknown wait: " + wait.getClass());
}
return objectMessage;
} catch (JMSException e) {
throw new MessageDeliveryException("Failed to queue push message for further processing", e);
} finally {
if (connection != null && autoClose) {
try {
connection.close();
} catch (JMSException e) {
logger.error("Failed to close JMS connection: ", e);
}
}
}
}
}
/**
* Utility that allows to specify how should be message received and from which destination
*/
public class JmsSender {
private Serializable message;
private boolean transacted;
private Map properties = new LinkedHashMap<>();
private int autoAcknowledgeMode = Session.AUTO_ACKNOWLEDGE;
public JmsSender(Serializable message) {
this.message = message;
}
/**
* Send the message in transaction (i.e. use JmsXA connection factory).
*
* @return this sender object
*/
public JmsSender inTransaction() {
this.transacted = true;
return this;
}
/**
* Sets the property that can be later used to query message by selector.
*
* @param name of property
* @param value of property
* @return this sender object
*/
public JmsSender withProperty(String name, String value) {
if (value == null) {
throw new NullPointerException("Property value cannot be null");
}
this.properties.put(name, value);
return this;
}
/**
* Sets the property that can be later used to query message by selector.
*
* @param name of property
* @param value of property
* @return this sender object
*/
public JmsSender withProperty(String name, Long value) {
if (value == null) {
throw new NullPointerException("Property value cannot be null");
}
this.properties.put(name, value);
return this;
}
/**
* The message sent with given ID will be delivered exactly once.
*
* Any other try to sent another message with exactly same ID won't result into queing the message,
* no matter what payload the another message has.
*
* @param duplicateDetectionId protection of ID for duplicate msgs
* @return this sender object
*/
public JmsSender withDuplicateDetectionId(String duplicateDetectionId) {
if (duplicateDetectionId == null) {
throw new NullPointerException("duplicateDetectionId");
}
this.properties.put("_HQ_DUPL_ID", duplicateDetectionId);
this.properties.put("_AMQ_DUPL_ID", duplicateDetectionId);
return this;
}
/**
* The message sent with given ID will be scheduled to be delivered after specified number of miliseconds.
*
* @param delayMs the delay in milliseconds
* @return this sender object
*/
public JmsSender withDelayedDelivery(Long delayMs) {
if (delayMs == null) {
throw new NullPointerException("delayMs");
}
this.properties.put("_HQ_SCHED_DELIVERY", new Long(System.currentTimeMillis() + delayMs));
this.properties.put("_AMQ_SCHED_DELIVERY", new Long(System.currentTimeMillis() + delayMs));
return this;
}
/**
* Sends the message to the destination.
*
* @param destination where to send
*/
public void to(Destination destination) {
Connection connection = null;
try {
if (transacted) {
connection = xaConnectionFactory.createConnection();
} else {
connection = connectionFactory.createConnection();
}
Session session = connection.createSession(transacted, autoAcknowledgeMode);
MessageProducer messageProducer = session.createProducer(destination);
connection.start();
ObjectMessage objectMessage = session.createObjectMessage(message);
for (Entry property : properties.entrySet()) {
final Object value = property.getValue();
if (value instanceof String) {
objectMessage.setStringProperty(property.getKey(), (String) value);
} else if (value instanceof Long) {
objectMessage.setLongProperty(property.getKey(), (Long) value);
}
}
messageProducer.send(objectMessage);
} catch (JMSException e) {
throw new MessageDeliveryException("Failed to queue push message for further processing", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
logger.error("Failed to close JMS connection: ", e);
}
}
}
}
}
private interface Wait {
}
private static class WaitIndefinitely implements Wait {
}
private static class NoWait implements Wait {
}
private static class WaitSpecificTime implements Wait {
private long time;
WaitSpecificTime(long time) {
super();
this.time = time;
}
public long getTime() {
return time;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy