package org.jacorb.poa;
/*
* JacORB - a free Java ORB
*
* Copyright (C) 1997-2015 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.Collection;
import java.util.HashMap;
import java.util.Map;
import org.jacorb.config.Configurable;
import org.jacorb.config.Configuration;
import org.jacorb.config.ConfigurationException;
import org.jacorb.orb.ORB;
import org.jacorb.orb.SystemExceptionHelper;
import org.jacorb.orb.dsi.ServerRequest;
import org.jacorb.orb.giop.ReplyOutputStream;
import org.jacorb.orb.portableInterceptor.InterceptorManager;
import org.jacorb.orb.portableInterceptor.ServerInterceptorIterator;
import org.jacorb.orb.portableInterceptor.ServerRequestInfoImpl;
import org.jacorb.poa.except.POAInternalError;
import org.jacorb.util.Time;
import org.omg.CORBA.Any;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.portable.InvokeHandler;
import org.omg.GIOP.ReplyStatusType_1_2;
import org.omg.IOP.ServiceContext;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.PortableInterceptor.SUCCESSFUL;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.omg.PortableInterceptor.USER_EXCEPTION;
import org.omg.PortableServer.DynamicImplementation;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.ServantActivator;
import org.omg.PortableServer.ServantLocator;
import org.omg.PortableServer.ServantLocatorPackage.CookieHolder;
import org.omg.PortableServer.ServantManager;
import org.slf4j.Logger;
/**
* This thread performs the request processing, the actual method invocation and
* it returns the ServerRequest object to the ORB.
*
* @author Reimo Tiedemann, FU Berlin
*/
public class RequestProcessor
extends Thread
implements InvocationContext, Configurable
{
/**
* Enumeration to represent special operations which are handled by stubs
*/
private static enum SpecialOps
{
IS_A("_is_a"),
/**
* See org.jacorb.orb.Delegate.get_interface_def
*/
INTERFACE("_interface"),
GET_POLICY("_get_policy"),
NON_EXISTENT("_non_existent"),
GET_COMPONENT("_get_component"),
REPO_ID("_repository_id"),
SET_POLICY("_set_policy_overrides");
private static final Map<String, SpecialOps> map = new HashMap<String, SpecialOps>(8);
private final String operation;
SpecialOps(String operation)
{
this.operation = operation;
}
static SpecialOps getSpecialOperation(String cf) throws ConfigurationException
{
return map.get(cf);
}
public String toString()
{
return operation;
}
}
private static final boolean JACORB_SERVANT;
/**
* ID for RequestProcessor
*/
private static int count = 0;
static
{
for (SpecialOps type : SpecialOps.values())
{
SpecialOps.map.put(type.operation, type);
}
boolean using_jacorb_servant1;
try
{
org.omg.PortableServer.Servant.class.getMethod("_repository_id");
// using JacORB supplied CORBA classes
using_jacorb_servant1 = true;
}
catch (java.lang.NoSuchMethodException ex)
{
// not using JacORB supplied CORBA classes
using_jacorb_servant1 = false;
}
JACORB_SERVANT = using_jacorb_servant1;
}
private final RPPoolManager poolManager;
private boolean start;
private boolean terminate;
private RequestController controller;
private ServerRequest request;
private Servant servant;
private ServantManager servantManager;
private CookieHolder cookieHolder;
/**
* Whether to check for expiry of any ReplyEndTimePolicy. Normally,
* it is sufficient to check this on the client side, but the additional
* check on the server side can save the server and the network some work.
* It requires that the clocks of the client and server machine are
* synchronized, though.
*/
private boolean checkReplyEndTime = false;
/**
* By default the servant code will be run in the currently executing class
* loader. This allows the servant to be run its thread context class loader
*/
private boolean useServantClassLoader = false;
/**
* this processor's logger instance, obtained from the request controller
*/
private Logger logger;
RequestProcessor(RPPoolManager _poolManager)
{
super("RequestProcessor-" + (++count));
poolManager = _poolManager;
}
public void configure(Configuration configuration)
throws ConfigurationException
{
checkReplyEndTime = configuration.getAttributeAsBoolean
(
"jacorb.poa.check_reply_end_time", false
);
useServantClassLoader = configuration.getAttributeAsBoolean
(
"jacorb.poa.useServantClassLoader", false
);
}
/**
* starts the request processor
*/
synchronized void begin()
{
start = true;
notify();
}
/**
* terminates the request processor
*/
synchronized void end()
{
terminate = true;
notify();
interrupt();
}
/**
* returns the oid associated current servant invocation
*/
public byte[] getObjectId()
{
if (!start)
{
throw new POAInternalError("error: RequestProcessor not started (getObjectId)");
}
return request.objectId();
}
/**
* returns the orb that has received the request
*/
public org.omg.CORBA.ORB getORB()
{
if (!start)
{
throw new POAInternalError("error: RequestProcessor not started (getORB)");
}
return controller.getORB();
}
/**
* returns the poa that has dispatched the request
*/
public POA getPOA()
{
if (!start)
{
throw new POAInternalError("error: RequestProcessor not started (getPOA)");
}
return controller.getPOA();
}
/**
* returns the actual servant
*/
public Servant getServant()
{
if (!start)
{
throw new POAInternalError("error: RequestProcessor not started (getServant)");
}
return servant;
}
/**
* initializes the request processor
*/
void init(RequestController requestController,
ServerRequest serverRequest,
Servant srvnt,
ServantManager manager)
{
this.controller = requestController;
this.request = serverRequest;
this.servant = srvnt;
this.servantManager = manager;
cookieHolder = null;
logger = requestController.getLogger();
}
private void clear()
{
controller = null;
request = null;
servant = null;
servantManager = null;
cookieHolder = null;
}
/**
* causes the aom to perform the incarnate call on a servant activator
*/
private void invokeIncarnate()
{
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + request.operation() +
" invoke incarnate on servant activator");
}
try
{
servant = controller.getAOM().incarnate(request.objectIdAsByteArrayKey(),
(ServantActivator) servantManager,
controller.getPOA());
if (servant == null)
{
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + request.operation() +
" incarnate: returns null");
}
request.setSystemException(new org.omg.CORBA.OBJ_ADAPTER());
}
}
catch (org.omg.CORBA.SystemException e)
{
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + request.operation() +
" incarnate: system exception was thrown.",
e);
}
request.setSystemException(e);
}
catch (org.omg.PortableServer.ForwardRequest e)
{
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + request.operation() +
" incarnate: forward exception was thrown.",
e);
}
request.setLocationForward(e);
}
catch (Throwable e)
{
/* not spec. */
if (logger.isErrorEnabled())
{
logger.error("rid: " + request.requestId() +
" opname: " + request.operation() +
" incarnate: throwable was thrown.",
e);
}
request.setSystemException(new org.omg.CORBA.OBJ_ADAPTER(e.toString()));
}
}
/**
* invokes the operation on servant,
*/
private void invokeOperation()
{
String operation = request.operation();
boolean specialOperation = false;
ClassLoader prevClassLoader = null;
Thread currentThread = null;
if (useServantClassLoader)
{
// Set the TCCL to the servant's classloader, but save the current TCCL first.
currentThread = Thread.currentThread();
prevClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(servant.getClass().getClassLoader());
}
try
{
if (servant instanceof org.omg.CORBA.portable.InvokeHandler)
{
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + operation +
" invokeOperation on servant (stream based)");
}
SpecialOps specOp = SpecialOps.getSpecialOperation(operation);
if (specOp != null)
{
if (specOp == SpecialOps.GET_POLICY)
{
// Check the number of args. If zero, then we assuming it's a "_get_policy"
// operation that was generated for an attribute named 'policy', instead
// of the 'get_policy' method on CORBA::Object
if (request.arguments() != null && request.arguments().count() >= 1)
{
specialOperation = true;
}
}
else if (JACORB_SERVANT ||
!(specOp == SpecialOps.REPO_ID || specOp == SpecialOps.GET_COMPONENT))
{
specialOperation = true;
}
}
if (specialOperation)
{
((org.jacorb.orb.ServantDelegate) servant._get_delegate())._invoke(servant,
operation,
request.getInputStream(),
request);
}
else
{
((InvokeHandler) servant)._invoke(operation,
request.getInputStream(),
request);
}
}
else if (servant instanceof org.omg.PortableServer.DynamicImplementation)
{
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + operation +
" invoke operation on servant (dsi based)");
}
SpecialOps specOps = SpecialOps.getSpecialOperation(operation);
if (specOps != null &&
!(servant instanceof org.jacorb.orb.Forwarder) &&
(JACORB_SERVANT ||
!(specOps == SpecialOps.REPO_ID || specOps == SpecialOps.GET_COMPONENT)))
{
((org.jacorb.orb.ServantDelegate) servant._get_delegate())
._invoke(servant,
operation,
request.getInputStream(),
request);
}
else
{
((DynamicImplementation) servant).invoke(request);
}
}
else
{
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + operation +
" unknown servant type (neither stream nor dsi based)");
}
}
}
catch (org.omg.CORBA.SystemException e)
{
if (logger.isInfoEnabled())
{
logger.info("rid: " + request.requestId() +
" opname: " + operation +
" invocation: system exception was thrown.",
e);
}
request.setSystemException(e);
}
catch (OutOfMemoryError e)
{
/* not spec. */
if (logger.isErrorEnabled())
{
logger.error("rid: " + request.requestId() +
" opname: " + operation +
" invocation: Caught OutOfMemory invoking operation.",
e);
}
request.setSystemException(new org.omg.CORBA.NO_MEMORY(e.toString()));
}
catch (Throwable e)
{
/* not spec. */
if (logger.isErrorEnabled())
{
logger.error("rid: " + request.requestId() +
" opname: " + operation +
" invocation: throwable was thrown.",
e);
}
request.setSystemException(new org.omg.CORBA.UNKNOWN(e.toString()));
}
finally
{
if (useServantClassLoader)
{
// Restore the original TCCL.
currentThread.setContextClassLoader(prevClassLoader);
}
}
}
/**
* performs the postinvoke call on a servant locator
*/
private void invokePostInvoke()
{
try
{
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + request.operation() +
" invoke postinvoke on servant locator");
}
((ServantLocator) servantManager).postinvoke(request.objectId(),
controller.getPOA(),
request.operation(),
cookieHolder.value,
servant);
}
catch (org.omg.CORBA.SystemException e)
{
if (logger.isInfoEnabled())
{
logger.info("rid: " + request.requestId() +
" opname: " + request.operation() +
" postinvoke: system exception was thrown.",
e);
}
request.setSystemException(e);
}
catch (Throwable e)
{
/* not spec. */
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + request.operation() +
" postinvoke: throwable was thrown.",
e);
}
request.setSystemException(new org.omg.CORBA.OBJ_ADAPTER(e.toString()));
/* which system exception I should raise? */
}
}
/**
* performs the preinvoke call on a servant locator
*/
private void invokePreInvoke()
{
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + request.operation() +
" invoke preinvoke on servant locator");
}
try
{
cookieHolder = new CookieHolder();
servant = ((ServantLocator) servantManager).preinvoke(request.objectId(),
controller.getPOA(),
request.operation(),
cookieHolder);
if (servant == null)
{
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + request.operation() +
" preinvoke: returns null");
}
request.setSystemException(new org.omg.CORBA.OBJ_ADAPTER());
}
controller.getORB().set_delegate(servant); // set the orb
}
catch (org.omg.CORBA.SystemException e)
{
if (logger.isInfoEnabled())
{
logger.info("rid: " + request.requestId() +
" opname: " + request.operation() +
" preinvoke: system exception was thrown.",
e);
}
request.setSystemException(e);
}
catch (org.omg.PortableServer.ForwardRequest e)
{
if (logger.isInfoEnabled())
{
logger.info("rid: " + request.requestId() +
" opname: " + request.operation() +
" preinvoke: forward exception was thrown.",
e);
}
request.setLocationForward(e);
}
catch (Throwable e)
{
/* not spec. */
if (logger.isWarnEnabled())
{
logger.warn("rid: " + request.requestId() +
" opname: " + request.operation() +
" preinvoke: throwable was thrown.",
e);
}
request.setSystemException(new org.omg.CORBA.OBJ_ADAPTER(e.toString()));
/* which system exception I should raise? */
}
}
boolean isActive()
{
return start;
}
/**
* the main request processing routine
*/
private void process()
{
final ORB orb = controller.getORB();
ServerRequestInfoImpl info = null;
// Notify parties interested in using information about a Transport
orb.notifyTransportListeners(request.getConnection());
if (orb.hasServerRequestInterceptors())
{
//RequestInfo attributes
info = new ServerRequestInfoImpl(orb,
request,
servant);
InterceptorManager manager = orb.getInterceptorManager();
info.setCurrent(manager.getEmptyCurrent());
if (!invokeInterceptors(info,
ServerInterceptorIterator.
RECEIVE_REQUEST_SERVICE_CONTEXTS))
{
//an interceptor bailed out, so don't continue request
//processing and return here. The service contexts for
//the result have to be set, of course.
ReplyOutputStream out = request.getReplyOutputStream();
Collection<ServiceContext> ctx = info.getReplyServiceContexts();
for (ServiceContext s : ctx)
{
out.addServiceContext(s);
}
return;
}
manager.setTSCurrent(info.current());
}
// TODO: The exception replies below should also trigger interceptors.
// Requires some re-arranging of the entire method.
if (Time.hasPassed(request.getRequestEndTime()))
{
request.setSystemException
(new org.omg.CORBA.TIMEOUT("Request End Time exceeded",
0, CompletionStatus.COMPLETED_NO));
return;
}
if (checkReplyEndTime && Time.hasPassed(request.getReplyEndTime()))
{
request.setSystemException
(new org.omg.CORBA.TIMEOUT("Reply End Time exceeded",
0, CompletionStatus.COMPLETED_NO));
return;
}
Time.waitFor(request.getRequestStartTime());
if (servantManager != null)
{
if (servantManager instanceof org.omg.PortableServer.ServantActivator)
{
invokeIncarnate();
}
else
{
invokePreInvoke();
}
orb.set_delegate(servant);
}
if (servant != null)
{
if (info != null)
{
info.setServant(servant);
if (servant instanceof org.omg.CORBA.portable.InvokeHandler)
{
if (!invokeInterceptors(info,
ServerInterceptorIterator.RECEIVE_REQUEST))
{
//an interceptor bailed out, so don't continue
//request processing and return here. The
//service contexts for the result have to be
//set, of course.
if (cookieHolder != null)
{
invokePostInvoke();
}
ReplyOutputStream out = request.getReplyOutputStream();
Collection<ServiceContext> ctx = info.getReplyServiceContexts();
for (ServiceContext s : ctx)
{
out.addServiceContext(s);
}
return;
}
}
else if (servant instanceof org.omg.PortableServer.DynamicImplementation)
{
request.setServerRequestInfo(info);
}
}
invokeOperation();
}
// preinvoke and postinvoke are always called in pairs
// but what happens if the servant is null
if (cookieHolder != null)
{
invokePostInvoke();
}
if (checkReplyEndTime && Time.hasPassed(request.getReplyEndTime()))
{
request.setSystemException
(new org.omg.CORBA.TIMEOUT("Reply End Time exceeded after invocation",
0, CompletionStatus.COMPLETED_YES));
}
if (info != null)
{
InterceptorManager manager =
orb.getInterceptorManager();
info.setCurrent(manager.getCurrent());
short op = 0;
switch (request.status().value())
{
case ReplyStatusType_1_2._NO_EXCEPTION:
{
op = ServerInterceptorIterator.SEND_REPLY;
info.setReplyStatus(SUCCESSFUL.value);
break;
}
case ReplyStatusType_1_2._USER_EXCEPTION:
{
info.setReplyStatus(USER_EXCEPTION.value);
Any sendingException = orb.create_any();
SystemExceptionHelper.insert(sendingException,
new org.omg.CORBA.UNKNOWN("Stream-based UserExceptions are not available!"));
info.sending_exception(sendingException);
op = ServerInterceptorIterator.SEND_EXCEPTION;
break;
}
case ReplyStatusType_1_2._SYSTEM_EXCEPTION:
{
info.setReplyStatus(SYSTEM_EXCEPTION.value);
Any sendingException = orb.create_any();
SystemExceptionHelper.insert(sendingException, request.getSystemException());
info.sending_exception(sendingException);
op = ServerInterceptorIterator.SEND_EXCEPTION;
break;
}
case ReplyStatusType_1_2._LOCATION_FORWARD:
{
info.setReplyStatus(LOCATION_FORWARD.value);
op = ServerInterceptorIterator.SEND_OTHER;
break;
}
}
invokeInterceptors(info, op);
ReplyOutputStream out = request.get_out();
Collection<ServiceContext> ctx = info.getReplyServiceContexts();
for (ServiceContext s : ctx)
{
out.addServiceContext(s);
}
manager.removeTSCurrent();
}
}
private boolean invokeInterceptors(ServerRequestInfoImpl info,
short op)
{
ServerInterceptorIterator intercept_iter =
controller.getORB().getInterceptorManager().getServerIterator();
try
{
intercept_iter.iterate(info, op);
}
catch (org.omg.CORBA.UserException ue)
{
if (ue instanceof org.omg.PortableInterceptor.ForwardRequest)
{
org.omg.PortableInterceptor.ForwardRequest fwd =
(org.omg.PortableInterceptor.ForwardRequest) ue;
request.setLocationForward(new org.omg.PortableServer.
ForwardRequest(fwd.forward));
}
return false;
}
catch (org.omg.CORBA.SystemException _sys_ex)
{
request.setSystemException(_sys_ex);
return false;
}
catch (Throwable e)
{
logger.error("unexpected exception during interceptor invocation", e);
UNKNOWN exception = new UNKNOWN(e.getMessage());
request.setSystemException(exception);
return false;
}
return true;
}
/**
* the main loop for request processing
*/
public void run()
{
while (true)
{
synchronized (this)
{
while (!terminate && !start)
{
try
{
wait(); /* waits for the next task */
}
catch (InterruptedException e)
{
// ignored
}
}
if (terminate)
{
return;
}
}
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + request.operation() +
" starts with request processing");
}
if (request.syncScope() == org.omg.Messaging.SYNC_WITH_SERVER.value)
{
controller.returnResult(request);
process();
}
else
{
process();
controller.returnResult(request);
}
// return the request to the request controller
if (logger.isDebugEnabled())
{
logger.debug("rid: " + request.requestId() +
" opname: " + request.operation() +
" ends with request processing");
}
controller.finish(request);
start = false;
clear();
// give back the processor into the pool
poolManager.releaseProcessor(this);
}
}
}