001    /** 
002     *
003     * Copyright 2004 Protique Ltd
004     * 
005     * Licensed under the Apache License, Version 2.0 (the "License"); 
006     * you may not use this file except in compliance with the License. 
007     * You may obtain a copy of the License at 
008     * 
009     * http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS, 
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
014     * See the License for the specific language governing permissions and 
015     * limitations under the License. 
016     * 
017     **/
018    
019    package org.activemq.web;
020    
021    import org.activemq.message.ActiveMQDestination;
022    import org.apache.commons.logging.Log;
023    import org.apache.commons.logging.LogFactory;
024    
025    import javax.jms.Destination;
026    import javax.jms.JMSException;
027    import javax.jms.Message;
028    import javax.jms.MessageConsumer;
029    import javax.jms.ObjectMessage;
030    import javax.jms.TextMessage;
031    import javax.servlet.ServletConfig;
032    import javax.servlet.ServletException;
033    import javax.servlet.http.HttpServletRequest;
034    import javax.servlet.http.HttpServletResponse;
035    
036    import java.io.IOException;
037    import java.io.PrintWriter;
038    
039    /**
040     * A servlet for sending and receiving messages to/from JMS destinations using
041     * HTTP POST for sending and HTTP GET for receiving. <p/> You can specify the
042     * destination and whether it is a topic or queue via configuration details on
043     * the servlet or as request parameters. <p/> For reading messages you can
044     * specify a readTimeout parameter to determine how long the servlet should
045     * block for.
046     * 
047     * @version $Revision: 1.1.1.1 $
048     */
049    public class MessageServlet extends MessageServletSupport {
050        private static final Log log = LogFactory.getLog(MessageServlet.class);
051    
052        private String readTimeoutParameter = "readTimeout";
053        private long defaultReadTimeout = -1;
054        private long maximumReadTimeout = 1200;
055    
056        public void init() throws ServletException {
057            ServletConfig servletConfig = getServletConfig();
058            String name = servletConfig.getInitParameter("defaultReadTimeout");
059            if (name != null) {
060                defaultReadTimeout = asLong(name);
061            }
062            name = servletConfig.getInitParameter("maximumReadTimeout");
063            if (name != null) {
064                maximumReadTimeout = asLong(name);
065            }
066        }
067    
068        /**
069         * Sends a message to a destination
070         * 
071         * @param request
072         * @param response
073         * @throws ServletException
074         * @throws IOException
075         */
076        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
077                IOException {
078            // lets turn the HTTP post into a JMS Message
079            try {
080                WebClient client = getWebClient(request);
081    
082                String text = getPostedMessageBody(request);
083    
084                // lets create the destination from the URI?
085                Destination destination = getDestination(client, request);
086    
087                if (log.isInfoEnabled()) {
088                    log.info("Sending message to: " + ActiveMQDestination.inspect(destination) + " with text: " + text);
089                }
090    
091                TextMessage message = client.getSession().createTextMessage(text);
092                appendParametersToMessage(request, message);
093                client.send(destination, message);
094    
095                // lets return a unique URI for reliable messaging
096                response.setHeader("messageID", message.getJMSMessageID());
097                response.setStatus(HttpServletResponse.SC_OK);
098            }
099            catch (JMSException e) {
100                throw new ServletException("Could not post JMS message: " + e, e);
101            }
102        }
103    
104        /**
105         * Reads a message from a destination up to some specific timeout period
106         * 
107         * @param request
108         * @param response
109         * @throws ServletException
110         * @throws IOException
111         */
112        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
113            try {
114                WebClient client = getWebClient(request);
115    
116                // lets create the destination from the URI?
117                Destination destination = getDestination(client, request);
118    
119                long timeout = getReadTimeout(request);
120    
121                if (log.isInfoEnabled()) {
122                    log.info("Receiving message from: " + ActiveMQDestination.inspect(destination) + " with timeout: "
123                            + timeout);
124                }
125    
126                MessageConsumer consumer = client.getConsumer(destination);
127    
128                Message message = null;
129                // ensure that only one thread uses the consumer at once
130                synchronized (consumer) {
131                    if (timeout == 0) {
132                        message = consumer.receiveNoWait();
133                    }
134                    else {
135                        message = consumer.receive(timeout);
136                    }
137                }
138    
139                if (log.isInfoEnabled()) {
140                    log.info("HTTP GET servlet done! message: " + message);
141                }
142    
143                sendMessageResponse(request, response, message);
144            }
145            catch (JMSException e) {
146                throw new ServletException("Could not post JMS message: " + e, e);
147            }
148    
149        }
150    
151        /**
152         * Supports a HTTP DELETE to be equivlanent of consuming a message from a
153         * queue
154         */
155        protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException,
156                IOException {
157            try {
158                WebClient client = getWebClient(request);
159    
160                // lets create the destination from the URI?
161                Destination destination = getDestination(client, request);
162    
163                MessageConsumer consumer = client.getConsumer(destination);
164    
165                Message message = null;
166                // ensure that only one thread uses the consumer at once
167                synchronized (consumer) {
168                    message = consumer.receiveNoWait();
169                }
170    
171                sendMessageResponse(request, response, message);
172            }
173            catch (JMSException e) {
174                throw new ServletException("Could not post JMS message: " + e, e);
175            }
176        }
177    
178        protected void sendMessageResponse(HttpServletRequest request, HttpServletResponse response, Message message)
179                throws JMSException, IOException {
180            response.setContentType("text/xml");
181            boolean ajax = isRicoAjax(request);
182            PrintWriter writer = response.getWriter();
183            if (ajax) {
184                writer.print("<ajax-response><response type='object' id='");
185                writer.print(request.getParameter("id"));
186                writer.println("'>");
187            }
188            if (message == null) {
189                if (ajax) {
190                    writer.println("</response></ajax-response>");
191                    response.setStatus(HttpServletResponse.SC_OK);
192                }
193                else {
194                    response.setStatus(HttpServletResponse.SC_NO_CONTENT);
195                }
196            }
197            else {
198                String type = getContentType(request);
199                if (type != null) {
200                    response.setContentType(type);
201                }
202                setResponseHeaders(response, message);
203    
204                if (message instanceof TextMessage) {
205                    TextMessage textMsg = (TextMessage) message;
206                    writer.print(textMsg.getText());
207                }
208                else if (message instanceof ObjectMessage) {
209                    ObjectMessage objectMsg = (ObjectMessage) message;
210                    Object object = objectMsg.getObject();
211                    writer.print(object.toString());
212                }
213                if (ajax) {
214                    writer.println("</response></ajax-response>");
215                }
216                response.setStatus(HttpServletResponse.SC_OK);
217            }
218        }
219    
220        protected boolean isRicoAjax(HttpServletRequest request) {
221            String rico = request.getParameter("rico");
222            return rico != null && rico.equals("true");
223        }
224    
225        protected String getContentType(HttpServletRequest request) {
226            /*
227             * log("Params: " + request.getParameterMap()); Enumeration iter =
228             * request.getHeaderNames(); while (iter.hasMoreElements()) { String
229             * name = (String) iter.nextElement(); log("Header: " + name + " = " +
230             * request.getHeader(name)); }
231             */
232            String value = request.getParameter("xml");
233            if (value != null && "true".equalsIgnoreCase(value)) {
234                return "text/xml";
235            }
236            return null;
237        }
238    
239        protected void setResponseHeaders(HttpServletResponse response, Message message) throws JMSException {
240            response.setHeader("destination", message.getJMSDestination().toString());
241            response.setHeader("id", message.getJMSMessageID());
242        }
243    
244        /**
245         * @return the timeout value for read requests which is always >= 0 and <=
246         *         maximumReadTimeout to avoid DoS attacks
247         */
248        protected long getReadTimeout(HttpServletRequest request) {
249            long answer = defaultReadTimeout;
250    
251            String name = request.getParameter(readTimeoutParameter);
252            if (name != null) {
253                answer = asLong(name);
254            }
255            if (answer < 0 || answer > maximumReadTimeout) {
256                answer = maximumReadTimeout;
257            }
258            return answer;
259        }
260    
261    }