/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * 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.util.HashMap;
import java.util.Iterator;

import javax.ejb.EJBException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;

import org.ow2.jonas.deployment.ejb.EntityDesc;


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

/**
 * DataBase lock policy : 1 instance per transactions.
 * Transaction Isolation is managed by the database.
 * @author Philippe Durieux
 */
public class JEntitySwitchDB extends JEntitySwitch {

    /**
     * EntityContext for non-transacted requests
     */
    protected JEntityContext ihContext = null;

    /**
     * Map of EntityContext's for transactions Key is the Transaction.
     */
    protected HashMap itsContext;

    /**
     * empty constructor. Object is initialized via init() because it is
     * implemented differently according to jorm mappers.
     */
    public JEntitySwitchDB() {
        lockpolicy = EntityDesc.LOCK_DATABASE;
        itsContext = new HashMap();
    }

    protected void initpolicy(JEntityFactory bf) {
        // TODO should be false, but the test is not correct.
        lazyregister = !bf.isPrefetch() && !shared;
    }

    /**
     * @param tx The Transaction
     * @return the JEntityContext used for this tx
     */
    protected JEntityContext getContext4Tx(Transaction tx) {
        if (tx == null) {
            return ihContext;
        } else {
            return (JEntityContext) itsContext.get(tx);
        }
    }

    /**
     * @param tx The Transaction
     * @param the JEntityContext used for this tx
     */
    protected void setContext4Tx(Transaction tx, JEntityContext ctx) {
        if (tx == null) {
            ihContext = ctx;
        } else {
            itsContext.put(tx, ctx);
        }
    }

    /**
     *
     */
    protected void removeContext4Tx(Transaction tx) {
        // free this Context
        if (tx == null) {
            ihContext = null;
        } else {
            itsContext.remove(tx);
        }
    }

    public void waitmyturn(Transaction tx) {
        // synchro done by database.
    }

    /**
     * try to passivate instances
     * @param store not used for this policy
     * @param passivate always true for this policy
     * @return result of operation: (not really used here)
     * ALL_DONE = instances passivated
     * NOT_DONE = not all passivated
     */
    public synchronized int passivateIH(boolean store, boolean passivate) {

        // Don't passivate too recent instances to avoid problems at create
        if (System.currentTimeMillis() < estimestamp) {
            TraceEjb.context.log(BasicLevel.DEBUG, "too recent ");
            return NOT_DONE;
        }

        // Instance used when no transaction.
        JEntityContext jec = ihContext;
        if (jec != null && countIH == 0) {
            if (TraceEjb.isDebugContext()) {
                TraceEjb.context.log(BasicLevel.DEBUG, "passivate: " + jec);
            }
            if (jec.passivate()) {
                // Will be pooled only if min-pool-size not reached in free list.
                bf.releaseJContext(jec, 1);
                ihContext = null;
                // notify waiters for new instances
                if (waiters > 0) {
                    notify();
                }
            }
        }

        // Instances used for transactions are passivated automatically
        // because usually shared=false with this policy.


        // all instances passivated
        if (ihContext == null && itsContext.size() == 0) {
            // look if we can destroy the objects
            if (inactivityTimeout > 0 && System.currentTimeMillis() - estimestamp > inactivityTimeout) {
                detachPk();
                estimestamp = System.currentTimeMillis();
            }

            return ALL_DONE;
        }
        return NOT_DONE;
    }

    public void endIH() {
        TraceEjb.synchro.log(BasicLevel.ERROR, ident);
        return;
    }

    /**
     * Get a context/instance associated with this transaction Called at each
     * request on the bean (including remove)
     * @param tx - the Transaction object
     * @return the BeanContext
     */
    public JEntityContext getICtx(Transaction tx, boolean checkr) {
        // Don't check reentrance in case of database for now
        // because it doesn't work in multithreading (countIT!)
        return mapICtx(tx, null, false, true, false);
    }

    /**
     * @return State of this instance. State values are 0=in-tx, 1=out-tx, 2=idle,
     *         3=passive, 4=removed. we don't synchronize this method to avoid
     *         jadmin blocks
     */
    public int getState() {
        if (ihContext != null) {
            if (ihContext.isMarkedRemoved()) {
                return 4;
            } else {
                return 2;
            }
        }
        if (itsContext.size() > 0) {
            return 0;
        }
        return 3;
    }

}
