/*
 * @(#)POAManagerImpl.java	1.22 04/06/21
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.oa.poa;

import java.util.Iterator;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;

import org.omg.CORBA.LocalObject;
import org.omg.CORBA.CompletionStatus ;

import org.omg.PortableServer.POAManager;
import org.omg.PortableServer.POAManagerPackage.State;
import org.omg.PortableServer.POA;

import org.omg.PortableInterceptor.DISCARDING ;
import org.omg.PortableInterceptor.ACTIVE ;
import org.omg.PortableInterceptor.HOLDING ;
import org.omg.PortableInterceptor.INACTIVE ;
import org.omg.PortableInterceptor.NON_EXISTENT ;

import com.sun.corba.se.spi.protocol.PIHandler ;

import com.sun.corba.se.impl.logging.POASystemException ;

import com.sun.corba.se.impl.orbutil.ORBUtility ;

/** POAManagerImpl is the implementation of the POAManager interface.
 *  Its public methods are activate(), hold_requests(), discard_requests()
 *  and deactivate().
 */

public class POAManagerImpl extends org.omg.CORBA.LocalObject implements 
    POAManager
{
    private final POAFactory factory ;	// factory which contains global state 
					// for all POAManagers 
    private PIHandler pihandler ;	// for adapterManagerStateChanged
    private State state;		// current state of this POAManager
    private Set poas = new HashSet(4) ; // all poas controlled by this POAManager
    private int nInvocations=0;		// Number of invocations in progress
    private int nWaiters=0;		// Number of threads waiting for 
					// invocations to complete
    private int myId = 0 ;		// This POAManager's ID
    private boolean debug ;
    private boolean explicitStateChange ; // initially false, set true as soon as 
					// one of activate, hold_request, 
					// discard_request, or deactivate is called.

    private String stateToString( State state )
    {
	switch (state.value()) {
	    case State._HOLDING : return "State[HOLDING]" ;
	    case State._ACTIVE : return "State[ACTIVE]" ;
	    case State._DISCARDING : return "State[DISCARDING]" ;
	    case State._INACTIVE : return "State[INACTIVE]" ;
	}

	return "State[UNKNOWN]" ;
    }

    public String toString()
    {
	return "POAManagerImpl[myId=" + myId + 
	    " state=" + stateToString(state) +
	    " nInvocations=" + nInvocations + 
	    " nWaiters=" + nWaiters + "]" ;
    }

    POAFactory getFactory()
    {
	return factory ;
    }

    PIHandler getPIHandler()
    {
	return pihandler ;
    }

    private void countedWait()
    {
	try {
	    if (debug) {
		ORBUtility.dprint( this, "Calling countedWait on POAManager " +
		    this + " nWaiters=" + nWaiters ) ;
	    }

	    nWaiters++ ;
	    wait(); 
	} catch ( java.lang.InterruptedException ex ) {
	    // NOP
	} finally {
	    nWaiters-- ;

	    if (debug) {
		ORBUtility.dprint( this, "Exiting countedWait on POAManager " +
		    this + " nWaiters=" + nWaiters ) ;
	    }
	}
    }

    private void notifyWaiters() 
    {
	if (debug) {
	    ORBUtility.dprint( this, "Calling notifyWaiters on POAManager " +
		this + " nWaiters=" + nWaiters ) ;
	}

	if (nWaiters >0)
	    notifyAll() ;
    }

    public int getManagerId() 
    {
	return myId ;
    }

    POAManagerImpl( POAFactory factory, PIHandler pihandler )
    {
	this.factory = factory ;
        factory.addPoaManager(this);
	this.pihandler = pihandler ;
	myId = factory.newPOAManagerId() ;
	state = State.HOLDING;
	debug = factory.getORB().poaDebugFlag ;
	explicitStateChange = false ;

	if (debug) {
	    ORBUtility.dprint( this, "Creating POAManagerImpl " + this ) ;
	}
    }

    synchronized void addPOA(POA poa)
    {
	// XXX This is probably not the correct error
	if (state.value() == State._INACTIVE) {
	    POASystemException wrapper = factory.getWrapper();
	    throw wrapper.addPoaInactive( CompletionStatus.COMPLETED_NO ) ;
	}
	    
        poas.add(poa);
    }

    synchronized void removePOA(POA poa)
    {
        poas.remove(poa);
        if ( poas.isEmpty() ) {
            factory.removePoaManager(this);
	}
    }

    public short getORTState() 
    {
	switch (state.value()) {
	    case State._HOLDING    : return HOLDING.value ;
	    case State._ACTIVE     : return ACTIVE.value ;
	    case State._INACTIVE   : return INACTIVE.value ;
	    case State._DISCARDING : return DISCARDING.value ;
	    default		   : return NON_EXISTENT.value ;
	}
    }

/****************************************************************************
 * The following four public methods are used to change the POAManager's state.
 *
 * A note on the design of synchronization code:
 * There are 4 places where a thread would need to wait for a condition:
 *      - in hold_requests, discard_requests, deactivate, enter 
 * There are 5 places where a thread notifies a condition:
 *      - in activate, hold_requests, discard_requests, deactivate, exit 
 *
 * Since each notify needs to awaken waiters in several of the 4 places,
 * and since wait() in Java has the nice property of releasing the lock
 * on its monitor before sleeping, it seemed simplest to have just one
 * monitor object: "this". Thus all notifies will awaken all waiters.
 * On waking up, each waiter verifies that the condition it was waiting 
 * for is satisfied, otherwise it goes back into a wait().
 * 
 ****************************************************************************/

    /**
     * <code>activate</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     */
    public synchronized void activate()
        throws org.omg.PortableServer.POAManagerPackage.AdapterInactive
    {
	explicitStateChange = true ;

	if (debug) {
	    ORBUtility.dprint( this, 
		"Calling activate on POAManager " + this ) ;
	}

	try {
	    if ( state.value() == State._INACTIVE )
		throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

	    // set the state to ACTIVE
	    state = State.ACTIVE;
	    
	    pihandler.adapterManagerStateChanged( myId, getORTState() ) ;

	    // Notify any invocations that were waiting because the previous
	    // state was HOLDING, as well as notify any threads that were waiting
	    // inside hold_requests() or discard_requests(). 
	    notifyWaiters();
	} finally {
	    if (debug) {
		ORBUtility.dprint( this, 
		    "Exiting activate on POAManager " + this ) ;
	    }
	}
    }

    /**
     * <code>hold_requests</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     */
    public synchronized void hold_requests(boolean wait_for_completion)
        throws org.omg.PortableServer.POAManagerPackage.AdapterInactive
    {
	explicitStateChange = true ;

	if (debug) {
	    ORBUtility.dprint( this, 
		"Calling hold_requests on POAManager " + this ) ;
	}

	try {
	    if ( state.value() == State._INACTIVE )
		throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();
	    // set the state to HOLDING
	    state  = State.HOLDING;

	    pihandler.adapterManagerStateChanged( myId, getORTState() ) ;

	    // Notify any threads that were waiting in the wait() inside
	    // discard_requests. This will cause discard_requests to return
	    // (which is in conformance with the spec).
	    notifyWaiters(); 

	    if ( wait_for_completion ) {
		while ( state.value() == State._HOLDING && nInvocations > 0 ) {
		    countedWait() ;
		}
	    }
	} finally {
	    if (debug) {
		ORBUtility.dprint( this, 
		    "Exiting hold_requests on POAManager " + this ) ;
	    }
	}
    }

    /**
     * <code>discard_requests</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     */
    public synchronized void discard_requests(boolean wait_for_completion)
        throws org.omg.PortableServer.POAManagerPackage.AdapterInactive
    {
	explicitStateChange = true ;

	if (debug) {
	    ORBUtility.dprint( this, 
		"Calling hold_requests on POAManager " + this ) ;
	}
	 
	try {
	    if ( state.value() == State._INACTIVE )
		throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

	    // set the state to DISCARDING
	    state = State.DISCARDING;

	    pihandler.adapterManagerStateChanged( myId, getORTState() ) ;

	    // Notify any invocations that were waiting because the previous
	    // state was HOLDING. Those invocations will henceforth be rejected with
	    // a TRANSIENT exception. Also notify any threads that were waiting
	    // inside hold_requests().
	    notifyWaiters(); 

	    if ( wait_for_completion ) {
		while ( state.value() == State._DISCARDING && nInvocations > 0 ) {
		    countedWait() ;
		}
	    }
	} finally {
	    if (debug) {
		ORBUtility.dprint( this, 
		    "Exiting hold_requests on POAManager " + this ) ;
	    }
	}
    }

    /**
     * <code>deactivate</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     * Note: INACTIVE is a permanent state.
     */

    public void deactivate(boolean etherealize_objects, boolean wait_for_completion)
        throws org.omg.PortableServer.POAManagerPackage.AdapterInactive
    {
	explicitStateChange = true ;

	try {
	    synchronized( this ) {
		if (debug) {
		    ORBUtility.dprint( this, 
			"Calling deactivate on POAManager " + this ) ;
		}

		if ( state.value() == State._INACTIVE )
		    throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

		state = State.INACTIVE;

		pihandler.adapterManagerStateChanged( myId, getORTState() ) ;

		// Notify any invocations that were waiting because the previous
		// state was HOLDING. Those invocations will then be rejected with
		// an OBJ_ADAPTER exception. Also notify any threads that were waiting
		// inside hold_requests() or discard_requests().
		notifyWaiters();
	    }

	    POAManagerDeactivator deactivator = new POAManagerDeactivator( this,
		etherealize_objects, debug ) ;

	    if (wait_for_completion)
		deactivator.run() ;
	    else {
		Thread thr = new Thread(deactivator) ;
		thr.start() ;
	    }
	} finally { 
	    synchronized(this) {
		if (debug) {
		    ORBUtility.dprint( this, 
			"Exiting deactivate on POAManager " + this ) ;
		}
	    }
	}
    }

    private class POAManagerDeactivator implements Runnable
    {
	private boolean etherealize_objects ;
	private POAManagerImpl pmi ;
	private boolean debug ;

	POAManagerDeactivator( POAManagerImpl pmi, boolean etherealize_objects,
	    boolean debug )
	{
	    this.etherealize_objects = etherealize_objects ;
	    this.pmi = pmi ;
	    this.debug = debug ;
	}

	public void run() 
	{
	    try {
		synchronized (pmi) {
		    if (debug) {
			ORBUtility.dprint( this,
			    "Calling run with etherealize_objects=" +
			    etherealize_objects + " pmi=" + pmi ) ;
		    }

		    while ( pmi.nInvocations > 0 ) { 
			countedWait() ;
		    }
		}

		if (etherealize_objects) {
		    Iterator iterator = null ;

		    // Make sure that poas cannot change while we copy it!
		    synchronized (pmi) {
			if (debug) {
			    ORBUtility.dprint( this,
				"run: Preparing to etherealize with pmi=" + 
				pmi ) ;
			}

			iterator = (new HashSet(pmi.poas)).iterator();
		    } 

		    while (iterator.hasNext()) {
			// Each RETAIN+USE_SERVANT_MGR poa
			// must call etherealize for all its objects
			((POAImpl)iterator.next()).etherealizeAll();
		    }

		    synchronized (pmi) {
			if (debug) {
			    ORBUtility.dprint( this,
				"run: removing POAManager and clearing poas " +
				"with pmi=" + pmi ) ;
			}

			factory.removePoaManager(pmi);
			poas.clear();
		    }
		}
	    } finally {
		if (debug) {
		    synchronized (pmi) {
			ORBUtility.dprint( this, "Exiting run" ) ;
		    }
		}
	    }
	}
    }

    /**
     * Added according to the spec CORBA V2.3; this returns the
     * state of the POAManager
     */

    public org.omg.PortableServer.POAManagerPackage.State get_state () {
	return state;
    }

/****************************************************************************
 * The following methods are used on the invocation path.
 ****************************************************************************/

    // called from POA.find_POA before calling 
    // AdapterActivator.unknown_adapter.
    synchronized void checkIfActive()
    {
	try {
	    if (debug) {
		ORBUtility.dprint( this,
		    "Calling checkIfActive for POAManagerImpl " + this ) ;
	    } 

	    checkState();
	} finally {
	    if (debug) {
		ORBUtility.dprint( this,
		    "Exiting checkIfActive for POAManagerImpl " + this ) ;
	    } 
	}
    }

    private void checkState()
    {
	while ( state.value() != State._ACTIVE ) {
	    switch ( state.value() ) {
		case State._HOLDING:
		    while ( state.value() == State._HOLDING ) {
			countedWait() ;
		    }
		    break;

		case State._DISCARDING:
		    throw factory.getWrapper().poaDiscarding() ;

		case State._INACTIVE:
		    throw factory.getWrapper().poaInactive() ;
	    }
	}
    }

    synchronized void enter()
    {
	try {
	    if (debug) {
		ORBUtility.dprint( this,
		    "Calling enter for POAManagerImpl " + this ) ;
	    } 

	    checkState();
	    nInvocations++;
	} finally {
	    if (debug) {
		ORBUtility.dprint( this,
		    "Exiting enter for POAManagerImpl " + this ) ;
	    } 
	}
    }

    synchronized void exit()
    {
	try {
	    if (debug) {
		ORBUtility.dprint( this,
		    "Calling exit for POAManagerImpl " + this ) ;
	    } 

	    nInvocations--; 

	    if ( nInvocations == 0 ) {
		// This notifies any threads that were in the 
		// wait_for_completion loop in hold/discard/deactivate().
		notifyWaiters();
	    }
	} finally {
	    if (debug) {
		ORBUtility.dprint( this,
		    "Exiting exit for POAManagerImpl " + this ) ;
	    } 
	}
    }

    /** Activate the POAManager if no explicit state change has ever been
     * previously invoked.
     */
    public synchronized void implicitActivation() 
    {
	if (!explicitStateChange)
	    try {
		activate() ;
	    } catch (org.omg.PortableServer.POAManagerPackage.AdapterInactive ai) {
		// ignore the exception.
	    }
    }
}
