/*
 * @(#)POAManagerImpl.java	1.17 01/12/03
 *
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.internal.POA;

import java.util.*;
import org.omg.CORBA.TRANSIENT;
import org.omg.CORBA.OBJ_ADAPTER;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.LocalObject;
import org.omg.PortableServer.POAManager;
import org.omg.PortableServer.POAManagerPackage.State;
import org.omg.PortableServer.POA;


/** POAManagerImpl is the implementation of the POAManager interface.
 *  Its public methods are activate(), hold_requests(), discard_requests()
 *  and deactivate().
 *
 *  A note on deadlocks: Since Java allows threads to acquire the same
 *  monitor multiple times, there will not be a deadlock on circular calls
 *  to synchronized methods (i.e. if a POAManager instance method calls an 
 *  object which calls another method on this same POAManager instance). 
 *  However, there is one "legal" deadlock which can occur: see the note 
 *  in the enter() method.
 */

public class POAManagerImpl extends org.omg.CORBA.LocalObject implements POAManager
{
    // POAManager states
    private static final State HOLDING = State.HOLDING;
    private static final State ACTIVE = State.ACTIVE;
    private static final State DISCARDING = State.DISCARDING;
    private static final State INACTIVE = State.INACTIVE;
   
    POAORB orb;
    private State state; // current state of this
    private Set poas = Collections.synchronizedSet(new HashSet(4)); // all poas controlled by this

    // Number of invocations in progress
    int nInvocations=0;	

    // Number of threads waiting for invocations to complete
    int nWaiters=0;

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

    private void notifyWaiters() 
    {
	if (nWaiters >0)
	    notifyAll() ;
    }

    public POAManagerImpl(POAORB orb)
    {
	this.orb = orb;
	state = HOLDING;
    }

    synchronized void addPOA(POA poa)
    {
        poas.add(poa);
        orb.addPoaManager(this);
    }


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

/****************************************************************************
 * 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
    {
	if ( state.value() == State._INACTIVE )
	    throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();
	// set the state to ACTIVE
	state = ACTIVE;
	
	// 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();
    }

    /**
     * <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
    {
	if ( state.value() == State._INACTIVE )
	    throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();
	// set the state to HOLDING
	state  = HOLDING;

	// 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() ;
	    }
	}
    }

    /**
     * <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
    {
	if ( state.value() == State._INACTIVE )
	    throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();
	// set the state to DISCARDING
	state = DISCARDING;

	// 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() ;
	    }
	}
    }

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

    public synchronized void deactivate(boolean etherealize_objects, boolean wait_for_completion)
        throws org.omg.PortableServer.POAManagerPackage.AdapterInactive
    {
	if ( state.value() == State._INACTIVE )
	    throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

        state = INACTIVE;

	// 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();

	if ( wait_for_completion ) {
	    while ( nInvocations > 0 ) { 
		countedWait() ;
	    }
	}

	if ( etherealize_objects ) {
	    if ( wait_for_completion ) {
                // stefanb 12/00: Why was this condition reversed? Spawning new threads means NOT to wait!
                etherealizePOAs();
	    } else {
                new Thread() {
                    public void run() {
                        POAManagerImpl.this.etherealizePOAs();
                    }
                }.start();
	    }	
	} else {
            orb.removePoaManager(this);
            // remove all POAs from poas, so that they can be GC'ed.
            poas.clear();
	}
    }

    /**
     * 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;
    }


    void etherealizePOAs() {
        Iterator iterator = (new HashSet(poas)).iterator();
        while (iterator.hasNext()) {
	    // if this is a RETAIN+USE_SERVANT_MGR poa
	    // then it must call etherealize for all its objects
            ((POAImpl)iterator.next()).etherealizeAll();
	}
        orb.removePoaManager(this);
        poas.clear();
    }

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

    // called from POA.find_POA before calling 
    // AdapterActivator.unknown_adapter.
    synchronized void checkIfActive()
    {
	checkState();
    }

    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 new TRANSIENT(MinorCodes.POA_DISCARDING,
				    CompletionStatus.COMPLETED_NO);
	    case State._INACTIVE:
		throw new OBJ_ADAPTER(MinorCodes.POA_INACTIVE,
				      CompletionStatus.COMPLETED_NO);
	    }
	}
    }

    synchronized void enter()
    {
        checkState();
	nInvocations++;
    }

    synchronized void exit()
    {
	nInvocations--; 

	if ( nInvocations == 0 ) {
	    // This notifies any threads that were in the 
	    // wait_for_completion loop in hold/discard/deactivate().
	    notifyWaiters();
	}
    }
}
