Merge pull request #236 from jboner/wip-1702-settings-ext-patriknw

DOC: Extension sample for application specific settings. See #1702
This commit is contained in:
patriknw 2012-01-23 07:12:49 -08:00
commit 45d178c276
11 changed files with 350 additions and 119 deletions

View file

@ -16,15 +16,13 @@ import static org.junit.Assert.*;
public class JavaExtension { public class JavaExtension {
static class Provider implements ExtensionIdProvider { static class TestExtensionId extends AbstractExtensionId<TestExtension> implements ExtensionIdProvider {
public final static TestExtensionId TestExtensionProvider = new TestExtensionId();
public ExtensionId<TestExtension> lookup() { public ExtensionId<TestExtension> lookup() {
return defaultInstance; return TestExtensionId.TestExtensionProvider;
} }
}
public final static TestExtensionId defaultInstance = new TestExtensionId();
static class TestExtensionId extends AbstractExtensionId<TestExtension> {
public TestExtension createExtension(ActorSystemImpl i) { public TestExtension createExtension(ActorSystemImpl i) {
return new TestExtension(i); return new TestExtension(i);
} }
@ -37,11 +35,13 @@ public class JavaExtension {
system = i; system = i;
} }
} }
static class OtherExtension implements Extension { static class OtherExtension implements Extension {
static final ExtensionKey<OtherExtension> key = new ExtensionKey<OtherExtension>(OtherExtension.class) {}; static final ExtensionKey<OtherExtension> key = new ExtensionKey<OtherExtension>(OtherExtension.class) {
};
public final ActorSystemImpl system; public final ActorSystemImpl system;
public OtherExtension(ActorSystemImpl i) { public OtherExtension(ActorSystemImpl i) {
system = i; system = i;
} }
@ -51,8 +51,8 @@ public class JavaExtension {
@BeforeClass @BeforeClass
public static void beforeAll() { public static void beforeAll() {
Config c = ConfigFactory.parseString("akka.extensions = [ \"akka.actor.JavaExtension$Provider\" ]").withFallback( Config c = ConfigFactory.parseString("akka.extensions = [ \"akka.actor.JavaExtension$TestExtensionId\" ]")
AkkaSpec.testConf()); .withFallback(AkkaSpec.testConf());
system = ActorSystem.create("JavaExtension", c); system = ActorSystem.create("JavaExtension", c);
} }
@ -64,10 +64,10 @@ public class JavaExtension {
@Test @Test
public void mustBeAccessible() { public void mustBeAccessible() {
assertSame(system.extension(defaultInstance).system, system); assertSame(system.extension(TestExtensionId.TestExtensionProvider).system, system);
assertSame(defaultInstance.apply(system).system, system); assertSame(TestExtensionId.TestExtensionProvider.apply(system).system, system);
} }
@Test @Test
public void mustBeAdHoc() { public void mustBeAdHoc() {
assertSame(OtherExtension.key.apply(system).system, system); assertSame(OtherExtension.key.apply(system).system, system);

View file

@ -189,15 +189,15 @@ before or after using them to construct an actor system:
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27). Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
Type in expressions to have them evaluated. Type in expressions to have them evaluated.
Type :help for more information. Type :help for more information.
scala> import com.typesafe.config._ scala> import com.typesafe.config._
import com.typesafe.config._ import com.typesafe.config._
scala> ConfigFactory.parseString("a.b=12") scala> ConfigFactory.parseString("a.b=12")
res0: com.typesafe.config.Config = Config(SimpleConfigObject({"a" : {"b" : 12}})) res0: com.typesafe.config.Config = Config(SimpleConfigObject({"a" : {"b" : 12}}))
scala> res0.root.render scala> res0.root.render
res1: java.lang.String = res1: java.lang.String =
{ {
# String: 1 # String: 1
"a" : { "a" : {
@ -217,3 +217,12 @@ and parsed by the actor system can be displayed like this:
println(system.settings()); println(system.settings());
// this is a shortcut for system.settings().config().root().render() // this is a shortcut for system.settings().config().root().render()
Application specific settings
-----------------------------
The configuration can also be used for application specific settings.
A good practice is to place those settings in an Extension, as described in:
* Scala API: :ref:`extending-akka-scala.settings`
* Java API: :ref:`extending-akka-java.settings`

View file

@ -9,63 +9,65 @@ import java.util.concurrent.atomic.AtomicLong;
//#imports //#imports
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*;
public class ExtensionDocTestBase { public class ExtensionDocTestBase {
//#extension //#extension
public static class CountExtensionImpl implements Extension { public static class CountExtensionImpl implements Extension {
//Since this Extension is a shared instance //Since this Extension is a shared instance
// per ActorSystem we need to be threadsafe // per ActorSystem we need to be threadsafe
private final AtomicLong counter = new AtomicLong(0); private final AtomicLong counter = new AtomicLong(0);
//This is the operation this Extension provides //This is the operation this Extension provides
public long increment() { public long increment() {
return counter.incrementAndGet(); return counter.incrementAndGet();
}
} }
//#extension }
//#extensionid //#extension
static class CountExtensionId extends AbstractExtensionId<CountExtensionImpl> {
//This method will be called by Akka
// to instantiate our Extension
public CountExtensionImpl createExtension(ActorSystemImpl i) {
return new CountExtensionImpl();
}
}
//#extensionid
public static class CountExtension extends AbstractExtensionId<CountExtensionImpl> implements ExtensionIdProvider {
//This will be the identifier of our CountExtension //This will be the identifier of our CountExtension
public final static CountExtensionId CountExtension = new CountExtensionId(); public final static CountExtension CountExtensionProvider = new CountExtension();
//#extensionid
//#extensionid-provider //The lookup method is required by ExtensionIdProvider,
static class CountExtensionIdProvider implements ExtensionIdProvider { // so we return ourselves here, this allows us
public CountExtensionId lookup() { // to configure our extension to be loaded when
return CountExtension; //The public static final // the ActorSystem starts up
} public CountExtension lookup() {
} return CountExtension.CountExtensionProvider; //The public static final
//#extensionid-provider
//#extension-usage-actor
static class MyActor extends UntypedActor {
public void onReceive(Object msg) {
CountExtension.get(getContext().system()).increment();
}
} }
//#extension-usage-actor
//This method will be called by Akka
// to instantiate our Extension
public CountExtensionImpl createExtension(ActorSystemImpl system) {
return new CountExtensionImpl();
}
}
@Test public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() { //#extensionid
//#extension-usage-actor
public static class MyActor extends UntypedActor {
public void onReceive(Object msg) {
// typically you would use static import of CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(getContext().system()).increment();
}
}
//#extension-usage-actor
@Test
public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() {
final ActorSystem system = null; final ActorSystem system = null;
try { try {
//#extension-usage //#extension-usage
CountExtension.get(system).increment(); // typically you would use static import of CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(system).increment();
//#extension-usage //#extension-usage
} catch(Exception e) { } catch (Exception e) {
//do nothing //do nothing
} }
} }

View file

@ -0,0 +1,8 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.docs.extension
import org.scalatest.junit.JUnitSuite
class SettingsExtensionDocTest extends SettingsExtensionDocTestBase with JUnitSuite

View file

@ -0,0 +1,87 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.docs.extension;
//#imports
import akka.actor.Extension;
import akka.actor.AbstractExtensionId;
import akka.actor.ExtensionIdProvider;
import akka.actor.ActorSystem;
import akka.actor.ActorSystemImpl;
import akka.util.Duration;
import com.typesafe.config.Config;
import java.util.concurrent.TimeUnit;
//#imports
import akka.actor.UntypedActor;
import org.junit.Test;
public class SettingsExtensionDocTestBase {
//#extension
public static class SettingsImpl implements Extension {
public final String DB_URI;
public final Duration CIRCUIT_BREAKER_TIMEOUT;
public SettingsImpl(Config config) {
DB_URI = config.getString(config.getString("myapp.db.uri"));
CIRCUIT_BREAKER_TIMEOUT = Duration.create(config.getMilliseconds("myapp.circuit-breaker.timeout"),
TimeUnit.MILLISECONDS);
}
}
//#extension
//#extensionid
public static class Settings extends AbstractExtensionId<SettingsImpl> implements ExtensionIdProvider {
public final static Settings SettingsProvider = new Settings();
public Settings lookup() {
return Settings.SettingsProvider;
}
public SettingsImpl createExtension(ActorSystemImpl system) {
return new SettingsImpl(system.settings().config());
}
}
//#extensionid
//#extension-usage-actor
public static class MyActor extends UntypedActor {
// typically you would use static import of CountExtension.CountExtensionProvider field
final SettingsImpl settings = Settings.SettingsProvider.get(getContext().system());
Connection connection = connect(settings.DB_URI, settings.CIRCUIT_BREAKER_TIMEOUT);
//#extension-usage-actor
public Connection connect(String dbUri, Duration circuitBreakerTimeout) {
return new Connection();
}
public void onReceive(Object msg) {
}
}
public static class Connection {
}
@Test
public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() {
final ActorSystem system = null;
try {
//#extension-usage
// typically you would use static import of CountExtension.CountExtensionProvider field
String dbUri = Settings.SettingsProvider.get(system).DB_URI;
//#extension-usage
} catch (Exception e) {
//do nothing
}
}
}

View file

@ -1,7 +1,8 @@
.. _extending-akka-java: .. _extending-akka-java:
Akka Extensions ########################
=============== Akka Extensions (Java)
########################
.. sidebar:: Contents .. sidebar:: Contents
@ -15,13 +16,13 @@ You can choose to have your Extension loaded on-demand or at ``ActorSystem`` cre
Details on how to make that happens are below, in the "Loading from Configuration" section. Details on how to make that happens are below, in the "Loading from Configuration" section.
.. warning:: .. warning::
Since an extension is a way to hook into Akka itself, the implementor of the extension needs to Since an extension is a way to hook into Akka itself, the implementor of the extension needs to
ensure the thread safety of his/her extension. ensure the thread safety of his/her extension.
Building an Extension Building an Extension
--------------------- =====================
So let's create a sample extension that just lets us count the number of times something has happened. So let's create a sample extension that just lets us count the number of times something has happened.
@ -33,7 +34,7 @@ First, we define what our ``Extension`` should do:
Then we need to create an ``ExtensionId`` for our extension so we can grab ahold of it. Then we need to create an ``ExtensionId`` for our extension so we can grab ahold of it.
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java .. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
:include: imports,extensionid :include: imports,extensionid
Wicked! Now all we need to do is to actually use it: Wicked! Now all we need to do is to actually use it:
@ -48,16 +49,37 @@ Or from inside of an Akka Actor:
That's all there is to it! That's all there is to it!
Loading from Configuration Loading from Configuration
-------------------------- ==========================
To be able to load extensions from your Akka configuration you must add FQCNs of implementations of either ``ExtensionId`` or ``ExtensionIdProvider`` To be able to load extensions from your Akka configuration you must add FQCNs of implementations of either ``ExtensionId`` or ``ExtensionIdProvider``
in the "akka.extensions" section of the config you provide to your ``ActorSystem``. in the "akka.extensions" section of the config you provide to your ``ActorSystem``.
.. includecode:: code/akka/docs/extension/ExtensionDocTestBase.java
:include: extensionid-provider
Applicability Applicability
------------- =============
The sky is the limit! The sky is the limit!
By the way, did you know that Akka's ``Typed Actors``, ``Serialization`` and other features are implemented as Akka Extensions? By the way, did you know that Akka's ``Typed Actors``, ``Serialization`` and other features are implemented as Akka Extensions?
.. _extending-akka-java.settings:
Application specific settings
-----------------------------
The :ref:`configuration` can be used for application specific settings. A good practice is to place those settings in an Extension.
Sample configuration:
.. includecode:: ../scala/code/akka/docs/extension/SettingsExtensionDocSpec.scala
:include: config
The ``Extension``:
.. includecode:: code/akka/docs/extension/SettingsExtensionDocTestBase.java
:include: imports,extension,extensionid
Use it:
.. includecode:: code/akka/docs/extension/SettingsExtensionDocTestBase.java
:include: extension-usage-actor

View file

@ -3,16 +3,13 @@
*/ */
package akka.docs.extension package akka.docs.extension
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
//#imports
import akka.actor._
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
import akka.actor.Actor
//#imports import akka.testkit.AkkaSpec
//#extension //#extension
import akka.actor.Extension
class CountExtensionImpl extends Extension { class CountExtensionImpl extends Extension {
//Since this Extension is a shared instance //Since this Extension is a shared instance
// per ActorSystem we need to be threadsafe // per ActorSystem we need to be threadsafe
@ -24,6 +21,10 @@ class CountExtensionImpl extends Extension {
//#extension //#extension
//#extensionid //#extensionid
import akka.actor.ExtensionId
import akka.actor.ExtensionIdProvider
import akka.actor.ActorSystemImpl
object CountExtension object CountExtension
extends ExtensionId[CountExtensionImpl] extends ExtensionId[CountExtensionImpl]
with ExtensionIdProvider { with ExtensionIdProvider {
@ -39,39 +40,37 @@ object CountExtension
} }
//#extensionid //#extensionid
//#extension-usage-actor object ExtensionDocSpec {
import akka.actor.Actor //#extension-usage-actor
class MyActor extends Actor { class MyActor extends Actor {
def receive = { def receive = {
case someMessage case someMessage
CountExtension(context.system).increment() CountExtension(context.system).increment()
}
}
//#extension-usage-actor
//#extension-usage-actor-trait
import akka.actor.Actor
trait Counting { self: Actor
def increment() = CountExtension(context.system).increment()
}
class MyCounterActor extends Actor with Counting {
def receive = {
case someMessage increment()
}
}
//#extension-usage-actor-trait
class ExtensionDocSpec extends WordSpec with MustMatchers {
"demonstrate how to create an extension in Scala" in {
val system: ActorSystem = null
intercept[Exception] {
//#extension-usage
CountExtension(system).increment
//#extension-usage
} }
} }
//#extension-usage-actor
//#extension-usage-actor-trait
trait Counting { self: Actor
def increment() = CountExtension(context.system).increment()
}
class MyCounterActor extends Actor with Counting {
def receive = {
case someMessage increment()
}
}
//#extension-usage-actor-trait
}
class ExtensionDocSpec extends AkkaSpec {
import ExtensionDocSpec._
"demonstrate how to create an extension in Scala" in {
//#extension-usage
CountExtension(system).increment
//#extension-usage
}
} }

View file

@ -0,0 +1,78 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.docs.extension
//#imports
import akka.actor.Extension
import akka.actor.ExtensionId
import akka.actor.ExtensionIdProvider
import akka.actor.ActorSystemImpl
import akka.util.Duration
import com.typesafe.config.Config
import java.util.concurrent.TimeUnit
//#imports
import akka.actor.Actor
import akka.testkit.AkkaSpec
//#extension
class SettingsImpl(config: Config) extends Extension {
val DbUri: String = config.getString("myapp.db.uri")
val CircuitBreakerTimeout: Duration = Duration(config.getMilliseconds("myapp.circuit-breaker.timeout"), TimeUnit.MILLISECONDS)
}
//#extension
//#extensionid
object Settings extends ExtensionId[SettingsImpl] with ExtensionIdProvider {
override def lookup = Settings
override def createExtension(system: ActorSystemImpl) = new SettingsImpl(system.settings.config)
}
//#extensionid
object SettingsExtensionDocSpec {
val config = """
//#config
myapp {
db {
uri = "mongodb://example1.com:27017,example2.com:27017"
}
circuit-breaker {
timeout = 30 seconds
}
}
//#config
"""
//#extension-usage-actor
class MyActor extends Actor {
val settings = Settings(context.system)
val connection = connect(settings.DbUri, settings.CircuitBreakerTimeout)
//#extension-usage-actor
def receive = {
case someMessage
}
def connect(dbUri: String, circuitBreakerTimeout: Duration) = {
"dummy"
}
}
}
class SettingsExtensionDocSpec extends AkkaSpec(SettingsExtensionDocSpec.config) {
"demonstrate how to create application specific settings extension in Scala" in {
//#extension-usage
val dbUri = Settings(system).DbUri
val circuitBreakerTimeout = Settings(system).CircuitBreakerTimeout
//#extension-usage
}
}

View file

@ -1,7 +1,9 @@
.. _extending-akka-scala: .. _extending-akka-scala:
Akka Extensions #########################
=============== Akka Extensions (Scala)
#########################
.. sidebar:: Contents .. sidebar:: Contents
@ -9,19 +11,19 @@ Akka Extensions
Building an Extension Building an Extension
--------------------- =====================
So let's create a sample extension that just lets us count the number of times something has happened. So let's create a sample extension that just lets us count the number of times something has happened.
First, we define what our ``Extension`` should do: First, we define what our ``Extension`` should do:
.. includecode:: code/akka/docs/extension/ExtensionDocSpec.scala .. includecode:: code/akka/docs/extension/ExtensionDocSpec.scala
:include: imports,extension :include: extension
Then we need to create an ``ExtensionId`` for our extension so we can grab ahold of it. Then we need to create an ``ExtensionId`` for our extension so we can grab ahold of it.
.. includecode:: code/akka/docs/extension/ExtensionDocSpec.scala .. includecode:: code/akka/docs/extension/ExtensionDocSpec.scala
:include: imports,extensionid :include: extensionid
Wicked! Now all we need to do is to actually use it: Wicked! Now all we need to do is to actually use it:
@ -41,13 +43,37 @@ You can also hide extension behind traits:
That's all there is to it! That's all there is to it!
Loading from Configuration Loading from Configuration
-------------------------- ==========================
To be able to load extensions from your Akka configuration you must add FQCNs of implementations of either ``ExtensionId`` or ``ExtensionIdProvider`` To be able to load extensions from your Akka configuration you must add FQCNs of implementations of either ``ExtensionId`` or ``ExtensionIdProvider``
in the "akka.extensions" section of the config you provide to your ``ActorSystem``. in the ``akka.extensions`` section of the config you provide to your ``ActorSystem``.
Applicability Applicability
------------- =============
The sky is the limit! The sky is the limit!
By the way, did you know that Akka's ``Typed Actors``, ``Serialization`` and other features are implemented as Akka Extensions? By the way, did you know that Akka's ``Typed Actors``, ``Serialization`` and other features are implemented as Akka Extensions?
.. _extending-akka-scala.settings:
Application specific settings
-----------------------------
The :ref:`configuration` can be used for application specific settings. A good practice is to place those settings in an Extension.
Sample configuration:
.. includecode:: code/akka/docs/extension/SettingsExtensionDocSpec.scala
:include: config
The ``Extension``:
.. includecode:: code/akka/docs/extension/SettingsExtensionDocSpec.scala
:include: imports,extension,extensionid
Use it:
.. includecode:: code/akka/docs/extension/SettingsExtensionDocSpec.scala
:include: extension-usage-actor

View file

@ -1,8 +1,8 @@
.. _akka-testkit: .. _akka-testkit:
##################### ##############################
Testing Actor Systems Testing Actor Systems (Scala)
##################### ##############################
.. toctree:: .. toctree::

View file

@ -1,8 +1,8 @@
.. _testkit-example: .. _testkit-example:
############### ########################
TestKit Example TestKit Example (Scala)
############### ########################
Ray Roestenburg's example code from `his blog <http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html>`_ adapted to work with Akka 1.1. Ray Roestenburg's example code from `his blog <http://roestenburg.agilesquad.com/2011/02/unit-testing-akka-actors-with-testkit_12.html>`_ adapted to work with Akka 1.1.