blob: 3a0ae1a9bc413d3b1fcbed3afcd7b1b0c9655925 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.yoko.orb.OB;
import static org.apache.yoko.orb.OB.MinorCodes.MinorDestroyWouldBlock;
import static org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed;
import static org.apache.yoko.orb.OB.MinorCodes.MinorShutdownCalled;
import static org.apache.yoko.orb.OB.MinorCodes.describeBadInvOrder;
import static org.apache.yoko.orb.OB.MinorCodes.describeInitialize;
import static org.omg.CORBA.CompletionStatus.COMPLETED_NO;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.yoko.orb.OBPortableServer.POAManagerFactory_impl;
import org.apache.yoko.orb.OBPortableServer.POA_impl;
import org.apache.yoko.orb.PortableServer.Current_impl;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.INITIALIZE;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.CurrentPackage.NoContext;
public final class ORBControl {
//
// The ORB instance
//
ORBInstance orbInstance_;
//
// The ORBControl state
//
private enum State { NOT_RUNNING, RUNNING, SERVER_SHUTDOWN, CLIENT_SHUTDOWN, DESTROYED };
private volatile State state; // State of the ORB
//
// Has shutdown been called?
//
private final CountDownLatch shutdown = new CountDownLatch(1);
//
// The Root POA
//
private POA rootPOA_; // The Root POA
//
// The thread id of the main thread (that is the first thread that
// calls run, perform_work or work_pending)
//
private volatile Thread mainThread_;
private long shutdownTimeout_ = 2;//seconds
// ----------------------------------------------------------------------
// ORBControl private and protected member implementations
// ----------------------------------------------------------------------
//
// Complete shutdown of the ORB, if necessary
//
private synchronized void completeServerShutdown() {
//
// If the shutdown_ is false, or the server side has already
// shutdown then do nothing
//
if (shutdown.getCount() != 0 || state == State.SERVER_SHUTDOWN)
return;
Assert._OB_assert(state != State.CLIENT_SHUTDOWN && state != State.DESTROYED);
//
// If run was called then only the main thread may complete the
// shutdown
//
Assert._OB_assert(state == State.NOT_RUNNING || mainThread_ == Thread.currentThread());
//
// Get the POAManagerFactory implementation
//
POAManagerFactory_impl factory = (POAManagerFactory_impl) orbInstance_.getPOAManagerFactory();
//
// Deactivate all of the POAManagers
//
factory._OB_deactivate();
//
// Wait for all the threads in the server worker group to
// terminate
//
waitForServerThreads();
notifyAll();
}
private void waitForServerThreads() {
shutdownExecutor(orbInstance_.getServerPhaser(), orbInstance_.getServerExecutor());
//
// Get the DispatchStrategyFactory implementation and
// destroy it. It must be destroyed here so that the
// thread pools get destroyed before OCI::Current_impl
// gets destroyed by the destruction of the Root
// POA. Otherwise, thread specific data for the thread
// pool threads will not get released.
//
DispatchStrategyFactory dsFactory = orbInstance_.getDispatchStrategyFactory();
DispatchStrategyFactory_impl dsFactoryImpl = (DispatchStrategyFactory_impl) dsFactory;
dsFactoryImpl._OB_destroy();
//
// Mark the server side state as shutdown and notify any
// waiting threads
//
state = State.SERVER_SHUTDOWN;
//
// Destroy the root POA
//
if (rootPOA_ != null) {
rootPOA_.destroy(true, true);
rootPOA_ = null;
}
}
//
// Validate the state
//
private synchronized void validateState() {
//
// The ORB destroys this object, so it's an initialization
// error if the this operation is called after ORB destruction
//
if (state == State.DESTROYED)
throw new org.omg.CORBA.INITIALIZE(
org.apache.yoko.orb.OB.MinorCodes.describeInitialize(org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed),
org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed, org.omg.CORBA.CompletionStatus.COMPLETED_NO);
if (state == State.SERVER_SHUTDOWN || state == State.CLIENT_SHUTDOWN)
throw new org.omg.CORBA.BAD_INV_ORDER(
org.apache.yoko.orb.OB.MinorCodes.describeBadInvOrder(org.apache.yoko.orb.OB.MinorCodes.MinorShutdownCalled),
org.apache.yoko.orb.OB.MinorCodes.MinorShutdownCalled, org.omg.CORBA.CompletionStatus.COMPLETED_NO);
if (state == State.NOT_RUNNING) {
//
// Remember the main thread id
//
mainThread_ = Thread.currentThread();
//
// Set the state to State.RUNNING
//
state = State.RUNNING;
}
}
private synchronized void blockServerShutdownComplete() {
//
// Wait for the server side to shutdown. Note that the client
// side shutting down or the state being destroyed also
// implies that the server side has shutdown
//
while (state == State.RUNNING) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
// ----------------------------------------------------------------------
// ORBControl public member implementations
// ----------------------------------------------------------------------
public ORBControl() {
state = State.NOT_RUNNING;
}
//
// Destroy the ORBControl
//
public synchronized void destroy() {
//
// destroy() may not be called unless the client side has been
// shutdown
//
Assert._OB_assert(state == State.CLIENT_SHUTDOWN);
state = State.DESTROYED;
//
// Set the ORBInstance object to nil
//
orbInstance_ = null;
}
//
// Set the ORBInstance object
//
public void setORBInstance(ORBInstance instance) {
orbInstance_ = instance;
}
//
// Determine if there if the ORB needs the main thread to perform
// some work
//
public boolean workPending() {
validateState();
//
// If this is not the main thread then do nothing
//
if (mainThread_ != Thread.currentThread())
return false;
//
// Validate state will throw an exception if state is
// ServerShutdown, ClientShutdown or Destroyed. Therefore if
// shutdown_ is true, then a server side shutdown is pending.
//
return (shutdown.getCount() == 0);
}
//
// Perform one unit of work
//
public void performWork() {
validateState();
//
// If this is not the main thread then do nothing
//
if (mainThread_ != Thread.currentThread())
return;
completeServerShutdown();
}
//
// Run the ORB event loop
//
public void run() {
validateState();
//
// If this is not the main thread then block until the ORB is
// shutdown
//
if (mainThread_ != Thread.currentThread()) {
blockServerShutdownComplete();
return;
}
//
// Validate state will throw an exception if state is
// ServerShutdown, ClientShutdown or Destroyed. Therefore if
// shutdown_ is true, then a server side shutdown is pending
// so complete it now.
//
if (shutdown.getCount() == 0) {
completeServerShutdown();
return;
}
//
// Block until the ORB server side has shutdown. Note that the
// client side shutting down or the state being destroyed also
// implies that the server side has shutdown
//
do {
try {
shutdown.await();
} catch (InterruptedException ignored) {
}
//
// After this call state is either ShutdownClient, or
// Running
//
completeServerShutdown();
} while (state == State.RUNNING);
}
//
// Shutdown the server side of the ORB
//
// ASYNC SAFE if waitForCompletion == false
//
public void shutdownServer(boolean waitForCompletion) {
//
// The ORB destroys this object, so it's an initialization error
// if the this operation is called after ORB destruction
//
if (state == State.DESTROYED)
throw new INITIALIZE(describeInitialize(MinorORBDestroyed), MinorORBDestroyed, COMPLETED_NO);
if (state == State.SERVER_SHUTDOWN || state == State.CLIENT_SHUTDOWN)
throw new BAD_INV_ORDER(describeBadInvOrder(MinorShutdownCalled), MinorShutdownCalled, COMPLETED_NO);
//
// If waitForCompletion is true then find out whether we're inside
// a method invocation -- if so throw a BAD_INV_ORDER exception
//
if (waitForCompletion) {
boolean inInvocation = false;
try {
InitialServiceManager initialServiceManager = orbInstance_.getInitialServiceManager();
org.omg.CORBA.Object o = initialServiceManager.resolveInitialReferences("POACurrent");
Current_impl current = (Current_impl) o;
inInvocation = current._OB_inUpcall();
if (inInvocation) {
//
// Check whether or not the request is dispatched in this
// POAManager's ORB or another ORB.
//
try {
POA_impl p = (POA_impl) current.get_POA();
inInvocation = (p._OB_ORBInstance() == orbInstance_);
} catch (NoContext ex) {
}
}
} catch (ClassCastException ex) {
} catch (InvalidName ex) {
}
if (inInvocation)
throw new BAD_INV_ORDER(describeBadInvOrder(MinorDestroyWouldBlock), MinorDestroyWouldBlock, COMPLETED_NO);
}
//
// Unblock run(). This should be done immediately before
// the return since this can cause the main loop to wake and
// complete the shutdown (thus, for instance, destroying the
// POAManagerFactory).
//
shutdown.countDown();
//
// waitForCompletion false? We're done.
//
if (!waitForCompletion)
return;
//
// If run was called and this is not the main thread and
// waitForCompletion is true then wait for the shutdown to
// complete.
//
if (state == State.RUNNING && mainThread_ != Thread.currentThread()) {
blockServerShutdownComplete();
return;
}
//
// This is the main thread -- complete the shutdown process
//
completeServerShutdown();
}
//
// Shutdown the server (if necessary) & client side of the ORB
//
public synchronized void shutdownServerClient() {
//
// The ORB destroys this object, so it's an initialization
// error if the this operation is called after ORB destruction
//
if (state == State.DESTROYED)
throw new org.omg.CORBA.INITIALIZE(
org.apache.yoko.orb.OB.MinorCodes.describeInitialize(org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed),
org.apache.yoko.orb.OB.MinorCodes.MinorORBDestroyed, org.omg.CORBA.CompletionStatus.COMPLETED_NO);
//
// If the ORB client side is already shutdown, then we're done
//
if (state == State.CLIENT_SHUTDOWN)
return;
if (orbInstance_ != null) {
//
// First shutdown the server side, if necessary
//
if (state != State.SERVER_SHUTDOWN)
shutdownServer(true);
//
// The server shutdown must have completed
//
Assert._OB_assert(state == State.SERVER_SHUTDOWN);
//
// Shutdown the client side. Continue to dispatch events until all
// client type event handlers have unregistered.
//
ClientManager clientManager = orbInstance_.getClientManager();
clientManager.destroy();
//
// Wait for all the threads in the client worker group to
// terminate
//
shutdownExecutor(orbInstance_.getClientPhaser(), orbInstance_.getClientExecutor());
}
//
// Mark the ORB's client side as shutdown and notify any
// waiters
//
state = State.CLIENT_SHUTDOWN;
notifyAll();
}
private void shutdownExecutor(Phaser phaser, ExecutorService executor) {
int phase = phaser.arrive();//release the system's "lock"
//phaser advances after all GIOPConnectionThreaded have shut down (gracefully or abort)
try {
phaser.awaitAdvanceInterruptibly(phase, shutdownTimeout_, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
} finally {
phaser.forceTermination();
}
executor.shutdownNow();
}
//
// Initialize the Root POA
//
public void initializeRootPOA(org.omg.CORBA.ORB orb) {
String serverId = orbInstance_.getServerId();
//
// If there is no server id then set to "_RootPOA"
//
if (serverId.length() == 0)
serverId = "_RootPOA";
//
// Get the initial service manager
//
InitialServiceManager ism = orbInstance_.getInitialServiceManager();
//
// Create the Root POAManager
//
org.apache.yoko.orb.OBPortableServer.POAManagerFactory factory = null;
try {
factory = org.apache.yoko.orb.OBPortableServer.POAManagerFactoryHelper.narrow(ism.resolveInitialReferences("POAManagerFactory"));
} catch (org.omg.CORBA.ORBPackage.InvalidName ex) {
Assert._OB_assert(ex);
}
//
// First attempt to locate the root POAManager
//
org.apache.yoko.orb.OBPortableServer.POAManager manager = null;
org.omg.PortableServer.POAManager[] managers = factory.list();
for (int i = 0; i < managers.length; i++) {
if (managers[i].get_id().equals("RootPOAManager")) {
manager = (org.apache.yoko.orb.OBPortableServer.POAManager) managers[i];
break;
}
}
//
// If the root POAManager doesn't exist then create it
//
if (manager == null) {
try {
org.omg.CORBA.Policy[] emptyPl = new org.omg.CORBA.Policy[0];
manager = (org.apache.yoko.orb.OBPortableServer.POAManager) (factory.create_POAManager("RootPOAManager", emptyPl));
} catch (org.omg.PortableServer.POAManagerFactoryPackage.ManagerAlreadyExists ex) {
Assert._OB_assert(ex);
}
// catch(org.apache.yoko.orb.OCI.InvalidParam ex)
// {
// Logger logger = orbInstance_.getLogger();
// String err = "invalid configuration parameter " +
// "for RootPOAManager: " + ex.reason;
// logger.error(err);
// throw new org.omg.CORBA.INITIALIZE(err);
// }
catch (org.omg.CORBA.PolicyError ex) {
// TODO : Is this correct?
Assert._OB_assert(ex);
}
}
//
// Create the Root POA
//
org.apache.yoko.orb.OBPortableServer.POA_impl root = new org.apache.yoko.orb.OBPortableServer.POA_impl(orb, orbInstance_, serverId, manager);
root._OB_addPolicyFactory();
rootPOA_ = root;
try {
ism.addInitialReference("RootPOA", root, true);
} catch (org.omg.CORBA.ORBPackage.InvalidName ex) {
Assert._OB_assert(ex);
}
//
// Ask the POAManagerFactory to initialize this servers connection
// to the IMR
//
// TODO-B3: Is there some other point that can be used to do this?
//
org.apache.yoko.orb.OBPortableServer.POAManagerFactory_impl factoryImpl = (org.apache.yoko.orb.OBPortableServer.POAManagerFactory_impl) factory;
factoryImpl._OB_initializeIMR(root, this);
}
}