2009-06-24 15:12:47 +02:00
/* *
2010-12-22 15:35:50 +01:00
* Copyright ( C ) 2009 - 2011 Scalable Solutions AB < http : //scalablesolutions.se>
2009-06-24 15:12:47 +02:00
*/
2011-01-03 12:42:30 +01:00
package akka.remote.netty
2009-06-24 15:12:47 +02:00
2010-12-15 17:52:31 +01:00
import akka.dispatch. { DefaultCompletableFuture , CompletableFuture , Future }
2010-10-26 12:49:25 +02:00
import akka.remote.protocol.RemoteProtocol._
import akka.remote.protocol.RemoteProtocol.ActorType._
2010-10-31 19:27:55 +01:00
import akka.config.ConfigurationException
2010-10-26 12:49:25 +02:00
import akka.serialization.RemoteActorSerialization
2011-01-03 12:42:30 +01:00
import akka.serialization.RemoteActorSerialization._
2010-12-15 17:52:31 +01:00
import akka.japi.Creator
import akka.config.Config._
2011-01-03 12:42:30 +01:00
import akka.remoteinterface._
import akka.actor. { Index , ActorInitializationException , LocalActorRef , newUuid , ActorRegistry , Actor , RemoteActorRef , TypedActor , ActorRef , IllegalActorStateException , RemoteActorSystemMessage , uuidFrom , Uuid , Exit , LifeCycleMessage , ActorType => AkkaActorType }
2010-12-15 17:52:31 +01:00
import akka.AkkaException
import akka.actor.Actor._
2011-03-02 18:19:17 +01:00
import akka.actor. { EventHandler }
2010-12-15 17:52:31 +01:00
import akka.util._
2011-01-03 13:49:39 +01:00
import akka.remote. { MessageSerializer , RemoteClientSettings , RemoteServerSettings }
2009-07-18 00:16:32 +02:00
2009-06-24 15:12:47 +02:00
import org.jboss.netty.channel._
2011-01-31 12:41:39 +01:00
import org.jboss.netty.channel.group. { DefaultChannelGroup , ChannelGroup , ChannelGroupFuture }
2010-12-15 17:52:31 +01:00
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory
2009-06-24 15:12:47 +02:00
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
2010-12-15 17:52:31 +01:00
import org.jboss.netty.bootstrap. { ServerBootstrap , ClientBootstrap }
import org.jboss.netty.handler.codec.frame. { LengthFieldBasedFrameDecoder , LengthFieldPrepender }
import org.jboss.netty.handler.codec.compression. { ZlibDecoder , ZlibEncoder }
import org.jboss.netty.handler.codec.protobuf. { ProtobufDecoder , ProtobufEncoder }
import org.jboss.netty.handler.timeout.ReadTimeoutHandler
import org.jboss.netty.util. { TimerTask , Timeout , HashedWheelTimer }
2010-04-25 20:32:52 +02:00
import org.jboss.netty.handler.ssl.SslHandler
2010-12-15 17:52:31 +01:00
import java.net. { SocketAddress , InetSocketAddress }
import java.util.concurrent. { TimeUnit , Executors , ConcurrentMap , ConcurrentHashMap , ConcurrentSkipListSet }
2011-01-03 12:42:30 +01:00
import scala.collection.mutable. { HashMap }
2010-08-21 16:37:33 +02:00
import scala.reflect.BeanProperty
2010-12-15 17:52:31 +01:00
import java.lang.reflect.InvocationTargetException
2010-12-20 16:58:05 +01:00
import java.util.concurrent.atomic. { AtomicReference , AtomicLong , AtomicBoolean }
2011-01-03 12:42:30 +01:00
2011-02-28 22:54:32 +01:00
trait NettyRemoteClientModule extends RemoteClientModule { self : ListenerManagement =>
2011-01-03 11:25:27 +01:00
private val remoteClients = new HashMap [ Address , RemoteClient ]
2011-01-01 21:01:44 +01:00
private val remoteActors = new Index [ Address , Uuid ]
2011-01-01 22:20:43 +01:00
private val lock = new ReadWriteGuard
2010-12-15 17:52:31 +01:00
protected [ akka ] def typedActorFor [ T ] ( intfClass : Class [ T ] , serviceId : String , implClassName : String , timeout : Long , hostname : String , port : Int , loader : Option [ ClassLoader ] ) : T =
2010-12-20 16:58:05 +01:00
TypedActor . createProxyForRemoteActorRef ( intfClass , RemoteActorRef ( serviceId , implClassName , hostname , port , timeout , loader , AkkaActorType . TypedActor ) )
2010-12-15 17:52:31 +01:00
protected [ akka ] def send [ T ] ( message : Any ,
senderOption : Option [ ActorRef ] ,
senderFuture : Option [ CompletableFuture [ T ] ] ,
remoteAddress : InetSocketAddress ,
timeout : Long ,
isOneWay : Boolean ,
actorRef : ActorRef ,
typedActorInfo : Option [ Tuple2 [ String , String ] ] ,
2010-12-20 12:10:46 +01:00
actorType : AkkaActorType ,
loader : Option [ ClassLoader ] ) : Option [ CompletableFuture [ T ] ] =
2011-01-03 12:42:30 +01:00
withClientFor ( remoteAddress , loader ) ( _ . send [ T ] ( message , senderOption , senderFuture , remoteAddress , timeout , isOneWay , actorRef , typedActorInfo , actorType ) )
2010-12-15 17:52:31 +01:00
2011-01-03 12:42:30 +01:00
private [ akka ] def withClientFor [ T ] (
address : InetSocketAddress , loader : Option [ ClassLoader ] ) ( fun : RemoteClient => T ) : T = {
2010-12-15 17:52:31 +01:00
loader . foreach ( MessageSerializer . setClassLoader ( _ ) ) //TODO: REVISIT: THIS SMELLS FUNNY
2011-01-01 21:01:44 +01:00
2011-01-03 12:42:30 +01:00
val key = Address ( address )
2011-01-01 21:01:44 +01:00
lock . readLock . lock
2011-01-01 23:18:51 +01:00
try {
2011-01-03 12:42:30 +01:00
val c = remoteClients . get ( key ) match {
2011-01-01 23:18:51 +01:00
case Some ( client ) => client
case None =>
lock . readLock . unlock
lock . writeLock . lock //Lock upgrade, not supported natively
try {
2011-01-03 11:25:27 +01:00
try {
remoteClients . get ( key ) match { //Recheck for addition, race between upgrades
case Some ( client ) => client //If already populated by other writer
case None => //Populate map
val client = new ActiveRemoteClient ( this , address , loader , self . notifyListeners _ )
client . connect ( )
remoteClients += key -> client
client
}
} finally { lock . readLock . lock } //downgrade
} finally { lock . writeLock . unlock }
2011-01-01 23:18:51 +01:00
}
2011-01-03 12:42:30 +01:00
fun ( c )
2011-01-01 23:18:51 +01:00
} finally { lock . readLock . unlock }
2010-12-15 17:52:31 +01:00
}
2011-01-01 22:20:43 +01:00
def shutdownClientConnection ( address : InetSocketAddress ) : Boolean = lock withWriteGuard {
2011-01-03 12:42:30 +01:00
remoteClients . remove ( Address ( address ) ) match {
2011-01-01 22:20:43 +01:00
case Some ( client ) => client . shutdown
case None => false
2010-12-29 17:59:38 +01:00
}
}
2011-01-01 22:20:43 +01:00
def restartClientConnection ( address : InetSocketAddress ) : Boolean = lock withReadGuard {
2011-01-03 12:42:30 +01:00
remoteClients . get ( Address ( address ) ) match {
2011-01-01 22:20:43 +01:00
case Some ( client ) => client . connect ( reconnectIfAlreadyConnected = true )
case None => false
2010-12-15 17:52:31 +01:00
}
}
2010-12-20 16:58:05 +01:00
private [ akka ] def registerSupervisorForActor ( actorRef : ActorRef ) : ActorRef =
2011-01-03 12:42:30 +01:00
withClientFor ( actorRef . homeAddress . get , None ) ( _ . registerSupervisorForActor ( actorRef ) )
2010-12-15 17:52:31 +01:00
2011-01-01 22:20:43 +01:00
private [ akka ] def deregisterSupervisorForActor ( actorRef : ActorRef ) : ActorRef = lock withReadGuard {
2011-01-03 12:42:30 +01:00
remoteClients . get ( Address ( actorRef . homeAddress . get ) ) match {
2011-01-01 22:20:43 +01:00
case Some ( client ) => client . deregisterSupervisorForActor ( actorRef )
case None => actorRef
2011-01-01 21:01:44 +01:00
}
}
2010-12-15 17:52:31 +01:00
/* *
* Clean - up all open connections .
*/
2011-01-01 21:01:44 +01:00
def shutdownClientModule = {
shutdownRemoteClients
//TODO: Should we empty our remoteActors too?
//remoteActors.clear
2010-12-15 17:52:31 +01:00
}
2011-01-01 22:20:43 +01:00
def shutdownRemoteClients = lock withWriteGuard {
2011-01-01 21:01:44 +01:00
remoteClients . foreach ( { case ( addr , client ) => client . shutdown } )
remoteClients . clear
2010-12-15 17:52:31 +01:00
}
2011-01-01 21:01:44 +01:00
def registerClientManagedActor ( hostname : String , port : Int , uuid : Uuid ) = {
remoteActors . put ( Address ( hostname , port ) , uuid )
2010-12-15 17:52:31 +01:00
}
2011-01-01 21:01:44 +01:00
private [ akka ] def unregisterClientManagedActor ( hostname : String , port : Int , uuid : Uuid ) = {
remoteActors . remove ( Address ( hostname , port ) , uuid )
//TODO: should the connection be closed when the last actor deregisters?
2010-12-15 17:52:31 +01:00
}
}
2011-01-03 12:42:30 +01:00
/* *
* This is the abstract baseclass for netty remote clients ,
* currently there 's only an ActiveRemoteClient , but otehrs could be feasible , like a PassiveRemoteClient that
* reuses an already established connection .
*/
2011-01-01 21:01:44 +01:00
abstract class RemoteClient private [ akka ] (
2010-12-29 16:08:43 +01:00
val module : NettyRemoteClientModule ,
2011-02-28 22:54:32 +01:00
val remoteAddress : InetSocketAddress ) {
2010-12-15 17:52:31 +01:00
2011-01-01 21:01:44 +01:00
val name = this . getClass . getSimpleName + "@" + remoteAddress . getHostName + "::" + remoteAddress . getPort
2010-12-15 17:52:31 +01:00
2011-01-01 21:01:44 +01:00
protected val futures = new ConcurrentHashMap [ Uuid , CompletableFuture [ _ ] ]
protected val supervisors = new ConcurrentHashMap [ Uuid , ActorRef ]
2010-12-15 17:52:31 +01:00
private [ remote ] val runSwitch = new Switch ( )
private [ remote ] val isAuthenticated = new AtomicBoolean ( false )
private [ remote ] def isRunning = runSwitch . isOn
2011-01-01 21:01:44 +01:00
protected def notifyListeners ( msg : => Any ) ; Unit
protected def currentChannel : Channel
2010-12-15 17:52:31 +01:00
2011-01-01 21:01:44 +01:00
def connect ( reconnectIfAlreadyConnected : Boolean = false ) : Boolean
def shutdown : Boolean
2010-12-15 17:52:31 +01:00
2011-01-03 12:42:30 +01:00
/* *
* Converts the message to the wireprotocol and sends the message across the wire
*/
2010-12-15 17:52:31 +01:00
def send [ T ] (
message : Any ,
senderOption : Option [ ActorRef ] ,
senderFuture : Option [ CompletableFuture [ T ] ] ,
remoteAddress : InetSocketAddress ,
timeout : Long ,
isOneWay : Boolean ,
actorRef : ActorRef ,
typedActorInfo : Option [ Tuple2 [ String , String ] ] ,
2011-01-05 11:43:49 +01:00
actorType : AkkaActorType ) : Option [ CompletableFuture [ T ] ] = synchronized { //TODO: find better strategy to prevent race
2011-01-05 16:36:50 +01:00
2010-12-15 17:52:31 +01:00
send ( createRemoteMessageProtocolBuilder (
Some ( actorRef ) ,
Left ( actorRef . uuid ) ,
actorRef . id ,
actorRef . actorClassName ,
actorRef . timeout ,
Left ( message ) ,
isOneWay ,
senderOption ,
typedActorInfo ,
actorType ,
2011-01-03 12:42:30 +01:00
if ( isAuthenticated . compareAndSet ( false , true ) ) RemoteClientSettings . SECURE_COOKIE else None
2010-12-15 17:52:31 +01:00
) . build , senderFuture )
}
2011-01-03 12:42:30 +01:00
/* *
* Sends the message across the wire
*/
2010-12-15 17:52:31 +01:00
def send [ T ] (
2011-01-01 21:01:44 +01:00
request : RemoteMessageProtocol ,
senderFuture : Option [ CompletableFuture [ T ] ] ) : Option [ CompletableFuture [ T ] ] = {
2010-12-15 17:52:31 +01:00
if ( isRunning ) {
if ( request . getOneWay ) {
2011-01-01 21:01:44 +01:00
currentChannel . write ( request ) . addListener ( new ChannelFutureListener {
2010-12-30 14:59:00 +01:00
def operationComplete ( future : ChannelFuture ) {
if ( future . isCancelled ) {
//We don't care about that right now
} else if ( ! future . isSuccess ) {
notifyListeners ( RemoteClientWriteFailed ( request , future . getCause , module , remoteAddress ) )
}
}
} )
2010-12-15 17:52:31 +01:00
None
} else {
val futureResult = if ( senderFuture . isDefined ) senderFuture . get
2010-12-17 16:09:21 +01:00
else new DefaultCompletableFuture [ T ] ( request . getActorInfo . getTimeout )
2011-02-11 20:13:42 +01:00
val futureUuid = uuidFrom ( request . getUuid . getHigh , request . getUuid . getLow )
futures . put ( futureUuid , futureResult ) //Add this prematurely, remove it if write fails
2011-01-01 21:01:44 +01:00
currentChannel . write ( request ) . addListener ( new ChannelFutureListener {
2010-12-30 14:59:00 +01:00
def operationComplete ( future : ChannelFuture ) {
if ( future . isCancelled ) {
2011-02-11 20:13:42 +01:00
futures . remove ( futureUuid ) //Clean this up
2010-12-30 14:59:00 +01:00
//We don't care about that right now
} else if ( ! future . isSuccess ) {
2011-02-11 20:13:42 +01:00
futures . remove ( futureUuid ) //Clean this up
2010-12-30 14:59:00 +01:00
notifyListeners ( RemoteClientWriteFailed ( request , future . getCause , module , remoteAddress ) )
}
}
} )
2010-12-15 17:52:31 +01:00
Some ( futureResult )
}
} else {
2010-12-29 16:08:43 +01:00
val exception = new RemoteClientException ( "Remote client is not running, make sure you have invoked 'RemoteClient.connect' before using it." , module , remoteAddress )
notifyListeners ( RemoteClientError ( exception , module , remoteAddress ) )
2010-12-15 17:52:31 +01:00
throw exception
}
}
private [ akka ] def registerSupervisorForActor ( actorRef : ActorRef ) : ActorRef =
if ( ! actorRef . supervisor . isDefined ) throw new IllegalActorStateException (
"Can't register supervisor for " + actorRef + " since it is not under supervision" )
else supervisors . putIfAbsent ( actorRef . supervisor . get . uuid , actorRef )
private [ akka ] def deregisterSupervisorForActor ( actorRef : ActorRef ) : ActorRef =
if ( ! actorRef . supervisor . isDefined ) throw new IllegalActorStateException (
"Can't unregister supervisor for " + actorRef + " since it is not under supervision" )
else supervisors . remove ( actorRef . supervisor . get . uuid )
2011-01-01 21:01:44 +01:00
}
/* *
2011-01-03 12:42:30 +01:00
* RemoteClient represents a connection to an Akka node . Is used to send messages to remote actors on the node .
2011-01-01 21:01:44 +01:00
*
* @author < a href = "http://jonasboner.com" > Jonas Bon & # 233 ; r </ a >
*/
class ActiveRemoteClient private [ akka ] (
2011-01-03 13:49:39 +01:00
module : NettyRemoteClientModule , remoteAddress : InetSocketAddress ,
val loader : Option [ ClassLoader ] = None , notifyListenersFun : ( => Any ) => Unit ) extends RemoteClient ( module , remoteAddress ) {
2011-01-03 12:42:30 +01:00
import RemoteClientSettings._
2011-01-01 21:01:44 +01:00
//FIXME rewrite to a wrapper object (minimize volatile access and maximize encapsulation)
@volatile private var bootstrap : ClientBootstrap = _
@volatile private [ remote ] var connection : ChannelFuture = _
@volatile private [ remote ] var openChannels : DefaultChannelGroup = _
@volatile private var timer : HashedWheelTimer = _
@volatile private var reconnectionTimeWindowStart = 0L
def notifyListeners ( msg : => Any ) : Unit = notifyListenersFun ( msg )
def currentChannel = connection . getChannel
def connect ( reconnectIfAlreadyConnected : Boolean = false ) : Boolean = {
runSwitch switchOn {
2011-01-31 12:41:39 +01:00
openChannels = new DefaultDisposableChannelGroup ( classOf [ RemoteClient ] . getName )
2011-01-01 21:01:44 +01:00
timer = new HashedWheelTimer
bootstrap = new ClientBootstrap ( new NioClientSocketChannelFactory ( Executors . newCachedThreadPool , Executors . newCachedThreadPool ) )
bootstrap . setPipelineFactory ( new ActiveRemoteClientPipelineFactory ( name , futures , supervisors , bootstrap , remoteAddress , timer , this ) )
bootstrap . setOption ( "tcpNoDelay" , true )
bootstrap . setOption ( "keepAlive" , true )
// Wait until the connection attempt succeeds or fails.
connection = bootstrap . connect ( remoteAddress )
openChannels . add ( connection . awaitUninterruptibly . getChannel )
if ( ! connection . isSuccess ) {
notifyListeners ( RemoteClientError ( connection . getCause , module , remoteAddress ) )
false
} else {
2011-02-08 15:06:13 +01:00
timer . newTimeout ( new TimerTask ( ) {
def run ( timeout : Timeout ) = {
if ( isRunning ) {
val i = futures . entrySet . iterator
while ( i . hasNext ) {
val e = i . next
if ( e . getValue . isExpired )
futures . remove ( e . getKey )
}
}
}
} , RemoteClientSettings . REAP_FUTURES_DELAY . length , RemoteClientSettings . REAP_FUTURES_DELAY . unit )
2011-01-01 21:01:44 +01:00
notifyListeners ( RemoteClientStarted ( module , remoteAddress ) )
true
}
} match {
case true => true
case false if reconnectIfAlreadyConnected =>
isAuthenticated . set ( false )
openChannels . remove ( connection . getChannel )
connection . getChannel . close
connection = bootstrap . connect ( remoteAddress )
openChannels . add ( connection . awaitUninterruptibly . getChannel ) // Wait until the connection attempt succeeds or fails.
if ( ! connection . isSuccess ) {
notifyListeners ( RemoteClientError ( connection . getCause , module , remoteAddress ) )
false
} else true
case false => false
}
}
def shutdown = runSwitch switchOff {
notifyListeners ( RemoteClientShutdown ( module , remoteAddress ) )
timer . stop
timer = null
openChannels . close . awaitUninterruptibly
openChannels = null
bootstrap . releaseExternalResources
bootstrap = null
connection = null
}
2010-12-15 17:52:31 +01:00
private [ akka ] def isWithinReconnectionTimeWindow : Boolean = {
if ( reconnectionTimeWindowStart == 0L ) {
reconnectionTimeWindowStart = System . currentTimeMillis
true
} else {
2011-01-01 21:01:44 +01:00
val timeLeft = RECONNECTION_TIME_WINDOW - ( System . currentTimeMillis - reconnectionTimeWindowStart )
2010-12-15 17:52:31 +01:00
if ( timeLeft > 0 ) {
true
} else false
}
}
private [ akka ] def resetReconnectionTimeWindow = reconnectionTimeWindowStart = 0L
}
/* *
* @author < a href = "http://jonasboner.com" > Jonas Bon & # 233 ; r </ a >
*/
2011-01-01 21:01:44 +01:00
class ActiveRemoteClientPipelineFactory (
2010-12-15 17:52:31 +01:00
name : String ,
futures : ConcurrentMap [ Uuid , CompletableFuture [ _ ] ] ,
supervisors : ConcurrentMap [ Uuid , ActorRef ] ,
bootstrap : ClientBootstrap ,
remoteAddress : SocketAddress ,
timer : HashedWheelTimer ,
2011-01-01 21:01:44 +01:00
client : ActiveRemoteClient ) extends ChannelPipelineFactory {
2010-12-15 17:52:31 +01:00
def getPipeline : ChannelPipeline = {
def join ( ch : ChannelHandler * ) = Array [ ChannelHandler ] ( ch : _ * )
lazy val engine = {
val e = RemoteServerSslContext . client . createSSLEngine ( )
e . setEnabledCipherSuites ( e . getSupportedCipherSuites ) //TODO is this sensible?
e . setUseClientMode ( true )
e
}
2011-01-03 12:42:30 +01:00
val ssl = if ( RemoteServerSettings . SECURE ) join ( new SslHandler ( engine ) ) else join ( )
val timeout = new ReadTimeoutHandler ( timer , RemoteClientSettings . READ_TIMEOUT . toMillis . toInt )
val lenDec = new LengthFieldBasedFrameDecoder ( RemoteClientSettings . MESSAGE_FRAME_SIZE , 0 , 4 , 0 , 4 )
2010-12-15 17:52:31 +01:00
val lenPrep = new LengthFieldPrepender ( 4 )
val protobufDec = new ProtobufDecoder ( RemoteMessageProtocol . getDefaultInstance )
val protobufEnc = new ProtobufEncoder
2011-01-03 12:42:30 +01:00
val ( enc , dec ) = RemoteServerSettings . COMPRESSION_SCHEME match {
case "zlib" => ( join ( new ZlibEncoder ( RemoteServerSettings . ZLIB_COMPRESSION_LEVEL ) ) , join ( new ZlibDecoder ) )
2010-12-15 17:52:31 +01:00
case _ => ( join ( ) , join ( ) )
}
2011-01-01 21:01:44 +01:00
val remoteClient = new ActiveRemoteClientHandler ( name , futures , supervisors , bootstrap , remoteAddress , timer , client )
2010-12-15 17:52:31 +01:00
val stages = ssl ++ join ( timeout ) ++ dec ++ join ( lenDec , protobufDec ) ++ enc ++ join ( lenPrep , protobufEnc , remoteClient )
new StaticChannelPipeline ( stages : _ * )
}
}
/* *
* @author < a href = "http://jonasboner.com" > Jonas Bon & # 233 ; r </ a >
*/
@ChannelHandler . Sharable
2011-01-01 21:01:44 +01:00
class ActiveRemoteClientHandler (
2010-12-15 17:52:31 +01:00
val name : String ,
val futures : ConcurrentMap [ Uuid , CompletableFuture [ _ ] ] ,
val supervisors : ConcurrentMap [ Uuid , ActorRef ] ,
val bootstrap : ClientBootstrap ,
val remoteAddress : SocketAddress ,
val timer : HashedWheelTimer ,
2011-01-01 21:01:44 +01:00
val client : ActiveRemoteClient )
2011-02-28 22:54:32 +01:00
extends SimpleChannelUpstreamHandler {
2010-12-15 17:52:31 +01:00
override def handleUpstream ( ctx : ChannelHandlerContext , event : ChannelEvent ) = {
if ( event . isInstanceOf [ ChannelStateEvent ] &&
event . asInstanceOf [ ChannelStateEvent ] . getState != ChannelState . INTEREST_OPS ) {
}
super . handleUpstream ( ctx , event )
}
override def messageReceived ( ctx : ChannelHandlerContext , event : MessageEvent ) {
try {
2010-12-17 16:09:21 +01:00
event . getMessage match {
case reply : RemoteMessageProtocol =>
val replyUuid = uuidFrom ( reply . getActorInfo . getUuid . getHigh , reply . getActorInfo . getUuid . getLow )
val future = futures . remove ( replyUuid ) . asInstanceOf [ CompletableFuture [ Any ] ]
2010-12-20 12:10:46 +01:00
2010-12-17 16:09:21 +01:00
if ( reply . hasMessage ) {
if ( future eq null ) throw new IllegalActorStateException ( "Future mapped to UUID " + replyUuid + " does not exist" )
val message = MessageSerializer . deserialize ( reply . getMessage )
future . completeWithResult ( message )
} else {
2010-12-20 12:10:46 +01:00
val exception = parseException ( reply , client . loader )
2010-12-17 16:09:21 +01:00
if ( reply . hasSupervisorUuid ( ) ) {
val supervisorUuid = uuidFrom ( reply . getSupervisorUuid . getHigh , reply . getSupervisorUuid . getLow )
if ( ! supervisors . containsKey ( supervisorUuid ) ) throw new IllegalActorStateException (
"Expected a registered supervisor for UUID [" + supervisorUuid + "] but none was found" )
val supervisedActor = supervisors . get ( supervisorUuid )
if ( ! supervisedActor . supervisor . isDefined ) throw new IllegalActorStateException (
"Can't handle restart for remote actor " + supervisedActor + " since its supervisor has been removed" )
2010-12-20 12:10:46 +01:00
else supervisedActor . supervisor . get ! Exit ( supervisedActor , exception )
2010-12-17 16:09:21 +01:00
}
2010-12-20 12:10:46 +01:00
future . completeWithException ( exception )
2010-12-15 17:52:31 +01:00
}
2010-12-17 16:09:21 +01:00
case other =>
2010-12-29 16:08:43 +01:00
throw new RemoteClientException ( "Unknown message received in remote client handler: " + other , client . module , client . remoteAddress )
2010-12-15 17:52:31 +01:00
}
} catch {
2011-03-02 18:19:17 +01:00
case e : Throwable =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2010-12-29 16:08:43 +01:00
client . notifyListeners ( RemoteClientError ( e , client . module , client . remoteAddress ) )
2010-12-15 17:52:31 +01:00
throw e
}
}
override def channelClosed ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = client . runSwitch ifOn {
if ( client . isWithinReconnectionTimeWindow ) {
timer . newTimeout ( new TimerTask ( ) {
def run ( timeout : Timeout ) = {
2011-02-08 15:06:13 +01:00
if ( client . isRunning ) {
client . openChannels . remove ( event . getChannel )
client . connect ( reconnectIfAlreadyConnected = true )
}
2010-12-15 17:52:31 +01:00
}
2011-01-03 12:42:30 +01:00
} , RemoteClientSettings . RECONNECT_DELAY . toMillis , TimeUnit . MILLISECONDS )
2010-12-15 17:52:31 +01:00
} else spawn { client . shutdown }
}
override def channelConnected ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = {
def connect = {
2010-12-29 16:08:43 +01:00
client . notifyListeners ( RemoteClientConnected ( client . module , client . remoteAddress ) )
2010-12-15 17:52:31 +01:00
client . resetReconnectionTimeWindow
}
2011-01-03 12:42:30 +01:00
if ( RemoteServerSettings . SECURE ) {
2010-12-15 17:52:31 +01:00
val sslHandler : SslHandler = ctx . getPipeline . get ( classOf [ SslHandler ] )
sslHandler . handshake . addListener ( new ChannelFutureListener {
def operationComplete ( future : ChannelFuture ) : Unit = {
if ( future . isSuccess ) connect
2010-12-29 16:08:43 +01:00
else throw new RemoteClientException ( "Could not establish SSL handshake" , client . module , client . remoteAddress )
2010-12-15 17:52:31 +01:00
}
} )
} else connect
}
override def channelDisconnected ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = {
2010-12-29 16:08:43 +01:00
client . notifyListeners ( RemoteClientDisconnected ( client . module , client . remoteAddress ) )
2010-12-15 17:52:31 +01:00
}
override def exceptionCaught ( ctx : ChannelHandlerContext , event : ExceptionEvent ) = {
2010-12-29 16:08:43 +01:00
client . notifyListeners ( RemoteClientError ( event . getCause , client . module , client . remoteAddress ) )
2011-02-28 22:54:32 +01:00
if ( event . getCause ne null ) event . getCause . printStackTrace
2010-12-15 17:52:31 +01:00
event . getChannel . close
}
private def parseException ( reply : RemoteMessageProtocol , loader : Option [ ClassLoader ] ) : Throwable = {
val exception = reply . getException
val classname = exception . getClassname
2010-12-20 12:10:46 +01:00
try {
val exceptionClass = if ( loader . isDefined ) loader . get . loadClass ( classname )
else Class . forName ( classname )
exceptionClass
. getConstructor ( Array [ Class [ _ ] ] ( classOf [ String ] ) : _ * )
. newInstance ( exception . getMessage ) . asInstanceOf [ Throwable ]
} catch {
2011-03-02 18:19:17 +01:00
case problem : Throwable =>
EventHandler notifyListeners EventHandler . Error ( problem , this )
2010-12-20 12:10:46 +01:00
UnparsableException ( classname , exception . getMessage )
}
2010-12-15 17:52:31 +01:00
}
}
2009-11-24 17:41:08 +01:00
/* *
2010-12-15 17:52:31 +01:00
* Provides the implementation of the Netty remote support
2009-11-24 17:41:08 +01:00
*/
2010-12-17 16:09:21 +01:00
class NettyRemoteSupport extends RemoteSupport with NettyRemoteServerModule with NettyRemoteClientModule {
//Needed for remote testing and switching on/off under run
2011-01-05 17:30:56 +01:00
val optimizeLocal = new AtomicBoolean ( true )
2010-12-17 16:09:21 +01:00
def optimizeLocalScoped_? ( ) = optimizeLocal . get
2010-12-15 17:52:31 +01:00
2010-12-21 14:36:47 +01:00
protected [ akka ] def actorFor ( serviceId : String , className : String , timeout : Long , host : String , port : Int , loader : Option [ ClassLoader ] ) : ActorRef = {
if ( optimizeLocalScoped_? ) {
val home = this . address
2010-12-22 10:10:04 +01:00
if ( host == home . getHostName && port == home . getPort ) { //TODO: switch to InetSocketAddress.equals?
2010-12-21 14:36:47 +01:00
val localRef = findActorByIdOrUuid ( serviceId , serviceId )
if ( localRef ne null ) return localRef //Code significantly simpler with the return statement
}
}
RemoteActorRef ( serviceId , className , host , port , timeout , loader )
2010-12-15 17:52:31 +01:00
}
2010-12-21 14:36:47 +01:00
def clientManagedActorOf ( factory : ( ) => Actor , host : String , port : Int ) : ActorRef = {
2010-12-22 10:10:04 +01:00
if ( optimizeLocalScoped_? ) {
val home = this . address
if ( host == home . getHostName && port == home . getPort ) //TODO: switch to InetSocketAddress.equals?
return new LocalActorRef ( factory , None ) // Code is much simpler with return
}
2011-02-04 15:14:49 +13:00
val ref = new LocalActorRef ( factory , Some ( new InetSocketAddress ( host , port ) ) , clientManaged = true )
2010-12-21 14:36:47 +01:00
//ref.timeout = timeout //removed because setting default timeout should be done after construction
2010-12-20 16:58:05 +01:00
ref
2010-12-15 17:52:31 +01:00
}
}
2010-12-20 16:58:05 +01:00
class NettyRemoteServer ( serverModule : NettyRemoteServerModule , val host : String , val port : Int , val loader : Option [ ClassLoader ] ) {
2009-12-18 21:26:03 +01:00
2010-12-20 16:58:05 +01:00
val name = "NettyRemoteServer@" + host + ":" + port
2010-12-21 14:36:47 +01:00
val address = new InetSocketAddress ( host , port )
2009-06-25 23:47:30 +02:00
2010-12-14 18:22:46 +01:00
private val factory = new NioServerSocketChannelFactory ( Executors . newCachedThreadPool , Executors . newCachedThreadPool )
2009-06-24 15:12:47 +02:00
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
2011-01-31 12:41:39 +01:00
private val openChannels : ChannelGroup = new DefaultDisposableChannelGroup ( "akka-remote-server" )
2009-12-18 21:26:03 +01:00
2010-12-20 16:58:05 +01:00
val pipelineFactory = new RemoteServerPipelineFactory ( name , openChannels , loader , serverModule )
bootstrap . setPipelineFactory ( pipelineFactory )
2011-02-07 10:31:05 +01:00
bootstrap . setOption ( "backlog" , RemoteServerSettings . BACKLOG )
2010-12-20 16:58:05 +01:00
bootstrap . setOption ( "child.tcpNoDelay" , true )
bootstrap . setOption ( "child.keepAlive" , true )
bootstrap . setOption ( "child.reuseAddress" , true )
2011-01-03 12:42:30 +01:00
bootstrap . setOption ( "child.connectTimeoutMillis" , RemoteServerSettings . CONNECTION_TIMEOUT_MILLIS . toMillis )
2010-12-20 16:58:05 +01:00
2010-12-21 14:36:47 +01:00
openChannels . add ( bootstrap . bind ( address ) )
2010-12-20 16:58:05 +01:00
serverModule . notifyListeners ( RemoteServerStarted ( serverModule ) )
def shutdown {
try {
openChannels . disconnect
openChannels . close . awaitUninterruptibly
bootstrap . releaseExternalResources
serverModule . notifyListeners ( RemoteServerShutdown ( serverModule ) )
} catch {
2011-03-02 18:19:17 +01:00
case e : Exception =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2010-12-20 16:58:05 +01:00
}
}
}
2011-01-03 11:25:27 +01:00
trait NettyRemoteServerModule extends RemoteServerModule { self : RemoteModule =>
2011-01-03 12:42:30 +01:00
import RemoteServerSettings._
2010-12-20 16:58:05 +01:00
private [ akka ] val currentServer = new AtomicReference [ Option [ NettyRemoteServer ] ] ( None )
2010-12-21 14:36:47 +01:00
def address = currentServer . get match {
case Some ( s ) => s . address
case None => ReflectiveAccess . Remote . configDefaultAddress
2010-12-20 16:58:05 +01:00
}
def name = currentServer . get match {
case Some ( s ) => s . name
2010-12-21 14:36:47 +01:00
case None =>
val a = ReflectiveAccess . Remote . configDefaultAddress
"NettyRemoteServer@" + a . getHostName + ":" + a . getPort
2010-12-20 16:58:05 +01:00
}
private val _isRunning = new Switch ( false )
2010-12-14 18:22:46 +01:00
def isRunning = _isRunning . isOn
2009-10-13 11:18:21 +02:00
2010-12-15 17:52:31 +01:00
def start ( _hostname : String , _port : Int , loader : Option [ ClassLoader ] = None ) : RemoteServerModule = guard withGuard {
2009-12-27 06:35:25 +01:00
try {
2010-12-14 18:22:46 +01:00
_isRunning switchOn {
2010-12-20 16:58:05 +01:00
currentServer . set ( Some ( new NettyRemoteServer ( this , _hostname , _port , loader ) ) )
2010-04-06 12:45:09 +02:00
}
2009-12-27 06:35:25 +01:00
} catch {
2011-03-02 18:19:17 +01:00
case e : Exception =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2010-09-12 11:24:27 +02:00
notifyListeners ( RemoteServerError ( e , this ) )
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-12-15 17:52:31 +01:00
def shutdownServerModule = guard withGuard {
2010-12-14 18:22:46 +01:00
_isRunning switchOff {
2010-12-20 16:58:05 +01:00
currentServer . getAndSet ( None ) foreach {
instance =>
instance . shutdown
2010-07-14 14:38:56 +02:00
}
2010-04-06 12:45:09 +02:00
}
2009-11-24 17:41:08 +01:00
}
2010-02-16 15:39:09 +01:00
2010-02-17 15:32:17 +01:00
/* *
2010-09-06 16:33:55 +02:00
* Register remote typed actor by a specific id .
* @param id custom actor id
* @param typedActor typed actor to register
2010-02-17 15:32:17 +01:00
*/
2010-12-14 18:22:46 +01:00
def registerTypedActor ( id : String , typedActor : AnyRef ) : Unit = guard withGuard {
2010-10-26 15:23:50 +02:00
if ( id . startsWith ( UUID_PREFIX ) ) registerTypedActor ( id . substring ( UUID_PREFIX . length ) , typedActor , typedActorsByUuid )
else registerTypedActor ( id , typedActor , typedActors )
2010-09-06 10:15:44 +02:00
}
2010-02-17 15:32:17 +01:00
2010-11-14 18:03:34 -06:00
/* *
* Register remote typed actor by a specific id .
* @param id custom actor id
* @param typedActor typed actor to register
*/
2010-12-14 18:22:46 +01:00
def registerTypedPerSessionActor ( id : String , factory : => AnyRef ) : Unit = guard withGuard {
2010-11-14 18:03:34 -06:00
registerTypedPerSessionActor ( id , ( ) => factory , typedActorsFactories )
}
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-12-14 18:22:46 +01:00
def register ( id : String , actorRef : ActorRef ) : Unit = guard withGuard {
2010-10-26 15:23:50 +02:00
if ( id . startsWith ( UUID_PREFIX ) ) register ( id . substring ( UUID_PREFIX . length ) , actorRef , actorsByUuid )
else register ( id , actorRef , actors )
2010-09-20 12:33:30 +02:00
}
2010-12-14 18:22:46 +01:00
def registerByUuid ( actorRef : ActorRef ) : Unit = guard withGuard {
register ( actorRef . uuid . toString , actorRef , actorsByUuid )
}
private def register [ Key ] ( id : Key , actorRef : ActorRef , registry : ConcurrentHashMap [ Key , ActorRef ] ) {
if ( _isRunning . isOn ) {
registry . put ( id , actorRef ) //TODO change to putIfAbsent
if ( ! actorRef . isRunning ) actorRef . start
}
2010-11-14 13:10:13 -06:00
}
2010-11-29 20:49:15 +01:00
/* *
* Register Remote Session Actor by a specific 'id' passed as argument .
* < p />
* NOTE : If you use this method to register your remote actor then you must unregister the actor by this ID yourself .
*/
2010-12-14 18:22:46 +01:00
def registerPerSession ( id : String , factory : => ActorRef ) : Unit = synchronized {
registerPerSession ( id , ( ) => factory , actorsFactories )
2010-09-20 12:33:30 +02:00
}
2010-11-14 13:10:13 -06:00
private def registerPerSession [ Key ] ( id : Key , factory : ( ) => ActorRef , registry : ConcurrentHashMap [ Key ,( ) => ActorRef ] ) {
2010-12-14 18:22:46 +01:00
if ( _isRunning . isOn )
2010-11-22 11:57:17 +01:00
registry . put ( id , factory ) //TODO change to putIfAbsent
2010-11-14 13:10:13 -06:00
}
2010-09-22 11:37:23 +02:00
private def registerTypedActor [ Key ] ( id : Key , typedActor : AnyRef , registry : ConcurrentHashMap [ Key , AnyRef ] ) {
2010-12-14 18:22:46 +01:00
if ( _isRunning . isOn )
2010-11-22 11:57:17 +01:00
registry . put ( id , typedActor ) //TODO change to putIfAbsent
2010-02-17 15:32:17 +01:00
}
2010-05-05 22:45:19 +02:00
2010-11-14 18:03:34 -06:00
private def registerTypedPerSessionActor [ Key ] ( id : Key , factory : ( ) => AnyRef , registry : ConcurrentHashMap [ Key ,( ) => AnyRef ] ) {
2010-12-14 18:22:46 +01:00
if ( _isRunning . isOn )
2010-11-22 11:57:17 +01:00
registry . put ( id , factory ) //TODO change to putIfAbsent
2010-02-17 15:32:17 +01:00
}
2010-05-05 22:45:19 +02:00
/* *
2010-09-07 12:54:10 +02:00
* Unregister Remote Actor that is registered using its 'id' field ( not custom ID ) .
2010-05-05 22:45:19 +02:00
*/
2010-12-14 18:22:46 +01:00
def unregister ( actorRef : ActorRef ) : Unit = guard withGuard {
if ( _isRunning . isOn ) {
2010-10-26 15:23:50 +02:00
actors . remove ( actorRef . id , actorRef )
actorsByUuid . remove ( actorRef . uuid , actorRef )
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
*/
2010-12-14 18:22:46 +01:00
def unregister ( id : String ) : Unit = guard withGuard {
if ( _isRunning . isOn ) {
2010-10-29 16:33:31 +02:00
if ( id . startsWith ( UUID_PREFIX ) ) actorsByUuid . remove ( id . substring ( UUID_PREFIX . length ) )
2010-10-26 15:23:50 +02:00
else {
val actorRef = actors get id
actorsByUuid . remove ( actorRef . uuid , actorRef )
actors . remove ( id , actorRef )
2010-09-20 12:33:30 +02:00
}
2010-05-05 22:45:19 +02:00
}
}
2010-08-18 09:52:11 +02:00
2010-11-14 13:10:13 -06:00
/* *
* Unregister Remote Actor by specific 'id' .
* < p />
* NOTE : You need to call this method if you have registered an actor by a custom ID .
*/
2010-12-14 18:22:46 +01:00
def unregisterPerSession ( id : String ) : Unit = {
if ( _isRunning . isOn ) {
2010-11-14 13:10:13 -06:00
actorsFactories . remove ( id )
}
}
2010-09-06 16:33:55 +02:00
/* *
* Unregister Remote Typed Actor by specific 'id' .
* < p />
* NOTE : You need to call this method if you have registered an actor by a custom ID .
*/
2010-12-14 18:22:46 +01:00
def unregisterTypedActor ( id : String ) : Unit = guard withGuard {
if ( _isRunning . isOn ) {
2010-10-26 15:23:50 +02:00
if ( id . startsWith ( UUID_PREFIX ) ) typedActorsByUuid . remove ( id . substring ( UUID_PREFIX . length ) )
else typedActors . remove ( id )
2010-09-06 16:33:55 +02:00
}
}
2010-09-06 10:15:44 +02:00
2010-11-14 18:03:34 -06:00
/* *
* Unregister Remote Typed Actor by specific 'id' .
* < p />
* NOTE : You need to call this method if you have registered an actor by a custom ID .
*/
2010-12-15 17:52:31 +01:00
def unregisterTypedPerSessionActor ( id : String ) : Unit =
if ( _isRunning . isOn ) typedActorsFactories . remove ( id )
2009-06-24 15:12:47 +02:00
}
2010-04-25 20:32:52 +02:00
object RemoteServerSslContext {
2010-07-15 21:33:44 +02:00
import javax.net.ssl.SSLContext
2010-04-25 20:32:52 +02:00
2010-08-11 01:15:01 +02:00
val ( client , server ) = {
2010-04-25 20:32:52 +02:00
val protocol = "TLS"
2010-07-15 21:33:44 +02:00
//val algorithm = Option(Security.getProperty("ssl.KeyManagerFactory.algorithm")).getOrElse("SunX509")
//val store = KeyStore.getInstance("JKS")
2010-04-25 20:32:52 +02:00
val s = SSLContext . getInstance ( protocol )
2010-08-12 15:36:05 +02:00
s . init ( null , null , null )
2010-04-25 20:32:52 +02:00
val c = SSLContext . getInstance ( protocol )
2010-08-12 15:36:05 +02:00
c . init ( null , null , null )
( c , s )
2010-04-25 20:32:52 +02:00
}
}
2009-07-27 21:21:28 +02:00
/* *
* @author < a href = "http://jonasboner.com" > Jonas Bon & # 233 ; 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-12-15 17:52:31 +01:00
val server : NettyRemoteServerModule ) extends ChannelPipelineFactory {
2011-01-03 12:42:30 +01:00
import RemoteServerSettings._
2009-11-24 17:41:08 +01:00
2009-12-18 21:26:03 +01:00
def getPipeline : ChannelPipeline = {
2010-07-15 21:33:44 +02:00
def join ( ch : ChannelHandler * ) = Array [ ChannelHandler ] ( ch : _ * )
2010-08-20 11:54:57 +02:00
lazy val engine = {
val e = RemoteServerSslContext . server . createSSLEngine ( )
e . setEnabledCipherSuites ( e . getSupportedCipherSuites ) //TODO is this sensible?
e . setUseClientMode ( false )
e
}
2010-04-25 20:32:52 +02:00
2011-01-03 12:42:30 +01:00
val ssl = if ( SECURE ) join ( new SslHandler ( engine ) ) else join ( )
val lenDec = new LengthFieldBasedFrameDecoder ( MESSAGE_FRAME_SIZE , 0 , 4 , 0 , 4 )
2010-07-15 21:33:44 +02:00
val lenPrep = new LengthFieldPrepender ( 4 )
2010-10-28 20:30:11 +02:00
val protobufDec = new ProtobufDecoder ( RemoteMessageProtocol . getDefaultInstance )
2010-07-15 21:33:44 +02:00
val protobufEnc = new ProtobufEncoder
2011-01-03 12:42:30 +01:00
val ( enc , dec ) = COMPRESSION_SCHEME match {
case "zlib" => ( join ( new ZlibEncoder ( ZLIB_COMPRESSION_LEVEL ) ) , join ( new ZlibDecoder ) )
2010-08-11 01:15:01 +02:00
case _ => ( join ( ) , join ( ) )
2009-11-22 14:32:27 +01:00
}
2010-07-15 21:33:44 +02:00
2010-09-07 10:12:26 +02:00
val remoteServer = new RemoteServerHandler ( name , openChannels , loader , server )
2010-07-15 21:33:44 +02:00
val stages = ssl ++ dec ++ join ( lenDec , protobufDec ) ++ enc ++ join ( 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 & # 233 ; 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 ] ,
2011-02-28 22:54:32 +01:00
val server : NettyRemoteServerModule ) extends SimpleChannelUpstreamHandler {
2011-01-03 12:42:30 +01:00
import RemoteServerSettings._
2010-10-26 12:04:32 +02:00
val CHANNEL_INIT = "channel-init" . intern
2009-12-18 21:26:03 +01:00
2011-01-03 14:44:15 +01:00
applicationLoader . foreach ( MessageSerializer . setClassLoader ( _ ) ) //TODO: REVISIT: THIS FEELS A BIT DODGY
2010-11-19 11:53:51 -06:00
val sessionActors = new ChannelLocal [ ConcurrentHashMap [ String , ActorRef ] ] ( )
val typedSessionActors = new ChannelLocal [ ConcurrentHashMap [ String , AnyRef ] ] ( )
2010-11-14 13:10:13 -06:00
2011-01-03 14:44:15 +01:00
//Writes the specified message to the specified channel and propagates write errors to listeners
2011-01-05 11:43:49 +01:00
private def write ( channel : Channel , message : AnyRef ) : Unit = {
2011-01-03 14:44:15 +01:00
channel . write ( message ) . addListener (
new ChannelFutureListener {
def operationComplete ( future : ChannelFuture ) : Unit = {
if ( future . isCancelled ) {
//Not interesting at the moment
} else if ( ! future . isSuccess ) {
val socketAddress = future . getChannel . getRemoteAddress match {
2011-01-05 16:36:50 +01:00
case i : InetSocketAddress => Some ( i )
case _ => None
2011-01-03 14:44:15 +01:00
}
server . notifyListeners ( RemoteServerWriteFailed ( message , future . getCause , server , socketAddress ) )
}
}
} )
2011-01-05 11:43:49 +01:00
}
2009-12-27 22:56:55 +01:00
2009-12-18 21:26:03 +01:00
/* *
2011-01-03 12:42:30 +01:00
* ChannelOpen overridden to store open channels for a clean postStop of a node .
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
*/
2010-08-21 16:37:33 +02:00
override def channelOpen ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = openChannels . add ( ctx . getChannel )
2010-08-10 21:42:27 +02:00
2010-08-21 16:37:33 +02:00
override def channelConnected ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = {
2010-10-31 07:13:00 +01:00
val clientAddress = getClientAddress ( ctx )
2010-11-19 11:53:51 -06:00
sessionActors . set ( event . getChannel ( ) , new ConcurrentHashMap [ String , ActorRef ] ( ) )
typedSessionActors . set ( event . getChannel ( ) , new ConcurrentHashMap [ String , AnyRef ] ( ) )
2011-01-03 12:42:30 +01:00
if ( SECURE ) {
2010-08-21 16:37:33 +02:00
val sslHandler : SslHandler = ctx . getPipeline . get ( classOf [ SslHandler ] )
2010-07-15 21:33:44 +02:00
// Begin handshake.
2010-08-21 16:37:33 +02:00
sslHandler . handshake ( ) . addListener ( new ChannelFutureListener {
2010-08-11 01:15:01 +02:00
def operationComplete ( future : ChannelFuture ) : Unit = {
2010-08-21 16:37:33 +02:00
if ( future . isSuccess ) {
openChannels . add ( future . getChannel )
2010-10-31 07:13:00 +01:00
server . notifyListeners ( RemoteServerClientConnected ( server , clientAddress ) )
2010-08-21 16:37:33 +02:00
} else future . getChannel . close
2010-07-15 21:33:44 +02:00
}
} )
2011-01-01 21:01:44 +01:00
} else {
server . notifyListeners ( RemoteServerClientConnected ( server , clientAddress ) )
}
2011-01-03 12:42:30 +01:00
if ( REQUIRE_COOKIE ) ctx . setAttachment ( CHANNEL_INIT ) // signal that this is channel initialization, which will need authentication
2010-04-25 20:32:52 +02:00
}
2010-10-31 07:13:00 +01:00
override def channelDisconnected ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = {
2011-01-03 14:44:15 +01:00
import scala.collection.JavaConversions.asScalaIterable
2010-10-31 07:13:00 +01:00
val clientAddress = getClientAddress ( ctx )
2011-01-03 14:44:15 +01:00
2010-11-14 16:26:33 -06:00
// stop all session actors
2011-01-03 14:44:15 +01:00
for ( map <- Option ( sessionActors . remove ( event . getChannel ) ) ;
actor <- asScalaIterable ( map . values ) ) {
2011-02-28 22:54:32 +01:00
try { actor . stop } catch { case e : Exception => }
2010-11-14 16:26:33 -06:00
}
2011-01-03 14:44:15 +01:00
// stop all typed session actors
for ( map <- Option ( typedSessionActors . remove ( event . getChannel ) ) ;
actor <- asScalaIterable ( map . values ) ) {
2011-02-28 22:54:32 +01:00
try { TypedActor . stop ( actor ) } catch { case e : Exception => }
2010-11-14 18:03:34 -06:00
}
2011-01-03 11:25:27 +01:00
2010-10-31 07:13:00 +01:00
server . notifyListeners ( RemoteServerClientDisconnected ( server , clientAddress ) )
}
2010-08-21 16:37:33 +02:00
override def channelClosed ( ctx : ChannelHandlerContext , event : ChannelStateEvent ) = {
2010-10-31 07:13:00 +01:00
val clientAddress = getClientAddress ( ctx )
server . notifyListeners ( RemoteServerClientClosed ( server , clientAddress ) )
2010-08-21 16:37:33 +02:00
}
2009-06-24 15:12:47 +02:00
override def handleUpstream ( ctx : ChannelHandlerContext , event : ChannelEvent ) = {
2010-10-26 12:04:32 +02:00
if ( event . isInstanceOf [ ChannelStateEvent ] && event . asInstanceOf [ ChannelStateEvent ] . getState != ChannelState . INTEREST_OPS ) {
2009-06-24 15:12:47 +02:00
}
super . handleUpstream ( ctx , event )
}
2010-12-14 18:57:45 +01:00
override def messageReceived ( ctx : ChannelHandlerContext , event : MessageEvent ) = event . getMessage match {
case null => throw new IllegalActorStateException ( "Message in remote MessageEvent is null: " + event )
case requestProtocol : RemoteMessageProtocol =>
2011-01-03 12:42:30 +01:00
if ( REQUIRE_COOKIE ) authenticateRemoteClient ( requestProtocol , ctx )
2010-10-28 20:30:11 +02:00
handleRemoteMessageProtocol ( requestProtocol , event . getChannel )
2010-12-14 18:57:45 +01:00
case _ => //ignore
2009-06-24 15:12:47 +02:00
}
override def exceptionCaught ( ctx : ChannelHandlerContext , event : ExceptionEvent ) = {
event . getChannel . close
2010-09-12 11:24:27 +02:00
server . notifyListeners ( RemoteServerError ( event . getCause , server ) )
2009-06-24 15:12:47 +02:00
}
2011-01-03 13:49:39 +01:00
private def getClientAddress ( ctx : ChannelHandlerContext ) : Option [ InetSocketAddress ] =
ctx . getChannel . getRemoteAddress match {
case inet : InetSocketAddress => Some ( inet )
case _ => None
}
2010-10-31 07:13:00 +01:00
2010-10-28 20:30:11 +02:00
private def handleRemoteMessageProtocol ( request : RemoteMessageProtocol , channel : Channel ) = {
2010-09-20 17:15:54 +02:00
request . getActorInfo . getActorType match {
case SCALA_ACTOR => dispatchToActor ( request , channel )
case TYPED_ACTOR => dispatchToTypedActor ( request , channel )
case JAVA_ACTOR => throw new IllegalActorStateException ( "ActorType JAVA_ACTOR is currently not supported" )
case other => throw new IllegalActorStateException ( "Unknown ActorType [" + other + "]" )
}
2009-06-24 15:12:47 +02:00
}
2010-11-02 21:18:31 +01:00
private def dispatchToActor ( request : RemoteMessageProtocol , channel : Channel ) {
2010-07-26 18:47:25 +02:00
val actorInfo = request . getActorInfo
2010-10-29 16:33:31 +02:00
val actorRef =
2011-01-03 14:44:15 +01:00
try { createActor ( actorInfo , channel ) . start } catch {
2010-10-29 16:33:31 +02:00
case e : SecurityException =>
2011-03-02 18:19:17 +01:00
EventHandler notifyListeners EventHandler . Error ( e , this )
2011-01-03 14:44:15 +01:00
write ( channel , createErrorReplyMessage ( e , request , AkkaActorType . ScalaActor ) )
2010-10-28 21:18:25 +02:00
server . notifyListeners ( RemoteServerError ( e , server ) )
return
}
2010-07-26 18:47:25 +02:00
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-07-26 18:47:25 +02:00
2010-08-12 15:36:05 +02:00
message match { // first match on system messages
2010-10-29 16:33:31 +02:00
case RemoteActorSystemMessage . Stop =>
2011-01-03 12:42:30 +01:00
if ( UNTRUSTED_MODE ) throw new SecurityException ( "Remote server is operating is untrusted mode, can not stop the actor" )
2010-10-28 21:18:25 +02:00
else actorRef . stop
2011-01-03 12:42:30 +01:00
case _ : LifeCycleMessage if ( UNTRUSTED_MODE ) =>
2010-10-28 21:18:25 +02:00
throw new SecurityException ( "Remote server is operating is untrusted mode, can not pass on a LifeCycleMessage to the remote actor" )
2010-08-12 15:36:05 +02:00
case _ => // then match on user defined messages
2010-10-28 20:30:11 +02:00
if ( request . getOneWay ) actorRef . ! ( message ) ( sender )
2010-10-26 12:04:32 +02:00
else actorRef . postMessageToMailboxAndCreateFutureResultWithTimeout (
message ,
request . getActorInfo . getTimeout ,
None ,
2010-11-12 12:11:53 +01:00
Some ( new DefaultCompletableFuture [ AnyRef ] ( request . getActorInfo . getTimeout ) .
onComplete ( f => {
val result = f . result
val exception = f . exception
if ( exception . isDefined ) {
2011-01-03 14:44:15 +01:00
write ( channel , createErrorReplyMessage ( exception . get , request , AkkaActorType . ScalaActor ) )
2010-09-16 15:58:46 +02:00
}
2010-11-12 12:11:53 +01:00
else if ( result . isDefined ) {
val messageBuilder = RemoteActorSerialization . createRemoteMessageProtocolBuilder (
Some ( actorRef ) ,
Right ( request . getUuid ) ,
actorInfo . getId ,
actorInfo . getTarget ,
actorInfo . getTimeout ,
Left ( result . get ) ,
true ,
Some ( actorRef ) ,
None ,
AkkaActorType . ScalaActor ,
None )
// FIXME lift in the supervisor uuid management into toh createRemoteMessageProtocolBuilder method
if ( request . hasSupervisorUuid ) messageBuilder . setSupervisorUuid ( request . getSupervisorUuid )
2011-01-03 14:44:15 +01:00
write ( channel , messageBuilder . build )
2010-09-16 15:58:46 +02:00
}
}
2010-11-12 12:11:53 +01:00
)
) )
2009-12-18 21:26:03 +01:00
}
2009-06-25 23:47:30 +02:00
}
2010-10-28 20:30:11 +02:00
private def dispatchToTypedActor ( request : RemoteMessageProtocol , channel : Channel ) = {
2010-07-26 18:47:25 +02:00
val actorInfo = request . getActorInfo
val typedActorInfo = actorInfo . getTypedActorInfo
2009-06-25 23:47:30 +02:00
2010-11-14 18:03:34 -06:00
val typedActor = createTypedActor ( actorInfo , channel )
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-06-25 23:47:30 +02:00
try {
2010-07-31 00:41:43 +02:00
val messageReceiver = typedActor . getClass . getDeclaredMethod ( typedActorInfo . getMethod , argClasses : _ * )
2010-10-28 20:30:11 +02:00
if ( request . getOneWay ) messageReceiver . invoke ( typedActor , args : _ * )
2009-06-25 23:47:30 +02:00
else {
2010-11-22 11:57:17 +01:00
//Sends the response
def sendResponse ( result : Either [ Any ,Throwable ] ) : Unit = try {
val messageBuilder = RemoteActorSerialization . createRemoteMessageProtocolBuilder (
None ,
Right ( request . getUuid ) ,
actorInfo . getId ,
actorInfo . getTarget ,
actorInfo . getTimeout ,
result ,
true ,
None ,
None ,
AkkaActorType . TypedActor ,
None )
if ( request . hasSupervisorUuid ) messageBuilder . setSupervisorUuid ( request . getSupervisorUuid )
2011-01-03 14:44:15 +01:00
write ( channel , messageBuilder . build )
2010-11-22 11:57:17 +01:00
} catch {
2011-03-02 18:19:17 +01:00
case e : Exception =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2011-03-02 00:14:45 +01:00
server . notifyListeners ( RemoteServerError ( e , server ) )
2010-11-11 19:41:06 +01:00
}
2010-11-02 18:11:58 +01:00
2010-11-22 11:57:17 +01:00
messageReceiver . invoke ( typedActor , args : _ * ) match {
case f : Future [ _ ] => //If it's a future, we can lift on that to defer the send to when the future is completed
f . onComplete ( future => {
2011-01-03 14:44:15 +01:00
val result : Either [ Any ,Throwable ] =
if ( future . exception . isDefined ) Right ( future . exception . get ) else Left ( future . result . get )
2010-11-22 11:57:17 +01:00
sendResponse ( result )
} )
case other => sendResponse ( Left ( other ) )
}
2009-06-25 23:47:30 +02:00
}
} catch {
2010-08-18 09:52:11 +02:00
case e : InvocationTargetException =>
2011-03-02 18:19:17 +01:00
EventHandler notifyListeners EventHandler . Error ( e , this )
2011-01-03 14:44:15 +01:00
write ( channel , createErrorReplyMessage ( e . getCause , request , AkkaActorType . TypedActor ) )
2010-09-12 11:24:27 +02:00
server . notifyListeners ( RemoteServerError ( e , server ) )
2011-03-02 18:19:17 +01:00
case e : Exception =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2011-01-03 14:44:15 +01:00
write ( channel , createErrorReplyMessage ( e , request , AkkaActorType . TypedActor ) )
2010-09-12 11:24:27 +02:00
server . notifyListeners ( RemoteServerError ( e , server ) )
2009-06-25 23:47:30 +02:00
}
}
2011-01-03 13:49:39 +01:00
private def findSessionActor ( id : String , channel : Channel ) : ActorRef =
sessionActors . get ( channel ) match {
case null => null
case map => map get id
}
2010-11-14 16:26:33 -06:00
2011-01-03 13:49:39 +01:00
private def findTypedSessionActor ( id : String , channel : Channel ) : AnyRef =
typedSessionActors . get ( channel ) match {
case null => null
case map => map get id
}
2010-11-14 18:03:34 -06:00
2010-04-06 12:45:09 +02:00
/* *
2010-11-18 12:48:31 -06:00
* gets the actor from the session , or creates one if there is a factory for it
2010-04-06 12:45:09 +02:00
*/
2010-11-18 12:48:31 -06:00
private def createSessionActor ( actorInfo : ActorInfoProtocol , channel : Channel ) : ActorRef = {
2010-09-16 13:50:57 +02:00
val uuid = actorInfo . getUuid
val id = actorInfo . getId
2011-01-03 13:49:39 +01:00
findSessionActor ( id , channel ) match {
case null => // we dont have it in the session either, see if we have a factory for it
server . findActorFactory ( id ) match {
case null => null
case factory =>
val actorRef = factory ( )
actorRef . uuid = parseUuid ( uuid ) //FIXME is this sensible?
sessionActors . get ( channel ) . put ( id , actorRef )
actorRef
}
case sessionActor => sessionActor
2010-11-18 12:48:31 -06:00
}
}
2010-11-14 16:26:33 -06:00
2010-11-18 11:09:55 -06:00
2010-11-18 12:48:31 -06:00
private def createClientManagedActor ( actorInfo : ActorInfoProtocol ) : ActorRef = {
val uuid = actorInfo . getUuid
val id = actorInfo . getId
val timeout = actorInfo . getTimeout
val name = actorInfo . getTarget
2010-11-14 16:26:33 -06:00
try {
2011-01-03 12:42:30 +01:00
if ( UNTRUSTED_MODE ) throw new SecurityException (
2010-11-14 16:26:33 -06:00
"Remote server is operating is untrusted mode, can not create remote actors on behalf of the remote client" )
val clazz = if ( applicationLoader . isDefined ) applicationLoader . get . loadClass ( name )
else Class . forName ( name )
2010-12-21 14:36:47 +01:00
val actorRef = Actor . actorOf ( clazz . asInstanceOf [ Class [ _ <: Actor ] ] )
2011-01-03 13:49:39 +01:00
actorRef . uuid = parseUuid ( uuid )
2010-11-14 16:26:33 -06:00
actorRef . id = id
actorRef . timeout = timeout
server . actorsByUuid . put ( actorRef . uuid . toString , actorRef ) // register by uuid
actorRef
} catch {
2011-03-02 18:19:17 +01:00
case e : Throwable =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2010-11-14 16:26:33 -06:00
server . notifyListeners ( RemoteServerError ( e , server ) )
throw e
}
2010-11-18 12:48:31 -06:00
2009-06-24 15:12:47 +02:00
}
2010-07-26 18:47:25 +02:00
2010-04-06 12:45:09 +02:00
/* *
* Creates a new instance of the actor with name , uuid and timeout specified as arguments .
2010-07-29 17:29:51 +02:00
*
2010-04-06 12:45:09 +02:00
* If actor already created then just return it from the registry .
2010-07-29 17:29:51 +02:00
*
2010-04-06 12:45:09 +02:00
* Does not start the actor .
*/
2010-11-18 12:48:31 -06:00
private def createActor ( actorInfo : ActorInfoProtocol , channel : Channel ) : ActorRef = {
2010-09-16 13:50:57 +02:00
val uuid = actorInfo . getUuid
val id = actorInfo . getId
2010-09-13 13:31:42 +02:00
2011-01-03 13:49:39 +01:00
server . findActorByIdOrUuid ( id , parseUuid ( uuid ) . toString ) match {
case null => // the actor has not been registered globally. See if we have it in the session
createSessionActor ( actorInfo , channel ) match {
case null => createClientManagedActor ( actorInfo ) // maybe it is a client managed actor
case sessionActor => sessionActor
}
case actorRef => actorRef
2010-11-18 12:48:31 -06:00
}
}
2010-10-28 21:18:25 +02:00
2010-11-18 12:48:31 -06:00
/* *
* gets the actor from the session , or creates one if there is a factory for it
*/
private def createTypedSessionActor ( actorInfo : ActorInfoProtocol , channel : Channel ) : AnyRef = {
val id = actorInfo . getId
2011-01-03 13:49:39 +01:00
findTypedSessionActor ( id , channel ) match {
case null =>
server . findTypedActorFactory ( id ) match {
case null => null
case factory =>
val newInstance = factory ( )
typedSessionActors . get ( channel ) . put ( id , newInstance )
newInstance
}
case sessionActor => sessionActor
2010-11-14 18:03:34 -06:00
}
2009-06-24 15:12:47 +02:00
}
2010-07-26 18:47:25 +02:00
2010-11-18 12:48:31 -06:00
private def createClientManagedTypedActor ( actorInfo : ActorInfoProtocol ) = {
2010-11-14 18:03:34 -06:00
val typedActorInfo = actorInfo . getTypedActorInfo
val interfaceClassname = typedActorInfo . getInterface
val targetClassname = actorInfo . getTarget
2010-09-16 13:50:57 +02:00
val uuid = actorInfo . getUuid
2010-09-13 13:31:42 +02:00
2010-11-14 18:03:34 -06:00
try {
2011-01-03 12:42:30 +01:00
if ( UNTRUSTED_MODE ) throw new SecurityException (
2010-11-14 18:03:34 -06:00
"Remote server is operating is untrusted mode, can not create remote actors on behalf of the remote client" )
2010-07-26 18:47:25 +02:00
2010-11-14 18:03:34 -06:00
val ( interfaceClass , targetClass ) =
if ( applicationLoader . isDefined ) ( applicationLoader . get . loadClass ( interfaceClassname ) ,
applicationLoader . get . loadClass ( targetClassname ) )
else ( Class . forName ( interfaceClassname ) , Class . forName ( targetClassname ) )
2010-10-28 21:18:25 +02:00
2010-11-14 18:03:34 -06:00
val newInstance = TypedActor . newInstance (
interfaceClass , targetClass . asInstanceOf [ Class [ _ <: TypedActor ] ] , actorInfo . getTimeout ) . asInstanceOf [ AnyRef ]
2011-01-03 13:49:39 +01:00
server . typedActors . put ( parseUuid ( uuid ) . toString , newInstance ) // register by uuid
2010-11-14 18:03:34 -06:00
newInstance
} catch {
2011-03-02 18:19:17 +01:00
case e : Throwable =>
EventHandler notifyListeners EventHandler . Error ( e , this )
2010-11-14 18:03:34 -06:00
server . notifyListeners ( RemoteServerError ( e , server ) )
throw e
}
2010-07-26 18:47:25 +02:00
}
2010-11-18 12:48:31 -06:00
private def createTypedActor ( actorInfo : ActorInfoProtocol , channel : Channel ) : AnyRef = {
val uuid = actorInfo . getUuid
2010-07-29 17:29:51 +02:00
2011-01-03 13:49:39 +01:00
server . findTypedActorByIdOrUuid ( actorInfo . getId , parseUuid ( uuid ) . toString ) match {
case null => // the actor has not been registered globally. See if we have it in the session
createTypedSessionActor ( actorInfo , channel ) match {
case null => createClientManagedTypedActor ( actorInfo ) //Maybe client managed actor?
case sessionActor => sessionActor
}
case typedActor => typedActor
2010-11-18 12:48:31 -06:00
}
2010-07-26 18:47:25 +02:00
}
2010-07-29 17:29:51 +02:00
2010-11-02 18:11:58 +01:00
private def createErrorReplyMessage ( exception : Throwable , request : RemoteMessageProtocol , actorType : AkkaActorType ) : RemoteMessageProtocol = {
2010-07-26 18:47:25 +02:00
val actorInfo = request . getActorInfo
2010-11-02 18:11:58 +01:00
val messageBuilder = RemoteActorSerialization . createRemoteMessageProtocolBuilder (
None ,
2010-11-24 21:05:12 +01:00
Right ( request . getUuid ) ,
actorInfo . getId ,
actorInfo . getTarget ,
actorInfo . getTimeout ,
Right ( exception ) ,
true ,
None ,
None ,
actorType ,
2010-11-02 18:11:58 +01:00
None )
if ( request . hasSupervisorUuid ) messageBuilder . setSupervisorUuid ( request . getSupervisorUuid )
messageBuilder . build
2010-07-26 18:47:25 +02:00
}
2010-10-26 12:04:32 +02:00
2010-10-28 20:30:11 +02:00
private def authenticateRemoteClient ( request : RemoteMessageProtocol , ctx : ChannelHandlerContext ) = {
2010-10-26 15:23:50 +02:00
val attachment = ctx . getAttachment
2010-10-29 16:33:31 +02:00
if ( ( attachment ne null ) &&
attachment . isInstanceOf [ String ] &&
2010-10-26 15:23:50 +02:00
attachment . asInstanceOf [ String ] == CHANNEL_INIT ) { // is first time around, channel initialization
2010-10-29 16:33:31 +02:00
ctx . setAttachment ( null )
2010-10-26 15:23:50 +02:00
val clientAddress = ctx . getChannel . getRemoteAddress . toString
if ( ! request . hasCookie ) throw new SecurityException (
"The remote client [" + clientAddress + "] does not have a secure cookie." )
2011-01-03 12:42:30 +01:00
if ( ! ( request . getCookie == SECURE_COOKIE . get ) ) throw new SecurityException (
2010-10-26 15:23:50 +02:00
"The remote client [" + clientAddress + "] secure cookie is not the same as remote server secure cookie" )
2010-10-26 12:04:32 +02:00
}
}
2011-01-03 13:49:39 +01:00
protected def parseUuid ( protocol : UuidProtocol ) : Uuid = uuidFrom ( protocol . getHigh , protocol . getLow )
2009-06-24 15:12:47 +02:00
}
2011-01-31 12:41:39 +01:00
class DefaultDisposableChannelGroup ( name : String ) extends DefaultChannelGroup ( name ) {
protected val guard = new ReadWriteGuard
protected val open = new AtomicBoolean ( true )
override def add ( channel : Channel ) : Boolean = guard withReadGuard {
if ( open . get ) {
super . add ( channel )
} else {
channel . close
false
}
}
override def close ( ) : ChannelGroupFuture = guard withWriteGuard {
if ( open . getAndSet ( false ) ) {
super . close
} else {
throw new IllegalStateException ( "ChannelGroup already closed, cannot add new channel" )
}
}
2011-02-04 15:14:49 +13:00
}