TaskInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { checkForBadSignatures(invocation); if (!localContext.isWorkflowInterception()) { return invocation.proceed(); } final Method method = invocation.getMethod(); final Task taskAnnotation = method.getAnnotationsByType(Task.class)[0]; final String taskIdentifier = generateTaskIdentifier(method, taskAnnotation); final List<EventDefinition> dependencySet = generateDependencySet(invocation.getArguments(),method.getParameterAnnotations(),method.getParameterTypes()); final Object proxyReturnObject = createProxyReturnObject(method); final EventDefinition outputEventDefintion = generateOutputEventDefintion(proxyReturnObject); if(localContext.getStateMachineDef() != null && localContext.getStateMachineDef().getVersion() != taskAnnotation.version()) { throw new VersionMismatchException("Mismatch between State machine and state versions for State: "+method.getDeclaringClass().getName()+"." +generateStateIdentifier(method)+". StateMachine version: "+localContext.getStateMachineDef().getVersion()+". State version: "+taskAnnotation.version()); } localContext.registerNewState(taskAnnotation.version(), generateStateIdentifier(method), null, null, taskIdentifier, taskAnnotation.retries(), taskAnnotation.timeout(), dependencySet, outputEventDefintion); executableRegistry.registerTask(taskIdentifier, new ExecutableImpl(invocation.getThis(), invocation.getMethod(), taskAnnotation.timeout())); return proxyReturnObject; } TaskInterceptor();   TaskInterceptor(LocalContext localContext,ExecutableRegistry executableRegistry, Provider<ObjectMapper> objectMapperProvider); @Override Object invoke(MethodInvocation invocation);  }
@Test public void testInterception_shouldSubmitNewState_methodWithOneParam() throws Throwable { setupMockLocalContext(); final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("simpleStringModifyingTask", StringEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod, new Object[]{new StringEvent("someEvent")})); final List<EventDefinition> expectedDependency = Collections.singletonList(new EventDefinition(STRING_EVENT_NAME+"0","com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent")); final EventDefinition expectedOutput = new EventDefinition("com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent1","com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent"); verify(localContext, times(1)). registerNewState(1l, "simpleStringModifyingTask", null, null, new MethodId(invokedMethod).toString()+_VERSION+"1", 2l, 2000l, expectedDependency, expectedOutput); }
@Test public void testInterception_shouldSubmitNewState_methodWithTwoParam() throws Throwable { setupMockLocalContext(); final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("someTaskWithIntegerAndString", StringEvent.class, IntegerEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod, new Object[]{new StringEvent("someEvent"), new IntegerEvent(1)})); final List<EventDefinition> expectedDependencies = new LinkedList<EventDefinition>() {{ add(new EventDefinition(STRING_EVENT_NAME+"0", "com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent")); add(new EventDefinition(INTEGER_EVENT_NAME+"1", "com.flipkart.flux.client.intercept.SimpleWorkflowForTest$IntegerEvent")); }}; EventDefinition expectedOutput = null; verify(localContext, times(1)). registerNewState(1l, "someTaskWithIntegerAndString", null, null, new MethodId(invokedMethod).toString()+_VERSION+"1", 0l, 1000l, expectedDependencies, expectedOutput); }
@Test public void testInterception_shouldReturnSubclassOfGivenReturnType() throws Throwable { when(localContext.generateEventName(any(Event.class))).thenReturn("generatedEventName"); final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("simpleStringModifyingTask", StringEvent.class); final Object returnedObject = taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod)); assertThat(returnedObject).isNotNull(); assertThat(StringEvent.class.isAssignableFrom(returnedObject.getClass())).isTrue(); assertThat(((Event) returnedObject).name()).isEqualTo("generatedEventName"); }
@Test public void testInterception_returnedObjectShouldImplementInterceptedInterface() throws Throwable { when(localContext.generateEventName(any(Event.class))).thenReturn("generatedEventName"); final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("simpleStringModifyingTask", StringEvent.class); final Object returnedObject = taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod)); assertThat(returnedObject).isNotNull(); assertThat(Intercepted.class.isAssignableFrom(returnedObject.getClass())).isTrue(); }
@Test public void testInterception_shouldReturnNothingIfMethodReturnsVoid() throws Throwable { final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("someTaskWithIntegerAndString", StringEvent.class, IntegerEvent.class); final Object invoke = taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod)); assertThat(invoke).isNull(); }
@Test public void shouldRegisterTaskMethodsWithRegistry() throws Throwable { final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("simpleStringModifyingTask", StringEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod, simpleWorkflowForTest)); final Executable expectedExecutable = new ExecutableImpl(simpleWorkflowForTest, invokedMethod, 2000l); verify(executableRegistry, times(1)).registerTask(new MethodId(invokedMethod).toString()+_VERSION+"1", expectedExecutable); }
@Test public void shouldPassThroughIfItsNotPartOfWorkflowInterception() throws Throwable { when(localContext.isWorkflowInterception()).thenReturn(false); final MethodInvocation dummyInvocation = TestUtil.dummyInvocation(simpleWorkflowForTest.getClass().getDeclaredMethod("simpleStringModifyingTask", StringEvent.class), simpleWorkflowForTest); taskInterceptor.invoke(dummyInvocation); verify(localContext,times(1)).isWorkflowInterception(); verifyNoMoreInteractions(localContext); verifyZeroInteractions(executableRegistry); assertThat(((MutableInt) ReflectionTestUtils.getField(dummyInvocation, "numProceedInvoctions")).getValue()).isEqualTo(1); }
@Test(expected = IllegalSignatureException.class) public void testTaskInterception_BarkIfParametersDontExtendEvent() throws Throwable { final MethodInvocation dummyInvocation = TestUtil.dummyInvocation(simpleWorkflowForTest.getClass().getDeclaredMethod("badWorkflowWithNonEventParams", String.class), simpleWorkflowForTest); taskInterceptor.invoke(dummyInvocation); }
@Test public void testRegisterExternalEventsWithTheirGivenName() throws Throwable { setupMockLocalContext(); final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("waitForExternalEvent", StringEvent.class,IntegerEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod,new Object[]{null,new IntegerEvent(1)})); final List<EventDefinition> expectedDependency = new LinkedList<EventDefinition>() {{ add(new EventDefinition("someExternalEvent", "com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent")); add(new EventDefinition(INTEGER_EVENT_NAME + "0", "com.flipkart.flux.client.intercept.SimpleWorkflowForTest$IntegerEvent")); }}; final EventDefinition expectedOutput = new EventDefinition("com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent1","com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent"); verify(localContext, times(1)). registerNewState(1l, "waitForExternalEvent", null, null, new MethodId(invokedMethod).toString()+_VERSION+"1", 2l, 2000l, expectedDependency, expectedOutput); }
@Test(expected = IllegalInvocationException.class) public void testExternalEvents_externalEventAnnotatedArgsShouldBeNull() throws Throwable { setupMockLocalContext(); final Method invokedMethod = simpleWorkflowForTest.getClass().getDeclaredMethod("waitForExternalEvent", StringEvent.class,IntegerEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(invokedMethod, new Object[]{new StringEvent("foo"), new IntegerEvent(1)})); }
@Test public void testExternalEvents_checkWithLocalContextForExistingDefs() throws Throwable { setupMockLocalContext(); final Method method1 = simpleWorkflowForTest.getClass().getDeclaredMethod("waitForExternalEvent", StringEvent.class, IntegerEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(method1,new Object[]{null,new IntegerEvent(1)})); final Method method2 = simpleWorkflowForTest.getClass().getDeclaredMethod("waitForExternalEvent", StringEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(method2,new Object[]{null})); final Method method3 = simpleWorkflowForTest.getClass().getDeclaredMethod("waitForExternalEvent", IntegerEvent.class); taskInterceptor.invoke(TestUtil.dummyInvocation(method3, new Object[]{null})); verify(localContext,times(2)).checkExistingDefinition(new EventDefinition("someExternalEvent", "com.flipkart.flux.client.intercept.SimpleWorkflowForTest$StringEvent")); verify(localContext,times(1)).checkExistingDefinition(new EventDefinition("someExternalEvent","com.flipkart.flux.client.intercept.SimpleWorkflowForTest$IntegerEvent")); }