/** * Indicates whether this tracking processor supports a "reset". Generally, a reset is supported if at least one * of the event handlers assigned to this processor supports it, and no handlers explicitly prevent the resets. * * @return {@code true} if resets are supported, {@code false} otherwise */ public boolean supportsReset() { return eventHandlerInvoker().supportsReset(); }
when(eventHandlerInvoker.supportsReset()).thenReturn(true); doReturn(true).when(eventHandlerInvoker).canHandle(any(), any()); List<TrackingToken> firstRun = new CopyOnWriteArrayList<>(); List<TrackingToken> replayRun = new CopyOnWriteArrayList<>(); firstRun.add(i.<TrackedEventMessage>getArgument(0).trackingToken()); return null; }).when(eventHandlerInvoker).handle(any(), any()); replayRun.add(i.<TrackedEventMessage>getArgument(0).trackingToken()); return null; }).when(eventHandlerInvoker).handle(any(), any());
@Override public boolean canHandle(EventMessage<?> eventMessage, Segment segment) { return delegates.stream().anyMatch(i -> i.canHandle(eventMessage, segment)); }
return new DefaultInterceptorChain<>(unitOfWork, interceptors, m -> { try { eventHandlerInvoker.handle(m, segment); monitorCallback.reportSuccess(); return null;
/** * Resets tokens to the given {@code startPosition}. This effectively causes a replay of events since that position. * <p> * Note that the new token must represent a position that is <em>before</em> the current position of the processor. * <p> * Before attempting to reset the tokens, the caller must stop this processor, as well as any instances of the * same logical processor that may be running in the cluster. Failure to do so will cause the reset to fail, * as a processor can only reset the tokens if it is able to claim them all. * * @param startPosition The token representing the position to reset the processor to. */ public void resetTokens(TrackingToken startPosition) { Assert.state(supportsReset(), () -> "The handlers assigned to this Processor do not support a reset"); Assert.state(!isRunning() && activeProcessorThreads() == 0, () -> "TrackingProcessor must be shut down before triggering a reset"); transactionManager.executeInTransaction(() -> { int[] segments = tokenStore.fetchSegments(getName()); TrackingToken[] tokens = new TrackingToken[segments.length]; for (int i = 0; i < segments.length; i++) { tokens[i] = tokenStore.fetchToken(getName(), segments[i]); } // we now have all tokens, hurray eventHandlerInvoker().performReset(); for (int i = 0; i < tokens.length; i++) { tokenStore.storeToken(ReplayToken.createReplayToken(tokens[i], startPosition), getName(), segments[i]); } }); }
/** * Indicates whether the processor can/should handle the given {@code eventMessage} for the given {@code segment}. * <p> * This implementation will delegate the decision to the {@link EventHandlerInvoker}. * * @param eventMessage The message for which to identify if the processor can handle it * @param segment The segment for which the event should be processed * @return {@code true} if the event message should be handled, otherwise {@code false} * * @throws Exception if the {@code errorHandler} throws an Exception back on the * {@link ErrorHandler#handleError(ErrorContext)} call */ protected boolean canHandle(EventMessage<?> eventMessage, Segment segment) throws Exception { try { return eventHandlerInvoker.canHandle(eventMessage, segment); } catch (Exception e) { errorHandler.handleError(new ErrorContext(getName(), e, Collections.singletonList(eventMessage))); return false; } }
/** * Handle the given {@code message}, regardless of any Segmentation of processing. * * @param message The message to handle * @throws Exception when an exception occurs while handling the message * @deprecated Callers should use {@link #handle(EventMessage, Segment)} instead */ @Deprecated default void handle(EventMessage<?> message) throws Exception { handle(message, Segment.ROOT_SEGMENT); }
/** * Resets tokens to the given {@code startPosition}. This effectively causes a replay of events since that position. * <p> * Note that the new token must represent a position that is <em>before</em> the current position of the processor. * <p> * Before attempting to reset the tokens, the caller must stop this processor, as well as any instances of the * same logical processor that may be running in the cluster. Failure to do so will cause the reset to fail, * as a processor can only reset the tokens if it is able to claim them all. * * @param startPosition The token representing the position to reset the processor to. */ public void resetTokens(TrackingToken startPosition) { Assert.state(supportsReset(), () -> "The handlers assigned to this Processor do not support a reset"); Assert.state(!isRunning() && activeProcessorThreads() == 0, () -> "TrackingProcessor must be shut down before triggering a reset"); transactionManager.executeInTransaction(() -> { int[] segments = tokenStore.fetchSegments(getName()); TrackingToken[] tokens = new TrackingToken[segments.length]; for (int i = 0; i < segments.length; i++) { tokens[i] = tokenStore.fetchToken(getName(), segments[i]); } // we now have all tokens, hurray eventHandlerInvoker().performReset(); for (int i = 0; i < tokens.length; i++) { tokenStore.storeToken(ReplayToken.createReplayToken(tokens[i], startPosition), getName(), segments[i]); } }); }
@Test public void testUpdateActiveSegmentsWhenBatchIsEmpty() throws Exception { StreamableMessageSource<TrackedEventMessage<?>> stubSource = mock(StreamableMessageSource.class); testSubject = TrackingEventProcessor.builder() .name("test") .eventHandlerInvoker(eventHandlerInvoker) .messageSource(stubSource) .tokenStore(tokenStore) .transactionManager(NoTransactionManager.INSTANCE).build(); when(stubSource.openStream(any())).thenReturn(new StubTrackingEventStream(0, 1, 2, 5)); doReturn(true, false).when(eventHandlerInvoker).canHandle(any(), any()); List<TrackingToken> trackingTokens = new CopyOnWriteArrayList<>(); doAnswer(i -> { trackingTokens.add(i.<TrackedEventMessage>getArgument(0).trackingToken()); return null; }).when(eventHandlerInvoker).handle(any(), any()); testSubject.start(); // give it a bit of time to start Thread.sleep(200); EventTrackerStatus eventTrackerStatus = testSubject.processingStatus().get(0); assertTrue(eventTrackerStatus.isCaughtUp()); GapAwareTrackingToken expectedToken = GapAwareTrackingToken.newInstance(5, asList(3L, 4L)); TrackingToken lastToken = eventTrackerStatus.getTrackingToken(); assertTrue(lastToken.covers(expectedToken)); }
@Override public boolean canHandle(EventMessage<?> eventMessage, Segment segment) { return delegates.stream().anyMatch(i -> i.canHandle(eventMessage, segment)); }
return new DefaultInterceptorChain<>(unitOfWork, interceptors, m -> { try { eventHandlerInvoker.handle(m, segment); monitorCallback.reportSuccess(); return null;
/** * Indicates whether this tracking processor supports a "reset". Generally, a reset is supported if at least one * of the event handlers assigned to this processor supports it, and no handlers explicitly prevent the resets. * * @return {@code true} if resets are supported, {@code false} otherwise */ public boolean supportsReset() { return eventHandlerInvoker().supportsReset(); }
/** * Resets tokens to the given {@code startPosition}. This effectively causes a replay of events since that position. * <p> * Note that the new token must represent a position that is <em>before</em> the current position of the processor. * <p> * Before attempting to reset the tokens, the caller must stop this processor, as well as any instances of the * same logical processor that may be running in the cluster. Failure to do so will cause the reset to fail, * as a processor can only reset the tokens if it is able to claim them all. * * @param startPosition The token representing the position to reset the processor to. */ public void resetTokens(TrackingToken startPosition) { Assert.state(supportsReset(), () -> "The handlers assigned to this Processor do not support a reset"); Assert.state(!isRunning() && activeProcessorThreads() == 0, () -> "TrackingProcessor must be shut down before triggering a reset"); transactionManager.executeInTransaction(() -> { int[] segments = tokenStore.fetchSegments(getName()); TrackingToken[] tokens = new TrackingToken[segments.length]; for (int i = 0; i < segments.length; i++) { tokens[i] = tokenStore.fetchToken(getName(), segments[i]); } // we now have all tokens, hurray eventHandlerInvoker().performReset(); for (int i = 0; i < tokens.length; i++) { tokenStore.storeToken(ReplayToken.createReplayToken(tokens[i], startPosition), getName(), segments[i]); } }); }
@Override public boolean canHandle(EventMessage<?> eventMessage, Segment segment) { return delegates.stream().anyMatch(i -> i.canHandle(eventMessage, segment)); }
return new DefaultInterceptorChain<>(unitOfWork, interceptors, m -> { try { eventHandlerInvoker.handle(m, segment); monitorCallback.reportSuccess(); return null;
/** * Indicates whether this tracking processor supports a "reset". Generally, a reset is supported if at least one * of the event handlers assigned to this processor supports it, and no handlers explicitly prevent the resets. * * @return {@code true} if resets are supported, {@code false} otherwise */ public boolean supportsReset() { return eventHandlerInvoker().supportsReset(); }
/** * Indicates whether the processor can/should handle the given {@code eventMessage} for the given {@code segment}. * <p> * This implementation will delegate the decision to the {@link EventHandlerInvoker}. * * @param eventMessage The message for which to identify if the processor can handle it * @param segment The segment for which the event should be processed * @return {@code true} if the event message should be handled, otherwise {@code false} * * @throws Exception if the {@code errorHandler} throws an Exception back on the * {@link ErrorHandler#handleError(ErrorContext)} call */ protected boolean canHandle(EventMessage<?> eventMessage, Segment segment) throws Exception { try { return eventHandlerInvoker.canHandle(eventMessage, segment); } catch (Exception e) { errorHandler.handleError(new ErrorContext(getName(), e, Collections.singletonList(eventMessage))); return false; } }