/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.net.axis.transport.mailto.server;

import javax.mail.Flags;
import javax.mail.Message;
import javax.mail.MessagingException;

import org.apache.axis.AxisEngine;
import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.server.AxisServer;
import org.jboss.net.axis.server.JMXEngineConfigurationFactory;
import org.jboss.net.axis.transport.mailto.server.MailTransportServiceMBean;
import org.jboss.net.axis.transport.mailto.AbstractMailTransportService;
import org.jboss.net.axis.transport.mailto.MailConstants;

/**
 * <dl>
 * <dt><b>Title: </b><dd>Mail Transport MBean</dd>
 * <p>
 * <dt><b>Description: </b>
 * <dd>
 * <p>This MBean is the service that polls the mail box (pop/imap) and processes any soap messages it finds there.
 * 
 * <p> To define a mail transport and the transport level handlers, the jboss-net.sar/server-config.wsdd file must be 
 * modified to include:
 * <pre>
 *   &lt;transport name="mail"&gt;
 *     &lt;requestFlow&gt;
 *       &lt;handler type="java:org.jboss.net.axis.transport.mail.server.handler.TargetServiceHandler"/&gt;
 *     &lt;/requestFlow&gt;
 *     &lt;responseFlow&gt;
 *       &lt;!-- response flow specific handlers can go here --&gt;
 *     &lt;/responseFlow&gt;
 *   &lt;/transport&gt;
 * </pre>
 * 
 * <p>To enable this service edit the jboss-net.sar/META-INF/jboss-service.xml file to include :
 * <pre>
 *   &lt;mbean code="org.jboss.net.axis.transport.mailto.server.MailTransportService" 
 *          name="jboss.net:service=MailTransport"&gt;
 *     &lt;depends&gt;jboss.net:service=Axis&lt;/depends&gt;
 *     &lt;attribute name="SessionName"&gt;java:/Mail&lt;/attribute&gt;
 *     &lt;attribute name="FolderName"&gt;INBOX&lt;/attribute&gt;
 *     &lt;attribute name="TransportName"&gt;mail&lt;/attribute&gt;
 *     &lt;attribute name="EngineName"&gt;jboss.net:service=Axis&lt;/attribute&gt;
 *     &lt;attribute name="DeleteMail"&gt;true&lt;/attribute&gt;
 *   &lt;/mbean&gt;
 * </pre>
 * 
 * <p> If you don't want to hammer on the pollMail button from the mbean interface (jmx|web-console) then it would be
 * best to set up a schedule to do it for you (again in the jboss-service.xml file:
 * <pre>
 *   &lt;mbean code="org.jboss.varia.scheduler.Scheduler" 
 *         name="jboss.net:service=Scheduler,name=MailTransport"&gt;
 *      &lt;attribute name="StartAtStartup"&gt;true&lt;/attribute&gt;
 *      &lt;attribute name="SchedulableMBean"&gt;jboss.net:service=MailTransport&lt;/attribute&gt;
 *      &lt;attribute name="SchedulableMBeanMethod"&gt;pollMail()&lt;/attribute&gt;
 *      &lt;attribute name="InitialStartDate"&gt;NOW&lt;/attribute&gt;
 *      &lt;attribute name="SchedulePeriod"&gt;30000&lt;/attribute&gt;
 *      &lt;attribute name="InitialRepetitions"&gt;-1&lt;/attribute&gt;
 *   &lt;/mbean&gt;
 * </pre>
 * 
 * <p> This transport assumes the use of <a href="http://ws.apache.org/ws-fx/addressing/">ws-addressing</a> 
 * handlers and as such has no provision for sending a response. The addressing handler will create a new call
 * with the response if there is a wsa:Reply-To header. Otherwise the response will disappear into the ether
 * with nary a log entry. 
 * 
 * </dd>
 * </dl>
 * @author <a href="mailto:jasone@greenrivercomputing.com">Jason Essington</a>
 * @version $Revision: 1.2 $
 *
 * @jboss.service
 *    servicefile="jboss"
 * 
 * @jmx.mbean
 *    name="jboss.net:service=MailTransport" 
 *    description="email transport for soap messages"
 *    extends="org.jboss.net.axis.transport.mailto.AbstractMailTransportServiceMBean"
 * 
 */
public class MailTransportService extends AbstractMailTransportService implements MailTransportServiceMBean,
   MailConstants
{

   // members

   private AxisServer server = null;

   private String transportName = "mailto";

   /**
    * @return The name this transport has been given.
    * 
    * @jmx.managed-attribute
    *    description="the name of the transport for email messages"
    *    value="mailto"
    */
   public String getTransportName()
   {
      return this.transportName;
   }

   /**
    * @param name The name this transport will be known as (smtp for instance).
    *  
    * @jmx.managed-attribute
    */
   public void setTransportName(String name)
   {
      this.transportName = name;
   }

   /** override AxisServlet.getEngine() in order to redirect to
    *  the corresponding AxisEngine.
    * 
    * This is taken pretty much verbatem from the AxisServiceServlet
    */
   public AxisServer getEngine() throws AxisFault
   {
      if (server == null)
      {
         try
         {
            server = JMXEngineConfigurationFactory.newJMXFactory(getEngineName()).getAxisServer();
         }
         catch (NullPointerException e)
         {
            throw new AxisFault("Could not access JMX configuration factory.", e);
         }
      }

      return server;
   }

   /* (non-Javadoc)
    * @see org.jboss.net.axis.transport.mail.AbstractMailTransportService#processMessages(javax.mail.Message[])
    */
   protected void processMessages(Message[] msgs)
   {
      // iterate over all the messages processing them in turn.
      for (int i = 0; i < msgs.length; i++)
      {
         processMessage(msgs[i]);

         /*
          * we will need to delete the request messages in all cases
          * otherwise the inbox will just pile up with the messages that could not
          * be processed.
          * 
          * if deleteMail is set to false then the server will respond to any valid messages repeatedly!
          * perhaps we should have a persistent way to remember which messages we have processed?
          */
         if (getDeleteMail())
         {
            try
            {
               msgs[i].setFlag(Flags.Flag.DELETED, true);
            }
            catch (MessagingException e1)
            {
               log.warn("Unable to flag the message for deletion.", e1);
            }
         }
      }
   }

   /**
    * Creates a message context that will be used while processing this request.
    * 
    * @param engine Server engine
    * @param msg Email message
    * @return
    * @throws AxisFault
    */
   protected MessageContext createMessageContext(AxisEngine engine, Message msg)
      throws AxisFault
   {
      MessageContext msgCtx = new MessageContext(engine);

      // set the transport
      msgCtx.setTransportName(transportName);

      // NOTE the axis servlet adds lot's more stuff than this to the message context, maybe we should too?
      return msgCtx;
   }

   /**
    * Serialize the email message body into a SOAPMessage.
    * @param mc MessageContext that will be used while processing this message
    * @param msg Email Message
    * @throws AxisFault if we need to abort this message (drop the context and delete the email message 
    * with out sending a response message.)
    */
   protected void processMessageBody(MessageContext mc, Message msg) throws AxisFault
   {
      String msgNS = null;
      try
      {
         org.apache.axis.Message soapMsg = new org.apache.axis.Message(msg.getInputStream(), false, msg
            .getContentType(), null);

         mc.setRequestMessage(soapMsg);
      }
      catch (Exception e)
      {
         log.warn("This message doesn't appear to be a SOAP Message,", e);
      }
   }

   /**
    * @param ctx MessageContext that is being used for processing this request
    * @param msg Email message that is theoretically holding a SOAP request.
    * @return The value of the first Message-ID header found. 
    */
   protected String processHeaders(MessageContext ctx, Message msg) throws AxisFault
   {
      // over ride this method if you need to do something funky with
      // the headers of the email message.
      String msgID = null;
      try
      {
         String[] msgIDs = msg.getHeader(HEADER_MESSAGE_ID);
         if (msgIDs != null && msgIDs.length > 0)
         {
            msgID = msgIDs[0];
         }
      } 
      catch (MessagingException e)
      {
         throw new AxisFault("Unable to process mail headers.", e);
      }
      return msgID;
   }
   
   /**
    * Does the actual work of making the incoming request suitable to send off to the axis engine.
    * 
    * @param msg incoming email message.
    */
   private void processMessage(Message msg)
   //private Message processMessage(Message msg)//, Session mail, Folder folder)
   {

      org.apache.axis.MessageContext sMsgCtx = null;
      org.apache.axis.Message sResMsg = null;

      try
      {
         // create a new soap message context
         sMsgCtx = createMessageContext(getEngine(), msg);

         // wsa can worry about the headers
         String msgID = processHeaders(sMsgCtx, msg);
         
         // decide if the body of our email is a soap envelope
         processMessageBody(sMsgCtx, msg);
         
         archiveMessage(msgID, sMsgCtx);

         // send it all off to the Axis Server
         if (log.isDebugEnabled())
            log.debug("Invoking Axis Server.");

         getEngine().invoke(sMsgCtx);

         if (log.isDebugEnabled())
            log.debug("Return from Axis Server.");

      }
      catch (AxisFault e)
      {
         // There is not much we can do here since we don't have the ability to send email
         // any faults that happened after engine.invoke should be handled by the addressingHandler's onFault() method.
         // any faults that happend prior to that will just be logged.
         log.fatal("The email message number "+msg.getMessageNumber()+" caused an AxisFault:",e);
      }
   }

   /**
    * Override this method if you need to store incoming messages.<br>
    * Note: If web service security handlers are in the handler chain,
    * the message will not have been verified/decrypted yet.
    * @param msgID
    * @param msgCtx
    */
   protected void archiveMessage(String msgID, MessageContext msgCtx)
   {
   }

}