Java tutorial
// // typica - A client library for Amazon Web Services // Copyright (C) 2007 Xerox Corporation // // 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 com.xerox.amazonws.sqs; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import com.xerox.amazonws.common.AWSQueryConnection; import com.xerox.amazonws.typica.jaxb.AddGrantResponse; import com.xerox.amazonws.typica.jaxb.AttributedValue; import com.xerox.amazonws.typica.jaxb.ChangeMessageVisibilityResponse; import com.xerox.amazonws.typica.jaxb.DeleteMessageResponse; import com.xerox.amazonws.typica.jaxb.DeleteQueueResponse; import com.xerox.amazonws.typica.jaxb.GetQueueAttributesResponse; import com.xerox.amazonws.typica.jaxb.ListGrantsResponse; import com.xerox.amazonws.typica.jaxb.PeekMessageResponse; import com.xerox.amazonws.typica.jaxb.ReceiveMessageResponse; import com.xerox.amazonws.typica.jaxb.RemoveGrantResponse; import com.xerox.amazonws.typica.jaxb.SendMessageResponse; import com.xerox.amazonws.typica.jaxb.SetQueueAttributesResponse; /** * This class provides an interface with the Amazon SQS message queue. It provides methods * for sending / receiving messages and deleting queues and messsages on queues. * * @author D. Kavanagh * @author developer@dotech.com */ public class MessageQueue extends AWSQueryConnection { public static final int MAX_MESSAGES = 600; public static final int MAX_MESSAGE_BODIES_SIZE = 4096; protected String queueId; private boolean enableEncoding = true; protected MessageQueue(String queueUrl, String awsAccessId, String awsSecretKey, boolean isSecure, String server) throws SQSException { super(awsAccessId, awsSecretKey, isSecure, server, isSecure ? 443 : 80); if (queueUrl.startsWith("http")) { queueId = queueUrl.substring(queueUrl.indexOf("//") + 2); } else { queueId = queueUrl; // this is the case where the queue is created from a // fully qualified queue name, not a full queue URL } queueId = queueId.substring(queueId.indexOf("/") + 1); QueueService.setVersionHeader(this); } /** * This method provides the URL for the message queue represented by this object. * * @return generated queue service url */ public URL getUrl() { try { return new URL(super.getUrl().toString()); } catch (MalformedURLException ex) { return null; } } /** * This method returns the state of the base64 encoding flag. By default, all messages * are encoded on send and decoded on receive. * * @return state of encoding flag */ public boolean isEncoding() { return enableEncoding; } /** * This method sets the state of the encoding flag. Use this to override the default and * turn off automatic base64 encoding. * * @param enable the new state of the encoding flag */ public void setEncoding(boolean enable) { enableEncoding = enable; } /** * Sends a message to a specified queue. The message must be between 1 and 256K bytes long. * * @param msg the message to be sent */ public String sendMessage(String msg) throws SQSException { Map<String, String> params = new HashMap<String, String>(); String encodedMsg = enableEncoding ? new String(Base64.encodeBase64(msg.getBytes())) : msg; PostMethod method = new PostMethod(); try { method.setRequestEntity(new StringRequestEntity(encodedMsg, "text/plain", null)); SendMessageResponse response = makeRequest(method, "SendMessage", params, SendMessageResponse.class); return response.getMessageId(); } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Attempts to receive a message from the queue. The queue default visibility timeout * is used. * * @return the message object */ public Message receiveMessage() throws SQSException { Message amessage[] = receiveMessages(BigInteger.valueOf(1L), ((BigInteger) (null))); if (amessage.length > 0) return amessage[0]; else return null; } /** * Attempts to receive a message from the queue. * * @param visibilityTimeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. * @return the message object */ public Message receiveMessage(int visibilityTimeout) throws SQSException { Message amessage[] = receiveMessages(BigInteger.valueOf(1L), BigInteger.valueOf(visibilityTimeout)); if (amessage.length > 0) return amessage[0]; else return null; } /** * Attempts to retrieve a number of messages from the queue. If less than that are availble, * the max returned is the number of messages in the queue, but not necessarily all messages * in the queue will be returned. The queue default visibility timeout is used. * * @param numMessages the maximum number of messages to return * @return an array of message objects */ public Message[] receiveMessages(int numMessages) throws SQSException { return receiveMessages(BigInteger.valueOf(numMessages), ((BigInteger) (null))); } /** * Attempts to retrieve a number of messages from the queue. If less than that are availble, * the max returned is the number of messages in the queue, but not necessarily all messages * in the queue will be returned. * * @param numMessages the maximum number of messages to return * @param visibilityTimeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. * @return an array of message objects */ public Message[] receiveMessages(int numMessages, int visibilityTimeout) throws SQSException { return receiveMessages(BigInteger.valueOf(numMessages), BigInteger.valueOf(visibilityTimeout)); } /** * Internal implementation of receiveMessages. * * @param numMessages the maximum number of messages to return * @param visibilityTimeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. * @return an array of message objects */ protected Message[] receiveMessages(BigInteger numMessages, BigInteger visibilityTimeout) throws SQSException { Map<String, String> params = new HashMap<String, String>(); if (numMessages != null) { params.put("NumberOfMessages", numMessages.toString()); } if (visibilityTimeout != null) { params.put("VisibilityTimeout", visibilityTimeout.toString()); } GetMethod method = new GetMethod(); try { ReceiveMessageResponse response = makeRequest(method, "ReceiveMessage", params, ReceiveMessageResponse.class); if (response.getMessages() == null) { return new Message[0]; } else { ArrayList<Message> msgs = new ArrayList(); for (com.xerox.amazonws.typica.jaxb.Message msg : response.getMessages()) { String decodedMsg = enableEncoding ? new String(Base64.decodeBase64(msg.getMessageBody().getBytes())) : msg.getMessageBody(); msgs.add(new Message(msg.getMessageId(), decodedMsg)); } return msgs.toArray(new Message[msgs.size()]); } } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Returns a specified message. This does not affect and is not affected by the visibility * timeout of either the queue or the message. * * @param msgId the id of the message to be read * @return the message object */ public Message peekMessage(String msgId) throws SQSException { Map<String, String> params = new HashMap<String, String>(); params.put("MessageId", msgId); GetMethod method = new GetMethod(); try { PeekMessageResponse response = makeRequest(method, "PeekMessage", params, PeekMessageResponse.class); com.xerox.amazonws.typica.jaxb.Message msg = response.getMessage(); if (msg == null) { return null; } else { String decodedMsg = enableEncoding ? new String(Base64.decodeBase64(msg.getMessageBody().getBytes())) : msg.getMessageBody(); return new Message(msg.getMessageId(), decodedMsg); } } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Deletes the message identified by message object on the queue this object represents. * * @param msg the message to be deleted */ public void deleteMessage(Message msg) throws SQSException { deleteMessage(msg.getMessageId()); } /** * Deletes the message identified by msgid on the queue this object represents. * * @param msgId the id of the message to be deleted */ public void deleteMessage(String msgId) throws SQSException { Map<String, String> params = new HashMap<String, String>(); params.put("MessageId", msgId); GetMethod method = new GetMethod(); try { //DeleteMessageResponse response = makeRequest(method, "DeleteMessage", params, DeleteMessageResponse.class); } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Deletes the message queue represented by this object. Will fail if queue isn't empty. */ public void deleteQueue() throws SQSException { deleteQueue(false); } /** * Deletes the message queue represented by this object. * * @param force when true, non-empty queues will be deleted */ public void deleteQueue(boolean force) throws SQSException { Map<String, String> params = new HashMap<String, String>(); if (force) { params.put("ForceDeletion", "true"); } GetMethod method = new GetMethod(); try { //DeleteQueueResponse response = makeRequest(method, "DeleteQueue", params, DeleteQueueResponse.class); } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Sets the message visibility timeout. * * @param msg the message * @param timeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. */ public void setVisibilityTimeout(Message msg, int timeout) throws SQSException { setVisibilityTimeout(msg.getMessageId(), timeout); } /** * Sets the message visibility timeout. * * @param msgId the id of the message * @param timeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. */ public void setVisibilityTimeout(String msgId, int timeout) throws SQSException { Map<String, String> params = new HashMap<String, String>(); params.put("MessageId", "" + msgId); params.put("VisibilityTimeout", "" + timeout); GetMethod method = new GetMethod(); try { //ChangeMessageVisibilityResponse response = makeRequest(method, "ChangeMessageVisibility", params, ChangeMessageVisibilityResponse.class); } catch (JAXBException ex) { throw new SQSException("Problem setting the visibility timeout.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Sets the messages' visibility timeout. * * @param msgIds the ids of the messages * @param timeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. */ public void setVisibilityTimeout(String[] msgIds, int timeout) throws SQSException { for (String id : msgIds) { setVisibilityTimeout(id, timeout); } } /** * Gets the visibility timeout for the queue. Uses {@link #getQueueAttributes(QueueAttribute)}. */ public int getVisibilityTimeout() throws SQSException { return Integer.parseInt(getQueueAttributes(QueueAttribute.VISIBILITY_TIMEOUT).values().iterator().next()); } /** * Gets the visibility timeout for the queue. Uses {@link #getQueueAttributes(QueueAttribute)}. */ public int getApproximateNumberOfMessages() throws SQSException { return Integer.parseInt( getQueueAttributes(QueueAttribute.APPROXIMATE_NUMBER_OF_MESSAGES).values().iterator().next()); } /** * Gets queue attributes. This is provided to expose the underlying functionality. * Currently supported attributes are ApproximateNumberOfMessages and VisibilityTimeout. * * @return a map of attributes and their values */ public Map<String, String> getQueueAttributes(QueueAttribute qAttr) throws SQSException { Map<String, String> params = new HashMap<String, String>(); params.put("Attribute", qAttr.queryAttribute()); GetMethod method = new GetMethod(); try { GetQueueAttributesResponse response = makeRequest(method, "GetQueueAttributes", params, GetQueueAttributesResponse.class); Map<String, String> ret = new HashMap<String, String>(); List<AttributedValue> attrs = response.getAttributedValues(); for (AttributedValue attr : attrs) { ret.put(attr.getAttribute(), attr.getValue()); } return ret; } catch (JAXBException ex) { throw new SQSException("Problem getting the visilibity timeout.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Sets the visibility timeout of the queue. Uses {@link #setQueueAttribute(String, String)}. * * @param timeout the duration (in seconds) the retrieved message is hidden from * subsequent calls to retrieve. */ public void setVisibilityTimeout(int timeout) throws SQSException { setQueueAttribute("VisibilityTimeout", "" + timeout); } /** * Sets a queue attribute. This is provided to expose the underlying functionality, although * the only attribute at this time is visibility timeout. * * @param attribute name of the attribute being set * @param value the value being set for this attribute */ public void setQueueAttribute(String attribute, String value) throws SQSException { Map<String, String> params = new HashMap<String, String>(); params.put("Attribute", attribute); params.put("Value", value); GetMethod method = new GetMethod(); try { //SetQueueAttributesResponse response = makeRequest(method, "SetQueueAttributes", params, SetQueueAttributesResponse.class); } catch (JAXBException ex) { throw new SQSException("Problem setting the visibility timeout.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Adds a grant for a specific user. * * @param eMailAddress the amazon address of the user * @param permission the permission to add (ReceiveMessage | SendMessage | FullControl) */ public void addGrantByEmailAddress(String eMailAddress, String permission) throws SQSException { Map<String, String> params = new HashMap<String, String>(); if (permission != null && !permission.trim().equals("")) { params.put("Permission", permission); } params.put("Grantee.EmailAddress", eMailAddress); addGrant(params); } /** * Adds a grant for a specific user. * * @param id the amazon user id of the user * @param displayName not sure if this can even be used * @param permission the permission to add (ReceiveMessage | SendMessage | FullControl) */ public void addGrantByCustomerId(String id, String displayName, String permission) throws SQSException { Map<String, String> params = new HashMap<String, String>(); if (permission != null && !permission.trim().equals("")) { params.put("Permission", permission); } params.put("Grantee.ID", id); addGrant(params); } private void addGrant(Map<String, String> params) throws SQSException { GetMethod method = new GetMethod(); try { //AddGrantResponse response = makeRequest(method, "AddGrant", params, AddGrantResponse.class); } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Removes a grant for a specific user. * * @param eMailAddress the amazon address of the user * @param permission the permission to add (ReceiveMessage | SendMessage | FullControl) */ public void removeGrantByEmailAddress(String eMailAddress, String permission) throws Exception { Map<String, String> params = new HashMap<String, String>(); if (permission != null && !permission.trim().equals("")) { params.put("Permission", permission); } params.put("Grantee.EmailAddress", eMailAddress); removeGrant(params); } /** * Removes a grant for a specific user. * * @param id the amazon user id of the user * @param permission the permission to add (ReceiveMessage | SendMessage | FullControl) */ public void removeGrantByCustomerId(String id, String permission) throws Exception { Map<String, String> params = new HashMap<String, String>(); if (permission != null && !permission.trim().equals("")) { params.put("Permission", permission); } params.put("Grantee.ID", id); removeGrant(params); } private void removeGrant(Map<String, String> params) throws SQSException { GetMethod method = new GetMethod(); try { //RemoveGrantResponse response = makeRequest(method, "RemoveGrant", params, RemoveGrantResponse.class); } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Retrieves a list of grants for this queue. The results can be filtered by specifying * a grantee or a particular permission. * * @param grantee the optional user or group * @param permission the optional permission * @return a list of objects representing the grants */ public Grant[] listGrants(Grantee grantee, String permission) throws Exception { Map<String, String> params = new HashMap<String, String>(); if (permission != null && !permission.trim().equals("")) { params.put("Permission", permission); } if (grantee instanceof CanonicalUser) { params.put("Grantee.ID", ((CanonicalUser) grantee).getID()); } GetMethod method = new GetMethod(); try { ListGrantsResponse response = makeRequest(method, "ListGrants", params, ListGrantsResponse.class); Grant[] grants = new Grant[response.getGrantLists().size()]; int i = 0; for (com.xerox.amazonws.typica.jaxb.Grant g : response.getGrantLists()) { Grantee g2 = null; if (g.getGrantee() instanceof com.xerox.amazonws.typica.jaxb.Group) { com.xerox.amazonws.typica.jaxb.Group grp = (com.xerox.amazonws.typica.jaxb.Group) g .getGrantee(); g2 = new Group(new URI(grp.getURI())); } else if (g.getGrantee() instanceof com.xerox.amazonws.typica.jaxb.CanonicalUser) { com.xerox.amazonws.typica.jaxb.CanonicalUser u = (com.xerox.amazonws.typica.jaxb.CanonicalUser) g .getGrantee(); g2 = new CanonicalUser(u.getID(), u.getDisplayName()); } grants[i] = new Grant(g2, g.getPermission()); i++; } return grants; } catch (JAXBException ex) { throw new SQSException("Problem parsing returned message.", ex); } catch (HttpException ex) { throw new SQSException(ex.getMessage(), ex); } catch (IOException ex) { throw new SQSException(ex.getMessage(), ex); } finally { method.releaseConnection(); } } /** * Overriding this because the queue name is baked into the URL and QUERY * assembles the URL within the baseclass. */ protected URL makeURL(String resource) throws MalformedURLException { return super.makeURL(queueId + resource); } public static List<MessageQueue> createList(String[] queueUrls, String awsAccesseyId, String awsSecretKey, boolean isSecure, String server, HttpClient hc) throws SQSException { ArrayList<MessageQueue> ret = new ArrayList<MessageQueue>(); for (int i = 0; i < queueUrls.length; i++) { MessageQueue mq = new MessageQueue(queueUrls[i], awsAccesseyId, awsSecretKey, isSecure, server); mq.setHttpClient(hc); ret.add(mq); } return ret; } }