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