2009-06-24 15:12:47 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009 Scalable Solutions.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package se.scalablesolutions.akka.kernel.nio
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.InvocationTargetException
|
|
|
|
|
import java.net.InetSocketAddress
|
|
|
|
|
import java.util.concurrent.{ConcurrentHashMap, Executors}
|
2009-06-25 23:47:30 +02:00
|
|
|
import kernel.actor._
|
|
|
|
|
import kernel.reactor.{DefaultCompletableFutureResult, CompletableFutureResult}
|
2009-06-24 15:12:47 +02:00
|
|
|
import kernel.util.Logging
|
|
|
|
|
import java.util.ArrayList
|
|
|
|
|
import java.util.List
|
|
|
|
|
import java.util.concurrent.atomic.AtomicLong
|
|
|
|
|
import java.util.logging.Level
|
|
|
|
|
import java.util.logging.Logger
|
|
|
|
|
|
2009-06-25 23:47:30 +02:00
|
|
|
import org.codehaus.aspectwerkz.intercept.Advisable
|
2009-06-24 15:12:47 +02:00
|
|
|
import org.jboss.netty.bootstrap.ServerBootstrap
|
|
|
|
|
import org.jboss.netty.channel._
|
|
|
|
|
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
|
|
|
|
|
import org.jboss.netty.handler.codec.serialization.ObjectDecoder
|
|
|
|
|
import org.jboss.netty.handler.codec.serialization.ObjectEncoder
|
|
|
|
|
|
|
|
|
|
class NettyServer extends Logging {
|
2009-06-25 23:47:30 +02:00
|
|
|
def connect = NettyServer.connect
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object NettyServer extends Logging {
|
2009-06-25 13:07:58 +02:00
|
|
|
private val HOSTNAME = "localhost"
|
|
|
|
|
private val PORT = 9999
|
|
|
|
|
private val CONNECTION_TIMEOUT_MILLIS = 100
|
2009-06-24 15:12:47 +02:00
|
|
|
|
2009-06-25 23:47:30 +02:00
|
|
|
@volatile private var isRunning = false
|
|
|
|
|
|
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 activeObjectFactory = new ActiveObjectFactory
|
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
|
|
|
// FIXME provide different codecs (Thrift, Avro, Protobuf, JSON)
|
|
|
|
|
|
2009-06-25 13:07:58 +02:00
|
|
|
private val handler = new ObjectServerHandler
|
2009-06-24 15:12:47 +02:00
|
|
|
bootstrap.getPipeline.addLast("handler", handler)
|
|
|
|
|
bootstrap.setOption("child.tcpNoDelay", true)
|
|
|
|
|
bootstrap.setOption("child.keepAlive", true)
|
|
|
|
|
bootstrap.setOption("child.reuseAddress", true)
|
|
|
|
|
bootstrap.setOption("child.connectTimeoutMillis", CONNECTION_TIMEOUT_MILLIS)
|
|
|
|
|
|
2009-06-25 23:47:30 +02:00
|
|
|
def connect = synchronized {
|
|
|
|
|
if (!isRunning) {
|
|
|
|
|
log.info("Starting NIO server at [%s:%s]", HOSTNAME, PORT)
|
|
|
|
|
bootstrap.bind(new InetSocketAddress(HOSTNAME, PORT))
|
|
|
|
|
isRunning = true
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ChannelPipelineCoverage {val value = "all"}
|
|
|
|
|
class ObjectServerHandler extends SimpleChannelUpstreamHandler with Logging {
|
|
|
|
|
private val activeObjectFactory = new ActiveObjectFactory
|
|
|
|
|
private val activeObjects = new ConcurrentHashMap[String, AnyRef]
|
2009-06-25 23:47:30 +02:00
|
|
|
private val actors = new ConcurrentHashMap[String, Actor]
|
|
|
|
|
|
|
|
|
|
private val MESSAGE_HANDLE = classOf[Actor].getDeclaredMethod(
|
|
|
|
|
"handle", Array[Class[_]](classOf[AnyRef], classOf[CompletableFutureResult]))
|
2009-06-24 15:12:47 +02:00
|
|
|
|
|
|
|
|
override def handleUpstream(ctx: ChannelHandlerContext, event: ChannelEvent) = {
|
|
|
|
|
if (event.isInstanceOf[ChannelStateEvent] && event.asInstanceOf[ChannelStateEvent].getState != ChannelState.INTEREST_OPS) {
|
|
|
|
|
log.debug(event.toString)
|
|
|
|
|
}
|
|
|
|
|
super.handleUpstream(ctx, event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def channelOpen(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
|
|
|
|
|
event.getChannel.getPipeline.addFirst("encoder", new ObjectEncoder)
|
|
|
|
|
event.getChannel.getPipeline.addFirst("decoder", new ObjectDecoder)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
|
2009-06-25 13:07:58 +02:00
|
|
|
//e.getChannel.write(firstMessage)
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) ={
|
|
|
|
|
val message = event.getMessage
|
|
|
|
|
if (message == null) throw new IllegalStateException("Message in MessageEvent is null: " + event)
|
|
|
|
|
if (message.isInstanceOf[RemoteRequest]) handleRemoteRequest(message.asInstanceOf[RemoteRequest], event.getChannel)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) = {
|
|
|
|
|
event.getCause.printStackTrace
|
|
|
|
|
log.error("Unexpected exception from downstream: %s", event.getCause)
|
|
|
|
|
event.getChannel.close
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def handleRemoteRequest(request: RemoteRequest, channel: Channel) = {
|
|
|
|
|
try {
|
|
|
|
|
log.debug(request.toString)
|
2009-06-25 23:47:30 +02:00
|
|
|
if (request.isActor) dispatchToActor(request, channel)
|
|
|
|
|
else dispatchToActiveObject(request, channel)
|
|
|
|
|
} catch {
|
2009-06-24 15:12:47 +02:00
|
|
|
case e: Exception =>
|
|
|
|
|
log.error("Could not invoke remote active object or actor [%s :: %s] due to: %s", request.method, request.target, e)
|
|
|
|
|
e.printStackTrace
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-25 23:47:30 +02:00
|
|
|
private def dispatchToActor(request: RemoteRequest, channel: Channel) = {
|
|
|
|
|
log.debug("Dispatching to actor [%s]", request.target)
|
|
|
|
|
val actor = createActor(request.target)
|
|
|
|
|
actor.start
|
|
|
|
|
if (request.isOneWay) actor ! request.message
|
|
|
|
|
else {
|
|
|
|
|
try {
|
|
|
|
|
val resultOrNone = actor !! request.message
|
|
|
|
|
val result = if (resultOrNone.isDefined) resultOrNone else null
|
|
|
|
|
log.debug("Returning result from actor invocation [%s]", result)
|
|
|
|
|
channel.write(request.newReplyWithMessage(result, ActiveObject.threadBoundTx.get))
|
|
|
|
|
} catch {
|
|
|
|
|
case e: InvocationTargetException =>
|
|
|
|
|
log.error("Could not invoke remote actor [%s] due to: %s", request.target, e.getCause)
|
|
|
|
|
e.getCause.printStackTrace
|
|
|
|
|
channel.write(request.newReplyWithException(e.getCause))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def dispatchToActiveObject(request: RemoteRequest, channel: Channel) = {
|
|
|
|
|
log.debug("Dispatching to [%s :: %s]", request.method, request.target)
|
|
|
|
|
val activeObject = createActiveObject(request.target)
|
|
|
|
|
|
|
|
|
|
val args = request.message.asInstanceOf[scala.List[AnyRef]]
|
|
|
|
|
val argClazzes = args.map(_.getClass)
|
|
|
|
|
val (unescapedArgs, unescapedArgClasses) = unescapeArgs(args, argClazzes)
|
|
|
|
|
|
|
|
|
|
continueTransaction(request)
|
|
|
|
|
try {
|
|
|
|
|
val messageReceiver = activeObject.getClass.getDeclaredMethod(request.method, unescapedArgClasses)
|
|
|
|
|
if (request.isOneWay) messageReceiver.invoke(activeObject, unescapedArgs)
|
|
|
|
|
else {
|
|
|
|
|
val result = messageReceiver.invoke(activeObject, unescapedArgs)
|
|
|
|
|
log.debug("Returning result from active object invocation [%s]", result)
|
|
|
|
|
channel.write(request.newReplyWithMessage(result, ActiveObject.threadBoundTx.get))
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
case e: InvocationTargetException =>
|
|
|
|
|
log.error("Could not invoke remote active object [%s :: %s] due to: %s", request.method, request.target, e.getCause)
|
|
|
|
|
e.getCause.printStackTrace
|
|
|
|
|
channel.write(request.newReplyWithException(e.getCause))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def continueTransaction(request: RemoteRequest) = {
|
|
|
|
|
val tx = request.tx
|
|
|
|
|
if (tx.isDefined) {
|
|
|
|
|
tx.get.reinit
|
|
|
|
|
ActiveObject.threadBoundTx.set(tx)
|
|
|
|
|
} else ActiveObject.threadBoundTx.set(None)
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-24 15:12:47 +02:00
|
|
|
private def unescapeArgs(args: scala.List[AnyRef], argClasses: scala.List[Class[_]]) = {
|
|
|
|
|
val unescapedArgs = new Array[AnyRef](args.size)
|
|
|
|
|
val unescapedArgClasses = new Array[Class[_]](args.size)
|
|
|
|
|
|
|
|
|
|
val escapedArgs = for (i <- 0 until args.size) {
|
|
|
|
|
if (args(i).isInstanceOf[ProxyWrapper]) {
|
|
|
|
|
val proxyName = args(i).asInstanceOf[ProxyWrapper].proxyName
|
|
|
|
|
val activeObject = createActiveObject(proxyName)
|
|
|
|
|
unescapedArgs(i) = activeObject
|
|
|
|
|
unescapedArgClasses(i) = Class.forName(proxyName)
|
|
|
|
|
} else {
|
|
|
|
|
unescapedArgs(i) = args(i)
|
|
|
|
|
unescapedArgClasses(i) = argClasses(i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(unescapedArgs, unescapedArgClasses)
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-25 23:47:30 +02:00
|
|
|
private def createActiveObject(name: String): AnyRef = {
|
2009-06-24 15:12:47 +02:00
|
|
|
val activeObjectOrNull = activeObjects.get(name)
|
|
|
|
|
if (activeObjectOrNull == null) {
|
|
|
|
|
val clazz = Class.forName(name)
|
2009-06-25 13:07:58 +02:00
|
|
|
try {
|
|
|
|
|
val actor = new Dispatcher(clazz.getName)
|
|
|
|
|
actor.start
|
|
|
|
|
val newInstance = activeObjectFactory.newInstance(clazz, actor, false).asInstanceOf[AnyRef]
|
|
|
|
|
activeObjects.put(name, newInstance)
|
|
|
|
|
newInstance
|
|
|
|
|
} catch {
|
|
|
|
|
case e =>
|
|
|
|
|
log.debug("Could not create remote active object instance due to: %s", e)
|
|
|
|
|
e.printStackTrace
|
2009-06-25 23:47:30 +02:00
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
} else activeObjectOrNull
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def createActor(name: String): Actor = {
|
|
|
|
|
val actorOrNull = actors.get(name)
|
|
|
|
|
if (actorOrNull == null) {
|
|
|
|
|
val clazz = Class.forName(name)
|
|
|
|
|
try {
|
|
|
|
|
val newInstance = clazz.newInstance.asInstanceOf[Actor]
|
|
|
|
|
actors.put(name, newInstance)
|
|
|
|
|
newInstance
|
|
|
|
|
} catch {
|
|
|
|
|
case e =>
|
|
|
|
|
log.debug("Could not create remote actor instance due to: %s", e)
|
|
|
|
|
e.printStackTrace
|
|
|
|
|
throw e
|
2009-06-25 13:07:58 +02:00
|
|
|
}
|
2009-06-25 23:47:30 +02:00
|
|
|
} else actorOrNull
|
2009-06-24 15:12:47 +02:00
|
|
|
}
|
|
|
|
|
}
|