/* ============================================================================
 * ----------------------------------------------------------------------------
 *
 * Name:    JMSQueueAppender.java
 * Created: December 19, 2001, 9:18 AM
 * Author: Steve Ebersole
 *
 * Description:
 *      Implements a log4j Appender for logging to a JMS Queue.
 *
 * Dependencies:
 *
 * ----------------------------------------------------------------------------
 * Change Log:
 *
 * YYYY-MM-DD   Programmer's Name   Change Description
 * ----------   ------------------  -------------------------------------------
 * 2002-01-02   Steve Ebersole      Removed the property LocationInfo.  
 *      LocationInfo is now always serialized out to the JMS queue.
 * ----------------------------------------------------------------------------
 * 2002-01-21   Steve Ebersole      Added back the properties for the Initial
 *      Context Factory and the Provider Url, to facilitate connecting to other
 *      boxes
 * ============================================================================
 */
package com.vignette.it.apps.util;


import javax.jms.*;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import java.util.Hashtable;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;


public class JMSQueueAppender
extends org.apache.log4j.AppenderSkeleton
{
    QueueConnection conn;
    QueueSession sess;
    QueueSender sender;
    
    String queueBindingName;
    String cfBindingName;
    String contextFactory;
    String providerUrl;

    /** Creates a new instance of JMSQueueAppender */
    public JMSQueueAppender()
    {
        super();
    }


    public void setContextFactory( String contextFactory )
    {
        this.contextFactory = contextFactory;
    }


    public String getContextFactory()
    {
        return contextFactory;
    }


    public void setProviderUrl( String providerUrl )
    {
        this.providerUrl = providerUrl;
    }


    public String getProviderUrl()
    {
        return providerUrl;
    }


    public void setQueueConnectionFactoryBindingName( String cfBindingName )
    {
        this.cfBindingName = cfBindingName;
    }
    
    
    /**
     * Returns the value of the <b>QueueConnectionFactoryBindingName</b> option.
     */
    public String getQueueConnectionFactoryBindingName()
    {
        return cfBindingName;
    }
    
    
    /**
     * The <b>QueueBindingName</b> option takes a
     * string value. Its value will be used to lookup the appropriate
     * <code>javax.jms.Queue</code> from the JNDI context.
     */
    public void setQueueBindingName(String queueBindingName)
    {
        this.queueBindingName = queueBindingName;
    }


    /**
     * Returns the value of the <b>QueueBindingName</b> option.
     */
    public String getQueueBindingName()
    {
        return queueBindingName;
    }


    public boolean requiresLayout()
    {
        return false;
    }
    
    
    public synchronized void close()
    {
        if(this.closed)
            return;
        
        LogLog.debug("Closing appender ["+name+"].");
        this.closed = true;
        
        try
        {
            if(sess != null)
                sess.close();
            if(conn != null)
                conn.close();
        }
        catch(Exception e)
        {
            LogLog.error("Error while closing JMSQueueAppender ["+name+"].", e);
        }
        // Help garbage collection
        sender = null;
        sess = null;
        conn = null;
    }
    
    
    public void append(LoggingEvent event)
    {
        if(!checkEntryConditions())
        {
            return;
        }
        
        try
        {
            event.getLocationInformation();

            ObjectMessage msg = sess.createObjectMessage( event );
            sender.send( msg );
        }
        catch(Exception e)
        {
            errorHandler.error("Could not publish message in JMSQueueAppender ["+name+"].", e,
            ErrorCode.GENERIC_FAILURE);
        }
    }
    
    
    public void activateOptions()
    {
        QueueConnectionFactory cf;
        
        if (contextFactory==null)
            contextFactory = System.getProperty( Context.INITIAL_CONTEXT_FACTORY );
        if (providerUrl==null)
            providerUrl = System.getProperty( Context.PROVIDER_URL );

        try
        {
            Hashtable ht = new Hashtable();

            if (contextFactory!=null && providerUrl!=null)
            {
                ht.put( Context.INITIAL_CONTEXT_FACTORY, contextFactory );
                ht.put( Context.PROVIDER_URL, providerUrl );
            }

            Context ctx = new InitialContext( ht );
            cf = (QueueConnectionFactory)lookup( ctx, cfBindingName );
            conn = cf.createQueueConnection();
            conn.start();
            
            sess = conn.createQueueSession( false, Session.AUTO_ACKNOWLEDGE );
            Queue queue = (Queue)lookup( ctx, queueBindingName );
            sender = sess.createSender( queue );
            
            ctx.close();
        }
        catch( Exception e )
        {
            errorHandler.error("Error while activating options for appender named ["+name+
            "].", e, ErrorCode.GENERIC_FAILURE);
        }
    }
    
    
    protected Object lookup( Context ctx, String name )
    throws NamingException
    {
        try
        {
            return ctx.lookup( name );
        }
        catch( NameNotFoundException nnfe )
        {
            LogLog.error( "Unable to locate JNDI name [" + name + "]" );
            throw nnfe;
        }
    }
    
    
    protected boolean checkEntryConditions()
    {
        String fail = null;
        
        if (this.conn==null)
        {
            fail = "No QueueConnection";
        }
        else if (this.sess==null)
        {
            fail = "No QueueSession";
        }
        else if (this.sender==null)
        {
            fail = "No QueueSender";
        }
        
        if (fail != null)
        {
            errorHandler.error(fail + " for JMSQueueAppender named ["+name+"].");
            return false;
        }
        else
        {
            return true;
        }
    }
    
}
