/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.rpc.internal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.easymock.EasyMock; import org.junit.Test; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils; import de.rcenvironment.core.communication.messaging.internal.MessageEndpointHandlerImpl; import de.rcenvironment.core.communication.messaging.internal.RPCNetworkRequestHandler; import de.rcenvironment.core.communication.model.NetworkRequest; import de.rcenvironment.core.communication.model.NetworkResponse; import de.rcenvironment.core.communication.protocol.NetworkRequestFactory; import de.rcenvironment.core.communication.protocol.NetworkResponseFactory; import de.rcenvironment.core.communication.protocol.ProtocolConstants; import de.rcenvironment.core.communication.routing.MessageRoutingService; import de.rcenvironment.core.communication.routing.internal.NetworkRoutingServiceImpl; import de.rcenvironment.core.communication.rpc.ServiceCallRequest; import de.rcenvironment.core.communication.rpc.ServiceCallResult; import de.rcenvironment.core.communication.rpc.ServiceCallResultFactory; import de.rcenvironment.core.communication.rpc.api.RemotableCallbackService; import de.rcenvironment.core.communication.rpc.spi.RemoteServiceCallHandlerService; import de.rcenvironment.core.communication.spi.CallbackMethod; import de.rcenvironment.core.communication.spi.CallbackObject; import de.rcenvironment.core.utils.common.LogUtils; /** * Test case for {@link CallbackInvocationHandler}. * * @author Doreen Seider * @author Robert Mischke (reworked for 2.5.0+; 8.0.0 id adaptations) */ public class CallbackInvocationHandlerTest { private final CallbackObject callbackObject = new DummyObject(); private final String objectID = "callMe"; private final String puffParam = "knaller"; private final Integer puffIterations = 5; private final String puffMethod = "makePuff"; private final String pengMethod = "makePeng"; private final String throwMethod = "throwSomething"; private final String puff1RetVal = "puff1"; private final String puff2RetVal = "puff2"; private final String pengRetVal = "peng"; private final ServiceCallResult puffResult = ServiceCallResultFactory.wrapReturnValue(puff1RetVal); private final ServiceCallResult pengResult = ServiceCallResultFactory.wrapReturnValue(pengRetVal); private final InstanceNodeSessionId instanceSessionIdLocal = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName( "mockLocalNode"); private final InstanceNodeSessionId instanceSessionIdRemote = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName( "mockRemoteNode"); private final Log log = LogFactory.getLog(getClass()); /** * Tests various remote method calls. * * @throws Throwable on unhandled exceptions */ @Test public void test() throws Throwable { // create the simulated ServiceCallRequestPayloadHandler final MessageEndpointHandlerImpl scrHandler = new MessageEndpointHandlerImpl(NodeIdentifierTestUtils.getTestNodeIdentifierService()); scrHandler.registerRequestHandler(ProtocolConstants.VALUE_MESSAGE_TYPE_RPC, new RPCNetworkRequestHandler( new SimulatingServiceCallHandler())); // create the network layer mock MessageRoutingService messageRoutingServiceMock = EasyMock.createMock(MessageRoutingService.class); // create an EasyMock delegate that delegates to the ServiceCallRequestPayloadHandler // created above MessageRoutingService messageRoutingServiceDelegate = new NetworkRoutingServiceImpl() { @Override public NetworkResponse performRoutedRequest(final byte[] payload, final String messageType, final InstanceNodeSessionId receiver, int timeoutMsec) { NetworkRequest request = NetworkRequestFactory.createNetworkRequest(payload, messageType, instanceSessionIdLocal, receiver); NetworkResponse result; try { NodeIdentifierTestUtils.attachTestNodeIdentifierServiceToCurrentThread(); result = scrHandler.onRequestArrivedAtDestination(request); } catch (RuntimeException e) { // TODO review: is this a useful test approach? String errorId = LogUtils.logExceptionWithStacktraceAndAssignUniqueMarker(log, "Uncaught RuntimeException thrown by request handler", e); result = NetworkResponseFactory.generateResponseForInternalErrorAtRecipient(request, errorId); } NodeIdentifierTestUtils.removeTestNodeIdentifierServiceFromCurrentThread(); return result; } }; EasyMock .expect( messageRoutingServiceMock.performRoutedRequest(EasyMock.anyObject(byte[].class), EasyMock.eq(ProtocolConstants.VALUE_MESSAGE_TYPE_RPC), EasyMock.eq(instanceSessionIdLocal))) .andDelegateTo(messageRoutingServiceDelegate).anyTimes(); RemoteServiceCallSenderServiceImpl remoteServiceCallService = new RemoteServiceCallSenderServiceImpl(); remoteServiceCallService.bindMessageRoutingService(messageRoutingServiceMock); // note that this approach only works for unit tests; the (currently unavoidable) singleton // nature of the holder would lead to erratic behaviour in multi-node tests - misc_ro CallbackInvocationHandler.RemoteServiceCallServiceHolder.bindRemoteServiceCallService(remoteServiceCallService); EasyMock.replay(messageRoutingServiceMock); CallbackInvocationHandler handler = new CallbackInvocationHandler(callbackObject, objectID, instanceSessionIdLocal, instanceSessionIdRemote); Method method = CallbackProxy.class.getMethod("getObjectIdentifier", new Class[] {}); assertEquals(objectID, handler.invoke(new Object(), method, new Object[] {})); method = CallbackProxy.class.getMethod("getHomePlatform", new Class[] {}); assertEquals(instanceSessionIdLocal, handler.invoke(new Object(), method, new Object[] {})); method = DummyInterface.class.getMethod(puffMethod, new Class[] { String.class }); assertEquals(puff1RetVal, handler.invoke(new Object(), method, new Object[] { puffParam })); method = DummyInterface.class.getMethod(puffMethod, new Class[] { Integer.class }); assertEquals(puff2RetVal, handler.invoke(new Object(), method, new Object[] { puffIterations })); method = DummyInterface.class.getMethod(pengMethod, new Class[] {}); assertEquals(pengRetVal, handler.invoke(new Object(), method, null)); method = DummyInterface.class.getMethod(throwMethod, new Class[] {}); try { handler.invoke(new Object(), method, new Object[] {}); fail(); } catch (NullPointerException e) { assertTrue(true); } } /** * Test {@link RemoteServiceCallHandlerService} implementation. * * @author Doreen Seider * @author Robert Mischke (changed from ServiceCallSender to ServiceCallHandler) */ private final class SimulatingServiceCallHandler implements RemoteServiceCallHandlerService { @SuppressWarnings("unchecked") @Override public ServiceCallResult handle(ServiceCallRequest serviceCallRequest) { if (serviceCallRequest.getTargetNodeId().isSameInstanceNodeSessionAs(instanceSessionIdLocal) && serviceCallRequest.getServiceName().equals(RemotableCallbackService.class.getCanonicalName()) && serviceCallRequest.getMethodName().equals("callback") && serviceCallRequest.getParameterList().get(0).equals(objectID)) { if (serviceCallRequest.getParameterList().get(1).equals(pengMethod) && ((ArrayList<Serializable>) serviceCallRequest.getParameterList().get(2)).size() == 0) { return pengResult; } else if (serviceCallRequest.getParameterList().get(1).equals(puffMethod) && ((ArrayList<Serializable>) serviceCallRequest.getParameterList().get(2)).size() == 1 && ((ArrayList<Serializable>) serviceCallRequest.getParameterList().get(2)).get(0).equals(puffParam)) { return puffResult; } else if (serviceCallRequest.getParameterList().get(1).equals(throwMethod) && ((ArrayList<Serializable>) serviceCallRequest.getParameterList().get(2)).size() == 0) { // RPC target exceptions are wrapped in ServiceCallResults, so emulate this return ServiceCallResultFactory.wrapMethodException(new NullPointerException()); } } // TODO review: is this the appropriate wrapper factory method? return ServiceCallResultFactory.representInternalErrorAtSender(serviceCallRequest, "Test error: no service call match in " + SimulatingServiceCallHandler.class.getName()); } } /** * Dummy interface. * * @author Doreen Seider */ private interface DummyInterface extends CallbackObject { @CallbackMethod Object makePuff(String string); Object makePuff(Integer iteration); @CallbackMethod Object makePeng(); @CallbackMethod void throwSomething(); } /** * Dummy object. * * @author Doreen Seider */ private class DummyObject implements DummyInterface { private static final long serialVersionUID = 5864698550749464575L; private static final String ERROR_MESSAGE = "should never be called, because annotated to be callback remotely."; @Override public Object makePuff(String string) { throw new RuntimeException(ERROR_MESSAGE); } @Override public Object makePeng() { throw new RuntimeException(ERROR_MESSAGE); } @Override public void throwSomething() { throw new RuntimeException(ERROR_MESSAGE); } @Override public Object makePuff(Integer iteration) { return puff2RetVal; } @Override public Class<?> getInterface() { return DummyInterface.class; } } }