com.amazon.sqs.javamessaging.SQSMessageProducer Maven / Gradle / Ivy
Show all versions of amazon-sqs-java-messaging-lib Show documentation
/*
* Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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 com.amazon.sqs.javamessaging;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.jms.Destination;
import javax.jms.IllegalStateException;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueSender;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazon.sqs.javamessaging.message.SQSBytesMessage;
import com.amazon.sqs.javamessaging.message.SQSMessage;
import com.amazon.sqs.javamessaging.message.SQSObjectMessage;
import com.amazon.sqs.javamessaging.message.SQSTextMessage;
import com.amazon.sqs.javamessaging.message.SQSMessage.JMSMessagePropertyValue;
import com.amazonaws.util.Base64;
import com.amazonaws.services.sqs.model.MessageAttributeValue;
import com.amazonaws.services.sqs.model.SendMessageRequest;
/**
* A client uses a MessageProducer object to send messages to a queue
* destination. A MessageProducer object is created by passing a Destination
* object to a message-producer creation method supplied by a session.
*
* A client also has the option of creating a message producer without supplying
* a queue destination. In this case, a destination must be provided with every send
* operation.
*
*/
public class SQSMessageProducer implements MessageProducer, QueueSender {
private static final Log LOG = LogFactory.getLog(SQSMessageProducer.class);
/** This field is not actually used. */
private long timeToLive;
/** This field is not actually used. */
private int defaultPriority;
/** This field is not actually used. */
private int deliveryMode;
/** This field is not actually used. */
private boolean disableMessageTimestamp;
/** This field is not actually used. */
private boolean disableMessageID;
/**
* State of MessageProducer.
* True if MessageProducer is closed.
*/
final AtomicBoolean closed = new AtomicBoolean(false);
private final AmazonSQSMessagingClientWrapper amazonSQSClient;
private final SQSQueueDestination sqsDestination;
private final SQSSession parentSQSSession;
SQSMessageProducer(AmazonSQSMessagingClientWrapper amazonSQSClient, SQSSession parentSQSSession,
SQSQueueDestination destination) throws JMSException {
this.sqsDestination = destination;
this.amazonSQSClient = amazonSQSClient;
this.parentSQSSession = parentSQSSession;
}
void sendInternal(Queue queue, Message message) throws JMSException {
checkClosed();
String sqsMessageBody = null;
String messageType = null;
if (message instanceof SQSMessage) {
message.setJMSDestination(queue);
if (message instanceof SQSBytesMessage) {
sqsMessageBody = Base64.encodeAsString(((SQSBytesMessage) message).getBodyAsBytes());
messageType = SQSMessage.BYTE_MESSAGE_TYPE;
} else if (message instanceof SQSObjectMessage) {
sqsMessageBody = ((SQSObjectMessage) message).getMessageBody();
messageType = SQSMessage.OBJECT_MESSAGE_TYPE;
} else if (message instanceof SQSTextMessage) {
sqsMessageBody = ((SQSTextMessage) message).getText();
messageType = SQSMessage.TEXT_MESSAGE_TYPE;
}
} else {
throw new MessageFormatException(
"Unrecognized message type. Messages have to be one of: SQSBytesMessage, SQSObjectMessage, or SQSTextMessage");
}
if (sqsMessageBody == null || sqsMessageBody.isEmpty()) {
throw new JMSException("Message body cannot be null or empty");
}
Map messageAttributes = propertyToMessageAttribute((SQSMessage) message);
addMessageTypeReservedAttribute(messageAttributes, (SQSMessage) message, messageType);
SendMessageRequest sendMessageRequest = new SendMessageRequest(((SQSQueueDestination) queue).getQueueUrl(), sqsMessageBody);
sendMessageRequest.setMessageAttributes(messageAttributes);
String messageId = amazonSQSClient.sendMessage(sendMessageRequest).getMessageId();
LOG.info("Message sent to SQS with SQS-assigned messageId: " + messageId);
/** TODO: Do not support disableMessageID for now.*/
message.setJMSMessageID(String.format(SQSMessagingClientConstants.MESSAGE_ID_FORMAT, messageId));
((SQSMessage)message).setSQSMessageId(messageId);
}
@Override
public Queue getQueue() throws JMSException {
return sqsDestination;
}
/**
* Sends a message to a queue.
*
* @param queue
* the queue destination to send this message to
* @param message
* the message to send
* @throws InvalidDestinationException
* If a client uses this method with a destination other than
* SQS queue destination.
* @throws MessageFormatException
* If an invalid message is specified.
* @throws UnsupportedOperationException
* If a client uses this method with a MessageProducer that
* specified a destination at creation time.
* @throws JMSException
* If session is closed or internal error.
*/
@Override
public void send(Queue queue, Message message) throws JMSException {
if (!(queue instanceof SQSQueueDestination)) {
throw new InvalidDestinationException(
"Incompatible implementation of Queue. Please use SQSQueueDestination implementation.");
}
checkIfDestinationAlreadySet();
sendInternal(queue, message);
}
/**
* Not verified on the client side, but SQS Attribute names must be valid
* letter or digit on the basic multilingual plane in addition to allowing
* '_', '-' and '.'. No component of an attribute name may be empty, thus an
* attribute name may neither start nor end in '.'. And it may not contain
* "..".
*/
Map propertyToMessageAttribute(SQSMessage message)
throws JMSException {
Map messageAttributes = new HashMap();
Enumeration propertyNames = message.getPropertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = propertyNames.nextElement();
// This is generated from SQS message attribute "ApproximateReceiveCount"
if (propertyName.equals(SQSMessagingClientConstants.JMSX_DELIVERY_COUNT)) {
continue;
}
JMSMessagePropertyValue propertyObject = message.getJMSMessagePropertyValue(propertyName);
MessageAttributeValue messageAttributeValue = new MessageAttributeValue();
messageAttributeValue.setDataType(propertyObject.getType());
messageAttributeValue.setStringValue(propertyObject.getStringMessageAttributeValue());
messageAttributes.put(propertyName, messageAttributeValue);
}
return messageAttributes;
}
/**
* Adds the message type attribute during send as part of the send message
* request.
*/
private void addMessageTypeReservedAttribute(Map messageAttributes,
SQSMessage message, String value) throws JMSException {
MessageAttributeValue messageAttributeValue = new MessageAttributeValue();
messageAttributeValue.setDataType(SQSMessagingClientConstants.STRING);
messageAttributeValue.setStringValue(value);
/**
* This will override the existing attribute if exists. Everything that
* has prefix JMS_ is reserved for JMS Provider, but if the user sets that
* attribute, it will be overwritten.
*/
messageAttributes.put(SQSMessage.JMS_SQS_MESSAGE_TYPE, messageAttributeValue);
}
/**
* Sends a message to a queue.
*
* Send does not support deliveryMode, priority, and timeToLive. It will
* ignore anything in deliveryMode, priority, and timeToLive.
*
* @param queue
* the queue destination to send this message to
* @param message
* the message to send
* @param deliveryMode
* @param priority
* @param timeToLive
* @throws InvalidDestinationException
* If a client uses this method with a destination other than
* SQS queue destination.
* @throws MessageFormatException
* If an invalid message is specified.
* @throws UnsupportedOperationException
* If a client uses this method with a MessageProducer that
* specified a destination at creation time.
* @throws JMSException
* If session is closed or internal error.
*/
@Override
public void send(Queue queue, Message message, int deliveryMode, int priority, long timeToLive)
throws JMSException {
send(queue, message);
}
/**
* Gets the destination associated with this MessageProducer.
*
* @return this producer's queue destination
*/
@Override
public Destination getDestination() throws JMSException {
return sqsDestination;
}
/**
* Closes the message producer.
*/
@Override
public void close() throws JMSException {
if (closed.compareAndSet(false, true)) {
parentSQSSession.removeProducer(this);
}
}
/**
* Sends a message to a destination created during the creation time of this
* message producer.
*
* @param message
* the message to send
* @throws MessageFormatException
* If an invalid message is specified.
* @throws UnsupportedOperationException
* If a client uses this method with a MessageProducer that did
* not specify a destination at creation time.
* @throws JMSException
* If session is closed or internal error.
*/
@Override
public void send(Message message) throws JMSException {
if (sqsDestination == null) {
throw new UnsupportedOperationException(
"MessageProducer has to specify a destination at creation time.");
}
sendInternal((Queue) sqsDestination, message);
}
/**
* Sends a message to a destination created during the creation time of this
* message producer.
*
* Send does not support deliveryMode, priority, and timeToLive. It will
* ignore anything in deliveryMode, priority, and timeToLive.
*
* @param message
* the message to send
* @param deliveryMode
* @param priority
* @param timeToLive
* @throws MessageFormatException
* If an invalid message is specified.
* @throws UnsupportedOperationException
* If a client uses this method with a MessageProducer that did
* not specify a destination at creation time.
* @throws JMSException
* If session is closed or internal error.
*/
@Override
public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
send(message);
}
/**
* Sends a message to a queue destination.
*
* @param destination
* the queue destination to send this message to
* @param message
* the message to send
* @throws InvalidDestinationException
* If a client uses this method with a destination other than
* valid SQS queue destination.
* @throws MessageFormatException
* If an invalid message is specified.
* @throws UnsupportedOperationException
* If a client uses this method with a MessageProducer that
* specified a destination at creation time.
* @throws JMSException
* If session is closed or internal error.
*/
@Override
public void send(Destination destination, Message message) throws JMSException {
if (destination == null) {
throw new InvalidDestinationException("Destination cannot be null");
}
if (destination instanceof SQSQueueDestination) {
send((Queue) destination, message);
} else {
throw new InvalidDestinationException("Incompatible implementation of Destination. Please use SQSQueueDestination implementation.");
}
}
/**
* Sends a message to a queue destination.
*
* Send does not support deliveryMode, priority, and timeToLive. It will
* ignore anything in deliveryMode, priority, and timeToLive.
*
* @param destination
* the queue destination to send this message to
* @param message
* the message to send
* @param deliveryMode
* @param priority
* @param timeToLive
* @throws InvalidDestinationException
* If a client uses this method with a destination other than
* valid SQS queue destination.
* @throws MessageFormatException
* If an invalid message is specified.
* @throws UnsupportedOperationException
* If a client uses this method with a MessageProducer that
* specified a destination at creation time.
* @throws JMSException
* If session is closed or internal error.
*/
@Override
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
send(destination, message);
}
/** This method is not supported. */
@Override
public void setDisableMessageID(boolean value) throws JMSException {
this.disableMessageID = value;
}
/** This method is not supported. */
@Override
public boolean getDisableMessageID() throws JMSException {
return disableMessageID;
}
/** This method is not supported. */
@Override
public void setDisableMessageTimestamp(boolean value) throws JMSException {
this.disableMessageTimestamp = value;
}
/** This method is not supported. */
@Override
public boolean getDisableMessageTimestamp() throws JMSException {
return disableMessageTimestamp;
}
/** This method is not supported. */
@Override
public void setDeliveryMode(int deliveryMode) throws JMSException {
this.deliveryMode = deliveryMode;
}
/** This method is not supported. */
@Override
public int getDeliveryMode() throws JMSException {
return deliveryMode;
}
/** This method is not supported. */
@Override
public void setPriority(int defaultPriority) throws JMSException {
this.defaultPriority = defaultPriority;
}
/** This method is not supported. */
@Override
public int getPriority() throws JMSException {
return defaultPriority;
}
/** This method is not supported. */
@Override
public void setTimeToLive(long timeToLive) throws JMSException {
this.timeToLive = timeToLive;
}
/** This method is not supported. */
@Override
public long getTimeToLive() throws JMSException {
return timeToLive;
}
void checkClosed() throws IllegalStateException {
if (closed.get()) {
throw new IllegalStateException("The producer is closed.");
}
}
void checkIfDestinationAlreadySet() {
if (sqsDestination != null) {
throw new UnsupportedOperationException(
"MessageProducer already specified a destination at creation time.");
}
}
/*
* Unit Tests Utility Functions
*/
AtomicBoolean isClosed() {
return closed;
}
}