diff --git a/akka-actor-tests/src/test/java/akka/actor/JavaExtension.java b/akka-actor-tests/src/test/java/akka/actor/JavaExtension.java index 5b4c7b1171..2600188cac 100644 --- a/akka-actor-tests/src/test/java/akka/actor/JavaExtension.java +++ b/akka-actor-tests/src/test/java/akka/actor/JavaExtension.java @@ -13,18 +13,25 @@ import static org.junit.Assert.*; public class JavaExtension { - static class Provider implements ExtensionProvider { - public Extension lookup() { return defaultInstance; } + static class Provider implements ExtensionIdProvider { + public ExtensionId lookup() { return defaultInstance; } } - public final static TestExtension defaultInstance = new TestExtension(); + public final static TestExtensionId defaultInstance = new TestExtensionId(); - static class TestExtension extends AbstractExtension { - public ActorSystemImpl createExtension(ActorSystemImpl i) { - return i; + static class TestExtensionId extends AbstractExtensionId { + public TestExtension createExtension(ActorSystemImpl i) { + return new TestExtension(i); } } + static class TestExtension implements Extension { + public final ActorSystemImpl system; + public TestExtension(ActorSystemImpl i) { + system = i; + } + } + private Config c = ConfigFactory.parseString("akka.extensions = [ \"akka.actor.JavaExtension$Provider\" ]", ConfigParseOptions.defaults()); @@ -32,8 +39,8 @@ public class JavaExtension { @Test public void mustBeAccessible() { - assertSame(system.extension(defaultInstance), system); - assertSame(defaultInstance.apply(system), system); + assertSame(system.extension(defaultInstance).system, system); + assertSame(defaultInstance.apply(system).system, system); } } diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala index 611a4400d7..3565cde2fb 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ActorSystemSpec.scala @@ -10,10 +10,12 @@ import com.typesafe.config.ConfigFactory class JavaExtensionSpec extends JavaExtension with JUnitSuite object ActorSystemSpec { - object TestExtension extends Extension[ActorSystem] with ExtensionProvider { + object TestExtension extends ExtensionId[TestExtension] with ExtensionIdProvider { def lookup = this - def createExtension(s: ActorSystemImpl) = s + def createExtension(s: ActorSystemImpl) = new TestExtension(s) } + + class TestExtension(val system: ActorSystemImpl) extends Extension } class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.ActorSystemSpec$TestExtension$"]""") { @@ -22,8 +24,8 @@ class ActorSystemSpec extends AkkaSpec("""akka.extensions = ["akka.actor.ActorSy "An ActorSystem" must { "support extensions" in { - TestExtension(system) must be === system - system.extension(TestExtension) must be === system + TestExtension(system).system must be === system + system.extension(TestExtension).system must be === system system.hasExtension(TestExtension) must be(true) } diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index 942884002f..83d7aa9d3c 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -256,7 +256,7 @@ abstract class ActorSystem extends ActorRefFactory with TypedActorFactory { * This method has putIfAbsent-semantics, this method can potentially block, waiting for the initialization * of the payload, if is in the process of registration from another Thread of execution */ - def registerExtension[T <: AnyRef](ext: Extension[T]): T + def registerExtension[T <: Extension](ext: ExtensionId[T]): T /** * Returns the payload that is associated with the provided extension @@ -264,13 +264,13 @@ abstract class ActorSystem extends ActorRefFactory with TypedActorFactory { * This method can potentially block, waiting for the initialization * of the payload, if is in the process of registration from another Thread of execution */ - def extension[T <: AnyRef](ext: Extension[T]): T + def extension[T <: Extension](ext: ExtensionId[T]): T /** * Returns whether the specified extension is already registered, this method can potentially block, waiting for the initialization * of the payload, if is in the process of registration from another Thread of execution */ - def hasExtension(ext: Extension[_ <: AnyRef]): Boolean + def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean } class ActorSystemImpl(val name: String, val applicationConfig: Config) extends ActorSystem { @@ -367,28 +367,30 @@ class ActorSystemImpl(val name: String, val applicationConfig: Config) extends A terminationFuture onComplete (_ ⇒ dispatcher.shutdown()) } - private val extensions = new ConcurrentIdentityHashMap[Extension[_], AnyRef] + private val extensions = new ConcurrentIdentityHashMap[ExtensionId[_], AnyRef] /** * Returns any extension registered to the specified Extension or returns null if not registered */ @tailrec - private def findExtension[T <: AnyRef](ext: Extension[T]): T = extensions.get(ext) match { + private def findExtension[T <: Extension](ext: ExtensionId[T]): T = extensions.get(ext) match { case c: CountDownLatch ⇒ c.await(); findExtension(ext) //Registration in process, await completion and retry case other ⇒ other.asInstanceOf[T] //could be a T or null, in which case we return the null as T } @tailrec - final def registerExtension[T <: AnyRef](ext: Extension[T]): T = { + final def registerExtension[T <: Extension](ext: ExtensionId[T]): T = { findExtension(ext) match { case null ⇒ //Doesn't already exist, commence registration val inProcessOfRegistration = new CountDownLatch(1) extensions.putIfAbsent(ext, inProcessOfRegistration) match { // Signal that registration is in process case null ⇒ try { // Signal was successfully sent - val instance = ext.createExtension(this) // Create and initialize the extension - if (instance == null) throw new IllegalStateException("Extension instance created as null for Extension: " + ext) - extensions.replace(ext, inProcessOfRegistration, instance) //Replace our in process signal with the initialized extension - instance //Profit! + ext.createExtension(this) match { // Create and initialize the extension + case null ⇒ throw new IllegalStateException("Extension instance created as null for Extension: " + ext) + case instance ⇒ + extensions.replace(ext, inProcessOfRegistration, instance) //Replace our in process signal with the initialized extension + instance //Profit! + } } catch { case t ⇒ extensions.remove(ext, inProcessOfRegistration) //In case shit hits the fan, remove the inProcess signal @@ -402,21 +404,22 @@ class ActorSystemImpl(val name: String, val applicationConfig: Config) extends A } } - def extension[T <: AnyRef](ext: Extension[T]): T = findExtension(ext) match { + def extension[T <: Extension](ext: ExtensionId[T]): T = findExtension(ext) match { case null ⇒ throw new IllegalArgumentException("Trying to get non-registered extension " + ext) case some ⇒ some.asInstanceOf[T] } - def hasExtension(ext: Extension[_ <: AnyRef]): Boolean = findExtension(ext) != null + def hasExtension(ext: ExtensionId[_ <: Extension]): Boolean = findExtension(ext) != null private def loadExtensions() { import scala.collection.JavaConversions._ settings.config.getStringList("akka.extensions") foreach { fqcn ⇒ import ReflectiveAccess._ - getObjectFor[ExtensionProvider](fqcn).fold(_ ⇒ createInstance[ExtensionProvider](fqcn, noParams, noArgs), Right(_)) match { - case Right(p: ExtensionProvider) ⇒ registerExtension(p.lookup()) - case Right(other) ⇒ log.error("'{}' is not an ExtensionProvider, skipping...", fqcn) - case Left(problem) ⇒ log.error(problem, "While trying to load extension '{}', skipping...", fqcn) + getObjectFor[AnyRef](fqcn).fold(_ ⇒ createInstance[AnyRef](fqcn, noParams, noArgs), Right(_)) match { + case Right(p: ExtensionIdProvider) ⇒ registerExtension(p.lookup()); + case Right(p: ExtensionId[_]) ⇒ registerExtension(p); + case Right(other) ⇒ log.error("'{}' is not an ExtensionIdProvider or ExtensionId, skipping...", fqcn) + case Left(problem) ⇒ log.error(problem, "While trying to load extension '{}', skipping...", fqcn) } } diff --git a/akka-actor/src/main/scala/akka/actor/Extension.scala b/akka-actor/src/main/scala/akka/actor/Extension.scala index 3aaeaf645b..3850ef4462 100644 --- a/akka-actor/src/main/scala/akka/actor/Extension.scala +++ b/akka-actor/src/main/scala/akka/actor/Extension.scala @@ -17,21 +17,35 @@ package akka.actor * to the ActorSystem implementation. * */ -trait Extension[T <: AnyRef] { + +/** + * Market interface to signify an Akka Extension + */ +trait Extension + +/** + * Identifies an Extension + * Lookup of Extensions is done by object identity, so the Id must be the same wherever it's used, + * otherwise you'll get the same extension loaded multiple times. + */ +trait ExtensionId[T <: Extension] { def apply(system: ActorSystem): T = system.registerExtension(this) def createExtension(system: ActorSystemImpl): T } /** - * Java API for Extension + * Java API for ExtensionId */ -abstract class AbstractExtension[T <: AnyRef] extends Extension[T] +abstract class AbstractExtensionId[T <: Extension] extends ExtensionId[T] /** - * To be able to load an Extension from the configuration, - * a class that implements ExtensionProvider must be specified. + * To be able to load an ExtensionId from the configuration, + * a class that implements ExtensionIdProvider must be specified. * The lookup method should return the canonical reference to the extension. */ -trait ExtensionProvider { - def lookup(): Extension[_ <: AnyRef] +trait ExtensionIdProvider { + /** + * Returns the canonical ExtensionId for this Extension + */ + def lookup(): ExtensionId[_ <: Extension] } diff --git a/akka-actor/src/main/scala/akka/serialization/Serialization.scala b/akka-actor/src/main/scala/akka/serialization/Serialization.scala index a9e1e37149..7232375fa8 100644 --- a/akka-actor/src/main/scala/akka/serialization/Serialization.scala +++ b/akka-actor/src/main/scala/akka/serialization/Serialization.scala @@ -6,11 +6,11 @@ package akka.serialization import akka.AkkaException import akka.util.ReflectiveAccess -import akka.actor.{ ActorSystem, ActorSystemImpl } import scala.util.DynamicVariable import com.typesafe.config.{ ConfigRoot, ConfigParseOptions, ConfigFactory, Config } import com.typesafe.config.Config._ import akka.config.ConfigurationException +import akka.actor.{ Extension, ActorSystem, ActorSystemImpl } case class NoSerializerFoundException(m: String) extends AkkaException(m) @@ -55,7 +55,7 @@ object Serialization { * Serialization module. Contains methods for serialization and deserialization as well as * locating a Serializer for a particular class as defined in the mapping in the 'akka.conf' file. */ -class Serialization(val system: ActorSystemImpl) { +class Serialization(val system: ActorSystemImpl) extends Extension { import Serialization._ val settings = new Settings(system.applicationConfig) diff --git a/akka-actor/src/main/scala/akka/serialization/SerializationExtension.scala b/akka-actor/src/main/scala/akka/serialization/SerializationExtension.scala index 4e32813d2e..a53ba832c7 100644 --- a/akka-actor/src/main/scala/akka/serialization/SerializationExtension.scala +++ b/akka-actor/src/main/scala/akka/serialization/SerializationExtension.scala @@ -2,9 +2,10 @@ * Copyright (C) 2009-2011 Typesafe Inc. */ package akka.serialization -import akka.actor.{ ExtensionProvider, Extension, ActorSystemImpl } -object SerializationExtension extends Extension[Serialization] with ExtensionProvider { +import akka.actor.{ ExtensionId, ExtensionIdProvider, ActorSystemImpl } + +object SerializationExtension extends ExtensionId[Serialization] with ExtensionIdProvider { override def lookup = SerializationExtension override def createExtension(system: ActorSystemImpl): Serialization = new Serialization(system) } \ No newline at end of file diff --git a/akka-durable-mailboxes/akka-beanstalk-mailbox/src/main/scala/akka/actor/mailbox/BeanstalkBasedMailboxExtension.scala b/akka-durable-mailboxes/akka-beanstalk-mailbox/src/main/scala/akka/actor/mailbox/BeanstalkBasedMailboxExtension.scala index 9f01e333d4..5f6fd40708 100644 --- a/akka-durable-mailboxes/akka-beanstalk-mailbox/src/main/scala/akka/actor/mailbox/BeanstalkBasedMailboxExtension.scala +++ b/akka-durable-mailboxes/akka-beanstalk-mailbox/src/main/scala/akka/actor/mailbox/BeanstalkBasedMailboxExtension.scala @@ -9,14 +9,14 @@ import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRoot import akka.util.Duration import java.util.concurrent.TimeUnit.MILLISECONDS -import akka.actor.{ ExtensionProvider, ActorSystem, Extension, ActorSystemImpl } +import akka.actor._ -object BeanstalkBasedMailboxExtension extends Extension[BeanstalkMailboxSettings] with ExtensionProvider { +object BeanstalkBasedMailboxExtension extends ExtensionId[BeanstalkMailboxSettings] with ExtensionIdProvider { def lookup() = this def createExtension(system: ActorSystemImpl) = new BeanstalkMailboxSettings(system.applicationConfig) } -class BeanstalkMailboxSettings(cfg: Config) { +class BeanstalkMailboxSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-beanstalk-mailbox-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false)) diff --git a/akka-durable-mailboxes/akka-file-mailbox/src/main/scala/akka/actor/mailbox/FileBasedMailboxExtension.scala b/akka-durable-mailboxes/akka-file-mailbox/src/main/scala/akka/actor/mailbox/FileBasedMailboxExtension.scala index 58d54ad425..1bdf9ae958 100644 --- a/akka-durable-mailboxes/akka-file-mailbox/src/main/scala/akka/actor/mailbox/FileBasedMailboxExtension.scala +++ b/akka-durable-mailboxes/akka-file-mailbox/src/main/scala/akka/actor/mailbox/FileBasedMailboxExtension.scala @@ -9,14 +9,14 @@ import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRoot import akka.util.Duration import java.util.concurrent.TimeUnit.MILLISECONDS -import akka.actor.{ ExtensionProvider, ActorSystem, Extension, ActorSystemImpl } +import akka.actor._ -object FileBasedMailboxExtension extends Extension[FileBasedMailboxSettings] with ExtensionProvider { +object FileBasedMailboxExtension extends ExtensionId[FileBasedMailboxSettings] with ExtensionIdProvider { def lookup() = this def createExtension(system: ActorSystemImpl) = new FileBasedMailboxSettings(system.applicationConfig) } -class FileBasedMailboxSettings(cfg: Config) { +class FileBasedMailboxSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-file-mailbox-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false)) diff --git a/akka-durable-mailboxes/akka-mongo-mailbox/src/main/scala/akka/actor/mailbox/MongoBasedMailboxExtension.scala b/akka-durable-mailboxes/akka-mongo-mailbox/src/main/scala/akka/actor/mailbox/MongoBasedMailboxExtension.scala index 2e8c000f59..88eb95438c 100644 --- a/akka-durable-mailboxes/akka-mongo-mailbox/src/main/scala/akka/actor/mailbox/MongoBasedMailboxExtension.scala +++ b/akka-durable-mailboxes/akka-mongo-mailbox/src/main/scala/akka/actor/mailbox/MongoBasedMailboxExtension.scala @@ -11,12 +11,12 @@ import akka.util.Duration import java.util.concurrent.TimeUnit.MILLISECONDS import akka.actor._ -object MongoBasedMailboxExtension extends Extension[MongoBasedMailboxSettings] with ExtensionProvider { +object MongoBasedMailboxExtension extends ExtensionId[MongoBasedMailboxSettings] with ExtensionIdProvider { def lookup() = this def createExtension(system: ActorSystemImpl) = new MongoBasedMailboxSettings(system.applicationConfig) } -class MongoBasedMailboxSettings(cfg: Config) { +class MongoBasedMailboxSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-mongo-mailbox-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false)) diff --git a/akka-durable-mailboxes/akka-redis-mailbox/src/main/scala/akka/actor/mailbox/RedisBasedMailboxExtension.scala b/akka-durable-mailboxes/akka-redis-mailbox/src/main/scala/akka/actor/mailbox/RedisBasedMailboxExtension.scala index 2d48c536f3..beccf4051f 100644 --- a/akka-durable-mailboxes/akka-redis-mailbox/src/main/scala/akka/actor/mailbox/RedisBasedMailboxExtension.scala +++ b/akka-durable-mailboxes/akka-redis-mailbox/src/main/scala/akka/actor/mailbox/RedisBasedMailboxExtension.scala @@ -7,14 +7,14 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRoot -import akka.actor.{ ExtensionProvider, ActorSystem, Extension, ActorSystemImpl } +import akka.actor._ -object RedisBasedMailboxExtension extends Extension[RedisBasedMailboxSettings] with ExtensionProvider { +object RedisBasedMailboxExtension extends ExtensionId[RedisBasedMailboxSettings] with ExtensionIdProvider { def lookup() = this def createExtension(system: ActorSystemImpl) = new RedisBasedMailboxSettings(system.applicationConfig) } -class RedisBasedMailboxSettings(cfg: Config) { +class RedisBasedMailboxSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-redis-mailbox-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false)) diff --git a/akka-durable-mailboxes/akka-zookeeper-mailbox/src/main/scala/akka/actor/mailbox/ZooKeeperBasedMailboxExtension.scala b/akka-durable-mailboxes/akka-zookeeper-mailbox/src/main/scala/akka/actor/mailbox/ZooKeeperBasedMailboxExtension.scala index db88d399e9..e2b0ad45f7 100644 --- a/akka-durable-mailboxes/akka-zookeeper-mailbox/src/main/scala/akka/actor/mailbox/ZooKeeperBasedMailboxExtension.scala +++ b/akka-durable-mailboxes/akka-zookeeper-mailbox/src/main/scala/akka/actor/mailbox/ZooKeeperBasedMailboxExtension.scala @@ -9,13 +9,13 @@ import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRoot import akka.util.Duration import java.util.concurrent.TimeUnit.MILLISECONDS -import akka.actor.{ ExtensionProvider, ActorSystem, Extension, ActorSystemImpl } +import akka.actor._ -object ZooKeeperBasedMailboxExtension extends Extension[ZooKeeperBasedMailboxSettings] with ExtensionProvider { +object ZooKeeperBasedMailboxExtension extends ExtensionId[ZooKeeperBasedMailboxSettings] with ExtensionIdProvider { def lookup() = this def createExtension(system: ActorSystemImpl) = new ZooKeeperBasedMailboxSettings(system.applicationConfig) } -class ZooKeeperBasedMailboxSettings(cfg: Config) { +class ZooKeeperBasedMailboxSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-zookeeper-mailbox-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false)) diff --git a/akka-remote/src/main/scala/akka/remote/RemoteExtension.scala b/akka-remote/src/main/scala/akka/remote/RemoteExtension.scala index 763e747fe8..70b15d2967 100644 --- a/akka-remote/src/main/scala/akka/remote/RemoteExtension.scala +++ b/akka-remote/src/main/scala/akka/remote/RemoteExtension.scala @@ -14,12 +14,12 @@ import akka.config.ConfigurationException import com.eaio.uuid.UUID import akka.actor._ -object RemoteExtension extends Extension[RemoteExtensionSettings] with ExtensionProvider { +object RemoteExtension extends ExtensionId[RemoteExtensionSettings] with ExtensionIdProvider { def lookup() = this def createExtension(system: ActorSystemImpl) = new RemoteExtensionSettings(system.applicationConfig) } -class RemoteExtensionSettings(cfg: Config) { +class RemoteExtensionSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-remote-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false)) diff --git a/akka-testkit/src/main/scala/akka/testkit/TestKitExtension.scala b/akka-testkit/src/main/scala/akka/testkit/TestKitExtension.scala index 7d2477dce3..5af1bde50a 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestKitExtension.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestKitExtension.scala @@ -3,21 +3,19 @@ */ package akka.testkit -import akka.actor.ActorSystem -import akka.actor.Extension -import akka.actor.ActorSystemImpl import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRoot import akka.util.Duration import java.util.concurrent.TimeUnit.MILLISECONDS +import akka.actor.{ ExtensionId, ActorSystem, Extension, ActorSystemImpl } -object TestKitExtension extends Extension[TestKitSettings] { +object TestKitExtension extends ExtensionId[TestKitSettings] { def createExtension(system: ActorSystemImpl): TestKitSettings = new TestKitSettings(system.applicationConfig) } -class TestKitSettings(cfg: Config) { +class TestKitSettings(cfg: Config) extends Extension { private def referenceConfig: Config = ConfigFactory.parseResource(classOf[ActorSystem], "/akka-testkit-reference.conf", ConfigParseOptions.defaults.setAllowMissing(false))