/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.messaging.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.SerializationException;
import de.rcenvironment.core.communication.messaging.NetworkRequestHandler;
import de.rcenvironment.core.communication.model.NetworkRequest;
import de.rcenvironment.core.communication.model.NetworkResponse;
import de.rcenvironment.core.communication.protocol.NetworkResponseFactory;
import de.rcenvironment.core.communication.rpc.ServiceCallRequest;
import de.rcenvironment.core.communication.rpc.ServiceCallResult;
import de.rcenvironment.core.communication.rpc.spi.RemoteServiceCallHandlerService;
import de.rcenvironment.core.toolkitbridge.api.StaticToolkitHolder;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.toolkit.modules.statistics.api.CounterCategory;
import de.rcenvironment.toolkit.modules.statistics.api.StatisticsFilterLevel;
import de.rcenvironment.toolkit.modules.statistics.api.StatisticsTrackerService;
import de.rcenvironment.toolkit.modules.statistics.api.ValueEventCategory;
/**
* Handler for incoming RPC (remote procedure call) requests.
*
* @author Robert Mischke
*/
public class RPCNetworkRequestHandler implements NetworkRequestHandler {
private static final long SLOW_SERVICE_CALL_LOGGING_THRESHOLD_MSEC = 10000; // 10 sec
// the service to dispatch remote service calls to
private final RemoteServiceCallHandlerService serviceCallHandler;
private final CounterCategory methodCallCounter;
private final ValueEventCategory slowMethodCallCounter;
private final Log log = LogFactory.getLog(getClass());
public RPCNetworkRequestHandler(RemoteServiceCallHandlerService serviceCallHandler) {
if (serviceCallHandler == null) {
throw new NullPointerException("Service call handler cannot be null");
}
this.serviceCallHandler = serviceCallHandler;
// not injecting this via OSGi-DS as this service is planned to move to the toolkit layer anyway - misc_ro
final StatisticsTrackerService statisticsService =
StaticToolkitHolder.getServiceWithUnitTestFallback(StatisticsTrackerService.class);
methodCallCounter =
statisticsService.getCounterCategory("Remote service calls (received): service methods",
StatisticsFilterLevel.RELEASE);
slowMethodCallCounter =
statisticsService.getValueEventCategory("Remote service calls (received): slow method calls (more than "
+ SLOW_SERVICE_CALL_LOGGING_THRESHOLD_MSEC + " msec)", StatisticsFilterLevel.RELEASE);
}
@Override
public NetworkResponse handleRequest(NetworkRequest request, InstanceNodeSessionId lastHopNodeId) throws InternalMessagingException {
ServiceCallRequest serviceCallRequest = (ServiceCallRequest) NetworkRequestUtils.deserializeWithExceptionHandling(request);
ServiceCallResult scResult;
try {
if (methodCallCounter.isEnabled()) {
methodCallCounter.count(StringUtils.format("%s#%s(...)", serviceCallRequest.getServiceName(),
serviceCallRequest.getMethodName()));
}
scResult = handleInternal(serviceCallRequest);
try {
// note: RPCs that throw a declared service exception are still considered successful on the network level
return NetworkResponseFactory.generateSuccessResponse(request, scResult);
} catch (SerializationException e) {
throw new InternalMessagingException("Failed to serialize the result of a call to "
+ formatGenericCallInfo(serviceCallRequest), e);
}
} catch (RuntimeException e) {
throw new InternalMessagingException("Caught an unhandled " + e.getClass().getSimpleName() + " while processing a call to "
+ formatGenericCallInfo(serviceCallRequest), e);
}
}
private String formatGenericCallInfo(ServiceCallRequest serviceCallRequest) {
return serviceCallRequest.getServiceName() + "#" + serviceCallRequest.getMethodName() + "; caller="
+ serviceCallRequest.getCallerNodeId();
}
private ServiceCallResult handleInternal(ServiceCallRequest serviceCallRequest) throws InternalMessagingException {
long startTime = System.currentTimeMillis();
ServiceCallResult scResult = serviceCallHandler.handle(serviceCallRequest);
if (scResult == null) {
// create synthetic exception for the stacktrace; do not actually throw it
log.warn("ServiceCallResult result was null immediately after dispatching an RPC request to "
+ formatGenericCallInfo(serviceCallRequest) + " (no exception thrown; null result forwarded for now)",
new RuntimeException());
}
long duration = System.currentTimeMillis() - startTime;
if (duration >= SLOW_SERVICE_CALL_LOGGING_THRESHOLD_MSEC) {
// log
log.debug(StringUtils.format("An incoming service call from %s to %s#%s() took %,d msec to complete",
serviceCallRequest.getCallerNodeId(), serviceCallRequest.getServiceName(), serviceCallRequest.getMethodName(), duration));
// add duration to statistics
slowMethodCallCounter.registerEvent(
StringUtils.format("%s#%s", serviceCallRequest.getServiceName(), serviceCallRequest.getMethodName()), duration);
}
return scResult;
}
}