/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2007 Bull S.A.S.
 * Contact: jonas-team@ow2.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.ow2.jonas.lib.ejb21;

import java.io.Serializable;

import javax.ejb.EJBException;
import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.Timer;
import javax.ejb.TimerHandle;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.ow2.jonas.ejb2.JTimerHandleInfo;
import org.ow2.jonas.ejb2.TimerHandleDelegate;
import org.ow2.jonas.lib.naming.SingletonNamingManager;
import org.ow2.jonas.lib.timer.TraceTimer;
import org.ow2.jonas.naming.JNamingManager;

import org.objectweb.util.monolog.api.BasicLevel;

/**
 * TimerHandle implementation
 */
public class JTimerHandle implements TimerHandle {

    /**
     * Datas used to retrieve the Timer.
     */
    private JTimerHandleInfo infos = null;

    /**
     * Delegate used to retrieve the TimerService via JNDI.
     */
    private transient TimerHandleDelegate delegate = null;

    /**
     * Constructor
     */
    public JTimerHandle(long starttime, long duration, long period, Serializable info, String beanname, String container, Serializable pk) {
        if (TraceTimer.isDebug()) {
            TraceTimer.logger.log(BasicLevel.DEBUG, "New JTimerHandle start = " + starttime + ", initial = " + duration + ", period = " + period);
        }
        // Populate the Info structure
        this.infos = new JTimerHandleInfo();
        infos.setContainerId(container);
        infos.setBeanId(beanname);
        infos.setDuration(duration);
        infos.setInfo(info);
        infos.setPeriod(period);
        infos.setPk(pk);
        infos.setStartTime(starttime);
    }

    /**
     * @return  a reference to the timer represented by this handle.
     * @throws java.lang.IllegalStateException If this method is invoked while the instance
     * is in a state that does not allow access to this method.
     * @throws NoSuchObjectLocalException If invoked on a handle whose associated timer
     * has expired or has been cancelled.
     * @throws EJBException If this method could not complete due to a system-level failure.
     */
    public Timer getTimer() throws IllegalStateException, NoSuchObjectLocalException, EJBException {

        // Must reject Timer operations according to the EJB spec
        // See EJB 2.1 page 87 (Stateful session beans)
        checkAllowedOperations();

        return getTimerHandleDelegate().getTimer(infos);
    }

    /**
     * Restart a Timer read from disk (persistent timer)
     * @return
     * @throws IllegalStateException
     * @throws NoSuchObjectLocalException
     * @throws EJBException
     */
    public Timer restartTimer() throws IllegalStateException, NoSuchObjectLocalException, EJBException {

        // Must reject Timer operations according to the EJB spec
        // See EJB 2.1 page 87 (Stateful session beans)
        checkAllowedOperations();

        return getTimerHandleDelegate().restartTimer(infos);
    }

    /**
     * Get the unique TimerHandleDelegate, registered in JNDI.
     * @return TimerHandleDelegate
     */
    private TimerHandleDelegate getTimerHandleDelegate() throws IllegalStateException {
        if (delegate == null) {
            try {
                // Prevent usage of the Handle outside of JOnAS.
                Context ictx = new InitialContext();
                delegate = (TimerHandleDelegate) ictx.lookup("java:comp/TimerHandleDelegate");
            } catch (NamingException e) {
                throw new IllegalStateException("TimerHandleDelegate not retrieved:", e);
            }
        }
        return delegate;
    }

    /**
     *  Must reject Timer operations according to the EJB spec
     *  See EJB 2.1 page 87 (Stateful session beans).
     */
    private void checkAllowedOperations() {

        // Must reject Timer operations according to the EJB spec
        // See EJB 2.1 page 87 (Stateful session beans)
        // A stateful session bean can get a Timer created by another bean by its
        // TimerHandle. Then, it can use it. But this is not allowed inside afterCompletion.
        try {
            JNamingManager naming = SingletonNamingManager.getInstance();
            Context ctx = naming.getComponentContext();
            JContext sc = (JContext) ctx.lookup("MY_SF_CONTEXT");
            if (sc.getState() != sc.CTX_STATE_ACTIVE) {
                TraceTimer.logger.log(BasicLevel.DEBUG, "This operation is not allowed here");
                throw new IllegalStateException("This operation is not allowed here");
            }
        } catch (NamingException ne) {
            // No context registered. Assume it's OK.
            // It's the case for Entity or MDB.
        }
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {

        if (!(obj instanceof JTimerHandle)) {
            return false;
        }
        JTimerHandle handle = (JTimerHandle) obj;

        return infos.equals(handle.infos);
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        return infos.hashCode();
    }
}
