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    
018    package org.apache.activemq.web;
019    
020    import java.io.BufferedReader;
021    import java.io.IOException;
022    import java.util.HashMap;
023    import java.util.Iterator;
024    import java.util.Map;
025    
026    import javax.jms.Destination;
027    import javax.jms.JMSException;
028    import javax.jms.TextMessage;
029    import javax.servlet.ServletConfig;
030    import javax.servlet.ServletException;
031    import javax.servlet.http.HttpServlet;
032    import javax.servlet.http.HttpServletRequest;
033    
034    import org.apache.activemq.command.ActiveMQDestination;
035    import org.apache.activemq.command.ActiveMQQueue;
036    import org.apache.activemq.command.ActiveMQTopic;
037    import org.slf4j.Logger;
038    import 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")
058    public 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 && contentType.toLowerCase().startsWith("text/xml")) {
345                // lets read the message body instead
346                BufferedReader reader = request.getReader();
347                StringBuffer buffer = new StringBuffer();
348                while (true) {
349                    String line = reader.readLine();
350                    if (line == null) {
351                        break;
352                    }
353                    buffer.append(line);
354                    buffer.append("\n");
355                }
356                return buffer.toString();
357            }
358            return answer;
359        }
360    
361        protected String getSelector(HttpServletRequest request) throws IOException {
362            return request.getHeader(WebClient.selectorName);
363        }
364    }