Merge with latest changes on akka/master and simplify OSGi configuration section
Conflicts: project/AkkaBuild.scala
This commit is contained in:
commit
e24f7077ec
264 changed files with 9794 additions and 3334 deletions
Binary file not shown.
|
|
@ -334,3 +334,10 @@ same machine at the same time.
|
|||
The machines that are used for testing (slaves) should have ssh access to the outside world and be able to talk
|
||||
to each other with the internal addresses given. On the master machine ssh client is required. Obviosly git
|
||||
and sbt should be installed on both master and slave machines.
|
||||
|
||||
The Test Conductor Extension
|
||||
============================
|
||||
|
||||
The Test Conductor Extension is aimed at enhancing the multi JVM and multi node testing facilities.
|
||||
|
||||
.. image:: ../images/akka-remote-testconductor.png
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ which means that we need not concern ourselves with their emotional state or
|
|||
moral issues). The result can then serve as a mental scaffolding for building
|
||||
the software implementation.
|
||||
|
||||
.. note::
|
||||
|
||||
An ActorSystem is a heavyweight structure that will allocate 1…N Threads,
|
||||
so create one per logical application.
|
||||
|
||||
Hierarchical Structure
|
||||
----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.config;
|
||||
package docs.config;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import com.typesafe.config.*;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.config
|
||||
package docs.config
|
||||
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
BIN
akka-docs/images/akka-remote-testconductor.png
Normal file
BIN
akka-docs/images/akka-remote-testconductor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -1,109 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.serialization;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
//#imports
|
||||
import akka.actor.*;
|
||||
import akka.serialization.*;
|
||||
import com.typesafe.config.*;
|
||||
|
||||
//#imports
|
||||
|
||||
public class SerializationDocTestBase {
|
||||
//#my-own-serializer
|
||||
public static class MyOwnSerializer extends JSerializer {
|
||||
|
||||
// This is whether "fromBinary" requires a "clazz" or not
|
||||
@Override public boolean includeManifest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pick a unique identifier for your Serializer,
|
||||
// you've got a couple of billions to choose from,
|
||||
// 0 - 16 is reserved by Akka itself
|
||||
@Override public int identifier() {
|
||||
return 1234567;
|
||||
}
|
||||
|
||||
// "toBinary" serializes the given object to an Array of Bytes
|
||||
@Override public byte[] toBinary(Object obj) {
|
||||
// Put the code that serializes the object here
|
||||
//#...
|
||||
return new byte[0];
|
||||
//#...
|
||||
}
|
||||
|
||||
// "fromBinary" deserializes the given array,
|
||||
// using the type hint (if any, see "includeManifest" above)
|
||||
@Override public Object fromBinaryJava(byte[] bytes,
|
||||
Class<?> clazz) {
|
||||
// Put your code that deserializes here
|
||||
//#...
|
||||
return null;
|
||||
//#...
|
||||
}
|
||||
}
|
||||
//#my-own-serializer
|
||||
|
||||
@Test public void serializeActorRefs() {
|
||||
final ActorSystem theActorSystem =
|
||||
ActorSystem.create("whatever");
|
||||
final ActorRef theActorRef =
|
||||
theActorSystem.deadLetters(); // Of course this should be you
|
||||
|
||||
//#actorref-serializer
|
||||
// Serialize
|
||||
// (beneath toBinary)
|
||||
final Address transportAddress =
|
||||
Serialization.currentTransportAddress().value();
|
||||
String identifier;
|
||||
|
||||
// If there is no transportAddress,
|
||||
// it means that either this Serializer isn't called
|
||||
// within a piece of code that sets it,
|
||||
// so either you need to supply your own,
|
||||
// or simply use the local path.
|
||||
if (transportAddress == null) identifier = theActorRef.path().toString();
|
||||
else identifier = theActorRef.path().toStringWithAddress(transportAddress);
|
||||
// Then just serialize the identifier however you like
|
||||
|
||||
|
||||
// Deserialize
|
||||
// (beneath fromBinary)
|
||||
final ActorRef deserializedActorRef = theActorSystem.actorFor(identifier);
|
||||
// Then just use the ActorRef
|
||||
//#actorref-serializer
|
||||
theActorSystem.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@Test public void demonstrateTheProgrammaticAPI() {
|
||||
//#programmatic
|
||||
ActorSystem system = ActorSystem.create("example");
|
||||
|
||||
// Get the Serialization Extension
|
||||
Serialization serialization = SerializationExtension.get(system);
|
||||
|
||||
// Have something to serialize
|
||||
String original = "woohoo";
|
||||
|
||||
// Find the Serializer for it
|
||||
Serializer serializer = serialization.findSerializerFor(original);
|
||||
|
||||
// Turn it into bytes
|
||||
byte[] bytes = serializer.toBinary(original);
|
||||
|
||||
// Turn it back into an object,
|
||||
// the nulls are for the class manifest and for the classloader
|
||||
String back = (String)serializer.fromBinary(bytes);
|
||||
|
||||
// Voilá!
|
||||
assertEquals(original, back);
|
||||
|
||||
//#programmatic
|
||||
system.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#imports-data
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class FaultHandlingTest extends FaultHandlingTestBase with JUnitSuite
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#testkit
|
||||
import akka.actor.ActorRef;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#receive-timeout
|
||||
import akka.actor.ReceiveTimeout;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#my-untyped-actor
|
||||
import akka.actor.UntypedActor;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#imports1
|
||||
import akka.actor.Props;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#imports
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
//#imports
|
||||
import akka.actor.ActorRef;
|
||||
|
|
@ -36,7 +36,7 @@ import static akka.pattern.Patterns.gracefulStop;
|
|||
import akka.dispatch.Future;
|
||||
import akka.dispatch.Await;
|
||||
import akka.util.Duration;
|
||||
import akka.actor.ActorTimeoutException;
|
||||
import akka.pattern.AskTimeoutException;
|
||||
//#import-gracefulStop
|
||||
|
||||
//#import-askPipe
|
||||
|
|
@ -207,7 +207,7 @@ public class UntypedActorDocTestBase {
|
|||
Future<Boolean> stopped = gracefulStop(actorRef, Duration.create(5, TimeUnit.SECONDS), system);
|
||||
Await.result(stopped, Duration.create(6, TimeUnit.SECONDS));
|
||||
// the actor has been stopped
|
||||
} catch (ActorTimeoutException e) {
|
||||
} catch (AskTimeoutException e) {
|
||||
// the actor wasn't stopped within 5 seconds
|
||||
}
|
||||
//#gracefulStop
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor;
|
||||
package docs.actor;
|
||||
|
||||
import static akka.docs.actor.UntypedActorSwapper.Swap.SWAP;
|
||||
import static docs.actor.UntypedActorSwapper.Swap.SWAP;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.ActorSystem;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor.japi;
|
||||
package docs.actor.japi;
|
||||
|
||||
//#all
|
||||
//#imports
|
||||
|
|
@ -26,10 +26,10 @@ import static akka.actor.SupervisorStrategy.*;
|
|||
import static akka.pattern.Patterns.ask;
|
||||
import static akka.pattern.Patterns.pipe;
|
||||
|
||||
import static akka.docs.actor.japi.FaultHandlingDocSample.WorkerApi.*;
|
||||
import static akka.docs.actor.japi.FaultHandlingDocSample.CounterServiceApi.*;
|
||||
import static akka.docs.actor.japi.FaultHandlingDocSample.CounterApi.*;
|
||||
import static akka.docs.actor.japi.FaultHandlingDocSample.StorageApi.*;
|
||||
import static docs.actor.japi.FaultHandlingDocSample.WorkerApi.*;
|
||||
import static docs.actor.japi.FaultHandlingDocSample.CounterServiceApi.*;
|
||||
import static docs.actor.japi.FaultHandlingDocSample.CounterApi.*;
|
||||
import static docs.actor.japi.FaultHandlingDocSample.StorageApi.*;
|
||||
|
||||
//#imports
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.agent
|
||||
package docs.agent
|
||||
|
||||
import org.scalatest.junit.JUnitWrapperSuite
|
||||
|
||||
class AgentDocJavaSpec extends JUnitWrapperSuite(
|
||||
"akka.docs.agent.AgentDocTest",
|
||||
"docs.agent.AgentDocTest",
|
||||
Thread.currentThread.getContextClassLoader)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.agent;
|
||||
package docs.agent;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.dispatcher
|
||||
package docs.dispatcher
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.dispatcher;
|
||||
package docs.dispatcher;
|
||||
|
||||
//#imports
|
||||
import akka.actor.*;
|
||||
|
|
@ -32,8 +32,8 @@ import static org.junit.Assert.*;
|
|||
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
|
||||
import akka.docs.actor.MyUntypedActor;
|
||||
import akka.docs.actor.UntypedActorDocTestBase.MyActor;
|
||||
import docs.actor.MyUntypedActor;
|
||||
import docs.actor.UntypedActorDocTestBase.MyActor;
|
||||
import akka.testkit.AkkaSpec;
|
||||
|
||||
public class DispatcherDocTestBase {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.event
|
||||
package docs.event
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.event;
|
||||
package docs.event;
|
||||
|
||||
//#imports
|
||||
import akka.event.Logging;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension
|
||||
package docs.extension
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension;
|
||||
package docs.extension;
|
||||
|
||||
//#imports
|
||||
import akka.actor.*;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension
|
||||
package docs.extension
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension;
|
||||
package docs.extension;
|
||||
|
||||
//#imports
|
||||
import akka.actor.Extension;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.future
|
||||
package docs.future
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.future;
|
||||
package docs.future;
|
||||
|
||||
//#imports1
|
||||
import akka.dispatch.*;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -22,9 +22,9 @@ import akka.testkit.AkkaSpec;
|
|||
import com.typesafe.config.ConfigFactory;
|
||||
import static akka.pattern.Patterns.ask;
|
||||
|
||||
import static akka.docs.jrouting.CustomRouterDocTestBase.DemocratActor;
|
||||
import static akka.docs.jrouting.CustomRouterDocTestBase.RepublicanActor;
|
||||
import static akka.docs.jrouting.CustomRouterDocTestBase.Message.*;
|
||||
import static docs.jrouting.CustomRouterDocTestBase.DemocratActor;
|
||||
import static docs.jrouting.CustomRouterDocTestBase.RepublicanActor;
|
||||
import static docs.jrouting.CustomRouterDocTestBase.Message.*;
|
||||
|
||||
public class CustomRouterDocTestBase {
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.routing.ScatterGatherFirstCompletedRouter;
|
||||
import akka.routing.BroadcastRouter;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.routing.FromConfig;
|
||||
import akka.actor.ActorRef;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.jrouting;
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.routing.RoundRobinRouter;
|
||||
import akka.routing.DefaultResizer;
|
||||
|
|
@ -55,7 +55,7 @@ public class RouterViaProgramExample {
|
|||
ActorRef actor2 = system.actorOf(new Props(ExampleActor.class));
|
||||
ActorRef actor3 = system.actorOf(new Props(ExampleActor.class));
|
||||
Iterable<ActorRef> routees = Arrays.asList(new ActorRef[] { actor1, actor2, actor3 });
|
||||
ActorRef router2 = system.actorOf(new Props(ExampleActor.class).withRouter(RoundRobinRouter.create(routees)));
|
||||
ActorRef router2 = system.actorOf(new Props().withRouter(RoundRobinRouter.create(routees)));
|
||||
//#programmaticRoutingRoutees
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
router2.tell(new ExampleActor.Message(i));
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.remoting;
|
||||
package docs.remoting;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.UntypedActor;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.remoting
|
||||
package docs.remoting
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.remoting;
|
||||
package docs.remoting;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.serialization
|
||||
package docs.serialization
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.serialization;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
//#imports
|
||||
import akka.actor.*;
|
||||
import akka.remote.RemoteActorRefProvider;
|
||||
import akka.serialization.*;
|
||||
import com.typesafe.config.*;
|
||||
|
||||
//#imports
|
||||
|
||||
public class SerializationDocTestBase {
|
||||
//#my-own-serializer
|
||||
public static class MyOwnSerializer extends JSerializer {
|
||||
|
||||
// This is whether "fromBinary" requires a "clazz" or not
|
||||
@Override public boolean includeManifest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pick a unique identifier for your Serializer,
|
||||
// you've got a couple of billions to choose from,
|
||||
// 0 - 16 is reserved by Akka itself
|
||||
@Override public int identifier() {
|
||||
return 1234567;
|
||||
}
|
||||
|
||||
// "toBinary" serializes the given object to an Array of Bytes
|
||||
@Override public byte[] toBinary(Object obj) {
|
||||
// Put the code that serializes the object here
|
||||
//#...
|
||||
return new byte[0];
|
||||
//#...
|
||||
}
|
||||
|
||||
// "fromBinary" deserializes the given array,
|
||||
// using the type hint (if any, see "includeManifest" above)
|
||||
@Override public Object fromBinaryJava(byte[] bytes,
|
||||
Class<?> clazz) {
|
||||
// Put your code that deserializes here
|
||||
//#...
|
||||
return null;
|
||||
//#...
|
||||
}
|
||||
}
|
||||
//#my-own-serializer
|
||||
|
||||
@Test public void serializeActorRefs() {
|
||||
final ActorSystem theActorSystem =
|
||||
ActorSystem.create("whatever");
|
||||
final ActorRef theActorRef =
|
||||
theActorSystem.deadLetters(); // Of course this should be you
|
||||
|
||||
//#actorref-serializer
|
||||
// Serialize
|
||||
// (beneath toBinary)
|
||||
final Address transportAddress =
|
||||
Serialization.currentTransportAddress().value();
|
||||
String identifier;
|
||||
|
||||
// If there is no transportAddress,
|
||||
// it means that either this Serializer isn't called
|
||||
// within a piece of code that sets it,
|
||||
// so either you need to supply your own,
|
||||
// or simply use the local path.
|
||||
if (transportAddress == null) identifier = theActorRef.path().toString();
|
||||
else identifier = theActorRef.path().toStringWithAddress(transportAddress);
|
||||
// Then just serialize the identifier however you like
|
||||
|
||||
|
||||
// Deserialize
|
||||
// (beneath fromBinary)
|
||||
final ActorRef deserializedActorRef = theActorSystem.actorFor(identifier);
|
||||
// Then just use the ActorRef
|
||||
//#actorref-serializer
|
||||
theActorSystem.shutdown();
|
||||
}
|
||||
|
||||
//#external-address
|
||||
public static class ExternalAddressExt implements Extension {
|
||||
private final ExtendedActorSystem system;
|
||||
|
||||
public ExternalAddressExt(ExtendedActorSystem system) {
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public Address getAddressFor(Address remoteAddress) {
|
||||
final scala.Option<Address> optAddr = system.provider()
|
||||
.getExternalAddressFor(remoteAddress);
|
||||
if (optAddr.isDefined()) {
|
||||
return optAddr.get();
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"cannot send to remote address " + remoteAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExternalAddress extends
|
||||
AbstractExtensionId<ExternalAddressExt> implements ExtensionIdProvider {
|
||||
public static final ExternalAddress ID = new ExternalAddress();
|
||||
|
||||
public ExternalAddress lookup() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public ExternalAddressExt createExtension(ExtendedActorSystem system) {
|
||||
return new ExternalAddressExt(system);
|
||||
}
|
||||
}
|
||||
|
||||
//#external-address
|
||||
|
||||
public void demonstrateExternalAddress() {
|
||||
// this is not meant to be run, only to be compiled
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final Address remoteAddr = new Address("", "");
|
||||
// #external-address
|
||||
final Address addr = ExternalAddress.ID.get(system).getAddressFor(remoteAddr);
|
||||
// #external-address
|
||||
}
|
||||
|
||||
//#external-address-default
|
||||
public static class DefaultAddressExt implements Extension {
|
||||
private final ExtendedActorSystem system;
|
||||
|
||||
public DefaultAddressExt(ExtendedActorSystem system) {
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
final ActorRefProvider provider = system.provider();
|
||||
if (provider instanceof RemoteActorRefProvider) {
|
||||
return ((RemoteActorRefProvider) provider).transport().address();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("need RemoteActorRefProvider");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefaultAddress extends
|
||||
AbstractExtensionId<DefaultAddressExt> implements ExtensionIdProvider {
|
||||
public static final DefaultAddress ID = new DefaultAddress();
|
||||
|
||||
public DefaultAddress lookup() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public DefaultAddressExt createExtension(ExtendedActorSystem system) {
|
||||
return new DefaultAddressExt(system);
|
||||
}
|
||||
}
|
||||
|
||||
//#external-address-default
|
||||
|
||||
public void demonstrateDefaultAddress() {
|
||||
// this is not meant to be run, only to be compiled
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final Address remoteAddr = new Address("", "");
|
||||
// #external-address-default
|
||||
final Address addr = DefaultAddress.ID.get(system).getAddress();
|
||||
// #external-address-default
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateTheProgrammaticAPI() {
|
||||
// #programmatic
|
||||
ActorSystem system = ActorSystem.create("example");
|
||||
|
||||
// Get the Serialization Extension
|
||||
Serialization serialization = SerializationExtension.get(system);
|
||||
|
||||
// Have something to serialize
|
||||
String original = "woohoo";
|
||||
|
||||
// Find the Serializer for it
|
||||
Serializer serializer = serialization.findSerializerFor(original);
|
||||
|
||||
// Turn it into bytes
|
||||
byte[] bytes = serializer.toBinary(original);
|
||||
|
||||
// Turn it back into an object,
|
||||
// the nulls are for the class manifest and for the classloader
|
||||
String back = (String) serializer.fromBinary(bytes);
|
||||
|
||||
// Voilá!
|
||||
assertEquals(original, back);
|
||||
|
||||
// #programmatic
|
||||
system.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
//#class
|
||||
import akka.actor.*;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
import akka.actor.*;
|
||||
import akka.transactor.*;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
//#class
|
||||
import akka.transactor.*;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
//#class
|
||||
import akka.actor.*;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
//#class
|
||||
import akka.actor.ActorRef;
|
||||
|
|
@ -2,6 +2,6 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
public class Message {}
|
||||
|
|
@ -2,10 +2,10 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor
|
||||
package docs.transactor
|
||||
|
||||
import org.scalatest.junit.JUnitWrapperSuite
|
||||
|
||||
class TransactorDocJavaSpec extends JUnitWrapperSuite(
|
||||
"akka.docs.transactor.TransactorDocTest",
|
||||
"docs.transactor.TransactorDocTest",
|
||||
Thread.currentThread.getContextClassLoader)
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor;
|
||||
package docs.transactor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.zeromq
|
||||
package docs.zeromq
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.zeromq;
|
||||
package docs.zeromq;
|
||||
|
||||
//#pub-socket
|
||||
import akka.zeromq.Bind;
|
||||
|
|
@ -70,7 +70,7 @@ There are 4 different types of message dispatchers:
|
|||
|
||||
* BalancingDispatcher
|
||||
|
||||
- This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.
|
||||
- This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.
|
||||
|
||||
- It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message.
|
||||
|
||||
|
|
@ -85,9 +85,11 @@ There are 4 different types of message dispatchers:
|
|||
"thread-pool-executor" or the FQCN of
|
||||
an ``akka.dispatcher.ExecutorServiceConfigurator``
|
||||
|
||||
- Note that you can **not** use a ``BalancingDispatcher`` together with any kind of ``Router``, trying to do so will make your actor fail verification.
|
||||
|
||||
* CallingThreadDispatcher
|
||||
|
||||
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
|
||||
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
|
||||
but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef`
|
||||
for details and restrictions.
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ in the "akka.extensions" section of the config you provide to your ``ActorSystem
|
|||
::
|
||||
|
||||
akka {
|
||||
extensions = ["akka.docs.extension.ExtensionDocTestBase.CountExtension"]
|
||||
extensions = ["docs.extension.ExtensionDocTestBase.CountExtension"]
|
||||
}
|
||||
|
||||
Applicability
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
Microkernel (Java)
|
||||
==================
|
||||
|
||||
The purpose of the Akka Microkernel is to offer a bundling mechanism so that you can distribute
|
||||
an Akka application as a single payload, without the need to run in a Java Application Server or manually
|
||||
having to create a launcher script.
|
||||
|
||||
The Akka Microkernel is included in the Akka download found at `downloads`_.
|
||||
|
||||
.. _downloads: http://akka.io/downloads
|
||||
|
|
|
|||
|
|
@ -294,3 +294,63 @@ which holds the transport used (RemoteTransport) and optionally the address that
|
|||
To intercept when an inbound remote client has been closed you listen to ``RemoteServerClientClosed``
|
||||
which holds the transport used (RemoteTransport) and optionally the address of the remote client that was closed (Option<Address>).
|
||||
|
||||
Remote Security
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Akka provides a couple of ways to enhance security between remote nodes (client/server):
|
||||
|
||||
* Untrusted Mode
|
||||
* Security Cookie Handshake
|
||||
|
||||
Untrusted Mode
|
||||
--------------
|
||||
|
||||
You can enable untrusted mode for preventing system messages to be send by clients, e.g. messages like.
|
||||
This will prevent the client to send these messages to the server:
|
||||
|
||||
* ``Create``
|
||||
* ``Recreate``
|
||||
* ``Suspend``
|
||||
* ``Resume``
|
||||
* ``Terminate``
|
||||
* ``Supervise``
|
||||
* ``ChildTerminated``
|
||||
* ``Link``
|
||||
* ``Unlink``
|
||||
|
||||
Here is how to turn it on in the config::
|
||||
|
||||
akka {
|
||||
actor {
|
||||
remote {
|
||||
untrusted-mode = on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Secure Cookie Handshake
|
||||
-----------------------
|
||||
|
||||
Akka remoting also allows you to specify a secure cookie that will be exchanged and ensured to be identical
|
||||
in the connection handshake between the client and the server. If they are not identical then the client
|
||||
will be refused to connect to the server.
|
||||
|
||||
The secure cookie can be any kind of string. But the recommended approach is to generate a cryptographically
|
||||
secure cookie using this script ``$AKKA_HOME/scripts/generate_config_with_secure_cookie.sh`` or from code
|
||||
using the ``akka.util.Crypt.generateSecureCookie()`` utility method.
|
||||
|
||||
You have to ensure that both the connecting client and the server have the same secure cookie as well
|
||||
as the ``require-cookie`` option turned on.
|
||||
|
||||
Here is an example config::
|
||||
|
||||
akka {
|
||||
actor {
|
||||
remote {
|
||||
netty {
|
||||
secure-cookie = "090A030E0F0A05010900000A0C0E0C0B03050D05"
|
||||
require-cookie = on
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ You can also give the router already created routees as in:
|
|||
|
||||
.. includecode:: code/akka/docs/jrouting/RouterViaProgramExample.java#programmaticRoutingRoutees
|
||||
|
||||
It should be noted that no actor factory or class needs to be provided in this
|
||||
case, as the ``Router`` will not create any children on its own (which is not
|
||||
true anymore when using a resizer). The routees can also be specified by giving
|
||||
their path strings.
|
||||
|
||||
When you create a router programmatically you define the number of routees *or* you pass already created routees to it.
|
||||
If you send both parameters to the router *only* the latter will be used, i.e. ``nrOfInstances`` is disregarded.
|
||||
|
||||
|
|
@ -48,7 +53,7 @@ Once you have the router actor it is just to send messages to it as you would to
|
|||
|
||||
router.tell(new MyMsg());
|
||||
|
||||
The router will apply its behavior to the message it receives and forward it to the routees.
|
||||
The router will forward the message to its routees according to its routing policy.
|
||||
|
||||
Remotely Deploying Routees
|
||||
**************************
|
||||
|
|
@ -375,7 +380,8 @@ The dispatcher for created children of the router will be taken from
|
|||
makes sense to configure the :class:`BalancingDispatcher` if the precise
|
||||
routing is not so important (i.e. no consistent hashing or round-robin is
|
||||
required); this enables newly created routees to pick up work immediately by
|
||||
stealing it from their siblings.
|
||||
stealing it from their siblings. Note that you can **not** use a ``BalancingDispatcher``
|
||||
together with any kind of ``Router``, trying to do so will make your actor fail verification.
|
||||
|
||||
The “head” router, of course, cannot run on the same balancing dispatcher,
|
||||
because it does not process the same messages, hence this special actor does
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Customization
|
|||
=============
|
||||
|
||||
So, lets say that you want to create your own ``Serializer``,
|
||||
you saw the ``akka.docs.serialization.MyOwnSerializer`` in the config example above?
|
||||
you saw the ``docs.serialization.MyOwnSerializer`` in the config example above?
|
||||
|
||||
Creating new Serializers
|
||||
------------------------
|
||||
|
|
@ -109,6 +109,47 @@ you might want to know how to serialize and deserialize them properly, here's th
|
|||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||
:include: imports,actorref-serializer
|
||||
|
||||
.. note::
|
||||
|
||||
``ActorPath.toStringWithAddress`` only differs from ``toString`` if the
|
||||
address does not already have ``host`` and ``port`` components, i.e. it only
|
||||
inserts address information for local addresses.
|
||||
|
||||
This assumes that serialization happens in the context of sending a message
|
||||
through the remote transport. There are other uses of serialization, though,
|
||||
e.g. storing actor references outside of an actor application (database,
|
||||
durable mailbox, etc.). In this case, it is important to keep in mind that the
|
||||
address part of an actor’s path determines how that actor is communicated with.
|
||||
Storing a local actor path might be the right choice if the retrieval happens
|
||||
in the same logical context, but it is not enough when deserializing it on a
|
||||
different network host: for that it would need to include the system’s remote
|
||||
transport address. An actor system is not limited to having just one remote
|
||||
transport per se, which makes this question a bit more interesting.
|
||||
|
||||
In the general case, the local address to be used depends on the type of remote
|
||||
address which shall be the recipient of the serialized information. Use
|
||||
:meth:`ActorRefProvider.getExternalAddressFor(remoteAddr)` to query the system
|
||||
for the appropriate address to use when sending to ``remoteAddr``:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||
:include: external-address
|
||||
|
||||
This requires that you know at least which type of address will be supported by
|
||||
the system which will deserialize the resulting actor reference; if you have no
|
||||
concrete address handy you can create a dummy one for the right protocol using
|
||||
``new Address(protocol, "", "", 0)`` (assuming that the actual transport used is as
|
||||
lenient as Akka’s RemoteActorRefProvider).
|
||||
|
||||
There is a possible simplification available if you are just using the default
|
||||
:class:`NettyRemoteTransport` with the :meth:`RemoteActorRefProvider`, which is
|
||||
enabled by the fact that this combination has just a single remote address:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||
:include: external-address-default
|
||||
|
||||
This solution has to be adapted once other providers are used (like the planned
|
||||
extensions for clustering).
|
||||
|
||||
Deep serialization of Actors
|
||||
----------------------------
|
||||
|
||||
|
|
@ -137,3 +178,10 @@ representation into a real reference. :class:`DynamicVariable` is a
|
|||
thread-local variable, so be sure to have it set while deserializing anything
|
||||
which might contain actor references.
|
||||
|
||||
External Akka Serializers
|
||||
=========================
|
||||
|
||||
`Akka-protostuff by Roman Levenstein<https://github.com/romix/akka-protostuff-serialization>`_
|
||||
|
||||
|
||||
`Akka-quickser by Roman Levenstein<https://github.com/romix/akka-quickser-serialization>`_
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor.mailbox
|
||||
package docs.actor.mailbox
|
||||
|
||||
//#imports
|
||||
import akka.actor.Props
|
||||
|
|
@ -107,7 +107,7 @@ import akka.actor.mailbox.DurableMailboxSpec
|
|||
object MyMailboxSpec {
|
||||
val config = """
|
||||
MyStorage-dispatcher {
|
||||
mailbox-type = akka.docs.actor.mailbox.MyMailboxType
|
||||
mailbox-type = docs.actor.mailbox.MyMailboxType
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor.mailbox
|
||||
package docs.actor.mailbox
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor.mailbox;
|
||||
package docs.actor.mailbox;
|
||||
|
||||
//#imports
|
||||
import akka.actor.Props;
|
||||
|
|
@ -443,7 +443,7 @@ An Actor has to implement the ``receive`` method to receive messages:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
protected def receive: PartialFunction[Any, Unit]
|
||||
def receive: PartialFunction[Any, Unit]
|
||||
|
||||
Note: Akka has an alias to the ``PartialFunction[Any, Unit]`` type called
|
||||
``Receive`` (``akka.actor.Actor.Receive``), so you can use this type instead for
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
//#imports1
|
||||
import akka.actor.Actor
|
||||
|
|
@ -326,14 +326,13 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
|||
//#gracefulStop
|
||||
import akka.pattern.gracefulStop
|
||||
import akka.dispatch.Await
|
||||
import akka.actor.ActorTimeoutException
|
||||
|
||||
try {
|
||||
val stopped: Future[Boolean] = gracefulStop(actorRef, 5 seconds)(system)
|
||||
Await.result(stopped, 6 seconds)
|
||||
// the actor has been stopped
|
||||
} catch {
|
||||
case e: ActorTimeoutException ⇒ // the actor wasn't stopped within 5 seconds
|
||||
case e: akka.pattern.AskTimeoutException ⇒ // the actor wasn't stopped within 5 seconds
|
||||
}
|
||||
//#gracefulStop
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
//#test-code
|
||||
import akka.testkit.AkkaSpec
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
//#all
|
||||
//#imports
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
//#testkit
|
||||
import akka.testkit.{ AkkaSpec, ImplicitSender, EventFilter }
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
//#imports1
|
||||
import akka.actor.Actor
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
//#imports
|
||||
import akka.dispatch.{ Promise, Future, Await }
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.actor
|
||||
package docs.actor
|
||||
|
||||
import akka.actor._
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.agent
|
||||
package docs.agent
|
||||
|
||||
import akka.agent.Agent
|
||||
import akka.util.duration._
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package akka.docs.camel
|
||||
package docs.camel
|
||||
|
||||
object Consumers {
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package akka.docs.camel
|
||||
package docs.camel
|
||||
|
||||
object Introduction {
|
||||
{
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.dispatcher
|
||||
package docs.dispatcher
|
||||
|
||||
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
|
@ -91,13 +91,13 @@ object DispatcherDocSpec {
|
|||
|
||||
//#prio-dispatcher-config
|
||||
prio-dispatcher {
|
||||
mailbox-type = "akka.docs.dispatcher.DispatcherDocSpec$MyPrioMailbox"
|
||||
mailbox-type = "docs.dispatcher.DispatcherDocSpec$MyPrioMailbox"
|
||||
}
|
||||
//#prio-dispatcher-config
|
||||
|
||||
//#prio-dispatcher-config-java
|
||||
prio-dispatcher-java {
|
||||
mailbox-type = "akka.docs.dispatcher.DispatcherDocTestBase$MyPrioMailbox"
|
||||
mailbox-type = "docs.dispatcher.DispatcherDocTestBase$MyPrioMailbox"
|
||||
//Other dispatcher configuration goes here
|
||||
}
|
||||
//#prio-dispatcher-config-java
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.event
|
||||
package docs.event
|
||||
|
||||
import akka.testkit.AkkaSpec
|
||||
import akka.actor.Actor
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension
|
||||
package docs.extension
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import akka.actor.Actor
|
||||
|
|
@ -45,7 +45,7 @@ object ExtensionDocSpec {
|
|||
val config = """
|
||||
//#config
|
||||
akka {
|
||||
extensions = ["akka.docs.extension.CountExtension$"]
|
||||
extensions = ["docs.extension.CountExtension$"]
|
||||
}
|
||||
//#config
|
||||
"""
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.extension
|
||||
package docs.extension
|
||||
|
||||
//#imports
|
||||
import akka.actor.Extension
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.future
|
||||
package docs.future
|
||||
|
||||
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.io
|
||||
package docs.io
|
||||
|
||||
//#imports
|
||||
import akka.actor._
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.remoting
|
||||
package docs.remoting
|
||||
|
||||
import akka.actor.{ ExtendedActorSystem, ActorSystem, Actor, ActorRef }
|
||||
import akka.testkit.{ AkkaSpec, ImplicitSender }
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.routing
|
||||
package docs.routing
|
||||
|
||||
import RouterDocSpec.MyActor
|
||||
import akka.actor.{ Props, Actor }
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.routing
|
||||
package docs.routing
|
||||
|
||||
import akka.routing.{ ScatterGatherFirstCompletedRouter, BroadcastRouter, RandomRouter, RoundRobinRouter }
|
||||
import annotation.tailrec
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.routing
|
||||
package docs.routing
|
||||
|
||||
import akka.actor.{ Actor, Props, ActorSystem }
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.routing
|
||||
package docs.routing
|
||||
|
||||
import akka.routing.RoundRobinRouter
|
||||
import akka.actor.{ ActorRef, Props, Actor, ActorSystem }
|
||||
|
|
@ -29,7 +29,7 @@ object RoutingProgrammaticallyExample extends App {
|
|||
val actor2 = system.actorOf(Props[ExampleActor1])
|
||||
val actor3 = system.actorOf(Props[ExampleActor1])
|
||||
val routees = Vector[ActorRef](actor1, actor2, actor3)
|
||||
val router2 = system.actorOf(Props[ExampleActor1].withRouter(
|
||||
val router2 = system.actorOf(Props().withRouter(
|
||||
RoundRobinRouter(routees = routees)))
|
||||
//#programmaticRoutingRoutees
|
||||
1 to 6 foreach { i ⇒ router2 ! Message1(i) }
|
||||
|
|
@ -1,17 +1,21 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.serialization
|
||||
package docs.serialization
|
||||
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import akka.testkit._
|
||||
import akka.actor.{ ActorRef, ActorSystem }
|
||||
|
||||
//#imports
|
||||
import akka.actor.{ ActorRef, ActorSystem }
|
||||
import akka.serialization._
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
//#imports
|
||||
import akka.actor.ExtensionKey
|
||||
import akka.actor.ExtendedActorSystem
|
||||
import akka.actor.Extension
|
||||
import akka.actor.Address
|
||||
import akka.remote.RemoteActorRefProvider
|
||||
|
||||
//#my-own-serializer
|
||||
class MyOwnSerializer extends Serializer {
|
||||
|
|
@ -87,7 +91,7 @@ class SerializationDocSpec extends AkkaSpec {
|
|||
serializers {
|
||||
java = "akka.serialization.JavaSerializer"
|
||||
proto = "akka.serialization.ProtobufSerializer"
|
||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||
myown = "docs.serialization.MyOwnSerializer"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,14 +109,14 @@ class SerializationDocSpec extends AkkaSpec {
|
|||
serializers {
|
||||
java = "akka.serialization.JavaSerializer"
|
||||
proto = "akka.serialization.ProtobufSerializer"
|
||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||
myown = "docs.serialization.MyOwnSerializer"
|
||||
}
|
||||
|
||||
serialization-bindings {
|
||||
"java.lang.String" = java
|
||||
"akka.docs.serialization.Customer" = java
|
||||
"docs.serialization.Customer" = java
|
||||
"com.google.protobuf.Message" = proto
|
||||
"akka.docs.serialization.MyOwnSerializable" = myown
|
||||
"docs.serialization.MyOwnSerializable" = myown
|
||||
"java.lang.Boolean" = myown
|
||||
}
|
||||
}
|
||||
|
|
@ -176,5 +180,38 @@ class SerializationDocSpec extends AkkaSpec {
|
|||
val deserializedActorRef = theActorSystem actorFor identifier
|
||||
// Then just use the ActorRef
|
||||
//#actorref-serializer
|
||||
|
||||
//#external-address
|
||||
object ExternalAddress extends ExtensionKey[ExternalAddressExt]
|
||||
|
||||
class ExternalAddressExt(system: ExtendedActorSystem) extends Extension {
|
||||
def addressFor(remoteAddr: Address): Address =
|
||||
system.provider.getExternalAddressFor(remoteAddr) getOrElse
|
||||
(throw new UnsupportedOperationException("cannot send to " + remoteAddr))
|
||||
}
|
||||
|
||||
def serializeTo(ref: ActorRef, remote: Address): String =
|
||||
ref.path.toStringWithAddress(ExternalAddress(theActorSystem).addressFor(remote))
|
||||
//#external-address
|
||||
}
|
||||
|
||||
"demonstrate how to do default Akka serialization of ActorRef" in {
|
||||
val theActorSystem: ActorSystem = system
|
||||
|
||||
//#external-address-default
|
||||
object ExternalAddress extends ExtensionKey[ExternalAddressExt]
|
||||
|
||||
class ExternalAddressExt(system: ExtendedActorSystem) extends Extension {
|
||||
def addressForAkka: Address = system.provider match {
|
||||
case r: RemoteActorRefProvider ⇒ r.transport.address
|
||||
case _ ⇒
|
||||
throw new UnsupportedOperationException(
|
||||
"this method requires the RemoteActorRefProvider to be configured")
|
||||
}
|
||||
}
|
||||
|
||||
def serializeAkkaDefault(ref: ActorRef): String =
|
||||
ref.path.toStringWithAddress(ExternalAddress(theActorSystem).addressForAkka)
|
||||
//#external-address-default
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.testkit
|
||||
package docs.testkit
|
||||
|
||||
//#plain-spec
|
||||
import akka.actor.ActorSystem
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.testkit
|
||||
package docs.testkit
|
||||
|
||||
//#testkit-usage
|
||||
import scala.util.Random
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.testkit
|
||||
package docs.testkit
|
||||
|
||||
//#imports-test-probe
|
||||
import akka.testkit.TestProbe
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.docs.transactor
|
||||
package docs.transactor
|
||||
|
||||
import akka.actor._
|
||||
import akka.transactor._
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package akka.docs.zeromq
|
||||
package docs.zeromq
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Props
|
||||
|
|
@ -71,7 +71,7 @@ There are 4 different types of message dispatchers:
|
|||
|
||||
* BalancingDispatcher
|
||||
|
||||
- This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.
|
||||
- This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.
|
||||
|
||||
- It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message.
|
||||
|
||||
|
|
@ -86,9 +86,11 @@ There are 4 different types of message dispatchers:
|
|||
"thread-pool-executor" or the FQCN of
|
||||
an ``akka.dispatcher.ExecutorServiceConfigurator``
|
||||
|
||||
- Note that you can **not** use a ``BalancingDispatcher`` together with any kind of ``Router``, trying to do so will make your actor fail verification.
|
||||
|
||||
* CallingThreadDispatcher
|
||||
|
||||
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
|
||||
- This dispatcher runs invocations on the current thread only. This dispatcher does not create any new threads,
|
||||
but it can be used from different threads concurrently for the same actor. See :ref:`TestCallingThreadDispatcherRef`
|
||||
for details and restrictions.
|
||||
|
||||
|
|
@ -112,8 +114,8 @@ And then using it:
|
|||
|
||||
.. includecode:: ../scala/code/akka/docs/dispatcher/DispatcherDocSpec.scala#defining-pinned-dispatcher
|
||||
|
||||
Note that ``thread-pool-executor`` configuration as per the above ``my-thread-pool-dispatcher`` exmaple is
|
||||
NOT applicable. This is because every actor will have its own thread pool when using ``PinnedDispatcher``,
|
||||
Note that ``thread-pool-executor`` configuration as per the above ``my-thread-pool-dispatcher`` exmaple is
|
||||
NOT applicable. This is because every actor will have its own thread pool when using ``PinnedDispatcher``,
|
||||
and that pool will have only one thread.
|
||||
|
||||
Mailboxes
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
Microkernel (Scala)
|
||||
===================
|
||||
|
||||
The purpose of the Akka Microkernel is to offer a bundling mechanism so that you can distribute
|
||||
an Akka application as a single payload, without the need to run in a Java Application Server or manually
|
||||
having to create a launcher script.
|
||||
|
||||
The Akka Microkernel is included in the Akka download found at `downloads`_.
|
||||
|
||||
.. _downloads: http://akka.io/downloads
|
||||
|
|
|
|||
|
|
@ -301,3 +301,64 @@ which holds the transport used (RemoteTransport) and optionally the address that
|
|||
|
||||
To intercept when an inbound remote client has been closed you listen to ``RemoteServerClientClosed``
|
||||
which holds the transport used (RemoteTransport) and optionally the address of the remote client that was closed (Option[Address]).
|
||||
|
||||
Remote Security
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Akka provides a couple of ways to enhance security between remote nodes (client/server):
|
||||
|
||||
* Untrusted Mode
|
||||
* Security Cookie Handshake
|
||||
|
||||
Untrusted Mode
|
||||
--------------
|
||||
|
||||
You can enable untrusted mode for preventing system messages to be send by clients, e.g. messages like.
|
||||
This will prevent the client to send these messages to the server:
|
||||
|
||||
* ``Create``
|
||||
* ``Recreate``
|
||||
* ``Suspend``
|
||||
* ``Resume``
|
||||
* ``Terminate``
|
||||
* ``Supervise``
|
||||
* ``ChildTerminated``
|
||||
* ``Link``
|
||||
* ``Unlink``
|
||||
|
||||
Here is how to turn it on in the config::
|
||||
|
||||
akka {
|
||||
actor {
|
||||
remote {
|
||||
untrusted-mode = on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Secure Cookie Handshake
|
||||
-----------------------
|
||||
|
||||
Akka remoting also allows you to specify a secure cookie that will be exchanged and ensured to be identical
|
||||
in the connection handshake between the client and the server. If they are not identical then the client
|
||||
will be refused to connect to the server.
|
||||
|
||||
The secure cookie can be any kind of string. But the recommended approach is to generate a cryptographically
|
||||
secure cookie using this script ``$AKKA_HOME/scripts/generate_config_with_secure_cookie.sh`` or from code
|
||||
using the ``akka.util.Crypt.generateSecureCookie()`` utility method.
|
||||
|
||||
You have to ensure that both the connecting client and the server have the same secure cookie as well
|
||||
as the ``require-cookie`` option turned on.
|
||||
|
||||
Here is an example config::
|
||||
|
||||
akka {
|
||||
actor {
|
||||
remote {
|
||||
netty {
|
||||
secure-cookie = "090A030E0F0A05010900000A0C0E0C0B03050D05"
|
||||
require-cookie = on
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ You can also give the router already created routees as in:
|
|||
|
||||
.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingRoutees
|
||||
|
||||
It should be noted that no actor factory or class needs to be provided in this
|
||||
case, as the ``Router`` will not create any children on its own (which is not
|
||||
true anymore when using a resizer). The routees can also be specified by giving
|
||||
their path strings.
|
||||
|
||||
When you create a router programmatically you define the number of routees *or* you pass already created routees to it.
|
||||
If you send both parameters to the router *only* the latter will be used, i.e. ``nrOfInstances`` is disregarded.
|
||||
|
||||
|
|
@ -48,7 +53,7 @@ Once you have the router actor it is just to send messages to it as you would to
|
|||
|
||||
router ! MyMsg
|
||||
|
||||
The router will apply its behavior to the message it receives and forward it to the routees.
|
||||
The router will forward the message to its routees according to its routing policy.
|
||||
|
||||
Remotely Deploying Routees
|
||||
**************************
|
||||
|
|
@ -375,7 +380,9 @@ The dispatcher for created children of the router will be taken from
|
|||
makes sense to configure the :class:`BalancingDispatcher` if the precise
|
||||
routing is not so important (i.e. no consistent hashing or round-robin is
|
||||
required); this enables newly created routees to pick up work immediately by
|
||||
stealing it from their siblings.
|
||||
stealing it from their siblings. Note that you can **not** use a ``BalancingDispatcher``
|
||||
together with any kind of ``Router``, trying to do so will make your actor fail verification.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ Customization
|
|||
=============
|
||||
|
||||
So, lets say that you want to create your own ``Serializer``,
|
||||
you saw the ``akka.docs.serialization.MyOwnSerializer`` in the config example above?
|
||||
you saw the ``docs.serialization.MyOwnSerializer`` in the config example above?
|
||||
|
||||
Creating new Serializers
|
||||
------------------------
|
||||
|
|
@ -107,6 +107,47 @@ you might want to know how to serialize and deserialize them properly, here's th
|
|||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
||||
:include: imports,actorref-serializer
|
||||
|
||||
.. note::
|
||||
|
||||
``ActorPath.toStringWithAddress`` only differs from ``toString`` if the
|
||||
address does not already have ``host`` and ``port`` components, i.e. it only
|
||||
inserts address information for local addresses.
|
||||
|
||||
This assumes that serialization happens in the context of sending a message
|
||||
through the remote transport. There are other uses of serialization, though,
|
||||
e.g. storing actor references outside of an actor application (database,
|
||||
durable mailbox, etc.). In this case, it is important to keep in mind that the
|
||||
address part of an actor’s path determines how that actor is communicated with.
|
||||
Storing a local actor path might be the right choice if the retrieval happens
|
||||
in the same logical context, but it is not enough when deserializing it on a
|
||||
different network host: for that it would need to include the system’s remote
|
||||
transport address. An actor system is not limited to having just one remote
|
||||
transport per se, which makes this question a bit more interesting.
|
||||
|
||||
In the general case, the local address to be used depends on the type of remote
|
||||
address which shall be the recipient of the serialized information. Use
|
||||
:meth:`ActorRefProvider.getExternalAddressFor(remoteAddr)` to query the system
|
||||
for the appropriate address to use when sending to ``remoteAddr``:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
||||
:include: external-address
|
||||
|
||||
This requires that you know at least which type of address will be supported by
|
||||
the system which will deserialize the resulting actor reference; if you have no
|
||||
concrete address handy you can create a dummy one for the right protocol using
|
||||
``Address(protocol, "", "", 0)`` (assuming that the actual transport used is as
|
||||
lenient as Akka’s RemoteActorRefProvider).
|
||||
|
||||
There is a possible simplification available if you are just using the default
|
||||
:class:`NettyRemoteTransport` with the :meth:`RemoteActorRefProvider`, which is
|
||||
enabled by the fact that this combination has just a single remote address:
|
||||
|
||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
||||
:include: external-address-default
|
||||
|
||||
This solution has to be adapted once other providers are used (like the planned
|
||||
extensions for clustering).
|
||||
|
||||
Deep serialization of Actors
|
||||
----------------------------
|
||||
|
||||
|
|
@ -135,3 +176,11 @@ representation into a real reference. :class:`DynamicVariable` is a
|
|||
thread-local variable, so be sure to have it set while deserializing anything
|
||||
which might contain actor references.
|
||||
|
||||
|
||||
External Akka Serializers
|
||||
=========================
|
||||
|
||||
`Akka-protostuff by Roman Levenstein<https://github.com/romix/akka-protostuff-serialization>`_
|
||||
|
||||
|
||||
`Akka-quickser by Roman Levenstein<https://github.com/romix/akka-quickser-serialization>`_
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue