/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* 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 (at your option) 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
*/
package alma.acs.container;
import java.util.logging.Logger;
import org.omg.CORBA.LocalObject;
import org.omg.CORBA.OBJECT_NOT_EXIST;
import org.omg.PortableServer.ForwardRequest;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.ServantActivator;
/**
* We use one ComponentServantManager per component POA.
* It's used to synchronize with component etherealization.
* <p>
* Impl note: until ACS 6.0.x this class inherited from <code>org.omg.PortableServer.ServantActivatorPOA</code>
* and was activated like a normal Corba object (<code>componentPOA.set_servant_manager(servantManager._this(m_orb))</code>.
* However this form of activation attached the servant manager instance to the ORB or root poa, with the effect
* that it was not garbage collected together with the component poa (memory leak!).
* It seems that a POJO (no Corba activation) inheriting from <code>LocalObject</code> is the correct choice instead.
*
* @author hsommer
* $Id$
*/
public class ComponentServantManager extends LocalObject implements ServantActivator // extends ServantActivatorPOA
{
private Logger m_logger;
private boolean DEBUG = false;
private volatile boolean receivedEtherealizeCall;
/**
* Constructor for ComponentServantManager.
* @param logger Logger to be used by this class. Should be the container logger.
*/
public ComponentServantManager(Logger logger)
{
super();
m_logger = logger;
}
/**
* This method should never be called, because all components are activated explicitly by the container
* and therefore should be registered in the active object map.
* Thus the implementation just throws a <code>OBJECT_NOT_EXIST</code> exception.
* <p>
* Note that by definition, this method acts as a fallback,
* if the POA can not find an object in that map (if RETAIN policy is used).
* The POA could call it after a component has been deactivated (<code>deactivate_object</code>),
* in an attempt to serve a new incoming call. This we don't allow though.
*
* @see org.omg.PortableServer.ServantActivatorOperations#incarnate(byte[], POA)
*/
public Servant incarnate(byte[] oid, POA adapter)
throws ForwardRequest
{
throw new OBJECT_NOT_EXIST();
}
/**
* See CORBA spec (2.4) 11.3.5.2 etherealize.
* This operation is invoked whenever a servant for an object is deactivated, assuming the POA has
* the USE_SERVANT_MANAGER and RETAIN policies.
* <p>
* This method does not deal with the servant (component) at all, just notifies a thread that has called
* {@link #waitForEtherealize(int)}.
*
* @param oid object Id associated with the object being deactivated.
* @param adapter object reference for the POA in which the object was active.
* @param serv contains reference to the servant associated with the object being deactivated.
* @param cleanup_in_progress if TRUE indicates that destroy or deactivate is called with
* etherealize_objects param of TRUE. FALSE indicates that etherealize was called due to other reasons.
* We ignore this parameter.
* @param remaining_activations indicates whether the Servant Manager can destroy a servant.
* If set to TRUE, the Servant Manager should wait until all invocations in progress have completed.
* This method seems never to be called with <code>remaining_activations==true</code>. If so, the call is ignored.
*
* @see org.omg.PortableServer.ServantActivatorOperations#etherealize(byte[], POA, Servant, boolean, boolean)
*/
public synchronized void etherealize(byte[] oid, POA adapter, Servant serv,
boolean cleanup_in_progress, boolean remaining_activations)
{
if (DEBUG) {
m_logger.info("ComponentServantManager#etherealize called for " +
"servant class = '" + serv.getClass().getName() + "'; rem.actions=" + remaining_activations);
}
if (!remaining_activations) {
if (DEBUG) {
logStackTrace("Callstack for ServantActivator#etherealize");
}
// release thread that blocks on waitForEtherealize
receivedEtherealizeCall = true;
notifyAll();
}
}
/**
* Resets the flag that gets raised when the <code>etherealize</code> method gets called.
* Such a flag is needed because servant etherealization occurs asynchonously some time after
* POA.destroy has been called.
* A thread that wants to wait for etherealization must first call <code>resetWaitForEtherealize</code>,
* then <code>POA.destroy</code>, and then <code>waitForEtherealize</code>.
*
* @see #waitForEtherealize(int)
*/
public synchronized void resetWaitForEtherealize() {
receivedEtherealizeCall = false;
}
/**
* Allows a thread to be notified of component servant etherealization.
* <p>
* Since we use one component POA per servant, it is not necessary to distinguish for which servant
* the etherealize method was called.
*
* @param maxWaitMillis the maximum time to wait, or 0 if no timeout should be used.
* @return true if etherealization occured, false if the operation timed out.
*/
public synchronized boolean waitForEtherealize(int maxWaitMillis) {
if (DEBUG) {
m_logger.info("will wait for component etherealization...");
}
long startedWaitingTime = System.currentTimeMillis();
long remainingWaitTime = maxWaitMillis;
while (!receivedEtherealizeCall && remainingWaitTime > 0) {
try {
wait(remainingWaitTime);
} catch (InterruptedException e) {
// just wait again
}
remainingWaitTime = maxWaitMillis - (System.currentTimeMillis() - startedWaitingTime);
}
if (!receivedEtherealizeCall) {
// timeout, no success
// m_logger.warning("Waiting for component to be etherealized timed out after " + maxWaitMillis + "ms.");
}
else if (DEBUG) {
m_logger.info("received component etherealization notification.");
}
return receivedEtherealizeCall;
}
// /**
// * @see org.omg.CORBA.portable.InvokeHandler#_invoke(String, InputStream, ResponseHandler)
// */
// public OutputStream _invoke(String method, InputStream input, ResponseHandler handler)
// throws SystemException
// {
// OutputStream os = super._invoke(method, input, handler);
// m_logger.fine("ComponentServantManager#_invoke called.");
// return os;
// }
/**
* Helper method for debugging, logs the stacktrace.
* @param msg
*/
private void logStackTrace(String msg) {
if (DEBUG) {
Exception fakeEx = new Exception("stacktrace fake ex");
StackTraceElement[] trace = fakeEx.getStackTrace();
msg += "\n";
for (int i=1; i < trace.length; i++) {
msg += "\tat " + trace[i] + '\n';
}
m_logger.info(msg);
}
}
}