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