2011-04-27 00:40:20 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.actor
|
|
|
|
|
|
|
|
|
|
import collection.immutable.Seq
|
|
|
|
|
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap
|
|
|
|
|
|
|
|
|
|
import akka.event.EventHandler
|
|
|
|
|
import akka.actor.DeploymentConfig._
|
2011-05-18 17:25:30 +02:00
|
|
|
import akka.config.{ ConfigurationException, Config }
|
2011-05-24 19:04:25 +02:00
|
|
|
import akka.routing.RouterType
|
2011-05-20 17:13:39 +02:00
|
|
|
import akka.util.ReflectiveAccess._
|
2011-06-07 06:36:21 +05:30
|
|
|
import akka.serialization._
|
2011-04-29 15:47:56 +02:00
|
|
|
import akka.AkkaException
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
/**
|
2011-05-20 17:13:39 +02:00
|
|
|
* Module holding the programmatic deployment configuration classes.
|
|
|
|
|
* Defines the deployment specification.
|
|
|
|
|
* Most values have defaults and can be left out.
|
2011-04-27 00:40:20 +02:00
|
|
|
*
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
|
|
|
|
object DeploymentConfig {
|
|
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// --- Deploy
|
|
|
|
|
// --------------------------------
|
2011-05-20 09:08:11 +02:00
|
|
|
case class Deploy(
|
|
|
|
|
address: String,
|
|
|
|
|
routing: Routing = Direct,
|
2011-06-07 06:36:21 +05:30
|
|
|
format: String = Serializer.defaultSerializerName, // Format.defaultSerializerName,
|
2011-05-20 09:08:11 +02:00
|
|
|
scope: Scope = Local)
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// --- Routing
|
|
|
|
|
// --------------------------------
|
|
|
|
|
sealed trait Routing
|
|
|
|
|
case class CustomRouter(router: AnyRef) extends Routing
|
|
|
|
|
|
|
|
|
|
// For Java API
|
|
|
|
|
case class Direct() extends Routing
|
|
|
|
|
case class RoundRobin() extends Routing
|
|
|
|
|
case class Random() extends Routing
|
|
|
|
|
case class LeastCPU() extends Routing
|
|
|
|
|
case class LeastRAM() extends Routing
|
|
|
|
|
case class LeastMessages() extends Routing
|
|
|
|
|
|
|
|
|
|
// For Scala API
|
|
|
|
|
case object Direct extends Routing
|
|
|
|
|
case object RoundRobin extends Routing
|
|
|
|
|
case object Random extends Routing
|
|
|
|
|
case object LeastCPU extends Routing
|
|
|
|
|
case object LeastRAM extends Routing
|
|
|
|
|
case object LeastMessages extends Routing
|
|
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// --- Scope
|
|
|
|
|
// --------------------------------
|
|
|
|
|
sealed trait Scope
|
|
|
|
|
case class Clustered(
|
2011-05-04 21:25:47 +02:00
|
|
|
home: Home = Host("localhost"),
|
2011-06-10 16:31:24 +01:00
|
|
|
replicas: Replicas = NoReplicas,
|
|
|
|
|
replication: ReplicationScheme = Transient) extends Scope
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Java API
|
|
|
|
|
case class Local() extends Scope
|
|
|
|
|
|
|
|
|
|
// For Scala API
|
|
|
|
|
case object Local extends Scope
|
|
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// --- Home
|
|
|
|
|
// --------------------------------
|
2011-05-04 21:25:47 +02:00
|
|
|
sealed trait Home
|
|
|
|
|
case class Host(hostName: String) extends Home
|
|
|
|
|
case class Node(nodeName: String) extends Home
|
|
|
|
|
case class IP(ipAddress: String) extends Home
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
2011-06-10 16:31:24 +01:00
|
|
|
// --- Replicas
|
2011-04-27 00:40:20 +02:00
|
|
|
// --------------------------------
|
2011-06-10 16:31:24 +01:00
|
|
|
sealed trait Replicas
|
|
|
|
|
case class Replicate(factor: Int) extends Replicas {
|
|
|
|
|
if (factor < 1) throw new IllegalArgumentException("Replicas factor can not be negative or zero")
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For Java API
|
2011-06-10 16:31:24 +01:00
|
|
|
case class AutoReplicate() extends Replicas
|
|
|
|
|
case class NoReplicas() extends Replicas
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Scala API
|
2011-06-10 16:31:24 +01:00
|
|
|
case object AutoReplicate extends Replicas
|
|
|
|
|
case object NoReplicas extends Replicas
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
2011-06-10 16:31:24 +01:00
|
|
|
// --- Replication
|
2011-04-27 00:40:20 +02:00
|
|
|
// --------------------------------
|
2011-06-10 16:31:24 +01:00
|
|
|
sealed trait ReplicationScheme
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Java API
|
2011-06-10 16:31:24 +01:00
|
|
|
case class Transient() extends ReplicationScheme
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Scala API
|
2011-06-10 16:31:24 +01:00
|
|
|
case object Transient extends ReplicationScheme
|
|
|
|
|
case class Replication(
|
2011-06-07 11:10:29 -07:00
|
|
|
storage: ReplicationStorage,
|
2011-06-10 16:31:24 +01:00
|
|
|
strategy: ReplicationStrategy) extends ReplicationScheme
|
2011-06-07 11:10:29 -07:00
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// --- ReplicationStorage
|
|
|
|
|
// --------------------------------
|
|
|
|
|
sealed trait ReplicationStorage
|
|
|
|
|
|
|
|
|
|
// For Java API
|
|
|
|
|
case class TransactionLog() extends ReplicationStorage
|
|
|
|
|
case class DataGrid() extends ReplicationStorage
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Scala API
|
2011-06-07 11:10:29 -07:00
|
|
|
case object TransactionLog extends ReplicationStorage
|
|
|
|
|
case object DataGrid extends ReplicationStorage
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
2011-06-07 11:10:29 -07:00
|
|
|
// --- ReplicationStrategy
|
2011-04-27 00:40:20 +02:00
|
|
|
// --------------------------------
|
2011-06-07 11:10:29 -07:00
|
|
|
sealed trait ReplicationStrategy
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Java API
|
2011-06-07 11:10:29 -07:00
|
|
|
case class WriteBehind() extends ReplicationStrategy
|
|
|
|
|
case class WriteThrough() extends ReplicationStrategy
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// For Scala API
|
2011-06-07 11:10:29 -07:00
|
|
|
case object WriteBehind extends ReplicationStrategy
|
|
|
|
|
case object WriteThrough extends ReplicationStrategy
|
2011-05-24 19:04:25 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// --- Helper methods for parsing
|
|
|
|
|
// --------------------------------
|
|
|
|
|
|
|
|
|
|
def isHomeNode(home: Home): Boolean = home match {
|
|
|
|
|
case Host(hostname) ⇒ hostname == Config.hostname
|
2011-06-07 20:10:08 -07:00
|
|
|
case IP(address) ⇒ address == "0.0.0.0" || address == "127.0.0.1" // FIXME look up IP address from the system
|
2011-05-24 19:04:25 +02:00
|
|
|
case Node(nodename) ⇒ nodename == Config.nodename
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-10 16:31:24 +01:00
|
|
|
def replicaValueFor(replicas: Replicas): Int = replicas match {
|
2011-05-24 19:04:25 +02:00
|
|
|
case Replicate(replicas) ⇒ replicas
|
|
|
|
|
case AutoReplicate ⇒ -1
|
|
|
|
|
case AutoReplicate() ⇒ -1
|
|
|
|
|
case NoReplicas ⇒ 0
|
|
|
|
|
case NoReplicas() ⇒ 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def routerTypeFor(routing: Routing): RouterType = routing match {
|
|
|
|
|
case Direct ⇒ RouterType.Direct
|
|
|
|
|
case Direct() ⇒ RouterType.Direct
|
|
|
|
|
case RoundRobin ⇒ RouterType.RoundRobin
|
|
|
|
|
case RoundRobin() ⇒ RouterType.RoundRobin
|
|
|
|
|
case Random ⇒ RouterType.Random
|
|
|
|
|
case Random() ⇒ RouterType.Random
|
|
|
|
|
case LeastCPU ⇒ RouterType.LeastCPU
|
|
|
|
|
case LeastCPU() ⇒ RouterType.LeastCPU
|
|
|
|
|
case LeastRAM ⇒ RouterType.LeastRAM
|
|
|
|
|
case LeastRAM() ⇒ RouterType.LeastRAM
|
|
|
|
|
case LeastMessages ⇒ RouterType.LeastMessages
|
|
|
|
|
case LeastMessages() ⇒ RouterType.LeastMessages
|
2011-06-04 16:00:14 -07:00
|
|
|
case c: CustomRouter ⇒ throw new UnsupportedOperationException("routerTypeFor: " + c)
|
2011-05-24 19:04:25 +02:00
|
|
|
}
|
2011-06-10 16:31:24 +01:00
|
|
|
|
|
|
|
|
def isReplicationAsync(strategy: ReplicationStrategy): Boolean = strategy match {
|
|
|
|
|
case _: WriteBehind | WriteBehind ⇒ true
|
|
|
|
|
case _: WriteThrough | WriteThrough ⇒ false
|
|
|
|
|
}
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deployer maps actor deployments to actor addresses.
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
|
|
|
|
object Deployer {
|
2011-05-03 21:04:45 +02:00
|
|
|
|
2011-05-18 14:15:17 +02:00
|
|
|
val defaultAddress = Host(Config.hostname)
|
2011-05-03 21:04:45 +02:00
|
|
|
|
2011-05-20 17:13:39 +02:00
|
|
|
lazy val instance: ClusterModule.ClusterDeployer = {
|
2011-05-03 21:04:45 +02:00
|
|
|
val deployer =
|
2011-05-20 17:13:39 +02:00
|
|
|
if (ClusterModule.isEnabled) ClusterModule.clusterDeployer
|
2011-05-03 21:04:45 +02:00
|
|
|
else LocalDeployer
|
|
|
|
|
deployer.init(deploymentsInConfig)
|
|
|
|
|
deployer
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-25 16:18:35 +02:00
|
|
|
def start() {
|
|
|
|
|
instance.toString
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-03 21:04:45 +02:00
|
|
|
def shutdown() {
|
|
|
|
|
instance.shutdown()
|
|
|
|
|
}
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
def deploy(deployment: Deploy) {
|
|
|
|
|
if (deployment eq null) throw new IllegalArgumentException("Deploy can not be null")
|
|
|
|
|
val address = deployment.address
|
|
|
|
|
Address.validate(address)
|
2011-05-03 21:04:45 +02:00
|
|
|
instance.deploy(deployment)
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
|
2011-04-30 15:44:46 +02:00
|
|
|
def deploy(deployment: Seq[Deploy]) {
|
|
|
|
|
deployment foreach (deploy(_))
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 00:40:20 +02:00
|
|
|
/**
|
|
|
|
|
* Undeploy is idemponent. E.g. safe to invoke multiple times.
|
|
|
|
|
*/
|
|
|
|
|
def undeploy(deployment: Deploy) {
|
2011-05-03 21:04:45 +02:00
|
|
|
instance.undeploy(deployment)
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def undeployAll() {
|
2011-05-03 21:04:45 +02:00
|
|
|
instance.undeployAll()
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
|
2011-05-03 21:04:45 +02:00
|
|
|
def isLocal(deployment: Deploy): Boolean = deployment match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case Deploy(_, _, _, Local) ⇒ true
|
|
|
|
|
case _ ⇒ false
|
2011-05-03 21:04:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def isClustered(deployment: Deploy): Boolean = isLocal(deployment)
|
|
|
|
|
|
|
|
|
|
def isLocal(address: String): Boolean = isLocal(deploymentFor(address))
|
|
|
|
|
|
|
|
|
|
def isClustered(address: String): Boolean = !isLocal(address)
|
|
|
|
|
|
2011-04-27 15:00:41 +02:00
|
|
|
/**
|
|
|
|
|
* Same as 'lookupDeploymentFor' but throws an exception if no deployment is bound.
|
|
|
|
|
*/
|
2011-05-03 21:04:45 +02:00
|
|
|
private[akka] def deploymentFor(address: String): Deploy = {
|
2011-04-27 15:00:41 +02:00
|
|
|
lookupDeploymentFor(address) match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case Some(deployment) ⇒ deployment
|
|
|
|
|
case None ⇒ thrownNoDeploymentBoundException(address)
|
2011-04-27 15:00:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-03 21:04:45 +02:00
|
|
|
private[akka] def lookupDeploymentFor(address: String): Option[Deploy] = {
|
|
|
|
|
val deployment_? = instance.lookupDeploymentFor(address)
|
2011-04-29 15:47:56 +02:00
|
|
|
if (deployment_?.isDefined && (deployment_?.get ne null)) deployment_?
|
2011-04-27 00:40:20 +02:00
|
|
|
else {
|
2011-04-29 15:47:56 +02:00
|
|
|
val newDeployment =
|
2011-04-27 00:40:20 +02:00
|
|
|
try {
|
|
|
|
|
lookupInConfig(address)
|
|
|
|
|
} catch {
|
2011-05-18 17:25:30 +02:00
|
|
|
case e: ConfigurationException ⇒
|
2011-04-27 00:40:20 +02:00
|
|
|
EventHandler.error(e, this, e.getMessage)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
2011-05-18 17:25:30 +02:00
|
|
|
newDeployment foreach { d ⇒
|
2011-04-27 15:00:41 +02:00
|
|
|
if (d eq null) {
|
|
|
|
|
val e = new IllegalStateException("Deployment for address [" + address + "] is null")
|
|
|
|
|
EventHandler.error(e, this, e.getMessage)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
2011-04-29 15:47:56 +02:00
|
|
|
deploy(d) // deploy and cache it
|
2011-04-27 15:00:41 +02:00
|
|
|
}
|
2011-04-29 15:47:56 +02:00
|
|
|
newDeployment
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-03 21:04:45 +02:00
|
|
|
private[akka] def deploymentsInConfig: List[Deploy] = {
|
|
|
|
|
for {
|
2011-05-18 17:25:30 +02:00
|
|
|
address ← addressesInConfig
|
|
|
|
|
deployment ← lookupInConfig(address)
|
2011-05-03 21:04:45 +02:00
|
|
|
} yield deployment
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private[akka] def addressesInConfig: List[String] = {
|
|
|
|
|
val deploymentPath = "akka.actor.deployment"
|
|
|
|
|
Config.config.getSection(deploymentPath) match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case None ⇒ Nil
|
|
|
|
|
case Some(addressConfig) ⇒
|
2011-05-03 21:04:45 +02:00
|
|
|
addressConfig.map.keySet
|
2011-05-18 17:25:30 +02:00
|
|
|
.map(path ⇒ path.substring(0, path.indexOf(".")))
|
2011-05-03 21:04:45 +02:00
|
|
|
.toSet.toList // toSet to force uniqueness
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 00:40:20 +02:00
|
|
|
/**
|
2011-04-27 15:00:41 +02:00
|
|
|
* Lookup deployment in 'akka.conf' configuration file.
|
2011-04-27 00:40:20 +02:00
|
|
|
*/
|
2011-05-03 21:04:45 +02:00
|
|
|
private[akka] def lookupInConfig(address: String): Option[Deploy] = {
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// akka.actor.deployment.<address>
|
|
|
|
|
// --------------------------------
|
|
|
|
|
val addressPath = "akka.actor.deployment." + address
|
|
|
|
|
Config.config.getSection(addressPath) match {
|
2011-06-07 06:36:21 +05:30
|
|
|
case None ⇒ Some(Deploy(address, Direct, Serializer.defaultSerializerName, Local))
|
2011-05-18 17:25:30 +02:00
|
|
|
case Some(addressConfig) ⇒
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// akka.actor.deployment.<address>.router
|
|
|
|
|
// --------------------------------
|
|
|
|
|
val router = addressConfig.getString("router", "direct") match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case "direct" ⇒ Direct
|
|
|
|
|
case "round-robin" ⇒ RoundRobin
|
|
|
|
|
case "random" ⇒ Random
|
|
|
|
|
case "least-cpu" ⇒ LeastCPU
|
|
|
|
|
case "least-ram" ⇒ LeastRAM
|
|
|
|
|
case "least-messages" ⇒ LeastMessages
|
|
|
|
|
case customRouterClassName ⇒
|
2011-04-27 00:40:20 +02:00
|
|
|
val customRouter = try {
|
|
|
|
|
Class.forName(customRouterClassName).newInstance.asInstanceOf[AnyRef]
|
|
|
|
|
} catch {
|
2011-05-18 17:25:30 +02:00
|
|
|
case e ⇒ throw new ConfigurationException(
|
|
|
|
|
"Config option [" + addressPath + ".router] needs to be one of " +
|
2011-04-27 00:40:20 +02:00
|
|
|
"[\"direct\", \"round-robin\", \"random\", \"least-cpu\", \"least-ram\", \"least-messages\" or FQN of router class]")
|
|
|
|
|
}
|
|
|
|
|
CustomRouter(customRouter)
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-16 09:47:23 +02:00
|
|
|
// --------------------------------
|
|
|
|
|
// akka.actor.deployment.<address>.format
|
|
|
|
|
// --------------------------------
|
2011-06-07 06:36:21 +05:30
|
|
|
val format = addressConfig.getString("format", Serializer.defaultSerializerName)
|
2011-05-16 09:47:23 +02:00
|
|
|
|
2011-04-27 00:40:20 +02:00
|
|
|
// --------------------------------
|
|
|
|
|
// akka.actor.deployment.<address>.clustered
|
|
|
|
|
// --------------------------------
|
|
|
|
|
addressConfig.getSection("clustered") match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case None ⇒
|
2011-06-07 06:36:21 +05:30
|
|
|
Some(Deploy(address, router, Serializer.defaultSerializerName, Local)) // deploy locally
|
2011-04-27 00:40:20 +02:00
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
case Some(clusteredConfig) ⇒
|
2011-04-27 00:40:20 +02:00
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// akka.actor.deployment.<address>.clustered.home
|
|
|
|
|
// --------------------------------
|
2011-05-23 22:35:01 +02:00
|
|
|
|
2011-05-04 21:25:47 +02:00
|
|
|
val home = clusteredConfig.getString("home", "") match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case "" ⇒ Host("localhost")
|
|
|
|
|
case home ⇒
|
2011-05-04 21:25:47 +02:00
|
|
|
def raiseHomeConfigError() = throw new ConfigurationException(
|
2011-05-18 17:25:30 +02:00
|
|
|
"Config option [" + addressPath +
|
2011-05-04 21:25:47 +02:00
|
|
|
".clustered.home] needs to be on format 'host:<hostname>', 'ip:<ip address>'' or 'node:<node name>', was [" +
|
|
|
|
|
home + "]")
|
|
|
|
|
|
|
|
|
|
if (!(home.startsWith("host:") || home.startsWith("node:") || home.startsWith("ip:"))) raiseHomeConfigError()
|
|
|
|
|
|
|
|
|
|
val tokenizer = new java.util.StringTokenizer(home, ":")
|
2011-05-18 17:25:30 +02:00
|
|
|
val protocol = tokenizer.nextElement
|
|
|
|
|
val address = tokenizer.nextElement.asInstanceOf[String]
|
2011-05-04 21:25:47 +02:00
|
|
|
|
|
|
|
|
protocol match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case "host" ⇒ Host(address)
|
|
|
|
|
case "node" ⇒ Node(address)
|
|
|
|
|
case "ip" ⇒ IP(address)
|
|
|
|
|
case _ ⇒ raiseHomeConfigError()
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
|
// akka.actor.deployment.<address>.clustered.replicas
|
|
|
|
|
// --------------------------------
|
2011-05-25 16:18:35 +02:00
|
|
|
val replicas = clusteredConfig.getAny("replicas", "0") match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case "auto" ⇒ AutoReplicate
|
2011-05-25 16:18:35 +02:00
|
|
|
case "0" ⇒ NoReplicas
|
2011-05-18 17:25:30 +02:00
|
|
|
case nrOfReplicas: String ⇒
|
2011-04-27 00:40:20 +02:00
|
|
|
try {
|
|
|
|
|
Replicate(nrOfReplicas.toInt)
|
|
|
|
|
} catch {
|
2011-05-18 17:25:30 +02:00
|
|
|
case e: NumberFormatException ⇒
|
2011-04-27 00:40:20 +02:00
|
|
|
throw new ConfigurationException(
|
|
|
|
|
"Config option [" + addressPath +
|
2011-05-25 16:18:35 +02:00
|
|
|
".clustered.replicas] needs to be either [\"auto\"] or [0-N] - was [" +
|
2011-05-18 17:25:30 +02:00
|
|
|
nrOfReplicas + "]")
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------
|
2011-06-10 16:31:24 +01:00
|
|
|
// akka.actor.deployment.<address>.clustered.replication
|
2011-04-27 00:40:20 +02:00
|
|
|
// --------------------------------
|
2011-06-10 16:31:24 +01:00
|
|
|
clusteredConfig.getSection("replication") match {
|
2011-06-07 11:10:29 -07:00
|
|
|
case None ⇒
|
2011-06-10 16:31:24 +01:00
|
|
|
Some(Deploy(address, router, format, Clustered(home, replicas, Transient)))
|
2011-06-07 11:10:29 -07:00
|
|
|
|
2011-06-10 16:31:24 +01:00
|
|
|
case Some(replicationConfig) ⇒
|
|
|
|
|
val storage = replicationConfig.getString("storage", "transaction-log") match {
|
2011-06-07 11:10:29 -07:00
|
|
|
case "transaction-log" ⇒ TransactionLog
|
|
|
|
|
case "data-grid" ⇒ DataGrid
|
|
|
|
|
case unknown ⇒
|
|
|
|
|
throw new ConfigurationException("Config option [" + addressPath +
|
2011-06-10 16:31:24 +01:00
|
|
|
".clustered.replication.storage] needs to be either [\"transaction-log\"] or [\"data-grid\"] - was [" +
|
2011-06-07 11:10:29 -07:00
|
|
|
unknown + "]")
|
|
|
|
|
}
|
2011-06-10 16:31:24 +01:00
|
|
|
val strategy = replicationConfig.getString("strategy", "write-through") match {
|
2011-06-07 11:10:29 -07:00
|
|
|
case "write-through" ⇒ WriteThrough
|
|
|
|
|
case "write-behind" ⇒ WriteBehind
|
2011-06-10 16:31:24 +01:00
|
|
|
case unknown ⇒
|
|
|
|
|
throw new ConfigurationException("Config option [" + addressPath +
|
|
|
|
|
".clustered.replication.strategy] needs to be either [\"write-through\"] or [\"write-behind\"] - was [" +
|
|
|
|
|
unknown + "]")
|
2011-06-07 11:10:29 -07:00
|
|
|
}
|
2011-06-10 16:31:24 +01:00
|
|
|
Some(Deploy(address, router, format, Clustered(home, replicas, Replication(storage, strategy))))
|
2011-06-07 11:10:29 -07:00
|
|
|
}
|
2011-04-27 00:40:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-30 10:53:25 +02:00
|
|
|
private[akka] def throwDeploymentBoundException(deployment: Deploy): Nothing = {
|
2011-04-29 15:47:56 +02:00
|
|
|
val e = new DeploymentAlreadyBoundException(
|
2011-04-27 00:40:20 +02:00
|
|
|
"Address [" + deployment.address +
|
2011-05-18 17:25:30 +02:00
|
|
|
"] already bound to [" + deployment +
|
|
|
|
|
"]. You have to invoke 'undeploy(deployment) first.")
|
2011-04-27 00:40:20 +02:00
|
|
|
EventHandler.error(e, this, e.getMessage)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-30 10:53:25 +02:00
|
|
|
private[akka] def thrownNoDeploymentBoundException(address: String): Nothing = {
|
2011-04-27 00:40:20 +02:00
|
|
|
val e = new NoDeploymentBoundException("Address [" + address + "] is not bound to a deployment")
|
|
|
|
|
EventHandler.error(e, this, e.getMessage)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:47:56 +02:00
|
|
|
/**
|
2011-05-18 12:25:27 +02:00
|
|
|
* TODO: Improved documentation
|
|
|
|
|
*
|
2011-04-29 15:47:56 +02:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
2011-05-03 21:04:45 +02:00
|
|
|
object LocalDeployer {
|
2011-04-29 15:47:56 +02:00
|
|
|
private val deployments = new ConcurrentHashMap[String, Deploy]
|
|
|
|
|
|
2011-05-03 21:04:45 +02:00
|
|
|
private[akka] def init(deployments: List[Deploy]) {
|
2011-05-17 14:48:06 +02:00
|
|
|
EventHandler.info(this, "Deploying actors locally [\n\t%s\n]" format deployments.mkString("\n\t"))
|
2011-05-03 21:04:45 +02:00
|
|
|
deployments foreach (deploy(_)) // deploy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private[akka] def shutdown() {
|
|
|
|
|
undeployAll()
|
|
|
|
|
deployments.clear()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private[akka] def deploy(deployment: Deploy) {
|
2011-04-29 15:47:56 +02:00
|
|
|
if (deployments.putIfAbsent(deployment.address, deployment) != deployment) {
|
2011-05-30 10:53:25 +02:00
|
|
|
//Deployer.throwDeploymentBoundException(deployment) // FIXME uncomment this and fix the issue with multiple deployments
|
2011-04-29 15:47:56 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-21 16:14:15 +02:00
|
|
|
private[akka] def undeploy(deployment: Deploy): Unit = deployments.remove(deployment.address)
|
2011-04-29 15:47:56 +02:00
|
|
|
|
2011-05-21 16:14:15 +02:00
|
|
|
private[akka] def undeployAll(): Unit = deployments.clear()
|
2011-04-29 15:47:56 +02:00
|
|
|
|
2011-05-21 16:14:15 +02:00
|
|
|
private[akka] def lookupDeploymentFor(address: String): Option[Deploy] = Option(deployments.get(address))
|
2011-04-29 15:47:56 +02:00
|
|
|
}
|
|
|
|
|
|
2011-04-27 00:40:20 +02:00
|
|
|
/**
|
2011-05-18 12:25:27 +02:00
|
|
|
* TODO: Improved documentation
|
|
|
|
|
*
|
2011-04-27 00:40:20 +02:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
|
|
|
|
object Address {
|
|
|
|
|
private val validAddressPattern = java.util.regex.Pattern.compile("[0-9a-zA-Z\\-\\_\\$\\.]+")
|
|
|
|
|
|
|
|
|
|
def validate(address: String) {
|
|
|
|
|
if (validAddressPattern.matcher(address).matches) true
|
|
|
|
|
else {
|
2011-06-10 16:31:24 +01:00
|
|
|
val e = new IllegalArgumentException(
|
|
|
|
|
"Address [" + address + "] is not valid, need to follow pattern [0-9a-zA-Z\\-\\_\\$]+")
|
2011-04-27 00:40:20 +02:00
|
|
|
EventHandler.error(e, this, e.getMessage)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
class DeploymentException private[akka] (message: String) extends AkkaException(message)
|
|
|
|
|
class DeploymentAlreadyBoundException private[akka] (message: String) extends AkkaException(message)
|
|
|
|
|
class NoDeploymentBoundException private[akka] (message: String) extends AkkaException(message)
|