org.apache.activemq.artemis.jms.client.ActiveMQMessageProducer Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.activemq.artemis.jms.client;
import jakarta.jms.BytesMessage;
import jakarta.jms.CompletionListener;
import jakarta.jms.DeliveryMode;
import jakarta.jms.Destination;
import jakarta.jms.IllegalStateException;
import jakarta.jms.InvalidDestinationException;
import jakarta.jms.JMSException;
import jakarta.jms.MapMessage;
import jakarta.jms.Message;
import jakarta.jms.MessageProducer;
import jakarta.jms.ObjectMessage;
import jakarta.jms.Queue;
import jakarta.jms.QueueSender;
import jakarta.jms.StreamMessage;
import jakarta.jms.TextMessage;
import jakarta.jms.Topic;
import jakarta.jms.TopicPublisher;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.SendAcknowledgementHandler;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.utils.UUID;
import org.apache.activemq.artemis.utils.UUIDGenerator;
import org.jboss.logging.Logger;
/**
* ActiveMQ Artemis implementation of a JMS MessageProducer.
*/
public class ActiveMQMessageProducer implements MessageProducer, QueueSender, TopicPublisher {
private static final Logger logger = Logger.getLogger(ActiveMQMessageProducer.class);
private final ConnectionFactoryOptions options;
private final ActiveMQConnection connection;
private final SimpleString connID;
private final ClientProducer clientProducer;
private final ActiveMQSession session;
private boolean disableMessageID = false;
private boolean disableMessageTimestamp = false;
private int defaultPriority = Message.DEFAULT_PRIORITY;
private long defaultTimeToLive = Message.DEFAULT_TIME_TO_LIVE;
private int defaultDeliveryMode = Message.DEFAULT_DELIVERY_MODE;
private long defaultDeliveryDelay = Message.DEFAULT_DELIVERY_DELAY;
private final ActiveMQDestination defaultDestination;
protected ActiveMQMessageProducer(final ActiveMQConnection connection,
final ClientProducer producer,
final ActiveMQDestination defaultDestination,
final ActiveMQSession session,
final ConnectionFactoryOptions options) throws JMSException {
this.options = options;
this.connection = connection;
connID = connection.getClientID() != null ? new SimpleString(connection.getClientID()) : connection.getUID();
this.clientProducer = producer;
this.defaultDestination = defaultDestination;
this.session = session;
}
// MessageProducer implementation --------------------------------
@Override
public void setDisableMessageID(final boolean value) throws JMSException {
checkClosed();
disableMessageID = value;
}
@Override
public boolean getDisableMessageID() throws JMSException {
checkClosed();
return disableMessageID;
}
@Override
public void setDisableMessageTimestamp(final boolean value) throws JMSException {
checkClosed();
disableMessageTimestamp = value;
}
@Override
public boolean getDisableMessageTimestamp() throws JMSException {
checkClosed();
return disableMessageTimestamp;
}
@Override
public void setDeliveryMode(final int deliveryMode) throws JMSException {
checkClosed();
if (deliveryMode != DeliveryMode.NON_PERSISTENT && deliveryMode != DeliveryMode.PERSISTENT) {
throw ActiveMQJMSClientBundle.BUNDLE.illegalDeliveryMode(deliveryMode);
}
defaultDeliveryMode = deliveryMode;
}
@Override
public int getDeliveryMode() throws JMSException {
checkClosed();
return defaultDeliveryMode;
}
@Override
public void setPriority(final int defaultPriority) throws JMSException {
checkClosed();
if (defaultPriority < 0 || defaultPriority > 9) {
throw new JMSException("Illegal priority value: " + defaultPriority);
}
this.defaultPriority = defaultPriority;
}
@Override
public int getPriority() throws JMSException {
checkClosed();
return defaultPriority;
}
@Override
public void setTimeToLive(final long timeToLive) throws JMSException {
checkClosed();
defaultTimeToLive = timeToLive;
}
@Override
public long getTimeToLive() throws JMSException {
checkClosed();
return defaultTimeToLive;
}
@Override
public Destination getDestination() throws JMSException {
checkClosed();
return defaultDestination;
}
@Override
public void close() throws JMSException {
connection.getThreadAwareContext().assertNotCompletionListenerThread();
try {
clientProducer.close();
} catch (ActiveMQException e) {
throw JMSExceptionHelper.convertFromActiveMQException(e);
}
}
@Override
public void send(final Message message) throws JMSException {
checkDefaultDestination();
doSendx(defaultDestination, message, defaultDeliveryMode, defaultPriority, defaultTimeToLive, null);
}
@Override
public void send(final Message message,
final int deliveryMode,
final int priority,
final long timeToLive) throws JMSException {
checkDefaultDestination();
doSendx(defaultDestination, message, deliveryMode, priority, timeToLive, null);
}
@Override
public void send(final Destination destination, final Message message) throws JMSException {
send(destination, message, defaultDeliveryMode, defaultPriority, defaultTimeToLive);
}
@Override
public void send(final Destination destination,
final Message message,
final int deliveryMode,
final int priority,
final long timeToLive) throws JMSException {
checkClosed();
checkDestination(destination);
doSendx((ActiveMQDestination) destination, message, deliveryMode, priority, timeToLive, null);
}
@Override
public void setDeliveryDelay(long deliveryDelay) throws JMSException {
this.defaultDeliveryDelay = deliveryDelay;
}
@Override
public long getDeliveryDelay() throws JMSException {
return defaultDeliveryDelay;
}
@Override
public void send(Message message, CompletionListener completionListener) throws JMSException {
send(message, defaultDeliveryMode, defaultPriority, defaultTimeToLive, completionListener);
}
@Override
public void send(Message message,
int deliveryMode,
int priority,
long timeToLive,
CompletionListener completionListener) throws JMSException {
checkCompletionListener(completionListener);
checkDefaultDestination();
doSendx(defaultDestination, message, deliveryMode, priority, timeToLive, completionListener);
}
@Override
public void send(Destination destination,
Message message,
CompletionListener completionListener) throws JMSException {
send(destination, message, defaultDeliveryMode, defaultPriority, defaultTimeToLive, completionListener);
}
@Override
public void send(Destination destination,
Message message,
int deliveryMode,
int priority,
long timeToLive,
CompletionListener completionListener) throws JMSException {
checkClosed();
checkCompletionListener(completionListener);
checkDestination(destination);
doSendx((ActiveMQDestination) destination, message, deliveryMode, priority, timeToLive, completionListener);
}
// TopicPublisher Implementation ---------------------------------
@Override
public Topic getTopic() throws JMSException {
return (Topic) getDestination();
}
@Override
public void publish(final Message message) throws JMSException {
send(message);
}
@Override
public void publish(final Topic topic, final Message message) throws JMSException {
send(topic, message);
}
@Override
public void publish(final Message message,
final int deliveryMode,
final int priority,
final long timeToLive) throws JMSException {
send(message, deliveryMode, priority, timeToLive);
}
@Override
public void publish(final Topic topic,
final Message message,
final int deliveryMode,
final int priority,
final long timeToLive) throws JMSException {
checkDestination(topic);
doSendx((ActiveMQDestination) topic, message, deliveryMode, priority, timeToLive, null);
}
// QueueSender Implementation ------------------------------------
@Override
public void send(final Queue queue, final Message message) throws JMSException {
send((Destination) queue, message);
}
@Override
public void send(final Queue queue,
final Message message,
final int deliveryMode,
final int priority,
final long timeToLive) throws JMSException {
checkDestination(queue);
doSendx((ActiveMQDestination) queue, message, deliveryMode, priority, timeToLive, null);
}
@Override
public Queue getQueue() throws JMSException {
return (Queue) getDestination();
}
@Override
public String toString() {
return "ActiveMQMessageProducer->" + clientProducer;
}
/**
* Check if the default destination has been set
*/
private void checkDefaultDestination() {
if (defaultDestination == null) {
throw new UnsupportedOperationException("Producer does not have a default destination");
}
}
/**
* Check if the destination is sent correctly
*/
private void checkDestination(Destination destination) throws InvalidDestinationException {
if (destination != null && !(destination instanceof ActiveMQDestination)) {
throw new InvalidDestinationException("Foreign destination:" + destination);
}
if (destination != null && defaultDestination != null) {
throw new UnsupportedOperationException("Cannot specify destination if producer has a default destination");
}
if (destination == null) {
throw ActiveMQJMSClientBundle.BUNDLE.nullTopic();
}
}
private void checkCompletionListener(CompletionListener completionListener) {
if (completionListener == null) {
throw ActiveMQJMSClientBundle.BUNDLE.nullArgumentNotAllowed("CompletionListener");
}
}
private void doSendx(ActiveMQDestination destination,
final Message jmsMessage,
final int deliveryMode,
final int priority,
final long timeToLive,
CompletionListener completionListener) throws JMSException {
jmsMessage.setJMSDeliveryMode(deliveryMode);
jmsMessage.setJMSPriority(priority);
if (timeToLive == 0) {
jmsMessage.setJMSExpiration(0);
} else {
jmsMessage.setJMSExpiration(System.currentTimeMillis() + timeToLive);
}
if (!disableMessageTimestamp) {
jmsMessage.setJMSTimestamp(System.currentTimeMillis());
} else {
jmsMessage.setJMSTimestamp(0);
}
SimpleString address = null;
ClientSession clientSession = session.getCoreSession();
if (destination == null) {
if (defaultDestination == null) {
throw new UnsupportedOperationException("Destination must be specified on send with an anonymous producer");
}
destination = defaultDestination;
// address is meant to be null on this case, as it will use the coreProducer's default address
} else {
if (defaultDestination != null && !destination.equals(defaultDestination)) {
// This is a JMS TCK & Rule Definition.
// if you specified a destination on the Producer, you cannot use it for a different destinations.
throw new UnsupportedOperationException("Where a default destination is specified " + "for the sender and a destination is " + "specified in the arguments to the send, " + "these destinations must be equal");
}
session.checkDestination(destination);
address = destination.getSimpleAddress();
}
ActiveMQMessage activeMQJmsMessage;
boolean foreign = false;
// First convert from foreign message if appropriate
if (!(jmsMessage instanceof ActiveMQMessage)) {
// JMS 1.1 Sect. 3.11.4: A provider must be prepared to accept, from a client,
// a message whose implementation is not one of its own.
if (jmsMessage instanceof BytesMessage) {
activeMQJmsMessage = new ActiveMQBytesMessage((BytesMessage) jmsMessage, clientSession);
} else if (jmsMessage instanceof MapMessage) {
activeMQJmsMessage = new ActiveMQMapMessage((MapMessage) jmsMessage, clientSession);
} else if (jmsMessage instanceof ObjectMessage) {
activeMQJmsMessage = new ActiveMQObjectMessage((ObjectMessage) jmsMessage, clientSession, options);
} else if (jmsMessage instanceof StreamMessage) {
activeMQJmsMessage = new ActiveMQStreamMessage((StreamMessage) jmsMessage, clientSession);
} else if (jmsMessage instanceof TextMessage) {
activeMQJmsMessage = new ActiveMQTextMessage((TextMessage) jmsMessage, clientSession);
} else {
activeMQJmsMessage = new ActiveMQMessage(jmsMessage, clientSession);
}
// Set the destination on the original message
jmsMessage.setJMSDestination(destination);
foreign = true;
} else {
activeMQJmsMessage = (ActiveMQMessage) jmsMessage;
}
if (!disableMessageID) {
// Generate a JMS id
UUID uid = UUIDGenerator.getInstance().generateUUID();
activeMQJmsMessage.getCoreMessage().setUserID(uid);
activeMQJmsMessage.resetMessageID(null);
}
if (foreign) {
jmsMessage.setJMSMessageID(activeMQJmsMessage.getJMSMessageID());
}
activeMQJmsMessage.setJMSDestination(destination);
try {
activeMQJmsMessage.doBeforeSend();
} catch (Exception e) {
JMSException je = new JMSException(e.getMessage());
je.initCause(e);
throw je;
}
if (defaultDeliveryDelay > 0) {
activeMQJmsMessage.setJMSDeliveryTime(System.currentTimeMillis() + defaultDeliveryDelay);
}
ClientMessage coreMessage = activeMQJmsMessage.getCoreMessage();
coreMessage.putStringProperty(ActiveMQConnection.CONNECTION_ID_PROPERTY_NAME, connID);
coreMessage.setRoutingType(destination.isQueue() ? RoutingType.ANYCAST : RoutingType.MULTICAST);
try {
/**
* Using a completionListener requires wrapping using a {@link CompletionListenerWrapper},
* so we avoid it if we can.
*/
if (completionListener != null) {
clientProducer.send(address, coreMessage, new CompletionListenerWrapper(completionListener, jmsMessage, this));
} else {
clientProducer.send(address, coreMessage);
}
} catch (ActiveMQInterruptedException e) {
JMSException jmsException = new JMSException(e.getMessage());
jmsException.initCause(e);
throw jmsException;
} catch (ActiveMQException e) {
throw JMSExceptionHelper.convertFromActiveMQException(e);
} catch (java.lang.IllegalStateException e) {
JMSException je = new IllegalStateException(e.getMessage());
je.setStackTrace(e.getStackTrace());
je.initCause(e);
throw je;
}
}
private void checkClosed() throws JMSException {
if (clientProducer.isClosed()) {
throw new IllegalStateException("Producer is closed");
}
session.checkClosed();
}
private static final class CompletionListenerWrapper implements SendAcknowledgementHandler {
private final CompletionListener completionListener;
private final Message jmsMessage;
private final ActiveMQMessageProducer producer;
/**
* @param jmsMessage
* @param producer
*/
private CompletionListenerWrapper(CompletionListener listener,
Message jmsMessage,
ActiveMQMessageProducer producer) {
this.completionListener = listener;
this.jmsMessage = jmsMessage;
this.producer = producer;
}
@Override
public void sendAcknowledged(org.apache.activemq.artemis.api.core.Message clientMessage) {
if (jmsMessage instanceof StreamMessage) {
try {
((StreamMessage) jmsMessage).reset();
} catch (JMSException e) {
logger.debug("ignoring exception", e);
}
}
if (jmsMessage instanceof BytesMessage) {
try {
((BytesMessage) jmsMessage).reset();
} catch (JMSException e) {
logger.debug("ignoring exception", e);
}
}
try {
producer.connection.getThreadAwareContext().setCurrentThread(true);
completionListener.onCompletion(jmsMessage);
} finally {
producer.connection.getThreadAwareContext().clearCurrentThread(true);
}
}
@Override
public void sendFailed(org.apache.activemq.artemis.api.core.Message clientMessage, Exception exception) {
if (jmsMessage instanceof StreamMessage) {
try {
((StreamMessage) jmsMessage).reset();
} catch (JMSException e) {
// HORNETQ-1209 XXX ignore?
}
}
if (jmsMessage instanceof BytesMessage) {
try {
((BytesMessage) jmsMessage).reset();
} catch (JMSException e) {
// HORNETQ-1209 XXX ignore?
}
}
try {
producer.connection.getThreadAwareContext().setCurrentThread(true);
if (exception instanceof ActiveMQException) {
exception = JMSExceptionHelper.convertFromActiveMQException((ActiveMQException) exception);
} else if (exception instanceof ActiveMQInterruptedException) {
exception = JMSExceptionHelper.convertFromActiveMQException((ActiveMQInterruptedException) exception);
}
completionListener.onException(jmsMessage, exception);
} finally {
producer.connection.getThreadAwareContext().clearCurrentThread(true);
}
}
@Override
public String toString() {
return CompletionListenerWrapper.class.getSimpleName() + "( completionListener=" + completionListener + ")";
}
}
}