/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation"
 *    must not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * Portions of this software are based upon public domain software
 * originally written at the National Center for Supercomputing Applications,
 * University of Illinois, Urbana-Champaign.
 */

package org.apache.james.transport.mailets;

import java.io.ByteArrayOutputStream ; 
import javax.mail.internet.InternetAddress ; 
import javax.mail.internet.MimeMessage ; 
import javax.mail.internet.MimePart ; 

import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPFault;
import org.apache.axis.server.AxisServer;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.utils.Messages;

import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;

/**
 * Simple Mailet wrapper for Apache Axis Web service engine.
 *
 */
public class Axis extends GenericMailet {

    /**
     * Set this mail to GHOST state, indicating that no further processing
     * should take place.
     *
     * @param mail the mail to process
     */
    public void service(Mail mail) {

        try {
            MimeMessage mimeMessage = mail.getMessage();
            if (mimeMessage != null) {
                log("Responding to message id: " + mimeMessage.getMessageID()) ; 
                AxisServer engine = getAxisServer() ; 

                // create and initialize a message context
                MessageContext msgContext = new MessageContext(engine);
                Message requestMsg;

                // buffers for the headers we care about
                StringBuffer soapAction = new StringBuffer();
                StringBuffer fileName = new StringBuffer();
                StringBuffer contentType = new StringBuffer();
                StringBuffer contentLocation = new StringBuffer();

                // This is an Axis message NOT a javamail message!
                Message responseMsg = null;
    
                try {
                    // parse headers info
                    contentType.append(mimeMessage.getContentType());
                    log("content type: " + contentType) ; 
                    contentLocation.append(mimeMessage.getContentID());
                    log("content location: " + contentLocation) ; 
                    String values[] = mimeMessage.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
                    log("soap action headers: " + java.util.Arrays.asList(values)) ; 
                    if (values != null)
                        soapAction.append(values[0]);

                    // TODO: Set message context properties for 
                    // path and jws class dir ? 

                    // this may be "" if either SOAPAction: "" or if no SOAPAction at all.
                    // for now, do not complain if no SOAPAction at all
                    // TODO: does not appear to work if not soap action! TS 09/06/2003
                    String soapActionString = soapAction.toString();
                    if (soapActionString != null) {
                        msgContext.setUseSOAPAction(true);
                        msgContext.setSOAPActionURI(soapActionString);

                        // Allow the SOAPAction to determine the service, if the service
                        // (a) has not already been determined, and (b) if a service matching
                        // the soap action has been deployed.
                        if (msgContext.getService() == null) {
                            log("Setting target service to: " + msgContext.getSOAPActionURI()) ; 
                            msgContext.setTargetService((String) msgContext.getSOAPActionURI());
                        } else { 
                            log("Service already set to: " + msgContext.getService()) ; 
                        } 
                    }
                    requestMsg = new Message(mimeMessage.getInputStream(), false,
                            contentType.toString(), contentLocation.toString());
                    msgContext.setRequestMessage(requestMsg);

                    // invoke the Axis engine
                    engine.invoke(msgContext);

                    // Retrieve the response from Axis
                    responseMsg = msgContext.getResponseMessage();
                    if (responseMsg == null) {
                        throw new AxisFault(Messages.getMessage("nullResponse00"));
                    }
                } catch (Exception e) {
                    log(Messages.getMessage("exception00"));
                    AxisFault af;
                    if (e instanceof AxisFault) {
                        af = (AxisFault) e;
                        log(Messages.getMessage("serverFault00"), af);
                    } else {
                        af = AxisFault.makeFault(e);
                    }

                    // There may be headers we want to preserve in the
                    // response message - so if it's there, just add the
                    // FaultElement to it.  Otherwise, make a new one.
                    responseMsg = msgContext.getResponseMessage();
                    if (responseMsg == null) {
                        responseMsg = new Message(af);
                    } else {
                        try {
                            SOAPEnvelope env = responseMsg.getSOAPEnvelope();
                            env.clearBody();
                            env.addBodyElement(new SOAPFault((AxisFault) e));
                        } catch (AxisFault fault) {
                            // Should never reach here!
                        }
                    }
                }

                // Get reply but do not 'reply to all' 
                MimeMessage mimeResponse = (MimeMessage) mimeMessage.reply(false) ; 
                if (mimeResponse == null) {
                    log("no reply message!") ; 
                }
                mimeResponse.setDisposition(MimePart.INLINE);

                ByteArrayOutputStream out = new ByteArrayOutputStream(8 * 1024);
                responseMsg.writeTo(out);
                mimeResponse.setContent(out.toString(), responseMsg.getContentType(msgContext.getSOAPConstants()));
                mimeResponse.setFrom(((InternetAddress) mimeMessage.getAllRecipients()[0]));

                // ask mailet to send response thru internal spooler
                log("Sending response with message id: " + mimeResponse.getMessageID()) ;
                getMailetContext().sendMail(mimeResponse);
            // TODO: Should this part of James admin? 
            /*if (msgContext.getProperty(msgContext.QUIT_REQUESTED) != null) {
                // why then, quit!
                try {
                    mailet.stop();
                } catch (Exception e) {
                }
            }*/
            } 
        } catch (Exception e) {
            // TODO: should wrap as axis fault in all cases i would have thought
            log("prob doing axis call:" + e.getClass().getName() + ":" + e.getMessage());
            log(Messages.getMessage("exception00"), e);
        }
        
        // remove message 
        mail.setState(Mail.GHOST);
    }

    /**
     * Return a string describing this mailet.
     *
     * @return a string describing this mailet
     */
    public String getMailetInfo() {
        return "Axis Mailet";
    }

    protected static synchronized AxisServer getAxisServer() {
        if (myAxisServer == null) {
            myAxisServer = new AxisServer();
        }
        // Explicit start seems to be unnecessary
        return myAxisServer;
    }

    /**
     * Stop this server.
     *
     * This will interrupt any pending accept().
     */
    public void destroy() {
        try {
            /* 
             * Close the server socket cleanly, but avoid fresh accepts while
             * the socket is closing.
             */
            stopped = true;
            log(Messages.getMessage("quit00", "AxisServer"));
            getAxisServer().stop() ; 
        } catch (Exception e) {
            log("Exception stopping embedded Axis server: " + e.getMessage(), e) ; 
        }
    }

    // are we stopped?
    // latch to true if stop() is called
    private boolean stopped = false;

    // Axis server (shared between instances)
    private static AxisServer myAxisServer ;


}


