├── .gitignore ├── LICENSE ├── README.md ├── build-tools ├── pom.xml └── src │ └── main │ └── resources │ └── build-tools │ ├── LICENSE.txt │ ├── checkstyle.xml │ └── ruleset.xml ├── core ├── nb-configuration.xml ├── nbactions.xml ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── offbynull │ │ └── actors │ │ ├── ActorSystem.java │ │ ├── address │ │ ├── Address.java │ │ └── package-info.java │ │ ├── common │ │ ├── BestEffortSerializer.java │ │ └── package-info.java │ │ ├── gateway │ │ ├── CommonAddresses.java │ │ ├── Gateway.java │ │ └── package-info.java │ │ ├── gateways │ │ ├── actor │ │ │ ├── Actor.java │ │ │ ├── ActorGateway.java │ │ │ ├── ActorRunnable.java │ │ │ ├── ActorShuttle.java │ │ │ ├── Context.java │ │ │ ├── FailListener.java │ │ │ ├── RuleSet.java │ │ │ ├── SerializableActor.java │ │ │ ├── Store.java │ │ │ ├── package-info.java │ │ │ └── stores │ │ │ │ └── memory │ │ │ │ ├── MemoryStore.java │ │ │ │ └── package-info.java │ │ ├── direct │ │ │ ├── DirectGateway.java │ │ │ ├── DirectShuttle.java │ │ │ └── package-info.java │ │ ├── log │ │ │ ├── LogGateway.java │ │ │ ├── LogMessage.java │ │ │ ├── LogRunnable.java │ │ │ └── package-info.java │ │ ├── servlet │ │ │ ├── AddressServlet.java │ │ │ ├── JsonConverter.java │ │ │ ├── MessageBridgeServlet.java │ │ │ ├── RequestBlock.java │ │ │ ├── ResponseBlock.java │ │ │ ├── ServletGateway.java │ │ │ ├── ServletShuttle.java │ │ │ ├── Store.java │ │ │ ├── package-info.java │ │ │ └── stores │ │ │ │ └── memory │ │ │ │ ├── MemoryStore.java │ │ │ │ └── package-info.java │ │ ├── threadpool │ │ │ ├── ThreadPoolGateway.java │ │ │ ├── ThreadPoolProcessor.java │ │ │ ├── ThreadPoolShuttle.java │ │ │ └── package-info.java │ │ └── timer │ │ │ ├── AddShuttle.java │ │ │ ├── RemoveShuttle.java │ │ │ ├── TimerGateway.java │ │ │ ├── TimerRunnable.java │ │ │ └── package-info.java │ │ ├── package-info.java │ │ ├── shuttle │ │ ├── Message.java │ │ ├── Shuttle.java │ │ └── package-info.java │ │ └── shuttles │ │ ├── blackhole │ │ ├── BlackholeShuttle.java │ │ └── package-info.java │ │ ├── pump │ │ ├── PumpShuttle.java │ │ ├── PumpShuttleController.java │ │ └── package-info.java │ │ └── simple │ │ ├── Bus.java │ │ ├── SimpleShuttle.java │ │ └── package-info.java │ └── test │ └── java │ └── com │ └── offbynull │ └── actors │ ├── ActorSystemTest.java │ ├── address │ └── AddressTest.java │ ├── gateways │ ├── actor │ │ ├── ActorChildSpawnTest.java │ │ ├── ActorFilterTest.java │ │ ├── ActorNeighbourSpawnTest.java │ │ ├── ActorShortcircuitTest.java │ │ ├── ContextTest.java │ │ ├── RuleSetTest.java │ │ ├── SerializableActorHelper.java │ │ └── stores │ │ │ └── memory │ │ │ ├── BestEffortSerializerTest.java │ │ │ └── MemoryStoreTest.java │ ├── direct │ │ └── DirectGatewayTest.java │ ├── log │ │ └── LogGatewayTest.java │ ├── servlet │ │ ├── JsonConverterTest.java │ │ ├── ServletGatewayTest.java │ │ └── stores │ │ │ └── memory │ │ │ └── MemoryStoreTest.java │ ├── threadpool │ │ └── ThreadPoolGatewayTest.java │ └── timer │ │ └── TimerGatewayTest.java │ └── shuttles │ ├── blackhole │ └── BlackholeShuttleTest.java │ ├── pump │ └── PumpShuttleTest.java │ └── simple │ └── SimpleShuttleTest.java ├── jdbc-storage ├── nb-configuration.xml ├── nbactions.xml ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── offbynull │ │ └── actors │ │ ├── gateways │ │ ├── actor │ │ │ └── stores │ │ │ │ └── jdbc │ │ │ │ ├── JdbcStore.java │ │ │ │ └── package-info.java │ │ └── servlet │ │ │ └── stores │ │ │ └── jdbc │ │ │ ├── InQueue.java │ │ │ ├── JdbcStore.java │ │ │ ├── OutQueue.java │ │ │ └── package-info.java │ │ └── jdbcclient │ │ ├── JdbcUtils.java │ │ └── package-info.java │ └── test │ └── java │ └── com │ └── offbynull │ └── actors │ ├── ActorSystemTest.java │ └── gateways │ ├── actor │ ├── SerializableActorHelper.java │ └── stores │ │ └── jdbc │ │ └── JdbcStoreTest.java │ └── servlet │ └── stores │ └── jdbc │ └── JdbcStoreTest.java ├── logo.png ├── logo.svg ├── nbactions.xml ├── pom.xml ├── redis-storage ├── nb-configuration.xml ├── nbactions.xml ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── offbynull │ │ └── actors │ │ ├── gateways │ │ ├── actor │ │ │ └── stores │ │ │ │ └── redis │ │ │ │ ├── ActorAccessor.java │ │ │ │ ├── ConversionUtils.java │ │ │ │ ├── QueueCountController.java │ │ │ │ ├── RedisStore.java │ │ │ │ ├── TimestampQueue.java │ │ │ │ └── package-info.java │ │ └── servlet │ │ │ └── stores │ │ │ └── redis │ │ │ ├── ConversionUtils.java │ │ │ ├── InQueue.java │ │ │ ├── OutQueue.java │ │ │ ├── QueueDetails.java │ │ │ ├── RedisStore.java │ │ │ └── package-info.java │ │ ├── redisclient │ │ ├── Connection.java │ │ ├── ConnectionException.java │ │ ├── Connector.java │ │ ├── RedisUtils.java │ │ ├── SortedSetItem.java │ │ ├── Transaction.java │ │ ├── TransactionBlock.java │ │ ├── TransactionQueue.java │ │ ├── TransactionResult.java │ │ ├── Watch.java │ │ ├── WatchBlock.java │ │ └── package-info.java │ │ └── redisclients │ │ ├── jedis │ │ ├── JedisConnection.java │ │ ├── JedisConnector.java │ │ ├── JedisPoolConnector.java │ │ └── package-info.java │ │ └── test │ │ ├── InternalList.java │ │ ├── InternalSortedSet.java │ │ ├── TestConnection.java │ │ ├── TestConnector.java │ │ └── package-info.java │ └── test │ └── java │ └── com │ └── offbynull │ └── actors │ ├── ActorSystemTest.java │ └── gateways │ ├── actor │ ├── SerializableActorHelper.java │ └── stores │ │ └── redis │ │ ├── ActorAccessorTest.java │ │ ├── RedisStoreTest.java │ │ └── TimestampQueueTest.java │ └── servlet │ └── stores │ └── redis │ └── RedisStoreTest.java └── servlet-gateway └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /eventframework/target/ 2 | /chord/target/ 3 | /communication-helper/target/ 4 | /eventframework-core/target/ 5 | /eventframework-network/target/ 6 | /eventframework/eventframework-basic/target/ 7 | /eventframework/eventframework-core/target/ 8 | /eventframework/eventframework/target/ 9 | /p2p-tools/target/ 10 | /bin-tools/target/ 11 | /basic-chord/target/ 12 | /p2p-rpc/target/ 13 | /rpc/target/ 14 | /rpc-common/target/ 15 | /target/ 16 | /overlay-unstructured/target/ 17 | /overlay-visualizer/target/ 18 | /demo/target/ 19 | /build-tools/target/ 20 | /visualizer/target/ 21 | /overlay-gui-visualizer/target/ 22 | /overlay-common/target/ 23 | /overlay-chord/target/ 24 | /rpc-transport/target/ 25 | /rpc-invoke/target/ 26 | /common/target/ 27 | /actor/target/ 28 | /actor-network/target/ 29 | /nat/target/ 30 | /router-natpmp/target/ 31 | /portmapper/target/ 32 | /netty-simulation/target/ 33 | /netty-p2p/target/ 34 | /network/target/ 35 | /netty-helper/target/ 36 | /network-tools/target/ 37 | /netty-extensions/target/ 38 | /core/target/ 39 | /debug/target/ 40 | /playground/unstructured-mesh/target/ 41 | /playground/unstructured-mesh/target2/ 42 | /playground/unstructured-mesh/target2222/ 43 | /playground/unstructured-mesh/target123123123/ 44 | /playground/unstructured-mesh/targetaaaaaaa/ 45 | /playground/unstructured-mesh/targetssssssssssssss/ 46 | /playground/chord-dht/target/ 47 | /unstructured-mesh/target/ 48 | /examples/target/ 49 | /io/target/ 50 | /core/nbproject/ 51 | /redis-storage/target/ 52 | /redis-storage/nbproject/ 53 | /jdbc-storage/target/ 54 | /jdbc-storage/derby.log 55 | /servlet-gateway/target/ 56 | /servlet-gateway/nbproject/ 57 | /redis-storage/nbproject/ 58 | /redis-storage/target/ 59 | /jdbc-storage/target/ 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Actors (pre-alpha) 2 | 3 |

Actors logo

4 | 5 | The Actors project is a lightweight Java actor framework that greatly simplifies the design and development of horizontally scalable software. Why use Actors over other actor frameworks? 6 | 7 | * Actors as lightweight fibers/coroutines 8 | * Seamless distribution of actors 9 | * Seamless checkpointing of actors 10 | * Seamless versioning of actors 11 | 12 | What does all this mean? *Distribution* means your actors will seamlessly execute across a cluster of servers. You can add and remove servers at will, and server crashes won't effect the execution of your actors. *Checkpointing* makes sure that if your actors enter into a bad state, they will automatically revert to the last known good state (as specified by you) and be notified of the problem. *Versioning* allows you to change your actor's logic on the fly, without breaking compatibility with older versions of that actor. Fix bugs and add features without stopping your service. You can even have your servers running different versions of the same actor. 13 | 14 | The Actors project gives you everything you need to write highly resilient/elastic/scalable systems, but without all the headaches. 15 | 16 | ## Example 17 | 18 | This is a simple "Hello World" style example. It doesn't show many of the core features that make the Actors project what it is, but more in-depth material and screencasts are in the works. 19 | 20 | First, create your actor. The Actors project uses the [Coroutines](https://github.com/offbynull/coroutines) project, so you'll need to use the coroutines plugin to instrument your actors. 21 | 22 | ```java 23 | public final class SimpleActor implements Coroutine { 24 | @Override 25 | public void run(Continuation cnt) throws Exception { 26 | Context ctx = (Context) cnt.getContext(); 27 | Object msg; 28 | 29 | // Display the priming message that's initially sent to this actor 30 | msg = ctx.in(); 31 | ctx.logInfo("Priming message: {}", msg); 32 | 33 | // Allow any address to communicate with this actor 34 | ctx.allow(); 35 | 36 | while (true) { 37 | // Add a checkpoint -- if no activity in 60 seconds send a message notifying as such. 38 | ctx.checkpoint("ACTOR HASN'T BEEN HIT IN OVER 60 SECONDS -- CHECKPOINT TRIGGERED", 60 * 1000L); 39 | // Add a 1000L timer to echo a message back after 1 second. 40 | ctx.timer(1000L, "timer!"); 41 | 42 | // Wait for next message 43 | cnt.suspend(); 44 | 45 | // Print message 46 | msg = ctx.in(); 47 | ctx.logInfo("Got message: {}", msg); 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | Next, set up a server to run instances of your actor. You can spawn as many instances of the server as you want. 54 | 55 | ```java 56 | public final class SimpleRedisStoreTest { 57 | private static final int WORKER_COUNT = 1000; 58 | private static final int THREAD_COUNT = 100; 59 | private static final int REDIS_POOL_COUNT = THREAD_COUNT; 60 | private static final int REDIS_QUEUE_COUNT = 10; 61 | 62 | public static void main(String[] args) throws Exception { 63 | // Start actor system backed by Redis 64 | RedisStore store = RedisStore.create( 65 | "actor", 66 | "192.168.56.101", 67 | 6379, 68 | REDIS_POOL_COUNT, 69 | REDIS_QUEUE_COUNT); 70 | 71 | ActorSystem actorSystem = ActorSystem.builder() 72 | .withLogGateway() 73 | .withActorGateway(THREAD_COUNT, store) 74 | .build(); 75 | 76 | // Add 1000 new actors into the system 77 | ActorGateway actorGateway = actorSystem.getActorGateway(); 78 | for (int i = 0; i < WORKER_COUNT; i++) { 79 | String name = InetAddress.getLocalHost().getHostName() + "_" + i; 80 | Coroutine coroutine = new SimpleActor(); 81 | 82 | actorGateway.addActor(name, coroutine, "start"); 83 | } 84 | 85 | // Block 86 | actorSystem.join(); 87 | } 88 | } 89 | ``` -------------------------------------------------------------------------------- /build-tools/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.offbynull.actors 7 | parent 8 | 1.0.0-SNAPSHOT 9 | 10 | com.offbynull.actors 11 | build-tools 12 | ${project.groupId}:${project.artifactId} 13 | 14 | UTF-8 15 | 16 | 17 | -------------------------------------------------------------------------------- /build-tools/src/main/resources/build-tools/LICENSE.txt: -------------------------------------------------------------------------------- 1 | \Q/*\E 2 | \Q * Copyright (c) \E\d{4}\Q, Kasra Faghihi, All rights reserved.\E 3 | \Q * \E 4 | \Q * This library is free software; you can redistribute it and/or\E 5 | \Q * modify it under the terms of the GNU Lesser General Public\E 6 | \Q * License as published by the Free Software Foundation; either\E 7 | \Q * version 3.0 of the License, or (at your option) any later version.\E 8 | \Q * \E 9 | \Q * This library is distributed in the hope that it will be useful,\E 10 | \Q * but WITHOUT ANY WARRANTY; without even the implied warranty of\E 11 | \Q * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\E 12 | \Q * Lesser General Public License for more details.\E 13 | \Q * \E 14 | \Q * You should have received a copy of the GNU Lesser General Public\E 15 | \Q * License along with this library.\E 16 | \Q */\E -------------------------------------------------------------------------------- /build-tools/src/main/resources/build-tools/ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core/nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | none 17 | 18 | 19 | -------------------------------------------------------------------------------- /core/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test 5 | 6 | * 7 | 8 | 9 | test 10 | coroutines:test-instrument 11 | 12 | 13 | 14 | test.single 15 | 16 | * 17 | 18 | 19 | test-compile 20 | coroutines:test-instrument 21 | surefire:test 22 | 23 | 24 | ${packageClassName} 25 | 26 | 27 | 28 | debug.test.single 29 | 30 | * 31 | 32 | 33 | test-compile 34 | coroutines:test-instrument 35 | surefire:test 36 | 37 | 38 | ${packageClassName} 39 | once 40 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} 41 | true 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.offbynull.actors 6 | parent 7 | 1.0.0-SNAPSHOT 8 | 9 | core 10 | jar 11 | 12 | ${project.groupId}:${project.artifactId} 13 | 14 | 15 | 16 | com.offbynull.coroutines 17 | user 18 | 19 | 20 | org.apache.commons 21 | commons-collections4 22 | 23 | 24 | org.apache.commons 25 | commons-lang3 26 | 27 | 28 | org.slf4j 29 | slf4j-api 30 | 31 | 32 | commons-io 33 | commons-io 34 | 35 | 36 | commons-codec 37 | commons-codec 38 | 39 | 40 | org.objenesis 41 | objenesis 42 | 43 | 44 | com.google.code.gson 45 | gson 46 | 47 | 48 | javax.servlet 49 | javax.servlet-api 50 | 51 | 52 | junit 53 | junit 54 | test 55 | 56 | 57 | org.mockito 58 | mockito-all 59 | test 60 | 61 | 62 | org.slf4j 63 | slf4j-simple 64 | test 65 | 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-checkstyle-plugin 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-pmd-plugin 76 | 77 | 78 | com.github.spotbugs 79 | spotbugs-maven-plugin 80 | 81 | 82 | com.offbynull.coroutines 83 | maven-plugin 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/address/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Provides classes and interfaces for defining addresses. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.address; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/common/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Common utility and helper classes. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.common; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateway/CommonAddresses.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateway; 18 | 19 | import com.offbynull.actors.address.Address; 20 | 21 | /** 22 | * Common gateway addresses. 23 | * @author Kasra Faghihi 24 | */ 25 | public final class CommonAddresses { 26 | /** 27 | * Default address to actor runner as String. 28 | */ 29 | public static final String DEFAULT_ACTOR = "actor"; 30 | 31 | /** 32 | * Default address to actor runner. 33 | */ 34 | public static final Address DEFAULT_RUNNER_ADDRESS = Address.of(DEFAULT_ACTOR); 35 | 36 | /** 37 | * Default address to direct gateway as String. 38 | */ 39 | public static final String DEFAULT_DIRECT = "direct"; 40 | 41 | /** 42 | * Default address to direct gateway. 43 | */ 44 | public static final Address DEFAULT_DIRECT_ADDRESS = Address.of(DEFAULT_DIRECT); 45 | 46 | /** 47 | * Default address to log gateway as String. 48 | */ 49 | public static final String DEFAULT_LOG = "log"; 50 | 51 | /** 52 | * Default address to log gateway. 53 | */ 54 | public static final Address DEFAULT_LOG_ADDRESS = Address.of(DEFAULT_LOG); 55 | 56 | /** 57 | * Default address to timer gateway as String. 58 | */ 59 | public static final String DEFAULT_TIMER = "timer"; 60 | 61 | /** 62 | * Default address to timer gateway. 63 | */ 64 | public static final Address DEFAULT_TIMER_ADDRESS = Address.of(DEFAULT_TIMER); 65 | 66 | /** 67 | * Default address to servlet gateway as String. 68 | */ 69 | public static final String DEFAULT_SERVLET = "servlet"; 70 | 71 | /** 72 | * Default address to servlet gateway. 73 | */ 74 | public static final Address DEFAULT_SERVLET_ADDRESS = Address.of(DEFAULT_SERVLET); 75 | 76 | private CommonAddresses() { 77 | // do nothing 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateway/Gateway.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateway; 18 | 19 | import com.offbynull.actors.shuttle.Shuttle; 20 | import java.io.Closeable; 21 | 22 | /** 23 | * A gateway, like an actor, communicates with other components through message-passing, but isn't bound by any of the same rules as 24 | * actors. Gateways are mainly used to interface with third-party components. As such, it's perfectly acceptable for a gateway to expose 25 | * internal state, share state, perform I/O, perform thread synchronization, or otherwise block. 26 | * 27 | * For example, a gateway could ... 28 | * 29 | * 35 | * 36 | * @author Kasra Faghihi 37 | */ 38 | public interface Gateway extends Closeable { 39 | 40 | /** 41 | * Get the shuttle used to receive messages. 42 | * @return shuttle for incoming messages 43 | * @throws IllegalStateException if this gateway is closed 44 | */ 45 | Shuttle getIncomingShuttle(); 46 | 47 | /** 48 | * Add an outgoing shuttle. 49 | * @param shuttle outgoing shuttle to add 50 | * @throws NullPointerException if any argument is {@code null} 51 | * @throws IllegalStateException if this gateway is closed 52 | */ 53 | void addOutgoingShuttle(Shuttle shuttle); 54 | 55 | /** 56 | * Remove an outgoing shuttle. If no shuttle with the prefix {@code shuttlePrefix} was added to this gateway, nothing happens. 57 | * @param shuttlePrefix address prefix for shuttle to remove 58 | * @throws NullPointerException if any argument is {@code null} 59 | * @throws IllegalStateException if this gateway is closed 60 | */ 61 | void removeOutgoingShuttle(String shuttlePrefix); 62 | 63 | /** 64 | * Waits for this gateway to die. 65 | * @throws InterruptedException if thread is interrupted while waiting 66 | */ 67 | void join() throws InterruptedException; 68 | } 69 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateway/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Provides classes and interfaces for defining gateways. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateway; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/actor/Actor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.actor; 18 | 19 | import com.offbynull.coroutines.user.CoroutineRunner; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import org.apache.commons.lang3.Validate; 23 | 24 | final class Actor { 25 | private final Actor parent; 26 | private final Map children; 27 | 28 | private final CoroutineRunner runner; 29 | private final Context context; 30 | 31 | Actor(Actor parent, CoroutineRunner runner, Context context) { 32 | this(parent, runner, new HashMap<>(), context); 33 | } 34 | 35 | Actor(Actor parent, CoroutineRunner runner, Map children, Context context) { 36 | // parent can be null 37 | Validate.notNull(runner); 38 | Validate.notNull(context); 39 | Validate.notNull(children); 40 | Validate.noNullElements(children.keySet()); 41 | Validate.noNullElements(children.values()); 42 | 43 | this.parent = parent; 44 | this.runner = runner; 45 | this.context = context; 46 | this.children = children; 47 | } 48 | 49 | Actor parent() { 50 | return parent; 51 | } 52 | 53 | Map children() { 54 | return children; 55 | } 56 | 57 | CoroutineRunner runner() { 58 | return runner; 59 | } 60 | 61 | Context context() { 62 | return context; 63 | } 64 | 65 | boolean isRoot() { 66 | return parent == null; 67 | } 68 | 69 | Actor getChild(String id) { 70 | Validate.notNull(id); 71 | return children.get(id); 72 | } 73 | 74 | boolean isChild(String id) { 75 | Validate.notNull(id); 76 | return children.containsKey(id); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/actor/ActorShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.actor; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import com.offbynull.actors.shuttle.Message; 21 | import com.offbynull.actors.shuttle.Shuttle; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import java.util.List; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | import org.apache.commons.lang3.Validate; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | final class ActorShuttle implements Shuttle { 31 | private static final Logger LOG = LoggerFactory.getLogger(ActorShuttle.class); 32 | 33 | private final String prefix; 34 | private final Store store; 35 | private final AtomicBoolean shutdownFlag; 36 | 37 | ActorShuttle(String prefix, Store store, AtomicBoolean shutdownFlag) { 38 | Validate.notNull(prefix); 39 | Validate.notNull(store); 40 | Validate.notNull(shutdownFlag); 41 | 42 | this.prefix = prefix; 43 | this.store = store; 44 | this.shutdownFlag = shutdownFlag; 45 | } 46 | 47 | @Override 48 | public String getPrefix() { 49 | return prefix; 50 | } 51 | 52 | @Override 53 | public void send(Collection messages) { 54 | Validate.notNull(messages); 55 | Validate.noNullElements(messages); 56 | 57 | if (shutdownFlag.get()) { 58 | return; 59 | } 60 | 61 | List filteredMessages = new ArrayList<>(messages.size()); 62 | messages.stream().forEach(m -> { 63 | try { 64 | Address dst = m.getDestinationAddress(); 65 | String dstPrefix = dst.getElement(0); 66 | Validate.isTrue(dstPrefix.equals(prefix)); 67 | 68 | filteredMessages.add(m); 69 | } catch (Exception e) { 70 | LOG.error("Error shuttling message: " + m, e); 71 | } 72 | }); 73 | 74 | store.store(filteredMessages); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/actor/FailListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.actor; 18 | 19 | /** 20 | * Listens for shutdowns in {@link ActorGateway} threads. 21 | * @author Kasra Faghihi 22 | */ 23 | public interface FailListener { 24 | /** 25 | * A {@link ActorGateway} thread has encountered a problem and has to shutdown. 26 | * @param t throwable that caused the error 27 | */ 28 | void failed(Throwable t); 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/actor/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Gateway that runs distributed actors. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.actor; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/actor/stores/memory/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * In-memory actor storage engine implementation. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.actor.stores.memory; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/direct/DirectShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.direct; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import com.offbynull.actors.shuttle.Message; 21 | import com.offbynull.actors.shuttle.Shuttle; 22 | import com.offbynull.actors.shuttles.simple.Bus; 23 | import java.util.Collection; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | import org.apache.commons.lang3.Validate; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | final class DirectShuttle implements Shuttle { 30 | 31 | private static final Logger LOG = LoggerFactory.getLogger(DirectShuttle.class); 32 | 33 | private final String prefix; 34 | private final ConcurrentHashMap readQueues; 35 | 36 | DirectShuttle(String prefix, ConcurrentHashMap readQueues) { 37 | Validate.notNull(prefix); 38 | Validate.notNull(readQueues); 39 | // don't null check outShuttle keys/values -- this is a concurrent map that gets modified elsewhere, so we have no control 40 | 41 | this.prefix = prefix; 42 | this.readQueues = readQueues; 43 | } 44 | 45 | 46 | @Override 47 | public String getPrefix() { 48 | return prefix; 49 | } 50 | 51 | @Override 52 | public void send(Collection messages) { 53 | Validate.notNull(messages); 54 | Validate.noNullElements(messages); 55 | 56 | messages.stream().forEach(m -> { 57 | Address dst = m.getDestinationAddress(); 58 | try { 59 | String dstPrefix = dst.getElement(0); 60 | Validate.isTrue(dstPrefix.equals(prefix)); 61 | } catch (Exception e) { 62 | LOG.error("Error shuttling message: " + m, e); 63 | } 64 | 65 | Address next = dst; 66 | while (true) { 67 | Bus queue = readQueues.get(next); 68 | if (queue != null) { 69 | queue.add(m); 70 | } 71 | if (next.size() == 1) { 72 | break; 73 | } 74 | next = next.removeSuffix(1); 75 | } 76 | }); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/direct/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Gateway that allows normal Java code to send messages to and receive messages to gateways. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.direct; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/log/LogGateway.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.log; 18 | 19 | import static com.offbynull.actors.gateway.CommonAddresses.DEFAULT_LOG; 20 | import com.offbynull.actors.gateway.Gateway; 21 | import com.offbynull.actors.shuttle.Shuttle; 22 | import com.offbynull.actors.shuttles.simple.Bus; 23 | import com.offbynull.actors.shuttles.simple.SimpleShuttle; 24 | import java.util.concurrent.atomic.AtomicBoolean; 25 | import org.apache.commons.lang3.Validate; 26 | 27 | /** 28 | * {@link Gateway} that logs by piping messages to SLF4J. Note that this gateway... 29 | *
    30 | *
  1. only consumes messages (doesn't send messages).
  2. 31 | *
  3. only accepts messages of type {@link LogMessage}.
  4. 32 | *
33 | * @author Kasra Faghihi 34 | */ 35 | public final class LogGateway implements Gateway { 36 | 37 | 38 | private final Thread thread; 39 | private final Bus bus; 40 | 41 | private final SimpleShuttle shuttle; 42 | 43 | private final AtomicBoolean shutdownFlag; 44 | 45 | /** 46 | * Create a {@link LogGateway} instance. Equivalent to calling {@code create(CommonAddresses.DEFAULT_LOG)}. 47 | * @return new direct gateway 48 | */ 49 | public static LogGateway create() { 50 | return create(DEFAULT_LOG); 51 | } 52 | 53 | /** 54 | * Create a {@link LogGateway} instance. 55 | * @param prefix address prefix for this gateway 56 | * @return new direct gateway 57 | * @throws NullPointerException if any argument is {@code null} 58 | */ 59 | public static LogGateway create(String prefix) { 60 | LogGateway gateway = new LogGateway(prefix); 61 | gateway.thread.start(); 62 | return gateway; 63 | } 64 | 65 | private LogGateway(String prefix) { 66 | Validate.notNull(prefix); 67 | 68 | bus = new Bus(); 69 | shuttle = new SimpleShuttle(prefix, bus); 70 | shutdownFlag = new AtomicBoolean(false); 71 | thread = new Thread(new LogRunnable(bus, shutdownFlag)); 72 | thread.setDaemon(true); 73 | thread.setName(getClass().getSimpleName() + "-" + prefix); 74 | } 75 | 76 | @Override 77 | public Shuttle getIncomingShuttle() { 78 | if (shutdownFlag.get()) { 79 | throw new IllegalStateException(); 80 | } 81 | return shuttle; 82 | } 83 | 84 | @Override 85 | public void addOutgoingShuttle(Shuttle shuttle) { 86 | if (shutdownFlag.get()) { 87 | throw new IllegalStateException(); 88 | } 89 | Validate.notNull(shuttle); 90 | // does nothing 91 | } 92 | 93 | @Override 94 | public void removeOutgoingShuttle(String shuttlePrefix) { 95 | if (shutdownFlag.get()) { 96 | throw new IllegalStateException(); 97 | } 98 | Validate.notNull(shuttlePrefix); 99 | // does nothing 100 | } 101 | 102 | @Override 103 | public void close() { 104 | shutdownFlag.set(true); 105 | bus.close(); 106 | } 107 | 108 | @Override 109 | public void join() throws InterruptedException { 110 | thread.join(); 111 | } 112 | } -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/log/LogRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.log; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import com.offbynull.actors.shuttle.Message; 21 | import com.offbynull.actors.shuttles.simple.Bus; 22 | import java.util.List; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | import org.apache.commons.lang3.Validate; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | final class LogRunnable implements Runnable { 29 | 30 | private static final Logger LOG = LoggerFactory.getLogger(LogRunnable.class); 31 | 32 | private final Bus bus; 33 | private final AtomicBoolean shutdownFlag; 34 | 35 | LogRunnable(Bus bus, AtomicBoolean shutdownFlag) { 36 | Validate.notNull(bus); 37 | Validate.notNull(shutdownFlag); 38 | this.bus = bus; 39 | this.shutdownFlag = shutdownFlag; 40 | } 41 | 42 | @Override 43 | public void run() { 44 | LOG.debug("Log gateway started"); 45 | try { 46 | while (!shutdownFlag.get()) { 47 | // Poll for new messages 48 | List incomingObjects = bus.pull(); 49 | 50 | Validate.notNull(incomingObjects); // sanity checks 51 | Validate.noNullElements(incomingObjects); 52 | 53 | for (Object incomingObj : incomingObjects) { 54 | if (!(incomingObj instanceof Message)) { 55 | continue; 56 | } 57 | 58 | Message message = (Message) incomingObj; 59 | Address src = message.getSourceAddress(); 60 | Address dst = message.getDestinationAddress(); 61 | Object payload = message.getMessage(); 62 | 63 | if (!(payload instanceof LogMessage)) { 64 | continue; 65 | } 66 | 67 | LogMessage logMsg = (LogMessage) payload; 68 | 69 | String msg = "{} - " + logMsg.getMessage(); 70 | 71 | Object[] origArgs = logMsg.getArguments(); 72 | Object[] args = new Object[origArgs.length + 1]; 73 | System.arraycopy(origArgs, 0, args, 1, origArgs.length); 74 | args[0] = src.toString(); 75 | 76 | switch (logMsg.getType()) { 77 | case TRACE: 78 | LOG.trace(msg, args); 79 | break; 80 | case DEBUG: 81 | LOG.debug(msg, args); 82 | break; 83 | case INFO: 84 | LOG.info(msg, args); 85 | break; 86 | case WARN: 87 | LOG.warn(msg, args); 88 | break; 89 | case ERROR: 90 | LOG.error(msg, args); 91 | break; 92 | default: 93 | throw new IllegalStateException(); // this should never happen 94 | } 95 | } 96 | } 97 | } catch (InterruptedException ie) { 98 | LOG.debug("Log gateway interrupted"); 99 | Thread.interrupted(); 100 | } catch (RuntimeException re) { 101 | LOG.error("Internal error encountered", re); 102 | } finally { 103 | shutdownFlag.set(true); 104 | bus.close(); 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/log/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Gateway that pipes log messages to SLF4J. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.log; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/AddressServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import java.io.IOException; 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | import org.apache.commons.io.IOUtils; 26 | import org.apache.commons.lang3.Validate; 27 | import java.io.Writer; 28 | import java.security.NoSuchAlgorithmException; 29 | import java.security.SecureRandom; 30 | import java.util.concurrent.CountDownLatch; 31 | import java.util.stream.Collectors; 32 | import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 33 | import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | 37 | final class AddressServlet extends HttpServlet { 38 | 39 | private static final Logger LOG = LoggerFactory.getLogger(AddressServlet.class); 40 | 41 | private final String prefix; 42 | private final SecureRandom secureRandom; 43 | private final CountDownLatch shutdownLatch; 44 | 45 | AddressServlet(String prefix, CountDownLatch shutdownLatch) { 46 | Validate.notNull(prefix); 47 | Validate.notNull(shutdownLatch); 48 | 49 | try { 50 | this.secureRandom = SecureRandom.getInstance("SHA1PRNG"); 51 | } catch (NoSuchAlgorithmException nsae) { 52 | throw new IllegalStateException(nsae); // should never happen 53 | } 54 | this.prefix = prefix; 55 | this.shutdownLatch = shutdownLatch; 56 | } 57 | 58 | @Override 59 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 60 | if (shutdownLatch.getCount() == 0L) { 61 | resp.setStatus(SC_SERVICE_UNAVAILABLE); 62 | return; 63 | } 64 | 65 | try { 66 | String id = secureRandom.ints(64, 0x20, 0x80) // printable ascii characters 67 | .mapToObj(r -> String.valueOf((char) r)) 68 | .collect(Collectors.joining()); 69 | Address address = Address.of(prefix, id); 70 | 71 | try (Writer writer = resp.getWriter()) { 72 | IOUtils.write(address.toString(), writer); 73 | } 74 | } catch (RuntimeException | IOException e) { 75 | resp.setStatus(SC_INTERNAL_SERVER_ERROR); 76 | LOG.error("Servlet failed: {}", e); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/MessageBridgeServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import java.io.IOException; 21 | import java.io.Reader; 22 | import javax.servlet.ServletException; 23 | import javax.servlet.http.HttpServlet; 24 | import javax.servlet.http.HttpServletRequest; 25 | import javax.servlet.http.HttpServletResponse; 26 | import org.apache.commons.io.IOUtils; 27 | import org.apache.commons.lang3.Validate; 28 | import com.offbynull.actors.shuttle.Shuttle; 29 | import java.io.Writer; 30 | import java.util.List; 31 | import java.util.concurrent.ConcurrentHashMap; 32 | import java.util.concurrent.CountDownLatch; 33 | import static java.util.stream.Collectors.groupingBy; 34 | import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; 35 | import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | final class MessageBridgeServlet extends HttpServlet { 40 | 41 | private static final Logger LOG = LoggerFactory.getLogger(MessageBridgeServlet.class); 42 | 43 | private final JsonConverter jsonConverter; 44 | 45 | private final String prefix; 46 | private final Store store; 47 | 48 | private final ConcurrentHashMap outShuttles; 49 | private final CountDownLatch shutdownLatch; 50 | 51 | MessageBridgeServlet(String prefix, 52 | Store store, 53 | ConcurrentHashMap outShuttles, 54 | CountDownLatch shutdownLatch) { 55 | Validate.notNull(prefix); 56 | Validate.notNull(store); 57 | Validate.notNull(outShuttles); 58 | Validate.notNull(shutdownLatch); 59 | // DONT CHECK outShuttles FOR NULL keys/values as there's no point -- map is concurrent, being modified by other threads 60 | 61 | this.jsonConverter = new JsonConverter(); 62 | this.prefix = prefix; 63 | this.store = store; 64 | this.outShuttles = outShuttles; 65 | this.shutdownLatch = shutdownLatch; 66 | } 67 | 68 | @Override 69 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 70 | if (shutdownLatch.getCount() == 0L) { 71 | resp.setStatus(SC_SERVICE_UNAVAILABLE); 72 | return; 73 | } 74 | 75 | 76 | try { 77 | // Read and parse response 78 | String requestJson; 79 | try (Reader reader = req.getReader()) { 80 | requestJson = IOUtils.toString(reader); 81 | } 82 | RequestBlock requestBlock = jsonConverter.fromJson(requestJson); 83 | 84 | 85 | 86 | 87 | // Get http client's id 88 | String id = requestBlock.getId(); 89 | 90 | 91 | 92 | 93 | // Check messages from the http client 94 | for (Message message : requestBlock.getInQueue()) { 95 | Validate.isTrue(message.getSourceAddress().size() >= 2); 96 | Validate.isTrue(message.getSourceAddress().getElement(0).equals(prefix)); 97 | Validate.isTrue(message.getSourceAddress().getElement(1).equals(id)); 98 | } 99 | 100 | // Queue (insert) messages from the http client 101 | store.queueIn(id, requestBlock.getInQueueOffset(), requestBlock.getInQueue()); 102 | 103 | // Dequeue (remove) messages from the http client and shuttle 104 | // there may be no messages returned here even if there were messages inserted above -- those messages may have been dupes 105 | store.dequeueIn(id).stream() 106 | .collect(groupingBy(x -> x.getDestinationAddress().getElement(0))).entrySet().stream() 107 | .forEach(e -> { 108 | Shuttle outShuttle = outShuttles.get(e.getKey()); 109 | if (outShuttle != null) { 110 | outShuttle.send(e.getValue()); 111 | } 112 | }); 113 | 114 | 115 | 116 | 117 | 118 | // Get messages to the http client starting from outQueueOffset, and dequeue (remove) everything before outQueueOffset 119 | // if it's asking for outQueueOffset, it means it successfully recvd everything before it, so we can dequeue it 120 | List outQueue = store.dequeueOut(id, requestBlock.getOutQueueOffset()); 121 | 122 | 123 | 124 | 125 | 126 | // Create and write response 127 | ResponseBlock responseBlock = new ResponseBlock(outQueue); 128 | String responseJson = jsonConverter.toJson(responseBlock); 129 | try (Writer writer = resp.getWriter()) { 130 | IOUtils.write(responseJson, writer); 131 | } 132 | } catch (RuntimeException | IOException e) { 133 | resp.setStatus(SC_INTERNAL_SERVER_ERROR); 134 | LOG.error("Servlet failed: {}", e); 135 | } 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/RequestBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import org.apache.commons.collections4.list.UnmodifiableList; 23 | import static org.apache.commons.collections4.list.UnmodifiableList.unmodifiableList; 24 | import org.apache.commons.lang3.Validate; 25 | 26 | final class RequestBlock { 27 | private final String id; 28 | private final int outQueueOffset; 29 | private final int inQueueOffset; 30 | private final UnmodifiableList inQueue; 31 | 32 | RequestBlock(String id, int outQueueOffset, int inQueueOffset, List inQueue) { 33 | Validate.notNull(id); 34 | Validate.notNull(inQueue); 35 | Validate.noNullElements(inQueue); 36 | Validate.isTrue(outQueueOffset >= 0); 37 | Validate.isTrue(inQueueOffset >= 0); 38 | 39 | this.id = id; 40 | this.outQueueOffset = outQueueOffset; 41 | this.inQueueOffset = inQueueOffset; 42 | this.inQueue = (UnmodifiableList) unmodifiableList(new ArrayList<>(inQueue)); 43 | } 44 | 45 | public String getId() { 46 | return id; 47 | } 48 | 49 | public int getOutQueueOffset() { 50 | return outQueueOffset; 51 | } 52 | 53 | public int getInQueueOffset() { 54 | return inQueueOffset; 55 | } 56 | 57 | public UnmodifiableList getInQueue() { 58 | return inQueue; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/ResponseBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import org.apache.commons.collections4.list.UnmodifiableList; 23 | import static org.apache.commons.collections4.list.UnmodifiableList.unmodifiableList; 24 | import org.apache.commons.lang3.Validate; 25 | 26 | final class ResponseBlock { 27 | private final UnmodifiableList outQueue; 28 | 29 | ResponseBlock(List outQueue) { 30 | Validate.notNull(outQueue); 31 | Validate.noNullElements(outQueue); 32 | this.outQueue = (UnmodifiableList) unmodifiableList(new ArrayList<>(outQueue)); 33 | } 34 | 35 | public UnmodifiableList getOutQueue() { 36 | return outQueue; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/ServletShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import com.offbynull.actors.shuttle.Shuttle; 21 | import java.util.Collection; 22 | import org.apache.commons.lang3.Validate; 23 | import java.util.concurrent.CountDownLatch; 24 | import static java.util.stream.Collectors.groupingBy; 25 | 26 | final class ServletShuttle implements Shuttle { 27 | 28 | private final String prefix; 29 | private final Store store; 30 | private final CountDownLatch shutdownLatch; 31 | 32 | ServletShuttle(String prefix, Store store, CountDownLatch shutdownLatch) { 33 | Validate.notNull(prefix); 34 | Validate.notNull(store); 35 | Validate.notNull(shutdownLatch); 36 | 37 | this.prefix = prefix; 38 | this.store = store; 39 | this.shutdownLatch = shutdownLatch; 40 | } 41 | 42 | @Override 43 | public String getPrefix() { 44 | return prefix; 45 | } 46 | 47 | @Override 48 | public void send(Collection messages) { 49 | Validate.notNull(messages); 50 | Validate.noNullElements(messages); 51 | 52 | if (shutdownLatch.getCount() == 0L) { 53 | return; 54 | } 55 | 56 | messages.stream() 57 | .filter(m -> m.getDestinationAddress().size() <= 2) 58 | .filter(m -> m.getDestinationAddress().getElement(0).equals(prefix)) 59 | .collect(groupingBy(x -> x.getDestinationAddress().getElement(1))).entrySet().stream() 60 | .forEach(e -> store.queueOut(e.getKey(), e.getValue())); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Gateway that allows HTTP clients to send and receive messages. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.servlet; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/servlet/stores/memory/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * In-memory servlet storage engine implementation. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.servlet.stores.memory; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/threadpool/ThreadPoolProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.threadpool; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import com.offbynull.actors.shuttle.Shuttle; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | /** 24 | * Processes a {@link ThreadPoolGateway}'s incoming messages. Incoming messages are processed in the gateway's thread pool. 25 | * @author Kasra Faghihi 26 | */ 27 | public interface ThreadPoolProcessor { 28 | /** 29 | * Process incoming message. 30 | * @param message message to be processed 31 | * @param outShuttles shuttles that can be be output to (DO NOT MODIFY) 32 | * @throws Exception on error 33 | * @throws NullPointerException if any argument is {@code null} 34 | */ 35 | void process(Message message, ConcurrentHashMap outShuttles) throws Exception; 36 | // outShuttles is concurrent map -- entries added/removed on the fly, don't nullcheck keys or values 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/threadpool/ThreadPoolShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.threadpool; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import com.offbynull.actors.shuttle.Shuttle; 21 | import java.util.Collection; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | import java.util.concurrent.Callable; 25 | import java.util.concurrent.ConcurrentHashMap; 26 | import java.util.concurrent.ExecutorService; 27 | import org.apache.commons.collections4.map.UnmodifiableMap; 28 | import static org.apache.commons.collections4.map.UnmodifiableMap.unmodifiableMap; 29 | import org.apache.commons.lang3.Validate; 30 | 31 | final class ThreadPoolShuttle implements Shuttle { 32 | private final String prefix; 33 | private final ExecutorService threadPool; 34 | private final ConcurrentHashMap outShuttles; 35 | private final UnmodifiableMap, ThreadPoolProcessor> payloadTypes; 36 | 37 | ThreadPoolShuttle( 38 | String prefix, 39 | ExecutorService threadPool, 40 | Map, ThreadPoolProcessor> payloadTypes, 41 | ConcurrentHashMap outShuttles) { 42 | Validate.notNull(prefix); 43 | Validate.notNull(threadPool); 44 | Validate.notNull(payloadTypes); 45 | Validate.notNull(outShuttles); // outShuttles is concurrent map -- entries added/removed on the fly, don't nullcheck keys or values 46 | Validate.noNullElements(payloadTypes.keySet()); 47 | Validate.noNullElements(payloadTypes.values()); 48 | this.prefix = prefix; 49 | this.threadPool = threadPool; 50 | this.payloadTypes = (UnmodifiableMap, ThreadPoolProcessor>) unmodifiableMap(new HashMap<>(payloadTypes)); 51 | this.outShuttles = outShuttles; 52 | } 53 | 54 | @Override 55 | public String getPrefix() { 56 | return prefix; 57 | } 58 | 59 | @Override 60 | public void send(Collection messages) { 61 | Validate.notNull(messages); 62 | Validate.noNullElements(messages); 63 | 64 | if (threadPool.isShutdown()) { 65 | return; 66 | } 67 | 68 | messages.stream() 69 | .filter(m -> m.getDestinationAddress().size() <= 2) 70 | .filter(m -> m.getDestinationAddress().getElement(0).equals(prefix)) 71 | .forEach(m -> { 72 | ThreadPoolProcessor processor = payloadTypes.get(m.getMessage().getClass()); 73 | if (processor != null) { 74 | Callable callable = () -> { 75 | processor.process(m, outShuttles); 76 | return null; 77 | }; 78 | threadPool.submit(callable); 79 | } 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/threadpool/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Gateway that pipes messages to a thread pool for processing. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.threadpool; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/timer/AddShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.timer; 18 | 19 | import com.offbynull.actors.shuttle.Shuttle; 20 | import org.apache.commons.lang3.Validate; 21 | 22 | final class AddShuttle { 23 | private final Shuttle shuttle; 24 | 25 | AddShuttle(Shuttle shuttle) { 26 | Validate.notNull(shuttle); 27 | this.shuttle = shuttle; 28 | } 29 | 30 | public Shuttle getShuttle() { 31 | return shuttle; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "AddShuttle{" + "shuttle=" + shuttle + '}'; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/timer/RemoveShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.timer; 18 | 19 | import org.apache.commons.lang3.Validate; 20 | 21 | final class RemoveShuttle { 22 | private final String prefix; 23 | 24 | RemoveShuttle(String prefix) { 25 | Validate.notNull(prefix); 26 | this.prefix = prefix; 27 | } 28 | 29 | public String getPrefix() { 30 | return prefix; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "RemoveShuttle{" + "prefix=" + prefix + '}'; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/timer/TimerGateway.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.timer; 18 | 19 | import static com.offbynull.actors.gateway.CommonAddresses.DEFAULT_TIMER; 20 | import com.offbynull.actors.gateway.Gateway; 21 | import com.offbynull.actors.shuttle.Shuttle; 22 | import com.offbynull.actors.shuttles.simple.Bus; 23 | import com.offbynull.actors.shuttles.simple.SimpleShuttle; 24 | import java.util.concurrent.atomic.AtomicBoolean; 25 | import org.apache.commons.lang3.Validate; 26 | 27 | /** 28 | * {@link Gateway} that accepts a message and echoes them back after a certain duration of time. To specify the duration when a message is 29 | * echoed back, append it to the destination address. For example, if this gateway has the prefix {@code "timer"} and you want it to echo 30 | * a message back after 2000 milliseconds, send that message to {@code "timer:2000"}. 31 | * @author Kasra Faghihi 32 | */ 33 | public final class TimerGateway implements Gateway { 34 | 35 | private final Thread thread; 36 | private final Bus bus; 37 | 38 | private final SimpleShuttle shuttle; 39 | 40 | private final AtomicBoolean shutdownFlag; 41 | 42 | /** 43 | * Create a {@link TimerGateway} instance. Equivalent to calling {@code create(CommonAddresses.DEFAULT_TIMER)}. 44 | * @return new direct gateway 45 | */ 46 | public static TimerGateway create() { 47 | return create(DEFAULT_TIMER); 48 | } 49 | 50 | /** 51 | * Create a {@link TimerGateway} instance. 52 | * @param prefix address prefix for this gateway 53 | * @return new direct gateway 54 | * @throws NullPointerException if any argument is {@code null} 55 | */ 56 | public static TimerGateway create(String prefix) { 57 | TimerGateway gateway = new TimerGateway(prefix); 58 | gateway.thread.start(); 59 | return gateway; 60 | } 61 | 62 | private TimerGateway(String prefix) { 63 | Validate.notNull(prefix); 64 | 65 | bus = new Bus(); 66 | shuttle = new SimpleShuttle(prefix, bus); 67 | shutdownFlag = new AtomicBoolean(false); 68 | thread = new Thread(new TimerRunnable(bus, shutdownFlag)); 69 | thread.setDaemon(true); 70 | thread.setName(getClass().getSimpleName() + "-" + prefix); 71 | } 72 | 73 | @Override 74 | public Shuttle getIncomingShuttle() { 75 | if (shutdownFlag.get()) { 76 | throw new IllegalStateException(); 77 | } 78 | return shuttle; 79 | } 80 | 81 | @Override 82 | public void addOutgoingShuttle(Shuttle shuttle) { 83 | Validate.notNull(shuttle); 84 | if (shutdownFlag.get()) { 85 | throw new IllegalStateException(); 86 | } 87 | 88 | bus.add(new AddShuttle(shuttle)); 89 | } 90 | 91 | @Override 92 | public void removeOutgoingShuttle(String shuttlePrefix) { 93 | Validate.notNull(shuttlePrefix); 94 | if (shutdownFlag.get()) { 95 | throw new IllegalStateException(); 96 | } 97 | 98 | bus.add(new RemoveShuttle(shuttlePrefix)); 99 | } 100 | 101 | @Override 102 | public void close() { 103 | shutdownFlag.set(true); 104 | bus.close(); 105 | } 106 | 107 | @Override 108 | public void join() throws InterruptedException { 109 | thread.join(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/gateways/timer/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Gateway that accepts a message and echoes it back after a certain duration of time. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.timer; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Provides main classes and interfaces for actors core. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttle/Message.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.shuttle; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import java.io.Serializable; 21 | import org.apache.commons.lang3.Validate; 22 | 23 | /** 24 | * A message. 25 | * @author Kasra Faghihi 26 | */ 27 | public final class Message implements Serializable { 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | private final Address sourceAddress; 32 | private final Address destinationAddress; 33 | private final Object message; 34 | 35 | /** 36 | * Constructs a {@link Message} instance. 37 | * @param sourceAddress source address of this message 38 | * @param destinationAddress destination address of this message 39 | * @param message content of this message 40 | * @throws NullPointerException if any argument is {@code null} 41 | */ 42 | public Message(Address sourceAddress, Address destinationAddress, Object message) { 43 | Validate.notNull(sourceAddress); 44 | Validate.notNull(destinationAddress); 45 | Validate.notNull(message); 46 | 47 | this.sourceAddress = sourceAddress; 48 | this.destinationAddress = destinationAddress; 49 | this.message = message; 50 | } 51 | 52 | /** 53 | * Constructs a {@link Message} instance. Equivalent to calling 54 | * {@code new Message(Address.fromString(sourceAddress), Address.fromString(destinationAddress), message)} 55 | * @param sourceAddress source address of this message 56 | * @param destinationAddress destination address of this message 57 | * @param message content of this message 58 | * @throws NullPointerException if any argument is {@code null} 59 | * @throws IllegalArgumentException if either {@code sourceAddress} or {@code destinationAddress} is empty 60 | */ 61 | public Message(String sourceAddress, String destinationAddress, Object message) { 62 | this(Address.fromString(sourceAddress), Address.fromString(destinationAddress), message); 63 | } 64 | 65 | /** 66 | * Get the source address. 67 | * @return source address 68 | */ 69 | public Address getSourceAddress() { 70 | return sourceAddress; 71 | } 72 | 73 | /** 74 | * Get the destination address. 75 | * @return destination address. 76 | */ 77 | public Address getDestinationAddress() { 78 | return destinationAddress; 79 | } 80 | 81 | /** 82 | * Get the message content. 83 | * @return content of this message 84 | */ 85 | public Object getMessage() { 86 | return message; 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | return "Message{" + "sourceAddress=" + sourceAddress + ", destinationAddress=" + destinationAddress + ", message=" + message + '}'; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttle/Shuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.shuttle; 18 | 19 | import java.util.Collection; 20 | import java.util.Collections; 21 | 22 | /** 23 | * A {@link Shuttle} transports {@link Message}s to a specific actor or gateway. Shuttles provide no reliability guarantees -- meaning that 24 | * messages may arrive delayed, out-of-order, or not at all. Reliability depends on the implementation and the underlying transport 25 | * mechanism used for message passing. For example, messages that are being passed locally will always arrive in a timely manner and 26 | * in-order, while messages passed over UDP may not. 27 | * @author Kasra Faghihi 28 | */ 29 | public interface Shuttle { 30 | /** 31 | * Address prefix this shuttle is for. 32 | * @return address prefix 33 | */ 34 | String getPrefix(); 35 | /** 36 | * Sends {@link Message}s to the specific actor or gateway this {@link Shuttle} is for. Each message's destination 37 | * address must contain the same prefix as that returned by {@link #getPrefix() }, otherwise that message will be silently discarded. 38 | * @param messages messages to send 39 | * @throws NullPointerException if any argument is {@code null} or contains {@code null} 40 | */ 41 | void send(Collection messages); 42 | 43 | /** 44 | * Sends a {@link Message} to the specific actor or gateway this {@link Shuttle} is for. Equivalent to calling 45 | * {@code send(Collections.singleton(message))}. 46 | * @param message message to send 47 | * @throws NullPointerException if any argument is {@code null} 48 | */ 49 | default void send(Message message) { 50 | send(Collections.singleton(message)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttle/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Provides classes and interfaces for defining shuttles. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.shuttle; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/blackhole/BlackholeShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.shuttles.blackhole; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import com.offbynull.actors.shuttle.Shuttle; 21 | import java.util.Collection; 22 | import org.apache.commons.lang3.Validate; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * A shuttle implementation that discards all messages sent to it. 28 | * @author Kasra Faghihi 29 | */ 30 | public final class BlackholeShuttle implements Shuttle { 31 | 32 | private static final Logger LOG = LoggerFactory.getLogger(BlackholeShuttle.class); 33 | private final String prefix; 34 | 35 | /** 36 | * Constructs a {@link BlackholeShuttle} instance. 37 | * @param prefix address prefix of this shuttle 38 | * @throws NullPointerException if any argument is {@code null} 39 | */ 40 | public BlackholeShuttle(String prefix) { 41 | Validate.notNull(prefix); 42 | this.prefix = prefix; 43 | } 44 | 45 | @Override 46 | public String getPrefix() { 47 | return prefix; 48 | } 49 | 50 | @Override 51 | public void send(Collection messages) { 52 | Validate.notNull(messages); 53 | Validate.noNullElements(messages); 54 | // do nothing 55 | 56 | LOG.debug("Received {} messages", messages.size()); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/blackhole/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Shuttle implementation that throws away all messages. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.shuttles.blackhole; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/pump/PumpShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.shuttles.pump; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import com.offbynull.actors.shuttle.Shuttle; 21 | import java.util.Collection; 22 | import java.util.concurrent.LinkedBlockingQueue; 23 | import org.apache.commons.lang3.Validate; 24 | 25 | /** 26 | * A shuttle implementation that queues up and pumps messages for a potentially slower shuttle. 27 | * @author Kasra Faghihi 28 | */ 29 | public final class PumpShuttle implements Shuttle { 30 | private final Shuttle backingShuttle; 31 | private final LinkedBlockingQueue> queue; 32 | 33 | /** 34 | * Create a {@link PumpShuttle}. Equivalent to calling {@code create(backingShuttle, 1000)}. 35 | * @param backingShuttle shuttle being pumped to 36 | * @return pump shuttle controller 37 | * @throws NullPointerException if any argument is {@code null} 38 | */ 39 | public static PumpShuttleController create(Shuttle backingShuttle) { 40 | return create(backingShuttle, 1000); 41 | } 42 | 43 | /** 44 | * Create a {@link PumpShuttle}. This method returns a controller -- use {@link PumpShuttleController#getPumpShuttle() } to get the 45 | * actual pump shuttle. 46 | * @param backingShuttle shuttle being pumped to 47 | * @param warnThreshold maximum queue size before logging a warning 48 | * @return pump shuttle controller 49 | * @throws NullPointerException if any argument is {@code null} 50 | * @throws IllegalArgumentException if {@code warnThreshold < 0} 51 | */ 52 | public static PumpShuttleController create(Shuttle backingShuttle, int warnThreshold) { 53 | Validate.notNull(backingShuttle); 54 | Validate.isTrue(warnThreshold >= 0); 55 | 56 | LinkedBlockingQueue> queue = new LinkedBlockingQueue<>(); 57 | 58 | PumpShuttle pumpShuttle = new PumpShuttle(backingShuttle, queue); 59 | PumpShuttleController controller = new PumpShuttleController(pumpShuttle, backingShuttle, queue, warnThreshold); 60 | 61 | try { 62 | controller.start(); 63 | } catch (RuntimeException re) { 64 | controller.close(); 65 | throw re; 66 | } 67 | 68 | return controller; 69 | } 70 | 71 | PumpShuttle(Shuttle backingShuttle, LinkedBlockingQueue> queue) { 72 | Validate.notNull(backingShuttle); 73 | Validate.notNull(queue); 74 | 75 | this.backingShuttle = backingShuttle; 76 | this.queue = queue; 77 | } 78 | 79 | @Override 80 | public String getPrefix() { 81 | return backingShuttle.getPrefix(); 82 | } 83 | 84 | @Override 85 | public void send(Collection messages) { 86 | Validate.notNull(messages); 87 | Validate.noNullElements(messages); 88 | 89 | if (messages.isEmpty()) { 90 | return; 91 | } 92 | 93 | queue.add(messages); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/pump/PumpShuttleController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.shuttles.pump; 18 | 19 | import com.offbynull.actors.shuttle.Message; 20 | import com.offbynull.actors.shuttle.Shuttle; 21 | import java.io.Closeable; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import java.util.List; 25 | import java.util.concurrent.LinkedBlockingQueue; 26 | import org.apache.commons.lang3.Validate; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | /** 31 | * Controls a {@link PumpShuttle}. 32 | * @author Kasra Faghihi 33 | */ 34 | public final class PumpShuttleController implements Closeable { 35 | 36 | private static final Logger LOG = LoggerFactory.getLogger(PumpShuttleController.class); 37 | 38 | private final LinkedBlockingQueue> queue; 39 | private final PumpShuttle pumpShuttle; 40 | private final Shuttle backingShuttle; 41 | private final PumpRunnable runnable; 42 | private final Thread thread; 43 | private final int warnThreshold; 44 | private volatile boolean closed; 45 | 46 | PumpShuttleController(PumpShuttle pumpShuttle, Shuttle backingShuttle, LinkedBlockingQueue> queue, 47 | int warnThreshold) { 48 | Validate.notNull(pumpShuttle); 49 | Validate.notNull(backingShuttle); 50 | Validate.notNull(queue); 51 | Validate.isTrue(warnThreshold >= 0); 52 | 53 | this.queue = queue; 54 | this.pumpShuttle = pumpShuttle; 55 | this.backingShuttle = backingShuttle; 56 | this.warnThreshold = warnThreshold; 57 | this.runnable = new PumpRunnable(); 58 | this.thread = new Thread(runnable); 59 | this.thread.setName("PumpShuttleThread - " + pumpShuttle.getPrefix()); 60 | this.thread.setDaemon(true); 61 | } 62 | 63 | void start() { 64 | thread.start(); 65 | } 66 | 67 | /** 68 | * Get shuttle for this controller. 69 | * @return pump shuttle 70 | */ 71 | public PumpShuttle getPumpShuttle() { 72 | return pumpShuttle; 73 | } 74 | 75 | @Override 76 | public void close() { 77 | closed = true; 78 | thread.interrupt(); 79 | } 80 | 81 | /** 82 | * Wait for the {@link PumpShuttle} thread to die. 83 | * @throws InterruptedException if interrupted 84 | */ 85 | public void join() throws InterruptedException { 86 | thread.join(); 87 | } 88 | 89 | 90 | 91 | private final class PumpRunnable implements Runnable { 92 | 93 | @Override 94 | public void run() { 95 | try { 96 | while (true) { 97 | Collection messages = queue.take(); 98 | backingShuttle.send(messages); 99 | 100 | int queueSize = queue.size(); 101 | if (queueSize >= warnThreshold) { 102 | LOG.warn("Queue size exceeds warning threshold: {} messages for {}", queueSize, backingShuttle.getPrefix()); 103 | } 104 | } 105 | } catch (InterruptedException ie) { 106 | // clear interrupted flag 107 | Thread.interrupted(); 108 | 109 | // flush remaining 110 | List> finalMessages = new ArrayList<>(); 111 | queue.drainTo(finalMessages); 112 | finalMessages.forEach(messages -> backingShuttle.send(messages)); 113 | 114 | // return if closed, or throw exception if actually interrupted 115 | if (closed) { 116 | return; 117 | } 118 | 119 | throw new IllegalStateException(ie); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/pump/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * A shuttle implementation that queues up and pumps messages into a potentially slower shuttle. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.shuttles.pump; 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/simple/SimpleShuttle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.shuttles.simple; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import com.offbynull.actors.shuttle.Message; 21 | import com.offbynull.actors.shuttle.Shuttle; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import java.util.List; 25 | import org.apache.commons.lang3.Validate; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | /** 30 | * A simple shuttle implementation. This shuttle writes to a {@link Bus}. Another thread can then read from that bus and process messages 31 | * process messages that are sent to this shuttle. 32 | * @author Kasra Faghihi 33 | */ 34 | public final class SimpleShuttle implements Shuttle { 35 | private static final Logger LOG = LoggerFactory.getLogger(SimpleShuttle.class); 36 | 37 | private final String prefix; 38 | private final Bus bus; 39 | 40 | /** 41 | * Constructs a {@link SimpleShuttle} instance. 42 | * @param prefix address prefix of this shuttle 43 | * @param bus bus to shuttle to 44 | * @throws NullPointerException if any arguments are {@code null} 45 | */ 46 | public SimpleShuttle(String prefix, Bus bus) { 47 | Validate.notNull(prefix); 48 | Validate.notNull(bus); 49 | 50 | this.prefix = prefix; 51 | this.bus = bus; 52 | } 53 | 54 | @Override 55 | public String getPrefix() { 56 | return prefix; 57 | } 58 | 59 | @Override 60 | public void send(Collection messages) { 61 | Validate.notNull(messages); 62 | Validate.noNullElements(messages); 63 | 64 | List filteredMessages = new ArrayList<>(messages.size()); 65 | messages.stream().forEach(x -> { 66 | try { 67 | Address dst = x.getDestinationAddress(); 68 | String dstPrefix = dst.getElement(0); 69 | Validate.isTrue(dstPrefix.equals(prefix)); 70 | 71 | filteredMessages.add(x); 72 | } catch (Exception e) { 73 | LOG.error("Error shuttling message: " + x, e); 74 | } 75 | }); 76 | 77 | LOG.debug("Shuttling {} messages", filteredMessages.size()); 78 | bus.add(filteredMessages); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/com/offbynull/actors/shuttles/simple/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * A shuttle implementation that sends messages to a local bus. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.shuttles.simple; 24 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/ActorSystemTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors; 2 | 3 | import com.offbynull.actors.gateways.actor.Context; 4 | import com.offbynull.coroutines.user.Coroutine; 5 | import static org.junit.Assert.assertEquals; 6 | import org.junit.Test; 7 | 8 | public class ActorSystemTest { 9 | 10 | @Test(timeout = 2000L) 11 | public void mustCreateAndCommunicateActorsAndGateways() throws Exception { 12 | Coroutine echoer = cnt -> { 13 | Context ctx = (Context) cnt.getContext(); 14 | ctx.allow(); 15 | 16 | cnt.suspend(); 17 | 18 | ctx.out("actor:sender", ctx.in()); 19 | }; 20 | 21 | Coroutine sender = cnt -> { 22 | Context ctx = (Context) cnt.getContext(); 23 | ctx.allow(); 24 | 25 | // Send a message to echoer 26 | ctx.out("actor:echoer", "hi"); 27 | cnt.suspend(); 28 | 29 | // Forward the response we got to the direct gateway 30 | String response = ctx.in(); 31 | ctx.out("direct:test", response); 32 | }; 33 | 34 | try (ActorSystem actorSystem = ActorSystem.createDefault()) { 35 | actorSystem.getDirectGateway().listen("direct:test"); 36 | 37 | actorSystem.getActorGateway().addActor("echoer", echoer, new Object()); 38 | actorSystem.getActorGateway().addActor("sender", sender, new Object()); 39 | 40 | String output = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 41 | 42 | assertEquals("hi", output); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/address/AddressTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.address; 2 | 3 | import java.util.Arrays; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertFalse; 6 | import static org.junit.Assert.assertTrue; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.ExpectedException; 10 | 11 | public class AddressTest { 12 | 13 | @Rule 14 | public ExpectedException exception = ExpectedException.none(); 15 | 16 | @Test 17 | public void mustConstructSingleElementAddressFromString() { 18 | Address fixture = Address.fromString("hi"); 19 | assertEquals(Arrays.asList("hi"), fixture.getElements()); 20 | } 21 | 22 | @Test 23 | public void mustConstructMultiElementAddressFromString() { 24 | Address fixture = Address.fromString("hi:to:you"); 25 | assertEquals(Arrays.asList("hi", "to", "you"), fixture.getElements()); 26 | } 27 | 28 | @Test 29 | public void mustConstructMultiElementAddressFromStringWithEscapeSequences() { 30 | Address fixture = Address.fromString("\\:h\\\\i\\::\\\\t\\:o\\\\:you"); 31 | assertEquals(Arrays.asList(":h\\i:", "\\t:o\\", "you"), fixture.getElements()); 32 | } 33 | 34 | @Test 35 | public void mustConstructAddressFromEmptyString() { 36 | Address fixture = Address.fromString(""); 37 | assertEquals(Arrays.asList(""), fixture.getElements()); 38 | } 39 | 40 | @Test 41 | public void mustProperlyConvertAddressToAndFromString() { 42 | Address fixture = Address.of(":h\\i:", "\\t:o\\", "you"); 43 | Address reconstructed = Address.fromString(fixture.toString()); 44 | assertEquals(fixture, reconstructed); 45 | } 46 | 47 | @Test 48 | public void mustIdentifyAsPrefix() { 49 | Address parent = Address.of("one", "two"); 50 | Address fixture = Address.of("one", "two", "three"); 51 | assertTrue(parent.isPrefixOf(fixture)); 52 | } 53 | 54 | @Test 55 | public void mustNotIdentifyAsPrefix() { 56 | Address other = Address.of("one", "two", "three"); 57 | Address fixture = Address.of("one", "xxx", "three"); 58 | assertFalse(other.isPrefixOf(fixture)); 59 | } 60 | 61 | @Test 62 | public void mustNotIdentifyAsPrefixWhenShorter() { 63 | Address other = Address.of("one", "two", "three"); 64 | Address fixture = Address.of("one", "two"); 65 | assertFalse(other.isPrefixOf(fixture)); 66 | } 67 | 68 | @Test 69 | public void mustIdentifyAsPrefixWhenEqual() { 70 | Address other = Address.of("one", "two", "three"); 71 | Address fixture = Address.of("one", "two", "three"); 72 | assertTrue(other.isPrefixOf(fixture)); 73 | } 74 | 75 | @Test 76 | public void mustIdentifyAsParentOrEqualWhenParent() { 77 | Address parent = Address.of("one", "two"); 78 | Address fixture = Address.of("one", "two", "three"); 79 | assertTrue(parent.isPrefixOf(fixture)); 80 | } 81 | 82 | @Test 83 | public void mustIdentifyAsParentOrEqualWhenEqual() { 84 | Address parent = Address.of("one", "two", "three"); 85 | Address fixture = Address.of("one", "two", "three"); 86 | assertTrue(parent.isPrefixOf(fixture)); 87 | } 88 | 89 | @Test 90 | public void mustNotIdentifyAsParentOrEqual() { 91 | Address other = Address.of("one", "two", "three"); 92 | Address fixture = Address.of("one", "xxx", "three"); 93 | assertFalse(other.isPrefixOf(fixture)); 94 | } 95 | 96 | @Test 97 | public void mustRelativizeToAddress() { 98 | Address parent = Address.of("one", "two"); 99 | Address fixture = Address.of("one", "two", "three"); 100 | assertEquals(Address.of("three"), fixture.removePrefix(parent)); 101 | } 102 | 103 | @Test 104 | public void mustFailIfRelativizedToEmpty() { 105 | Address parent = Address.of("one", "two"); 106 | Address fixture = Address.of("one", "two"); 107 | exception.expect(IllegalArgumentException.class); 108 | fixture.removePrefix(parent); 109 | } 110 | 111 | @Test 112 | public void mustFailToRemovePrefix() { 113 | Address other = Address.of("one", "two", "three"); 114 | Address fixture = Address.of("one", "xxx", "three"); 115 | exception.expect(IllegalArgumentException.class); 116 | fixture.removePrefix(other); 117 | } 118 | 119 | @Test 120 | public void mustRemoveSuffix() { 121 | Address fixture = Address.of("one", "xxx", "three"); 122 | assertEquals(Address.of("one"), fixture.removeSuffix(2)); 123 | } 124 | 125 | @Test 126 | public void mustNotRemoveSuffixIfRemoveCountIs0() { 127 | Address fixture = Address.of("one", "xxx", "three"); 128 | assertEquals(fixture, fixture.removeSuffix(0)); 129 | } 130 | 131 | @Test 132 | public void mustFailToRemoveSuffixIfRemoveCountIsAll() { 133 | Address fixture = Address.of("one", "xxx", "three"); 134 | exception.expect(IllegalArgumentException.class); 135 | fixture.removeSuffix(3); 136 | } 137 | 138 | @Test 139 | public void mustFailToRemoveSuffixIfRemoveCountIsOutOfBounds() { 140 | Address fixture = Address.of("one", "xxx", "three"); 141 | exception.expect(IllegalArgumentException.class); 142 | fixture.removeSuffix(4); 143 | } 144 | 145 | @Test 146 | public void mustFailToConstructAnAddressWith0Elements() { 147 | exception.expect(IllegalArgumentException.class); 148 | Address.of(); // no crash means success 149 | } 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/actor/ActorNeighbourSpawnTest.java: -------------------------------------------------------------------------------- 1 | 2 | package com.offbynull.actors.gateways.actor; 3 | 4 | import com.offbynull.actors.gateways.direct.DirectGateway; 5 | import com.offbynull.coroutines.user.Coroutine; 6 | import org.junit.After; 7 | import static org.junit.Assert.assertEquals; 8 | import org.junit.Test; 9 | import org.junit.Before; 10 | 11 | public class ActorNeighbourSpawnTest { 12 | 13 | private ActorGateway runner; 14 | private DirectGateway direct; 15 | 16 | @Before 17 | public void setUp() { 18 | runner = ActorGateway.create("runner", 1); 19 | direct = DirectGateway.create("direct"); 20 | 21 | direct.addOutgoingShuttle(runner.getIncomingShuttle()); 22 | runner.addOutgoingShuttle(direct.getIncomingShuttle()); 23 | 24 | direct.listen("direct"); 25 | } 26 | 27 | @After 28 | public void tearDown() throws Exception { 29 | runner.close(); 30 | direct.close(); 31 | } 32 | 33 | @Test(timeout = 2000L) 34 | public void mustSpawnRootActorFromAnotherRootActor() throws Exception { 35 | Coroutine actor1 = cnt -> { 36 | Context ctx = (Context) cnt.getContext(); 37 | ctx.allow(); 38 | 39 | ctx.out("direct", "ready from 1"); 40 | }; 41 | 42 | Coroutine actor0 = cnt -> { 43 | Context ctx = (Context) cnt.getContext(); 44 | ctx.allow(); 45 | ctx.out("direct", "ready from 0"); 46 | 47 | ctx.root("actor1", actor1, new Object()); 48 | }; 49 | 50 | runner.addActor("actor0", actor0, new Object()); 51 | 52 | 53 | assertEquals("ready from 0", direct.readMessagePayloadOnly("direct")); 54 | assertEquals("ready from 1", direct.readMessagePayloadOnly("direct")); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/actor/ContextTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor; 2 | 3 | import com.offbynull.actors.gateways.actor.Context.BatchedOutgoingMessageCommand; 4 | import com.offbynull.actors.address.Address; 5 | import java.util.List; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | public class ContextTest { 12 | 13 | private Context fixture; 14 | 15 | @Before 16 | public void setUp() { 17 | fixture = new Context(Address.fromString("self")); 18 | } 19 | 20 | @Test 21 | public void mustQueueUpOutgoingMessages() { 22 | List outgoingMsgsView = fixture.viewOuts(); 23 | 24 | fixture.out("test1", "1"); 25 | fixture.out("self", "test2", "2"); 26 | fixture.out("self:a", "test3", "3"); 27 | 28 | assertEquals(3, outgoingMsgsView.size()); 29 | 30 | assertEquals(Address.fromString("self"), outgoingMsgsView.get(0).getSource()); 31 | assertEquals(Address.fromString("test1"), outgoingMsgsView.get(0).getDestination()); 32 | assertEquals("1", outgoingMsgsView.get(0).getMessage()); 33 | 34 | assertEquals(Address.fromString("self"), outgoingMsgsView.get(1).getSource()); 35 | assertEquals(Address.fromString("test2"), outgoingMsgsView.get(1).getDestination()); 36 | assertEquals("2", outgoingMsgsView.get(1).getMessage()); 37 | 38 | assertEquals(Address.fromString("self:a"), outgoingMsgsView.get(2).getSource()); 39 | assertEquals(Address.fromString("test3"), outgoingMsgsView.get(2).getDestination()); 40 | assertEquals("3", outgoingMsgsView.get(2).getMessage()); 41 | } 42 | 43 | @Test 44 | public void mustDrainOutgoingMessages() { 45 | fixture.out("test1", "1"); 46 | fixture.out("self", "test2", "2"); 47 | fixture.out("self:a", "test3", "3"); 48 | 49 | List outgoingMsgs = fixture.copyAndClearOutgoingMessages(); 50 | 51 | assertTrue(fixture.copyAndClearOutgoingMessages().isEmpty()); 52 | assertTrue(fixture.viewOuts().isEmpty()); 53 | 54 | assertEquals(3, outgoingMsgs.size()); 55 | 56 | assertEquals(Address.fromString("self"), outgoingMsgs.get(0).getSource()); 57 | assertEquals(Address.fromString("test1"), outgoingMsgs.get(0).getDestination()); 58 | assertEquals("1", outgoingMsgs.get(0).getMessage()); 59 | 60 | assertEquals(Address.fromString("self"), outgoingMsgs.get(1).getSource()); 61 | assertEquals(Address.fromString("test2"), outgoingMsgs.get(1).getDestination()); 62 | assertEquals("2", outgoingMsgs.get(1).getMessage()); 63 | 64 | assertEquals(Address.fromString("self:a"), outgoingMsgs.get(2).getSource()); 65 | assertEquals(Address.fromString("test3"), outgoingMsgs.get(2).getDestination()); 66 | assertEquals("3", outgoingMsgs.get(2).getMessage()); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/actor/SerializableActorHelper.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor; 2 | 3 | import com.offbynull.actors.address.Address; 4 | import com.offbynull.coroutines.user.CoroutineRunner; 5 | 6 | public final class SerializableActorHelper { 7 | private SerializableActorHelper() { 8 | // do nothing 9 | } 10 | 11 | public static SerializableActor createFake(String address) { 12 | Context context = new Context(Address.fromString(address)); 13 | CoroutineRunner runner = new CoroutineRunner(cnt -> {}); 14 | Actor actor = new Actor(null, runner, context); 15 | 16 | return SerializableActor.serialize(actor); 17 | } 18 | 19 | public static SerializableActor createFake(String address, Object checkpointMsg, long checkpointTimeout) { 20 | Context context = new Context(Address.fromString(address)); 21 | context.checkpointTimeout(checkpointTimeout); 22 | context.checkpointPayload(checkpointMsg); 23 | CoroutineRunner runner = new CoroutineRunner(cnt -> {}); 24 | Actor actor = new Actor(null, runner, context); 25 | 26 | return SerializableActor.serialize(actor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/actor/stores/memory/BestEffortSerializerTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor.stores.memory; 2 | 3 | import com.offbynull.actors.common.BestEffortSerializer; 4 | import java.util.Arrays; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Random; 9 | import java.util.function.Consumer; 10 | import org.apache.commons.collections4.list.UnmodifiableList; 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertTrue; 13 | import static org.junit.Assert.assertArrayEquals; 14 | import static org.junit.Assert.assertNotEquals; 15 | import org.junit.Rule; 16 | import org.junit.Test; 17 | import org.junit.rules.ExpectedException; 18 | 19 | public class BestEffortSerializerTest { 20 | 21 | @Rule 22 | public ExpectedException expected = ExpectedException.none(); 23 | 24 | private BestEffortSerializer fixture = new BestEffortSerializer(); 25 | 26 | @Test 27 | public void mustSerializeAndDeserializeComplexObject() { 28 | TestClass writeObj = new TestClass("ffff"); 29 | TestClass readObj; 30 | 31 | byte[] data = fixture.serialize(writeObj); 32 | System.out.println(""); 33 | System.out.println(""); 34 | System.out.println(""); 35 | System.out.println(""); 36 | System.out.println(""); 37 | readObj = fixture.deserialize(data); 38 | 39 | assertTrue(readObj != null); 40 | assertNotEquals(readObj, writeObj); 41 | 42 | assertTrue(readObj.innerClass != null); 43 | assertNotEquals(readObj.innerClass, writeObj.innerClass); 44 | 45 | assertTrue(readObj.anonymousClass != null); 46 | assertNotEquals(readObj.anonymousClass, writeObj.anonymousClass); 47 | 48 | assertArrayEquals(writeObj.x, readObj.x, 0.0f); 49 | assertEquals(writeObj.b, readObj.b); 50 | assertEquals(writeObj.i, readObj.i); 51 | assertNotEquals(writeObj.r, readObj.r); 52 | assertEquals(writeObj.c, readObj.c); 53 | assertEquals(writeObj.a, readObj.a); 54 | assertEquals(writeObj.nsc.size(), readObj.nsc.size()); 55 | assertTrue(readObj.nsc.get(0) != null && readObj.nsc.get(0) != writeObj.nsc.get(0)); 56 | assertTrue(readObj.nsc.get(1) != null && readObj.nsc.get(1) != writeObj.nsc.get(1)); 57 | assertTrue(readObj.nsc.get(2) != null && readObj.nsc.get(2) != writeObj.nsc.get(2)); 58 | assertEquals(writeObj.s, readObj.s); 59 | assertEquals(writeObj.nullmap, readObj.nullmap); 60 | } 61 | 62 | @Test 63 | public void mustFailToDeserializeWithNonSerializableLambda() { 64 | TestClass writeObj = new TestClass("ffff"); 65 | writeObj.c = x -> {}; 66 | byte[] data = fixture.serialize(writeObj); 67 | 68 | expected.expect(IllegalStateException.class); 69 | expected.expectMessage("Lambda"); 70 | fixture.deserialize(data); 71 | } 72 | 73 | 74 | private static final class TestClass { 75 | 76 | private transient TestClass self = this; 77 | private transient MyInnerClass innerClass = new MyInnerClass(99L); 78 | private transient Object anonymousClass; 79 | private transient float[] x = { 0.0f, 1.0f, 2.0f }; 80 | private transient long b = 2; 81 | private transient Integer i = 111; 82 | private Random r = new Random(0); 83 | private Consumer c = null; //x -> {} 84 | private final int a = 1; 85 | private final List nsc = UnmodifiableList.unmodifiableList(new LinkedList<>(Arrays.asList( 86 | new NonSerializableClass(), 87 | new NonSerializableClass(), 88 | new NonSerializableClass() 89 | ))); 90 | private final String marker = "HEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEYHEY"; 91 | private final String s; 92 | private final Map nullmap = null; 93 | 94 | public TestClass(String s) { 95 | this.s = s; 96 | anonymousClass = new MyInnerInterface() { 97 | String x = "anonClass " + s; 98 | 99 | @Override 100 | public void test() { 101 | System.out.println(x); 102 | } 103 | }; 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return "NonSerializableWithSerializable{" + "a=" + a + ", nsc=" + nsc + ", s=" + s + '}'; 109 | } 110 | 111 | public class MyInnerClass { 112 | long x; 113 | 114 | public MyInnerClass(long x) { 115 | this.x = x; 116 | } 117 | 118 | } 119 | 120 | public interface MyInnerInterface { 121 | void test(); 122 | } 123 | } 124 | 125 | private static final class NonSerializableClass { 126 | 127 | private final double d = Math.random(); 128 | 129 | @Override 130 | public String toString() { 131 | return "NonSerializableClass{" + "d=" + d + '}'; 132 | } 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/direct/DirectGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.direct; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import org.apache.commons.io.IOUtils; 5 | import org.junit.After; 6 | import static org.junit.Assert.assertEquals; 7 | import org.junit.Before; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.ExpectedException; 11 | 12 | public class DirectGatewayTest { 13 | 14 | private DirectGateway direct1Gateway; 15 | private DirectGateway direct2Gateway; 16 | 17 | @Rule 18 | public ExpectedException thrown = ExpectedException.none(); 19 | 20 | @Before 21 | public void before() { 22 | direct1Gateway = DirectGateway.create("direct1"); 23 | direct2Gateway = DirectGateway.create("direct2"); 24 | 25 | direct1Gateway.addOutgoingShuttle(direct2Gateway.getIncomingShuttle()); 26 | direct2Gateway.addOutgoingShuttle(direct1Gateway.getIncomingShuttle()); 27 | } 28 | 29 | @After 30 | public void after() { 31 | IOUtils.closeQuietly(direct1Gateway); 32 | IOUtils.closeQuietly(direct2Gateway); 33 | } 34 | 35 | @Test 36 | public void mustFailToListenOnAddressWithBadPrefix() throws InterruptedException { 37 | thrown.expect(IllegalArgumentException.class); 38 | direct1Gateway.listen("xxxx:in"); 39 | } 40 | 41 | @Test 42 | public void mustFailToReadFromAddressBelowListenAddress() throws InterruptedException { 43 | direct1Gateway.listen("direct1:in1:in2"); 44 | thrown.expect(IllegalArgumentException.class); 45 | direct1Gateway.readMessagePayloadOnly("direct1:in1", 100, TimeUnit.MILLISECONDS); 46 | } 47 | 48 | @Test 49 | public void mustFailToUnlistenOnAddressWithBadPrefix() throws InterruptedException { 50 | thrown.expect(IllegalArgumentException.class); 51 | direct1Gateway.unlisten("xxxx:in"); 52 | } 53 | 54 | @Test 55 | public void mustFailToWriteWithBadSourcePrefix() throws InterruptedException { 56 | thrown.expect(IllegalArgumentException.class); 57 | direct1Gateway.writeMessage("xxxx:in", "direct2", "payload"); 58 | } 59 | 60 | @Test 61 | public void mustNotFailWhenUnlisteningAddressThatWasNeverListenedTo() throws InterruptedException { 62 | direct1Gateway.unlisten("direct1:never_listened_to"); 63 | } 64 | 65 | @Test 66 | public void mustListenOnRootAddresses() throws InterruptedException { 67 | direct1Gateway.listen("direct1"); 68 | direct2Gateway.listen("direct2"); 69 | 70 | direct1Gateway.writeMessage("direct1", "direct2", "1to2"); 71 | direct2Gateway.writeMessage("direct2", "direct1", "2to1"); 72 | 73 | assertEquals("2to1", direct1Gateway.readMessagePayloadOnly("direct1")); 74 | assertEquals("1to2", direct2Gateway.readMessagePayloadOnly("direct2")); 75 | } 76 | 77 | @Test 78 | public void mustListenBelowRootAddresses() throws InterruptedException { 79 | direct1Gateway.listen("direct1:in"); 80 | direct2Gateway.listen("direct2:in"); 81 | 82 | direct1Gateway.writeMessage("direct1:in", "direct2:in", "1to2"); 83 | direct2Gateway.writeMessage("direct2:in", "direct1:in", "2to1"); 84 | 85 | assertEquals("2to1", direct1Gateway.readMessagePayloadOnly("direct1:in")); 86 | assertEquals("1to2", direct2Gateway.readMessagePayloadOnly("direct2:in")); 87 | } 88 | 89 | @Test 90 | public void mustListenToBothRootAddressAndAddressBelowRoot() throws InterruptedException { 91 | direct1Gateway.listen("direct1"); 92 | direct1Gateway.listen("direct1:in"); 93 | direct2Gateway.listen("direct2:in"); 94 | 95 | direct1Gateway.writeMessage("direct1:in", "direct2:in", "1to2"); 96 | direct2Gateway.writeMessage("direct2:in", "direct1:in", "2to1"); 97 | 98 | assertEquals("2to1", direct1Gateway.readMessagePayloadOnly("direct1")); 99 | assertEquals("2to1", direct1Gateway.readMessagePayloadOnly("direct1:in")); 100 | assertEquals("1to2", direct2Gateway.readMessagePayloadOnly("direct2:in")); 101 | } 102 | 103 | @Test 104 | public void mustUnlistenToAddressThatWasBeingListenedOn() throws InterruptedException { 105 | direct1Gateway.listen("direct1:in"); 106 | direct2Gateway.listen("direct2:in"); 107 | 108 | direct1Gateway.writeMessage("direct1:in", "direct2:in", "1to2"); 109 | direct2Gateway.writeMessage("direct2:in", "direct1:in", "2to1"); 110 | 111 | assertEquals("2to1", direct1Gateway.readMessagePayloadOnly("direct1:in")); 112 | assertEquals("1to2", direct2Gateway.readMessagePayloadOnly("direct2:in")); 113 | 114 | direct2Gateway.unlisten("direct2:in"); 115 | 116 | direct1Gateway.writeMessage("direct1:in", "direct2:in", "fake"); 117 | thrown.expect(IllegalArgumentException.class); 118 | direct2Gateway.readMessagePayloadOnly("direct2:in"); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/log/LogGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.log; 2 | 3 | import com.offbynull.actors.gateways.direct.DirectGateway; 4 | import org.apache.commons.io.IOUtils; 5 | import org.junit.After; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class LogGatewayTest { 10 | 11 | private DirectGateway directGateway; 12 | private LogGateway logGateway; 13 | 14 | @Before 15 | public void before() { 16 | directGateway = DirectGateway.create("direct"); 17 | logGateway = LogGateway.create("log"); 18 | 19 | directGateway.addOutgoingShuttle(logGateway.getIncomingShuttle()); 20 | logGateway.addOutgoingShuttle(directGateway.getIncomingShuttle()); 21 | } 22 | 23 | @After 24 | public void after() { 25 | IOUtils.closeQuietly(directGateway); 26 | IOUtils.closeQuietly(logGateway); 27 | } 28 | 29 | // This is difficult to test because its piping to SLF4J, but we can atleast add in a sanity test here 30 | @Test 31 | public void mustNotCrash() throws InterruptedException { 32 | // Remember that since these gateways running in different threads, tehy can close before the log gateway gets all 4 messages 33 | directGateway.writeMessage("direct", "log", LogMessage.error("log msg!!!! {}", 1)); 34 | directGateway.writeMessage("direct", "log", LogMessage.error("log msg!!!! {}", 2)); 35 | directGateway.writeMessage("direct", "log", LogMessage.error("log msg!!!! {}", 3)); 36 | directGateway.writeMessage("direct", "log", LogMessage.error("log msg!!!! {}", 4)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/servlet/JsonConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.servlet; 2 | 3 | import com.offbynull.actors.shuttle.Message; 4 | import java.util.Arrays; 5 | import static org.junit.Assert.assertEquals; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class JsonConverterTest { 10 | 11 | private JsonConverter fixture; 12 | 13 | @Before 14 | public void setUp() { 15 | fixture = new JsonConverter(); 16 | } 17 | 18 | @Test 19 | public void mustConvertJsonStringToRequestBlock() { 20 | String json = 21 | "{\n" 22 | + " id: 'hi',\n" 23 | + " outQueueOffset: 2," 24 | + " inQueueOffset: 3," 25 | + " inQueue: [\n" 26 | + " {\n" 27 | + " source='a:a1',\n" 28 | + " destination='b:b1',\n" 29 | + " type='java.lang.String',\n" 30 | + " data='payload1'\n" 31 | + " },\n" 32 | + " {\n" 33 | + " source='a:a2',\n" 34 | + " destination='b:b2',\n" 35 | + " type='java.lang.String',\n" 36 | + " data='payload2'\n" 37 | + " }\n" 38 | + " ]\n" 39 | + "}"; 40 | 41 | RequestBlock rb = fixture.fromJson(json); 42 | 43 | assertEquals("hi", rb.getId()); 44 | assertEquals(2, rb.getOutQueueOffset()); 45 | assertEquals(3, rb.getInQueueOffset()); 46 | 47 | assertEquals(2, rb.getInQueue().size()); 48 | 49 | assertEquals("a:a1", rb.getInQueue().get(0).getSourceAddress().toString()); 50 | assertEquals("b:b1", rb.getInQueue().get(0).getDestinationAddress().toString()); 51 | assertEquals("payload1", rb.getInQueue().get(0).getMessage()); 52 | 53 | assertEquals("a:a2", rb.getInQueue().get(1).getSourceAddress().toString()); 54 | assertEquals("b:b2", rb.getInQueue().get(1).getDestinationAddress().toString()); 55 | assertEquals("payload2", rb.getInQueue().get(1).getMessage()); 56 | } 57 | 58 | @Test 59 | public void mustGenerateResponseBlockToJsonString() { 60 | ResponseBlock rb = new ResponseBlock(Arrays.asList( 61 | new Message("a:a1", "b:b1", "payload1"), 62 | new Message("a:a2", "b:b2", "payload2") 63 | )); 64 | String json = fixture.toJson(rb); 65 | assertEquals("{\"outQueue\":[{\"source\":\"a:a1\",\"destination\":\"b:b1\",\"type\":\"java.lang.String\",\"data\":\"payload1\"},{\"source\":\"a:a2\",\"destination\":\"b:b2\",\"type\":\"java.lang.String\",\"data\":\"payload2\"}]}", json); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/servlet/ServletGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.servlet; 2 | 3 | import com.offbynull.actors.shuttle.Message; 4 | import java.io.BufferedReader; 5 | import java.io.PrintWriter; 6 | import java.io.StringReader; 7 | import java.io.StringWriter; 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import org.junit.After; 12 | import static org.junit.Assert.assertEquals; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import static org.mockito.Mockito.mock; 16 | import static org.mockito.Mockito.when; 17 | 18 | public final class ServletGatewayTest { 19 | 20 | private ServletGateway fixture; 21 | 22 | @Before 23 | public void setUp() { 24 | fixture = ServletGateway.create(); 25 | } 26 | 27 | @After 28 | public void tearDown() { 29 | fixture.close(); 30 | } 31 | 32 | // This is difficult to test because we need to spin up a full servlet container, but we can atleast add in a sanity test here 33 | @Test 34 | public void mustNotCrash() throws Throwable { 35 | HttpServlet servlet = fixture.getMessageServlet(); 36 | 37 | fixture.getIncomingShuttle().send(new Message("src:src", "servlet:test_id", "hi!")); 38 | 39 | HttpServletRequest req = mock(HttpServletRequest.class); 40 | HttpServletResponse resp = mock(HttpServletResponse.class); 41 | 42 | StringWriter out; 43 | 44 | out = new StringWriter(); 45 | when(req.getMethod()).thenReturn("POST"); 46 | when(req.getReader()).thenReturn(new BufferedReader(new StringReader( 47 | "{\n" 48 | + " id: 'test_id',\n" 49 | // no outDequeueOffset because we haven't gotten anything to dequeue yet 50 | + " outQueueOffset: 0," 51 | + " inQueueOffset: 0," 52 | + " inQueue: [\n" 53 | + " {\n" 54 | + " source: 'servlet:test_id',\n" 55 | + " destination: 'actor:worker123:querier',\n" 56 | + " type: 'java.lang.String',\n" 57 | + " data: 'work'\n" 58 | + " },\n" 59 | + " {\n" 60 | + " source: 'servlet:test_id:subsystem1',\n" 61 | + " destination: 'actor:worker555',\n" 62 | + " type: 'java.lang.Integer',\n" 63 | + " data: 5\n" 64 | + " }\n" 65 | + " ]\n" 66 | + "}"))); 67 | when(resp.getWriter()).thenReturn(new PrintWriter(out)); 68 | servlet.service(req, resp); 69 | assertEquals("{\"outQueue\":[{\"source\":\"src:src\",\"destination\":\"servlet:test_id\",\"type\":\"java.lang.String\",\"data\":\"hi!\"}]}", out.toString()); 70 | 71 | out = new StringWriter(); 72 | when(req.getMethod()).thenReturn("POST"); 73 | when(req.getReader()).thenReturn(new BufferedReader(new StringReader( 74 | "{\n" 75 | + " id: 'test_id',\n" 76 | + " outDequeueOffset: 0," 77 | + " outQueueOffset: 1," 78 | + " inQueueOffset: 2," 79 | + " inQueue: [\n" 80 | + " {\n" 81 | + " source: 'servlet:test_id:subsystem1',\n" 82 | + " destination: 'actor:worker666',\n" 83 | + " type: 'java.lang.Integer',\n" 84 | + " data: 6\n" 85 | + " }\n" 86 | + " ]\n" 87 | + "}"))); 88 | when(resp.getWriter()).thenReturn(new PrintWriter(out)); 89 | servlet.service(req, resp); 90 | assertEquals("{\"outQueue\":[]}", out.toString()); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/threadpool/ThreadPoolGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.threadpool; 2 | 3 | import com.offbynull.actors.address.Address; 4 | import com.offbynull.actors.gateways.direct.DirectGateway; 5 | import com.offbynull.actors.shuttle.Message; 6 | import com.offbynull.actors.shuttle.Shuttle; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import org.apache.commons.io.IOUtils; 10 | import org.junit.After; 11 | import static org.junit.Assert.assertEquals; 12 | import org.junit.Before; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.rules.ExpectedException; 16 | 17 | public class ThreadPoolGatewayTest { 18 | private DirectGateway directGateway; 19 | private ThreadPoolGateway threadPoolGateway; 20 | 21 | @Rule 22 | public ExpectedException thrown = ExpectedException.none(); 23 | 24 | @Before 25 | public void before() { 26 | directGateway = DirectGateway.create("direct"); 27 | Map, ThreadPoolProcessor> payloadTypes = new HashMap<>(); 28 | payloadTypes.put(String.class, (message, outShuttles) -> { 29 | String fromPrefix = message.getSourceAddress().getElement(0); 30 | 31 | Shuttle outShuttle = outShuttles.get(fromPrefix); 32 | if (outShuttle != null) { 33 | Message outMessage = new Message( 34 | message.getDestinationAddress(), 35 | message.getSourceAddress(), 36 | message.getMessage() + "RESPONSE"); 37 | outShuttle.send(outMessage); 38 | } 39 | }); 40 | threadPoolGateway = ThreadPoolGateway.create("threadpool", payloadTypes, 1, 10); 41 | 42 | directGateway.addOutgoingShuttle(threadPoolGateway.getIncomingShuttle()); 43 | threadPoolGateway.addOutgoingShuttle(directGateway.getIncomingShuttle()); 44 | } 45 | 46 | @After 47 | public void after() { 48 | IOUtils.closeQuietly(directGateway); 49 | IOUtils.closeQuietly(threadPoolGateway); 50 | } 51 | 52 | @Test(timeout = 2000L) 53 | public void mustListenOnRootAddresses() throws InterruptedException { 54 | directGateway.listen("direct:a"); 55 | 56 | directGateway.writeMessage("direct:a", "threadpool:b", "test"); 57 | Message msg = directGateway.readMessage("direct:a"); 58 | 59 | assertEquals("testRESPONSE", msg.getMessage()); 60 | assertEquals(Address.fromString("threadpool:b"), msg.getSourceAddress()); 61 | assertEquals(Address.fromString("direct:a"), msg.getDestinationAddress()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/gateways/timer/TimerGatewayTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.timer; 2 | 3 | import com.offbynull.actors.gateways.direct.DirectGateway; 4 | import com.offbynull.actors.shuttle.Message; 5 | import java.util.concurrent.TimeUnit; 6 | import org.apache.commons.io.IOUtils; 7 | import org.junit.After; 8 | import static org.junit.Assert.assertEquals; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | public class TimerGatewayTest { 13 | 14 | private DirectGateway directGateway; 15 | private TimerGateway timerGateway; 16 | 17 | @Before 18 | public void before() { 19 | directGateway = DirectGateway.create("direct"); 20 | timerGateway = TimerGateway.create("timer"); 21 | 22 | directGateway.addOutgoingShuttle(timerGateway.getIncomingShuttle()); 23 | timerGateway.addOutgoingShuttle(directGateway.getIncomingShuttle()); 24 | } 25 | 26 | @After 27 | public void after() { 28 | IOUtils.closeQuietly(directGateway); 29 | IOUtils.closeQuietly(timerGateway); 30 | } 31 | 32 | @Test 33 | public void mustEchoBackMessageAfter500Milliseconds() throws Exception { 34 | directGateway.listen("direct:tester"); 35 | 36 | directGateway.writeMessage("direct:tester:inner1:inner2", "timer:500:inner3:inner4", "payload"); 37 | Message echoed = directGateway.readMessage("direct:tester", 500 + 250 /* 250ms of padding */, TimeUnit.MILLISECONDS); 38 | 39 | assertEquals("timer:500:inner3:inner4", echoed.getSourceAddress().toString()); 40 | assertEquals("direct:tester:inner1:inner2", echoed.getDestinationAddress().toString()); 41 | assertEquals("payload", echoed.getMessage()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/shuttles/blackhole/BlackholeShuttleTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.shuttles.blackhole; 2 | 3 | import com.offbynull.actors.shuttles.blackhole.BlackholeShuttle; 4 | import com.offbynull.actors.address.Address; 5 | import com.offbynull.actors.shuttle.Message; 6 | import java.util.Arrays; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class BlackholeShuttleTest { 11 | 12 | private BlackholeShuttle fixture; 13 | 14 | @Before 15 | public void setUp() { 16 | fixture = new BlackholeShuttle("test"); 17 | } 18 | 19 | @Test 20 | public void mustNotCrash() { 21 | fixture.send(Arrays.asList(new Message(Address.fromString("src:fake1"), Address.fromString("test:sub1"), "hi1"))); 22 | fixture.send(Arrays.asList(new Message(Address.fromString("src:fake2"), Address.fromString("fake"), "hi2"))); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/shuttles/pump/PumpShuttleTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.shuttles.pump; 2 | 3 | import com.offbynull.actors.gateways.direct.DirectGateway; 4 | import com.offbynull.actors.shuttle.Message; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.concurrent.TimeUnit; 8 | import org.junit.After; 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertNull; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public class PumpShuttleTest { 15 | 16 | private PumpShuttleController pumpShuttleController; 17 | private DirectGateway directGateway; 18 | 19 | @Before 20 | public void setUp() { 21 | directGateway = DirectGateway.create(); 22 | pumpShuttleController = PumpShuttle.create(directGateway.getIncomingShuttle()); 23 | } 24 | 25 | @After 26 | public void tearDown() throws InterruptedException { 27 | pumpShuttleController.close(); 28 | pumpShuttleController.join(); 29 | directGateway.close(); 30 | } 31 | 32 | @Test 33 | public void mustPumpMessagesToGateway() throws InterruptedException { 34 | List messages = Arrays.asList( 35 | new Message("direct:src:1", "direct:dst:1", "payload1"), 36 | new Message("direct:src:2", "direct:dst:2", "payload2"), 37 | new Message("direct:src:3", "direct:dst:3", "payload3") 38 | ); 39 | 40 | directGateway.listen("direct:dst"); 41 | pumpShuttleController.getPumpShuttle().send(messages); 42 | 43 | Message message; 44 | 45 | message = directGateway.readMessage("direct:dst"); 46 | assertEquals(messages.get(0), message); 47 | message = directGateway.readMessage("direct:dst"); 48 | assertEquals(messages.get(1), message); 49 | message = directGateway.readMessage("direct:dst"); 50 | assertEquals(messages.get(2), message); 51 | } 52 | 53 | @Test 54 | public void mustNotPumpMessagesIfClosed() throws InterruptedException { 55 | List messages = Arrays.asList( 56 | new Message("direct:src:1", "direct:dst:1", "payload1"), 57 | new Message("direct:src:2", "direct:dst:2", "payload2"), 58 | new Message("direct:src:3", "direct:dst:3", "payload3") 59 | ); 60 | 61 | directGateway.listen("direct:dst"); 62 | pumpShuttleController.close(); 63 | pumpShuttleController.join(); 64 | pumpShuttleController.getPumpShuttle().send(messages); 65 | 66 | Message message; 67 | message = directGateway.readMessage("direct:dst", 300L, TimeUnit.MILLISECONDS); 68 | assertNull(message); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /core/src/test/java/com/offbynull/actors/shuttles/simple/SimpleShuttleTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.shuttles.simple; 2 | 3 | import com.offbynull.actors.address.Address; 4 | import com.offbynull.actors.shuttle.Message; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import org.junit.After; 8 | import static org.junit.Assert.assertEquals; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | public class SimpleShuttleTest { 13 | 14 | private Bus bus; 15 | private SimpleShuttle fixture; 16 | 17 | @Before 18 | public void setUp() { 19 | bus = new Bus(); 20 | fixture = new SimpleShuttle("test", bus); 21 | } 22 | 23 | @After 24 | public void tearDown() { 25 | bus.close(); 26 | } 27 | 28 | @Test 29 | public void mustReadMessagesFromBusWhenInsertedInToShuttle() throws InterruptedException { 30 | fixture.send(Arrays.asList(new Message(Address.fromString("src:fake1"), Address.fromString("test:sub1"), "hi1"))); 31 | fixture.send(Arrays.asList(new Message(Address.fromString("src:fake2"), Address.fromString("test:sub2"), "hi2"))); 32 | 33 | List read = bus.pull(); 34 | 35 | assertEquals(2, read.size()); 36 | 37 | assertEquals("src:fake1", ((Message) read.get(0)).getSourceAddress().toString()); 38 | assertEquals("test:sub1", ((Message) read.get(0)).getDestinationAddress().toString()); 39 | assertEquals("hi1", ((Message) read.get(0)).getMessage()); 40 | 41 | assertEquals("src:fake2", ((Message) read.get(1)).getSourceAddress().toString()); 42 | assertEquals("test:sub2", ((Message) read.get(1)).getDestinationAddress().toString()); 43 | assertEquals("hi2", ((Message) read.get(1)).getMessage()); 44 | } 45 | 46 | @Test 47 | public void mustIgnoreMessagesBeingAddedThatHaveIncorrectDestinations() throws InterruptedException { 48 | fixture.send(Arrays.asList(new Message(Address.fromString("src:fake1"), Address.fromString("wrongdestination"), "hi1"))); 49 | fixture.send(Arrays.asList(new Message(Address.fromString("src:fake2"), Address.fromString("test:sub2"), "hi2"))); 50 | 51 | List read = bus.pull(); 52 | 53 | assertEquals(1, read.size()); 54 | 55 | assertEquals("src:fake2", ((Message) read.get(0)).getSourceAddress().toString()); 56 | assertEquals("test:sub2", ((Message) read.get(0)).getDestinationAddress().toString()); 57 | assertEquals("hi2", ((Message) read.get(0)).getMessage()); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /jdbc-storage/nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | none 17 | 18 | 19 | -------------------------------------------------------------------------------- /jdbc-storage/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test 5 | 6 | * 7 | 8 | 9 | test 10 | coroutines:test-instrument 11 | 12 | 13 | 14 | test.single 15 | 16 | * 17 | 18 | 19 | test-compile 20 | coroutines:test-instrument 21 | surefire:test 22 | 23 | 24 | ${packageClassName} 25 | 26 | 27 | 28 | debug.test.single 29 | 30 | * 31 | 32 | 33 | test-compile 34 | coroutines:test-instrument 35 | surefire:test 36 | 37 | 38 | ${packageClassName} 39 | once 40 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} 41 | true 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /jdbc-storage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.offbynull.actors 6 | parent 7 | 1.0.0-SNAPSHOT 8 | 9 | jdbc-storage 10 | jar 11 | 12 | ${project.groupId}:${project.artifactId} 13 | 14 | 15 | ${project.groupId} 16 | core 17 | ${project.version} 18 | 19 | 20 | 21 | junit 22 | junit 23 | 24 | 25 | org.slf4j 26 | slf4j-simple 27 | test 28 | 29 | 30 | org.apache.derby 31 | derby 32 | test 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-checkstyle-plugin 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-pmd-plugin 44 | 45 | 46 | com.github.spotbugs 47 | spotbugs-maven-plugin 48 | 49 | 50 | com.offbynull.coroutines 51 | maven-plugin 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /jdbc-storage/src/main/java/com/offbynull/actors/gateways/actor/stores/jdbc/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * JDBC actor storage engine implementation. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.actor.stores.jdbc; 24 | -------------------------------------------------------------------------------- /jdbc-storage/src/main/java/com/offbynull/actors/gateways/servlet/stores/jdbc/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * JDBC servlet storage engine implementation. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.servlet.stores.jdbc; 24 | -------------------------------------------------------------------------------- /jdbc-storage/src/main/java/com/offbynull/actors/jdbcclient/JdbcUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.jdbcclient; 18 | 19 | import java.sql.Connection; 20 | import java.sql.SQLException; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | /** 25 | * JDBC client utilities. 26 | * @author Kasra Faghihi 27 | */ 28 | public final class JdbcUtils { 29 | 30 | private static final Logger LOGGER = LoggerFactory.getLogger(JdbcUtils.class); 31 | 32 | private JdbcUtils() { 33 | // do nothing 34 | } 35 | 36 | 37 | 38 | /** 39 | * Commit the connection, if not {@code null}. 40 | * @param conn connection to commit 41 | * @throws SQLException on SQL exception 42 | */ 43 | public static void commitFinally(Connection conn) throws SQLException { 44 | if (conn != null) { 45 | try { 46 | conn.commit(); 47 | } catch (Exception e) { 48 | // swallow exception 49 | } 50 | } 51 | } 52 | 53 | 54 | 55 | 56 | 57 | /** 58 | * Retry a JDBC operation. 59 | * @param return type 60 | * @param block retry block 61 | * @return return value 62 | */ 63 | public static V retry(RetryReturnBlock block) { 64 | while (true) { 65 | try { 66 | return block.run(); 67 | } catch (SQLException sqle) { 68 | boolean connProblem = sqle.getSQLState().startsWith("08"); // 08xxx class of statecodes indicate connection problems 69 | if (connProblem) { 70 | LOGGER.error("Connection problem encountered, retrying...", sqle); 71 | } else { 72 | LOGGER.error("Non-connection problem encountered", sqle); 73 | throw new IllegalStateException(sqle); 74 | } 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * Retry block with a return value. 81 | * @param return type 82 | */ 83 | public interface RetryReturnBlock { 84 | /** 85 | * Retry block. 86 | * @return return value 87 | * @throws SQLException sql error 88 | */ 89 | V run() throws SQLException; 90 | } 91 | 92 | /** 93 | * Retry a JDBC operation. 94 | * @param block retry block 95 | */ 96 | public static void retry(RetryBlock block) { 97 | while (true) { 98 | try { 99 | block.run(); 100 | return; 101 | } catch (SQLException sqle) { 102 | boolean connProblem = sqle.getSQLState().startsWith("08"); // 08xxx class of statecodes indicate connection problems 103 | if (connProblem) { 104 | LOGGER.error("Connection problem encountered, retrying...", sqle); 105 | } else { 106 | LOGGER.error("Non-connection problem encountered", sqle); 107 | throw new IllegalStateException(sqle); 108 | } 109 | } 110 | } 111 | } 112 | 113 | /** 114 | * Retry block. 115 | */ 116 | public interface RetryBlock { 117 | /** 118 | * Retry block. 119 | * @throws SQLException sql error 120 | */ 121 | void run() throws SQLException; 122 | } 123 | 124 | 125 | } 126 | -------------------------------------------------------------------------------- /jdbc-storage/src/main/java/com/offbynull/actors/jdbcclient/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Provides classes and interfaces for JDBC access. 20 | * @author Kasra Faghihi 21 | */ 22 | package com.offbynull.actors.jdbcclient; -------------------------------------------------------------------------------- /jdbc-storage/src/test/java/com/offbynull/actors/ActorSystemTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors; 2 | 3 | import com.offbynull.actors.gateways.actor.Context; 4 | import com.offbynull.actors.gateways.actor.stores.jdbc.JdbcStore; 5 | import com.offbynull.coroutines.user.Coroutine; 6 | import java.io.PrintWriter; 7 | import java.sql.Connection; 8 | import java.sql.DriverManager; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import org.apache.derby.jdbc.EmbeddedConnectionPoolDataSource; 12 | import org.junit.After; 13 | import static org.junit.Assert.assertEquals; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | 17 | public class ActorSystemTest { 18 | 19 | private ActorSystem actorSystem; 20 | 21 | @Before 22 | public void before() throws SQLException { 23 | EmbeddedConnectionPoolDataSource ds = new EmbeddedConnectionPoolDataSource(); 24 | ds.setDatabaseName("memory:testDB"); 25 | ds.setCreateDatabase("create"); 26 | ds.setLogWriter(new PrintWriter(System.out)); 27 | 28 | try (Connection conn = ds.getConnection(); 29 | Statement statement = conn.createStatement()) { 30 | statement.execute( 31 | "CREATE TABLE ACTOR (\n" 32 | + " ADDRESS VARCHAR(1024) NOT NULL, \n" 33 | + " CHECKPOINT_DATA BLOB NOT NULL,\n" 34 | + " CHECKPOINT_MESSAGE_DATA BLOB NOT NULL,\n" 35 | + " CHECKPOINT_TIME BIGINT NOT NULL,\n" 36 | + " CHECKPOINT_INSTANCE INTEGER NOT NULL,\n" 37 | + " IDLE INTEGER NOT NULL,\n" 38 | + " DATA BLOB NOT NULL,\n" 39 | + " PRIMARY KEY (ADDRESS)\n" 40 | + ")\n" 41 | ); 42 | statement.execute( 43 | "CREATE TABLE MESSAGE_QUEUE (\n" 44 | + " MSG_NUMBER INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),\n" 45 | + " ADDRESS VARCHAR(1024) NOT NULL,\n" 46 | + " DATA BLOB NOT NULL,\n" 47 | + " PRIMARY KEY (MSG_NUMBER),\n" 48 | + " FOREIGN KEY (ADDRESS) REFERENCES ACTOR(ADDRESS) ON DELETE CASCADE\n" 49 | + ")\n" 50 | ); 51 | } 52 | 53 | JdbcStore store = JdbcStore.create("actor", ds); 54 | actorSystem = ActorSystem.builder() 55 | .withDirectGateway() 56 | .withActorGateway(1, store) 57 | .build(); 58 | } 59 | 60 | @After 61 | public void after() throws Exception { 62 | actorSystem.close(); 63 | try { 64 | DriverManager.getConnection("jdbc:derby:memory:testDB;drop=true"); 65 | } catch (SQLException sqle) { 66 | // https://db.apache.org/derby/docs/10.8/devguide/cdevdvlpinmemdb.html -- See section "Removing an in-memory database" 67 | if (!"08006".equals(sqle.getSQLState())) { // 08006 is expected 68 | throw sqle; 69 | } 70 | } 71 | actorSystem.join(); 72 | } 73 | 74 | @Test(timeout = 5000L) 75 | public void mustCreateAndCommunicateActorsAndGateways() throws Exception { 76 | Coroutine echoer = cnt -> { 77 | Context ctx = (Context) cnt.getContext(); 78 | ctx.allow(); 79 | 80 | // tell test that echoer is ready 81 | ctx.out("direct:test", "echoer_ready"); 82 | cnt.suspend(); 83 | 84 | ctx.out("actor:sender", ctx.in()); 85 | }; 86 | 87 | Coroutine sender = cnt -> { 88 | Context ctx = (Context) cnt.getContext(); 89 | ctx.allow(); 90 | 91 | // tell test that sender is ready 92 | ctx.out("direct:test", "sender_ready"); 93 | cnt.suspend(); 94 | 95 | // next message should be "go", telling use to send a message to the echoer, wait for its response, and forward it back to the 96 | // direct gateway 97 | ctx.out("actor:echoer", "echo_msg"); // send msg to get echoer 98 | cnt.suspend(); 99 | String response = ctx.in(); // get msg from echoer and forward it to direct gateway 100 | ctx.out("direct:test", response); 101 | }; 102 | 103 | 104 | actorSystem.getDirectGateway().listen("direct:test"); 105 | Object directMsg; 106 | 107 | // start echoer 108 | actorSystem.getActorGateway().addActor("echoer", echoer, new Object()); 109 | directMsg = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 110 | assertEquals("echoer_ready", directMsg); 111 | 112 | // start sender 113 | actorSystem.getActorGateway().addActor("sender", sender, new Object()); 114 | directMsg = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 115 | assertEquals("sender_ready", directMsg); 116 | 117 | // tell sender to send 118 | actorSystem.getDirectGateway().writeMessage("actor:sender", "go"); 119 | directMsg = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 120 | assertEquals("echo_msg", directMsg); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /jdbc-storage/src/test/java/com/offbynull/actors/gateways/actor/SerializableActorHelper.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor; 2 | 3 | import com.offbynull.actors.address.Address; 4 | import com.offbynull.coroutines.user.CoroutineRunner; 5 | 6 | public final class SerializableActorHelper { 7 | private SerializableActorHelper() { 8 | // do nothing 9 | } 10 | 11 | public static SerializableActor createFake(String address) { 12 | Context context = new Context(Address.fromString(address)); 13 | CoroutineRunner runner = new CoroutineRunner(cnt -> {}); 14 | Actor actor = new Actor(null, runner, context); 15 | 16 | return SerializableActor.serialize(actor); 17 | } 18 | 19 | public static SerializableActor createFake(String address, Object checkpointMsg, long checkpointTimeout) { 20 | Context context = new Context(Address.fromString(address)); 21 | context.checkpointTimeout(checkpointTimeout); 22 | context.checkpointPayload(checkpointMsg); 23 | CoroutineRunner runner = new CoroutineRunner(cnt -> {}); 24 | Actor actor = new Actor(null, runner, context); 25 | 26 | return SerializableActor.serialize(actor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/offbynull/actors/70b56206bb7bd4188907466578f77dc6f369e7e4/logo.png -------------------------------------------------------------------------------- /nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CUSTOM-check_for_new_dependencies 5 | check_for_new_dependencies 6 | false 7 | 8 | versions:display-dependency-updates 9 | 10 | 11 | 12 | CUSTOM-check_for_new_plugins 13 | check_for_new_plugins 14 | false 15 | 16 | versions:display-plugin-updates 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /redis-storage/nb-configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | none 17 | 18 | 19 | -------------------------------------------------------------------------------- /redis-storage/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test 5 | 6 | * 7 | 8 | 9 | test 10 | coroutines:test-instrument 11 | 12 | 13 | 14 | test.single 15 | 16 | * 17 | 18 | 19 | test-compile 20 | coroutines:test-instrument 21 | surefire:test 22 | 23 | 24 | ${packageClassName} 25 | 26 | 27 | 28 | debug.test.single 29 | 30 | * 31 | 32 | 33 | test-compile 34 | coroutines:test-instrument 35 | surefire:test 36 | 37 | 38 | ${packageClassName} 39 | once 40 | -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} 41 | true 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /redis-storage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.offbynull.actors 6 | parent 7 | 1.0.0-SNAPSHOT 8 | 9 | redis-storage 10 | jar 11 | 12 | ${project.groupId}:${project.artifactId} 13 | 14 | 15 | ${project.groupId} 16 | core 17 | ${project.version} 18 | 19 | 20 | 21 | redis.clients 22 | jedis 23 | 24 | 25 | org.apache.commons 26 | commons-text 27 | 28 | 29 | 30 | junit 31 | junit 32 | 33 | 34 | org.slf4j 35 | slf4j-simple 36 | test 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-checkstyle-plugin 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-pmd-plugin 48 | 49 | 50 | com.github.spotbugs 51 | spotbugs-maven-plugin 52 | 53 | 54 | com.offbynull.coroutines 55 | maven-plugin 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/gateways/actor/stores/redis/ConversionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.actor.stores.redis; 18 | 19 | import static java.nio.charset.StandardCharsets.UTF_8; 20 | 21 | final class ConversionUtils { 22 | 23 | private ConversionUtils() { 24 | // do nothing 25 | } 26 | 27 | static String byteArrayToString(byte[] data) { 28 | return data == null ? null : new String(data, UTF_8); 29 | } 30 | 31 | static Integer stringToInt(byte[] data) { 32 | return data == null ? null : Integer.valueOf(new String(data, UTF_8)); 33 | } 34 | 35 | static Long stringToLong(byte[] data) { 36 | return data == null ? null : Long.valueOf(new String(data, UTF_8)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/gateways/actor/stores/redis/QueueCountController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.actor.stores.redis; 18 | 19 | import org.apache.commons.lang3.Validate; 20 | 21 | /** 22 | * Controls the number of timestamp queues. This count can be modified even after being passed into {@link RedisStore}. 23 | *

24 | * Increase to spread load over multiple queues in times of high-load, then decrease to reduce the number of queues back down. 25 | * @author Kasra Faghihi 26 | */ 27 | public final class QueueCountController { 28 | 29 | private volatile int count; 30 | 31 | /** 32 | * Constructs a {@link QueueCountController} object. 33 | * @param count initial number of queues 34 | * @throws IllegalArgumentException if {@code count <= 0} 35 | */ 36 | public QueueCountController(int count) { 37 | Validate.isTrue(count >= 1); 38 | this.count = count; 39 | } 40 | 41 | /** 42 | * Get queue count. 43 | * @return queue count 44 | */ 45 | public int getCount() { 46 | return count; 47 | } 48 | 49 | /** 50 | * Set queue count. 51 | * @param count queue count 52 | * @throws IllegalArgumentException if {@code count <= 0} 53 | */ 54 | public void setCount(int count) { 55 | Validate.isTrue(count >= 1); 56 | this.count = count; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/gateways/actor/stores/redis/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Redis actor storage engine implementation. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.actor.stores.redis; 24 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/gateways/servlet/stores/redis/ConversionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet.stores.redis; 18 | 19 | import static java.nio.charset.StandardCharsets.UTF_8; 20 | 21 | final class ConversionUtils { 22 | 23 | private ConversionUtils() { 24 | // do nothing 25 | } 26 | 27 | static Integer stringToInt(byte[] data) { 28 | return data == null ? null : Integer.valueOf(new String(data, UTF_8)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/gateways/servlet/stores/redis/QueueDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.gateways.servlet.stores.redis; 18 | 19 | import com.offbynull.actors.address.Address; 20 | import static com.offbynull.actors.redisclient.RedisUtils.toClusterKey; 21 | import org.apache.commons.lang3.Validate; 22 | 23 | final class QueueDetails { 24 | private static final String KEY_PREFIX = "servlet:"; 25 | 26 | private static final String IN_QUEUE_OFFSET_SUFFIX = ":in_offset"; 27 | private static final String IN_QUEUE_SUFFIX = ":in"; 28 | private static final String OUT_QUEUE_OFFSET_SUFFIX = ":out_offset"; 29 | private static final String OUT_QUEUE_SUFFIX = ":out"; 30 | 31 | private final String inQueueOffsetKey; 32 | private final String inQueueKey; 33 | private final String outQueueOffsetKey; 34 | private final String outQueueKey; 35 | private final long timeout; 36 | 37 | QueueDetails(Address address, long timeout) { 38 | Validate.notNull(address); 39 | Validate.isTrue(timeout >= 0L); 40 | 41 | this.outQueueOffsetKey = toClusterKey(KEY_PREFIX, address, OUT_QUEUE_OFFSET_SUFFIX); 42 | this.outQueueKey = toClusterKey(KEY_PREFIX, address, OUT_QUEUE_SUFFIX); 43 | this.inQueueOffsetKey = toClusterKey(KEY_PREFIX, address, IN_QUEUE_OFFSET_SUFFIX); 44 | this.inQueueKey = toClusterKey(KEY_PREFIX, address, IN_QUEUE_SUFFIX); 45 | this.timeout = timeout; 46 | } 47 | 48 | public String getInQueueOffsetKey() { 49 | return inQueueOffsetKey; 50 | } 51 | 52 | public String getInQueueKey() { 53 | return inQueueKey; 54 | } 55 | 56 | public String getOutQueueOffsetKey() { 57 | return outQueueOffsetKey; 58 | } 59 | 60 | public String getOutQueueKey() { 61 | return outQueueKey; 62 | } 63 | 64 | public long getTimeout() { 65 | return timeout; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/gateways/servlet/stores/redis/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Redis servlet storage engine implementation. 20 | * 21 | * @author Kasra Faghihi 22 | */ 23 | package com.offbynull.actors.gateways.servlet.stores.redis; 24 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/ConnectionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | /** 20 | * A low-level Redis connection exception. 21 | * @author Kasra Faghihi 22 | */ 23 | public final class ConnectionException extends Exception { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final boolean connectionProblem; 27 | 28 | /** 29 | * Constructs a {@link ConnectionException} object. 30 | * @param connectionProblem {@code true} if this exception was caused by connection issues, {@code false} otherwise 31 | * @param cause cause of this exception 32 | */ 33 | public ConnectionException(boolean connectionProblem, Throwable cause) { 34 | super(cause); 35 | this.connectionProblem = connectionProblem; 36 | } 37 | 38 | /** 39 | * Gets the connection problem flag. 40 | * @return {@code true} if this exception was caused by connection issues 41 | */ 42 | public boolean isConnectionProblem() { 43 | return connectionProblem; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/Connector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | import java.io.Closeable; 20 | 21 | /** 22 | * A low-level Redis connector. 23 | *

24 | * Implementations may or may not be bound to to this connector. That means that if this {@link Connector} is closed, the generated 25 | * {@link Connection}s is may also be closed. 26 | *

27 | * Implementations must be thread-safe. 28 | * @author Kasra Faghihi 29 | */ 30 | public interface Connector extends Closeable { 31 | /** 32 | * Get a {@link Connection}. 33 | * @return connection 34 | * @throws IllegalStateException if closed 35 | */ 36 | Connection getConnection(); 37 | } 38 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/SortedSetItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | /** 20 | * Item for a Redis sorted set (score and item). 21 | * @author Kasra Faghihi 22 | */ 23 | public final class SortedSetItem { 24 | 25 | private final double score; 26 | private final Object item; 27 | 28 | /** 29 | * Constructs a {@link SortedSetItem} object. 30 | * @param score score 31 | * @param item item 32 | */ 33 | public SortedSetItem(double score, Object item) { 34 | this.score = score; 35 | this.item = item; 36 | } 37 | 38 | /** 39 | * Get score. 40 | * @return score 41 | */ 42 | public double getScore() { 43 | return score; 44 | } 45 | 46 | /** 47 | * Get item. 48 | * @param expected type 49 | * @return item 50 | * @throws ClassCastException if expected type is not the type stored 51 | */ 52 | public T getItem() { 53 | return (T) item; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/Transaction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | import org.apache.commons.lang3.Validate; 20 | 21 | /** 22 | * Redis MULTI/EXEC transaction operation. 23 | * @author Kasra Faghihi 24 | */ 25 | public final class Transaction { 26 | 27 | private final TransactionBlock block; 28 | private final boolean retry; 29 | 30 | /** 31 | * Constructs a {@link Transaction} object. 32 | * @param retry if {@code true}, transaction will be retried until it passes 33 | * @param block block that defines queued transaction operations 34 | * @throws NullPointerException if any argument is {@code null} 35 | */ 36 | public Transaction(boolean retry, TransactionBlock block) { 37 | Validate.notNull(block); 38 | this.retry = retry; 39 | this.block = block; 40 | } 41 | 42 | /** 43 | * Get retry flag. 44 | * @return if {@code true}, transaction will be retried until it passes 45 | */ 46 | public boolean isRetry() { 47 | return retry; 48 | } 49 | 50 | /** 51 | * Get transaction block. 52 | * @return block that defines queued transaction operations 53 | */ 54 | public TransactionBlock getBlock() { 55 | return block; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/TransactionBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | /** 20 | * Redis operations to perform inside a MULTI/EXEC block. 21 | * @author Kasra Faghihi 22 | */ 23 | public interface TransactionBlock { 24 | /** 25 | * Queues Redis operations to perform in a MULTI/EXEC block. 26 | * @param queue transaction queue 27 | * @throws ConnectionException if there was a problem with redis or the connection to redis 28 | * @throws NullPointerException if any argument is {@code null} 29 | * @throws IllegalStateException if connector closed 30 | */ 31 | void execute(TransactionQueue queue) throws ConnectionException; 32 | } 33 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/TransactionResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import org.apache.commons.lang3.Validate; 22 | 23 | /** 24 | * Results for each operation performed on a {@link TransactionQueue}. 25 | * @author Kasra Faghihi 26 | */ 27 | public final class TransactionResult { 28 | private final List result; 29 | 30 | /** 31 | * Constructs a {@link TransactionResult} object. 32 | * @param result results (can contain elements that are {@code null}) 33 | * @throws NullPointerException if any argument is {@code null} 34 | */ 35 | public TransactionResult(List result) { 36 | Validate.notNull(result); 37 | this.result = new ArrayList<>(result); 38 | } 39 | 40 | /** 41 | * Get result for a queued command. 42 | * @param expected time 43 | * @param idx index of command 44 | * @throws IllegalArgumentException if {@code idx < 0 || idx >= size()} 45 | * @return result result for index at command {@code idx} 46 | */ 47 | public T get(int idx) { 48 | Validate.isTrue(idx >= 0 && idx < result.size()); 49 | return (T) result.get(idx); 50 | } 51 | 52 | /** 53 | * Get number of results. 54 | * @return number of results 55 | */ 56 | public int size() { 57 | return result.size(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/Watch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | import org.apache.commons.lang3.Validate; 20 | 21 | /** 22 | * Redis WATCH operation. 23 | * @author Kasra Faghihi 24 | */ 25 | public final class Watch { 26 | 27 | private final String key; 28 | private final boolean retry; 29 | private final WatchBlock block; 30 | 31 | /** 32 | * Constructs a {@link Watch} object. 33 | * @param key key to watch 34 | * @param retry if {@code true}, watch condition will be retried until it passes 35 | * @param block block that defines watch conditions 36 | * @throws NullPointerException if any argument is {@code null} 37 | */ 38 | public Watch(String key, boolean retry, WatchBlock block) { 39 | Validate.notNull(key); 40 | Validate.notNull(block); 41 | this.key = key; 42 | this.retry = retry; 43 | this.block = block; 44 | } 45 | 46 | /** 47 | * Get key. 48 | * @return key to watch. 49 | */ 50 | public String getKey() { 51 | return key; 52 | } 53 | 54 | /** 55 | * Get retry flag. 56 | * @return if {@code true}, watch condition will be retried until it passes 57 | */ 58 | public boolean getRetry() { 59 | return retry; 60 | } 61 | 62 | /** 63 | * Get watch condition block. 64 | * @return block that defines watch conditions 65 | */ 66 | public WatchBlock getBlock() { 67 | return block; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/WatchBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclient; 18 | 19 | /** 20 | * Block that defines watch conditions. 21 | * @author Kasra Faghihi 22 | */ 23 | public interface WatchBlock { 24 | /** 25 | * Performs Redis operations to check that certain conditions are met. 26 | * @return if conditions were met 27 | * @throws ConnectionException if there was a problem with redis or the connection to redis 28 | * @throws IllegalStateException if connector closed 29 | */ 30 | boolean execute() throws ConnectionException; 31 | } 32 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclient/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Provides classes and interfaces for defining low-level Redis connectors. 20 | * @author Kasra Faghihi 21 | */ 22 | package com.offbynull.actors.redisclient; -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/jedis/JedisConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclients.jedis; 18 | 19 | import java.io.IOException; 20 | import org.apache.commons.lang3.Validate; 21 | import redis.clients.jedis.Jedis; 22 | import com.offbynull.actors.redisclient.Connection; 23 | import com.offbynull.actors.redisclient.Connector; 24 | import java.util.WeakHashMap; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | import org.apache.commons.io.IOUtils; 27 | 28 | /** 29 | * Jedis connector. This class is thread-safe. 30 | *

31 | * All {@link Connection} objects derived from this {@link Connector} point to the same Redis server. If this {@link Connector} is closed, 32 | * the generated {@link Connection}s are also closed. 33 | * @author Kasra Faghihi 34 | */ 35 | public final class JedisConnector implements Connector { 36 | 37 | private final String host; 38 | private final int port; 39 | 40 | private final WeakHashMap conns; 41 | private final AtomicBoolean closed; 42 | 43 | /** 44 | * Constructs a {@link JedisConnector} object. 45 | * @param host redis host 46 | * @param port redis port 47 | * @throws NullPointerException if any argument is {@code null} 48 | * @throws IllegalArgumentException if {@code port} is an invalid port number 49 | */ 50 | public JedisConnector(String host, int port) { 51 | Validate.notNull(host); 52 | Validate.isTrue(port > 0 && port <= 65535); 53 | this.host = host; 54 | this.port = port; 55 | this.conns = new WeakHashMap<>(); 56 | this.closed = new AtomicBoolean(); 57 | } 58 | 59 | @Override 60 | public Connection getConnection() { 61 | Validate.validState(!closed.get(), "Closed"); 62 | 63 | Jedis jedis = new Jedis(host, port); 64 | JedisConnection conn = new JedisConnection(jedis, closed); 65 | 66 | conns.put(conn, null); 67 | 68 | return conn; 69 | } 70 | 71 | @Override 72 | public void close() throws IOException { 73 | closed.set(true); 74 | conns.keySet().forEach(jc -> IOUtils.closeQuietly(jc)); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/jedis/JedisPoolConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclients.jedis; 18 | 19 | import java.io.IOException; 20 | import org.apache.commons.lang3.Validate; 21 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 22 | import redis.clients.jedis.Jedis; 23 | import redis.clients.jedis.JedisPool; 24 | import com.offbynull.actors.redisclient.Connection; 25 | import com.offbynull.actors.redisclient.Connector; 26 | import java.util.concurrent.atomic.AtomicBoolean; 27 | 28 | /** 29 | * Jedis connector backed by a {@link JedisPool}. This class is thread-safe. 30 | *

31 | * All {@link Connection} objects derived from this {@link Connector} point to the same Redis server. If this {@link Connector} is closed, 32 | * any generated {@link Connection}s are also closed. 33 | * @author Kasra Faghihi 34 | */ 35 | public final class JedisPoolConnector implements Connector { 36 | 37 | private final JedisPool pool; 38 | private final AtomicBoolean closed; 39 | 40 | /** 41 | * Constructs a {@link JedisConnector} object. 42 | * @param host redis host 43 | * @param port redis port 44 | * @param idleCount number of idle connections to keep pooled 45 | * @throws NullPointerException if any argument is {@code null} 46 | * @throws IllegalArgumentException if {@code idleCount} is negative or {@code port} is an invalid port number 47 | */ 48 | public JedisPoolConnector(String host, int port, int idleCount) { 49 | Validate.notNull(host); 50 | Validate.isTrue(port > 0 && port <= 65535); 51 | Validate.isTrue(idleCount >= 0); 52 | GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 53 | poolConfig.setMinIdle(idleCount); 54 | poolConfig.setMaxIdle(idleCount); 55 | poolConfig.setMaxTotal(Integer.MAX_VALUE); 56 | poolConfig.setMaxWaitMillis(-1L); 57 | poolConfig.setMinEvictableIdleTimeMillis(60 * 1000L); 58 | poolConfig.setTestOnBorrow(true); 59 | poolConfig.setBlockWhenExhausted(true); 60 | poolConfig.setFairness(false); 61 | poolConfig.setNumTestsPerEvictionRun(1); 62 | 63 | this.pool = new JedisPool(poolConfig, host, port); 64 | this.closed = new AtomicBoolean(); 65 | } 66 | 67 | @Override 68 | public Connection getConnection() { 69 | Validate.validState(!closed.get(), "Closed"); 70 | 71 | Jedis jedis = pool.getResource(); // throws illegalstateexc if closed 72 | 73 | Connection client = new JedisConnection(jedis, closed); 74 | return client; 75 | } 76 | 77 | @Override 78 | public void close() throws IOException { 79 | closed.set(true); 80 | pool.close(); // closes all connections generated from this pool 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/jedis/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Redis connector that uses Jedis. 20 | * @author Kasra Faghihi 21 | */ 22 | package com.offbynull.actors.redisclients.jedis; -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/test/InternalList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclients.test; 18 | 19 | import java.util.LinkedList; 20 | import java.util.List; 21 | 22 | final class InternalList { 23 | private final LinkedList items = new LinkedList<>(); 24 | 25 | byte[] rpop() { 26 | return items.removeLast(); 27 | } 28 | 29 | byte[] lpop() { 30 | return items.removeFirst(); 31 | } 32 | 33 | void rpush(byte[] e) { 34 | items.addLast(e); 35 | } 36 | 37 | void lpush(byte[] e) { 38 | items.addFirst(e); 39 | } 40 | 41 | List lrange(int start, int end) { 42 | int revisedStart = start; 43 | if (revisedStart >= items.size()) { 44 | revisedStart = items.size() - 1; 45 | } 46 | 47 | int revisedEnd = end; 48 | if (revisedEnd >= items.size()) { 49 | revisedEnd = items.size() - 1; 50 | } 51 | 52 | return new LinkedList<>(items.subList(revisedStart, revisedEnd + 1)); 53 | } 54 | 55 | int size() { 56 | return items.size(); 57 | } 58 | 59 | boolean isEmpty() { 60 | return items.isEmpty(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/test/InternalSortedSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclients.test; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.HashSet; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | import java.util.Map.Entry; 25 | import java.util.Set; 26 | import java.util.TreeMap; 27 | 28 | final class InternalSortedSet { 29 | private final HashSet items = new HashSet<>(); 30 | private final TreeMap> sortedItems = new TreeMap<>(); 31 | 32 | void put(double score, byte[] data) { 33 | Item item = new Item(score, data); 34 | items.add(item); 35 | 36 | HashSet innerItems = sortedItems.computeIfAbsent(score, k -> new HashSet<>()); 37 | innerItems.add(item); 38 | } 39 | 40 | List getByRank(long start, long end) { 41 | List ret = new ArrayList<>(); 42 | 43 | long counter = 0; 44 | top: 45 | for (Entry> entry : sortedItems.entrySet()) { 46 | for (Item item : entry.getValue()) { 47 | if (counter < start) { 48 | continue; 49 | } 50 | 51 | ret.add(item); 52 | 53 | counter++; 54 | 55 | if (counter > end) { 56 | break top; 57 | } 58 | } 59 | } 60 | 61 | return ret; 62 | } 63 | 64 | void removeByRank(long start, long end) { 65 | long counter = 0; 66 | Set scoresToRemove = new HashSet<>(); 67 | top: 68 | for (Entry> entry : sortedItems.entrySet()) { 69 | Double score = entry.getKey(); 70 | HashSet scoreItems = entry.getValue(); 71 | 72 | Iterator it = scoreItems.iterator(); 73 | while (it.hasNext()) { 74 | Item item = it.next(); 75 | 76 | if (counter < start) { 77 | continue; 78 | } 79 | 80 | items.remove(item); 81 | it.remove(); 82 | 83 | counter++; 84 | 85 | if (counter > end) { 86 | if (scoreItems.isEmpty()) { 87 | scoresToRemove.add(score); 88 | } 89 | break top; 90 | } 91 | } 92 | if (scoreItems.isEmpty()) { 93 | scoresToRemove.add(score); 94 | } 95 | } 96 | 97 | sortedItems.keySet().removeAll(scoresToRemove); 98 | } 99 | 100 | boolean isEmpty() { 101 | return items.isEmpty(); 102 | } 103 | 104 | 105 | static final class Item { 106 | private final Double score; 107 | private final byte[] data; 108 | 109 | Item(Double score, byte[] data) { 110 | this.score = score; 111 | this.data = data; 112 | } 113 | 114 | public Double getScore() { 115 | return score; 116 | } 117 | 118 | public byte[] getData() { 119 | return data; 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | int hash = 7; 125 | hash = 31 * hash + Arrays.hashCode(this.data); 126 | return hash; 127 | } 128 | 129 | @Override 130 | public boolean equals(Object obj) { 131 | if (this == obj) { 132 | return true; 133 | } 134 | if (obj == null) { 135 | return false; 136 | } 137 | if (getClass() != obj.getClass()) { 138 | return false; 139 | } 140 | final Item other = (Item) obj; 141 | if (!Arrays.equals(this.data, other.data)) { 142 | return false; 143 | } 144 | return true; 145 | } 146 | 147 | 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/test/TestConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | package com.offbynull.actors.redisclients.test; 18 | 19 | import com.offbynull.actors.redisclient.Connection; 20 | import com.offbynull.actors.redisclient.Connector; 21 | import com.offbynull.actors.redisclients.test.TestConnection.Item; 22 | import java.io.IOException; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | import java.util.concurrent.atomic.AtomicBoolean; 26 | import org.apache.commons.lang3.Validate; 27 | 28 | /** 29 | * Test connector. This class is thread-safe. 30 | *

31 | * All {@link Connection} objects derived from this {@link Connector} point to the same database. If this {@link Connector} is closed, any 32 | * generated {@link Connection}s are also closed. 33 | * @author Kasra Faghihi 34 | */ 35 | public final class TestConnector implements Connector { 36 | 37 | private final Map keyspace = new HashMap<>(); 38 | private final AtomicBoolean closed = new AtomicBoolean(); 39 | 40 | @Override 41 | public Connection getConnection() { 42 | Validate.validState(!closed.get(), "Closed"); 43 | return new TestConnection(keyspace, closed); 44 | } 45 | 46 | @Override 47 | public void close() throws IOException { 48 | closed.set(true); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /redis-storage/src/main/java/com/offbynull/actors/redisclients/test/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017, Kasra Faghihi, All rights reserved. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3.0 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library. 16 | */ 17 | 18 | /** 19 | * Redis connector that mocks a local Redis server. This is mainly used for testing higher-level classes. 20 | * @author Kasra Faghihi 21 | */ 22 | package com.offbynull.actors.redisclients.test; -------------------------------------------------------------------------------- /redis-storage/src/test/java/com/offbynull/actors/ActorSystemTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors; 2 | 3 | import com.offbynull.actors.gateways.actor.Context; 4 | import com.offbynull.actors.gateways.actor.stores.redis.RedisStore; 5 | import com.offbynull.actors.gateways.actor.stores.redis.QueueCountController; 6 | import com.offbynull.actors.redisclient.Connector; 7 | import com.offbynull.actors.redisclients.test.TestConnector; 8 | import com.offbynull.coroutines.user.Coroutine; 9 | import org.junit.After; 10 | import static org.junit.Assert.assertEquals; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public class ActorSystemTest { 15 | 16 | private ActorSystem actorSystem; 17 | 18 | @Before 19 | public void before() { 20 | // try (Jedis jedis = new Jedis("192.168.56.101", 6379)) { 21 | // jedis.flushDB(); 22 | // } 23 | // Connector connector = new JedisPoolConnector("192.168.56.101", 6379, 1); 24 | 25 | Connector connector = new TestConnector(); 26 | RedisStore store = RedisStore.create("actor", connector, new QueueCountController(1), new QueueCountController(1)); 27 | 28 | actorSystem = ActorSystem.builder() 29 | .withDirectGateway() 30 | .withActorGateway(1, store) 31 | .build(); 32 | } 33 | 34 | @After 35 | public void after() throws Exception { 36 | actorSystem.close(); 37 | actorSystem.join(); 38 | } 39 | 40 | @Test(timeout = 5000L) 41 | public void mustCreateAndCommunicateActorsAndGateways() throws Exception { 42 | Coroutine echoer = cnt -> { 43 | Context ctx = (Context) cnt.getContext(); 44 | ctx.allow(); 45 | 46 | // tell test that echoer is ready 47 | ctx.out("direct:test", "echoer_ready"); 48 | cnt.suspend(); 49 | 50 | ctx.out("actor:sender", ctx.in()); 51 | }; 52 | 53 | Coroutine sender = cnt -> { 54 | Context ctx = (Context) cnt.getContext(); 55 | ctx.allow(); 56 | 57 | // tell test that sender is ready 58 | ctx.out("direct:test", "sender_ready"); 59 | cnt.suspend(); 60 | 61 | // next message should be "go", telling use to send a message to the echoer, wait for its response, and forward it back to the 62 | // direct gateway 63 | ctx.out("actor:echoer", "echo_msg"); // send msg to get echoer 64 | cnt.suspend(); 65 | String response = ctx.in(); // get msg from echoer and forward it to direct gateway 66 | ctx.out("direct:test", response); 67 | }; 68 | 69 | 70 | actorSystem.getDirectGateway().listen("direct:test"); 71 | Object directMsg; 72 | 73 | // start echoer 74 | actorSystem.getActorGateway().addActor("echoer", echoer, new Object()); 75 | directMsg = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 76 | assertEquals("echoer_ready", directMsg); 77 | 78 | // start sender 79 | actorSystem.getActorGateway().addActor("sender", sender, new Object()); 80 | directMsg = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 81 | assertEquals("sender_ready", directMsg); 82 | 83 | // tell sender to send 84 | actorSystem.getDirectGateway().writeMessage("actor:sender", "go"); 85 | directMsg = actorSystem.getDirectGateway().readMessagePayloadOnly("direct:test"); 86 | assertEquals("echo_msg", directMsg); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /redis-storage/src/test/java/com/offbynull/actors/gateways/actor/SerializableActorHelper.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor; 2 | 3 | import com.offbynull.actors.address.Address; 4 | import com.offbynull.coroutines.user.CoroutineRunner; 5 | 6 | public final class SerializableActorHelper { 7 | private SerializableActorHelper() { 8 | // do nothing 9 | } 10 | 11 | public static SerializableActor createFake(String address) { 12 | Context context = new Context(Address.fromString(address)); 13 | CoroutineRunner runner = new CoroutineRunner(cnt -> {}); 14 | Actor actor = new Actor(null, runner, context); 15 | 16 | return SerializableActor.serialize(actor); 17 | } 18 | 19 | public static SerializableActor createFake(String address, Object checkpointMsg, long checkpointTimeout) { 20 | Context context = new Context(Address.fromString(address)); 21 | context.checkpointTimeout(checkpointTimeout); 22 | context.checkpointPayload(checkpointMsg); 23 | CoroutineRunner runner = new CoroutineRunner(cnt -> {}); 24 | Actor actor = new Actor(null, runner, context); 25 | 26 | return SerializableActor.serialize(actor); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /redis-storage/src/test/java/com/offbynull/actors/gateways/actor/stores/redis/ActorAccessorTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor.stores.redis; 2 | 3 | import com.offbynull.actors.gateways.actor.stores.redis.ActorAccessor.Work; 4 | import com.offbynull.actors.redisclient.Connection; 5 | import static com.offbynull.actors.address.Address.fromString; 6 | import com.offbynull.actors.redisclient.Connector; 7 | import com.offbynull.actors.redisclients.test.TestConnector; 8 | import org.junit.After; 9 | import static org.junit.Assert.assertArrayEquals; 10 | import static org.junit.Assert.assertNull; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public final class ActorAccessorTest { 15 | 16 | private Connector connector; 17 | private Connection connection; 18 | 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | // connector = new JedisPoolConnector("192.168.56.101", 6379, 1); 23 | // clientFactory = new ConnectorClientFactory(connector); 24 | connector = new TestConnector(); 25 | connection = connector.getConnection(); 26 | } 27 | 28 | @After 29 | public void tearDown() throws Exception { 30 | connection.close(); 31 | connector.close(); 32 | } 33 | 34 | @Test 35 | public void mustProperlyWriteAndReadMessages() throws Exception { 36 | byte[] actorData = new byte[] { 1, 2, 3 }; 37 | byte[] msgData1 = new byte[] { 3, 4, 5 }; 38 | byte[] msgData2 = new byte[] { 5, 6, 7 }; 39 | byte[] msgData3 = new byte[] { 8, 9, 10 }; 40 | 41 | ActorAccessor raa = new ActorAccessor(connection, fromString("test1:a")); 42 | raa.remove(); 43 | 44 | raa.update(actorData, null, -1L, 0); 45 | raa.putMessage(msgData1); 46 | raa.putMessage(msgData2); 47 | 48 | ActorAccessor.Work pm; 49 | pm = raa.nextMessage(); 50 | assertArrayEquals(msgData1, pm.getMessageData()); 51 | assertArrayEquals(actorData, pm.getActorData()); 52 | raa.putMessage(msgData3); 53 | 54 | pm = raa.nextMessage(); 55 | assertNull(pm); 56 | 57 | raa.update(actorData, null, -1L, 0); 58 | pm = raa.nextMessage(); 59 | assertArrayEquals(msgData2, pm.getMessageData()); 60 | assertArrayEquals(actorData, pm.getActorData()); 61 | 62 | raa.remove(); 63 | } 64 | 65 | @Test 66 | public void mustProperlyHitCheckpoint() throws Exception { 67 | byte[] actorData1 = new byte[] { 1, 2, 3 }; 68 | byte[] actorData2 = new byte[] { 3, 4, 5 }; 69 | byte[] checkpointMsg = new byte[] { 6, 7, 8 }; 70 | 71 | ActorAccessor raa = new ActorAccessor(connection, fromString("test1:b")); 72 | raa.remove(); 73 | 74 | raa.update(actorData1, checkpointMsg, 0L, 0); 75 | raa.update(actorData2, null, -1, 0); 76 | 77 | Work pm; 78 | 79 | pm = raa.nextMessage(); 80 | assertNull(pm); 81 | 82 | pm = raa.checkpointMessage(0L); 83 | assertArrayEquals(checkpointMsg, pm.getMessageData()); 84 | assertArrayEquals(actorData1, pm.getActorData()); 85 | 86 | pm = raa.nextMessage(); 87 | assertNull(pm); 88 | 89 | raa.remove(); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /redis-storage/src/test/java/com/offbynull/actors/gateways/actor/stores/redis/TimestampQueueTest.java: -------------------------------------------------------------------------------- 1 | package com.offbynull.actors.gateways.actor.stores.redis; 2 | 3 | import com.offbynull.actors.redisclient.Connection; 4 | import static com.offbynull.actors.address.Address.fromString; 5 | import com.offbynull.actors.redisclient.Connector; 6 | import com.offbynull.actors.redisclients.test.TestConnector; 7 | import org.junit.After; 8 | import static org.junit.Assert.assertArrayEquals; 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertNull; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public final class TimestampQueueTest { 15 | 16 | private Connector connector; 17 | private Connection connection; 18 | 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | // connector = new JedisPoolConnector("192.168.56.101", 6379, 1); 23 | // clientFactory = new ConnectorClientFactory(connector); 24 | connector = new TestConnector(); 25 | connection = connector.getConnection(); 26 | } 27 | 28 | @After 29 | public void tearDown() throws Exception { 30 | connection.close(); 31 | connector.close(); 32 | } 33 | 34 | @Test 35 | public void mustProperlyPullFromTimestampQueue() throws Exception { 36 | TimestampQueue rtq = new TimestampQueue(connection, "test", 1); 37 | 38 | rtq.insert(0, fromString("test1:test2:a")); 39 | rtq.insert(5, fromString("test1:test2:d")); 40 | rtq.insert(2, fromString("test1:test2:c")); 41 | rtq.insert(1, fromString("test1:test2:b")); 42 | 43 | assertEquals(fromString("test1:test2:a"), rtq.remove(10L)); 44 | assertEquals(fromString("test1:test2:b"), rtq.remove(10L)); 45 | assertEquals(fromString("test1:test2:c"), rtq.remove(10L)); 46 | assertEquals(fromString("test1:test2:d"), rtq.remove(10L)); 47 | assertNull(rtq.remove(10L)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /servlet-gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.offbynull.actors 6 | parent 7 | 1.0.0-SNAPSHOT 8 | 9 | servlet-gateway 10 | jar 11 | 12 | ${project.groupId}:${project.artifactId} 13 | 14 | 15 | ${project.groupId} 16 | core 17 | ${project.version} 18 | 19 | 20 | com.google.code.gson 21 | gson 22 | 2.8.0 23 | 24 | 25 | javax.servlet 26 | javax.servlet-api 27 | 3.1.0 28 | jar 29 | 30 | 31 | junit 32 | junit 33 | 34 | 35 | org.mockito 36 | mockito-all 37 | 38 | 39 | --------------------------------------------------------------------------------