├── .gitignore ├── README.md ├── clustertest ├── build.sbt ├── project │ ├── ExampleBuild.scala │ └── plugins.sbt └── src │ ├── main │ └── java │ │ ├── akka │ │ └── cluster │ │ │ ├── CommandDispatcher.java │ │ │ ├── Increase.java │ │ │ ├── TestCommand.java │ │ │ ├── TestProjection.java │ │ │ └── TestProjectionFactory.java │ │ └── no │ │ └── test │ │ └── IncreaseSaga.java │ ├── multi-jvm │ └── scala │ │ └── akka │ │ ├── cluster │ │ ├── Database.scala │ │ ├── EventStore2Test.scala │ │ ├── FailureDetectorPuppet.scala │ │ ├── MultiNodeClusterSpec.scala │ │ ├── STMultiNodeSpec.scala │ │ └── SagaManagerClusterTest.scala │ │ └── multinode │ │ ├── MulitNodeSample.scala │ │ └── STMultiNodeSpec.scala │ └── test │ └── resources │ ├── logback-test.xml │ └── schema.sql ├── eventstore ├── checkstyle.xml ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── no │ │ │ │ └── ks │ │ │ │ └── eventstore2 │ │ │ │ ├── AkkaClusterInfo.java │ │ │ │ ├── DeadLetterLogger.java │ │ │ │ ├── Event.java │ │ │ │ ├── Handler.java │ │ │ │ ├── KyroSerializable.java │ │ │ │ ├── ProtobufHelper.java │ │ │ │ ├── RestartActorException.java │ │ │ │ ├── SubscriberConfigurationException.java │ │ │ │ ├── TakeBackup.java │ │ │ │ ├── TakeSnapshot.java │ │ │ │ ├── ask │ │ │ │ ├── Asker.java │ │ │ │ └── ObjectConverter.java │ │ │ │ ├── command │ │ │ │ ├── Command.java │ │ │ │ ├── CommandDispatcher.java │ │ │ │ ├── CommandHandler.java │ │ │ │ ├── HandlesCommand.java │ │ │ │ └── OnlyExecuteOnLeaderCommandHandler.java │ │ │ │ ├── eventstore │ │ │ │ ├── AbstractJournalStorage.java │ │ │ │ ├── AcknowledgePreviousEventsProcessed.java │ │ │ │ ├── AsyncSubscription.java │ │ │ │ ├── CompleteAsyncSubscriptionPleaseSendSyncSubscription.java │ │ │ │ ├── CompleteSubscriptionRegistered.java │ │ │ │ ├── EventBatch.java │ │ │ │ ├── EventStore.java │ │ │ │ ├── EventStoreKryo.java │ │ │ │ ├── EventstoreSingelton.java │ │ │ │ ├── H2JournalStorage.java │ │ │ │ ├── HandleEvent.java │ │ │ │ ├── HandleEventMetadata.java │ │ │ │ ├── IncompleteSubscriptionPleaseSendNew.java │ │ │ │ ├── JournalStorage.java │ │ │ │ ├── KryoClassRegistration.java │ │ │ │ ├── LiveSubscription.java │ │ │ │ ├── MongoDBJournalV2.java │ │ │ │ ├── MongoDbOperations.java │ │ │ │ ├── MysqlJournalStorage.java │ │ │ │ ├── NewEventstoreStarting.java │ │ │ │ ├── RefreshSubscription.java │ │ │ │ ├── RemoveSubscription.java │ │ │ │ ├── RetreiveAggregateEvents.java │ │ │ │ ├── RetrieveAggregateEventsAsync.java │ │ │ │ ├── StoreEvents.java │ │ │ │ ├── Subscription.java │ │ │ │ ├── SubscriptionRemoved.java │ │ │ │ └── UpgradeAggregate.java │ │ │ │ ├── json │ │ │ │ └── Adapter.java │ │ │ │ ├── projection │ │ │ │ ├── Call.java │ │ │ │ ├── CallProjection.java │ │ │ │ ├── ListensTo.java │ │ │ │ ├── LiveSubscriptionProjection.java │ │ │ │ ├── MongoDbProjection.java │ │ │ │ ├── MongoDbProtobufProjection.java │ │ │ │ ├── Projection.java │ │ │ │ ├── ProjectionError.java │ │ │ │ ├── ProjectionErrorListener.java │ │ │ │ ├── ProjectionFailedError.java │ │ │ │ ├── ProjectionManager.java │ │ │ │ ├── ProjectionProtobuf.java │ │ │ │ ├── ProjectionProtobufSnapshot.java │ │ │ │ ├── ProjectionSnapshot.java │ │ │ │ └── Subscriber.java │ │ │ │ ├── reflection │ │ │ │ ├── HandlerFinder.java │ │ │ │ └── HandlerFinderProtobuf.java │ │ │ │ ├── response │ │ │ │ ├── NoResult.java │ │ │ │ └── Success.java │ │ │ │ ├── saga │ │ │ │ ├── EventIdBind.java │ │ │ │ ├── InvalidSagaConfigurationException.java │ │ │ │ ├── ListensTo.java │ │ │ │ ├── MysqlSagaRepository.java │ │ │ │ ├── Saga.java │ │ │ │ ├── SagaCompositeId.java │ │ │ │ ├── SagaDatasourceRepository.java │ │ │ │ ├── SagaEventId.java │ │ │ │ ├── SagaEventIdProperty.java │ │ │ │ ├── SagaEventMapping.java │ │ │ │ ├── SagaInMemoryRepository.java │ │ │ │ ├── SagaManager.java │ │ │ │ ├── SagaMongoDBRepository.java │ │ │ │ ├── SagaRepository.java │ │ │ │ ├── SqlSagaRepository.java │ │ │ │ ├── State.java │ │ │ │ ├── TimeOutSaga.java │ │ │ │ ├── TimeOutStore.java │ │ │ │ └── UpgradeSagaRepoStore.java │ │ │ │ ├── store │ │ │ │ └── MongoDbStore.java │ │ │ │ └── util │ │ │ │ └── IdUtil.java │ │ └── protobuf │ │ │ ├── agg.proto │ │ │ └── messages.proto │ └── test │ │ ├── java │ │ └── no │ │ │ └── ks │ │ │ └── eventstore2 │ │ │ ├── EmbeddedDatabaseTest.java │ │ │ ├── EventStoreTest.java │ │ │ ├── Eventstore2TestKit.java │ │ │ ├── FormProcessorIntegrationTest.java │ │ │ ├── ProtobufHelperTest.java │ │ │ ├── command │ │ │ ├── CommandDispatcherTest.java │ │ │ ├── CommandHandlerNewStyleAnnotationsTest.java │ │ │ └── CommandHandlerTest.java │ │ │ ├── events │ │ │ ├── Event1.java │ │ │ ├── Event2.java │ │ │ ├── Event3.java │ │ │ ├── Event4.java │ │ │ ├── NewEvent.java │ │ │ ├── OldEvent.java │ │ │ └── OldEventShouldBeUpgradedToOrderSearchResult.java │ │ │ ├── eventstore │ │ │ ├── AggEvent.java │ │ │ ├── EvenstoreTestReadAggregateEvents.java │ │ │ ├── EvenstoreTestReadAggregateProtoEvents.java │ │ │ ├── EventListProjection.java │ │ │ ├── EventStoreProtoTest.java │ │ │ ├── EventStoreTest.java │ │ │ ├── H2JournalStorageTest.java │ │ │ ├── MongoDBJournalv2ProtoEventsTest.java │ │ │ └── MongoDBJournalv2Test.java │ │ │ ├── formProcessorProject │ │ │ ├── DeliverForm.java │ │ │ ├── DummyActor.java │ │ │ ├── FormDelivered.java │ │ │ ├── FormDeliverer.java │ │ │ ├── FormParsed.java │ │ │ ├── FormParser.java │ │ │ ├── FormProcess.java │ │ │ ├── FormReceived.java │ │ │ ├── FormStatus.java │ │ │ ├── FormStatuses.java │ │ │ └── ParseForm.java │ │ │ ├── projection │ │ │ ├── FailingProjection.java │ │ │ ├── FutureCallProjection.java │ │ │ ├── LiveSubscriptionProjectionTest.java │ │ │ ├── MongoDbEventstore2TestKit.java │ │ │ ├── ProjectionCallTest.java │ │ │ ├── ProjectionErrorListenerTest.java │ │ │ ├── ProjectionManagerTest.java │ │ │ ├── ProjectionPolymorphismEventTest.java │ │ │ ├── ProjectionProtobufSnapshotTest.java │ │ │ ├── ProjectionProtobufSubscriptionTest.java │ │ │ ├── ProjectionProtobufTest.java │ │ │ ├── ProjectionReceiveEventTest.java │ │ │ ├── ProjectionSnapshotTest.java │ │ │ ├── ProjectionSubscriptionTest.java │ │ │ ├── RestartProjectionProtobufTest.java │ │ │ ├── RestartProjectionTest.java │ │ │ └── TestLiveSubscriptionProjection.java │ │ │ ├── saga │ │ │ ├── SagaDatasourceRepositoryTest.java │ │ │ ├── SagaManagerNewAnnotationStyleTest.java │ │ │ ├── SagaManagerPolymorphismEventTest.java │ │ │ ├── SagaManagerTest.java │ │ │ ├── SagaMongoDBRepositoryTest.java │ │ │ ├── SagaNewAnnotationStyleTest.java │ │ │ ├── SagaPolymorphismEventTest.java │ │ │ ├── SagaTest.java │ │ │ ├── SagaTimerUt.java │ │ │ └── TiemoutSagaTest.java │ │ │ ├── testapplication │ │ │ ├── AggregateType.java │ │ │ ├── TestEvent.java │ │ │ └── TestProjection.java │ │ │ └── testkit │ │ │ ├── EventReceiver.java │ │ │ └── EventStoreTestKit.java │ │ ├── protobuf │ │ ├── Order.proto │ │ └── form.proto │ │ └── resources │ │ ├── application.conf │ │ └── schema.sql └── suppressions.xml ├── pom.xml └── suppressions.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | bin 3 | *.iml 4 | *.iws 5 | *.ipr 6 | .classpath 7 | .project 8 | .settings 9 | .vagrant 10 | .idea 11 | checkstylecachefile 12 | -------------------------------------------------------------------------------- /clustertest/build.sbt: -------------------------------------------------------------------------------- 1 | name := "clustertests" 2 | 3 | version := "2.2-SNAPSHOT" 4 | 5 | scalaVersion := "2.10.2" 6 | 7 | resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" 8 | 9 | resolvers += "artifactory" at "http://jenkins.usrv.ubergenkom.no/artifactory/libs-snapshot-local" 10 | 11 | resolvers += "Local Maven Repository" at "file:///"+Path.userHome.absolutePath+"/.m2/repository" 12 | 13 | libraryDependencies += "no.ks" % "eventstore2" % "2.2-SNAPSHOT" 14 | 15 | libraryDependencies+= "ch.qos.logback" % "logback-classic" % "1.0.13" 16 | 17 | libraryDependencies+= "org.springframework" % "spring-context" % "3.1.3.RELEASE" 18 | 19 | libraryDependencies+= "org.springframework" % "spring-jdbc" % "3.1.3.RELEASE" 20 | 21 | libraryDependencies+= "org.springframework" % "spring-test" % "3.1.3.RELEASE" -------------------------------------------------------------------------------- /clustertest/project/ExampleBuild.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import com.typesafe.sbt.SbtMultiJvm 4 | import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys.{ MultiJvm } 5 | 6 | object ExampleBuild extends Build { 7 | 8 | lazy val buildSettings = Defaults.defaultSettings ++ multiJvmSettings ++ Seq( 9 | organization := "example", 10 | version := "1.0", 11 | scalaVersion := "2.10.2", 12 | // make sure that the artifacts don't have the scala version in the name 13 | crossPaths := false 14 | ) 15 | 16 | lazy val example = Project( 17 | id = "example", 18 | base = file("."), 19 | settings = buildSettings ++ 20 | Seq(libraryDependencies ++= Dependencies.example) 21 | ) configs(MultiJvm) 22 | 23 | lazy val multiJvmSettings = SbtMultiJvm.multiJvmSettings ++ Seq( 24 | // make sure that MultiJvm test are compiled by the default test compilation 25 | compile in MultiJvm <<= (compile in MultiJvm) triggeredBy (compile in Test), 26 | // disable parallel tests 27 | parallelExecution in Test := false, 28 | // make sure that MultiJvm tests are executed by the default test target 29 | executeTests in Test <<= 30 | ((executeTests in Test), (executeTests in MultiJvm)) map { 31 | case ((_, testResults), (_, multiJvmResults)) => 32 | val results = testResults ++ multiJvmResults 33 | (Tests.overall(results.values), results) 34 | } 35 | ) 36 | 37 | object Dependencies { 38 | val example = Seq( 39 | // ---- application dependencies ---- 40 | "com.typesafe.akka" %% "akka-actor" % "2.2.1" , 41 | "com.typesafe.akka" %% "akka-remote" % "2.2.1" , 42 | 43 | // ---- test dependencies ---- 44 | "com.typesafe.akka" %% "akka-testkit" % "2.2.1" % 45 | "test" , 46 | "com.typesafe.akka" %% "akka-multi-node-testkit" % "2.2.1" % 47 | "test" , 48 | "org.scalatest" %% "scalatest" % "1.9.1" % "test", 49 | "junit" % "junit" % "4.5" % "test" 50 | ) 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /clustertest/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-multi-jvm" % "0.3.6") -------------------------------------------------------------------------------- /clustertest/src/main/java/akka/cluster/CommandDispatcher.java: -------------------------------------------------------------------------------- 1 | package akka.cluster; 2 | 3 | import akka.actor.UntypedActor; 4 | import no.ks.eventstore2.command.Command; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class CommandDispatcher extends UntypedActor { 10 | 11 | 12 | public List commands = new ArrayList(); 13 | 14 | /** 15 | * To be implemented by concrete UntypedActor, this defines the behavior of the 16 | * UntypedActor. 17 | */ 18 | @Override 19 | public void onReceive(Object message) { 20 | 21 | if (message instanceof Command) { 22 | System.out.println("Got command " + message + " from " + sender().path()); 23 | commands.add((Command) message); 24 | } 25 | if("list".equals(message)){ 26 | System.out.println("Size is " + commands.size()); 27 | sender().tell(commands.size()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /clustertest/src/main/java/akka/cluster/Increase.java: -------------------------------------------------------------------------------- 1 | package akka.cluster; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class Increase extends Event { 6 | 7 | private String id="id"; 8 | 9 | public String getId() { 10 | return id; 11 | } 12 | 13 | public void setId(String id) { 14 | this.id = id; 15 | } 16 | 17 | public Increase() { 18 | aggregateId = "test"; 19 | } 20 | 21 | @Override 22 | public String getLogMessage() { 23 | return toString(); 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "Increase{" + 29 | "id='" + id + '\'' + 30 | '}'; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /clustertest/src/main/java/akka/cluster/TestCommand.java: -------------------------------------------------------------------------------- 1 | package akka.cluster; 2 | 3 | import no.ks.eventstore2.command.Command; 4 | 5 | import java.io.Serializable; 6 | 7 | public class TestCommand extends Command implements Serializable { 8 | } 9 | -------------------------------------------------------------------------------- /clustertest/src/main/java/akka/cluster/TestProjection.java: -------------------------------------------------------------------------------- 1 | package akka.cluster; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.projection.ListensTo; 5 | import no.ks.eventstore2.projection.Projection; 6 | 7 | @ListensTo(value = {Increase.class}, aggregates = "test") 8 | public class TestProjection extends Projection { 9 | 10 | public TestProjection(ActorRef eventStore) { 11 | super(eventStore); 12 | } 13 | 14 | int count = 0; 15 | 16 | public void handleEvent(Increase event){ 17 | System.out.println("IncreasingCount"); 18 | count++; 19 | } 20 | 21 | public Integer getCount(){ 22 | return count; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /clustertest/src/main/java/akka/cluster/TestProjectionFactory.java: -------------------------------------------------------------------------------- 1 | package akka.cluster; 2 | 3 | import akka.actor.Actor; 4 | import akka.actor.ActorRef; 5 | import no.ks.eventstore2.projection.Projection; 6 | import no.ks.eventstore2.projection.ProjectionFactory; 7 | 8 | public class TestProjectionFactory extends ProjectionFactory { 9 | 10 | protected TestProjectionFactory(ActorRef eventstore) { 11 | super(eventstore); 12 | } 13 | 14 | @Override 15 | public Class getProjectionClass() { 16 | return TestProjection.class; 17 | } 18 | 19 | /** 20 | * This method must return a different instance upon every call. 21 | */ 22 | @Override 23 | public Actor create() { 24 | return new TestProjection(eventstore); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /clustertest/src/main/java/no/test/IncreaseSaga.java: -------------------------------------------------------------------------------- 1 | package no.test; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.cluster.Increase; 5 | import akka.cluster.TestCommand; 6 | import no.ks.eventstore2.saga.EventIdBind; 7 | import no.ks.eventstore2.saga.ListensTo; 8 | import no.ks.eventstore2.saga.Saga; 9 | import no.ks.eventstore2.saga.SagaRepository; 10 | 11 | @ListensTo(value = @EventIdBind(eventClass = Increase.class, idProperty = "id"), aggregates = "test") 12 | public class IncreaseSaga extends Saga { 13 | 14 | public IncreaseSaga(String id, ActorRef commandDispatcher, SagaRepository repository) { 15 | super(id, commandDispatcher, repository); 16 | } 17 | 18 | public void handleEvent(Increase event){ 19 | System.out.println("Got increase event " + event + " state is " + getState()); 20 | if(getState() == STATE_INITIAL){ 21 | transitionState(STATE_FINISHED); 22 | commandDispatcher.tell(new TestCommand()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /clustertest/src/multi-jvm/scala/akka/cluster/Database.scala: -------------------------------------------------------------------------------- 1 | package akka.cluster 2 | 3 | import org.h2.jdbcx.JdbcDataSource 4 | import org.springframework.test.jdbc.{JdbcTestUtils, SimpleJdbcTestUtils} 5 | import org.springframework.core.io.ClassPathResource 6 | import org.springframework.jdbc.core.JdbcTemplate 7 | import org.springframework.jdbc.core.simple.SimpleJdbcTemplate 8 | import reflect.io.File 9 | import javax.sql.DataSource 10 | 11 | object Database { 12 | 13 | def datasource = { 14 | val ds = new JdbcDataSource(); 15 | ds.setURL("jdbc:h2:/tmp/test"); 16 | ds.setUser("sa"); 17 | ds.setPassword("sa"); 18 | ds; 19 | } 20 | 21 | def setupDatabase(ds:DataSource){ 22 | val template = new SimpleJdbcTemplate(ds); 23 | val resource = new ClassPathResource("schema.sql"); 24 | SimpleJdbcTestUtils.executeSqlScript(template, resource, true); 25 | } 26 | 27 | def cleanDatabase = { 28 | File("/tmp/test.h2.db").deleteIfExists(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /clustertest/src/multi-jvm/scala/akka/cluster/FailureDetectorPuppet.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2009-2012 Typesafe Inc. 3 | */ 4 | 5 | package akka.cluster 6 | 7 | import akka.actor.{ Address, ActorSystem } 8 | import akka.event.{ Logging, LogSource } 9 | import akka.remote.testkit.MultiNodeConfig 10 | import akka.cluster.{FailureDetector, ClusterSettings} 11 | 12 | /** 13 | * User controllable "puppet" failure detector. 14 | */ 15 | class FailureDetectorPuppet(system: ActorSystem, settings: ClusterSettings) extends FailureDetector { 16 | import java.util.concurrent.ConcurrentHashMap 17 | 18 | trait Status 19 | object Up extends Status 20 | object Down extends Status 21 | 22 | implicit private val logSource: LogSource[AnyRef] = new LogSource[AnyRef] { 23 | def genString(o: AnyRef): String = o.getClass.getName 24 | override def getClazz(o: AnyRef): Class[_] = o.getClass 25 | } 26 | 27 | private val log = Logging(system, this) 28 | 29 | private val connections = new ConcurrentHashMap[Address, Status] 30 | 31 | def markNodeAsUnavailable(connection: Address): this.type = { 32 | connections.put(connection, Down) 33 | this 34 | } 35 | 36 | def markNodeAsAvailable(connection: Address): this.type = { 37 | connections.put(connection, Up) 38 | this 39 | } 40 | 41 | def isAvailable(connection: Address): Boolean = connections.get(connection) match { 42 | case null ⇒ 43 | log.debug("Adding cluster node [{}]", connection) 44 | connections.put(connection, Up) 45 | true 46 | case Up ⇒ 47 | log.debug("isAvailable: Cluster node IS NOT available [{}]", connection) 48 | true 49 | case Down ⇒ 50 | log.debug("isAvailable: Cluster node IS available [{}]", connection) 51 | false 52 | } 53 | 54 | def heartbeat(connection: Address): Unit = log.debug("Heart beat from cluster node[{}]", connection) 55 | 56 | def remove(connection: Address): Unit = { 57 | log.debug("Removing cluster node [{}]", connection) 58 | connections.remove(connection) 59 | } 60 | 61 | def reset(): Unit = { 62 | log.debug("Resetting failure detector") 63 | connections.clear() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /clustertest/src/multi-jvm/scala/akka/cluster/STMultiNodeSpec.scala: -------------------------------------------------------------------------------- 1 | package akka.cluster 2 | 3 | import akka.remote.testkit.MultiNodeSpecCallbacks 4 | import org.scalatest.matchers.MustMatchers 5 | import org.scalatest.{BeforeAndAfterAll, WordSpec} 6 | 7 | trait STMultiNodeSpec extends MultiNodeSpecCallbacks with WordSpec with MustMatchers with BeforeAndAfterAll { 8 | override def beforeAll() = multiNodeSpecBeforeAll() 9 | override def afterAll() = multiNodeSpecAfterAll() 10 | } -------------------------------------------------------------------------------- /clustertest/src/multi-jvm/scala/akka/multinode/MulitNodeSample.scala: -------------------------------------------------------------------------------- 1 | package akka.multinode 2 | 3 | import akka.testkit.ImplicitSender 4 | import akka.actor.{ActorSystem, Props, Actor} 5 | import akka.remote.testkit.{MultiNodeSpecCallbacks, MultiNodeConfig, MultiNodeSpec} 6 | import org.scalatest.{BeforeAndAfterAll, WordSpec} 7 | import org.scalatest.matchers.MustMatchers 8 | import akka.multinode.STMultiNodeSpec 9 | import no.ks.eventstore2.eventstore.EventStoreFactory 10 | import org.springframework.jdbc.datasource.embedded.{EmbeddedDatabaseType, EmbeddedDatabaseBuilder} 11 | import akka.cluster.routing.{ClusterRouterSettings, ClusterRouterConfig} 12 | import akka.routing.RoundRobinRouter 13 | import scala.concurrent.duration._ 14 | import akka.cluster.{Increase, TestProjection} 15 | import concurrent.Await 16 | import com.typesafe.config.ConfigFactory 17 | 18 | class MultiNodeSampleSpecMultiJvmNode1 extends MultiNodeSample 19 | class MultiNodeSampleSpecMultiJvmNode2 extends MultiNodeSample 20 | 21 | object MultiNodeSampleConfig extends MultiNodeConfig { 22 | val node1 = role("node1") 23 | val node2 = role("node2") 24 | } 25 | 26 | class MultiNodeSample extends MultiNodeSpec(MultiNodeSampleConfig) 27 | with STMultiNodeSpec with ImplicitSender { 28 | 29 | import MultiNodeSampleConfig._ 30 | 31 | def initialParticipants = roles.size 32 | 33 | "A MultiNodeSample" must { 34 | 35 | "wait for all nodes to enter a barrier" in { 36 | enterBarrier("startup") 37 | } 38 | 39 | /* "be able to send event from one node to another" in { 40 | 41 | runOn(node1){ 42 | val eventStoreFactory: EventStoreFactory = new EventStoreFactory() 43 | val builder: EmbeddedDatabaseBuilder = new EmbeddedDatabaseBuilder 44 | val db = builder.setType(EmbeddedDatabaseType.H2).addScript("schema.sql").build 45 | eventStoreFactory.setDs(db); 46 | eventStoreFactory.addRemoteEventStores(system.actorFor(node(node2) / "user" / "eventStore")); 47 | println("setup" + system.actorOf(Props(eventStoreFactory),name = "eventStore").path) 48 | 49 | system.actorOf(Props[TestProjection],"testProjection") 50 | } 51 | 52 | runOn(node2){ 53 | val eventStoreFactory: EventStoreFactory = new EventStoreFactory() 54 | val builder: EmbeddedDatabaseBuilder = new EmbeddedDatabaseBuilder 55 | val db = builder.setType(EmbeddedDatabaseType.H2).addScript("schema.sql").build 56 | eventStoreFactory.setDs(db); 57 | eventStoreFactory.addRemoteEventStores(system.actorFor(node(node1) / "user" / "eventStore")); 58 | println("setup" + system.actorOf(Props(eventStoreFactory),name = "eventStore").path) 59 | } 60 | enterBarrier("startup_finished") 61 | runOn(node2){ 62 | system.actorFor(node(node1) / "user" / "eventStore") ! new Increase 63 | } 64 | 65 | runOn(node1){ 66 | import no.ks.eventstore2.projection.CallProjection.call 67 | import akka.pattern.{ ask, pipe} 68 | var result = 0; 69 | while(result == 0){ 70 | Thread.sleep(100); 71 | val future = system.actorFor("akka://MultiNodeSample/user/testProjection").ask(call("getCount"))(5 seconds) 72 | result = Await.result(future, 5.seconds).asInstanceOf[Integer] 73 | 74 | } 75 | assert(result == 1) 76 | } 77 | enterBarrier("finished") 78 | } 79 | 80 | "be able to handle node down" in { 81 | runOn(node2){ 82 | system.shutdown(); 83 | 84 | testConductor.removeNode(myself) 85 | 86 | } 87 | 88 | 89 | }*/ 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /clustertest/src/multi-jvm/scala/akka/multinode/STMultiNodeSpec.scala: -------------------------------------------------------------------------------- 1 | package akka.multinode 2 | 3 | import akka.remote.testkit.MultiNodeSpecCallbacks 4 | import org.scalatest.matchers.MustMatchers 5 | import org.scalatest.{BeforeAndAfterAll, WordSpec} 6 | 7 | trait STMultiNodeSpec extends MultiNodeSpecCallbacks with WordSpec with MustMatchers with BeforeAndAfterAll { 8 | override def beforeAll() = multiNodeSpecBeforeAll() 9 | override def afterAll() = multiNodeSpecAfterAll() 10 | } -------------------------------------------------------------------------------- /clustertest/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | SvarUt 3 | 4 | 5 | 7 | 8 | %-4relative [%thread] %-5level %logger{35} - %msg %n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /clustertest/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE SEQUENCE seq; 2 | 3 | create table Event( 4 | id number primary key, 5 | aggregateid varchar(255), 6 | class varchar(255), 7 | kryoeventdata BLOB, 8 | dataversion integer default 0 9 | ); 10 | 11 | create table Saga( 12 | id varchar(255), 13 | clazz varchar(255), 14 | state tinyint, 15 | PRIMARY KEY(id, clazz) 16 | ); -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/AkkaClusterInfo.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import akka.ConfigurationException; 4 | import akka.actor.ActorRef; 5 | import akka.actor.ActorSystem; 6 | import akka.actor.Address; 7 | import akka.cluster.Cluster; 8 | import akka.cluster.ClusterEvent; 9 | import akka.cluster.MemberStatus; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | public class AkkaClusterInfo { 14 | private static Logger log = LoggerFactory.getLogger(AkkaClusterInfo.class); 15 | 16 | private ActorSystem system; 17 | private Address leaderAdress; 18 | private boolean leader; 19 | private ActorRef subscriber; 20 | 21 | public AkkaClusterInfo(ActorSystem system) { 22 | this.system = system; 23 | } 24 | 25 | public void subscribeToClusterEvents(ActorRef subscriber) { 26 | this.subscriber = subscriber; 27 | try { 28 | Cluster cluster = Cluster.get(system); 29 | cluster.subscribe(subscriber, ClusterEvent.ClusterDomainEvent.class); 30 | log.info("{} subscribes to cluster events", subscriber); 31 | } catch (ConfigurationException e) { 32 | } 33 | } 34 | 35 | public void updateLeaderState(ClusterEvent.LeaderChanged leaderChanged) { 36 | try { 37 | Cluster cluster = Cluster.get(system); 38 | boolean oldLeader = leader; 39 | if (leaderChanged == null ) { 40 | boolean notReady = true; 41 | while (!cluster.readView().self().status().equals(MemberStatus.up())) { 42 | try { 43 | Thread.sleep(1000); 44 | } catch (InterruptedException e) {} 45 | } 46 | leader = cluster.readView().isLeader(); 47 | leaderAdress = cluster.readView().leader().get(); 48 | } else { 49 | leaderAdress = leaderChanged.getLeader(); 50 | leader = cluster.readView().selfAddress().equals(leaderAdress); 51 | } 52 | log.info("{} leader changed from {} to {}",subscriber, oldLeader, leader); 53 | log.debug("leader adress {}", leaderAdress); 54 | 55 | } catch (ConfigurationException e) { 56 | log.debug("Not cluster system"); 57 | leader = true; 58 | } 59 | } 60 | 61 | public Address getLeaderAdress() { 62 | return leaderAdress; 63 | } 64 | 65 | public boolean isLeader() { 66 | return leader; 67 | } 68 | 69 | public boolean amIUp() { 70 | try { 71 | Cluster cluster = Cluster.get(system); 72 | return cluster.readView().self().status().equals(MemberStatus.up()); 73 | } catch (ConfigurationException e) { 74 | return true; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/DeadLetterLogger.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import akka.actor.DeadLetter; 4 | import akka.actor.UntypedActor; 5 | import no.ks.eventstore2.response.Success; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public class DeadLetterLogger extends UntypedActor { 10 | private static Logger log = LoggerFactory.getLogger(DeadLetterLogger.class); 11 | 12 | @Override 13 | public void preStart() { 14 | getContext().system().eventStream().subscribe(self(), DeadLetter.class); 15 | } 16 | 17 | @Override 18 | public void onReceive(Object o) throws Exception { 19 | if(o instanceof DeadLetter && ((DeadLetter) o).message() instanceof Success) { 20 | return; 21 | } 22 | log.warn("Dead letter: {}", o); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/Event.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import eventstore.Messages; 4 | import org.joda.time.DateTime; 5 | 6 | import java.io.Serializable; 7 | 8 | public abstract class Event implements Serializable { 9 | private static final long serialVersionUID = 1L; 10 | 11 | private String journalid; 12 | protected DateTime created; 13 | private int version = -1; 14 | 15 | public abstract String getAggregateType(); 16 | 17 | public DateTime getCreated() { 18 | return created; 19 | } 20 | 21 | public void setCreated(DateTime created) { 22 | this.created = created; 23 | } 24 | 25 | public abstract String getLogMessage(); 26 | 27 | public Event upgrade() { 28 | return this; 29 | } 30 | 31 | public String getJournalid() { 32 | return journalid; 33 | } 34 | 35 | public void setJournalid(String journalid) { 36 | this.journalid = journalid; 37 | } 38 | 39 | public abstract String getAggregateRootId(); 40 | 41 | @Override 42 | public String toString() { 43 | return "Event{" 44 | + "aggregateType='" + getAggregateType() + '\'' 45 | + "aggregateRootId='" + getAggregateRootId() + '\'' 46 | + ", journalid='" + getJournalid() + '\'' 47 | + ", created=" + created 48 | + '}'; 49 | } 50 | 51 | public int getVersion() { 52 | return version; 53 | } 54 | 55 | public void setVersion(int version) { 56 | this.version = version; 57 | } 58 | 59 | public Messages.EventWrapper upgradeToProto(){throw new RuntimeException("UpgradeToProto not implemented in " + this.getClass().toString());} 60 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/Handler.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Handler { 11 | } 12 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/KyroSerializable.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import java.io.Serializable; 4 | 5 | public interface KyroSerializable extends Serializable { 6 | } 7 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/RestartActorException.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | public class RestartActorException extends RuntimeException{ 4 | public RestartActorException() { 5 | } 6 | 7 | public RestartActorException(String message) { 8 | super(message); 9 | } 10 | 11 | public RestartActorException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public RestartActorException(Throwable cause) { 16 | super(cause); 17 | } 18 | 19 | public RestartActorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 20 | super(message, cause, enableSuppression, writableStackTrace); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/SubscriberConfigurationException.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | public class SubscriberConfigurationException extends RuntimeException { 4 | public SubscriberConfigurationException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/TakeBackup.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | public class TakeBackup implements KyroSerializable { 4 | 5 | private String backupdir; 6 | 7 | public TakeBackup() { 8 | } 9 | 10 | public TakeBackup(String backupdir) { 11 | this.backupdir = backupdir; 12 | } 13 | 14 | public String getBackupdir() { 15 | return backupdir; 16 | } 17 | 18 | public void setBackupdir(String backupdir) { 19 | this.backupdir = backupdir; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "Take backup to dir " + backupdir; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/TakeSnapshot.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | public class TakeSnapshot implements KyroSerializable { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/ask/Asker.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.ask; 2 | 3 | 4 | import akka.actor.ActorRef; 5 | import no.ks.eventstore2.command.Command; 6 | import scala.concurrent.Await; 7 | import scala.concurrent.Future; 8 | import scala.concurrent.duration.Duration; 9 | import scala.util.Failure; 10 | 11 | import static akka.pattern.Patterns.ask; 12 | import static no.ks.eventstore2.projection.CallProjection.call; 13 | 14 | public class Asker { 15 | 16 | private static final int THREE_SECONDS = 3000; 17 | private static final int TEN_SECONDS = 10000; 18 | 19 | private static final String THREE_SECONDS_STR = "3 seconds"; 20 | private static final String TEN_SECONDS_STR = "10 seconds"; 21 | 22 | private Asker(){} 23 | 24 | public static ObjectConverter askProjection(ActorRef projection, String method, Object... args) throws Exception { 25 | if(isNotNull(projection, method, args)) { 26 | Future future = ask(projection, call(method, args), THREE_SECONDS); 27 | return new ObjectConverter(Await.result(future, Duration.create(THREE_SECONDS_STR))); 28 | } 29 | throw new RuntimeException("Unexpected error occurred"); 30 | } 31 | 32 | public static void dispatchCommand(ActorRef commandDispatcher, Command command) { 33 | Future future = ask(commandDispatcher, command, TEN_SECONDS); 34 | try { 35 | Object result = Await.result(future, Duration.create(TEN_SECONDS_STR)); 36 | if (result instanceof Failure) { 37 | throw ((Failure) result).exception(); 38 | } 39 | } catch (RuntimeException e) { 40 | throw e; 41 | } catch (Throwable e) { 42 | throw new RuntimeException("Could not dispatch command", e); 43 | } 44 | } 45 | 46 | private static boolean isNotNull(ActorRef projection, String method, Object... args) { 47 | if(projection == null) { 48 | throw new RuntimeException("Projection actor-ref can't be null when trying to call method '" + method + "'"); 49 | } else if(method == null) { 50 | throw new RuntimeException("Name of method can't be null when calling the projection actor-ref '" + projection + "'"); 51 | } else if(args != null && isOneArgumentNull(args)) { 52 | throw new RuntimeException("Arguments for the method '" + method + "' can't be null " + getErrorMessage(args)); 53 | } 54 | return true; 55 | } 56 | 57 | private static boolean isOneArgumentNull(Object[] args) { 58 | boolean isNull = false; 59 | for(Object arg : args) { 60 | if(arg == null) { 61 | isNull = true; 62 | } 63 | } 64 | return isNull; 65 | } 66 | 67 | private static String getErrorMessage(Object[] args) { 68 | String error = "args=["; 69 | for(Object arg : args) { 70 | error += " '" + arg + "' "; 71 | } 72 | error += "]"; 73 | return error; 74 | } 75 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/ask/ObjectConverter.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.ask; 2 | 3 | import no.ks.eventstore2.response.NoResult; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class ObjectConverter { 9 | 10 | private Object object; 11 | 12 | public ObjectConverter(Object object) { 13 | this.object = object; 14 | } 15 | 16 | public T single(Class clz) { 17 | if(clz.isInstance(object)) { 18 | return (T) object; 19 | } 20 | return null; 21 | } 22 | 23 | public List list(Class clz) { 24 | return (List) object; 25 | } 26 | 27 | public Map map(Class clz) { 28 | if(object instanceof NoResult) { 29 | return null; 30 | } 31 | return (Map) object; 32 | } 33 | 34 | public Map map(Class keyClz, Class valueClz) { 35 | return (Map) object; 36 | } 37 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/command/Command.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import java.io.Serializable; 4 | 5 | public abstract class Command implements Serializable{ 6 | 7 | } 8 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/command/CommandDispatcher.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import akka.actor.UntypedActor; 6 | import com.google.common.collect.ImmutableSet; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class CommandDispatcher extends UntypedActor { 15 | 16 | private static Logger log = LoggerFactory.getLogger(CommandDispatcher.class); 17 | 18 | private List commandHandlerProps; 19 | private ActorRef eventStore; 20 | private Map, ActorRef> commandHandlers = new HashMap, ActorRef>(); 21 | 22 | private int remainingCommandHandlers = 0; 23 | 24 | public static Props mkProps(List commandHandlerProps){ 25 | return Props.create(CommandDispatcher.class, commandHandlerProps); 26 | } 27 | 28 | public CommandDispatcher(List commandHandlerProps) { 29 | this.commandHandlerProps = commandHandlerProps; 30 | log.debug("CommandDispatcher created"); 31 | } 32 | 33 | @Override 34 | public void preStart() throws InterruptedException { 35 | log.debug("PreStartCalled"); 36 | if(commandHandlerProps != null) { 37 | for (Props prop : commandHandlerProps) { 38 | ActorRef ref = getContext().actorOf(prop); 39 | log.debug("Created subactor " + ref); 40 | ref.tell("HandlesClasses", self()); 41 | log.debug("sent Handles classes to " + ref); 42 | remainingCommandHandlers++; 43 | } 44 | } 45 | //a small wait so that all children can start, before we start procesing messages. 46 | Thread.sleep(100); 47 | } 48 | 49 | @Override 50 | public void onReceive(Object o) throws Exception { 51 | 52 | if (o instanceof Command) { 53 | log.debug("Got command " + o); 54 | ActorRef actorRef = commandHandlers.get(o.getClass()); 55 | if(actorRef == null && remainingCommandHandlers > 0) { 56 | log.debug("RemainingCommandHandlers is " + remainingCommandHandlers + " sending message to self"); 57 | self().tell(o, sender()); 58 | } else { 59 | if(actorRef == null){ 60 | log.error("Failed to find commandHandler for command {}", o); 61 | } else { 62 | actorRef.tell(o, sender()); 63 | } 64 | } 65 | } else if(o instanceof ImmutableSet){ 66 | log.debug("Got ImmutableSet " + o); 67 | ImmutableSet> handles = (ImmutableSet>) o; 68 | for (Class clz : handles) { 69 | log.debug("Putting class " + clz + " into map with actor " + sender()); 70 | commandHandlers.put(clz, sender()); 71 | remainingCommandHandlers--; 72 | log.debug("remainingCommandHandlers is " + remainingCommandHandlers); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/command/CommandHandler.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.UntypedActor; 5 | import com.google.common.collect.ImmutableSet; 6 | import no.ks.eventstore2.reflection.HandlerFinder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.lang.reflect.Method; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public abstract class CommandHandler extends UntypedActor{ 15 | 16 | private static Logger log= LoggerFactory.getLogger(CommandHandler.class); 17 | 18 | protected ActorRef eventStore; 19 | private Map, Method> handleCommandMap = null; 20 | 21 | public CommandHandler(ActorRef eventStore) { 22 | this.eventStore = eventStore; 23 | log.debug("created commandhandler"); 24 | } 25 | 26 | @Override 27 | public void preStart() throws Exception { 28 | log.debug("PrestartCalled"); 29 | super.preStart(); 30 | init(); 31 | } 32 | 33 | @Override 34 | public void onReceive(Object o) throws Exception{ 35 | try { 36 | if (o instanceof Command){ 37 | log.debug("Received command {}", o); 38 | handleCommand((Command) o); 39 | } else if("HandlesClasses".equals(o)){ 40 | log.debug("Handles classes received sending map to " + sender()); 41 | sender().tell(ImmutableSet.copyOf(handleCommandMap.keySet()), self()); 42 | } 43 | } catch (Exception e) { 44 | log.error("Command handler threw exception when handling message: ", e); 45 | throw new RuntimeException("Command handler threw exception when handling message: ", e); 46 | } 47 | } 48 | 49 | public void handleCommand(Command command) { 50 | Method method = handleCommandMap.get(command.getClass()); 51 | try { 52 | method.invoke(this, command); 53 | } catch (Exception e) { 54 | throw new RuntimeException(e); 55 | } 56 | } 57 | 58 | private void init() { 59 | handleCommandMap = new HashMap, Method>(); 60 | try { 61 | 62 | handleCommandMap.putAll(getOldStyleHandlers()); 63 | handleCommandMap.putAll(getNewStyleHandlers()); 64 | 65 | } catch (Exception e) { 66 | throw new RuntimeException(e); 67 | } 68 | } 69 | 70 | private Map, Method> getNewStyleHandlers() { 71 | return HandlerFinder.getCommandHandlers(this.getClass()); 72 | } 73 | 74 | private Map, Method> getOldStyleHandlers() throws NoSuchMethodException { 75 | HashMap, Method> oldStyleMap = new HashMap, Method>(); 76 | Class handlerClass = this.getClass(); 77 | HandlesCommand annotation = handlerClass.getAnnotation(HandlesCommand.class); 78 | if (annotation != null) { 79 | Class[] handledCommandClasses = annotation.value(); 80 | for (Class handledEventClass : handledCommandClasses) { 81 | Method handleCommandMethod = handlerClass.getMethod("handleCommand", handledEventClass); 82 | oldStyleMap.put(handledEventClass, handleCommandMethod); 83 | } 84 | } 85 | return oldStyleMap; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/command/HandlesCommand.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | 7 | @Retention(RetentionPolicy.RUNTIME) 8 | @Deprecated 9 | public @interface HandlesCommand { 10 | Class[] value(); 11 | } 12 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/command/OnlyExecuteOnLeaderCommandHandler.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.cluster.ClusterEvent; 5 | import no.ks.eventstore2.AkkaClusterInfo; 6 | 7 | public abstract class OnlyExecuteOnLeaderCommandHandler extends CommandHandler { 8 | 9 | private AkkaClusterInfo akkaClusterInfo; 10 | 11 | public OnlyExecuteOnLeaderCommandHandler(ActorRef eventStore) { 12 | super(eventStore); 13 | } 14 | 15 | @Override 16 | public void preStart() throws Exception { 17 | akkaClusterInfo = new AkkaClusterInfo(getContext().system()); 18 | akkaClusterInfo.subscribeToClusterEvents(self()); 19 | akkaClusterInfo.updateLeaderState(null); 20 | super.preStart(); 21 | } 22 | 23 | @Override 24 | public void onReceive(Object o) throws Exception { 25 | if( o instanceof ClusterEvent.LeaderChanged){ 26 | akkaClusterInfo.updateLeaderState((ClusterEvent.LeaderChanged) o); 27 | } 28 | 29 | if(akkaClusterInfo.isLeader() || "HandlesClasses".equals(o)) { 30 | super.onReceive(o); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/AbstractJournalStorage.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import no.ks.eventstore2.Event; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.jdbc.core.JdbcTemplate; 10 | import org.springframework.jdbc.core.ResultSetExtractor; 11 | 12 | import javax.sql.DataSource; 13 | import java.io.ByteArrayOutputStream; 14 | import java.sql.Blob; 15 | import java.sql.ResultSet; 16 | import java.sql.SQLException; 17 | 18 | public abstract class AbstractJournalStorage implements JournalStorage { 19 | private static Logger log = LoggerFactory.getLogger(AbstractJournalStorage.class); 20 | private final ThreadLocal kryoThread = new ThreadLocal<>(); 21 | private KryoClassRegistration kryoClassRegistration; 22 | protected JdbcTemplate template; 23 | private static final int READ_LIMIT = 5000; 24 | 25 | public AbstractJournalStorage(DataSource dataSource, KryoClassRegistration kryoClassRegistration) { 26 | this.kryoClassRegistration = kryoClassRegistration; 27 | template = new JdbcTemplate(dataSource); 28 | } 29 | 30 | public abstract void saveEvent(Event event); 31 | 32 | public boolean loadEventsAndHandle(String aggregateType, final HandleEvent handleEvent) { 33 | return loadEventsAndHandle(aggregateType, handleEvent, "0"); 34 | } 35 | 36 | @Override 37 | public boolean loadEventsAndHandle(String aggregateType, final HandleEvent handleEvent, String fromKey) { 38 | Integer count = template.query("SELECT * FROM event WHERE aggregatetype = ? AND id > ? ORDER BY id LIMIT ?", new Object[]{aggregateType, Long.parseLong(fromKey), READ_LIMIT}, new ResultSetExtractor() { 39 | @Override 40 | public Integer extractData(ResultSet resultSet) throws SQLException { 41 | int count = 0; 42 | while (resultSet.next()) { 43 | if (resultSet.getInt("dataversion") == 2) { 44 | Blob blob = resultSet.getBlob("kryoeventdata"); 45 | Input input = new Input(blob.getBinaryStream()); 46 | Event event = (Event) getKryo().readClassAndObject(input); 47 | input.close(); 48 | event.setJournalid(resultSet.getBigDecimal("id").toPlainString()); 49 | handleEvent.handleEvent(event); 50 | } 51 | count++; 52 | } 53 | return count; 54 | } 55 | }); 56 | log.info("Loaded " + count + " event(s) from database."); 57 | return count < READ_LIMIT; 58 | } 59 | 60 | public abstract void open(); 61 | 62 | public abstract void close(); 63 | 64 | public abstract void upgradeFromOldStorage(String aggregateType, JournalStorage storage); 65 | 66 | public abstract void doBackup(String backupDirectory, String backupfilename); 67 | 68 | public abstract EventBatch loadEventsForAggregateId(String aggregateType, String aggregateId, String fromJournalId); 69 | 70 | protected ByteArrayOutputStream createByteArrayOutputStream(final Event event) { 71 | final ByteArrayOutputStream output = new ByteArrayOutputStream(); 72 | Output kryodata = new Output(output); 73 | getKryo().writeClassAndObject(kryodata, event); 74 | kryodata.close(); 75 | return output; 76 | } 77 | 78 | public Kryo getKryo() { 79 | if (kryoThread.get() == null) { 80 | EventStoreKryo kryo = new EventStoreKryo(); 81 | kryoClassRegistration.registerClasses(kryo); 82 | kryoThread.set(kryo); 83 | } 84 | return kryoThread.get(); 85 | } 86 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/AcknowledgePreviousEventsProcessed.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class AcknowledgePreviousEventsProcessed implements KyroSerializable { 6 | } 7 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/AsyncSubscription.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | public class AsyncSubscription extends Subscription { 4 | public AsyncSubscription(String aggregateType) { 5 | super(aggregateType); 6 | } 7 | 8 | public AsyncSubscription(String aggregateType, String fromJournalId) { 9 | super(aggregateType, fromJournalId); 10 | } 11 | 12 | @Override 13 | public String toString() { 14 | return "Asyncsubscription on '" + getAggregateType() + "' from '" + getFromJournalId() + "'"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/CompleteAsyncSubscriptionPleaseSendSyncSubscription.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | public class CompleteAsyncSubscriptionPleaseSendSyncSubscription extends CompleteSubscriptionRegistered { 4 | public CompleteAsyncSubscriptionPleaseSendSyncSubscription(String aggregateType) { 5 | super(aggregateType); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/CompleteSubscriptionRegistered.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class CompleteSubscriptionRegistered implements KyroSerializable { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String aggregateType; 9 | 10 | public CompleteSubscriptionRegistered(String aggregateType) { 11 | this.aggregateType = aggregateType; 12 | } 13 | 14 | public String getAggregateType() { 15 | return aggregateType; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/EventBatch.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.Event; 4 | import no.ks.eventstore2.KyroSerializable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class EventBatch implements KyroSerializable { 10 | 11 | private final String aggregateType; 12 | private final String aggregateId; 13 | private final boolean readAllEvents; 14 | private List events; 15 | 16 | public EventBatch(String aggregateType, String aggregateId, List events, boolean readAllEvents) { 17 | this.aggregateType = aggregateType; 18 | this.aggregateId = aggregateId; 19 | this.readAllEvents = readAllEvents; 20 | setEvents(events); 21 | } 22 | 23 | private void setEvents(List events){ 24 | if(events == null) this.events = new ArrayList<>(); 25 | else this.events = events; 26 | upgradeEvents(); 27 | } 28 | 29 | private void upgradeEvents() { 30 | List result = new ArrayList<>(); 31 | for (Event event : events) { 32 | result.add(upgradeEvent(event)); 33 | } 34 | events = result; 35 | } 36 | 37 | private Event upgradeEvent(Event event) { 38 | Event currentEvent = event; 39 | Event upgraded = currentEvent.upgrade(); 40 | while (upgraded != currentEvent) { 41 | currentEvent = upgraded; 42 | upgraded = currentEvent.upgrade(); 43 | } 44 | return upgraded; 45 | } 46 | 47 | public String getAggregateId() { 48 | return aggregateId; 49 | } 50 | 51 | public List getEvents() { 52 | return events; 53 | } 54 | 55 | public String getAggregateType() { 56 | return aggregateType; 57 | } 58 | 59 | public boolean isReadAllEvents() { 60 | return readAllEvents; 61 | } 62 | 63 | public String latestJournalId(){ 64 | if(events.size() == 0) return null; 65 | return events.get(events.size()-1).getJournalid(); 66 | } 67 | 68 | @Override 69 | public boolean equals(Object o) { 70 | if (this == o) return true; 71 | if (!(o instanceof EventBatch)) return false; 72 | 73 | EventBatch that = (EventBatch) o; 74 | 75 | if (readAllEvents != that.readAllEvents) return false; 76 | if (aggregateId != null ? !aggregateId.equals(that.aggregateId) : that.aggregateId != null) return false; 77 | if (aggregateType != null ? !aggregateType.equals(that.aggregateType) : that.aggregateType != null) 78 | return false; 79 | if (events != null ? !events.equals(that.events) : that.events != null) return false; 80 | 81 | return true; 82 | } 83 | 84 | @Override 85 | public int hashCode() { 86 | int result = aggregateType != null ? aggregateType.hashCode() : 0; 87 | result = 31 * result + (aggregateId != null ? aggregateId.hashCode() : 0); 88 | result = 31 * result + (readAllEvents ? 1 : 0); 89 | result = 31 * result + (events != null ? events.hashCode() : 0); 90 | return result; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/EventStoreKryo.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer; 5 | import com.esotericsoftware.kryo.serializers.MapSerializer; 6 | import com.esotericsoftware.shaded.org.objenesis.strategy.SerializingInstantiatorStrategy; 7 | import com.google.common.collect.HashMultimap; 8 | import com.google.common.collect.Multimap; 9 | import de.javakaffee.kryoserializers.jodatime.JodaDateTimeSerializer; 10 | import org.joda.time.DateTime; 11 | 12 | import java.util.*; 13 | 14 | public class EventStoreKryo extends Kryo { 15 | 16 | public EventStoreKryo() { 17 | super(); 18 | setInstantiatorStrategy(new SerializingInstantiatorStrategy()); 19 | setDefaultSerializer(CompatibleFieldSerializer.class); 20 | 21 | register(ArrayList.class, 100); 22 | register(HashMap.class, new MapSerializer()); 23 | register(HashMap.class, 101); 24 | register(DateTime.class, new JodaDateTimeSerializer()); 25 | register(DateTime.class, 102); 26 | register(Multimap.class, 103); 27 | register(HashMultimap.class, 104); 28 | register(Set.class, 105); 29 | register(Collection.class, 106); 30 | register(Object[].class, 107); 31 | register(TreeMap.class, new MapSerializer()); 32 | register(TreeMap.class, 108); 33 | register(TreeSet.class, 109); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/HandleEvent.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public interface HandleEvent { 6 | 7 | void handleEvent(Event event); 8 | } 9 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/HandleEventMetadata.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import eventstore.Messages; 4 | 5 | public abstract class HandleEventMetadata { 6 | public abstract void handleEvent(Messages.EventWrapper event); 7 | } 8 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/IncompleteSubscriptionPleaseSendNew.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class IncompleteSubscriptionPleaseSendNew implements KyroSerializable { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String aggregateType; 9 | 10 | public IncompleteSubscriptionPleaseSendNew(String aggregateType) { 11 | this.aggregateType = aggregateType; 12 | } 13 | 14 | public String getAggregateType() { 15 | return aggregateType; 16 | } 17 | 18 | @Override 19 | public boolean equals(Object o) { 20 | if (this == o) return true; 21 | if (o == null || getClass() != o.getClass()) return false; 22 | 23 | IncompleteSubscriptionPleaseSendNew that = (IncompleteSubscriptionPleaseSendNew) o; 24 | 25 | if (aggregateType != null ? !aggregateType.equals(that.aggregateType) : that.aggregateType != null) return false; 26 | 27 | return true; 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return aggregateType != null ? aggregateType.hashCode() : 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/JournalStorage.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import eventstore.Messages; 4 | import no.ks.eventstore2.Event; 5 | import scala.concurrent.Future; 6 | 7 | import java.util.List; 8 | 9 | public interface JournalStorage { 10 | 11 | void saveEvent(Event event); 12 | 13 | List saveEventsBatch(List events); 14 | 15 | /** 16 | * Load events 17 | * @param aggregateType 18 | * @param handleEvent 19 | * @return true if all events sent 20 | */ 21 | boolean loadEventsAndHandle(String aggregateType, HandleEvent handleEvent); 22 | 23 | boolean loadEventsAndHandle(String aggregateType, HandleEventMetadata handleEvent); 24 | 25 | /** 26 | * LoadEvent from a key 27 | * @param aggregateType 28 | * @param handleEvent 29 | * @param fromKey from this key 30 | * @return true if all events sent 31 | */ 32 | boolean loadEventsAndHandle(String aggregateType, HandleEvent handleEvent, String fromKey); 33 | 34 | /** 35 | * LoadEvent from a key 36 | * @param aggregateType 37 | * @param handleEvent 38 | * @param fromKey from this key 39 | * @return true if all events sent 40 | */ 41 | boolean loadEventsAndHandle(String aggregateType, HandleEventMetadata handleEvent, long fromKey); 42 | 43 | void open(); 44 | void close(); 45 | 46 | void upgradeFromOldStorage(String aggregateType, JournalStorage storage); 47 | 48 | /** 49 | * 50 | * @param backupDirectory 51 | * @param backupfilename without file ending 52 | */ 53 | void doBackup(String backupDirectory, String backupfilename); 54 | 55 | /** 56 | * 57 | * @param aggregateType 58 | * @param aggregateId 59 | * @param fromJournalId null if read from begining 60 | * @return 61 | */ 62 | EventBatch loadEventsForAggregateId(String aggregateType, String aggregateId, String fromJournalId); 63 | 64 | /** 65 | * 66 | * @param aggregateType 67 | * @param aggregateId 68 | * @param fromJournalId null if read from begining 69 | * @return 70 | */ 71 | Future loadEventsForAggregateIdAsync(String aggregateType, String aggregateId, String fromJournalId); 72 | 73 | void saveEvents(List events); 74 | 75 | Messages.EventWrapper saveEvent(Messages.EventWrapper eventWrapper); 76 | 77 | Future loadEventWrappersForAggregateIdAsync(String aggregateType, String aggregateRootId, long fromJournalId); 78 | 79 | Future loadEventWrappersForCorrelationIdAsync(String aggregateType, String correlationId, long fromJournalId); 80 | 81 | Messages.EventWrapperBatch loadEventWrappersForAggregateId(String aggregateType, String aggregateRootId, long fromJournalId); 82 | } 83 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/KryoClassRegistration.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | 5 | public interface KryoClassRegistration { 6 | void registerClasses(Kryo kryo); 7 | } 8 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/LiveSubscription.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | public class LiveSubscription extends Subscription{ 4 | 5 | public LiveSubscription(String aggregateType) { 6 | super(aggregateType); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/MongoDbOperations.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.Callable; 7 | 8 | 9 | public class MongoDbOperations { 10 | 11 | private static final Logger log = LoggerFactory.getLogger(MongoDbOperations.class); 12 | 13 | private MongoDbOperations(){} 14 | 15 | public static T doDbOperation(Callable callable, int retries, int sleepms) { 16 | T result = null; 17 | boolean finished = false; 18 | int retrycount = 0; 19 | Exception exception = null; 20 | while (!finished && retrycount <= retries) { 21 | try { 22 | retrycount++; 23 | if (retrycount > 1) 24 | log.info("MongDb got exception retrying operation", new RuntimeException("Retrying db operation")); 25 | result = callable.call(); 26 | finished = true; 27 | } catch (Exception e) { 28 | log.info("failed db operation", e); 29 | exception = e; 30 | try { 31 | Thread.sleep(sleepms); 32 | } catch (InterruptedException e1) { 33 | } 34 | } 35 | } 36 | if(exception != null) throw new RuntimeException(exception); 37 | return result; 38 | } 39 | 40 | public static T doDbOperation(Callable callable) { 41 | return doDbOperation(callable, 50, 500); 42 | } 43 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/NewEventstoreStarting.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class NewEventstoreStarting implements KyroSerializable { 6 | } 7 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/RefreshSubscription.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class RefreshSubscription implements KyroSerializable { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private final String aggregateType; 9 | 10 | public RefreshSubscription(String aggregateType) { 11 | this.aggregateType = aggregateType; 12 | } 13 | 14 | public String getAggregateType() { 15 | return aggregateType; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "RefreshSubscription{" 21 | +"aggregateType='" + aggregateType + '\'' 22 | +'}'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/RemoveSubscription.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | 4 | import no.ks.eventstore2.KyroSerializable; 5 | 6 | public class RemoveSubscription implements KyroSerializable { 7 | 8 | private String aggregateType; 9 | 10 | public RemoveSubscription(String aggregateType) { 11 | this.aggregateType = aggregateType; 12 | } 13 | 14 | public String getAggregateType() { 15 | return aggregateType; 16 | } 17 | 18 | public void setAggregateType(String aggregateType) { 19 | this.aggregateType = aggregateType; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/RetreiveAggregateEvents.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class RetreiveAggregateEvents implements KyroSerializable { 6 | private String aggregateId; 7 | private String fromJournalId; 8 | private String aggregateType; 9 | 10 | /** 11 | * 12 | * @param aggregateType 13 | * @param aggregateId 14 | * @param fromJournalId null if read from start 15 | */ 16 | public RetreiveAggregateEvents(String aggregateType, String aggregateId, String fromJournalId) { 17 | this.aggregateId = aggregateId; 18 | this.fromJournalId = fromJournalId; 19 | this.aggregateType = aggregateType; 20 | } 21 | 22 | public String getAggregateId() { 23 | return aggregateId; 24 | } 25 | 26 | public String getFromJournalId() { 27 | return fromJournalId; 28 | } 29 | 30 | public String getAggregateType() { 31 | return aggregateType; 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/RetrieveAggregateEventsAsync.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class RetrieveAggregateEventsAsync implements KyroSerializable { 6 | private String aggregateId; 7 | private String fromJournalId; 8 | private String aggregateType; 9 | 10 | /** 11 | * 12 | * @param aggregateType 13 | * @param aggregateId 14 | * @param fromJournalId null if read from start 15 | */ 16 | public RetrieveAggregateEventsAsync(String aggregateType, String aggregateId, String fromJournalId) { 17 | this.aggregateId = aggregateId; 18 | this.fromJournalId = fromJournalId; 19 | this.aggregateType = aggregateType; 20 | } 21 | 22 | public String getAggregateId() { 23 | return aggregateId; 24 | } 25 | 26 | public String getFromJournalId() { 27 | return fromJournalId; 28 | } 29 | 30 | public String getAggregateType() { 31 | return aggregateType; 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/StoreEvents.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.Event; 4 | import no.ks.eventstore2.KyroSerializable; 5 | 6 | import java.util.List; 7 | 8 | public class StoreEvents implements KyroSerializable { 9 | 10 | private String aggregateType; 11 | 12 | private List events; 13 | 14 | public StoreEvents() { 15 | } 16 | 17 | public StoreEvents(List events) { 18 | this.events = events; 19 | } 20 | 21 | public List getEvents() { 22 | return events; 23 | } 24 | 25 | public void setEvents(List events) { 26 | this.events = events; 27 | } 28 | 29 | public String getLogMessage() { 30 | String logmessage = ""; 31 | for (Event event : events) { 32 | logmessage += event.getLogMessage(); 33 | } 34 | return logmessage; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "StoreEvents{" + 40 | "aggregateType='" + aggregateType + '\'' + 41 | ", events=" + events + 42 | '}'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/Subscription.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class Subscription implements KyroSerializable { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String aggregateType; 9 | private String fromJournalId; 10 | 11 | /** 12 | * Subscription from start 13 | * @param aggregateType 14 | */ 15 | public Subscription(String aggregateType) { 16 | this.aggregateType = aggregateType; 17 | } 18 | 19 | /** 20 | * Subscription from last event 21 | * @param aggregateType 22 | * @param fromJournalId lastJournalidReceived (Not the next one to expect) 23 | */ 24 | public Subscription(String aggregateType, String fromJournalId) { 25 | this.aggregateType = aggregateType; 26 | this.fromJournalId = fromJournalId; 27 | } 28 | 29 | public String getAggregateType() { 30 | return aggregateType; 31 | } 32 | 33 | public String getFromJournalId() { 34 | return fromJournalId; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "Subscription{" + 40 | "aggregateType='" + aggregateType + '\'' + 41 | ", fromJournalId='" + fromJournalId + '\'' + 42 | '}'; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (!(o instanceof Subscription)) return false; 49 | 50 | Subscription that = (Subscription) o; 51 | 52 | if (aggregateType != null ? !aggregateType.equals(that.aggregateType) : that.aggregateType != null) return false; 53 | if (fromJournalId != null ? !fromJournalId.equals(that.fromJournalId) : that.fromJournalId != null) 54 | return false; 55 | 56 | return true; 57 | } 58 | 59 | @Override 60 | public int hashCode() { 61 | int result = aggregateType != null ? aggregateType.hashCode() : 0; 62 | result = 31 * result + (fromJournalId != null ? fromJournalId.hashCode() : 0); 63 | return result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/SubscriptionRemoved.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class SubscriptionRemoved implements KyroSerializable { 6 | private String aggregateType; 7 | 8 | public SubscriptionRemoved(String aggregateType) { 9 | this.aggregateType = aggregateType; 10 | } 11 | 12 | public String getAggregateType() { 13 | return aggregateType; 14 | } 15 | 16 | public void setAggregateType(String aggregateType) { 17 | this.aggregateType = aggregateType; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/eventstore/UpgradeAggregate.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class UpgradeAggregate implements KyroSerializable { 6 | private JournalStorage oldStorage; 7 | private String aggregateType; 8 | 9 | public UpgradeAggregate() { 10 | } 11 | 12 | public UpgradeAggregate(JournalStorage oldStorage, String aggregateType) { 13 | this.oldStorage = oldStorage; 14 | this.aggregateType = aggregateType; 15 | } 16 | 17 | public JournalStorage getOldStorage() { 18 | return oldStorage; 19 | } 20 | 21 | public void setOldStorage(JournalStorage oldStorage) { 22 | this.oldStorage = oldStorage; 23 | } 24 | 25 | public String getAggregateType() { 26 | return aggregateType; 27 | } 28 | 29 | public void setAggregateType(String aggregateType) { 30 | this.aggregateType = aggregateType; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/json/Adapter.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.json; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | public class Adapter { 6 | 7 | private Type type; 8 | private Object typeAdapter; 9 | 10 | public Adapter(Type type, Object adapter) { 11 | this.type = type; 12 | this.typeAdapter = adapter; 13 | } 14 | 15 | public Type getType() { 16 | return type; 17 | } 18 | 19 | public void setType(Type type) { 20 | this.type = type; 21 | } 22 | 23 | public Object getTypeAdapter() { 24 | return typeAdapter; 25 | } 26 | 27 | public void setTypeAdapter(Object typeAdapter) { 28 | this.typeAdapter = typeAdapter; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/Call.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Call { 6 | private final String method; 7 | private final Object[] args; 8 | 9 | public Call(String method, Object[] args) { 10 | this.method = method; 11 | this.args = args; 12 | } 13 | 14 | public String getMethodName() { 15 | return method; 16 | } 17 | 18 | public Object[] getArgs() { 19 | return args; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (o == null || getClass() != o.getClass()) return false; 26 | 27 | Call call = (Call) o; 28 | 29 | if (method != null ? !method.equals(call.method) : call.method != null) return false; 30 | // Probably incorrect - comparing Object[] arrays with Arrays.equals 31 | return Arrays.equals(args, call.args); 32 | 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | int result = method != null ? method.hashCode() : 0; 38 | result = 31 * result + (args != null ? Arrays.hashCode(args) : 0); 39 | return result; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "Call{" 45 | + "method='" + method + '\'' 46 | + ", args=" + (args == null ? null : Arrays.asList(args)) 47 | + '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/CallProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | public class CallProjection { 4 | 5 | private CallProjection(){} 6 | 7 | public static Call call(String method, Object... args){ 8 | return new Call(method, args); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/ListensTo.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Deprecated 7 | @Retention(RetentionPolicy.RUNTIME) 8 | public @interface ListensTo { 9 | Class[] value(); 10 | 11 | String[] aggregates(); 12 | } 13 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/LiveSubscriptionProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.eventstore.LiveSubscription; 5 | 6 | public class LiveSubscriptionProjection extends Projection { 7 | 8 | public LiveSubscriptionProjection(ActorRef eventStore) { 9 | super(eventStore); 10 | } 11 | 12 | @Override 13 | protected void subscribe() { 14 | setInSubscribe(); 15 | eventStore.tell(new LiveSubscription(getSubscribe().getAggregateType()), self()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/ProjectionError.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import no.ks.eventstore2.Event; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.io.PrintWriter; 9 | import java.io.StringWriter; 10 | import java.text.SimpleDateFormat; 11 | 12 | public class ProjectionError { 13 | 14 | private static Logger log = LoggerFactory.getLogger(ProjectionError.class); 15 | 16 | private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 17 | private String event; 18 | private String projection; 19 | private String reason = ""; 20 | private String date; 21 | 22 | public ProjectionError(ProjectionFailedError error) { 23 | this.event = error.getMessage().toString(); 24 | this.projection = error.getProjection().toString(); 25 | if (error.getMessage() instanceof Event) { 26 | try { 27 | this.date = format.format(((Event) error.getMessage()).getCreated().toDate()); 28 | } catch (Exception e) { 29 | log.warn("Could not format created date", e); 30 | } 31 | } 32 | reason = joinStackTrace(error.getReason()); 33 | } 34 | 35 | public String getEvent() { 36 | return event; 37 | } 38 | 39 | public void setEvent(String event) { 40 | this.event = event; 41 | } 42 | 43 | public String getProjection() { 44 | return projection; 45 | } 46 | 47 | public void setProjection(String projection) { 48 | this.projection = projection; 49 | } 50 | 51 | public String getReason() { 52 | return reason; 53 | } 54 | 55 | public void setReason(String reason) { 56 | this.reason = reason; 57 | } 58 | 59 | public String getDate() { 60 | return date; 61 | } 62 | 63 | public void setDate(String date) { 64 | this.date = date; 65 | } 66 | 67 | private static String joinStackTrace(Throwable e) { 68 | StringWriter writer = null; 69 | try { 70 | writer = new StringWriter(); 71 | joinStackTrace(e, writer); 72 | return writer.toString(); 73 | } finally { 74 | if (writer != null) { 75 | try { 76 | writer.close(); 77 | } catch (IOException e1) { 78 | log.warn("Could not close close writer", e); 79 | } 80 | } 81 | } 82 | } 83 | 84 | private static void joinStackTrace(Throwable e, StringWriter writer) { 85 | PrintWriter printer = null; 86 | try { 87 | printer = new PrintWriter(writer); 88 | 89 | while (e != null) { 90 | 91 | printer.println(e); 92 | StackTraceElement[] trace = e.getStackTrace(); 93 | for (int i = 0; i < trace.length; i++) { 94 | printer.println("\tat " + trace[i]); 95 | } 96 | 97 | e = e.getCause(); 98 | if (e != null) { 99 | printer.println("\r\n Caused by:" + e); 100 | } 101 | } 102 | } finally { 103 | if (printer != null) { 104 | printer.close(); 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/ProjectionErrorListener.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.UntypedActor; 5 | import no.ks.eventstore2.ask.Asker; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class ProjectionErrorListener extends UntypedActor { 11 | 12 | private static final int ERRORS_SIZE = 100; 13 | private List errors = new ArrayList<>(); 14 | 15 | public ProjectionErrorListener() { 16 | } 17 | 18 | @Override 19 | public void onReceive(Object o) { 20 | if (o instanceof ProjectionFailedError) { 21 | if (errors.size() < ERRORS_SIZE) { 22 | errors.add(new ProjectionError((ProjectionFailedError) o)); 23 | } 24 | } else if (o instanceof Call) { 25 | Call call = (Call) o; 26 | if("getErrors".equals(call.getMethodName())) { 27 | sender().tell(errors, self()); 28 | } 29 | } 30 | } 31 | 32 | public static List askErrors(ActorRef projectionErrorListener) { 33 | try { 34 | return Asker.askProjection(projectionErrorListener, "getErrors").list(ProjectionError.class); 35 | } catch (Exception e) { 36 | throw new RuntimeException("Kunne ikke hente feil fra projectionErrorListener", e); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/ProjectionFailedError.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | 5 | import java.io.Serializable; 6 | 7 | public class ProjectionFailedError implements Serializable { 8 | private ActorRef projection; 9 | private Throwable reason; 10 | private Object message; 11 | 12 | public ProjectionFailedError(ActorRef projection, Throwable reason, Object message) { 13 | this.projection = projection; 14 | this.message = message; 15 | this.reason = reason; 16 | } 17 | 18 | public ProjectionFailedError(ActorRef projection, Throwable reason) { 19 | this.projection = projection; 20 | this.reason = reason; 21 | } 22 | 23 | public ActorRef getProjection() { 24 | return projection; 25 | } 26 | 27 | public void setProjection(ActorRef projection) { 28 | this.projection = projection; 29 | } 30 | 31 | public Throwable getReason() { 32 | return reason; 33 | } 34 | 35 | public void setReason(Throwable reason) { 36 | this.reason = reason; 37 | } 38 | 39 | public Object getMessage() { 40 | return message; 41 | } 42 | 43 | public void setMessage(Object message) { 44 | this.message = message; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "ProjectionFailedError{" + 50 | "projection=" + projection + 51 | ", reason=" + reason + 52 | ", message=" + message + 53 | '}'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/ProjectionProtobufSnapshot.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | 4 | import akka.actor.ActorRef; 5 | import no.ks.eventstore2.TakeSnapshot; 6 | 7 | public abstract class ProjectionProtobufSnapshot extends ProjectionProtobuf { 8 | public ProjectionProtobufSnapshot(ActorRef eventStore) { 9 | super(eventStore); 10 | } 11 | 12 | @Override 13 | public void preStart() { 14 | loadSnapshot(); 15 | super.preStart(); 16 | } 17 | 18 | @Override 19 | public void onReceive(Object o) { 20 | super.onReceive(o); 21 | if (o instanceof TakeSnapshot) { 22 | saveSnapshot(); 23 | } 24 | } 25 | 26 | public abstract void saveSnapshot(); 27 | 28 | public abstract void loadSnapshot(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/ProjectionSnapshot.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | 4 | import akka.actor.ActorRef; 5 | import no.ks.eventstore2.TakeSnapshot; 6 | 7 | public abstract class ProjectionSnapshot extends Projection { 8 | public ProjectionSnapshot(ActorRef eventStore) { 9 | super(eventStore); 10 | } 11 | 12 | @Override 13 | public void preStart() { 14 | loadSnapshot(); 15 | super.preStart(); 16 | } 17 | 18 | @Override 19 | public void onReceive(Object o) { 20 | super.onReceive(o); 21 | if (o instanceof TakeSnapshot) { 22 | saveSnapshot(); 23 | } 24 | } 25 | 26 | public abstract void saveSnapshot(); 27 | 28 | public abstract void loadSnapshot(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/projection/Subscriber.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface Subscriber { 11 | String value(); 12 | } 13 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/reflection/HandlerFinder.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.reflection; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import no.ks.eventstore2.Event; 8 | import no.ks.eventstore2.Handler; 9 | import no.ks.eventstore2.SubscriberConfigurationException; 10 | import no.ks.eventstore2.command.Command; 11 | import no.ks.eventstore2.command.CommandHandler; 12 | import akka.actor.UntypedActor; 13 | 14 | public class HandlerFinder { 15 | 16 | private HandlerFinder() {} 17 | 18 | public static Method findHandlingMethod(Map, Method> handlers, Event event) { 19 | Method method = null; 20 | 21 | Class theclass = event.getClass(); 22 | while (method == null && theclass != Object.class){ 23 | method = handlers.get(theclass); 24 | theclass = theclass.getSuperclass(); 25 | } 26 | return method; 27 | } 28 | 29 | public static Map, Method> getEventHandlers(Class clazz) { 30 | return getHandlers(clazz, Event.class); 31 | } 32 | 33 | public static Map, Method> getCommandHandlers(Class clazz) { 34 | return getHandlers(clazz, Command.class); 35 | } 36 | 37 | public static Map, Method> getHandlers(Class clazz, Class handlesClass) { 38 | HashMap, Method> handlers = new HashMap, Method>(); 39 | for (Method method : clazz.getMethods()) { 40 | Handler handlerAnnotation = method.getAnnotation(Handler.class); 41 | 42 | if (handlerAnnotation != null) { 43 | Class[] types = method.getParameterTypes(); 44 | if (types.length != 1) { 45 | throw new SubscriberConfigurationException("Invalid handler signature on " + clazz.getName() + "." + method.getName() + ". Handler should have one, and only one, parameter"); 46 | } else { 47 | if (handlesClass.isAssignableFrom(types[0])) { 48 | Class handledType = (Class) types[0]; 49 | if (handlers.get(handledType) != null) { 50 | throw new SubscriberConfigurationException("More than one handler with parameter " + handledType.getName() + " in subscriber " + clazz.getName() + ". Handlers should be non-ambiguous"); 51 | } else { 52 | handlers.put(handledType, method); 53 | } 54 | } 55 | 56 | } 57 | } 58 | } 59 | return handlers; 60 | } 61 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/reflection/HandlerFinderProtobuf.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.reflection; 2 | 3 | import com.google.protobuf.Message; 4 | import no.ks.eventstore2.Handler; 5 | import no.ks.eventstore2.SubscriberConfigurationException; 6 | import no.ks.eventstore2.command.Command; 7 | import no.ks.eventstore2.command.CommandHandler; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * Created by roopan on 07.09.16. 15 | */ 16 | public class HandlerFinderProtobuf { 17 | 18 | private HandlerFinderProtobuf() {} 19 | 20 | public static Method findHandlingMethod(Map, Method> handlers, Message event) { 21 | Method method = null; 22 | 23 | Class theclass = event.getClass(); 24 | while (method == null && theclass != Object.class){ 25 | method = handlers.get(theclass); 26 | theclass = theclass.getSuperclass(); 27 | } 28 | return method; 29 | } 30 | 31 | public static Map, Method> getEventHandlers(Class clazz) { 32 | return getHandlers(clazz, Message.class); 33 | } 34 | 35 | public static Map, Method> getCommandHandlers(Class clazz) { 36 | return getHandlers(clazz, Command.class); 37 | } 38 | 39 | public static Map, Method> getHandlers(Class clazz, Class handlesClass) { 40 | HashMap, Method> handlers = new HashMap, Method>(); 41 | for (Method method : clazz.getMethods()) { 42 | Handler handlerAnnotation = method.getAnnotation(Handler.class); 43 | 44 | if (handlerAnnotation != null) { 45 | Class[] types = method.getParameterTypes(); 46 | if (types.length != 1) { 47 | throw new SubscriberConfigurationException("Invalid handler signature on " + clazz.getName() + "." + method.getName() + ". Handler should have one, and only one, parameter"); 48 | } else { 49 | if (handlesClass.isAssignableFrom(types[0])) { 50 | Class handledType = (Class) types[0]; 51 | if (handlers.get(handledType) != null) { 52 | throw new SubscriberConfigurationException("More than one handler with parameter " + handledType.getName() + " in subscriber " + clazz.getName() + ". Handlers should be non-ambiguous"); 53 | } else { 54 | handlers.put(handledType, method); 55 | } 56 | } 57 | 58 | } 59 | } 60 | } 61 | return handlers; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/response/NoResult.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.response; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class NoResult implements KyroSerializable { 6 | private static final long serialVersionUID = 1L; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/response/Success.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.response; 2 | 3 | import no.ks.eventstore2.KyroSerializable; 4 | 5 | public class Success implements KyroSerializable { 6 | } 7 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/EventIdBind.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | public @interface EventIdBind { 10 | Class eventClass(); 11 | String idProperty(); 12 | } 13 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/InvalidSagaConfigurationException.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | public class InvalidSagaConfigurationException extends RuntimeException{ 4 | public InvalidSagaConfigurationException(String message) { 5 | super(message); 6 | } 7 | 8 | public InvalidSagaConfigurationException(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/ListensTo.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Deprecated 8 | public @interface ListensTo { 9 | EventIdBind[] value(); 10 | 11 | String[] aggregates(); 12 | } 13 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/MysqlSagaRepository.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import org.joda.time.DateTime; 4 | 5 | import javax.sql.DataSource; 6 | import java.util.List; 7 | 8 | public class MysqlSagaRepository extends SqlSagaRepository { 9 | 10 | public MysqlSagaRepository(DataSource dataSource) { 11 | super(dataSource); 12 | } 13 | 14 | @Override 15 | protected String getUpdateSagaSql() { 16 | return "update saga set state = ? where id = ? and clazz = ?"; 17 | } 18 | 19 | @Override 20 | protected String getInsertSagaSql() { 21 | return "insert into saga (id,clazz,state) values(?,?,?)"; 22 | } 23 | 24 | @Override 25 | protected String getSelectStateSql() { 26 | return "select state from saga where id= ? and clazz = ?"; 27 | } 28 | 29 | @Override 30 | public void storeScheduleAwake(String sagaid, String sagaclass, DateTime when) { 31 | 32 | } 33 | 34 | @Override 35 | public void clearAwake(String sagaid, String sagaclass) { 36 | 37 | } 38 | 39 | @Override 40 | public List whoNeedsToWake() { 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaCompositeId.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | public class SagaCompositeId { 4 | 5 | private Class clz; 6 | private String id; 7 | 8 | public SagaCompositeId(Class clz, String id) { 9 | this.clz = clz; 10 | this.id = id; 11 | } 12 | 13 | @Override 14 | public boolean equals(Object o) { 15 | if (this == o) return true; 16 | if (o == null || getClass() != o.getClass()) return false; 17 | 18 | SagaCompositeId that = (SagaCompositeId) o; 19 | 20 | if (clz != null ? !clz.equals(that.clz) : that.clz != null) return false; 21 | if (id != null ? !id.equals(that.id) : that.id != null) return false; 22 | 23 | return true; 24 | } 25 | 26 | @Override 27 | public int hashCode() { 28 | int result = clz != null ? clz.hashCode() : 0; 29 | result = 31 * result + (id != null ? id.hashCode() : 0); 30 | return result; 31 | } 32 | 33 | public Class getClz() { 34 | return clz; 35 | } 36 | 37 | public String getId() { 38 | return id; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "SagaCompositeId{" + 44 | "clz=" + clz + 45 | ", id='" + id + '\'' + 46 | '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaDatasourceRepository.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import org.joda.time.DateTime; 4 | 5 | import javax.sql.DataSource; 6 | import java.util.List; 7 | 8 | public class SagaDatasourceRepository extends SqlSagaRepository{ 9 | 10 | public SagaDatasourceRepository(DataSource dataSource) { 11 | super(dataSource); 12 | } 13 | 14 | @Override 15 | protected String getUpdateSagaSql() { 16 | return "update Saga set state = ? where id = ? and clazz = ?"; 17 | } 18 | 19 | @Override 20 | protected String getInsertSagaSql() { 21 | return "insert into Saga (id,clazz,state) values(?,?,?)"; 22 | } 23 | 24 | @Override 25 | protected String getSelectStateSql() { 26 | return "select state from Saga where id= ? and clazz = ?"; 27 | } 28 | 29 | @Override 30 | public void storeScheduleAwake(String sagaid, String sagaclass, DateTime when) { 31 | 32 | } 33 | 34 | @Override 35 | public void clearAwake(String sagaid, String sagaclass) { 36 | 37 | } 38 | 39 | @Override 40 | public List whoNeedsToWake() { 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaEventId.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class SagaEventId { 6 | private final Class sagaClass; 7 | private final Class eventClass; 8 | 9 | public SagaEventId(Class sagaClass, Class eventClass) { 10 | this.sagaClass = sagaClass; 11 | this.eventClass = eventClass; 12 | } 13 | 14 | public Class getSagaClass() { 15 | return sagaClass; 16 | } 17 | 18 | public Class getEventClass() { 19 | return eventClass; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (o == null || getClass() != o.getClass()) return false; 26 | 27 | SagaEventId that = (SagaEventId) o; 28 | 29 | if (eventClass != null ? !eventClass.equals(that.eventClass) : that.eventClass != null) return false; 30 | if (sagaClass != null ? !sagaClass.equals(that.sagaClass) : that.sagaClass != null) return false; 31 | 32 | return true; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | int result = sagaClass != null ? sagaClass.hashCode() : 0; 38 | result = 31 * result + (eventClass != null ? eventClass.hashCode() : 0); 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaEventIdProperty.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.TYPE) 10 | public @interface SagaEventIdProperty { 11 | String value() default ""; 12 | boolean useAggregateRootId() default false; 13 | } 14 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaEventMapping.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class SagaEventMapping { 6 | private final Class sagaClass; 7 | private final boolean useAggregateRootID; 8 | private final Method propertyMethod; 9 | 10 | public SagaEventMapping(Class sagaClass, Method propertyMethod) { 11 | this.sagaClass = sagaClass; 12 | this.propertyMethod = propertyMethod; 13 | this.useAggregateRootID = false; 14 | } 15 | 16 | public SagaEventMapping(Class sagaClass, boolean useAggregateRootID) { 17 | propertyMethod = null; 18 | this.sagaClass = sagaClass; 19 | this.useAggregateRootID = useAggregateRootID; 20 | } 21 | 22 | public Class getSagaClass() { 23 | return sagaClass; 24 | } 25 | 26 | public Method getPropertyMethod() { 27 | return propertyMethod; 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | 35 | SagaEventMapping that = (SagaEventMapping) o; 36 | 37 | if (propertyMethod != null ? !propertyMethod.equals(that.propertyMethod) : that.propertyMethod != null) 38 | return false; 39 | if (sagaClass != null ? !sagaClass.equals(that.sagaClass) : that.sagaClass != null) return false; 40 | 41 | return true; 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | int result = sagaClass != null ? sagaClass.hashCode() : 0; 47 | result = 31 * result + (propertyMethod != null ? propertyMethod.hashCode() : 0); 48 | return result; 49 | } 50 | 51 | public boolean isUseAggregateRootID() { 52 | return useAggregateRootID; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaInMemoryRepository.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import org.joda.time.DateTime; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class SagaInMemoryRepository extends SagaRepository{ 11 | 12 | private Map map = new HashMap<>(); 13 | 14 | private Map awakemap = new HashMap<>(); 15 | 16 | @Override 17 | public void storeScheduleAwake(String sagaid, String sagaclass, DateTime when) { 18 | try { 19 | awakemap.put(new SagaCompositeId(Class.forName(sagaclass), sagaid), when); 20 | } catch (ClassNotFoundException e) { 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | 25 | @Override 26 | public void clearAwake(String sagaid, String sagaclass) { 27 | try { 28 | awakemap.remove(new SagaCompositeId(Class.forName(sagaclass), sagaid)); 29 | } catch (ClassNotFoundException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } 33 | 34 | @Override 35 | public List whoNeedsToWake() { 36 | List result = new ArrayList<>(); 37 | for (Map.Entry sagaCompositeIdDateTimeEntry : awakemap.entrySet()) { 38 | if(sagaCompositeIdDateTimeEntry.getValue().isBefore(DateTime.now())) 39 | result.add(sagaCompositeIdDateTimeEntry.getKey()); 40 | } 41 | return result; 42 | } 43 | 44 | @Override 45 | public void saveState(String sagaStateId, String sagaid, byte state) { 46 | map.put(sagaStateId + "_" + sagaid, state); 47 | } 48 | 49 | @Override 50 | public byte getState(String sagaStateId, String sagaid) { 51 | return (map.containsKey(sagaStateId + "_" + sagaid) ? map.get(sagaStateId + "_" + sagaid) : Saga.STATE_INITIAL); 52 | } 53 | 54 | @Override 55 | public void close() { 56 | 57 | } 58 | 59 | @Override 60 | public void open() { 61 | 62 | } 63 | 64 | @Override 65 | public void readAllStatesToNewRepository(SagaRepository repository) { 66 | 67 | } 68 | 69 | @Override 70 | public void doBackup(String backupdir, String backupfilename) { 71 | 72 | } 73 | 74 | @Override 75 | public long loadLatestJournalID(String aggregate) { 76 | return 0; 77 | } 78 | 79 | @Override 80 | public void saveLatestJournalId(String aggregate, long latestJournalId) { 81 | 82 | } 83 | 84 | @Override 85 | public void saveStates(List list) { 86 | for (State state : list) { 87 | saveState(state.getSagaStateId(), state.getId(), state.getState()); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SagaRepository.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import org.joda.time.DateTime; 4 | 5 | import java.util.List; 6 | 7 | public abstract class SagaRepository { 8 | 9 | public abstract void storeScheduleAwake(String sagaid, String sagaclass, DateTime when); 10 | 11 | public abstract void clearAwake(String sagaid, String sagaclass); 12 | 13 | public abstract List whoNeedsToWake(); 14 | 15 | public abstract void saveState(String sagaStateId, String sagaid, byte state); 16 | 17 | public abstract byte getState(String sagaStateId, String sagaid); 18 | 19 | public abstract void close(); 20 | 21 | public abstract void open(); 22 | public abstract void readAllStatesToNewRepository(SagaRepository repository); 23 | 24 | public abstract void doBackup(String backupdir, String backupfilename); 25 | 26 | public abstract long loadLatestJournalID(String aggregate); 27 | 28 | public abstract void saveLatestJournalId(String aggregate, long latestJournalId); 29 | 30 | public abstract void saveStates(List list); 31 | } -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/SqlSagaRepository.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import javax.sql.DataSource; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.dao.EmptyResultDataAccessException; 13 | import org.springframework.jdbc.core.JdbcTemplate; 14 | import org.springframework.jdbc.core.RowCallbackHandler; 15 | 16 | public abstract class SqlSagaRepository extends SagaRepository { 17 | private static Logger log = LoggerFactory.getLogger(SagaDatasourceRepository.class); 18 | 19 | private JdbcTemplate template; 20 | 21 | public SqlSagaRepository(DataSource dataSource) { 22 | template = new JdbcTemplate(dataSource); 23 | } 24 | 25 | @Override 26 | public void saveState(String sagaStateId, String sagaid, byte state) { 27 | log.debug("Saving state {} sagaid {} state "+ state, sagaStateId, sagaid); 28 | int i = template.queryForObject("select count(0) from saga where id = ? and clazz = ?", new Object[]{sagaid, sagaStateId}, Integer.class ); 29 | if(i > 0) { 30 | template.update(getUpdateSagaSql(), state, sagaid, sagaStateId); 31 | } else { 32 | template.update(getInsertSagaSql(), new Object[]{sagaid, sagaStateId, state}); 33 | } 34 | } 35 | 36 | @Override 37 | public byte getState(String sagaStateId, String sagaid) { 38 | int result = 0; 39 | try{ 40 | result = template.queryForObject(getSelectStateSql(), new Object[]{sagaid, sagaStateId}, Integer.class); 41 | } catch (EmptyResultDataAccessException e){ 42 | 43 | } 44 | if(result > Byte.MAX_VALUE) { 45 | throw new RuntimeException("Failed to convert to byte " + result); 46 | } 47 | log.debug("Loading state from repository for clz " + sagaStateId + " sagaid " + sagaid + " state " + result); 48 | return (byte) result; 49 | } 50 | 51 | public void readAllStatesToNewRepository(final SagaRepository repository){ 52 | final List list = new ArrayList(); 53 | template.query("select state,id,clazz from Saga",new RowCallbackHandler() { 54 | @Override 55 | public void processRow(ResultSet resultSet) throws SQLException { 56 | list.add(new State(resultSet.getString("clazz"),resultSet.getString("id"),(byte)resultSet.getInt("state"))); 57 | } 58 | }); 59 | repository.saveStates(list); 60 | } 61 | 62 | @Override 63 | public void doBackup(String backupdir, String backupfilename) { 64 | 65 | } 66 | 67 | @Override 68 | public long loadLatestJournalID(String aggregate) { 69 | return 0; 70 | } 71 | 72 | @Override 73 | public void saveLatestJournalId(String aggregate, long latestJournalId) { 74 | 75 | } 76 | 77 | @Override 78 | public void saveStates(List list) { 79 | for (State state : list) { 80 | saveState(state.getSagaStateId(), state.getId(), state.getState()); 81 | } 82 | } 83 | 84 | @Override 85 | public void close() { 86 | 87 | } 88 | 89 | @Override 90 | public void open() { 91 | 92 | } 93 | 94 | protected abstract String getUpdateSagaSql(); 95 | 96 | protected abstract String getInsertSagaSql(); 97 | 98 | protected abstract String getSelectStateSql(); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/State.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | public class State { 4 | private final String sagaStateId; 5 | private final String id; 6 | private final byte state; 7 | 8 | public State(String clazz, String id, byte state) { 9 | 10 | this.sagaStateId = clazz; 11 | this.id = id; 12 | this.state = state; 13 | } 14 | 15 | public String getSagaStateId() { 16 | return sagaStateId; 17 | } 18 | 19 | public String getId() { 20 | return id; 21 | } 22 | 23 | public byte getState() { 24 | return state; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/TimeOutSaga.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import eventstore.Messages; 5 | import no.ks.eventstore2.ProtobufHelper; 6 | import org.joda.time.DateTime; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | public abstract class TimeOutSaga extends Saga { 13 | 14 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 15 | 16 | public TimeOutSaga(String id, ActorRef commandDispatcher, SagaRepository repository) { 17 | super(id, commandDispatcher, repository); 18 | } 19 | 20 | @Override 21 | public void onReceive(Object o) { 22 | if ("awake".equals(o)) { 23 | log.debug("{} {} awake called", getSelf(), id); 24 | awake(); 25 | return; 26 | } 27 | super.onReceive(o); 28 | } 29 | 30 | @Override 31 | protected void transitionState(byte state) { 32 | clearAwake(); 33 | super.transitionState(state); 34 | } 35 | 36 | private void clearAwake() { 37 | getContext().parent().tell(Messages.ClearAwake.newBuilder().setSagaid(getSagaCompositeId()).build(), self()); 38 | log.debug("{} {} cleared awake", getSelf(), id); 39 | } 40 | 41 | protected abstract void awake(); 42 | 43 | protected void scheduleAwake(int time, TimeUnit timeUnit) { 44 | log.debug("{} {} scheduling awake in {} {}", getSelf(), id, time, timeUnit); 45 | DateTime now = DateTime.now(); 46 | if (TimeUnit.SECONDS.equals(timeUnit)) { 47 | now = now.plusSeconds(time); 48 | } else if (TimeUnit.MILLISECONDS.equals(timeUnit)) { 49 | now = now.plusMillis(time); 50 | } else if (TimeUnit.MINUTES.equals(timeUnit)) { 51 | now = now.plusMinutes(time); 52 | } else if (TimeUnit.HOURS.equals(timeUnit)) { 53 | now = now.plusHours(time); 54 | } else if (TimeUnit.DAYS.equals(timeUnit)) { 55 | now = now.plusDays(time); 56 | } else { 57 | log.error("No valid DateTime units for " + timeUnit); 58 | return; 59 | } 60 | Messages.SagaCompositeId sagaid = getSagaCompositeId(); 61 | log.debug("Sending awake for " + now.toString()); 62 | getContext().parent().tell(Messages.ScheduleAwake.newBuilder().setAwake(ProtobufHelper.toTimestamp(now)).setSagaid(sagaid).build(), self()); 63 | } 64 | 65 | private Messages.SagaCompositeId getSagaCompositeId() { 66 | return Messages.SagaCompositeId.newBuilder().setClazz(getClass().getName()).setId(id).build(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/TimeOutStore.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.Cancellable; 4 | import akka.actor.Props; 5 | import akka.actor.UntypedActor; 6 | import eventstore.Messages; 7 | import no.ks.eventstore2.ProtobufHelper; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import scala.Option; 11 | import scala.concurrent.duration.Duration; 12 | 13 | import java.util.List; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | public class TimeOutStore extends UntypedActor { 17 | 18 | private static Logger log = LoggerFactory.getLogger(TimeOutStore.class); 19 | private SagaRepository repository; 20 | private Cancellable cancellable; 21 | 22 | public TimeOutStore(SagaRepository repository) { 23 | this.repository = repository; 24 | } 25 | 26 | @Override 27 | public void preStart() throws Exception { 28 | cancellable = startScheduledAwake(); 29 | } 30 | 31 | private Cancellable startScheduledAwake() { 32 | return getContext().system().scheduler().schedule(Duration.create(10, TimeUnit.SECONDS), Duration.create(3, TimeUnit.SECONDS), self(), "awake", getContext().system().dispatcher(), self()); 33 | } 34 | 35 | @Override 36 | public void preRestart(Throwable reason, Option message) throws Exception { 37 | super.preRestart(reason, message); 38 | cancellable.cancel(); 39 | cancellable = null; 40 | } 41 | 42 | @Override 43 | public void aroundPostRestart(Throwable reason) { 44 | super.aroundPostRestart(reason); 45 | if(cancellable == null || cancellable.isCancelled()){ 46 | cancellable = startScheduledAwake(); 47 | } 48 | } 49 | 50 | @Override 51 | public void postStop() throws Exception { 52 | cancellable.cancel(); 53 | cancellable = null; 54 | } 55 | 56 | @Override 57 | public void onReceive(Object message) throws Throwable { 58 | if (message instanceof Messages.ScheduleAwake) { 59 | repository.storeScheduleAwake(((Messages.ScheduleAwake) message).getSagaid().getId(), ((Messages.ScheduleAwake) message).getSagaid().getClazz(), ProtobufHelper.fromTimestamp(((Messages.ScheduleAwake) message).getAwake())); 60 | } else if (message instanceof Messages.ClearAwake) { 61 | repository.clearAwake(((Messages.ClearAwake) message).getSagaid().getId(), ((Messages.ClearAwake) message).getSagaid().getClazz()); 62 | } else if ("awake".equals(message)) { 63 | final List sagaCompositeIds = repository.whoNeedsToWake(); 64 | log.info("Awoke {} sagas", sagaCompositeIds.size()); 65 | sagaCompositeIds.stream().forEach((v) -> { 66 | final Messages.SagaCompositeId.Builder builder = Messages.SagaCompositeId.newBuilder(); 67 | builder.setClazz(v.getClz().getName()); 68 | builder.setId(v.getId()); 69 | final Messages.SagaCompositeId sagaid = builder.build(); 70 | final Messages.SendAwake sendAwake = Messages.SendAwake.newBuilder().setSagaid(sagaid).build(); 71 | repository.clearAwake(v.getId(), v.getClz().getName()); 72 | getContext().parent().tell(sendAwake, self()); 73 | } 74 | ); 75 | } 76 | } 77 | 78 | public static Props mkProps(SagaRepository repository) { 79 | return Props.create(TimeOutStore.class, repository); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/saga/UpgradeSagaRepoStore.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import java.io.Serializable; 4 | 5 | public class UpgradeSagaRepoStore implements Serializable{ 6 | 7 | private SagaRepository sagaRepository; 8 | 9 | public UpgradeSagaRepoStore() { 10 | } 11 | 12 | public UpgradeSagaRepoStore(SagaRepository sagaRepository) { 13 | this.sagaRepository = sagaRepository; 14 | } 15 | 16 | public SagaRepository getSagaRepository() { 17 | return sagaRepository; 18 | } 19 | 20 | public void setSagaRepository(SagaRepository sagaRepository) { 21 | this.sagaRepository = sagaRepository; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/store/MongoDbStore.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.store; 2 | 3 | 4 | import com.mongodb.DB; 5 | import com.mongodb.DBCollection; 6 | import com.mongodb.MongoClient; 7 | 8 | public class MongoDbStore { 9 | 10 | private DB db; 11 | 12 | public MongoDbStore(MongoClient mongoClient, String name) { 13 | db = mongoClient.getDB(name); 14 | } 15 | 16 | public DBCollection getCollection(String collectionName) { 17 | return db.getCollection(collectionName); 18 | } 19 | 20 | public DB getDb() { 21 | return db; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /eventstore/src/main/java/no/ks/eventstore2/util/IdUtil.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.util; 2 | 3 | import java.util.UUID; 4 | 5 | public class IdUtil { 6 | 7 | private IdUtil(){} 8 | public static String createUUID() { 9 | return UUID.randomUUID().toString(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /eventstore/src/main/protobuf/agg.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package events.Aggevents; 4 | 5 | message Aggevent { 6 | string query = 1; 7 | } 8 | -------------------------------------------------------------------------------- /eventstore/src/main/protobuf/messages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/timestamp.proto"; 4 | import "google/protobuf/any.proto"; 5 | 6 | package eventstore; 7 | 8 | message EventWrapper { 9 | string correlation_id = 1; 10 | string proto_serialization_type = 2; 11 | string aggregate_root_id = 3; 12 | int64 journalid = 4; 13 | string aggregate_type = 5; 14 | int64 version = 6; 15 | int64 occurred_on = 7; 16 | google.protobuf.Any event = 8; 17 | string created_by_user = 9; 18 | } 19 | 20 | message EventWrapperBatch { 21 | string aggregate_type = 1; 22 | string aggregate_root_id = 2; 23 | bool read_all_events = 3; 24 | repeated EventWrapper events = 4; 25 | } 26 | 27 | message Subscription { 28 | string aggregateType = 1; 29 | int64 fromJournalId = 2; 30 | } 31 | 32 | message AsyncSubscription { 33 | string aggregateType = 1; 34 | int64 fromJournalId = 2; 35 | } 36 | 37 | message IncompleteSubscriptionPleaseSendNew { 38 | string aggregateType = 1; 39 | } 40 | 41 | message CompleteSubscriptionRegistered { 42 | string aggregateType = 1; 43 | } 44 | 45 | message CompleteAsyncSubscriptionPleaseSendSyncSubscription { 46 | string aggregateType = 1; 47 | } 48 | 49 | message LiveSubscription { 50 | string aggregateType = 1; 51 | } 52 | 53 | message RemoveSubscription { 54 | string aggregateType = 1; 55 | } 56 | 57 | message SubscriptionRemoved { 58 | string aggregateType = 1; 59 | } 60 | 61 | message AcknowledgePreviousEventsProcessed { 62 | } 63 | 64 | message Success { 65 | 66 | } 67 | 68 | message RetreiveAggregateEvents { 69 | string aggregateRootId = 1; 70 | int64 fromJournalId = 2; 71 | string aggregateType = 3; 72 | } 73 | 74 | message RetreiveAggregateEventsAsync { 75 | string aggregateRootId = 1; 76 | int64 fromJournalId = 2; 77 | string aggregateType = 3; 78 | } 79 | 80 | message RetreiveCorrelationIdEventsAsync { 81 | string correlationId = 1; 82 | int64 fromJournalId = 2; 83 | string aggregateType = 3; 84 | } 85 | 86 | message GetSubscribers { 87 | } 88 | 89 | message Subscribers{ 90 | message Subscriber { 91 | string actorRef = 1; 92 | string aggregateType = 2; 93 | } 94 | repeated Subscriber subscribers = 1; 95 | } 96 | 97 | message SagaCompositeId{ 98 | string clazz = 1; 99 | string id = 2; 100 | } 101 | 102 | message ScheduleAwake{ 103 | SagaCompositeId sagaid = 1; 104 | google.protobuf.Timestamp awake = 2; 105 | } 106 | 107 | message SendAwake{ 108 | SagaCompositeId sagaid = 1; 109 | } 110 | 111 | message ClearAwake{ 112 | SagaCompositeId sagaid = 1; 113 | } 114 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/EmbeddedDatabaseTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; 7 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 9 | 10 | public class EmbeddedDatabaseTest { 11 | protected EmbeddedDatabase db; 12 | 13 | @Before 14 | public void setUp() throws Exception { 15 | EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); 16 | db = builder.setType(EmbeddedDatabaseType.H2).addScript("schema.sql").build(); 17 | } 18 | 19 | @Test 20 | public void testNothing() throws Exception { 21 | 22 | } 23 | 24 | @After 25 | public void tearDown() throws Exception { 26 | db.shutdown(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/EventStoreTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | 4 | import akka.actor.ActorRef; 5 | import akka.actor.ActorSystem; 6 | import akka.actor.Props; 7 | import akka.testkit.TestKit; 8 | import com.esotericsoftware.kryo.Kryo; 9 | import com.typesafe.config.ConfigFactory; 10 | import no.ks.eventstore2.events.Event1; 11 | import no.ks.eventstore2.events.Event4; 12 | import no.ks.eventstore2.events.NewEvent; 13 | import no.ks.eventstore2.events.OldEvent; 14 | import no.ks.eventstore2.eventstore.*; 15 | import no.ks.eventstore2.formProcessorProject.FormParsed; 16 | import no.ks.eventstore2.response.Success; 17 | import no.ks.eventstore2.testapplication.TestProjection; 18 | import org.junit.After; 19 | import org.junit.Test; 20 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; 21 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 22 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 23 | 24 | public class EventStoreTest extends TestKit { 25 | 26 | private static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory.load().getConfig("TestSys")); 27 | 28 | private KryoClassRegistration kryoClassRegistration = new KryoClassRegistration() { 29 | @Override 30 | public void registerClasses(Kryo kryo) { 31 | kryo.register(FormParsed.class, 1001); 32 | kryo.register(OldEvent.class, 1002); 33 | kryo.register(NewEvent.class, 1003); 34 | kryo.register(Event1.class, 1004); 35 | kryo.register(Event4.class, 1005); 36 | } 37 | }; 38 | 39 | private final EmbeddedDatabase db; 40 | 41 | public EventStoreTest() { 42 | super(_system); 43 | EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); 44 | db = builder.setType(EmbeddedDatabaseType.H2).addScript("schema.sql").build(); 45 | } 46 | 47 | @Test 48 | public void testAcknowledgeRespondsCorrectly() throws Exception { 49 | ActorRef eventstore = _system.actorOf(EventStore.mkProps(new H2JournalStorage(db, kryoClassRegistration)), "eventstore"); 50 | eventstore.tell(new AcknowledgePreviousEventsProcessed(),super.testActor()); 51 | expectMsgClass(Success.class); 52 | } 53 | 54 | @Test 55 | public void testPendingSubscriptionsIsFilled() throws Exception { 56 | ActorRef eventstore = _system.actorOf(EventStore.mkProps(new H2JournalStorage(db, kryoClassRegistration)), "eventstore_pendingSubscriptiontest"); 57 | FormParsed event = new FormParsed("formid"); 58 | eventstore.tell(event,super.testActor()); 59 | eventstore.tell(new Subscription(event.getAggregateType()),super.testActor()); 60 | expectMsg(event); 61 | } 62 | 63 | 64 | @Test 65 | public void testEventsAreUpgraded() throws Exception { 66 | ActorRef eventstore = _system.actorOf(EventStore.mkProps(new H2JournalStorage(db, kryoClassRegistration)), "eventstore_upgradEvent"); 67 | eventstore.tell(new OldEvent(),super.testActor()); 68 | eventstore.tell(new Subscription(new NewEvent().getAggregateType()),super.testActor()); 69 | expectMsgClass(NewEvent.class); 70 | } 71 | 72 | @Test 73 | public void testEventsAreUpgradedMultipleTimes() throws Exception { 74 | ActorRef eventstore = _system.actorOf(EventStore.mkProps(new H2JournalStorage(db, kryoClassRegistration)), "eventstore_upgradEventMultipleTimes"); 75 | eventstore.tell(new Event1(),super.testActor()); 76 | eventstore.tell(new Subscription(new Event4().getAggregateType()),super.testActor()); 77 | expectMsgClass(Event4.class); 78 | } 79 | 80 | @After 81 | public void tearDown() throws Exception { 82 | db.shutdown(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/Eventstore2TestKit.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import akka.actor.ActorSystem; 4 | import akka.testkit.TestKit; 5 | import com.typesafe.config.ConfigFactory; 6 | import events.test.Order.Order; 7 | import events.test.form.Form; 8 | import org.junit.Before; 9 | 10 | public class Eventstore2TestKit extends TestKit{ 11 | 12 | protected static final ActorSystem _system = ActorSystem.create("eventstore2", ConfigFactory.parseResources("classpath:/application_local.conf")); 13 | protected static final int DURATION = 10000; 14 | 15 | public Eventstore2TestKit() { 16 | super(_system); 17 | System.setProperty("CONSTRETTO_TAGS", "ITEST"); 18 | } 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | ProtobufHelper.registerDeserializeMethod(Order.SearchRequest.getDefaultInstance()); 23 | ProtobufHelper.registerDeserializeMethod(Order.SearchResult.getDefaultInstance()); 24 | ProtobufHelper.registerDeserializeMethod(Form.FormReceived.getDefaultInstance()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/FormProcessorIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.ActorSystem; 5 | import akka.actor.Props; 6 | import akka.testkit.JavaTestKit; 7 | import com.esotericsoftware.kryo.Kryo; 8 | import no.ks.eventstore2.command.CommandDispatcher; 9 | import no.ks.eventstore2.eventstore.*; 10 | import no.ks.eventstore2.formProcessorProject.*; 11 | import no.ks.eventstore2.projection.MongoDbEventstore2TestKit; 12 | import no.ks.eventstore2.saga.SagaInMemoryRepository; 13 | import no.ks.eventstore2.saga.SagaManager; 14 | import org.junit.AfterClass; 15 | import org.junit.BeforeClass; 16 | import org.junit.Test; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | 21 | public class FormProcessorIntegrationTest extends MongoDbEventstore2TestKit { 22 | 23 | private static ActorSystem system; 24 | 25 | private KryoClassRegistration kryoClassRegistration = new KryoClassRegistration() { 26 | @Override 27 | public void registerClasses(Kryo kryo) { 28 | kryo.register(FormDelivered.class, 1001); 29 | kryo.register(FormDeliverer.class, 1002); 30 | kryo.register(FormParsed.class, 1003); 31 | kryo.register(FormParser.class, 1004); 32 | kryo.register(FormReceived.class, 1005); 33 | } 34 | }; 35 | 36 | @BeforeClass 37 | public static void setup() { 38 | system = ActorSystem.create(); 39 | EventstoreSingelton.kryoSerializedEvents.add("FORM"); 40 | } 41 | 42 | @AfterClass 43 | public static void teardown() { 44 | system.terminate(); 45 | } 46 | 47 | @Test 48 | public void testFormStatusIsCorrectlyUpdatedOnFormReceived() throws Exception { 49 | new JavaTestKit(system) {{ 50 | 51 | final Props eventStoreProps = EventStore.mkProps(new MongoDBJournalV2(mongoClient.getDatabase("Journal"), kryoClassRegistration, Arrays.asList(new String[]{"FORM"}), null)); 52 | final ActorRef eventStore = system.actorOf(eventStoreProps, "eventStore"); 53 | 54 | ArrayList commandHandlerProps = new ArrayList<>(); 55 | 56 | commandHandlerProps.add(Props.create(FormParser.class, eventStore)); 57 | 58 | commandHandlerProps.add(Props.create(FormDeliverer.class, eventStore)); 59 | 60 | 61 | final ActorRef commandDispatcher = system.actorOf(CommandDispatcher.mkProps(commandHandlerProps), "commandDispatcher"); 62 | final ActorRef sagaManager = system.actorOf(SagaManager.mkProps(system, commandDispatcher, new SagaInMemoryRepository(), eventStore), "sagaManager"); 63 | 64 | eventStore.tell(new FormReceived("form_id_1"), getRef()); 65 | eventStore.tell(new Subscription("FORM"), getRef()); 66 | expectMsgClass(FormReceived.class); 67 | expectMsgClass(CompleteSubscriptionRegistered.class); 68 | expectMsgClass(FormParsed.class); 69 | expectMsgClass(FormDelivered.class); 70 | 71 | }}; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/ProtobufHelperTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2; 2 | 3 | import com.google.protobuf.Timestamp; 4 | import org.joda.time.DateTime; 5 | import org.junit.Test; 6 | 7 | import java.util.Date; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | public class ProtobufHelperTest { 12 | 13 | @Test 14 | public void TimestampConversion() throws Exception { 15 | final DateTime now = DateTime.now(); 16 | final Timestamp timestamp = ProtobufHelper.toTimestamp(now); 17 | final DateTime now2 = ProtobufHelper.fromTimestamp(timestamp); 18 | assertEquals(now, now2); 19 | } 20 | 21 | @Test 22 | public void DateConversion() throws Exception { 23 | final Date now = new Date(); 24 | final Timestamp timestamp = ProtobufHelper.toTimestamp(now); 25 | final Date now2 = ProtobufHelper.fromTimestampToDate(timestamp); 26 | assertEquals(now, now2); 27 | } 28 | 29 | @Test 30 | public void DateAndDateTime() throws Exception { 31 | final Date now = new Date(); 32 | final Timestamp timestamp = ProtobufHelper.toTimestamp(now); 33 | final DateTime now2 = ProtobufHelper.fromTimestamp(timestamp); 34 | assertEquals(now, now2.toDate()); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/command/CommandDispatcherTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import akka.actor.ActorSystem; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import akka.testkit.TestKit; 7 | import com.typesafe.config.ConfigFactory; 8 | import no.ks.eventstore2.formProcessorProject.FormParsed; 9 | import no.ks.eventstore2.formProcessorProject.FormParser; 10 | import no.ks.eventstore2.formProcessorProject.ParseForm; 11 | import org.junit.Test; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import static akka.testkit.JavaTestKit.duration; 19 | 20 | public class CommandDispatcherTest extends TestKit { 21 | 22 | private static Logger log = LoggerFactory.getLogger(CommandDispatcherTest.class); 23 | 24 | static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory 25 | .load().getConfig("TestSys")); 26 | 27 | public CommandDispatcherTest() { 28 | super(_system); 29 | } 30 | 31 | @Test 32 | public void testSendsCommandToNotificationCommandHandler() throws Exception { 33 | log.debug("Test started"); 34 | List props = new ArrayList<>(); 35 | props.add(Props.create(FormParser.class, super.testActor())); 36 | TestActorRef commandDispatcherRef = TestActorRef.create(_system, CommandDispatcher.mkProps(props), "commandDispatcherKing"); 37 | Thread.sleep(2000); 38 | commandDispatcherRef.tell(new ParseForm("1"), super.testActor()); 39 | expectMsgClass(duration("300 seconds"), FormParsed.class); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/command/CommandHandlerNewStyleAnnotationsTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import no.ks.eventstore2.Eventstore2TestKit; 7 | import no.ks.eventstore2.Handler; 8 | import org.junit.Test; 9 | 10 | import java.util.UUID; 11 | 12 | import static junit.framework.Assert.assertTrue; 13 | 14 | public class CommandHandlerNewStyleAnnotationsTest extends Eventstore2TestKit { 15 | 16 | @Test 17 | public void test_that_a_command_handler_accepts_commands_annotated_with_handleCommand() throws Exception { 18 | TestActorRef testActor = TestActorRef.create(_system, Props.create(CommandHandlerWithNewStyleAnnotations.class, super.testActor()), UUID.randomUUID().toString()); 19 | testActor.tell(new TestCommand(), super.testActor()); 20 | assertTrue(testActor.underlyingActor().commandReceived); 21 | } 22 | 23 | private static class CommandHandlerWithNewStyleAnnotations extends CommandHandler { 24 | 25 | public boolean commandReceived = false; 26 | 27 | public CommandHandlerWithNewStyleAnnotations(ActorRef eventStore) { 28 | super(eventStore); 29 | } 30 | 31 | @Handler 32 | public void handle(TestCommand command){ 33 | commandReceived = true; 34 | } 35 | } 36 | 37 | private static class TestCommand extends Command {} 38 | } 39 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/command/CommandHandlerTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.command; 2 | 3 | import akka.actor.ActorSystem; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import akka.testkit.TestKit; 7 | import com.typesafe.config.ConfigFactory; 8 | import no.ks.eventstore2.formProcessorProject.FormParsed; 9 | import no.ks.eventstore2.formProcessorProject.FormParser; 10 | import no.ks.eventstore2.formProcessorProject.ParseForm; 11 | import org.junit.Test; 12 | import org.springframework.test.util.ReflectionTestUtils; 13 | 14 | public class CommandHandlerTest extends TestKit { 15 | 16 | static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory 17 | .load().getConfig("TestSys")); 18 | 19 | public CommandHandlerTest() { 20 | super(_system); 21 | } 22 | 23 | @Test 24 | public void testCommandHandlerReceivesCommandAndDispatchesCorrespondingEvent() throws Exception { 25 | final TestActorRef ref = TestActorRef.create(_system, Props.create(FormParser.class, super.testActor()) ,"notification_handler"); 26 | ReflectionTestUtils.setField(ref.underlyingActor(), "eventStore", super.testActor()); 27 | ref.tell(new ParseForm("formId"), super.testActor()); 28 | expectMsgClass(FormParsed.class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/Event1.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class Event1 extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | @Override 9 | public String getLogMessage() { 10 | return null; 11 | } 12 | 13 | @Override 14 | public Event upgrade() { 15 | return new Event2(); 16 | } 17 | 18 | @Override 19 | public String getAggregateRootId() { 20 | return null; 21 | } 22 | 23 | @Override 24 | public String getAggregateType() { 25 | return "upgrade"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/Event2.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class Event2 extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | @Override 9 | public String getLogMessage() { 10 | return null; 11 | } 12 | 13 | @Override 14 | public Event upgrade() { 15 | return new Event3(); 16 | } 17 | 18 | @Override 19 | public String getAggregateRootId() { 20 | return "1"; 21 | } 22 | 23 | @Override 24 | public String getAggregateType() { 25 | return "upgrade"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/Event3.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class Event3 extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | @Override 9 | public String getLogMessage() { 10 | return null; 11 | } 12 | 13 | @Override 14 | public Event upgrade() { 15 | return new Event4(); 16 | } 17 | 18 | @Override 19 | public String getAggregateRootId() { 20 | return null; 21 | } 22 | 23 | @Override 24 | public String getAggregateType() { 25 | return "upgrade"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/Event4.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class Event4 extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | @Override 9 | public String getLogMessage() { 10 | return null; 11 | } 12 | 13 | @Override 14 | public String getAggregateRootId() { 15 | return "1"; 16 | } 17 | 18 | @Override 19 | public String getAggregateType() { 20 | return "upgrade"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/NewEvent.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class NewEvent extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | @Override 9 | public String getLogMessage() { 10 | return null; 11 | } 12 | 13 | @Override 14 | public String getAggregateRootId() { 15 | return null; 16 | } 17 | 18 | @Override 19 | public String getAggregateType() { 20 | return "upgrade"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/OldEvent.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class OldEvent extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | @Override 9 | public String getLogMessage() { 10 | return null; 11 | } 12 | 13 | @Override 14 | public Event upgrade() { 15 | return new NewEvent(); 16 | } 17 | 18 | @Override 19 | public String getAggregateRootId() { 20 | return "1"; 21 | } 22 | 23 | @Override 24 | public String getAggregateType() { 25 | return "upgrade"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/events/OldEventShouldBeUpgradedToOrderSearchResult.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.events; 2 | 3 | import events.test.Order.Order; 4 | import eventstore.Messages; 5 | import no.ks.eventstore2.Event; 6 | import no.ks.eventstore2.ProtobufHelper; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class OldEventShouldBeUpgradedToOrderSearchResult extends Event { 11 | 12 | String rootId; 13 | 14 | ArrayList results = new ArrayList<>(); 15 | 16 | public OldEventShouldBeUpgradedToOrderSearchResult(String rootId, ArrayList results) { 17 | this.rootId = rootId; 18 | this.results = results; 19 | } 20 | 21 | @Override 22 | public String getAggregateType() { 23 | return "ORDER"; 24 | } 25 | 26 | @Override 27 | public String getLogMessage() { 28 | return null; 29 | } 30 | 31 | @Override 32 | public String getAggregateRootId() { 33 | return rootId; 34 | } 35 | 36 | @Override 37 | public Messages.EventWrapper upgradeToProto() { 38 | return ProtobufHelper.newEventWrapper(getAggregateType(), getAggregateRootId(), Long.valueOf(getJournalid()), 39 | Order.SearchResult.newBuilder().addAllResult(results).build()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/eventstore/AggEvent.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class AggEvent extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String aggregateRootId; 9 | private String aggregateType; 10 | 11 | public AggEvent(String aggregateType) { 12 | this.aggregateRootId = "aggregaterootid"; 13 | this.aggregateType = aggregateType; 14 | } 15 | 16 | public AggEvent(String aggregateRootId, String aggregateType) { 17 | this.aggregateRootId = aggregateRootId; 18 | this.aggregateType = aggregateType; 19 | } 20 | 21 | @Override 22 | public String getLogMessage() { 23 | return null; 24 | } 25 | 26 | @Override 27 | public String getAggregateRootId() { 28 | return aggregateRootId; 29 | } 30 | 31 | @Override 32 | public String getAggregateType() { 33 | return aggregateType; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (this == o) return true; 39 | if (!(o instanceof AggEvent)) return false; 40 | 41 | AggEvent aggEvent = (AggEvent) o; 42 | 43 | if (aggregateRootId != null ? !aggregateRootId.equals(aggEvent.aggregateRootId) : aggEvent.aggregateRootId != null) 44 | return false; 45 | if (aggregateType != null ? !aggregateType.equals(aggEvent.aggregateType) : aggEvent.aggregateType != null) 46 | return false; 47 | 48 | return true; 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | int result = aggregateRootId != null ? aggregateRootId.hashCode() : 0; 54 | result = 31 * result + (aggregateType != null ? aggregateType.hashCode() : 0); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/eventstore/EvenstoreTestReadAggregateEvents.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import akka.actor.Actor; 4 | import akka.testkit.TestActorRef; 5 | import com.esotericsoftware.kryo.Kryo; 6 | import com.mongodb.client.MongoDatabase; 7 | import no.ks.eventstore2.Event; 8 | import no.ks.eventstore2.projection.MongoDbEventstore2TestKit; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | public class EvenstoreTestReadAggregateEvents extends MongoDbEventstore2TestKit{ 17 | 18 | private KryoClassRegistration kryoClassRegistration = new KryoClassRegistration() { 19 | @Override 20 | public void registerClasses(Kryo kryo) { 21 | kryo.register(AggEvent.class, 1001); 22 | } 23 | }; 24 | private MongoDBJournalV2 journal; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | super.setUp(); 29 | 30 | MongoDatabase db = mongoClient.getDatabase("Journal"); 31 | journal = new MongoDBJournalV2(db, kryoClassRegistration, Arrays.asList(new String[]{"agg1", "agg", "agg2"}), 10, null); 32 | } 33 | 34 | @Test 35 | public void testReadEventsForOneAggregateId() throws Exception { 36 | for(int i = 0; i<3;i++) 37 | journal.saveEvent(new AggEvent("id","agg")); 38 | TestActorRef actorTestActorRef = TestActorRef.create(_system, EventStore.mkProps(journal)); 39 | actorTestActorRef.tell(new RetreiveAggregateEvents("agg", "id", null), super.testActor()); 40 | List events = new ArrayList<>(); 41 | for(int i = 0; i<3;i++) 42 | events.add(new AggEvent("id","agg")); 43 | expectMsg(new EventBatch("agg", "id", events, true)); 44 | } 45 | 46 | @Test 47 | public void testReadEventsForOneAggregateIdAndContinueWhenBatchIsFull() throws Exception { 48 | for(int i = 0; i<11;i++) 49 | journal.saveEvent(new AggEvent("id","agg2")); 50 | TestActorRef actorTestActorRef = TestActorRef.create(_system, EventStore.mkProps(journal)); 51 | actorTestActorRef.tell(new RetreiveAggregateEvents("agg2", "id", null), super.testActor()); 52 | List events = new ArrayList<>(); 53 | for(int i = 0; i<10;i++) 54 | events.add(new AggEvent("id","agg2")); 55 | expectMsg(new EventBatch("agg2", "id", events, false)); 56 | actorTestActorRef.tell(new RetreiveAggregateEvents("agg2", "id", "10"), super.testActor()); 57 | events.clear(); 58 | for(int i = 0; i<1;i++) 59 | events.add(new AggEvent("id","agg2")); 60 | expectMsg(new EventBatch("agg2", "id", events, true)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/eventstore/EvenstoreTestReadAggregateProtoEvents.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import akka.actor.Actor; 4 | import akka.actor.Inbox; 5 | import akka.testkit.TestActorRef; 6 | import com.esotericsoftware.kryo.Kryo; 7 | import com.mongodb.client.MongoDatabase; 8 | import events.test.Order.Order; 9 | import eventstore.Messages; 10 | import no.ks.eventstore2.ProtobufHelper; 11 | import no.ks.eventstore2.projection.MongoDbEventstore2TestKit; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import scala.concurrent.duration.Duration; 15 | 16 | import java.util.Arrays; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | public class EvenstoreTestReadAggregateProtoEvents extends MongoDbEventstore2TestKit{ 22 | 23 | private KryoClassRegistration kryoClassRegistration = new KryoClassRegistration() { 24 | @Override 25 | public void registerClasses(Kryo kryo) { 26 | kryo.register(AggEvent.class, 1001); 27 | } 28 | }; 29 | private MongoDBJournalV2 journal; 30 | 31 | @Before 32 | public void setUp() throws Exception { 33 | super.setUp(); 34 | 35 | MongoDatabase db = mongoClient.getDatabase("Journal"); 36 | journal = new MongoDBJournalV2(db, kryoClassRegistration, Arrays.asList(new String[]{"agg1", "agg", "agg2"}), 10, null); 37 | } 38 | 39 | @Test 40 | public void testReadEventsForOneAggregateId() throws Exception { 41 | for(int i = 0; i<3;i++) 42 | journal.saveEvent(ProtobufHelper.newEventWrapper("agg", "id", -1, Order.SearchRequest.newBuilder().setQuery("query").setPageNumber(4).build())); 43 | TestActorRef actorTestActorRef = TestActorRef.create(_system, EventStore.mkProps(journal)); 44 | Inbox inbox = Inbox.create(_system); 45 | actorTestActorRef.tell(Messages.RetreiveAggregateEvents.newBuilder().setAggregateType("agg").setAggregateRootId("id").build(), inbox.getRef()); 46 | final Object receive = inbox.receive(Duration.create(3, TimeUnit.SECONDS)); 47 | assertEquals(receive.getClass(), Messages.EventWrapperBatch.class); 48 | assertEquals("id", ((Messages.EventWrapperBatch)receive).getAggregateRootId()); 49 | assertEquals(3, ((Messages.EventWrapperBatch) receive).getEventsCount()); 50 | } 51 | 52 | @Test 53 | public void testReadEventsForOneAggregateIdAndContinueWhenBatchIsFull() throws Exception { 54 | for(int i = 0; i<11;i++) 55 | journal.saveEvent(ProtobufHelper.newEventWrapper("agg2", "id", -1, Order.SearchRequest.newBuilder().setQuery("query").setPageNumber(4).build())); 56 | TestActorRef actorTestActorRef = TestActorRef.create(_system, EventStore.mkProps(journal)); 57 | Inbox inbox = Inbox.create(_system); 58 | actorTestActorRef.tell(Messages.RetreiveAggregateEvents.newBuilder().setAggregateType("agg2").setAggregateRootId("id").build(), inbox.getRef()); 59 | Messages.EventWrapperBatch receive = (Messages.EventWrapperBatch) inbox.receive(Duration.create(3, TimeUnit.SECONDS)); 60 | assertEquals("id",receive.getAggregateRootId()); 61 | assertEquals(10,receive.getEventsCount()); 62 | actorTestActorRef.tell(Messages.RetreiveAggregateEvents.newBuilder() 63 | .setAggregateType("agg2") 64 | .setAggregateRootId("id") 65 | .setFromJournalId(10).build(), inbox.getRef()); 66 | receive = (Messages.EventWrapperBatch) inbox.receive(Duration.create(3, TimeUnit.SECONDS)); 67 | assertEquals("agg2", receive.getAggregateType()); 68 | assertEquals(1, receive.getEventsCount()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/eventstore/EventListProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import akka.actor.UntypedActor; 4 | import no.ks.eventstore2.Event; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | 10 | public class EventListProjection extends UntypedActor { 11 | 12 | private List events = new ArrayList<>(); 13 | 14 | @Override 15 | public void onReceive(Object o) throws Exception { 16 | if(o instanceof Event){ 17 | events.add((Event) o); 18 | } 19 | } 20 | 21 | public List getEvents(){ 22 | return events; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/eventstore/H2JournalStorageTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.eventstore; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import no.ks.eventstore2.Event; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; 9 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 10 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 11 | 12 | import static org.junit.Assert.assertTrue; 13 | 14 | public class H2JournalStorageTest { 15 | 16 | private H2JournalStorage h2JournalStorage; 17 | private String aggregateType = "aggregateType"; 18 | private EmbeddedDatabase dataSource; 19 | private Event lastEvent; 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | dataSource = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("schema.sql").build(); 24 | h2JournalStorage = new H2JournalStorage(dataSource, createKryoClassRegistration()); 25 | for(int i=0; i < 20350; i++) { 26 | h2JournalStorage.saveEvent(new AggEvent("id_" + i, aggregateType)); 27 | } 28 | } 29 | 30 | @After 31 | public void tearDown() throws Exception { 32 | dataSource.shutdown(); 33 | } 34 | 35 | @Test 36 | public void testLoadingOfEvents() { 37 | String fromKey = "0"; 38 | boolean finished = false; 39 | while(!finished) { 40 | if(lastEvent != null) { 41 | fromKey = lastEvent.getJournalid(); 42 | } 43 | finished = h2JournalStorage.loadEventsAndHandle(aggregateType, createHandleEvent(), fromKey); 44 | } 45 | assertTrue(finished); 46 | } 47 | 48 | private HandleEvent createHandleEvent() { 49 | return new HandleEvent() { 50 | @Override 51 | public void handleEvent(Event event) { 52 | lastEvent = event; 53 | } 54 | }; 55 | } 56 | 57 | private KryoClassRegistration createKryoClassRegistration() { 58 | return new KryoClassRegistration() { 59 | @Override 60 | public void registerClasses(Kryo kryo) { 61 | kryo.register(AggEvent.class, 1001); 62 | } 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/DeliverForm.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import no.ks.eventstore2.command.Command; 4 | 5 | public class DeliverForm extends Command { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String formId; 9 | 10 | public DeliverForm(String formId) { 11 | this.formId = formId; 12 | } 13 | 14 | public String getFormId() { 15 | return formId; 16 | } 17 | 18 | public void setFormId(String formId) { 19 | this.formId = formId; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/DummyActor.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import akka.actor.UntypedActor; 4 | 5 | public class DummyActor extends UntypedActor { 6 | @Override 7 | public void onReceive(Object o) throws Exception { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormDelivered.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class FormDelivered extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String formId; 9 | 10 | public FormDelivered(String formId) { 11 | this.formId = formId; 12 | } 13 | 14 | public String getFormId() { 15 | return formId; 16 | } 17 | 18 | public void setFormId(String formId) { 19 | this.formId = formId; 20 | } 21 | 22 | @Override 23 | public String getLogMessage() { 24 | return "Form has been delivered"; 25 | } 26 | 27 | @Override 28 | public String getAggregateRootId() { 29 | return formId; 30 | } 31 | 32 | @Override 33 | public String getAggregateType() { 34 | return "FORM"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormDeliverer.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.command.CommandHandler; 5 | import no.ks.eventstore2.command.HandlesCommand; 6 | 7 | @HandlesCommand(DeliverForm.class) 8 | public class FormDeliverer extends CommandHandler { 9 | public FormDeliverer(ActorRef eventStore) { 10 | super(eventStore); 11 | } 12 | 13 | public void handleCommand(DeliverForm command){ 14 | eventStore.tell(new FormDelivered(command.getFormId()), self()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormParsed.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class FormParsed extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String formId; 9 | 10 | public FormParsed(String formId) { 11 | this.formId = formId; 12 | } 13 | 14 | public String getFormId() { 15 | return formId; 16 | } 17 | 18 | public void setFormId(String formId) { 19 | this.formId = formId; 20 | } 21 | 22 | @Override 23 | public String getLogMessage() { 24 | return "Form has been parsed"; 25 | } 26 | 27 | @Override 28 | public String getAggregateRootId() { 29 | return formId; 30 | } 31 | 32 | @Override 33 | public String getAggregateType() { 34 | return "FORM"; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object o) { 39 | if (this == o) return true; 40 | if (o == null || getClass() != o.getClass()) return false; 41 | 42 | FormParsed that = (FormParsed) o; 43 | 44 | if (formId != null ? !formId.equals(that.formId) : that.formId != null) return false; 45 | 46 | return true; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return formId != null ? formId.hashCode() : 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormParser.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.command.CommandHandler; 5 | import no.ks.eventstore2.command.HandlesCommand; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | @HandlesCommand(ParseForm.class) 10 | public class FormParser extends CommandHandler { 11 | 12 | private static Logger log = LoggerFactory.getLogger(FormParser.class); 13 | public FormParser(ActorRef eventStore) { 14 | super(eventStore); 15 | log.debug("FormParser created"); 16 | } 17 | 18 | public void handleCommand(ParseForm command){ 19 | log.debug("got command " + command); 20 | eventStore.tell(new FormParsed(command.getFormId()), self()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormProcess.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import akka.actor.ActorRef; 4 | import events.test.form.Form; 5 | import no.ks.eventstore2.Handler; 6 | import no.ks.eventstore2.projection.Subscriber; 7 | import no.ks.eventstore2.saga.Saga; 8 | import no.ks.eventstore2.saga.SagaEventIdProperty; 9 | import no.ks.eventstore2.saga.SagaRepository; 10 | 11 | @SagaEventIdProperty(useAggregateRootId = true) 12 | @Subscriber("FORM") 13 | public class FormProcess extends Saga { 14 | private static final byte FORM_RECEIVED = 2; 15 | private static final byte FORM_PARSED = 3; 16 | private static final byte FORM_DELIVERED = 4; 17 | 18 | public FormProcess(String id, ActorRef commandDispatcher, SagaRepository repository) { 19 | super(id, commandDispatcher, repository); 20 | } 21 | 22 | @Override 23 | protected String getSagaStateId() { 24 | return "FormProcess"; 25 | } 26 | 27 | @Handler 28 | public void handleEvent(FormReceived event){ 29 | if (getState() == STATE_INITIAL){ 30 | commandDispatcher.tell(new ParseForm(event.getFormId()), self()); 31 | transitionState(FORM_RECEIVED); 32 | } 33 | } 34 | 35 | @Handler 36 | public void handleEvent(Form.FormReceived event){ 37 | if (getState() == STATE_INITIAL){ 38 | commandDispatcher.tell(new ParseForm(eventWrapper().getAggregateRootId()), self()); 39 | transitionState(FORM_RECEIVED); 40 | } 41 | } 42 | 43 | @Handler 44 | public void handleEvent(FormParsed event){ 45 | if (getState() == FORM_RECEIVED){ 46 | commandDispatcher.tell(new DeliverForm(event.getFormId()), self()); 47 | transitionState(FORM_PARSED); 48 | } 49 | } 50 | @Handler 51 | public void handleEvent(FormDelivered event){ 52 | if (getState() == FORM_PARSED){ 53 | transitionState(FORM_DELIVERED); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormReceived.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class FormReceived extends Event { 6 | private static final long serialVersionUID = 1L; 7 | 8 | private String formId; 9 | 10 | public FormReceived(String formId) { 11 | this.formId = formId; 12 | } 13 | 14 | public String getFormId() { 15 | return formId; 16 | } 17 | 18 | public void setFormId(String formId) { 19 | this.formId = formId; 20 | } 21 | 22 | @Override 23 | public String getLogMessage() { 24 | return "Form has been received"; 25 | } 26 | 27 | @Override 28 | public String getAggregateRootId() { 29 | return formId; 30 | } 31 | 32 | @Override 33 | public String getAggregateType() { 34 | return "FORM"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormStatus.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | public enum FormStatus { 4 | DELIVERED, PARSED, RECEIVED 5 | } 6 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/FormStatuses.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.projection.ListensTo; 5 | import no.ks.eventstore2.projection.Projection; 6 | 7 | import java.util.HashMap; 8 | import java.util.LinkedHashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | @ListensTo(value = {FormReceived.class, FormParsed.class, FormDelivered.class}, aggregates = "FORM") 13 | public class FormStatuses extends Projection { 14 | 15 | Map statuses = new LinkedHashMap(); 16 | 17 | public FormStatuses(ActorRef eventStore) { 18 | super(eventStore); 19 | } 20 | 21 | public void handleEvent(FormReceived event){ 22 | statuses.put(event.getFormId(), FormStatus.RECEIVED); 23 | } 24 | 25 | public void handleEvent(FormParsed event){ 26 | statuses.put(event.getFormId(), FormStatus.PARSED); 27 | } 28 | 29 | public void handleEvent(FormDelivered event){ 30 | statuses.put(event.getFormId(), FormStatus.DELIVERED); 31 | } 32 | 33 | public int getNumberOfForms() { 34 | return statuses.size(); 35 | } 36 | 37 | public FormStatus getStatus(String formId) { 38 | return statuses.get(formId); 39 | } 40 | 41 | public Map getStatuses(List formIds){ 42 | Map result = new HashMap(); 43 | for (String formId : formIds){ 44 | result.put(formId, statuses.get(formId)); 45 | } 46 | return result; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/formProcessorProject/ParseForm.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.formProcessorProject; 2 | 3 | import no.ks.eventstore2.command.Command; 4 | 5 | public class ParseForm extends Command { 6 | private String formId; 7 | 8 | public ParseForm(String formId) { 9 | this.formId = formId; 10 | } 11 | 12 | public String getFormId() { 13 | return formId; 14 | } 15 | 16 | public void setFormId(String formId) { 17 | this.formId = formId; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/FailingProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.Event; 5 | import no.ks.eventstore2.Handler; 6 | import no.ks.eventstore2.eventstore.Subscription; 7 | 8 | public class FailingProjection extends Projection { 9 | 10 | boolean failed = false; 11 | 12 | public FailingProjection(ActorRef eventStore) { 13 | super(eventStore); 14 | } 15 | 16 | @Override 17 | protected Subscription getSubscribe() { 18 | return new Subscription("agg"); 19 | } 20 | 21 | @Handler 22 | public void handleEvent(Event event) { 23 | if (failed) 24 | sender().tell(event, self()); 25 | else { 26 | failed = true; 27 | throw new RuntimeException("Failing"); 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/FutureCallProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.eventstore.Subscription; 5 | import scala.concurrent.ExecutionContext; 6 | import scala.concurrent.Future; 7 | 8 | import java.util.concurrent.Callable; 9 | 10 | import static akka.dispatch.Futures.future; 11 | 12 | public class FutureCallProjection extends Projection { 13 | 14 | public FutureCallProjection(ActorRef eventStore) { 15 | super(eventStore); 16 | } 17 | 18 | @Override 19 | protected Subscription getSubscribe() { 20 | return new Subscription("agg"); 21 | } 22 | 23 | public Future getString(){ 24 | ExecutionContext ec = getContext().system().dispatcher(); 25 | return future(new Callable() { 26 | @Override 27 | public String call() throws Exception { 28 | return "OK"; 29 | } 30 | }, ec); 31 | } 32 | 33 | public Future getFailure(){ 34 | ExecutionContext ec = getContext().system().dispatcher(); 35 | return future(new Callable() { 36 | @Override 37 | public String call() throws Exception { 38 | throw new RuntimeException("Failing"); 39 | } 40 | }, ec); 41 | } 42 | 43 | public int getInt(final int test){ 44 | return test; 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/LiveSubscriptionProjectionTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import no.ks.eventstore2.Eventstore2TestKit; 6 | import no.ks.eventstore2.eventstore.CompleteSubscriptionRegistered; 7 | import no.ks.eventstore2.eventstore.LiveSubscription; 8 | import no.ks.eventstore2.formProcessorProject.FormParsed; 9 | import org.junit.Test; 10 | 11 | public class LiveSubscriptionProjectionTest extends Eventstore2TestKit{ 12 | 13 | 14 | @Test 15 | public void testProjectionDoesNotGetOldEventsAfterSubscription() throws Exception { 16 | final ActorRef projection = _system.actorOf(Props.create(TestLiveSubscriptionProjection.class, super.testActor())); 17 | 18 | expectMsgClass(LiveSubscription.class); 19 | } 20 | 21 | @Test 22 | public void testProjectionGetsNewEvents() throws Exception { 23 | final ActorRef projection = _system.actorOf(Props.create(TestLiveSubscriptionProjection.class, super.testActor())); 24 | 25 | expectMsgClass(LiveSubscription.class); 26 | projection.tell(new CompleteSubscriptionRegistered("liveTest"), super.testActor()); 27 | 28 | projection.tell(new FormParsed("fromid"), super.testActor()); 29 | expectMsg("OK"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/MongoDbEventstore2TestKit.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import com.mongodb.MongoClient; 4 | import com.mongodb.ServerAddress; 5 | import com.mongodb.client.MongoIterable; 6 | import de.flapdoodle.embed.mongo.Command; 7 | import de.flapdoodle.embed.mongo.MongodExecutable; 8 | import de.flapdoodle.embed.mongo.MongodProcess; 9 | import de.flapdoodle.embed.mongo.MongodStarter; 10 | import de.flapdoodle.embed.mongo.config.*; 11 | import de.flapdoodle.embed.mongo.distribution.Version; 12 | import de.flapdoodle.embed.process.config.IRuntimeConfig; 13 | import events.test.Order.Order; 14 | import events.test.form.Form; 15 | import no.ks.eventstore2.Eventstore2TestKit; 16 | import no.ks.eventstore2.ProtobufHelper; 17 | import org.junit.After; 18 | import org.junit.AfterClass; 19 | import org.junit.Before; 20 | import org.junit.BeforeClass; 21 | 22 | import java.util.function.Consumer; 23 | 24 | public class MongoDbEventstore2TestKit extends Eventstore2TestKit { 25 | 26 | protected MongoClient mongoClient; 27 | private static MongodExecutable mongodExecutable = null; 28 | private static MongodProcess mongod = null; 29 | private static IMongodConfig mongodConfig; 30 | static MongodStarter runtime; 31 | 32 | @BeforeClass 33 | public static void setUpMongoDb() throws Exception { 34 | Command command = Command.MongoD; 35 | 36 | IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder() 37 | .defaults(command) 38 | .build(); 39 | runtime = MongodStarter.getInstance(runtimeConfig); 40 | 41 | mongodConfig = new MongodConfigBuilder().version(Version.Main.V3_2).cmdOptions(new MongoCmdOptionsBuilder() 42 | .useNoPrealloc(false) 43 | .useSmallFiles(true) 44 | .useNoJournal(false) 45 | .enableTextSearch(true) 46 | .build()).build(); 47 | mongodExecutable = runtime.prepare(mongodConfig); 48 | mongod = mongodExecutable.start(); 49 | } 50 | 51 | @AfterClass 52 | public static void tearDownMongoDb() throws Exception { 53 | mongod.stop(); 54 | mongodExecutable.stop(); 55 | } 56 | 57 | @Before 58 | public void setUp() throws Exception { 59 | mongoClient = new MongoClient(new ServerAddress(mongodConfig.net().getServerAddress(), mongodConfig.net().getPort())); 60 | super.setUp(); 61 | } 62 | 63 | @After 64 | public void tearDown() throws Exception { 65 | final MongoIterable strings = mongoClient.listDatabaseNames(); 66 | strings.forEach((Consumer) (db) -> mongoClient.getDatabase(db).drop()); 67 | mongoClient.close(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionErrorListenerTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.Props; 4 | import akka.testkit.TestActorRef; 5 | import no.ks.eventstore2.testkit.EventStoreTestKit; 6 | import no.ks.eventstore2.util.IdUtil; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import java.util.List; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertNotNull; 14 | 15 | public class ProjectionErrorListenerTest extends EventStoreTestKit { 16 | 17 | 18 | private TestActorRef projectionErrorListener; 19 | 20 | @Before 21 | public void before() { 22 | projectionErrorListener = TestActorRef.create(actorSystem, Props.create(ProjectionErrorListener.class), IdUtil.createUUID()); 23 | } 24 | 25 | @Test 26 | public void testAskErrorsReturnsEmptyList() { 27 | List projectionErrors = ProjectionErrorListener.askErrors(projectionErrorListener); 28 | assertNotNull(projectionErrors); 29 | assertEquals(0, projectionErrors.size()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionManagerTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.ActorSelection; 5 | import akka.actor.ActorSystem; 6 | import akka.actor.Props; 7 | import akka.testkit.TestActorRef; 8 | import akka.testkit.TestKit; 9 | import com.typesafe.config.ConfigFactory; 10 | import no.ks.eventstore2.eventstore.CompleteSubscriptionRegistered; 11 | import no.ks.eventstore2.formProcessorProject.FormStatuses; 12 | import org.junit.Test; 13 | import scala.concurrent.Await; 14 | import scala.concurrent.Future; 15 | import scala.concurrent.duration.Duration; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import static akka.pattern.Patterns.ask; 21 | import static junit.framework.Assert.assertNotNull; 22 | import static junit.framework.Assert.assertTrue; 23 | import static no.ks.eventstore2.projection.CallProjection.call; 24 | 25 | 26 | public class ProjectionManagerTest extends TestKit { 27 | 28 | static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory 29 | .load().getConfig("TestSys")); 30 | 31 | public ProjectionManagerTest() { 32 | super(_system); 33 | } 34 | 35 | @Test 36 | public void testProjectionManagerCreatesProjections() throws Exception { 37 | 38 | List factories = new ArrayList<>(); 39 | factories.add(Props.create(FormStatuses.class, super.testActor())); 40 | 41 | final TestActorRef ref = TestActorRef.create(_system, ProjectionManager.mkProps(super.testActor(), factories), "projectionManager"); 42 | 43 | Future getProjectionref = ask(ref, call("getProjectionRef", FormStatuses.class), 3000); 44 | 45 | ActorRef projectionRef = (ActorRef) Await.result(getProjectionref, Duration.create("3 seconds")); 46 | 47 | assertNotNull(projectionRef); 48 | } 49 | 50 | @Test 51 | public void testProjectionsInSubscribePhase () throws Exception { 52 | 53 | List factories = new ArrayList<>(); 54 | factories.add(Props.create(FormStatuses.class, super.testActor())); 55 | 56 | final TestActorRef actorRef = TestActorRef.create(_system, ProjectionManager.mkProps(super.testActor(), factories), "projectionManager2"); 57 | 58 | completeSubscription(actorRef); 59 | 60 | Future getProjectionref = ask(actorRef, call("isAnyoneInSubscribePhase", FormStatuses.class), 3000 ); 61 | Boolean isInSubscribePhase = (Boolean) Await.result(getProjectionref, Duration.create("3 seconds")); 62 | 63 | assertTrue(!isInSubscribePhase.booleanValue()); 64 | 65 | } 66 | 67 | private void completeSubscription(TestActorRef actorRef) { 68 | ActorSelection sel = _system.actorSelection("/user/projectionManager2/FormStatuses"); 69 | sel.tell(new CompleteSubscriptionRegistered("agg"), actorRef); 70 | final Future getNumberOfForms = ask(sel, call("getNumberOfForms"), 3000); 71 | try { 72 | Await.result(getNumberOfForms, Duration.create("3 seconds")); 73 | } catch (Exception e) { 74 | throw new RuntimeException(e); 75 | } 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionProtobufSubscriptionTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.Actor; 4 | import akka.actor.ActorRef; 5 | import akka.actor.Props; 6 | import akka.testkit.TestActorRef; 7 | import events.test.Order.Order; 8 | import eventstore.Messages; 9 | import no.ks.eventstore2.Event; 10 | import no.ks.eventstore2.Handler; 11 | import no.ks.eventstore2.ProtobufHelper; 12 | import org.junit.Test; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.UUID; 17 | import java.util.concurrent.atomic.AtomicBoolean; 18 | 19 | import static org.junit.Assert.assertTrue; 20 | 21 | public class ProjectionProtobufSubscriptionTest extends MongoDbEventstore2TestKit{ 22 | 23 | @Test 24 | public void test_that_a_projection_can_save_and_load_snapshot() throws Exception { 25 | 26 | TestActorRef testActor = TestActorRef.create(_system, Props.create(TestProjection.class, super.testActor()), UUID.randomUUID().toString()); 27 | expectMsgClass(Messages.AsyncSubscription.class); 28 | testActor.tell(ProtobufHelper.newEventWrapper("TestAggregate", "id", 1, Order.SearchRequest.getDefaultInstance()),super.testActor()); 29 | testActor.tell(Messages.IncompleteSubscriptionPleaseSendNew.newBuilder().setAggregateType("TestAggregate").build(),super.testActor()); 30 | expectMsgClass(Messages.AsyncSubscription.class); 31 | testActor.tell(Messages.CompleteAsyncSubscriptionPleaseSendSyncSubscription.newBuilder().setAggregateType("TestAggregate").build(),super.testActor()); 32 | expectMsgClass(Messages.Subscription.class); 33 | testActor.tell(Messages.CompleteSubscriptionRegistered.newBuilder().setAggregateType("TestAggregate").build(),super.testActor()); 34 | } 35 | 36 | @Test 37 | public void test_that_a_projection_invokes_on_finished_when_subscribe_is_finished() throws InterruptedException { 38 | TestActorRef testActor = TestActorRef.create(_system, Props.create(TestProjection.class, super.testActor()), UUID.randomUUID().toString()); 39 | testActor.tell(Messages.CompleteSubscriptionRegistered.newBuilder().setAggregateType(UUID.randomUUID().toString()).build(), super.testActor()); 40 | assertTrue(((TestProjection) testActor.underlyingActor()).invoked); 41 | } 42 | 43 | @Subscriber("TestAggregate") 44 | private static class TestProjection extends ProjectionProtobuf { 45 | 46 | public boolean testEventRecieved = false; 47 | private Map data = new HashMap(); 48 | boolean invoked = false; 49 | 50 | public TestProjection(ActorRef eventStore) { 51 | super(eventStore); 52 | } 53 | 54 | @Override 55 | protected void onSubscribeFinished(){ 56 | invoked = true; 57 | } 58 | 59 | @Handler 60 | public void handleEvent(TestEvent event){ 61 | testEventRecieved = true; 62 | data.put("1",event); 63 | } 64 | 65 | } 66 | private static class TestEvent extends Event { 67 | private static final long serialVersionUID = 1L; 68 | 69 | TestEvent() { 70 | setJournalid("000000001"); 71 | } 72 | 73 | @Override 74 | public String getLogMessage() { 75 | return null; 76 | } 77 | 78 | @Override 79 | public String getAggregateRootId() { 80 | return null; 81 | } 82 | 83 | @Override 84 | public String getAggregateType() { 85 | return "TestAggregate"; 86 | } 87 | } 88 | 89 | @Test 90 | public void name() throws Exception { 91 | final Order.SearchRequest defaultInstance = Order.SearchRequest.newBuilder().setQuery("Query er lang").build(); 92 | 93 | System.out.println("PRINT:" + ProtobufHelper.toLog(ProtobufHelper.newEventWrapper("TestAggregate", "id", 1, defaultInstance))); 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionProtobufTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.ActorSystem; 5 | import akka.actor.Props; 6 | import akka.testkit.TestActorRef; 7 | import akka.testkit.TestKit; 8 | import com.typesafe.config.ConfigFactory; 9 | import no.ks.eventstore2.formProcessorProject.FormStatuses; 10 | import org.junit.Test; 11 | import scala.concurrent.Await; 12 | import scala.concurrent.Future; 13 | import scala.concurrent.duration.Duration; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import static akka.pattern.Patterns.ask; 19 | import static junit.framework.Assert.assertNotNull; 20 | import static no.ks.eventstore2.projection.CallProjection.call; 21 | 22 | public class ProjectionProtobufTest extends TestKit { 23 | 24 | static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory 25 | .load().getConfig("TestSys")); 26 | 27 | public ProjectionProtobufTest() { 28 | super(_system); 29 | } 30 | 31 | @Test 32 | public void testProjectionManagerCreatesProjections() throws Exception { 33 | 34 | List factories = new ArrayList<>(); 35 | factories.add(Props.create(FormStatuses.class, super.testActor())); 36 | 37 | final TestActorRef ref = TestActorRef.create(_system, ProjectionManager.mkProps(super.testActor(), factories), "projectionProtobufManager"); 38 | 39 | Future getProjectionref = ask(ref, call("getProjectionRef", FormStatuses.class), 3000); 40 | 41 | ActorRef projectionRef = (ActorRef) Await.result(getProjectionref, Duration.create("3 seconds")); 42 | 43 | assertNotNull(projectionRef); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionReceiveEventTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.Props; 4 | import akka.testkit.TestActorRef; 5 | import no.ks.eventstore2.eventstore.Subscription; 6 | import no.ks.eventstore2.testapplication.TestEvent; 7 | import no.ks.eventstore2.testapplication.TestProjection; 8 | import no.ks.eventstore2.testkit.EventStoreTestKit; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | import static org.junit.Assert.assertTrue; 13 | 14 | public class ProjectionReceiveEventTest extends EventStoreTestKit { 15 | 16 | private TestActorRef projection; 17 | 18 | @Before 19 | public void before() { 20 | projection = createProjectionRef(Props.create(TestProjection.class, testActor())); 21 | } 22 | 23 | @Test 24 | public void test_that_a_projection_handles_events_using_annotated_methods() throws Exception { 25 | projection.tell(new TestEvent(), testActor()); 26 | assertTrue(TestProjection.askIsEventReceived(projection)); 27 | } 28 | 29 | @Test 30 | public void test_that_a_projection_subscribes_to_the_aggregates_of_its_event_handlers() throws Exception { 31 | expectMsg(new Subscription("TestAggregate")); 32 | } 33 | 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionSnapshotTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | 4 | import akka.actor.Actor; 5 | import akka.actor.ActorRef; 6 | import akka.actor.Props; 7 | import akka.testkit.TestActorRef; 8 | import com.esotericsoftware.kryo.Kryo; 9 | import com.esotericsoftware.kryo.io.Input; 10 | import com.esotericsoftware.kryo.io.Output; 11 | import com.esotericsoftware.kryo.serializers.MapSerializer; 12 | import com.mongodb.MongoClient; 13 | import no.ks.eventstore2.Event; 14 | import no.ks.eventstore2.Handler; 15 | import no.ks.eventstore2.TakeSnapshot; 16 | import no.ks.eventstore2.eventstore.Subscription; 17 | import org.junit.Test; 18 | 19 | import java.io.ByteArrayOutputStream; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.UUID; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertTrue; 26 | 27 | public class ProjectionSnapshotTest extends MongoDbEventstore2TestKit { 28 | 29 | private static Kryo kryo = new Kryo(); 30 | 31 | 32 | @Test 33 | public void test_that_a_projection_can_save_and_load_snapshot() throws Exception { 34 | 35 | TestActorRef testActor = TestActorRef.create(_system, Props.create(TestProjection.class, super.testActor(), mongoClient), UUID.randomUUID().toString()); 36 | expectMsgClass(Subscription.class); 37 | testActor.tell(new TestEvent(), super.testActor()); 38 | testActor.tell(new TakeSnapshot(), super.testActor()); 39 | 40 | TestProjection testProjection = (TestProjection) testActor.underlyingActor(); 41 | 42 | assertTrue(testProjection.testEventRecieved); 43 | assertTrue(testProjection.data.size() == 1); 44 | 45 | TestActorRef testActorReader = TestActorRef.create(_system, Props.create(TestProjection.class, super.testActor(), mongoClient), UUID.randomUUID().toString()); 46 | expectMsg(new Subscription("TestAggregate","000000001")); 47 | 48 | TestProjection testProjectionRead = (TestProjection) testActorReader.underlyingActor(); 49 | assertTrue(testProjectionRead.data.size() == 1); 50 | 51 | Event event = testProjection.data.get("1"); 52 | assertEquals("TestAggregate", event.getAggregateType()); 53 | assertEquals("000000001", event.getJournalid()); 54 | } 55 | 56 | 57 | 58 | @Subscriber("TestAggregate") 59 | private static class TestProjection extends MongoDbProjection { 60 | 61 | public boolean testEventRecieved = false; 62 | private Map data = new HashMap(); 63 | 64 | public TestProjection(ActorRef eventStore, MongoClient client) throws Exception { 65 | super(eventStore, client); 66 | MapSerializer serializer = new MapSerializer(); 67 | kryo.register(HashMap.class, serializer); 68 | kryo.register(HashMap.class, 10); 69 | kryo.register(TestEvent.class, 20); 70 | } 71 | 72 | @Override 73 | protected byte[] serializeData() { 74 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 75 | Output output = new Output(outputStream); 76 | kryo.writeClassAndObject(output, data); 77 | output.close(); 78 | return outputStream.toByteArray(); 79 | } 80 | 81 | @Override 82 | protected String getSnapshotDataVersion() { 83 | return "2"; 84 | } 85 | 86 | @Override 87 | protected void deSerializeData(byte[] bytes) { 88 | Input input = new Input(bytes); 89 | data = (Map) kryo.readClassAndObject(input); 90 | } 91 | 92 | @Handler 93 | public void handleEvent(TestEvent event){ 94 | testEventRecieved = true; 95 | data.put("1",event); 96 | } 97 | 98 | } 99 | private static class TestEvent extends Event { 100 | private static final long serialVersionUID = 1L; 101 | 102 | TestEvent() { 103 | setJournalid("000000001"); 104 | } 105 | 106 | @Override 107 | public String getLogMessage() { 108 | return null; 109 | } 110 | 111 | @Override 112 | public String getAggregateRootId() { 113 | return null; 114 | } 115 | 116 | @Override 117 | public String getAggregateType() { 118 | return "TestAggregate"; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/ProjectionSubscriptionTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.Actor; 4 | import akka.actor.ActorRef; 5 | import akka.actor.Props; 6 | import akka.testkit.TestActorRef; 7 | import no.ks.eventstore2.Event; 8 | import no.ks.eventstore2.Handler; 9 | import no.ks.eventstore2.eventstore.*; 10 | import org.junit.Test; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.UUID; 15 | 16 | public class ProjectionSubscriptionTest extends MongoDbEventstore2TestKit{ 17 | 18 | @Test 19 | public void test_that_a_projection_can_save_and_load_snapshot() throws Exception { 20 | 21 | TestActorRef testActor = TestActorRef.create(_system, Props.create(TestProjection.class, super.testActor()), UUID.randomUUID().toString()); 22 | expectMsgClass(AsyncSubscription.class); 23 | testActor.tell(new TestEvent(),super.testActor()); 24 | testActor.tell(new IncompleteSubscriptionPleaseSendNew("TestAggregate"),super.testActor()); 25 | expectMsgClass(AsyncSubscription.class); 26 | testActor.tell(new CompleteAsyncSubscriptionPleaseSendSyncSubscription("TestAggregate"),super.testActor()); 27 | expectMsgClass(Subscription.class); 28 | testActor.tell(new CompleteSubscriptionRegistered("TestAggregate"),super.testActor()); 29 | } 30 | 31 | 32 | 33 | @Subscriber("TestAggregate") 34 | private static class TestProjection extends Projection { 35 | 36 | public boolean testEventRecieved = false; 37 | private Map data = new HashMap(); 38 | 39 | public TestProjection(ActorRef eventStore) { 40 | super(eventStore); 41 | } 42 | 43 | @Handler 44 | public void handleEvent(TestEvent event){ 45 | testEventRecieved = true; 46 | data.put("1",event); 47 | } 48 | 49 | } 50 | private static class TestEvent extends Event { 51 | private static final long serialVersionUID = 1L; 52 | 53 | TestEvent() { 54 | setJournalid("000000001"); 55 | } 56 | 57 | @Override 58 | public String getLogMessage() { 59 | return null; 60 | } 61 | 62 | @Override 63 | public String getAggregateRootId() { 64 | return null; 65 | } 66 | 67 | @Override 68 | public String getAggregateType() { 69 | return "TestAggregate"; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/RestartProjectionProtobufTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import events.test.Order.Order; 7 | import eventstore.Messages; 8 | import no.ks.eventstore2.Handler; 9 | import no.ks.eventstore2.ProtobufHelper; 10 | import no.ks.eventstore2.testapplication.AggregateType; 11 | import no.ks.eventstore2.testapplication.TestEvent; 12 | import no.ks.eventstore2.testkit.EventStoreTestKit; 13 | import no.ks.eventstore2.util.IdUtil; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import scala.concurrent.Await; 17 | import scala.concurrent.duration.Duration; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static akka.pattern.Patterns.ask; 24 | import static no.ks.eventstore2.projection.CallProjection.call; 25 | import static org.junit.Assert.assertEquals; 26 | 27 | public class RestartProjectionProtobufTest extends EventStoreTestKit { 28 | 29 | private TestActorRef projection; 30 | 31 | @Before 32 | public void before() { 33 | 34 | TestActorRef actorRef = TestActorRef.create(actorSystem, Props.create(TestProjection.class, super.testActor()), IdUtil.createUUID()); 35 | 36 | projection = actorRef; 37 | } 38 | 39 | @Test 40 | public void test_restart() throws Exception { 41 | expectMsgClass(Messages.AsyncSubscription.class); 42 | projection.tell(createEvent(0), super.testActor()); 43 | projection.tell(createEvent(1), super.testActor()); 44 | projection.tell("restart", super.testActor()); 45 | projection.tell(createEvent(2), super.testActor()); 46 | projection.tell(createEvent(3), super.testActor()); 47 | expectMsgClass(Messages.RemoveSubscription.class); 48 | projection.tell(createEvent(4), super.testActor()); 49 | projection.tell(Messages.SubscriptionRemoved.newBuilder().setAggregateType(AggregateType.TEST_AGGREGATE).build(), super.testActor()); 50 | expectMsgClass(Messages.AsyncSubscription.class); 51 | projection.tell(createEvent(0), super.testActor()); 52 | projection.tell(createEvent(1), super.testActor()); 53 | projection.tell(createEvent(2), super.testActor()); 54 | projection.tell(createEvent(3), super.testActor()); 55 | projection.tell(Messages.CompleteSubscriptionRegistered.newBuilder().setAggregateType(AggregateType.TEST_AGGREGATE).build(),super.testActor()); 56 | final List events = (List)Await.result(ask(projection, call("getEvents"), 3000), Duration.create(3, TimeUnit.SECONDS)); 57 | assertEquals(4, events.size()); 58 | } 59 | 60 | private Messages.EventWrapper createEvent(long jid) { 61 | return ProtobufHelper.newEventWrapper("TestAggregate", "id", jid, Order.SearchResult.getDefaultInstance()).toBuilder().setVersion(jid).build(); 62 | } 63 | @Subscriber("TestAggregate") 64 | private static class TestProjection extends ProjectionProtobuf { 65 | 66 | public boolean testEventRecieved = false; 67 | private Messages.EventWrapper data = null; 68 | List events = new ArrayList<>(); 69 | 70 | public TestProjection(ActorRef eventStore) { 71 | super(eventStore); 72 | } 73 | 74 | 75 | @Handler 76 | public void handleEvent(Order.SearchResult event){ 77 | testEventRecieved = true; 78 | data = currentMessage(); 79 | events.add(currentMessage()); 80 | } 81 | 82 | public List getEvents(){ 83 | return events; 84 | } 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/RestartProjectionTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.Props; 4 | import akka.testkit.TestActorRef; 5 | import no.ks.eventstore2.eventstore.AsyncSubscription; 6 | import no.ks.eventstore2.eventstore.CompleteSubscriptionRegistered; 7 | import no.ks.eventstore2.eventstore.RemoveSubscription; 8 | import no.ks.eventstore2.eventstore.SubscriptionRemoved; 9 | import no.ks.eventstore2.testapplication.AggregateType; 10 | import no.ks.eventstore2.testapplication.TestEvent; 11 | import no.ks.eventstore2.testapplication.TestProjection; 12 | import no.ks.eventstore2.testkit.EventStoreTestKit; 13 | import org.joda.time.DateTime; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import scala.concurrent.Await; 17 | import scala.concurrent.duration.Duration; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import static akka.pattern.Patterns.ask; 23 | 24 | import static no.ks.eventstore2.projection.CallProjection.call; 25 | import static org.junit.Assert.assertEquals; 26 | 27 | public class RestartProjectionTest extends EventStoreTestKit { 28 | 29 | private TestActorRef projection; 30 | 31 | @Before 32 | public void before() { 33 | projection = createProjectionRef(Props.create(TestProjection.class, testActor())); 34 | } 35 | 36 | @Test 37 | public void test_restart() throws Exception { 38 | expectMsgClass(AsyncSubscription.class); 39 | projection.tell(createEvent(0), super.testActor()); 40 | projection.tell(createEvent(1), super.testActor()); 41 | projection.tell("restart", super.testActor()); 42 | projection.tell(createEvent(2), super.testActor()); 43 | projection.tell(createEvent(3), super.testActor()); 44 | expectMsgClass(RemoveSubscription.class); 45 | projection.tell(createEvent(4), super.testActor()); 46 | projection.tell(new SubscriptionRemoved(AggregateType.TEST_AGGREGATE), super.testActor()); 47 | expectMsgClass(AsyncSubscription.class); 48 | projection.tell(createEvent(0), super.testActor()); 49 | projection.tell(createEvent(1), super.testActor()); 50 | projection.tell(createEvent(2), super.testActor()); 51 | projection.tell(createEvent(3), super.testActor()); 52 | projection.tell(new CompleteSubscriptionRegistered(AggregateType.TEST_AGGREGATE),super.testActor()); 53 | final List events = (List)Await.result(ask(projection, call("getEvents"), 3000), Duration.create(3, TimeUnit.SECONDS)); 54 | assertEquals(4, events.size()); 55 | } 56 | 57 | private TestEvent createEvent(long jid) { 58 | final TestEvent event = new TestEvent(); 59 | event.setCreated(DateTime.now()); 60 | event.setJournalid(String.valueOf(jid)); 61 | return event; 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/projection/TestLiveSubscriptionProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.projection; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.Handler; 5 | import no.ks.eventstore2.formProcessorProject.FormParsed; 6 | 7 | @Subscriber("liveTest") 8 | public class TestLiveSubscriptionProjection extends LiveSubscriptionProjection { 9 | 10 | public TestLiveSubscriptionProjection(ActorRef eventStore) { 11 | super(eventStore); 12 | } 13 | 14 | @Handler 15 | public void handleEvent(FormParsed event) { 16 | sender().tell("OK", self()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaDatasourceRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import no.ks.eventstore2.EmbeddedDatabaseTest; 4 | import org.junit.Test; 5 | 6 | import static junit.framework.Assert.assertEquals; 7 | 8 | public class SagaDatasourceRepositoryTest extends EmbeddedDatabaseTest{ 9 | 10 | @Test 11 | public void testSaveAndRetrieveState() throws Exception { 12 | SagaDatasourceRepository repo = new SagaDatasourceRepository(db); 13 | repo.saveState("FormProcess", "id", (byte) 45); 14 | assertEquals(45,repo.getState("FormProcess", "id")); 15 | } 16 | 17 | @Test 18 | public void testTwoSaves() throws Exception { 19 | SagaDatasourceRepository repo = new SagaDatasourceRepository(db); 20 | repo.saveState("FormProcess", "30d91ec9-f7de-4c56-850a-1b9e4ed92e85", (byte) 45); 21 | repo = new SagaDatasourceRepository(db); 22 | repo.saveState("FormProcess", "30d91ec9-f7de-4c56-850a-1b9e4ed92e85", (byte) 45); 23 | repo.saveState("FormProcess", "30d91ec9-f7de-4c56-850a-1b9e4ed92e85", (byte) 45); 24 | assertEquals(45,repo.getState("FormProcess", "30d91ec9-f7de-4c56-850a-1b9e4ed92e85")); 25 | 26 | 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaManagerNewAnnotationStyleTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import eventstore.Messages; 7 | import no.ks.eventstore2.Event; 8 | import no.ks.eventstore2.Eventstore2TestKit; 9 | import no.ks.eventstore2.Handler; 10 | import no.ks.eventstore2.eventstore.Subscription; 11 | import no.ks.eventstore2.projection.Subscriber; 12 | import org.junit.Test; 13 | 14 | import java.util.UUID; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | 18 | public class SagaManagerNewAnnotationStyleTest extends Eventstore2TestKit{ 19 | 20 | 21 | @Test 22 | public void test_that_methods_with_eventHandler_annotations_are_called_when_saga_receives_events() throws Exception { 23 | SagaInMemoryRepository sagaInMemoryRepository = new SagaInMemoryRepository(); 24 | Props sagaManagerProps = Props.create(SagaManager.class, super.testActor(), sagaInMemoryRepository, super.testActor(), "no"); 25 | TestActorRef sagaManager = TestActorRef.create(_system, sagaManagerProps, UUID.randomUUID().toString()); 26 | 27 | sagaManager.tell(new TestEvent("a test id"), super.testActor()); 28 | //TODO: make sagaManagerFactory accept package path for scanning as a parmeter, and then fix this test so that the manager only scans this path 29 | expectMsgClass(Messages.Subscription.class); 30 | expectMsgClass(Messages.Subscription.class); 31 | expectMsgClass(TestEvent.class); 32 | assertEquals(Saga.STATE_FINISHED, sagaInMemoryRepository.getState("SagaWithNewAnotation", "a test id")); 33 | } 34 | 35 | @SagaEventIdProperty("testId") 36 | @Subscriber("TestAggregate") 37 | private static class SagaWithNewAnotation extends Saga { 38 | 39 | public SagaWithNewAnotation(String id, ActorRef commandDispatcher, SagaRepository repository) { 40 | super(id, commandDispatcher, repository); 41 | } 42 | 43 | @Override 44 | protected String getSagaStateId() { 45 | return "SagaWithNewAnotation"; 46 | } 47 | 48 | @Handler 49 | public void handleEvent(TestEvent event){ 50 | transitionState(STATE_FINISHED); 51 | commandDispatcher.tell(new TestEvent("We have been here"),self()); 52 | } 53 | } 54 | 55 | private static class TestEvent extends Event{ 56 | private String testId; 57 | 58 | private TestEvent(String testId) { 59 | this.testId = testId; 60 | } 61 | 62 | @Override 63 | public String getLogMessage() { 64 | return null; 65 | } 66 | 67 | @Override 68 | public String getAggregateRootId() { 69 | return null; 70 | } 71 | 72 | public String getTestId(){ 73 | return testId; 74 | } 75 | 76 | @Override 77 | public String getAggregateType() { 78 | return "TestAggregate"; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaManagerPolymorphismEventTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import eventstore.Messages; 7 | import no.ks.eventstore2.Event; 8 | import no.ks.eventstore2.Eventstore2TestKit; 9 | import no.ks.eventstore2.Handler; 10 | import no.ks.eventstore2.eventstore.Subscription; 11 | import no.ks.eventstore2.projection.Subscriber; 12 | import no.ks.eventstore2.response.Success; 13 | import org.junit.Test; 14 | 15 | import java.util.UUID; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | 19 | public class SagaManagerPolymorphismEventTest extends Eventstore2TestKit{ 20 | 21 | 22 | @Test 23 | public void test_that_methods_with_eventHandler_annotations_are_called_when_saga_receives_events() throws Exception { 24 | SagaInMemoryRepository sagaInMemoryRepository = new SagaInMemoryRepository(); 25 | Props sagaManagerProps = Props.create(SagaManager.class, super.testActor(), sagaInMemoryRepository, super.testActor(), "no"); 26 | TestActorRef sagaManager = TestActorRef.create(_system, sagaManagerProps, UUID.randomUUID().toString()); 27 | 28 | sagaManager.tell(new SubEvent("a test id"), super.testActor()); 29 | expectMsgClass(Messages.Subscription.class); 30 | expectMsgClass(Messages.Subscription.class); 31 | expectMsgClass(Success.class); 32 | assertEquals(Saga.STATE_FINISHED, sagaInMemoryRepository.getState("SagaHandlingASuperclassOfEvent", "a test id")); 33 | } 34 | 35 | @SagaEventIdProperty("testId") 36 | @Subscriber("TestAggregate") 37 | private static class SagaHandlingASuperclassOfEvent extends Saga { 38 | 39 | public SagaHandlingASuperclassOfEvent(String id, ActorRef commandDispatcher, SagaRepository repository) { 40 | super(id, commandDispatcher, repository); 41 | } 42 | 43 | @Override 44 | protected String getSagaStateId() { 45 | return "SagaHandlingASuperclassOfEvent"; 46 | } 47 | 48 | @Handler 49 | public void handleEvent(SuperEvent event){ 50 | transitionState(STATE_FINISHED); 51 | commandDispatcher.tell(new Success(), self()); 52 | } 53 | } 54 | 55 | public abstract static class SuperEvent extends Event { 56 | private static final long serialVersionUID = 1L; 57 | 58 | private String testId; 59 | 60 | SuperEvent(String testId) { 61 | this.testId = testId; 62 | } 63 | 64 | public String getTestId() { 65 | return testId; 66 | } 67 | 68 | public void setTestId(String testId) { 69 | this.testId = testId; 70 | } 71 | 72 | @Override 73 | public String getAggregateType() { 74 | return "TestAggregate"; 75 | } 76 | } 77 | 78 | public static class SubEvent extends SuperEvent { 79 | private static final long serialVersionUID = 1L; 80 | 81 | SubEvent(String testId) { 82 | super(testId); 83 | } 84 | 85 | @Override 86 | public String getLogMessage() { 87 | return null; 88 | } 89 | 90 | @Override 91 | public String getAggregateRootId() { 92 | return null; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaMongoDBRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import com.mongodb.DB; 4 | import com.mongodb.client.MongoDatabase; 5 | import no.ks.eventstore2.formProcessorProject.FormProcess; 6 | import no.ks.eventstore2.projection.MongoDbEventstore2TestKit; 7 | import org.joda.time.DateTime; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import static junit.framework.Assert.assertEquals; 12 | 13 | public class SagaMongoDBRepositoryTest extends MongoDbEventstore2TestKit { 14 | 15 | private SagaMongoDBRepository repo; 16 | private MongoDatabase db; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | super.setUp(); 21 | db = mongoClient.getDatabase("SagaStore"); 22 | repo = new SagaMongoDBRepository(db); 23 | } 24 | 25 | @Test 26 | public void testSaveAndGetState() throws Exception { 27 | repo.saveState("FormProcess", "id2", (byte) 45); 28 | assertEquals(45, repo.getState("FormProcess", "id2")); 29 | } 30 | 31 | @Test 32 | public void testStateUpdateGenerateOneDocument() throws Exception { 33 | db = mongoClient.getDatabase("SagaStore2"); 34 | repo = new SagaMongoDBRepository(db); 35 | repo.saveState("FormProcess", "id5", (byte) 45); 36 | repo.saveState("FormProcess", "id5", (byte) 45); 37 | assertEquals(1, db.getCollection("states").count()); 38 | } 39 | 40 | @Test 41 | public void testTwoSaves() throws Exception { 42 | repo.saveState("FormProcess", "b30d91ec9-f7de-4c56-850a-1b9e4ed92e85", (byte) 45); 43 | repo.saveState("FormProcess", "a30d91ec9-f7de-4c56-850a-1b9e4ed92e85", (byte) 45); 44 | repo.saveState("FormProcess", "30d91ec9-f7de-4c56-850a-1b9e4ed92e85", (byte) 47); 45 | assertEquals(47,repo.getState("FormProcess", "30d91ec9-f7de-4c56-850a-1b9e4ed92e85")); 46 | } 47 | 48 | @Test 49 | public void testNullValueInGetState() throws Exception { 50 | assertEquals((byte)0, repo.getState("FormProcess", "NotValidSagaID")); 51 | } 52 | 53 | @Test 54 | public void testSaveLatestJournalId() throws Exception { 55 | repo.saveLatestJournalId("agg",1l); 56 | assertEquals(1, repo.loadLatestJournalID("agg")); 57 | } 58 | 59 | @Test 60 | public void saveAwake() { 61 | repo.storeScheduleAwake("id", FormProcess.class.getName(), DateTime.now().minusMinutes(1)); 62 | assertEquals(1,repo.whoNeedsToWake().size()); 63 | repo.clearAwake("id", FormProcess.class.getName()); 64 | assertEquals(0,repo.whoNeedsToWake().size()); 65 | } 66 | 67 | @Test 68 | public void awakeInFutureDosentTrigger() { 69 | repo.storeScheduleAwake("id", FormProcess.class.getName(), DateTime.now().plusMinutes(5)); 70 | assertEquals(0,repo.whoNeedsToWake().size()); 71 | repo.clearAwake("id", FormProcess.class.getName()); 72 | assertEquals(0,repo.whoNeedsToWake().size()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaNewAnnotationStyleTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.Props; 5 | import akka.testkit.TestActorRef; 6 | import no.ks.eventstore2.Event; 7 | import no.ks.eventstore2.Eventstore2TestKit; 8 | import no.ks.eventstore2.Handler; 9 | import no.ks.eventstore2.projection.Subscriber; 10 | import org.junit.Test; 11 | 12 | import java.util.UUID; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | 16 | public class SagaNewAnnotationStyleTest extends Eventstore2TestKit{ 17 | 18 | 19 | @Test 20 | public void test_that_methods_with_eventHandler_annotations_are_called_when_saga_receives_events() throws Exception { 21 | Props props = Props.create(SagaWithNewAnotation.class, "a test id", super.testActor(), new SagaInMemoryRepository()); 22 | TestActorRef testActor = TestActorRef.create(_system, props, UUID.randomUUID().toString()); 23 | 24 | testActor.tell(new TestEvent("a test id"), super.testActor()); 25 | assertEquals(Saga.STATE_FINISHED, testActor.underlyingActor().getState()); 26 | } 27 | 28 | @SagaEventIdProperty("testId") 29 | @Subscriber("TestAggregate") 30 | static class SagaWithNewAnotation extends Saga { 31 | 32 | public SagaWithNewAnotation(String id, ActorRef commandDispatcher, SagaRepository repository) { 33 | super(id, commandDispatcher, repository); 34 | } 35 | 36 | @Override 37 | protected String getSagaStateId() { 38 | return "SagaWithNewAnotation"; 39 | } 40 | 41 | @Handler 42 | public void handleEvent(TestEvent event){ 43 | transitionState(STATE_FINISHED); 44 | } 45 | } 46 | 47 | static class TestEvent extends Event{ 48 | private static final long serialVersionUID = 1L; 49 | 50 | private String testId; 51 | 52 | private TestEvent(String testId) { 53 | this.testId = testId; 54 | } 55 | 56 | @Override 57 | public String getLogMessage() { 58 | return null; 59 | } 60 | 61 | @Override 62 | public String getAggregateRootId() { 63 | return null; 64 | } 65 | 66 | public String getTestId(){ 67 | return testId; 68 | } 69 | 70 | @Override 71 | public String getAggregateType() { 72 | return "TestAggregate"; 73 | } 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.ActorSystem; 5 | import akka.actor.Props; 6 | import akka.testkit.TestActorRef; 7 | import akka.testkit.TestKit; 8 | import com.typesafe.config.ConfigFactory; 9 | import no.ks.eventstore2.formProcessorProject.FormProcess; 10 | import no.ks.eventstore2.formProcessorProject.FormReceived; 11 | import no.ks.eventstore2.formProcessorProject.ParseForm; 12 | import org.junit.Test; 13 | 14 | import static junit.framework.Assert.assertEquals; 15 | 16 | public class SagaTest extends TestKit { 17 | 18 | 19 | static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory 20 | .load().getConfig("TestSys")); 21 | private SagaInMemoryRepository sagaInMemoryRepository; 22 | 23 | public SagaTest() { 24 | super(_system); 25 | sagaInMemoryRepository = new SagaInMemoryRepository(); 26 | } 27 | 28 | @Test 29 | public void sagaChangesStateOnMessageRecieved() { 30 | final Props props = getNotificationSagaProps(); 31 | final TestActorRef ref = TestActorRef.create(_system, props, "not_saga_a"); 32 | final FormProcess saga = ref.underlyingActor(); 33 | ref.tell(new FormReceived("1"), null); 34 | assertEquals(2, saga.getState()); 35 | } 36 | 37 | @Test 38 | public void testSagaDispatchesCommandOnMessageRecieved() throws Exception { 39 | final Props props = getNotificationSagaProps(); 40 | final TestActorRef ref = TestActorRef.create(_system, props, "not_saga_b"); 41 | ref.tell(new FormReceived("1"), super.testActor()); 42 | expectMsgClass(ParseForm.class); 43 | } 44 | 45 | @Test 46 | public void testSagaPersistsState() throws Exception { 47 | final Props props = getNotificationSagaProps(); 48 | final TestActorRef ref = TestActorRef.create(_system, props, "not_saga_c"); 49 | ref.tell(new FormReceived("1"), super.testActor()); 50 | assertEquals(2, sagaInMemoryRepository.getState("FormProcess", "123")); 51 | } 52 | 53 | @Test 54 | public void testSagaRestoresStateFromRepository() throws Exception { 55 | final Props props = getNotificationSagaProps("123123"); 56 | sagaInMemoryRepository.saveState("FormProcess", "123123", (byte) 5); 57 | final TestActorRef ref = TestActorRef.create(_system, props, "not_saga_d"); 58 | final FormProcess saga = ref.underlyingActor(); 59 | assertEquals(5, saga.getState()); 60 | } 61 | 62 | private Props getNotificationSagaProps(final String sagaId) { 63 | final ActorRef commandDispatcher = super.testActor(); 64 | return Props.create(FormProcess.class,sagaId, commandDispatcher, sagaInMemoryRepository); 65 | } 66 | 67 | private Props getNotificationSagaProps() { 68 | return getNotificationSagaProps("123"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/SagaTimerUt.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.Handler; 5 | import no.ks.eventstore2.formProcessorProject.FormParsed; 6 | import no.ks.eventstore2.formProcessorProject.FormReceived; 7 | import no.ks.eventstore2.projection.Subscriber; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | @SagaEventIdProperty(useAggregateRootId = true) 12 | @Subscriber("FORM") 13 | public class SagaTimerUt extends TimeOutSaga{ 14 | 15 | public SagaTimerUt(String id, ActorRef commandDispatcher, SagaRepository repository) { 16 | super(id, commandDispatcher, repository); 17 | } 18 | 19 | @Handler 20 | public void handleEvent(FormParsed event){ 21 | scheduleAwake(0, TimeUnit.SECONDS); 22 | } 23 | 24 | @Handler 25 | public void handleEvent(FormReceived event){ 26 | scheduleAwake(120, TimeUnit.SECONDS); 27 | } 28 | 29 | @Override 30 | protected void awake() { 31 | System.out.println("awoke"); 32 | transitionState(STATE_FINISHED); 33 | } 34 | 35 | @Override 36 | protected String getSagaStateId() { 37 | return "idsagatimerut"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/saga/TiemoutSagaTest.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.saga; 2 | 3 | import akka.actor.ActorRef; 4 | import akka.actor.ActorSystem; 5 | import akka.actor.Inbox; 6 | import akka.actor.Props; 7 | import akka.testkit.TestActorRef; 8 | import akka.testkit.TestKit; 9 | import com.typesafe.config.ConfigFactory; 10 | import eventstore.Messages; 11 | import no.ks.eventstore2.formProcessorProject.FormParsed; 12 | import no.ks.eventstore2.formProcessorProject.FormReceived; 13 | import org.joda.time.DateTime; 14 | import org.junit.Test; 15 | import scala.concurrent.duration.Duration; 16 | 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | import static junit.framework.Assert.assertEquals; 21 | import static junit.framework.TestCase.assertTrue; 22 | 23 | 24 | public class TiemoutSagaTest extends TestKit{ 25 | 26 | static ActorSystem _system = ActorSystem.create("TestSys", ConfigFactory 27 | .load().getConfig("TestSys")); 28 | private SagaInMemoryRepository sagaInMemoryRepository; 29 | 30 | public TiemoutSagaTest() { 31 | super(_system); 32 | sagaInMemoryRepository = new SagaInMemoryRepository(); 33 | } 34 | 35 | @Test 36 | public void schedulesAwake() { 37 | final ActorRef commandDispatcher = super.testActor(); 38 | final Props sagaprop = Props.create(SagaTimerUt.class, "sagaid", commandDispatcher, sagaInMemoryRepository); 39 | 40 | final TestActorRef ref = TestActorRef.create(_system, sagaprop, super.testActor(), "sagatimerut"); 41 | final SagaTimerUt saga = ref.underlyingActor(); 42 | ref.tell(new FormParsed("1"), super.testActor()); 43 | expectMsgClass(Messages.ScheduleAwake.class); 44 | ref.tell("awake", super.testActor()); 45 | assertEquals(Saga.STATE_FINISHED, saga.getState()); 46 | } 47 | 48 | @Test 49 | public void schedulesAwakeInTheFuture() throws TimeoutException { 50 | final ActorRef commandDispatcher = super.testActor(); 51 | final Props sagaprop = Props.create(SagaTimerUt.class, "sagaid", commandDispatcher, sagaInMemoryRepository); 52 | 53 | 54 | final TestActorRef ref = TestActorRef.create(_system, sagaprop, super.testActor(), "sagatimerut"); 55 | final SagaTimerUt saga = ref.underlyingActor(); 56 | ref.tell(new FormReceived("1"), super.testActor()); 57 | 58 | final Messages.ScheduleAwake receive = (Messages.ScheduleAwake) receiveOne(Duration.create(3, TimeUnit.SECONDS)); 59 | 60 | assertTrue("Correct timeout value", receive.getAwake().getSeconds() - DateTime.now().plusSeconds(120).toDate().getTime()/1000 < 5); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/testapplication/AggregateType.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.testapplication; 2 | 3 | public final class AggregateType { 4 | 5 | public static final String TEST_AGGREGATE = "TestAggregate"; 6 | } 7 | -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/testapplication/TestEvent.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.testapplication; 2 | 3 | import no.ks.eventstore2.Event; 4 | 5 | public class TestEvent extends Event { 6 | 7 | @Override 8 | public String getLogMessage() { 9 | return "This is a test event"; 10 | } 11 | 12 | @Override 13 | public String getAggregateRootId() { 14 | return null; 15 | } 16 | 17 | @Override 18 | public String getAggregateType() { 19 | return AggregateType.TEST_AGGREGATE; 20 | } 21 | } -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/testapplication/TestProjection.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.testapplication; 2 | 3 | import akka.actor.ActorRef; 4 | import no.ks.eventstore2.Handler; 5 | import no.ks.eventstore2.ask.Asker; 6 | import no.ks.eventstore2.projection.Projection; 7 | import no.ks.eventstore2.projection.Subscriber; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @Subscriber(AggregateType.TEST_AGGREGATE) 15 | public class TestProjection extends Projection { 16 | 17 | private static Logger log = LoggerFactory.getLogger(TestProjection.class); 18 | 19 | private boolean isEventReceived = false; 20 | private List events = new ArrayList<>(); 21 | 22 | public TestProjection(ActorRef eventStore) { 23 | super(eventStore); 24 | } 25 | 26 | @Handler 27 | public void handleEvent(TestEvent event){ 28 | isEventReceived = true; 29 | events.add(event); 30 | } 31 | 32 | public boolean isEventReceived() { 33 | return isEventReceived; 34 | } 35 | 36 | public List getEvents() { 37 | return events; 38 | } 39 | 40 | public static boolean askIsEventReceived(ActorRef projection) { 41 | try { 42 | return Asker.askProjection(projection, "isEventReceived").single(Boolean.class); 43 | } catch (Exception e) { 44 | throw new RuntimeException("Error when asking projection " + e); 45 | } 46 | } 47 | 48 | public static List askEvents(ActorRef projection) { 49 | try { 50 | return Asker.askProjection(projection, "getEvents").list(TestEvent.class); 51 | } catch (Exception e) { 52 | throw new RuntimeException("Error when asking projection " + e); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/testkit/EventReceiver.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.testkit; 2 | 3 | import akka.actor.UntypedActor; 4 | import no.ks.eventstore2.Event; 5 | import no.ks.eventstore2.eventstore.AcknowledgePreviousEventsProcessed; 6 | import no.ks.eventstore2.response.Success; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class EventReceiver extends UntypedActor { 12 | public List receivedEvents = new ArrayList(); 13 | 14 | @Override 15 | public void onReceive(Object o) throws Exception { 16 | if (o instanceof AcknowledgePreviousEventsProcessed) { 17 | sender().tell(new Success(), self()); 18 | } else if (o instanceof Event) { 19 | receivedEvents.add((Event) o); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /eventstore/src/test/java/no/ks/eventstore2/testkit/EventStoreTestKit.java: -------------------------------------------------------------------------------- 1 | package no.ks.eventstore2.testkit; 2 | 3 | import akka.actor.Actor; 4 | import akka.actor.ActorSystem; 5 | import akka.actor.Props; 6 | import akka.testkit.TestActorRef; 7 | import akka.testkit.TestKit; 8 | import events.test.Order.Order; 9 | import events.test.form.Form; 10 | import no.ks.eventstore2.ProtobufHelper; 11 | import no.ks.eventstore2.eventstore.CompleteSubscriptionRegistered; 12 | import no.ks.eventstore2.saga.Saga; 13 | import no.ks.eventstore2.saga.SagaInMemoryRepository; 14 | import no.ks.eventstore2.util.IdUtil; 15 | import org.junit.Before; 16 | 17 | public class EventStoreTestKit extends TestKit { 18 | 19 | protected static final ActorSystem actorSystem = ActorSystem.create("testSystem"); 20 | 21 | public EventStoreTestKit() { 22 | super(actorSystem); 23 | } 24 | 25 | protected TestActorRef createCommandHandler(Props props) { 26 | return TestActorRef.create(actorSystem, props, IdUtil.createUUID()); 27 | } 28 | 29 | protected TestActorRef createProjectionRef(Props props) { 30 | TestActorRef actorRef = TestActorRef.create(actorSystem, props, IdUtil.createUUID()); 31 | actorRef.tell(new CompleteSubscriptionRegistered(null), testActor()); 32 | return actorRef; 33 | } 34 | 35 | protected T createSaga(Class clz) { 36 | Props sagaProps = Props.create(clz, IdUtil.createUUID(), testActor(), new SagaInMemoryRepository()); 37 | return (T) TestActorRef.create(actorSystem, sagaProps, IdUtil.createUUID()).underlyingActor(); 38 | } 39 | 40 | protected EventReceiver createEventReceiver() { 41 | return (EventReceiver) TestActorRef.create(actorSystem, Props.create(EventReceiver.class), IdUtil.createUUID()).underlyingActor(); 42 | } 43 | 44 | @Before 45 | public void setUp() throws Exception { 46 | ProtobufHelper.registerDeserializeMethod(Order.SearchRequest.getDefaultInstance()); 47 | ProtobufHelper.registerDeserializeMethod(Order.SearchResult.getDefaultInstance()); 48 | ProtobufHelper.registerDeserializeMethod(Form.FormReceived.getDefaultInstance()); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /eventstore/src/test/protobuf/Order.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | //option java_package = "events.test"; 4 | package events.test.Order; 5 | 6 | message SearchRequest { 7 | string query = 1; 8 | int32 page_number = 2; 9 | int32 result_per_page = 3; 10 | } 11 | 12 | message SearchResult { 13 | repeated string result = 1; 14 | int32 number_of_results = 2; 15 | } 16 | -------------------------------------------------------------------------------- /eventstore/src/test/protobuf/form.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | //option java_package = "events.test"; 4 | package events.test.form; 5 | 6 | message FormReceived { 7 | } 8 | 9 | message SearchResult { 10 | repeated string result = 1; 11 | int32 number_of_results = 2; 12 | } 13 | -------------------------------------------------------------------------------- /eventstore/src/test/resources/application.conf: -------------------------------------------------------------------------------- 1 | TestSys { 2 | akka { 3 | mode = test 4 | loglevel = DEBUG 5 | actor { 6 | debug { 7 | receive = on 8 | autoreceive = on 9 | lifecycle = on 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /eventstore/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE SEQUENCE seq; 2 | 3 | create table Event( 4 | id number primary key, 5 | aggregatetype varchar(255), 6 | class varchar(255), 7 | kryoeventdata BLOB, 8 | dataversion integer default 0 9 | ); 10 | 11 | create table Saga( 12 | id varchar(255), 13 | clazz varchar(255), 14 | state tinyint, 15 | PRIMARY KEY(id, clazz) 16 | ); 17 | -------------------------------------------------------------------------------- /eventstore/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | no.ks 6 | eventstore2-parent 7 | 3.2.1-SNAPSHOT 8 | 9 | pom 10 | 11 | Eventstore2 Parent 12 | A framework for building event sourced Java applications 13 | 14 | https://github.com/ks-no/eventstore2 15 | 16 | 17 | eventstore 18 | 19 | 20 | 21 | 22 | MIT License 23 | http://www.opensource.org/licenses/mit-license.php 24 | 25 | 26 | 27 | 28 | https://github.com/ks-no/eventstore2 29 | scm:git:https://github.com/ks-no/eventstore2.git 30 | scm:git:https://github.com/ks-no/eventstore2.git 31 | HEAD 32 | 33 | 34 | 35 | github 36 | https://github.com/ks-no/eventstore2/issues 37 | 38 | 39 | 40 | 41 | false 42 | ks-nexus 43 | KS Repository 44 | http://ksjenkins1-u.usrv.ubergenkom.no:8082/repository/ks-nexus/ 45 | 46 | 47 | true 48 | ks-nexus-snapshot 49 | KS Snapshots 50 | http://ksjenkins1-u.usrv.ubergenkom.no:8082/repository/ks-nexus-snapshot/ 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-enforcer-plugin 59 | 3.0.0-M1 60 | 61 | 62 | default-cli 63 | 64 | 65 | 66 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | validate-snap 75 | 76 | 77 | 78 | false 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.scijava 87 | scijava-maven-plugin 88 | 1.0.0 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------