/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2000-2009 Sun Microsystems, Inc. All rights reserved. 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You may
 * not use this file except in compliance with the License.  You can obtain
 * a copy of the License at https://mq.dev.java.net/LICENSE.txt.  See the
 * License for the specific language governing permissions and limitations
 * under the License.  When distributing the software, include this
 * License Header Notice in each file and include the License file at
 * mq/legal/LICENSE.txt.  Sun designates this particular file as subject to
 * the "Classpath" exception as provided by Sun in the GPL Version 2 section
 * of the License file that accompanied this code.  If applicable, add the
 * following below the License Header, with the fields enclosed by
 * brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or
 * GPL Version 2] license."  If you don't indicate a single choice of license,
 * a recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or  to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright holder. 
 */
package com.sun.messaging.ums.core;

import com.sun.messaging.ums.simple.SimpleMessage;
import com.sun.messaging.ums.simple.SimpleMessageFactory;
import com.sun.messaging.ums.common.Constants;
import com.sun.messaging.ums.provider.openmq.ProviderDestinationService;
import com.sun.messaging.ums.resources.UMSResources;
import com.sun.messaging.ums.readonly.DefaultReadOnlyService;
import com.sun.messaging.ums.readonly.ReadOnlyMessageFactory;
import com.sun.messaging.ums.readonly.ReadOnlyRequestMessage;
import com.sun.messaging.ums.readonly.ReadOnlyResponseMessage;
import com.sun.messaging.ums.service.DestinationService;
import com.sun.messaging.ums.service.BrokerInfoService;
import com.sun.messaging.ums.service.SOAP2JMSService;
import com.sun.messaging.ums.service.UMSServiceImpl;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

//import java.io.PrintWriter;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Enumeration;

import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;

//import javax.xml.messaging.ReqRespListener;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.SOAPConstants;

/**
 * 
 * The entry class for UMS services.
 * 
 * The configurations are defined in web.xml for thi servlet.
 * 
 * <servlet>
 *   <servlet-name>UMS</servlet-name>
 *   <servlet-class>
 *       com.sun.messaging.ums.core.UMSServlet
 *   </servlet-class>
 *	
 *   <!-- set openmq broker address -->
 *   <!--
 *	 <init-param>
 *     	<param-name>imqAddressList</param-name>
 *     	<param-value>localhost</param-value>
 *	 </init-param>
 *   -->
 *   
 *   
 *    <!-- provider registeration -->
 *    <!-- mom.provider.num-->
 *    
 *    <!--
 *    <init-param>
 *     	<param-name>mom.provider.1</param-name>
 *     	<param-value>jmsgrid</param-value>
 *	 </init-param>
 *    -->
 *    
 *    <!--
 *    <init-param>
 *     	<param-name>mom.provider.0</param-name>
 *     	<param-value>openmq</param-value>
 *	 </init-param>
 *    -->
 *    
 *    <!-- provider factory class registration, not used in first release -->
 *    <!--
 *    <init-param>
 *     	<param-name>mom.jmsgrid.providerFactory</param-name>
 *     	<param-value>com.sun.messaging.ums.jmsgrid.ProviderFactory</param-value>
 *	 </init-param>
 *    
 *    <init-param>
 *     	<param-name>mom.openmq.providerFactory</param-name>
 *     	<param-value>com.sun.messaging.ums.openmq.ProviderFactory</param-value>
 *	 </init-param>
 *    --> 
 * 
 *    <!-- jms grid daemon host configuration, not used in first release
 *    <init-param>
 *     	<param-name>grid.host</param-name>
 *     	<param-value>niagra2</param-value>
 *	 </init-param>
 *    -->
 *    
 *    <!-- authenticate with JMS server -->
 *    <!-- applications must provide user/pass for JMS server if set to true -->
 *    <init-param>
 *     	<param-name>ums.service.authenticate</param-name>
 *     	<param-value>false</param-value>
 *	 </init-param>
 *    
 *    <!-- applications must encode password with base64 encoding if set to true -->
 *    <init-param>
 *     	<param-name>ums.service.authenticate.basic</param-name>
 *     	<param-value>false</param-value>
 *	 </init-param>
 *    
 *    <!-- user name for UMS to authenticate with JMS Server -->
 *    <init-param>
 *     	<param-name>ums.user.name</param-name>
 *     	<param-value>guest</param-value>
 *	 </init-param>
 *    
 *    <!-- password for ums to authenticate with JMS server -->
 *    <init-param>
 *     	<param-name>ums.password</param-name>
 *     	<param-value>guest</param-value>
 *	 </init-param>
 *    
 * </servlet>
 * 
 * <!-- simple messaging service url -->
 *  <servlet-mapping>
 *   <servlet-name>UMS</servlet-name>
 *   <url-pattern>/simple</url-pattern>
 *  </servlet-mapping>
 * 
 * <!-- xml messaging service url -->
 * <servlet-mapping>
 *   <servlet-name>UMS</servlet-name>
 *   <url-pattern>/xml</url-pattern>
 * </servlet-mapping>
 * 
 * @author chiaming
 */
public class UMSServlet extends HttpServlet {

    /**
     * message factory to be used to construct soap message.
     */
    protected MessageFactory mfactory = null;

    private static Logger logger = UMSServiceImpl.logger;

    protected Properties props = null;

    /**
     * <p>define class name in the web.xml as follows:
     * <p> <init-param>
     * <p>  <param-name>mq.soap.service</param-name>
     * <p>  <param-value>MQSOAPService-CLASS-FULL-NAME</param-value>
     * <p> </init-param>
     *
     */
    //public static final String MQ_SERVICE_PARAM_NAME = "mq.soap.service";
    
    /**
     * default MQ SOAP service class name.
     */
    //public static final String MQ_SERVICE_DEFAULT_CLASSNAME = 
    //        "com.sun.messaging.ums.jms.SOAP2JMSService";

    /**
     * MQ SERVICE class name.
     */
    //protected String mqServiceClassName = null;

    /**
     * MQ SERVICE instance.
     */
    protected UMSService mqService = null;

    /**
     * Service Context instance.
     */
    protected ServiceContext serviceContext = null;
    
    /**
     * MQ JMS Service
     */
    protected UMSServiceImpl JMSService = null;
    
    /**
     * receive timeout for HTTP/GET JMS service.
     */
    static protected long receiveTimeout = 7000;
    
    /**
     * topic/queue domain
     */
    private static final String TOPIC_DOMAIN = "topic";
    
    private static final String QUEUE_DOMAIN = "queue";
    
    /**
     * service field string used in HTTP/GET response message
     */
    private static final String SERVICE = "service=";
    
    /**
     * message field string used in HTTP/GET response message
     */
    private static final String MESSAGE = "text=";
    
    /**
     * destination field string used in HTTP/GET response message
     */
    private static final String DESTINATION = "destination=";
    
    /**
     * domain field string used in HTTP/GET response message
     */
    private static final String DOMAIN = "domain=";
    
    /**
     * UUID/SID/CLIENTID used in HTTP/GET response message
     */
    private static final String UUID = "sid=";
    
    /**
     * Content-Length
     */
    private static final String CONTENT_LENGTH = "Content-Length";
    
    /**
     * content-type
     */
    private static final String CONTENT_TYPE = "Content-Type";
    
    /**
     * 
     */
    private static final String TEXT_XML = "text/xml";
     
    /**
     * authenticate
     */
    protected boolean shouldAuthenticate = false;
    
    /**
     * admin debug service.
     * this is sent from HTTP/GET with service=admin
     * and debug=true/false.
     */
    private static final String ADMIN_DEBUG = "debug";
    
    private static final String SIMPLE = "simple";
    
    /**
     * simple messaging response message header fields
     */
    private static final String UMS_SERVICE = "ums.service";
    private static final String UMS_MOM = "ums.mom";
    private static final String UMS_DESTINATION = "ums.destination";
    private static final String UMS_DOMAIN = "ums.domain";
    private static final String UMS_STATUS = "ums.status";
    
    /**
     * UTF-8
     */
    private static final String UTF8 = "UTF-8";
    
    /**
     * empty string
     */
    private static final String EMPTY_STRING = "";
    
    /**
     * utf-8 content type
     */
    private static final String PLAIN_TEXT_CONTENT_TYPE = "text/plain;charset=UTF-8";
    
    private static final String HTML_CONTENT_TYPE = "text/html;charset=UTF-8";
    
    private static final String XML_CONTENT_TYPE = "text/xml;charset=UTF-8";
    
    /**
     * read only service -- this is to be used to dispatch readonly request.
     */
    private DefaultReadOnlyService readOnlyService = null;
    
    /**
     * init message factory and soap intermediate object.
     */
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        /**
         * init logger.
         */
        initLogger();

        /**
         * init config params:
         * 1. get mq service class name.
         * 2. construct init properties object from servlet config object.
         */
        initParams (servletConfig);

        /**
         * init/construct message factory.
         */
        initMessageFactory();

        /**
         * construct/init/start mq service.
         */
        initService ();
        
    }

    /**
     * Init logger
     */
    protected void initLogger() {
        
        //logger = Logger.getLogger(UMSService.LOGGER_CAT_NAME);

        String msg = UMSResources.getResources().getKString(UMSResources.UMS_LOGGER_INIT, logger.getName());
        logger.info(msg);
        
        //logger.fine("**** servlet logger init: " + logger.getName() );
    }

    /**
     * 1. Get mq service class name.
     * 2. Convert servlet init parameters into Properties object.
     */
    protected void initParams (ServletConfig servletConfig) {

        /**
         * get MQ Service Class Name.
         */
        //mqServiceClassName =
        //servletConfig.getInitParameter(MQ_SERVICE_PARAM_NAME);
        
        //if (mqServiceClassName == null) {
        //    mqServiceClassName = MQ_SERVICE_DEFAULT_CLASSNAME;
        //}

        props = new Properties();
        Enumeration enum2 = servletConfig.getInitParameterNames();

        while ( enum2.hasMoreElements() ) {
            String pname = (String) enum2.nextElement();
            String pvalue = (String) servletConfig.getInitParameter(pname);

            props.put(pname, pvalue);
        }
        
        String msg = UMSResources.getResources().getKString(UMSResources.UMS_CONFIG_INIT);
        logger.info(msg);

    }

    /**
     * init message factory -- may be over ridden by sub class.
     */
    protected void
    initMessageFactory () throws ServletException {
        try {
            mfactory = MessageFactory.newInstance(SOAPConstants.DEFAULT_SOAP_PROTOCOL);
        } catch (Exception e) {
            throw new ServletException (e.getMessage(), e);
        }
    }

    /**
     * instantiate/init/start MQ SOAP Service.
     */
    protected void
    initService () throws ServletException {

        try {

            /**
             * construct mq service.
             */
            //mqService =
            //(UMSService) Class.forName(mqServiceClassName).newInstance();
            
            mqService = new SOAP2JMSService();
            
            /**
             * construct service context.
             */
            initServiceContext();

            /**
             * init mq service with service context.
             */
            this.mqService.init( serviceContext );

            /**
             * start mq service.
             */
            this.mqService.start();
            
            /**
             * JMSService for doGet
             */
            //if (mqService instanceof SOAP2JMSService) {
            this.JMSService = ((SOAP2JMSService) mqService).getJMSService();
            //}
            
            //make the JMSService available to ReadOnlyServices
            props.put(DefaultReadOnlyService.JMSSERVICE, this.JMSService);
            
            /**
             * receive timeout - default 7 seconds
             */
            String tmp = props.getProperty (Constants.IMQ_RECEIVE_TIMEOUT, Constants.IMQ_RECEIVE_TIMEOUT_DEFAULT_VALUE);
            if (tmp != null) {
                receiveTimeout = Long.parseLong(tmp);
            }
            
            String msg = UMSResources.getResources().getKString(UMSResources.UMS_DEFAULT_RECEIVE_TIMEOUT, receiveTimeout);
            logger.info(msg);
            //logger.info("default receive timeout=" + receiveTimeout + " milli seconds.");
            
            /**
             * authenticate
             */
            tmp = props.getProperty(Constants.JMS_AUTHENTICATE, "false");
            this.shouldAuthenticate = Boolean.valueOf(tmp).booleanValue();
            
            /**
             * destination service
             * 
             */
            DestinationService.init(props);
            
            /**
             * broker info service
             * 
             */
            BrokerInfoService.init(props);
            
            /**
             * init readonly service
             */
            readOnlyService = new DefaultReadOnlyService();
            readOnlyService.init(props);
            
            msg = UMSResources.getResources().getKString(UMSResources.UMS_SERVICE_STARTED);
            logger.info(msg);
            
        } catch (Exception e) {
            throw new ServletException (e.getMessage(), e);
        }
    }

    /**
     * init service context.  Sub class may over ride this method and
     * provide a customized service object.
     */
    protected void initServiceContext() throws ServletException {
        serviceContext = new UMSServiceContext (props);
    }
    
    public void
    doGet(HttpServletRequest req, HttpServletResponse resp)
                        throws ServletException, IOException {
        //this.doSimpleMessaging(req, resp);
        this.doReadOnlyService(req, resp);
    }
    
    /**
     * The features are not yet ready for public use yet.
     * XXX
     * @param req
     * @param resp
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     */
    private void doReadOnlyService (HttpServletRequest req, HttpServletResponse resp)
                        throws ServletException, IOException {
        
        int status = resp.SC_BAD_REQUEST;
        
        String service = req.getParameter(Constants.SERVICE_NAME);
        //String user = req.getParameter(Constants.USER);
        //String pass = req.getParameter(Constants.PASSWORD);
        
        String respMsg = null;
        
        if (UMSServiceImpl.getDebug()) {
            
            String remoteHost = req.getRemoteHost();
            int remotePort = req.getRemotePort();
            
            String requestURL = req.getRequestURL().toString();
            logger.info("requestURL=" + requestURL + ", client=" + remoteHost + ":" + remotePort + ", request service=" + service);
        }
        
        Map map = this.getHeadersAsMap(req);
        
        //use string[] to be consistent
        String[] ru = new String[1];
        ru[0] = req.getRequestURL().toString();
        map.put(DefaultReadOnlyService.REQUEST_URL, ru);
        
        try {
            
	        //respMsg = readOnlyService.request(map);
	        ReadOnlyRequestMessage rom = ReadOnlyMessageFactory.createRequestMessage(map, req.getInputStream());
	        ReadOnlyResponseMessage respm = readOnlyService.request(rom);
	        
	        respMsg = respm.getResponseMessage();
	        
	        //get status code from respm
	        status = respm.getStatusCode();
	        
	        resp.setStatus(status);
	
	        if (isHTML (respMsg)) {
	            resp.setHeader(CONTENT_TYPE, HTML_CONTENT_TYPE);
	        } else if (isXML (respMsg)) {
	            resp.setHeader(CONTENT_TYPE, XML_CONTENT_TYPE);
	        } else {
	            resp.setHeader(CONTENT_TYPE, PLAIN_TEXT_CONTENT_TYPE);
	        }
	        
	        byte[] data = null;
	
	        if (respMsg != null) {
	            data = respMsg.getBytes(UTF8);
	        } else {
	            data = EMPTY_STRING.getBytes(UTF8);
	        }
	
	        DataOutputStream dos = new DataOutputStream(resp.getOutputStream());
	        
	        resp.setContentLength(data.length);
	
	        dos.write(data, 0, data.length);
	
	        dos.flush();
	        dos.close();
        
        } catch (Exception e) {

            logger.log(Level.WARNING, e.getMessage(), e);

            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resp.setHeader(CONTENT_TYPE, PLAIN_TEXT_CONTENT_TYPE);
           
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            
            PrintStream ps = new PrintStream (baos);
            ps.println(e);

            // don't print a stack trace: too much information
            //e.printStackTrace(ps);
            
            byte[] data = baos.toString().getBytes(UTF8);
            
            DataOutputStream dos = new DataOutputStream(resp.getOutputStream());

            resp.setContentLength(data.length);

            dos.write(data, 0, data.length);

            dos.flush();
            dos.close();
            resp.flushBuffer();
        
        }
    }
    
    public void
    doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
        
        String uri = req.getServletPath();
        
        if (UMSServiceImpl.debug) {
            
            String remoteHost = req.getRemoteHost();
            int remotePort = req.getRemotePort();
            
            String requestURL = req.getRequestURL().toString();
            logger.info("requestURL=" + requestURL + ", client=" + remoteHost + ":" + remotePort);
        }
        
        if ( uri != null && uri.indexOf(SIMPLE) > 0 ) {
            doSimpleMessaging (req, resp);
            //doTest(req, resp);    
        } else {
            doXmlMessaging (req, resp);
        }
        
    }
    
    /**
     * Send/Receive a simple text message.  Default domain is queue.  To specify 
     * domain, add domain=queue/topic in the query string.
     * 
     * Example:
     * 
     * http://host:port/xmlprotocol/service?clientId=chiaming&service=receive&destination=testQ
     * http://host:port/xmlprotocol/service?clientId=chiaming&service=send&destination=testQ&text=myMessage 
     * 
     * @param req
     * @param resp
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     */
    public void doSimpleMessaging(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String respMsg = null;

        int status = resp.SC_BAD_REQUEST;

        boolean isSend = false;
        boolean isReceive = false;
        boolean isAdmin = false;
        boolean isLogin = false;
        boolean isClose = false;
        boolean isCommit = false;
        boolean isRollback = false;

        boolean isValidRequest = true;

        //String separator = ",";

        Map map = this.getHeadersAsMap(req);

        //String text = this.readHttpBody(req);
        InputStream in = req.getInputStream();

        SimpleMessage msg = SimpleMessageFactory.createMessage(map, in);

        in.close();

        //String text = msg.getText();

        String destName = msg.getMessageProperty(Constants.DESTINATION_NAME);
        boolean isTopic = msg.isTopicDomain();

        String clientId = msg.getMessageProperty(Constants.CLIENT_ID);

        if (UMSServiceImpl.debug) {
            logger.info("Simple messaging sid=" + clientId);
        }

        if (msg.isSendService()) {
            isSend = true;
        } else if (msg.isReceiveService()) {
            isReceive = true;
        } else if (msg.isLoginService()) {
            isLogin = true;
        } else if (msg.isCloseService()) {
            isClose = true;
        } else if (msg.isAdminService()) {
            isAdmin = true;
        } else if (msg.isCommitService()) {
            isCommit = true;
        } else if (msg.isRollbackService()) {
            isRollback = true;
        } else {
            isValidRequest = false;
        }

        try {

            //logger.info("request servlet path=" + req.getServletPath());

            if (isValidRequest == false) {

                status = resp.SC_BAD_REQUEST;
                //throw exception   
                respMsg = "Invalid query string., see http://host:port/<context>/";

            } else {

                if (isSend) {

                    //String text = req.getParameter(Constants.HTTP_GET_SEND_TEXT);

                    String text = msg.getText();

                    if (UMSServiceImpl.debug) {
                        logger.info("Simple messaging, sending text=" + text);
                    }

                    this.JMSService.sendText(clientId, isTopic, destName, text, map);

                    resp.setHeader(UMS_SERVICE, Constants.SERVICE_VALUE_SEND_MESSAGE_REPLY);
                    resp.setHeader(UMS_DESTINATION, destName);
                    resp.setHeader(UMS_DOMAIN, getDomain(isTopic));

                    resp.setHeader(UMS_MOM, JMSService.getProvider(map));
                    resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_OK);

                } else if (isReceive) {

                    long timeout = this.getServiceTimeout(msg);

                    //receive message
                    String text = this.JMSService.receiveText(clientId, destName, isTopic, timeout, map);

                    resp.setHeader(UMS_SERVICE, Constants.SERVICE_VALUE_RECEIVE_MESSAGE_REPLY);
                    resp.setHeader(UMS_DESTINATION, destName);
                    resp.setHeader(UMS_DOMAIN, getDomain(isTopic));

                    resp.setHeader(UMS_MOM, JMSService.getProvider(map));

                    respMsg = text;

                    if (respMsg == null) {
                        //respMsg = "null";
                        resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_NO_MESSAGE);
                    } else {
                        resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_OK);
                    }

                } else if (isLogin) {

                    String sid = this.JMSService.authenticate(map);

                    resp.setHeader(UMS_SERVICE, Constants.SERVICE_VALUE_LOGIN_REPLY);

                    resp.setHeader(UMS_MOM, JMSService.getProvider(map));
                    resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_OK);

                    respMsg = sid;
                } else if (isClose) {

                    String sid = this.JMSService.closeClient2(map);

                    resp.setHeader(UMS_SERVICE, Constants.SERVICE_VALUE_CLOSE_REPLY);

                    resp.setHeader(UMS_MOM, JMSService.getProvider(map));
                    resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_OK);

                } else if (isCommit) {

                    this.JMSService.commit(msg);

                    resp.setHeader(UMS_SERVICE, Constants.SERVICE_VALUE_COMMIT_REPLY);

                    resp.setHeader(UMS_MOM, JMSService.getProvider(map));
                    resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_OK);
                } else if (isRollback) {

                    this.JMSService.rollback(msg);

                    resp.setHeader(UMS_SERVICE, Constants.SERVICE_VALUE_ROLLBACK_REPLY);

                    resp.setHeader(UMS_MOM, JMSService.getProvider(map));

                    resp.setHeader(UMS_STATUS, Constants.SERVICE_STATUS_VALUE_OK);

                } else if (isAdmin) {

                    String user = req.getParameter(Constants.USER);
                    String pass = req.getParameter(Constants.PASSWORD);
                    ProviderDestinationService pds = DestinationService.getProviderDestinationService(null);

                    pds.authenticate(user, pass);

                    String flag = req.getParameter(ADMIN_DEBUG);

                    boolean debug = Boolean.valueOf(flag).booleanValue();

                    UMSServiceImpl.debug = debug;

                    respMsg = SERVICE + "admin, " + ADMIN_DEBUG + "=" + debug;
                }

                status = resp.SC_OK;
            }

            resp.setStatus(status);

            //resp.setHeader("Content-Type", "text/plain;charset=UTF-8");
            resp.setHeader(CONTENT_TYPE, PLAIN_TEXT_CONTENT_TYPE);
            //resp.setCharacterEncoding("UTF-8");

            byte[] data = null;

            if (respMsg != null) {
                data = respMsg.getBytes(UTF8);
            } else {
                //data = "".getBytes(UTF8);
                data = EMPTY_STRING.getBytes(UTF8);
            }

            DataOutputStream dos = new DataOutputStream(resp.getOutputStream());


            resp.setContentLength(data.length);

            dos.write(data, 0, data.length);

            //dos.writeUTF(respMsg);

            dos.flush();
            dos.close();

        } catch (Exception e) {

            logger.log(Level.WARNING, e.getMessage(), e);

            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resp.setHeader(CONTENT_TYPE, PLAIN_TEXT_CONTENT_TYPE);
           
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            
            PrintStream ps = new PrintStream (baos);
            e.printStackTrace(ps);
            byte[] data = baos.toString().getBytes(UTF8);
            
            DataOutputStream dos = new DataOutputStream(resp.getOutputStream());

            resp.setContentLength(data.length);

            dos.write(data, 0, data.length);

            dos.flush();
            dos.close();
            resp.flushBuffer();
        }

    }

    /**
    * Http request services.  HTTP requests are transformed into
    * SOAP messages and dispated to the appropriate services.
    */
   public void
   doXmlMessaging (HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {

       SOAPMessage msg = null;

       try {
           // Get all the headers from the HTTP request.
           MimeHeaders headers = getHeaders(req);

           // Get the body of the HTTP request.
           InputStream is = req.getInputStream();

           // Now internalize the contents of a HTTP request and
           // create a SOAPMessage
           msg = mfactory.createMessage(headers, is);

           SOAPMessage reply = null;

           /**
            * Dispatch message to mq soap service.
            */
           reply = onMessage (msg);
           
           //System.out.println ("@@@@@@ in doPost() \n");
           //reply.writeTo(System.out);
                   

           if ( reply != null ) {

               // Need to saveChanges 'cos we're going to use the
               // MimeHeaders to set HTTP response information. These
               // MimeHeaders are generated as part of the save.

               if (reply.saveRequired()) {
                   reply.saveChanges();
               }

               if ( isSOAPFault (reply) ) {
                   resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
               } else {
                   resp.setStatus(HttpServletResponse.SC_OK);
               }

               //remove content-length header field
               reply.getMimeHeaders().removeHeader(CONTENT_LENGTH);
               
               putHeaders (reply.getMimeHeaders(), resp);
               
               String[] sa = reply.getMimeHeaders().getHeader(CONTENT_TYPE);
               if ( sa == null || sa.length == 0 ) {
            	   //XXX: 
            	   //int acount = reply.countAttachments();
            	   resp.setHeader (CONTENT_TYPE, TEXT_XML);
               }
               
               // Write out the message on the response stream.
               OutputStream os = resp.getOutputStream();
               reply.writeTo(os);

               os.flush();

           } else {
               resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
           }
       } catch (Throwable ex) {
           /**
            * Log exception.
            */
           logger.log(Level.WARNING, ex.getMessage(), ex);

           resp.setStatus (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
       }
   }

   /**
     * Returns a <code>MimeHeaders</code> object that contains the headers
     * in the given <code>HttpServletRequest</code> object.
     *
     * @param req the <code>HttpServletRequest</code> object that a
     *        messaging provider sent to the servlet
     * @return a new <code>MimeHeaders</code> object containing the headers
     *         in the message sent to the servlet
     */
    protected static MimeHeaders getHeaders(HttpServletRequest req) {
        Enumeration enum2 = req.getHeaderNames();
        MimeHeaders headers = new MimeHeaders();

        while (enum2.hasMoreElements()) {
            String headerName = (String)enum2.nextElement();
            String headerValue = req.getHeader(headerName);

            StringTokenizer values = new StringTokenizer(headerValue, ",");
            while (values.hasMoreTokens())
                headers.addHeader(headerName, values.nextToken().trim());
        }

        return headers;
    }
    
    
    private void parseQueryString (Map map, String querystr) {
                
        String[] values = querystr.split("&");
        
        for (int i=0; i<values.length; i++) {
              
            //String[] keyValuePair = values[i].split("=");
            
            //String key = keyValuePair[0];
            
            //value as array -- to be consisitent with the rest of http headers
            //in the map.
            //String[] value = new String[1];
            //value[0] = keyValuePair[1];
            
            int index = values[i].indexOf('=');
            String key = values[i].substring(0, index);
            
            String[] value = new String[1];
            value[0] = values[i].substring(index+1);
            
            map.put(key, value);
            
            if (UMSServiceImpl.debug) {
                logger.info ("Adding query string param, " + key+"="+key + ", value[0]=" + value[0]);
            }
        }
                
    }
    
    protected Map getHeadersAsMap (HttpServletRequest req) {
        
        Hashtable<String, String[]> headers = new Hashtable <String, String[]> ();
        
        String querystr = req.getQueryString();
        
        if (querystr != null) {
            parseQueryString (headers, querystr);
        }
        
        Enumeration enum2 = req.getHeaderNames();
        

        while (enum2.hasMoreElements()) {
            String headerName = (String)enum2.nextElement();
            String headerValue = req.getHeader(headerName);

            ArrayList <String> alist = new ArrayList<String>();
            
            StringTokenizer values = new StringTokenizer(headerValue, ",");
            while (values.hasMoreTokens()) {
                //headers.addHeader(headerName, values.nextToken().trim());
                String hvtrim = values.nextToken().trim();
                alist.add(hvtrim);
                
                if (UMSServiceImpl.debug) {
                    logger.info ("Adding http header to map, " + headerName +"=" + hvtrim);
                }
            }
            
            String[] sarray = new String [alist.size()];
            
            alist.toArray(sarray);
            
            headers.put (headerName, sarray);
            
            //logger.info ("**** header in map, header=" + headerName + ", value=" + sarray);
        }

        return headers;
    }
    
     protected Map getRequestQueryAsMap_deprecated (HttpServletRequest req) {
         
         Hashtable <String, String> map = new Hashtable <String, String>();
         
         String qstr = req.getQueryString();
         
         StringTokenizer tokens = new StringTokenizer (qstr, " ");
         
         while (tokens.hasMoreTokens()) {
             
             String pair = tokens.nextToken();
            
         }
         
         return map;
     }
    
    protected String readHttpBody(HttpServletRequest req)
            throws ServletException, IOException {

        String text = null;

        InputStream in = req.getInputStream();
        DataInputStream din = new DataInputStream(in);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        byte[] bytes = new byte[1024];

        boolean more = true;
        int len = 0;

        while (more) {

            len = din.read(bytes);
            
            if (UMSServiceImpl.debug) {
                logger.info("Simple messaging, reading message body ... len=" + len);
            }
            
            if (len > 0) {
                baos.write(bytes, 0, len);
            } else if (len < 0) {
                more = false;
            }
        }

        byte[] body = baos.toByteArray();

        String enc = req.getCharacterEncoding();
        if (enc == null) {
            enc = UTF8;
        }
        
        baos.close();
        din.close();

        text = new String(body, enc);
        return text;
    }

    /**
     * Sets the given <code>HttpServletResponse</code> object with the
     * headers in the given <code>MimeHeaders</code> object.
     *
     * @param headers the <code>MimeHeaders</code> object containing the
     *        the headers in the message sent to the servlet
     * @param res the <code>HttpServletResponse</code> object to which the
     *        headers are to be written
     * @see #getHeaders
     */
    protected static void
    putHeaders(MimeHeaders headers, HttpServletResponse res) {
         
        Iterator it = headers.getAllHeaders();
        
        while (it.hasNext()) {
            MimeHeader header = (MimeHeader)it.next();

            String[] values = headers.getHeader(header.getName());
            
            if (values.length == 1) {
                
                res.setHeader(header.getName(), header.getValue());
            } else {
                StringBuffer concat = new StringBuffer();
                int i = 0;

                while (i < values.length) {
                    if (i != 0) {
                        concat.append(',');
                    }
                    concat.append(values[i++]);
                }
                
                res.setHeader(header.getName(), concat.toString());
                  
            }
        }
    }

    /**
     * check if this is a soap fault message.
     */
    protected boolean isSOAPFault (SOAPMessage message) throws SOAPException {
        SOAPBody body = message.getSOAPPart().getEnvelope().getBody();
        return body.hasFault();
    }

    /**
     * Message delivered from doPost().  Subclass can override this method
     * or define mq.soap.service property in web.xml file.
     */
    public SOAPMessage onMessage ( SOAPMessage message ) {
        SOAPMessage reply = null;

        try {
            reply = mqService.onMessage(message);
        } catch (Throwable e) {
            //this should not happen.  the exception is handled in onMessage()
            //and doPost().
            log (e.getMessage(), e);
        }

        return reply;
    }

    /**
     * destroy this servlet.
     */
    public void destroy() {
        mqService.close();
    }
    
    /**
     * Send/Receive a simple text message.  Default domain is queue.  To specify 
     * domain, add domain=queue/topic in the query string.
     * 
     * Example:
     * 
     * http://host:port/xmlprotocol/service?clientId=chiaming&service=receive&destination=testQ
     * http://host:port/xmlprotocol/service?clientId=chiaming&service=send&destination=testQ&text=myMessage 
     * 
     * @param req
     * @param resp
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     */
    
    /**
    public void
    doGetOld(HttpServletRequest req, HttpServletResponse resp)
                        throws ServletException, IOException {
        
        String respMsg = null;
        
        int status = resp.SC_BAD_REQUEST;
        
        boolean isSend = false;
        boolean isReceive = false;
        boolean isAdmin = false;
        boolean isLogin = false;
        boolean isClose = false;
        
        boolean isValidRequest = true;
        
        boolean isTopic = false;
        
        String separator = ",";
        
        //check the query string
        String clientId = req.getParameter(Constants.CLIENT_ID);
        String domain = req.getParameter(Constants.DOMAIN);
        if (Constants.TOPIC_DOMAIN.equals(domain)) {
            isTopic = true;
        }
        
        String destName = req.getParameter(Constants.DESTINATION_NAME);
        
        String service = req.getParameter(Constants.SERVICE_NAME);
        
        if (Constants.SERVICE_VALUE_SEND_MESSAGE.equals(service)) {
            isSend = true;
        } else if (Constants.SERVICE_VALUE_RECEIVE_MESSAGE.equals(service)) {
            isReceive = true;
        } else if (Constants.SERVICE_VALUE_LOGIN.equals(service)) {
            isLogin = true;
        } else if (Constants.SERVICE_VALUE_CLOSE.equals(service)) {
            isClose = true;
        } else if ("admin".equals(service)) {
            isAdmin = true;
        } else {
            isValidRequest = false;
        }
       
        try {
            
            //logger.info("request servlet path=" + req.getServletPath());
        
        if (isValidRequest == false) {
            
           status = resp.SC_BAD_REQUEST; 
          //throw exception   
           respMsg = "Invalid query string., Usage: http://hostport/service?clientId=ID&destination=destName";
              
        } else {
            
            Map map = req.getParameterMap();
            
            separator = req.getParameter(Constants.HTTP_GET_RESPONSE_SEPARATOR);
            if (separator ==null) {
                separator = Constants.HTTP_GET_RESPONSE_SEPARATOR_DEFAULT_VALUE;
            }
        
            if (isSend) {
                
                String text = req.getParameter(Constants.HTTP_GET_SEND_TEXT);
                
                this.JMSService.sendText(clientId, isTopic, destName, text, map);
                
                respMsg = SERVICE + service + separator + DESTINATION + 
                        destName + separator + DOMAIN + getDomain (isTopic) + separator + MESSAGE + text;
                
            } else if (isReceive) {
                
                long timeout = this.getServiceTimeout(req);
                
                //receive message
                String text = this.JMSService.receiveText(clientId, destName, isTopic, timeout, map);
                
                respMsg = SERVICE + service + separator + DESTINATION + destName + 
                        separator + DOMAIN + getDomain (isTopic) + separator + MESSAGE + text;
                
            } else if (isLogin) {
                
                String uuid = this.JMSService.authenticate(map);
                
                respMsg = SERVICE + service + separator + UUID + uuid;
            } else if (isClose) {
                
                String sid = this.JMSService.closeClient2 (map);
                
                respMsg = SERVICE + service + separator + UUID + sid;
                
            } else if (isAdmin) {
                
                String flag = req.getParameter(ADMIN_DEBUG);
                
                boolean debug = Boolean.valueOf(flag).booleanValue();
                
                MQServiceImpl.debug = debug;
            
                respMsg = SERVICE + service + separator + ADMIN_DEBUG + "=" + debug;
            }
            
            status = resp.SC_OK;
        }
        
        } catch (Exception e) {
            
            throw new ServletException (e);
                  
        }
        
        resp.setStatus(status);
        
        resp.setHeader ("Content-Type", "text/plain");

        OutputStream os = resp.getOutputStream();
        
        PrintWriter pw = new PrintWriter (os);
           
        pw.println(respMsg);
        
        pw.close();
    }
    **/
    
    protected static long getServiceTimeout (SimpleMessage msg) {
        
        long timeout = receiveTimeout;
        
        try {
            
            String str = msg.getMessageProperty (Constants.RECEIVE_TIMEOUT);
            
            if (str != null) {
                
                timeout = Long.parseLong(str);
            
                if (timeout <=0 || timeout > 60000) {
                    timeout = receiveTimeout;
                }
            }
            
        } catch (Exception e) {
            logger.log(Level.WARNING, e.getMessage(), e);
        }
        
        return timeout;
    }
    
    private String getDomain (boolean isTopic) {
        if (isTopic) {
            return TOPIC_DOMAIN;
            
        } else {
            return QUEUE_DOMAIN;
        }
    }
    
    protected static boolean isHTML (String msg) {
        boolean flag = false;
        
        if (msg == null || msg.length()==0) {
            return false;
        }
        
        String prefix = msg.substring(0, 5);
        if ("<html".equalsIgnoreCase(prefix) == true) {
            flag = true;
        }
        
        return flag;
    }
    
    protected static boolean isXML (String msg) {
        boolean flag = false;
        
        if (msg == null || msg.length()==0) {
            return false;
        }
        
        String prefix = msg.substring(0, 5);
        if ("<?xml".equalsIgnoreCase(prefix) == true) {
            flag = true;
        }
        
        return flag;
    }

}
