/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas.jtests.beans.message;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.ejb.TimedObject;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.JMSException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.transaction.Transaction;
import javax.transaction.UserTransaction;

import org.ow2.jonas.lib.util.Log;
import org.objectweb.jonas.jtests.util.JBean;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;

/**
 * Common code for all Message Driven Beans
 * @author Philippe Durieux, Philippe Coq
 */
public abstract class Listener extends JBean implements MessageDrivenBean, MessageListener, TimedObject {
    protected static Logger logger = null;
    protected transient MessageDrivenContext mdbContext;
    protected transient MRecordHome arh = null;
    protected String myname;

    abstract protected String getMyDest();

    public static final int CREATE_TIMER_MIN = 1500;
    public static final int CREATE_TIMER_MAX = 1600;
    public static final int CREATE_P_TIMER_MIN = 25000;
    public static final int CREATE_P_TIMER_MAX = 26000;

    /**
     * Default constructor
     */
    public Listener() {
    }

    // ------------------------------------------------------------------
    // MessageDrivenBean implementation
    // ------------------------------------------------------------------

    /**
     * Set the associated context. The container call this method
     * after the instance creation.
     * The enterprise Bean instance should store the reference to the context
     * object in an instance variable.
     * This method is called with no transaction context.
     *
     * @param ctx A MessageDrivenContext interface for the instance.
     * @throws EJBException Thrown by the method to indicate a failure caused by
     * a system-level error.
     */

    public void setMessageDrivenContext(MessageDrivenContext ctx) {
        if (logger == null) {
            logger = Log.getLogger(Log.JONAS_TESTS_PREFIX);
        }
        logger.log(BasicLevel.DEBUG, "");
        mdbContext = ctx;
    }

    /**
     * A container invokes this method before it ends the life of the message-driven object.
     * This happens when a container decides to terminate the message-driven object.
     *
     * This method is called with no transaction context.
     *
     * @throws EJBException Thrown by the method to indicate a failure caused by
     * a system-level error.
     */
    public void ejbRemove() {
        logger.log(BasicLevel.DEBUG, "");
    }

    /**
     * The Message driven  bean must define an ejbCreate methods with no args.
     */
    public void ejbCreate() {
        logger.log(BasicLevel.DEBUG, "");

        // Get a ref on MRecordHome
        InitialContext ictx = null;
        try {
            ictx = new InitialContext();
            arh = (MRecordHome) PortableRemoteObject.narrow(ictx.lookup("messageMRecordECHome"), MRecordHome.class);
        } catch (NamingException e) {
            logger.log(BasicLevel.ERROR, "Listener : Cannot get messageMRecordHome:" + e);
        }

        // Accesses the bean env to get the bean name
        // This test also that we can access java:comp/env from here.
        try {
            myname = (String) ictx.lookup("java:comp/env/mdbname");
        } catch (NamingException e) {
            logger.log(BasicLevel.ERROR, "Listener : Cannot access java:comp/env/mdbname from ejbCreate:" + e);
        }
    }

    /**
     * @return current Transaction
     */
	public int getCurrentTransactionStatus() {
		int ret = -1;
		try {
			UserTransaction ut = getUserTransaction();
			ret = ut.getStatus();
		} catch (Exception e) {
			logger.log(BasicLevel.ERROR, "Cannot get the current transaction");
		}
		return ret;
	}

	/**
	 * onMessage method
	 */
    public void onMessage(Message message) {
        logger.log(BasicLevel.DEBUG, "");

        sleep(20);

        // Decode the message (MapMessage)
        String uuid = null;
        String dest = null;
        int value = 0;
        MapMessage msg = (MapMessage) message;
        try {
            uuid = msg.getString("Id");
            dest = msg.getString("Text");
            value = msg.getInt("Value");
        } catch (JMSException e) {
            logger.log(BasicLevel.ERROR, "Listener exception:" + e);
            return;
        }

        // Check destination
        if (! dest.equals(getMyDest())) {
            logger.log(BasicLevel.ERROR, "Bad destination: " + dest + ". Expected is " + getMyDest());
            return;
        }

        // Create a timer if required
        if (value >= CREATE_TIMER_MIN && value <= CREATE_TIMER_MAX) {
            TimerService timerservice = mdbContext.getTimerService();
            int dur = value - CREATE_TIMER_MIN;
            Info info = new Info(uuid, dest, value, myname);
            Timer mt = timerservice.createTimer(dur * 1000, dur * 1000, info);
            return;
        }

        // Create a persistent timer (time in minuts)
        if (value >= CREATE_P_TIMER_MIN && value <= CREATE_P_TIMER_MAX) {
            TimerService timerservice = mdbContext.getTimerService();
            int dur = value - CREATE_P_TIMER_MIN;
            Info info = new Info(uuid, dest, value, myname);
            Timer mt = timerservice.createTimer(dur * 1000 * 60, 0, info);
            return;
        }

        // Create a new Entity bean for this message
        // Check that transaction association did not change after create.
        // This should be better checked, but need to access the Transaction directly.
        try {
            int t1 = getCurrentTransactionStatus();
            arh.create(uuid, dest, value, myname);
            if (t1 != getCurrentTransactionStatus()) {
                logger.log(BasicLevel.ERROR, "Error in transaction association");
            }
        } catch (CreateException e) {
            logger.log(BasicLevel.ERROR, "Listener exception:" + e);
        } catch (RemoteException e) {
            logger.log(BasicLevel.ERROR, "Listener exception:" + e);
        }


    }

    // -----------------------------------------------------------
    // TimedObject implementation
    // -----------------------------------------------------------

    /**
     * A timer is expired.
     */
    public void ejbTimeout(Timer timer) {
        logger.log(BasicLevel.DEBUG, "");
        Info info = (Info) timer.getInfo();
        // Create a new Entity bean for this message
        try {
            MRecord mr = arh.findByPrimaryKey(new MRecordPK(info.uuid, info.ejbname));
            int cnt = mr.getCount();
            if (cnt <= 3) {
                mr.updateCount();
            } else {
                timer.cancel();
            }
        } catch (RemoteException e) {
            logger.log(BasicLevel.ERROR, "Listener exception :" + e);
        } catch (FinderException e) {
            try {
                arh.create(info.uuid, info.dest, info.value, info.ejbname);
            } catch (CreateException ee) {
                logger.log(BasicLevel.ERROR, "Listener exception:" + ee);
            } catch (RemoteException ee) {
                logger.log(BasicLevel.ERROR, "Listener exception on create:" + ee);
            }
        }

    }

}
