2012-01-20 14:29:50 +01:00
/* *
* Copyright ( C ) 2009 - 2010 Typesafe Inc . < http : //www.typesafe.com>
*/
package akka.remote
import scala.reflect.BeanProperty
import akka.dispatch.SystemMessage
import akka.event. { LoggingAdapter , Logging }
import akka.AkkaException
2012-01-27 12:14:28 +01:00
import akka.serialization.Serialization
2012-02-01 16:06:30 +01:00
import akka.remote.RemoteProtocol._
2012-04-23 18:59:49 +02:00
import akka.actor._
2012-01-20 14:29:50 +01:00
/* *
* Remote life - cycle events .
*/
2012-03-16 13:45:32 +01:00
sealed trait RemoteLifeCycleEvent extends Serializable {
2012-01-20 14:29:50 +01:00
def logLevel : Logging.LogLevel
}
/* *
* Life - cycle events for RemoteClient .
*/
trait RemoteClientLifeCycleEvent extends RemoteLifeCycleEvent {
def remoteAddress : Address
}
2012-05-24 11:44:39 +02:00
/* *
* A RemoteClientError is a general error that is thrown within or from a RemoteClient
*/
2012-01-20 14:29:50 +01:00
case class RemoteClientError (
@BeanProperty cause : Throwable ,
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty remoteAddress : Address ) extends RemoteClientLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . ErrorLevel
override def toString : String = "RemoteClientError@" + remoteAddress + ": Error[" + cause + "]"
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteClientDisconnected is published when a RemoteClient 's connection is disconnected
*/
2012-01-20 14:29:50 +01:00
case class RemoteClientDisconnected (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty remoteAddress : Address ) extends RemoteClientLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . DebugLevel
override def toString : String = "RemoteClientDisconnected@" + remoteAddress
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteClientConnected is published when a RemoteClient 's connection is established
*/
2012-01-20 14:29:50 +01:00
case class RemoteClientConnected (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty remoteAddress : Address ) extends RemoteClientLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . DebugLevel
override def toString : String = "RemoteClientConnected@" + remoteAddress
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteClientStarted is published when a RemoteClient has started up
*/
2012-01-20 14:29:50 +01:00
case class RemoteClientStarted (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty remoteAddress : Address ) extends RemoteClientLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . InfoLevel
override def toString : String = "RemoteClientStarted@" + remoteAddress
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteClientShutdown is published when a RemoteClient has shut down
*/
2012-01-20 14:29:50 +01:00
case class RemoteClientShutdown (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty remoteAddress : Address ) extends RemoteClientLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . InfoLevel
override def toString : String = "RemoteClientShutdown@" + remoteAddress
2012-01-20 14:29:50 +01:00
}
/* *
* Life - cycle events for RemoteServer .
*/
trait RemoteServerLifeCycleEvent extends RemoteLifeCycleEvent
2012-05-24 11:44:39 +02:00
/* *
* RemoteServerStarted is published when a local RemoteServer has started up
*/
2012-01-20 14:29:50 +01:00
case class RemoteServerStarted (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ) extends RemoteServerLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . InfoLevel
override def toString : String = "RemoteServerStarted@" + remote
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteServerShutdown is published when a local RemoteServer has shut down
*/
2012-01-20 14:29:50 +01:00
case class RemoteServerShutdown (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ) extends RemoteServerLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . InfoLevel
override def toString : String = "RemoteServerShutdown@" + remote
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* A RemoteServerError is a general error that is thrown within or from a RemoteServer
*/
2012-01-20 14:29:50 +01:00
case class RemoteServerError (
@BeanProperty val cause : Throwable ,
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ) extends RemoteServerLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . ErrorLevel
override def toString : String = "RemoteServerError@" + remote + "] Error[" + cause + "]"
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteServerClientConnected is published when an inbound connection has been established
*/
2012-01-20 14:29:50 +01:00
case class RemoteServerClientConnected (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty val clientAddress : Option [ Address ] ) extends RemoteServerLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . DebugLevel
override def toString : String =
"RemoteServerClientConnected@" + remote + ": Client[" + clientAddress . getOrElse ( "no address" ) + "]"
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteServerClientConnected is published when an inbound connection has been disconnected
*/
2012-01-20 14:29:50 +01:00
case class RemoteServerClientDisconnected (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty val clientAddress : Option [ Address ] ) extends RemoteServerLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . DebugLevel
override def toString : String =
"RemoteServerClientDisconnected@" + remote + ": Client[" + clientAddress . getOrElse ( "no address" ) + "]"
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteServerClientClosed is published when an inbound RemoteClient is closed
*/
2012-01-20 14:29:50 +01:00
case class RemoteServerClientClosed (
2012-03-16 13:45:32 +01:00
@transient @BeanProperty remote : RemoteTransport ,
2012-01-20 14:29:50 +01:00
@BeanProperty val clientAddress : Option [ Address ] ) extends RemoteServerLifeCycleEvent {
2012-05-24 11:44:39 +02:00
override def logLevel : Logging . LogLevel = Logging . DebugLevel
override def toString : String =
"RemoteServerClientClosed@" + remote + ": Client[" + clientAddress . getOrElse ( "no address" ) + "]"
2012-01-20 14:29:50 +01:00
}
/* *
* Thrown for example when trying to send a message using a RemoteClient that is either not started or shut down .
*/
class RemoteClientException private [ akka ] (
message : String ,
2012-03-16 13:45:32 +01:00
@transient @BeanProperty val client : RemoteTransport ,
2012-01-20 14:29:50 +01:00
val remoteAddress : Address , cause : Throwable = null ) extends AkkaException ( message , cause )
2012-05-24 11:44:39 +02:00
/* *
* RemoteTransportException represents a general failure within a RemoteTransport ,
* such as inability to start , wrong configuration etc .
*/
2012-01-20 14:29:50 +01:00
class RemoteTransportException ( message : String , cause : Throwable ) extends AkkaException ( message , cause )
2012-01-27 15:21:05 +01:00
/* *
* The remote transport is responsible for sending and receiving messages .
* Each transport has an address , which it should provide in
* Serialization . currentTransportAddress ( thread - local ) while serializing
* actor references ( which might also be part of messages ) . This address must
* be available ( i . e . fully initialized ) by the time the first message is
* received or when the start ( ) method returns , whatever happens first .
*/
2012-04-23 18:59:49 +02:00
abstract class RemoteTransport ( val system : ExtendedActorSystem , val provider : RemoteActorRefProvider ) {
2012-01-20 14:29:50 +01:00
/* *
* Shuts down the remoting
*/
def shutdown ( ) : Unit
/* *
* Address to be used in RootActorPath of refs generated for this transport .
*/
def address : Address
/* *
2012-01-27 12:14:28 +01:00
* Start up the transport , i . e . enable incoming connections .
2012-01-20 14:29:50 +01:00
*/
2012-01-27 12:14:28 +01:00
def start ( ) : Unit
2012-01-20 14:29:50 +01:00
/* *
* Shuts down a specific client connected to the supplied remote address returns true if successful
*/
def shutdownClientConnection ( address : Address ) : Boolean
/* *
* Restarts a specific client connected to the supplied remote address , but only if the client is not shut down
*/
def restartClientConnection ( address : Address ) : Boolean
2012-05-24 11:44:39 +02:00
/* *
* Sends the given message to the recipient supplying the sender if any
*/
def send ( message : Any , senderOption : Option [ ActorRef ] , recipient : RemoteActorRef ) : Unit
2012-01-20 14:29:50 +01:00
2012-05-24 11:44:39 +02:00
/* *
* Default implementation both publishes the message to the eventStream as well as logs it using the system logger
*/
2012-04-08 00:25:53 +02:00
def notifyListeners ( message : RemoteLifeCycleEvent ) : Unit = {
2012-01-20 14:29:50 +01:00
system . eventStream . publish ( message )
2012-02-18 17:40:58 +01:00
system . log . log ( message . logLevel , "{}" , message )
2012-01-20 14:29:50 +01:00
}
2012-05-24 11:44:39 +02:00
/* *
* Returns this RemoteTransports Address ' textual representation
*/
override def toString : String = address . toString
2012-01-20 14:29:50 +01:00
2012-05-24 11:44:39 +02:00
/* *
* A Logger that can be used to log issues that may occur
*/
2012-01-20 14:29:50 +01:00
def log : LoggingAdapter
2012-05-24 11:44:39 +02:00
/* *
* When this method returns true , some functionality will be turned off for security purposes .
*/
2012-01-20 14:29:50 +01:00
protected def useUntrustedMode : Boolean
2012-05-24 11:44:39 +02:00
/* *
* Returns a newly created AkkaRemoteProtocol with the given message payload .
*/
def createMessageSendEnvelope ( rmp : RemoteMessageProtocol ) : AkkaRemoteProtocol =
AkkaRemoteProtocol . newBuilder . setMessage ( rmp ) . build
2012-01-20 14:29:50 +01:00
2012-05-24 11:44:39 +02:00
/* *
* Returns a newly created AkkaRemoteProtocol with the given control payload .
*/
def createControlEnvelope ( rcp : RemoteControlProtocol ) : AkkaRemoteProtocol =
AkkaRemoteProtocol . newBuilder . setInstruction ( rcp ) . build
2012-01-20 14:29:50 +01:00
/* *
* Serializes the ActorRef instance into a Protocol Buffers ( protobuf ) Message .
*/
2012-05-24 11:44:39 +02:00
def toRemoteActorRefProtocol ( actor : ActorRef ) : ActorRefProtocol =
2012-01-27 12:14:28 +01:00
ActorRefProtocol . newBuilder . setPath ( actor . path . toStringWithAddress ( address ) ) . build
2012-01-20 14:29:50 +01:00
2012-05-24 11:44:39 +02:00
/* *
* Returns a new RemoteMessageProtocol containing the serialized representation of the given parameters .
*/
def createRemoteMessageProtocolBuilder ( recipient : ActorRef , message : Any , senderOption : Option [ ActorRef ] ) : RemoteMessageProtocol . Builder = {
2012-01-20 14:29:50 +01:00
val messageBuilder = RemoteMessageProtocol . newBuilder . setRecipient ( toRemoteActorRefProtocol ( recipient ) )
if ( senderOption . isDefined ) messageBuilder . setSender ( toRemoteActorRefProtocol ( senderOption . get ) )
2012-01-27 12:14:28 +01:00
Serialization . currentTransportAddress . withValue ( address ) {
messageBuilder . setMessage ( MessageSerializer . serialize ( system , message . asInstanceOf [ AnyRef ] ) )
}
2012-01-20 14:29:50 +01:00
messageBuilder
}
2012-05-24 11:44:39 +02:00
/* *
* Call this method with an inbound RemoteMessage and this will take care of security ( see : " useUntrustedMode " )
* as well as making sure that the message ends up at its destination ( best effort ) .
* There is also a fair amount of logging produced by this method , which is good for debugging .
*/
def receiveMessage ( remoteMessage : RemoteMessage ) : Unit = {
2012-01-20 14:29:50 +01:00
val remoteDaemon = provider . remoteDaemon
remoteMessage . recipient match {
case `remoteDaemon` ⇒
2012-01-27 12:14:28 +01:00
if ( provider . remoteSettings . LogReceive ) log . debug ( "received daemon message {}" , remoteMessage )
2012-01-20 14:29:50 +01:00
remoteMessage . payload match {
case m @ ( _ : DaemonMsg | _ : Terminated ) ⇒
try remoteDaemon ! m catch {
case e : Exception ⇒ log . error ( e , "exception while processing remote command {} from {}" , m , remoteMessage . sender )
}
case x ⇒ log . warning ( "remoteDaemon received illegal message {} from {}" , x , remoteMessage . sender )
}
case l : LocalRef ⇒
2012-01-27 12:14:28 +01:00
if ( provider . remoteSettings . LogReceive ) log . debug ( "received local message {}" , remoteMessage )
2012-01-20 14:29:50 +01:00
remoteMessage . payload match {
2012-05-30 13:29:48 +02:00
case msg : PossiblyHarmful if useUntrustedMode ⇒ log . warning ( "operating in UntrustedMode, dropping inbound PossiblyHarmful message of type {}" , msg . getClass )
case msg : SystemMessage ⇒ l . sendSystemMessage ( msg )
case msg ⇒ l . ! ( msg ) ( remoteMessage . sender )
2012-01-20 14:29:50 +01:00
}
2012-01-27 12:14:28 +01:00
case r : RemoteRef ⇒
if ( provider . remoteSettings . LogReceive ) log . debug ( "received remote-destined message {}" , remoteMessage )
2012-01-20 14:29:50 +01:00
remoteMessage . originalReceiver match {
2012-02-27 10:28:20 +01:00
case AddressFromURIString ( address ) if address == provider . transport . address ⇒
2012-01-27 12:14:28 +01:00
// if it was originally addressed to us but is in fact remote from our point of view (i.e. remote-deployed)
2012-01-20 14:29:50 +01:00
r . ! ( remoteMessage . payload ) ( remoteMessage . sender )
2012-04-23 16:38:22 +02:00
case r ⇒ log . error ( "dropping message {} for non-local recipient {} arriving at {} inbound address is {}" , remoteMessage . payload , r , address , provider . transport . address )
2012-01-20 14:29:50 +01:00
}
2012-04-23 16:38:22 +02:00
case r ⇒ log . error ( "dropping message {} for non-local recipient {} arriving at {} inbound address is {}" , remoteMessage . payload , r , address , provider . transport . address )
2012-01-20 14:29:50 +01:00
}
}
}
2012-05-24 11:44:39 +02:00
/* *
* RemoteMessage is a wrapper around a message that has come in over the wire ,
* it allows to easily obtain references to the deserialized message , its intended recipient
* and the sender .
*/
class RemoteMessage ( input : RemoteMessageProtocol , system : ExtendedActorSystem ) {
/* *
* Returns a String - representation of the ActorPath that this RemoteMessage is destined for
*/
def originalReceiver : String = input . getRecipient . getPath
/* *
* Returns an Option with the String representation of the ActorPath of the Actor who is the sender of this message
*/
def originalSender : Option [ String ] = if ( input . hasSender ) Some ( input . getSender . getPath ) else None
/* *
* Returns a reference to the Actor that sent this message , or DeadLetterActorRef if not present or found .
*/
lazy val sender : ActorRef =
if ( input . hasSender ) system . provider . actorFor ( system . provider . rootGuardian , input . getSender . getPath )
else system . deadLetters
/* *
* Returns a reference to the Actor that this message is destined for .
* In case this returns a DeadLetterActorRef , you have access to the path using the "originalReceiver" method .
*/
lazy val recipient : InternalActorRef = system . provider . actorFor ( system . provider . rootGuardian , originalReceiver )
/* *
* Returns the message
*/
lazy val payload : AnyRef = MessageSerializer . deserialize ( system , input . getMessage )
/* *
* Returns a String representation of this RemoteMessage , intended for debugging purposes .
*/
override def toString : String = "RemoteMessage: " + payload + " to " + recipient + "<+{" + originalReceiver + "} from " + sender
}