@Test
  public void testCreateAggregate() {
    inMemoryEventStore.storedEvents.clear();

    testSubject =
        new DisruptorCommandBus<StubAggregate>(
            new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
            inMemoryEventStore,
            eventBus,
            new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY),
            new DisruptorConfiguration()
                .setClaimStrategy(new SingleThreadedClaimStrategy(8))
                .setWaitStrategy(new SleepingWaitStrategy()));
    testSubject.subscribe(StubCommand.class, stubHandler);
    testSubject.subscribe(CreateCommand.class, stubHandler);
    testSubject.subscribe(ErrorCommand.class, stubHandler);
    stubHandler.setRepository(testSubject);

    testSubject.dispatch(
        new GenericCommandMessage<Object>(
            new CreateCommand(aggregateIdentifier),
            singletonMap(TARGET_AGGREGATE_PROPERTY, (Object) aggregateIdentifier)));

    testSubject.stop();

    DomainEventMessage lastEvent = inMemoryEventStore.storedEvents.get(aggregateIdentifier);
    assertEquals(0, lastEvent.getSequenceNumber());
    assertEquals(aggregateIdentifier, lastEvent.getAggregateIdentifier());
  }
 @Test(timeout = 10000)
 public void testCommandsAgainstInexistentAggregatesOnlyRetriedOnce() throws Throwable {
   inMemoryEventStore.storedEvents.clear();
   testSubject =
       new DisruptorCommandBus<StubAggregate>(
           new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
           inMemoryEventStore,
           eventBus,
           new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY));
   stubHandler.setRepository(testSubject);
   StubHandler spy = spy(stubHandler);
   testSubject.subscribe(StubCommand.class, spy);
   List<CommandMessage<StubCommand>> dispatchedCommands =
       new ArrayList<CommandMessage<StubCommand>>();
   for (int i = 0; i < 10; i++) {
     CommandMessage<StubCommand> subsequentCommand =
         new GenericCommandMessage<StubCommand>(
             new StubCommand(aggregateIdentifier),
             singletonMap(TARGET_AGGREGATE_PROPERTY, (Object) aggregateIdentifier));
     testSubject.dispatch(subsequentCommand);
     dispatchedCommands.add(subsequentCommand);
   }
   testSubject.stop();
   assertTrue(inMemoryEventStore.storedEvents.isEmpty());
   for (CommandMessage<StubCommand> command : dispatchedCommands) {
     // subsequent commands could be retried more that once, as the aggregate they work in is
     // blacklisted
     verify(spy, atLeast(2)).handle(same(command), isA(UnitOfWork.class));
   }
 }
  @Test
  public void testCommandProcessedAndEventsStored() throws InterruptedException {
    testSubject =
        new DisruptorCommandBus<StubAggregate>(
            new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
            inMemoryEventStore,
            eventBus,
            new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY));
    testSubject.subscribe(StubCommand.class, stubHandler);
    stubHandler.setRepository(testSubject);

    for (int i = 0; i < COMMAND_COUNT; i++) {
      CommandMessage<StubCommand> command =
          new GenericCommandMessage<StubCommand>(
              new StubCommand(aggregateIdentifier),
              singletonMap(TARGET_AGGREGATE_PROPERTY, (Object) aggregateIdentifier));
      testSubject.dispatch(command);
    }

    inMemoryEventStore.countDownLatch.await(5, TimeUnit.SECONDS);
    eventBus.publisherCountDown.await(1, TimeUnit.SECONDS);
    assertEquals(
        "Seems that some events are not published", 0, eventBus.publisherCountDown.getCount());
    assertEquals(
        "Seems that some events are not stored", 0, inMemoryEventStore.countDownLatch.getCount());
  }
  @SuppressWarnings("unchecked")
  @Test
  public void testCallbackInvokedBeforeUnitOfWorkCleanup() throws Throwable {
    CommandHandlerInterceptor mockInterceptor = mock(CommandHandlerInterceptor.class);
    ExecutorService customExecutor = Executors.newCachedThreadPool();
    testSubject =
        new DisruptorCommandBus<StubAggregate>(
            new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
            inMemoryEventStore,
            eventBus,
            new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY),
            new DisruptorConfiguration()
                .setInvokerInterceptors(Arrays.asList(mockInterceptor))
                .setClaimStrategy(new SingleThreadedClaimStrategy(8))
                .setWaitStrategy(new SleepingWaitStrategy())
                .setExecutor(customExecutor));
    testSubject.subscribe(StubCommand.class, stubHandler);
    stubHandler.setRepository(testSubject);
    final UnitOfWorkListener mockUnitOfWorkListener = mock(UnitOfWorkListener.class);
    when(mockUnitOfWorkListener.onEventRegistered(any(EventMessage.class)))
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                return invocation.getArguments()[0];
              }
            });
    when(mockInterceptor.handle(
            any(CommandMessage.class), any(UnitOfWork.class), any(InterceptorChain.class)))
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                ((UnitOfWork) invocation.getArguments()[1])
                    .registerListener(mockUnitOfWorkListener);
                return ((InterceptorChain) invocation.getArguments()[2]).proceed();
              }
            });
    CommandMessage<StubCommand> command =
        new GenericCommandMessage<StubCommand>(
            new StubCommand(aggregateIdentifier),
            singletonMap(TARGET_AGGREGATE_PROPERTY, (Object) aggregateIdentifier));
    CommandCallback mockCallback = mock(CommandCallback.class);
    testSubject.dispatch(command, mockCallback);

    testSubject.stop();
    assertFalse(customExecutor.awaitTermination(250, TimeUnit.MILLISECONDS));
    customExecutor.shutdown();
    assertTrue(customExecutor.awaitTermination(5, TimeUnit.SECONDS));
    InOrder inOrder = inOrder(mockInterceptor, mockUnitOfWorkListener, mockCallback);
    inOrder
        .verify(mockInterceptor)
        .handle(any(CommandMessage.class), any(UnitOfWork.class), any(InterceptorChain.class));
    inOrder.verify(mockUnitOfWorkListener).onPrepareCommit(any(Set.class), any(List.class));
    inOrder.verify(mockUnitOfWorkListener).afterCommit();
    inOrder.verify(mockUnitOfWorkListener).onCleanup();

    verify(mockCallback).onSuccess(any());
  }
  @Test(expected = IllegalStateException.class)
  public void testCommandRejectedAfterShutdown() throws InterruptedException {
    testSubject =
        new DisruptorCommandBus<StubAggregate>(
            new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
            inMemoryEventStore,
            eventBus,
            new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY));
    testSubject.subscribe(StubCommand.class, stubHandler);
    stubHandler.setRepository(testSubject);

    testSubject.stop();
    testSubject.dispatch(new GenericCommandMessage<Object>(new Object()));
  }
  private CommandCallback dispatchCommands(
      CommandHandlerInterceptor mockInterceptor,
      ExecutorService customExecutor,
      GenericCommandMessage<ErrorCommand> errorCommand)
      throws Throwable {
    inMemoryEventStore.storedEvents.clear();
    testSubject =
        new DisruptorCommandBus<StubAggregate>(
            new GenericAggregateFactory<StubAggregate>(StubAggregate.class),
            inMemoryEventStore,
            eventBus,
            new MetaDataCommandTargetResolver(TARGET_AGGREGATE_PROPERTY),
            new DisruptorConfiguration()
                .setInvokerInterceptors(Arrays.asList(mockInterceptor))
                .setClaimStrategy(new MultiThreadedClaimStrategy(8))
                .setWaitStrategy(new SleepingWaitStrategy())
                .setExecutor(customExecutor)
                .setRollbackConfiguration(new RollbackOnAllExceptionsConfiguration()));
    testSubject.subscribe(StubCommand.class, stubHandler);
    testSubject.subscribe(CreateCommand.class, stubHandler);
    testSubject.subscribe(ErrorCommand.class, stubHandler);
    stubHandler.setRepository(testSubject);
    final UnitOfWorkListener mockUnitOfWorkListener = mock(UnitOfWorkListener.class);
    when(mockUnitOfWorkListener.onEventRegistered(any(EventMessage.class)))
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                return invocation.getArguments()[0];
              }
            });
    when(mockInterceptor.handle(
            any(CommandMessage.class), any(UnitOfWork.class), any(InterceptorChain.class)))
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                ((UnitOfWork) invocation.getArguments()[1])
                    .registerListener(mockUnitOfWorkListener);
                return ((InterceptorChain) invocation.getArguments()[2]).proceed();
              }
            });
    testSubject.dispatch(
        new GenericCommandMessage<CreateCommand>(
            new CreateCommand(aggregateIdentifier),
            singletonMap(TARGET_AGGREGATE_PROPERTY, aggregateIdentifier)));
    CommandCallback mockCallback = mock(CommandCallback.class);
    for (int t = 0; t < 1000; t++) {
      CommandMessage command;
      if (t % 100 == 10) {
        command = errorCommand;
      } else {
        command =
            new GenericCommandMessage<StubCommand>(
                new StubCommand(aggregateIdentifier),
                singletonMap(TARGET_AGGREGATE_PROPERTY, (Object) aggregateIdentifier));
      }
      testSubject.dispatch(command, mockCallback);
    }

    testSubject.stop();
    return mockCallback;
  }