001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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
018package org.apache.activemq.web;
019
020import java.io.BufferedReader;
021import java.io.IOException;
022import java.io.InputStreamReader;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.Map;
026
027import javax.jms.Destination;
028import javax.jms.JMSException;
029import javax.jms.TextMessage;
030import javax.servlet.ServletConfig;
031import javax.servlet.ServletException;
032import javax.servlet.http.HttpServlet;
033import javax.servlet.http.HttpServletRequest;
034
035import org.apache.activemq.command.ActiveMQDestination;
036import org.apache.activemq.command.ActiveMQQueue;
037import org.apache.activemq.command.ActiveMQTopic;
038import org.apache.commons.io.input.BoundedInputStream;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042/**
043 * A useful base class for any JMS related servlet; there are various ways to
044 * map JMS operations to web requests so we put most of the common behaviour in
045 * a reusable base class. This servlet can be configured with the following init
046 * parameters
047 * <dl>
048 * <dt>topic</dt>
049 * <dd>Set to 'true' if the servlet should default to using topics rather than
050 * channels</dd>
051 * <dt>destination</dt>
052 * <dd>The default destination to use if one is not specifiied</dd>
053 * <dt></dt>
054 * <dd></dd>
055 * </dl>
056 *
057 *
058 */
059@SuppressWarnings("serial")
060public abstract class MessageServletSupport extends HttpServlet {
061
062    private static final transient Logger LOG = LoggerFactory.getLogger(MessageServletSupport.class);
063    /**
064     * A configuration tag to specify the maximum message size (in bytes) for the servlet. The default
065     * is given by DEFAULT_MAX_MESSAGE_SIZE below.
066     */
067    private static final String MAX_MESSAGE_SIZE_TAG = "maxMessageSize";
068    private static final Long DEFAULT_MAX_MESSAGE_SIZE = 100000L;
069
070    private boolean defaultTopicFlag = true;
071    private Destination defaultDestination;
072    private String destinationParameter = "destination";
073    private String typeParameter = "type";
074    private String bodyParameter = "body";
075    private boolean defaultMessagePersistent = true;
076    private int defaultMessagePriority = 5;
077    private long defaultMessageTimeToLive;
078    private String destinationOptions;
079    private long maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
080
081    public void init(ServletConfig servletConfig) throws ServletException {
082        super.init(servletConfig);
083
084        destinationOptions = servletConfig.getInitParameter("destinationOptions");
085
086        String name = servletConfig.getInitParameter("topic");
087        if (name != null) {
088            defaultTopicFlag = asBoolean(name);
089        }
090
091        if (LOG.isDebugEnabled()) {
092            LOG.debug("Defaulting to use topics: " + defaultTopicFlag);
093        }
094        name = servletConfig.getInitParameter("destination");
095        if (name != null) {
096            if (defaultTopicFlag) {
097                defaultDestination = new ActiveMQTopic(name);
098            } else {
099                defaultDestination = new ActiveMQQueue(name);
100            }
101        }
102
103        String maxMessageSizeConfigured = servletConfig.getInitParameter(MAX_MESSAGE_SIZE_TAG);
104        if (maxMessageSizeConfigured != null) {
105            maxMessageSize = Long.parseLong(maxMessageSizeConfigured);
106        }
107
108        // lets check to see if there's a connection factory set
109        WebClient.initContext(getServletContext());
110    }
111
112    public static boolean asBoolean(String param) {
113        return asBoolean(param, false);
114    }
115
116    public static boolean asBoolean(String param, boolean defaultValue) {
117        if (param == null) {
118            return defaultValue;
119        } else {
120            return param.equalsIgnoreCase("true");
121        }
122    }
123
124    @SuppressWarnings({ "rawtypes", "unchecked" })
125    protected void appendParametersToMessage(HttpServletRequest request, TextMessage message) throws JMSException {
126        Map parameterMap = request.getParameterMap();
127        if (parameterMap == null) {
128            return;
129        }
130        Map parameters = new HashMap(parameterMap);
131        String correlationID = asString(parameters.remove("JMSCorrelationID"));
132        if (correlationID != null) {
133            message.setJMSCorrelationID(correlationID);
134        }
135        Long expiration = asLong(parameters.remove("JMSExpiration"));
136        if (expiration != null) {
137            message.setJMSExpiration(expiration.longValue());
138        }
139        Destination replyTo = asDestination(parameters.remove("JMSReplyTo"));
140        if (replyTo != null) {
141            message.setJMSReplyTo(replyTo);
142        }
143        String type = (String)asString(parameters.remove("JMSType"));
144        if (type != null) {
145            message.setJMSType(type);
146        }
147
148        for (Iterator iter = parameters.entrySet().iterator(); iter.hasNext();) {
149            Map.Entry entry = (Map.Entry)iter.next();
150            String name = (String)entry.getKey();
151            if (!destinationParameter.equals(name) && !typeParameter.equals(name) && !bodyParameter.equals(name) && !"JMSDeliveryMode".equals(name) && !"JMSPriority".equals(name)
152                && !"JMSTimeToLive".equals(name)) {
153                Object value = entry.getValue();
154                if (value instanceof Object[]) {
155                    Object[] array = (Object[])value;
156                    if (array.length == 1) {
157                        value = array[0];
158                    } else {
159                        LOG.warn("Can't use property: " + name + " which is of type: " + value.getClass().getName() + " value");
160                        value = null;
161                        int size = array.length;
162                        for (int i = 0; i < size; i++) {
163                            LOG.debug("value[" + i + "] = " + array[i]);
164                        }
165                    }
166                }
167                if (value != null) {
168                    message.setObjectProperty(name, value);
169                }
170            }
171        }
172    }
173
174    protected long getSendTimeToLive(HttpServletRequest request) {
175        String text = request.getParameter("JMSTimeToLive");
176        if (text != null) {
177            return asLong(text);
178        }
179        return defaultMessageTimeToLive;
180    }
181
182    protected int getSendPriority(HttpServletRequest request) {
183        String text = request.getParameter("JMSPriority");
184        if (text != null) {
185            return asInt(text);
186        }
187        return defaultMessagePriority;
188    }
189
190    protected boolean isSendPersistent(HttpServletRequest request) {
191        String text = request.getParameter("JMSDeliveryMode");
192        if (text != null) {
193            return text.trim().equalsIgnoreCase("persistent");
194        }
195        return defaultMessagePersistent;
196    }
197
198    protected boolean isSync(HttpServletRequest request) {
199        String text = request.getParameter("sync");
200        if (text != null) {
201            return true;
202        }
203        return false;
204    }
205
206    protected Destination asDestination(Object value) {
207        if (value instanceof Destination) {
208            return (Destination)value;
209        }
210        if (value instanceof String) {
211            String text = (String)value;
212            return ActiveMQDestination.createDestination(text, ActiveMQDestination.QUEUE_TYPE);
213        }
214        if (value instanceof String[]) {
215            String text = ((String[])value)[0];
216            if (text == null) {
217                return null;
218            }
219            return ActiveMQDestination.createDestination(text, ActiveMQDestination.QUEUE_TYPE);
220        }
221        return null;
222    }
223
224    protected Integer asInteger(Object value) {
225        if (value instanceof Integer) {
226            return (Integer)value;
227        }
228        if (value instanceof String) {
229            return Integer.valueOf((String)value);
230        }
231        if (value instanceof String[]) {
232            return Integer.valueOf(((String[])value)[0]);
233        }
234        return null;
235    }
236
237    protected Long asLong(Object value) {
238        if (value instanceof Long) {
239            return (Long)value;
240        }
241        if (value instanceof String) {
242            return Long.valueOf((String)value);
243        }
244        if (value instanceof String[]) {
245            return Long.valueOf(((String[])value)[0]);
246        }
247        return null;
248    }
249
250    protected long asLong(String name) {
251        return Long.parseLong(name);
252    }
253
254    protected int asInt(String name) {
255        return Integer.parseInt(name);
256    }
257
258    protected String asString(Object value) {
259        if (value instanceof String[]) {
260            return ((String[])value)[0];
261        }
262
263        if (value != null) {
264            return value.toString();
265        }
266
267        return null;
268    }
269
270    /**
271     * @return the destination to use for the current request
272     */
273    protected Destination getDestination(WebClient client, HttpServletRequest request) throws JMSException {
274        String destinationName = request.getParameter(destinationParameter);
275        if (destinationName == null  || destinationName.equals("")) {
276            if (defaultDestination == null) {
277                return getDestinationFromURI(client, request);
278            } else {
279                return defaultDestination;
280            }
281        }
282
283        return getDestination(client, request, destinationName);
284    }
285
286    /**
287     * @return the destination to use for the current request using the relative
288     *         URI from where this servlet was invoked as the destination name
289     */
290    protected Destination getDestinationFromURI(WebClient client, HttpServletRequest request) throws JMSException {
291        String uri = request.getPathInfo();
292        if (uri == null) {
293            return null;
294        }
295
296        // replace URI separator with JMS destination separator
297        if (uri.startsWith("/")) {
298            uri = uri.substring(1);
299            if (uri.length() == 0) {
300                return null;
301            }
302        }
303
304        uri = uri.replace('/', '.');
305        LOG.debug("destination uri=" + uri);
306        return getDestination(client, request, uri);
307    }
308
309    /**
310     * @return the Destination object for the given destination name
311     */
312    protected Destination getDestination(WebClient client, HttpServletRequest request, String destinationName) throws JMSException {
313
314        // TODO cache destinations ???
315
316        boolean isTopic = defaultTopicFlag;
317        if (destinationName.startsWith("topic://")) {
318            isTopic = true;
319        } else if (destinationName.startsWith("channel://") || destinationName.startsWith("queue://")) {
320            isTopic = false;
321        } else {
322            isTopic = isTopic(request);
323        }
324        if (destinationName.indexOf("://") != -1) {
325            destinationName = destinationName.substring(destinationName.indexOf("://") + 3);
326        }
327
328        if (destinationOptions != null) {
329            destinationName += "?" + destinationOptions;
330        }
331        LOG.debug(destinationName + " (" + (isTopic ? "topic" : "queue") + ")");
332        if (isTopic) {
333            return client.getSession().createTopic(destinationName);
334        } else {
335            return client.getSession().createQueue(destinationName);
336        }
337    }
338
339    /**
340     * @return true if the current request is for a topic destination, else
341     *         false if its for a queue
342     */
343    protected boolean isTopic(HttpServletRequest request) {
344        String typeText = request.getParameter(typeParameter);
345        if (typeText == null) {
346            return defaultTopicFlag;
347        }
348        return typeText.equalsIgnoreCase("topic");
349    }
350
351    /**
352     * @return the text that was posted to the servlet which is used as the body
353     *         of the message to be sent
354     */
355    protected String getPostedMessageBody(HttpServletRequest request) throws IOException {
356        String answer = request.getParameter(bodyParameter);
357        String contentType = request.getContentType();
358        if (answer == null && contentType != null) {
359            LOG.debug("Content-Type={}", contentType);
360            // lets read the message body instead
361            BoundedInputStream boundedInputStream = new BoundedInputStream(request.getInputStream(), maxMessageSize);
362            BufferedReader reader = new BufferedReader(new InputStreamReader(boundedInputStream));
363            StringBuilder buffer = new StringBuilder();
364            while (true) {
365                String line = reader.readLine();
366                if (line == null) {
367                    break;
368                }
369                buffer.append(line);
370                buffer.append("\n");
371            }
372            return buffer.toString();
373        }
374        return answer;
375    }
376
377    protected String getSelector(HttpServletRequest request) throws IOException {
378        return request.getHeader(WebClient.selectorName);
379    }
380}