package org.jacorb.poa; /* * JacORB - a free Java ORB * * Copyright (C) 1997-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.util.HashSet; import org.jacorb.config.Configurable; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.dsi.ServerRequest; import org.jacorb.orb.util.CorbaLoc; import org.jacorb.poa.except.CompletionRequestedException; import org.jacorb.poa.except.ResourceLimitReachedException; import org.jacorb.poa.except.ShutdownInProgressException; import org.jacorb.poa.util.ByteArrayKey; import org.jacorb.poa.util.POAUtil; import org.omg.PortableServer.Servant; import org.omg.PortableServer.ServantManager; import org.slf4j.Logger; /** * This class manages all request processing affairs. The main thread takes the * requests out from the queue and will see that the necessary steps are taken. * * @author Reimo Tiedemann */ public final class RequestController extends Thread implements Configurable { private final POA poa; private final org.jacorb.orb.ORB orb; private final RequestQueue requestQueue; private final AOM aom; private final RPPoolManager poolManager; private int localRequests = 0; private static int count = 0; /** the configuration object for this controller */ private org.jacorb.config.Configuration configuration = null; /** this controller's logger instance */ private Logger logger; // stores all active requests private HashSet activeRequestTable; // RequestProcessor -> oid // for synchronisation with the object deactiviation process private final HashSet deactivationList = new HashSet(); // oid's // other synchronisation stuff private boolean terminate; private boolean waitForCompletionCalled; private boolean waitForShutdownCalled; private final java.lang.Object queueLog = new java.lang.Object(); private int threadPriority = Thread.MAX_PRIORITY; RequestController( POA _poa, org.jacorb.orb.ORB _orb, AOM _aom, RPPoolManager _poolManager) { super("RequestController-" + (++count)); poa = _poa; aom = _aom; orb = _orb; poolManager = _poolManager; requestQueue = new RequestQueue(); } public void configure(Configuration myConfiguration) throws ConfigurationException { this.configuration = (org.jacorb.config.Configuration)myConfiguration; logger = configuration.getLogger("org.jacorb.poa.controller"); int threadPoolMax = orb.getConfiguration().getAttributeAsInteger("jacorb.poa.thread_pool_max", 20); activeRequestTable = poa.isSingleThreadModel() ? new HashSet(1) : new HashSet(threadPoolMax); requestQueue.configure(myConfiguration); threadPriority = configuration.getAttributeAsInteger("jacorb.poa.thread_priority", Thread.MAX_PRIORITY); if( threadPriority < Thread.MIN_PRIORITY ) { threadPriority = Thread.MIN_PRIORITY; } else if( threadPriority > Thread.MAX_PRIORITY ) { threadPriority = Thread.MAX_PRIORITY; } setPriority(threadPriority); setDaemon(true); start(); } void clearUpPool() { poolManager.destroy(); } /** * rejects all queued requests with specified system exception */ void clearUpQueue(org.omg.CORBA.SystemException exception) { ServerRequest request; while ((request = requestQueue.removeLast()) != null) { rejectRequest(request, exception); } } /** * indicates that the assumptions for blocking the * request controller thread have changed, * a waiting request controller thread will notified */ void continueToWork() { synchronized (queueLog) { queueLog.notifyAll(); } } synchronized void end() { terminate = true; continueToWork(); } /** * frees an object from the deactivation in progress state, * a call indicates that the object deactivation process is complete */ synchronized void freeObject(ByteArrayKey oid) { deactivationList.remove( oid ); } AOM getAOM() { return aom; } Logger getLogger() { return logger; } org.jacorb.orb.ORB getORB() { return orb; } POA getPOA() { return poa; } RPPoolManager getPoolManager() { return poolManager; } RequestQueue getRequestQueue() { return requestQueue; } synchronized boolean isDeactivating (ByteArrayKey oid) { return deactivationList.contains( oid ); } /** * requests will dispatched to request processors, * attention, if the processor pool is empty, this method returns only * if the getProcessor() method from RequestProcessorPool can satisfied */ private void processRequest(ServerRequest request) throws ShutdownInProgressException, CompletionRequestedException { Servant servant = null; ServantManager servantManager = null; boolean invalid = false; final ByteArrayKey oid = request.objectIdAsByteArrayKey(); synchronized (this) { if (waitForCompletionCalled) { /* state has changed to holding, discarding or inactive */ if (logger.isInfoEnabled()) { logger.info("rid: " + request.requestId() + " opname: " + request.operation() + " cannot process request because waitForCompletion was called"); } throw new CompletionRequestedException(); } if (waitForShutdownCalled) { /* poa goes down */ if (logger.isInfoEnabled()) { logger.info("rid: " + request.requestId() + " opname: " + request.operation() + " cannot process request because POA shutdown in progress"); } throw new ShutdownInProgressException(); } /* below this point it's save that the poa is active */ if ((aom != null && aom.isDeactivating( oid )) || deactivationList.contains( oid )) { if (!poa.isUseServantManager() && !poa.isUseDefaultServant()) { if (logger.isInfoEnabled()) { logger.info("rid: " + request.requestId() + " opname: " + request.operation() + " objectKey: " + CorbaLoc.parseKey (request.objectKey ()) + " cannot process request, because object is already in the deactivation process"); } throw new org.omg.CORBA.OBJECT_NOT_EXIST(); } invalid = true; } /* below this point it's save that the object is not in a deactivation process */ if (!invalid && poa.isRetain()) { servant = aom.getServant(oid); } if (servant == null) { if (poa.isUseDefaultServant()) { if ((servant = poa.defaultServant) == null) { if (logger.isWarnEnabled()) { logger.warn("rid: " + request.requestId() + " opname: " + request.operation() + " cannot process request because default servant is not set"); } throw new org.omg.CORBA.OBJ_ADAPTER(); } } else if (poa.isUseServantManager()) { if ((servantManager = poa.servantManager) == null) { if (logger.isWarnEnabled()) { logger.warn("rid: " + request.requestId() + " opname: " + request.operation() + " cannot process request because servant manager is not set"); } throw new org.omg.CORBA.OBJ_ADAPTER(); } // USE_OBJECT_MAP_ONLY is in effect but object not exists } else { if (logger.isWarnEnabled()) { logger.warn("rid: " + request.requestId() + " opname: " + request.operation() + " connection: " + request.getConnection ().toString () + " objectKey: " + CorbaLoc.parseKey (request.objectKey ()) + " cannot process request, because object doesn't exist"); } throw new org.omg.CORBA.OBJECT_NOT_EXIST(); } } /* below this point it's save that the request is valid (all preconditions can be met) */ activeRequestTable.add(oid); } // get and initialize a processor for request processing if (logger.isDebugEnabled()) { logger.debug("rid: " + request.requestId() + " opname: " + request.operation() + " trying to get a RequestProcessor"); } RequestProcessor processor = poolManager.getProcessor(); processor.init(this, request, servant, servantManager); processor.begin(); } void queueRequest(ServerRequest request) throws ResourceLimitReachedException { requestQueue.add(request); if (requestQueue.size() == 1) { continueToWork(); } } /** * this method calls the basic adapter and hands out the request if * something went wrong, the specified system exception will set */ void rejectRequest(ServerRequest request, org.omg.CORBA.SystemException exception) { if (exception != null) request.setSystemException(exception); orb.getBasicAdapter().return_result(request); if (logger.isWarnEnabled()) { logger.warn("rid: " + request.requestId() + " opname: " + request.operation() + " request rejected with exception: " + exception.getMessage()); } } /** * resets a previous waitForCompletion call, * everybody who is waiting will notified */ synchronized void resetPreviousCompletionCall() { logger.debug("reset a previous completion call"); waitForCompletionCalled = false; notifyAll(); /* maybe somebody waits for completion */ } /** * Sends the reply of the given request via the BasicAdapter. */ void returnResult(ServerRequest request) { orb.getBasicAdapter().return_result(request); } /** * Called from RequestProcessor when the request has been handled. * The request is removed from the active request table. */ synchronized void finish (ServerRequest request) { final ByteArrayKey oid = request.objectIdAsByteArrayKey(); activeRequestTable.remove (oid); notifyAll(); } /** * the main loop for dispatching requests to request processors */ public void run() { org.omg.PortableServer.POAManagerPackage.State state; ServerRequest request; org.omg.CORBA.OBJ_ADAPTER closed_connection_exception = new org.omg.CORBA.OBJ_ADAPTER("connection closed: adapter inactive"); org.omg.CORBA.TRANSIENT transient_exception = new org.omg.CORBA.TRANSIENT(); while (!terminate) { state = poa.getState(); if (POAUtil.isActive(state)) { request = requestQueue.getFirst(); /* Request available */ if (request != null) { if (request.remainingPOAName() != null) { orb.getBasicAdapter().deliverRequest(request, poa); requestQueue.removeFirst(); } else { try { processRequest(request); requestQueue.removeFirst(); } catch (CompletionRequestedException e) { /* if waitForCompletion was called the poa state was changed to holding, discarding or inactive, the loop don't block in waitForContinue, the loop continues and will detect the changed state in the next turn (for this turn the request will not processed) */ } catch (ShutdownInProgressException e) { /* waitForShutdown was called */ waitForQueue(); } catch (org.omg.CORBA.OBJ_ADAPTER e) { requestQueue.removeFirst(); rejectRequest(request, e); } catch (org.omg.CORBA.OBJECT_NOT_EXIST e) { requestQueue.removeFirst(); rejectRequest(request, e); } catch (org.omg.CORBA.TIMEOUT e) { requestQueue.removeFirst(); rejectRequest(request, e); } } continue; } } else { if (!waitForShutdownCalled && (POAUtil.isDiscarding(state) || POAUtil.isInactive(state))) { request = requestQueue.removeLast(); /* Request available */ if (request != null) { if (POAUtil.isDiscarding(state)) { rejectRequest(request, transient_exception); } else { rejectRequest(request, closed_connection_exception); } continue; } } } /* if waitForShutdown was called the RequestController loop blocks for ALL TIME in waitForQueue (the poa behaves as if he is in holding state now) ATTENTION, it's a lazy synchronisation, a request could be rejected if waitForShutdown was called but couldn't be processed (it's save) */ waitForQueue(); } } /** * called from external thread for synchronizing with the * request controller thread, * a caller waits for completion of all active requests, * no new requests will started from now on */ synchronized void waitForCompletion() { waitForCompletionCalled = true; while (waitForCompletionCalled && !activeRequestTable.isEmpty()) { try { logger.debug("somebody waits for completion and there are active processors"); wait(); } catch (InterruptedException e) { } } } /** * called from external thread for synchronizing with the request * controller thread, a caller waits for completion of all active * requests on this object. No new requests on this object will be * started from now on because a steady stream of incoming * requests could keep the object from being deactivated, a * servant may invoke recursive method calls on the object it * incarnates and deactivation should not necessarily prevent * those invocations. */ synchronized void waitForObjectCompletion( ByteArrayKey oid ) { while (activeRequestTable.contains(oid)) { try { wait(); } catch (InterruptedException e) { } } if (logger.isDebugEnabled()) { logger.debug( POAUtil.convert(oid.getBytes ()) + "all active processors for this object have finished"); } deactivationList.add( oid ); } /** * blocks the request controller thread if the queue is empty, * the poa is in holding state or waitForShutdown was called, * if waitForShutdown was called the RequestController loop blocks for * ALL TIME in this method (the poa behaves as if he is in holding state now) */ private void waitForQueue() { if (logger.isDebugEnabled()) { logger.debug("waiting for queue"); } synchronized (queueLog) { while ((requestQueue.isEmpty() || POAUtil.isHolding (poa.the_POAManager().get_state()) || waitForShutdownCalled) && !terminate) { try { queueLog.wait(); } catch (java.lang.InterruptedException e) { // ignored } } } } /** * called from external thread for synchronizing with the * request controller thread, * a caller waits for completion of all active requests, * no new requests will started for ALL TIME */ synchronized void waitForShutdown() { waitForShutdownCalled = true; while ((waitForShutdownCalled && ! activeRequestTable.isEmpty()) || (localRequests > 0) ) { try { if (logger.isDebugEnabled()) { logger.debug("somebody waits for shutdown and there are active processors"); } wait(); } catch (InterruptedException e) { } } } synchronized void addLocalRequest() { localRequests++; } synchronized void removeLocalRequest() { localRequests--; notifyAll(); } }