/** 
 *
 * Copyright 2004 Protique Ltd
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 * 
 **/

package org.activemq.web;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.message.ActiveMQDestination;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * A servlet for sending and receiving messages to/from JMS destinations
 * using HTTP POST for sending and HTTP GET for receiving.
 * <p/>
 * You can specify the destination and whether it is a topic or queue
 * via configuration details on the servlet or as request parameters.
 * <p/>
 * For reading messages you can specify a readTimeout parameter to determine
 * how long the servlet should block for.
 *
 * @version $Revision: 1.1.1.1 $
 */
public class MessageServlet extends MessageServletSupport {
    private static final Log log = LogFactory.getLog(MessageServlet.class);

    private String readTimeoutParameter = "readTimeout";
    private long defaultReadTimeout = -1;
    private long maximumReadTimeout = 30000;

    public void init() throws ServletException {
        ServletConfig servletConfig = getServletConfig();
        String name = servletConfig.getInitParameter("defaultReadTimeout");
        if (name != null) {
            defaultReadTimeout = asLong(name);
        }
        name = servletConfig.getInitParameter("maximumReadTimeout");
        if (name != null) {
            maximumReadTimeout = asLong(name);
        }
    }

    /**
     * Sends a message to a destination
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // lets turn the HTTP post into a JMS Message
        try {
            WebClient client = getWebClient(request);

            String text = getPostedMessageBody(request);

            // lets create the destination from the URI?
            Destination destination = getDestination(client, request);

            log.info("Sending message to: " + ActiveMQDestination.inspect(destination) + " with text: " + text);

            TextMessage message = client.getSession().createTextMessage(text);
            appendParametersToMessage(request, message);
            client.send(destination, message);

            // lets return a unique URI for reliable messaging
            response.setHeader("messageID", message.getJMSMessageID());
            response.setStatus(HttpServletResponse.SC_OK);
        }
        catch (JMSException e) {
            throw new ServletException("Could not post JMS message: " + e, e);
        }
    }

    /**
     * Reads a message from a destination up to some specific timeout period
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            WebClient client = getWebClient(request);

            // lets create the destination from the URI?
            Destination destination = getDestination(client, request);

            long timeout = getReadTimeout(request);

            log.info("Receiving message from: " + ActiveMQDestination.inspect(destination) + " with timeout: " + timeout);

            MessageConsumer consumer = client.getConsumer(destination);

            Message message = null;
            // ensure that only one thread uses the consumer at once
            synchronized (consumer) {
                if (timeout == 0) {
                    message = consumer.receiveNoWait();
                }
                else {
                    message = consumer.receive(timeout);
                }
            }

            log.info("HTTP GET servlet done! message: " + message);

            sendMessageResponse(request, response, message);
        }
        catch (JMSException e) {
            throw new ServletException("Could not post JMS message: " + e, e);
        }

    }

    /**
     * Supports a HTTP DELETE to be equivlanent of consuming a message from a queue
     */
    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            WebClient client = getWebClient(request);

            // lets create the destination from the URI?
            Destination destination = getDestination(client, request);

            MessageConsumer consumer = client.getConsumer(destination);

            Message message = null;
            // ensure that only one thread uses the consumer at once
            synchronized (consumer) {
                message = consumer.receiveNoWait();
            }

            sendMessageResponse(request, response, message);
        }
        catch (JMSException e) {
            throw new ServletException("Could not post JMS message: " + e, e);
        }
    }

    protected void sendMessageResponse(HttpServletRequest request, HttpServletResponse response, Message message) throws JMSException, IOException {
        if (message == null) {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        }
        else {
            String type = getContentType(request);
            if (type != null) {
                response.setContentType(type);
            }
            setResponseHeaders(response, message);
            if (message instanceof TextMessage) {
                TextMessage textMsg = (TextMessage) message;
                response.getWriter().print(textMsg.getText());
            }
            else if (message instanceof ObjectMessage) {
                ObjectMessage objectMsg = (ObjectMessage) message;
                Object object = objectMsg.getObject();
                response.getWriter().print(object.toString());
            }
            response.setStatus(HttpServletResponse.SC_OK);
        }
    }

    protected String getContentType(HttpServletRequest request) {
        /*
        log("Params: " + request.getParameterMap());
        Enumeration iter = request.getHeaderNames();
        while (iter.hasMoreElements()) {
            String name = (String) iter.nextElement();
            log("Header: " + name + " = " + request.getHeader(name));
        }
        */
        String value = request.getParameter("xml");
        if (value != null && "true".equalsIgnoreCase(value)) {
            return "text/xml";
        }
        return null;
    }

    protected void setResponseHeaders(HttpServletResponse response, Message message) throws JMSException {
        response.setHeader("destination", message.getJMSDestination().toString());
        response.setHeader("id", message.getJMSMessageID());
    }

    /**
     * @return the timeout value for read requests which is always >= 0
     *         and <= maximumReadTimeout to avoid DoS attacks
     */
    protected long getReadTimeout(HttpServletRequest request) {
        long answer = defaultReadTimeout;

        String name = request.getParameter(readTimeoutParameter);
        if (name != null) {
            answer = asLong(name);
        }
        if (answer < 0 || answer > maximumReadTimeout) {
            answer = maximumReadTimeout;
        }
        return answer;
    }

}
