2009-06-24 15:12:47 +02:00
|
|
|
/**
|
2009-12-27 16:01:53 +01:00
|
|
|
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
2009-06-24 15:12:47 +02:00
|
|
|
*/
|
|
|
|
|
|
2009-12-18 21:26:03 +01:00
|
|
|
package se.scalablesolutions.akka.remote
|
2009-06-24 15:12:47 +02:00
|
|
|
|
|
|
|
|
import java.lang.reflect.InvocationTargetException
|
|
|
|
|
import java.net.InetSocketAddress
|
|
|
|
|
import java.util.concurrent.{ConcurrentHashMap, Executors}
|
2009-12-30 08:36:24 +01:00
|
|
|
import java.util.{Map => JMap}
|
2009-07-02 13:23:03 +02:00
|
|
|
|
2009-10-06 00:07:27 +02:00
|
|
|
import se.scalablesolutions.akka.actor._
|
2010-06-21 12:25:24 +02:00
|
|
|
import se.scalablesolutions.akka.actor.Actor._
|
2009-10-06 00:07:27 +02:00
|
|
|
import se.scalablesolutions.akka.util._
|
2010-06-10 11:39:26 +02:00
|
|
|
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
2010-03-10 22:38:52 +01:00
|
|
|
import se.scalablesolutions.akka.config.Config.config
|
2009-07-18 00:16:32 +02:00
|
|
|
|
2009-06-24 15:12:47 +02:00
|
|
|
import org.jboss.netty.bootstrap.ServerBootstrap
|
|
|
|
|
import org.jboss.netty.channel._
|
2009-12-18 21:26:03 +01:00
|
|
|
import org.jboss.netty.channel.group.{DefaultChannelGroup, ChannelGroup}
|
2009-06-24 15:12:47 +02:00
|
|
|
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
|
2009-07-12 23:08:17 +02:00
|
|
|
import org.jboss.netty.handler.codec.frame.{LengthFieldBasedFrameDecoder, LengthFieldPrepender}
|
|
|
|
|
import org.jboss.netty.handler.codec.protobuf.{ProtobufDecoder, ProtobufEncoder}
|
2009-11-21 22:04:10 +01:00
|
|
|
import org.jboss.netty.handler.codec.compression.{ZlibEncoder, ZlibDecoder}
|
2010-04-25 20:32:52 +02:00
|
|
|
import org.jboss.netty.handler.ssl.SslHandler
|
|
|
|
|
|
2009-07-12 23:08:17 +02:00
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
import scala.collection.mutable.Map
|
|
|
|
|
|
2009-07-23 20:01:37 +02:00
|
|
|
/**
|
2009-11-24 17:41:08 +01:00
|
|
|
* Use this object if you need a single remote server on a specific node.
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
2009-12-18 21:26:03 +01:00
|
|
|
* // takes hostname and port from 'akka.conf'
|
2009-11-25 12:42:50 +01:00
|
|
|
* RemoteNode.start
|
2009-11-24 17:41:08 +01:00
|
|
|
* </pre>
|
|
|
|
|
*
|
2009-12-18 21:26:03 +01:00
|
|
|
* <pre>
|
|
|
|
|
* RemoteNode.start(hostname, port)
|
|
|
|
|
* </pre>
|
2010-04-06 12:45:09 +02:00
|
|
|
*
|
2010-02-16 09:39:56 +01:00
|
|
|
* You can specify the class loader to use to load the remote actors.
|
|
|
|
|
* <pre>
|
|
|
|
|
* RemoteNode.start(hostname, port, classLoader)
|
|
|
|
|
* </pre>
|
2009-12-18 21:26:03 +01:00
|
|
|
*
|
2009-11-24 17:41:08 +01:00
|
|
|
* If you need to create more than one, then you can use the RemoteServer:
|
2009-12-18 21:26:03 +01:00
|
|
|
*
|
2009-11-24 17:41:08 +01:00
|
|
|
* <pre>
|
|
|
|
|
* val server = new RemoteServer
|
2009-12-18 21:26:03 +01:00
|
|
|
* server.start(hostname, port)
|
2009-11-24 17:41:08 +01:00
|
|
|
* </pre>
|
|
|
|
|
*
|
2009-07-23 20:01:37 +02:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
2010-01-04 21:54:54 +01:00
|
|
|
object RemoteNode extends RemoteServer
|
2009-11-24 17:41:08 +01:00
|
|
|
|
|
|
|
|
/**
|
2010-01-05 08:57:12 +01:00
|
|
|
* For internal use only.
|
|
|
|
|
* Holds configuration variables, remote actors, remote active objects and remote servers.
|
2009-12-18 21:26:03 +01:00
|
|
|
*
|
2009-11-24 17:41:08 +01:00
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
|
|
|
|
object RemoteServer {
|
2009-10-22 11:14:36 +02:00
|
|
|
val HOSTNAME = config.getString("akka.remote.server.hostname", "localhost")
|
|
|
|
|
val PORT = config.getInt("akka.remote.server.port", 9999)
|
2009-11-24 17:41:08 +01:00
|
|
|
|
|
|
|
|
val CONNECTION_TIMEOUT_MILLIS = config.getInt("akka.remote.server.connection-timeout", 1000)
|
|
|
|
|
|
2009-11-22 14:32:27 +01:00
|
|
|
val COMPRESSION_SCHEME = config.getString("akka.remote.compression-scheme", "zlib")
|
|
|
|
|
val ZLIB_COMPRESSION_LEVEL = {
|
|
|
|
|
val level = config.getInt("akka.remote.zlib-compression-level", 6)
|
|
|
|
|
if (level < 1 && level > 9) throw new IllegalArgumentException(
|
|
|
|
|
"zlib compression level has to be within 1-9, with 1 being fastest and 9 being the most compressed")
|
|
|
|
|
level
|
|
|
|
|
}
|
2009-12-30 08:36:24 +01:00
|
|
|
|
|
|
|
|
object Address {
|
|
|
|
|
def apply(hostname: String, port: Int) = new Address(hostname, port)
|
|
|
|
|
}
|
|
|
|
|
class Address(val hostname: String, val port: Int) {
|
|
|
|
|
override def hashCode: Int = {
|
|
|
|
|
var result = HashCode.SEED
|
|
|
|
|
result = HashCode.hash(result, hostname)
|
|
|
|
|
result = HashCode.hash(result, port)
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
override def equals(that: Any): Boolean = {
|
|
|
|
|
that != null &&
|
|
|
|
|
that.isInstanceOf[Address] &&
|
|
|
|
|
that.asInstanceOf[Address].hostname == hostname &&
|
|
|
|
|
that.asInstanceOf[Address].port == port
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-06 12:45:09 +02:00
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private class RemoteActorSet {
|
|
|
|
|
private[RemoteServer] val actors = new ConcurrentHashMap[String, ActorRef]
|
|
|
|
|
private[RemoteServer] val activeObjects = new ConcurrentHashMap[String, AnyRef]
|
2009-12-30 08:36:24 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private val guard = new ReadWriteGuard
|
|
|
|
|
private val remoteActorSets = Map[Address, RemoteActorSet]()
|
|
|
|
|
private val remoteServers = Map[Address, RemoteServer]()
|
2010-04-06 12:45:09 +02:00
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private[akka] def registerActor(address: InetSocketAddress, uuid: String, actor: ActorRef) = guard.withWriteGuard {
|
|
|
|
|
actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).actors.put(uuid, actor)
|
2009-12-30 08:36:24 +01:00
|
|
|
}
|
2010-05-21 20:08:49 +02:00
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private[akka] def registerActiveObject(address: InetSocketAddress, name: String, activeObject: AnyRef) = guard.withWriteGuard {
|
|
|
|
|
actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).activeObjects.put(name, activeObject)
|
|
|
|
|
}
|
2010-05-21 20:08:49 +02:00
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private[akka] def getOrCreateServer(address: InetSocketAddress): RemoteServer = guard.withWriteGuard {
|
|
|
|
|
serverFor(address) match {
|
|
|
|
|
case Some(server) => server
|
|
|
|
|
case None => (new RemoteServer).start(address)
|
|
|
|
|
}
|
2010-01-04 21:54:54 +01:00
|
|
|
}
|
|
|
|
|
|
2010-05-16 10:59:06 +02:00
|
|
|
private[akka] def serverFor(address: InetSocketAddress): Option[RemoteServer] =
|
|
|
|
|
serverFor(address.getHostName, address.getPort)
|
|
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private[akka] def serverFor(hostname: String, port: Int): Option[RemoteServer] = guard.withReadGuard {
|
|
|
|
|
remoteServers.get(Address(hostname, port))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private[akka] def register(hostname: String, port: Int, server: RemoteServer) = guard.withWriteGuard {
|
2010-01-04 21:54:54 +01:00
|
|
|
remoteServers.put(Address(hostname, port), server)
|
2010-05-20 19:14:31 +02:00
|
|
|
}
|
2010-04-06 12:45:09 +02:00
|
|
|
|
2010-05-20 19:14:31 +02:00
|
|
|
private[akka] def unregister(hostname: String, port: Int) = guard.withWriteGuard {
|
2010-01-04 21:54:54 +01:00
|
|
|
remoteServers.remove(Address(hostname, port))
|
2010-05-20 19:14:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def actorsFor(remoteServerAddress: RemoteServer.Address): RemoteActorSet = {
|
2010-05-22 21:11:08 +02:00
|
|
|
remoteActorSets.getOrElseUpdate(remoteServerAddress,new RemoteActorSet)
|
2010-05-20 19:14:31 +02:00
|
|
|
}
|
2009-11-24 17:41:08 +01:00
|
|
|
}
|
2009-11-22 14:32:27 +01:00
|
|
|
|
2009-11-24 17:41:08 +01:00
|
|
|
/**
|
|
|
|
|
* Use this class if you need a more than one remote server on a specific node.
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* val server = new RemoteServer
|
|
|
|
|
* server.start
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* If you need to create more than one, then you can use the RemoteServer:
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
2009-11-25 12:42:50 +01:00
|
|
|
* RemoteNode.start
|
2009-11-24 17:41:08 +01:00
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
2010-01-04 21:54:54 +01:00
|
|
|
class RemoteServer extends Logging {
|
2009-11-24 17:41:08 +01:00
|
|
|
val name = "RemoteServer@" + hostname + ":" + port
|
2009-08-11 12:16:50 +02:00
|
|
|
|
2009-11-24 17:41:08 +01:00
|
|
|
private var hostname = RemoteServer.HOSTNAME
|
2009-12-30 08:36:24 +01:00
|
|
|
private var port = RemoteServer.PORT
|
2009-12-18 21:26:03 +01:00
|
|
|
|
2010-04-06 12:45:09 +02:00
|
|
|
@volatile private var _isRunning = false
|
2009-06-25 23:47:30 +02:00
|
|
|
|
2009-06-25 13:07:58 +02:00
|
|
|
private val factory = new NioServerSocketChannelFactory(
|
2009-06-24 15:12:47 +02:00
|
|
|
Executors.newCachedThreadPool,
|
|
|
|
|
Executors.newCachedThreadPool)
|
|
|
|
|
|
2009-06-25 13:07:58 +02:00
|
|
|
private val bootstrap = new ServerBootstrap(factory)
|
2009-06-24 15:12:47 +02:00
|
|
|
|
2009-12-18 21:26:03 +01:00
|
|
|
// group of open channels, used for clean-up
|
2009-12-30 08:36:24 +01:00
|
|
|
private val openChannels: ChannelGroup = new DefaultChannelGroup("akka-remote-server")
|
2009-12-18 21:26:03 +01:00
|
|
|
|
2010-04-06 12:45:09 +02:00
|
|
|
def isRunning = _isRunning
|
|
|
|
|
|
2010-05-16 10:59:06 +02:00
|
|
|
def start: RemoteServer =
|
|
|
|
|
start(hostname, port, None)
|
|
|
|
|
|
2010-05-25 15:14:53 +02:00
|
|
|
def start(loader: ClassLoader): RemoteServer =
|
|
|
|
|
start(hostname, port, Some(loader))
|
2010-05-16 10:59:06 +02:00
|
|
|
|
|
|
|
|
def start(address: InetSocketAddress): RemoteServer =
|
|
|
|
|
start(address.getHostName, address.getPort, None)
|
2009-10-13 11:18:21 +02:00
|
|
|
|
2010-05-25 15:14:53 +02:00
|
|
|
def start(address: InetSocketAddress, loader: ClassLoader): RemoteServer =
|
|
|
|
|
start(address.getHostName, address.getPort, Some(loader))
|
2009-10-13 11:18:21 +02:00
|
|
|
|
2010-05-16 10:59:06 +02:00
|
|
|
def start(_hostname: String, _port: Int): RemoteServer =
|
|
|
|
|
start(_hostname, _port, None)
|
2009-10-13 11:18:21 +02:00
|
|
|
|
2010-06-01 18:41:39 +02:00
|
|
|
private def start(_hostname: String, _port: Int, loader: ClassLoader): RemoteServer =
|
2010-05-25 15:14:53 +02:00
|
|
|
start(_hostname, _port, Some(loader))
|
|
|
|
|
|
|
|
|
|
private def start(_hostname: String, _port: Int, loader: Option[ClassLoader]): RemoteServer = synchronized {
|
2009-12-27 06:35:25 +01:00
|
|
|
try {
|
2010-04-06 12:45:09 +02:00
|
|
|
if (!_isRunning) {
|
2009-12-27 06:35:25 +01:00
|
|
|
hostname = _hostname
|
|
|
|
|
port = _port
|
|
|
|
|
log.info("Starting remote server at [%s:%s]", hostname, port)
|
2010-01-04 21:54:54 +01:00
|
|
|
RemoteServer.register(hostname, port, this)
|
2009-12-30 08:36:24 +01:00
|
|
|
val remoteActorSet = RemoteServer.actorsFor(RemoteServer.Address(hostname, port))
|
2010-05-05 22:45:19 +02:00
|
|
|
val pipelineFactory = new RemoteServerPipelineFactory(
|
|
|
|
|
name, openChannels, loader, remoteActorSet.actors, remoteActorSet.activeObjects)
|
2009-12-30 08:36:24 +01:00
|
|
|
bootstrap.setPipelineFactory(pipelineFactory)
|
2009-12-27 06:35:25 +01:00
|
|
|
bootstrap.setOption("child.tcpNoDelay", true)
|
|
|
|
|
bootstrap.setOption("child.keepAlive", true)
|
|
|
|
|
bootstrap.setOption("child.reuseAddress", true)
|
|
|
|
|
bootstrap.setOption("child.connectTimeoutMillis", RemoteServer.CONNECTION_TIMEOUT_MILLIS)
|
|
|
|
|
openChannels.add(bootstrap.bind(new InetSocketAddress(hostname, port)))
|
2010-04-06 12:45:09 +02:00
|
|
|
_isRunning = true
|
2010-01-04 21:54:54 +01:00
|
|
|
Cluster.registerLocalNode(hostname, port)
|
2010-04-06 12:45:09 +02:00
|
|
|
}
|
2009-12-27 06:35:25 +01:00
|
|
|
} catch {
|
|
|
|
|
case e => log.error(e, "Could not start up remote server")
|
2009-06-25 23:47:30 +02:00
|
|
|
}
|
2010-05-16 10:59:06 +02:00
|
|
|
this
|
2009-06-25 23:47:30 +02:00
|
|
|
}
|
2009-11-24 17:41:08 +01:00
|
|
|
|
2010-04-06 12:45:09 +02:00
|
|
|
def shutdown = synchronized {
|
|
|
|
|
if (_isRunning) {
|
|
|
|
|
RemoteServer.unregister(hostname, port)
|
|
|
|
|
openChannels.disconnect
|
|
|
|
|
openChannels.close.awaitUninterruptibly
|
|
|
|
|
bootstrap.releaseExternalResources
|
|
|
|
|
Cluster.deregisterLocalNode(hostname, port)
|
|
|
|
|
}
|
2009-11-24 17:41:08 +01:00
|
|
|
}
|
2010-02-16 15:39:09 +01:00
|
|
|
|
|
|
|
|
// TODO: register active object in RemoteServer as well
|
2010-02-17 15:32:17 +01:00
|
|
|
|
|
|
|
|
/**
|
2010-05-16 20:15:08 +02:00
|
|
|
* Register Remote Actor by the Actor's 'id' field. It starts the Actor if it is not started already.
|
2010-02-17 15:32:17 +01:00
|
|
|
*/
|
2010-05-07 11:19:19 +02:00
|
|
|
def register(actorRef: ActorRef) = synchronized {
|
2010-04-06 12:45:09 +02:00
|
|
|
if (_isRunning) {
|
2010-05-16 20:15:08 +02:00
|
|
|
if (!actorRef.isRunning) actorRef.start
|
2010-05-07 11:19:19 +02:00
|
|
|
log.info("Registering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, actorRef.id)
|
|
|
|
|
RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors.put(actorRef.id, actorRef)
|
2010-04-06 12:45:09 +02:00
|
|
|
}
|
2010-02-16 15:39:09 +01:00
|
|
|
}
|
2010-02-17 15:32:17 +01:00
|
|
|
|
|
|
|
|
/**
|
2010-04-06 12:45:09 +02:00
|
|
|
* Register Remote Actor by a specific 'id' passed as argument.
|
2010-05-07 11:19:19 +02:00
|
|
|
* <p/>
|
2010-05-21 20:08:49 +02:00
|
|
|
* NOTE: If you use this method to register your remote actor then you must unregister the actor by this ID yourself.
|
2010-02-17 15:32:17 +01:00
|
|
|
*/
|
2010-05-07 11:19:19 +02:00
|
|
|
def register(id: String, actorRef: ActorRef) = synchronized {
|
2010-04-06 12:45:09 +02:00
|
|
|
if (_isRunning) {
|
2010-05-16 20:15:08 +02:00
|
|
|
if (!actorRef.isRunning) actorRef.start
|
2010-05-07 11:19:19 +02:00
|
|
|
log.info("Registering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, id)
|
|
|
|
|
RemoteServer.actorsFor(RemoteServer.Address(hostname, port)).actors.put(id, actorRef)
|
2010-04-06 12:45:09 +02:00
|
|
|
}
|
2010-02-17 15:32:17 +01:00
|
|
|
}
|
2010-05-05 22:45:19 +02:00
|
|
|
|
|
|
|
|
/**
|
2010-05-07 11:19:19 +02:00
|
|
|
* Unregister Remote Actor that is registered using its 'id' field (not custom ID).
|
2010-05-05 22:45:19 +02:00
|
|
|
*/
|
2010-05-07 11:19:19 +02:00
|
|
|
def unregister(actorRef: ActorRef) = synchronized {
|
2010-05-05 22:45:19 +02:00
|
|
|
if (_isRunning) {
|
2010-05-07 11:19:19 +02:00
|
|
|
log.info("Unregistering server side remote actor [%s] with id [%s]", actorRef.actorClass.getName, actorRef.id)
|
2010-05-05 22:45:19 +02:00
|
|
|
val server = RemoteServer.actorsFor(RemoteServer.Address(hostname, port))
|
2010-05-07 11:19:19 +02:00
|
|
|
server.actors.remove(actorRef.id)
|
2010-05-13 15:40:49 +02:00
|
|
|
if (actorRef.registeredInRemoteNodeDuringSerialization) server.actors.remove(actorRef.uuid)
|
2010-05-07 11:19:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-05-21 20:08:49 +02:00
|
|
|
* Unregister Remote Actor by specific 'id'.
|
2010-05-07 11:19:19 +02:00
|
|
|
* <p/>
|
2010-05-21 20:08:49 +02:00
|
|
|
* NOTE: You need to call this method if you have registered an actor by a custom ID.
|
2010-05-07 11:19:19 +02:00
|
|
|
*/
|
|
|
|
|
def unregister(id: String) = synchronized {
|
|
|
|
|
if (_isRunning) {
|
|
|
|
|
log.info("Unregistering server side remote actor with id [%s]", id)
|
|
|
|
|
val server = RemoteServer.actorsFor(RemoteServer.Address(hostname, port))
|
|
|
|
|
val actorRef = server.actors.get(id)
|
|
|
|
|
server.actors.remove(id)
|
2010-05-13 15:40:49 +02:00
|
|
|
if (actorRef.registeredInRemoteNodeDuringSerialization) server.actors.remove(actorRef.uuid)
|
2010-05-05 22:45:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-07 11:19:19 +02:00
|
|
|
case class Codec(encoder: ChannelHandler, decoder: ChannelHandler)
|
2009-12-29 20:12:46 +01:00
|
|
|
|
2010-04-25 20:32:52 +02:00
|
|
|
object RemoteServerSslContext {
|
|
|
|
|
import java.security.{KeyStore,Security}
|
|
|
|
|
import javax.net.ssl.{KeyManager,KeyManagerFactory,SSLContext,TrustManagerFactory}
|
|
|
|
|
|
|
|
|
|
val (client,server) = {
|
|
|
|
|
val protocol = "TLS"
|
|
|
|
|
val algorithm = Option(Security.getProperty("ssl.KeyManagerFactory.algorithm")).getOrElse("SunX509")
|
|
|
|
|
val store = KeyStore.getInstance("JKS")
|
2010-04-25 23:11:28 +02:00
|
|
|
store.load(DummyKeyStore.asInputStream,DummyKeyStore.getKeyStorePassword) //TODO replace with getResourceAsStream + config-pass
|
2010-04-25 20:32:52 +02:00
|
|
|
|
|
|
|
|
val keyMan = KeyManagerFactory.getInstance(algorithm)
|
2010-04-25 23:11:28 +02:00
|
|
|
keyMan.init(store, DummyKeyStore.getCertificatePassword) //TODO replace with config-pass
|
2010-04-25 20:32:52 +02:00
|
|
|
|
2010-04-25 23:11:28 +02:00
|
|
|
//val trustMan = TrustManagerFactory.getInstance("SunX509");
|
|
|
|
|
//trustMan.init(store) //TODO safe to use same keystore? Or should use it's own keystore?
|
2010-04-25 20:32:52 +02:00
|
|
|
|
|
|
|
|
val s = SSLContext.getInstance(protocol)
|
|
|
|
|
s.init(keyMan.getKeyManagers, null, null)
|
|
|
|
|
|
|
|
|
|
val c = SSLContext.getInstance(protocol)
|
2010-04-25 23:11:28 +02:00
|
|
|
c.init(null, DummyTrustManagerFactory.getTrustManagers, null) //TODO replace with TrustManagerFactory
|
2010-04-25 20:32:52 +02:00
|
|
|
|
|
|
|
|
(c,s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-27 21:21:28 +02:00
|
|
|
/**
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
2009-12-27 22:56:55 +01:00
|
|
|
class RemoteServerPipelineFactory(
|
2009-12-30 08:36:24 +01:00
|
|
|
val name: String,
|
|
|
|
|
val openChannels: ChannelGroup,
|
|
|
|
|
val loader: Option[ClassLoader],
|
2010-05-06 08:13:12 +02:00
|
|
|
val actors: JMap[String, ActorRef],
|
2009-12-30 08:36:24 +01:00
|
|
|
val activeObjects: JMap[String, AnyRef]) extends ChannelPipelineFactory {
|
2009-11-24 17:41:08 +01:00
|
|
|
import RemoteServer._
|
|
|
|
|
|
2009-12-18 21:26:03 +01:00
|
|
|
def getPipeline: ChannelPipeline = {
|
2010-04-25 20:32:52 +02:00
|
|
|
val engine = RemoteServerSslContext.server.createSSLEngine()
|
2010-04-25 23:11:28 +02:00
|
|
|
engine.setEnabledCipherSuites(engine.getSupportedCipherSuites) //TODO is this sensible?
|
2010-04-25 20:32:52 +02:00
|
|
|
engine.setUseClientMode(false)
|
|
|
|
|
|
|
|
|
|
val ssl = new SslHandler(engine)
|
2009-12-29 20:12:46 +01:00
|
|
|
val lenDec = new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4)
|
|
|
|
|
val lenPrep = new LengthFieldPrepender(4)
|
2010-05-07 11:19:19 +02:00
|
|
|
val protobufDec = new ProtobufDecoder(RemoteRequestProtocol.getDefaultInstance)
|
2009-12-29 20:12:46 +01:00
|
|
|
val protobufEnc = new ProtobufEncoder
|
2009-12-30 08:48:22 +01:00
|
|
|
val zipCodec = RemoteServer.COMPRESSION_SCHEME match {
|
2009-12-30 09:24:10 +01:00
|
|
|
case "zlib" => Some(Codec(new ZlibEncoder(RemoteServer.ZLIB_COMPRESSION_LEVEL), new ZlibDecoder))
|
2009-12-29 20:12:46 +01:00
|
|
|
//case "lzf" => Some(Codec(new LzfEncoder, new LzfDecoder))
|
|
|
|
|
case _ => None
|
2009-11-22 14:32:27 +01:00
|
|
|
}
|
2010-04-06 12:45:09 +02:00
|
|
|
val remoteServer = new RemoteServerHandler(name, openChannels, loader, actors, activeObjects)
|
2009-12-30 08:48:22 +01:00
|
|
|
|
2010-04-06 12:45:09 +02:00
|
|
|
val stages: Array[ChannelHandler] =
|
2010-04-25 20:32:52 +02:00
|
|
|
zipCodec.map(codec => Array(ssl,codec.decoder, lenDec, protobufDec, codec.encoder, lenPrep, protobufEnc, remoteServer))
|
|
|
|
|
.getOrElse(Array(ssl,lenDec, protobufDec, lenPrep, protobufEnc, remoteServer))
|
2009-12-30 08:48:22 +01:00
|
|
|
new StaticChannelPipeline(stages: _*)
|
2009-07-18 00:16:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-27 21:21:28 +02:00
|
|
|
/**
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
2010-03-29 09:33:32 +02:00
|
|
|
@ChannelHandler.Sharable
|
2009-12-27 22:56:55 +01:00
|
|
|
class RemoteServerHandler(
|
|
|
|
|
val name: String,
|
2009-12-30 08:36:24 +01:00
|
|
|
val openChannels: ChannelGroup,
|
|
|
|
|
val applicationLoader: Option[ClassLoader],
|
2010-05-06 08:13:12 +02:00
|
|
|
val actors: JMap[String, ActorRef],
|
2009-12-30 08:36:24 +01:00
|
|
|
val activeObjects: JMap[String, AnyRef]) extends SimpleChannelUpstreamHandler with Logging {
|
2009-11-22 15:25:16 +01:00
|
|
|
val AW_PROXY_PREFIX = "$$ProxiedByAW".intern
|
2009-12-18 21:26:03 +01:00
|
|
|
|
2010-06-24 08:48:48 +02:00
|
|
|
applicationLoader.foreach(MessageSerializer.setClassLoader(_))
|
2009-12-27 22:56:55 +01:00
|
|
|
|
2009-12-18 21:26:03 +01:00
|
|
|
/**
|
2010-06-30 16:26:15 +02:00
|
|
|
* ChannelOpen overridden to store open channels for a clean shutdown of a RemoteServer.
|
2010-06-15 13:15:00 +02:00
|
|
|
* If a channel is closed before, it is automatically removed from the open channels group.
|
2009-12-18 21:26:03 +01:00
|
|
|
*/
|
|
|
|
|
override def channelOpen(ctx: ChannelHandlerContext, event: ChannelStateEvent) {
|
|
|
|
|
openChannels.add(ctx.getChannel)
|
|
|
|
|
}
|
2010-04-25 20:32:52 +02:00
|
|
|
|
|
|
|
|
override def channelConnected(ctx : ChannelHandlerContext, e : ChannelStateEvent) {
|
|
|
|
|
val sslHandler : SslHandler = ctx.getPipeline.get(classOf[SslHandler])
|
|
|
|
|
|
|
|
|
|
// Begin handshake.
|
2010-04-25 20:40:47 +02:00
|
|
|
sslHandler.handshake().addListener( new ChannelFutureListener {
|
|
|
|
|
def operationComplete(future : ChannelFuture) : Unit = {
|
|
|
|
|
if(future.isSuccess)
|
|
|
|
|
openChannels.add(future.getChannel)
|
|
|
|
|
else
|
|
|
|
|
future.getChannel.close
|
|
|
|
|
}
|
|
|
|
|
})
|
2010-04-25 20:32:52 +02:00
|
|
|
}
|
|
|
|
|
|
2009-12-18 21:26:03 +01:00
|
|
|
|
2009-06-24 15:12:47 +02:00
|
|
|
override def handleUpstream(ctx: ChannelHandlerContext, event: ChannelEvent) = {
|
2009-11-21 20:51:03 +01:00
|
|
|
if (event.isInstanceOf[ChannelStateEvent] &&
|
|
|
|
|
event.asInstanceOf[ChannelStateEvent].getState != ChannelState.INTEREST_OPS) {
|
2009-06-24 15:12:47 +02:00
|
|
|
log.debug(event.toString)
|
|
|
|
|
}
|
|
|
|
|
super.handleUpstream(ctx, event)
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-03 17:15:36 +02:00
|
|
|
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) = {
|
2009-06-24 15:12:47 +02:00
|
|
|
val message = event.getMessage
|
2010-07-02 11:14:49 +02:00
|
|
|
if (message eq null) throw new IllegalActorStateException("Message in remote MessageEvent is null: " + event)
|
2010-05-07 11:19:19 +02:00
|
|
|
if (message.isInstanceOf[RemoteRequestProtocol]) {
|
|
|
|
|
handleRemoteRequestProtocol(message.asInstanceOf[RemoteRequestProtocol], event.getChannel)
|
2009-11-21 20:51:03 +01:00
|
|
|
}
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) = {
|
2010-05-07 11:19:19 +02:00
|
|
|
event.getCause.printStackTrace
|
2009-11-22 15:25:16 +01:00
|
|
|
log.error(event.getCause, "Unexpected exception from remote downstream")
|
2009-06-24 15:12:47 +02:00
|
|
|
event.getChannel.close
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-07 11:19:19 +02:00
|
|
|
private def handleRemoteRequestProtocol(request: RemoteRequestProtocol, channel: Channel) = {
|
|
|
|
|
log.debug("Received RemoteRequestProtocol[\n%s]", request.toString)
|
2009-07-18 00:16:32 +02:00
|
|
|
if (request.getIsActor) dispatchToActor(request, channel)
|
2009-07-01 15:29:06 +02:00
|
|
|
else dispatchToActiveObject(request, channel)
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-07 11:19:19 +02:00
|
|
|
private def dispatchToActor(request: RemoteRequestProtocol, channel: Channel) = {
|
|
|
|
|
log.debug("Dispatching to remote actor [%s:%s]", request.getTarget, request.getUuid)
|
|
|
|
|
val actorRef = createActor(request.getTarget, request.getUuid, request.getTimeout)
|
|
|
|
|
actorRef.start
|
2010-06-24 08:48:48 +02:00
|
|
|
val message = MessageSerializer.deserialize(request.getMessage)
|
2010-06-30 16:26:15 +02:00
|
|
|
val sender =
|
2010-07-02 00:16:11 +05:30
|
|
|
if (request.hasSender) Some(RemoteActorSerialization.fromProtobufToRemoteActorRef(request.getSender, applicationLoader))
|
2010-06-15 13:15:00 +02:00
|
|
|
else None
|
2010-06-04 15:58:26 +02:00
|
|
|
if (request.getIsOneWay) actorRef.!(message)(sender)
|
|
|
|
|
else {
|
2009-06-25 23:47:30 +02:00
|
|
|
try {
|
2010-06-21 12:25:24 +02:00
|
|
|
val resultOrNone = (actorRef.!!(message)(sender)).as[AnyRef]
|
2010-06-18 16:31:25 +02:00
|
|
|
val result = if (resultOrNone.isDefined) resultOrNone.get else null
|
2009-06-25 23:47:30 +02:00
|
|
|
log.debug("Returning result from actor invocation [%s]", result)
|
2010-05-07 11:19:19 +02:00
|
|
|
val replyBuilder = RemoteReplyProtocol.newBuilder
|
2009-12-18 21:26:03 +01:00
|
|
|
.setId(request.getId)
|
2010-06-24 08:48:48 +02:00
|
|
|
.setMessage(MessageSerializer.serialize(result))
|
2009-12-18 21:26:03 +01:00
|
|
|
.setIsSuccessful(true)
|
|
|
|
|
.setIsActor(true)
|
2009-07-18 00:16:32 +02:00
|
|
|
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
2009-08-11 12:16:50 +02:00
|
|
|
val replyMessage = replyBuilder.build
|
|
|
|
|
channel.write(replyMessage)
|
2009-06-25 23:47:30 +02:00
|
|
|
} catch {
|
2009-07-01 15:29:06 +02:00
|
|
|
case e: Throwable =>
|
2009-11-22 15:25:16 +01:00
|
|
|
log.error(e, "Could not invoke remote actor [%s]", request.getTarget)
|
2010-05-07 11:19:19 +02:00
|
|
|
val replyBuilder = RemoteReplyProtocol.newBuilder
|
2009-12-18 21:26:03 +01:00
|
|
|
.setId(request.getId)
|
2010-06-10 11:39:26 +02:00
|
|
|
.setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build)
|
2009-12-18 21:26:03 +01:00
|
|
|
.setIsSuccessful(false)
|
|
|
|
|
.setIsActor(true)
|
2009-07-18 00:16:32 +02:00
|
|
|
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
2009-08-11 12:16:50 +02:00
|
|
|
val replyMessage = replyBuilder.build
|
|
|
|
|
channel.write(replyMessage)
|
2009-06-25 23:47:30 +02:00
|
|
|
}
|
2009-12-18 21:26:03 +01:00
|
|
|
}
|
2009-06-25 23:47:30 +02:00
|
|
|
}
|
|
|
|
|
|
2010-05-07 11:19:19 +02:00
|
|
|
private def dispatchToActiveObject(request: RemoteRequestProtocol, channel: Channel) = {
|
2009-07-18 00:16:32 +02:00
|
|
|
log.debug("Dispatching to remote active object [%s :: %s]", request.getMethod, request.getTarget)
|
|
|
|
|
val activeObject = createActiveObject(request.getTarget, request.getTimeout)
|
2009-06-25 23:47:30 +02:00
|
|
|
|
2010-06-24 08:48:48 +02:00
|
|
|
val args = MessageSerializer.deserialize(request.getMessage).asInstanceOf[Array[AnyRef]].toList
|
2009-07-01 15:29:06 +02:00
|
|
|
val argClasses = args.map(_.getClass)
|
2009-07-18 00:16:32 +02:00
|
|
|
val (unescapedArgs, unescapedArgClasses) = unescapeArgs(args, argClasses, request.getTimeout)
|
2009-06-25 23:47:30 +02:00
|
|
|
|
|
|
|
|
try {
|
2009-11-21 20:51:03 +01:00
|
|
|
val messageReceiver = activeObject.getClass.getDeclaredMethod(
|
|
|
|
|
request.getMethod, unescapedArgClasses: _*)
|
2009-07-18 00:16:32 +02:00
|
|
|
if (request.getIsOneWay) messageReceiver.invoke(activeObject, unescapedArgs: _*)
|
2009-06-25 23:47:30 +02:00
|
|
|
else {
|
2009-07-06 23:45:15 +02:00
|
|
|
val result = messageReceiver.invoke(activeObject, unescapedArgs: _*)
|
2009-07-01 15:29:06 +02:00
|
|
|
log.debug("Returning result from remote active object invocation [%s]", result)
|
2010-05-07 11:19:19 +02:00
|
|
|
val replyBuilder = RemoteReplyProtocol.newBuilder
|
2009-12-18 21:26:03 +01:00
|
|
|
.setId(request.getId)
|
2010-06-24 08:48:48 +02:00
|
|
|
.setMessage(MessageSerializer.serialize(result))
|
2009-12-18 21:26:03 +01:00
|
|
|
.setIsSuccessful(true)
|
|
|
|
|
.setIsActor(false)
|
2009-07-18 00:16:32 +02:00
|
|
|
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
2009-08-11 12:16:50 +02:00
|
|
|
val replyMessage = replyBuilder.build
|
|
|
|
|
channel.write(replyMessage)
|
2009-06-25 23:47:30 +02:00
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
case e: InvocationTargetException =>
|
2009-11-22 15:25:16 +01:00
|
|
|
log.error(e.getCause, "Could not invoke remote active object [%s :: %s]", request.getMethod, request.getTarget)
|
2010-05-07 11:19:19 +02:00
|
|
|
val replyBuilder = RemoteReplyProtocol.newBuilder
|
2009-12-18 21:26:03 +01:00
|
|
|
.setId(request.getId)
|
2010-06-29 15:19:09 +02:00
|
|
|
.setException(ExceptionProtocol.newBuilder.setClassname(e.getCause.getClass.getName).setMessage(e.getCause.getMessage).build)
|
2009-12-18 21:26:03 +01:00
|
|
|
.setIsSuccessful(false)
|
|
|
|
|
.setIsActor(false)
|
2009-08-11 12:16:50 +02:00
|
|
|
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
|
|
|
|
val replyMessage = replyBuilder.build
|
|
|
|
|
channel.write(replyMessage)
|
2009-07-01 15:29:06 +02:00
|
|
|
case e: Throwable =>
|
2010-06-29 15:19:09 +02:00
|
|
|
log.error(e, "Could not invoke remote active object [%s :: %s]", request.getMethod, request.getTarget)
|
2010-05-07 11:19:19 +02:00
|
|
|
val replyBuilder = RemoteReplyProtocol.newBuilder
|
2009-12-18 21:26:03 +01:00
|
|
|
.setId(request.getId)
|
2010-06-10 11:39:26 +02:00
|
|
|
.setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build)
|
2009-12-18 21:26:03 +01:00
|
|
|
.setIsSuccessful(false)
|
|
|
|
|
.setIsActor(false)
|
2009-08-11 12:16:50 +02:00
|
|
|
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
|
|
|
|
val replyMessage = replyBuilder.build
|
|
|
|
|
channel.write(replyMessage)
|
2009-06-25 23:47:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 15:29:06 +02:00
|
|
|
private def unescapeArgs(args: scala.List[AnyRef], argClasses: scala.List[Class[_]], timeout: Long) = {
|
2009-06-24 15:12:47 +02:00
|
|
|
val unescapedArgs = new Array[AnyRef](args.size)
|
|
|
|
|
val unescapedArgClasses = new Array[Class[_]](args.size)
|
|
|
|
|
|
|
|
|
|
val escapedArgs = for (i <- 0 until args.size) {
|
2009-07-18 00:16:32 +02:00
|
|
|
val arg = args(i)
|
2009-11-22 15:25:16 +01:00
|
|
|
if (arg.isInstanceOf[String] && arg.asInstanceOf[String].startsWith(AW_PROXY_PREFIX)) {
|
2009-07-18 00:16:32 +02:00
|
|
|
val argString = arg.asInstanceOf[String]
|
2010-04-06 12:45:09 +02:00
|
|
|
val proxyName = argString.replace(AW_PROXY_PREFIX, "")
|
2009-07-01 15:29:06 +02:00
|
|
|
val activeObject = createActiveObject(proxyName, timeout)
|
2009-06-24 15:12:47 +02:00
|
|
|
unescapedArgs(i) = activeObject
|
2009-12-18 21:26:03 +01:00
|
|
|
unescapedArgClasses(i) = Class.forName(proxyName)
|
2009-06-24 15:12:47 +02:00
|
|
|
} else {
|
|
|
|
|
unescapedArgs(i) = args(i)
|
2009-12-18 21:26:03 +01:00
|
|
|
unescapedArgClasses(i) = argClasses(i)
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(unescapedArgs, unescapedArgClasses)
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-01 15:29:06 +02:00
|
|
|
private def createActiveObject(name: String, timeout: Long): AnyRef = {
|
2009-06-24 15:12:47 +02:00
|
|
|
val activeObjectOrNull = activeObjects.get(name)
|
2009-12-27 08:24:11 +01:00
|
|
|
if (activeObjectOrNull eq null) {
|
2009-06-25 13:07:58 +02:00
|
|
|
try {
|
2009-10-06 00:07:27 +02:00
|
|
|
log.info("Creating a new remote active object [%s]", name)
|
2009-08-11 12:16:50 +02:00
|
|
|
val clazz = if (applicationLoader.isDefined) applicationLoader.get.loadClass(name)
|
2009-12-18 21:26:03 +01:00
|
|
|
else Class.forName(name)
|
2009-10-17 00:37:56 +02:00
|
|
|
val newInstance = ActiveObject.newInstance(clazz, timeout).asInstanceOf[AnyRef]
|
2009-06-25 13:07:58 +02:00
|
|
|
activeObjects.put(name, newInstance)
|
|
|
|
|
newInstance
|
|
|
|
|
} catch {
|
|
|
|
|
case e =>
|
2009-11-22 15:25:16 +01:00
|
|
|
log.error(e, "Could not create remote active object instance")
|
2009-06-25 23:47:30 +02:00
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
} else activeObjectOrNull
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-06 12:45:09 +02:00
|
|
|
/**
|
|
|
|
|
* Creates a new instance of the actor with name, uuid and timeout specified as arguments.
|
|
|
|
|
* If actor already created then just return it from the registry.
|
|
|
|
|
* Does not start the actor.
|
|
|
|
|
*/
|
2010-05-06 08:13:12 +02:00
|
|
|
private def createActor(name: String, uuid: String, timeout: Long): ActorRef = {
|
2010-05-07 11:19:19 +02:00
|
|
|
val actorRefOrNull = actors.get(uuid)
|
|
|
|
|
if (actorRefOrNull eq null) {
|
2009-06-25 23:47:30 +02:00
|
|
|
try {
|
2009-11-21 20:51:03 +01:00
|
|
|
log.info("Creating a new remote actor [%s:%s]", name, uuid)
|
2009-08-11 12:16:50 +02:00
|
|
|
val clazz = if (applicationLoader.isDefined) applicationLoader.get.loadClass(name)
|
2010-05-16 20:15:08 +02:00
|
|
|
else Class.forName(name)
|
|
|
|
|
val actorRef = Actor.actorOf(clazz.newInstance.asInstanceOf[Actor])
|
2010-05-13 15:40:49 +02:00
|
|
|
actorRef.uuid = uuid
|
2010-05-08 10:04:13 +02:00
|
|
|
actorRef.timeout = timeout
|
2010-05-13 15:40:49 +02:00
|
|
|
actorRef.remoteAddress = None
|
2010-05-07 11:19:19 +02:00
|
|
|
actors.put(uuid, actorRef)
|
|
|
|
|
actorRef
|
2009-06-25 23:47:30 +02:00
|
|
|
} catch {
|
|
|
|
|
case e =>
|
2010-01-05 08:57:12 +01:00
|
|
|
log.error(e, "Could not create remote actor instance")
|
2009-06-25 23:47:30 +02:00
|
|
|
throw e
|
2009-06-25 13:07:58 +02:00
|
|
|
}
|
2010-05-07 11:19:19 +02:00
|
|
|
} else actorRefOrNull
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
}
|