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);
} }
@ -39,9 +37,11 @@ public class JavaExtension {
} }
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,8 +64,8 @@ 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

View file

@ -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,10 +9,7 @@ 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 {
@ -27,45 +24,50 @@ public class ExtensionDocTestBase {
return counter.incrementAndGet(); return counter.incrementAndGet();
} }
} }
//#extension //#extension
//#extensionid //#extensionid
static class CountExtensionId extends AbstractExtensionId<CountExtensionImpl> { public static class CountExtension extends AbstractExtensionId<CountExtensionImpl> implements ExtensionIdProvider {
//This will be the identifier of our CountExtension
public final static CountExtension CountExtensionProvider = new CountExtension();
//The lookup method is required by ExtensionIdProvider,
// so we return ourselves here, this allows us
// to configure our extension to be loaded when
// the ActorSystem starts up
public CountExtension lookup() {
return CountExtension.CountExtensionProvider; //The public static final
}
//This method will be called by Akka //This method will be called by Akka
// to instantiate our Extension // to instantiate our Extension
public CountExtensionImpl createExtension(ActorSystemImpl i) { public CountExtensionImpl createExtension(ActorSystemImpl system) {
return new CountExtensionImpl(); return new CountExtensionImpl();
} }
} }
//This will be the identifier of our CountExtension
public final static CountExtensionId CountExtension = new CountExtensionId();
//#extensionid //#extensionid
//#extensionid-provider
static class CountExtensionIdProvider implements ExtensionIdProvider {
public CountExtensionId lookup() {
return CountExtension; //The public static final
}
}
//#extensionid-provider
//#extension-usage-actor //#extension-usage-actor
static class MyActor extends UntypedActor { public static class MyActor extends UntypedActor {
public void onReceive(Object msg) { public void onReceive(Object msg) {
CountExtension.get(getContext().system()).increment(); // typically you would use static import of CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(getContext().system()).increment();
} }
} }
//#extension-usage-actor //#extension-usage-actor
@Test
@Test public void demonstrateHowToCreateAndUseAnAkkaExtensionInJava() { 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
@ -21,7 +22,7 @@ Details on how to make that happens are below, in the "Loading from Configuratio
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.
@ -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
//#extension-usage-actor-trait //#extension-usage-actor-trait
import akka.actor.Actor
trait Counting { self: Actor trait Counting { self: Actor
def increment() = CountExtension(context.system).increment() def increment() = CountExtension(context.system).increment()
} }
class MyCounterActor extends Actor with Counting { class MyCounterActor extends Actor with Counting {
def receive = { def receive = {
case someMessage increment() case someMessage increment()
} }
}
//#extension-usage-actor-trait
} }
//#extension-usage-actor-trait
class ExtensionDocSpec extends WordSpec with MustMatchers { class ExtensionDocSpec extends AkkaSpec {
import ExtensionDocSpec._
"demonstrate how to create an extension in Scala" in { "demonstrate how to create an extension in Scala" in {
val system: ActorSystem = null
intercept[Exception] {
//#extension-usage //#extension-usage
CountExtension(system).increment CountExtension(system).increment
//#extension-usage //#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.