193 lines
6.3 KiB
Scala
193 lines
6.3 KiB
Scala
/* __ *\
|
|
** ________ ___ / / ___ Scala API **
|
|
** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL **
|
|
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
|
|
** /____/\___/_/ |_/____/_/ | | **
|
|
** |/ **
|
|
\* */
|
|
|
|
// $Id: Channel.scala 18365 2009-07-21 11:00:42Z michelou $
|
|
|
|
package scala.remoting
|
|
|
|
import java.io._
|
|
import java.net._
|
|
import java.rmi.server.RMIClassLoader
|
|
|
|
/** <p>
|
|
* The class <code>Channel</code> implements (basic) typed channels
|
|
* which use <a href="http://java.sun.com/docs/books/tutorial/networking/sockets/"
|
|
* target="_top"/>Java socket</a> communication and Scala type manifests to
|
|
* provide type-safe send/receive operations between a localhost and another
|
|
* remote machine by specifying some <code>host</code> and <code>port</code>.
|
|
* </p>
|
|
*
|
|
* @author Stephane Micheloud
|
|
* @version 1.1
|
|
*/
|
|
class Channel protected (socket: Socket) {
|
|
|
|
// Create a socket without a timeout
|
|
def this(host: String, port: Int) = this(new Socket(host, port))
|
|
|
|
// // Create a socket with a timeout
|
|
// val sockaddr: SocketAddress = new InetSocketAddress(addr, port)
|
|
// val socket = new Socket()
|
|
// // If the timeout occurs, SocketTimeoutException is thrown.
|
|
// socket.connect(sockaddr, 2000) // 2 seconds
|
|
|
|
/** Returns the local address of this channel. */
|
|
val host = socket.getInetAddress.getHostAddress
|
|
|
|
/** Returns the port on which this channel is listening. */
|
|
val port = socket.getLocalPort
|
|
|
|
private var cl: ClassLoader =
|
|
try {
|
|
// requires permission in Java policy file
|
|
val codebase = System.getProperty("java.rmi.server.codebase")
|
|
if (codebase != null) info("codebase="+codebase)
|
|
RMIClassLoader.getClassLoader(codebase)
|
|
}
|
|
catch {
|
|
case e: Exception =>
|
|
error("Class loader undefined: " + e.getMessage)
|
|
null
|
|
}
|
|
def classLoader: ClassLoader = cl
|
|
def classLoader_=(x: ClassLoader) { cl = x }
|
|
|
|
info(""+this)
|
|
|
|
private class CustomObjectInputStream(in: InputStream)
|
|
extends ObjectInputStream(in) {
|
|
override def resolveClass(desc: ObjectStreamClass): Class[_] =
|
|
if (cl eq null)
|
|
super.resolveClass(desc)
|
|
else
|
|
try {
|
|
info("resolve class "+desc.getName)
|
|
cl loadClass desc.getName
|
|
}
|
|
catch {
|
|
case e: ClassNotFoundException =>
|
|
super.resolveClass(desc)
|
|
}
|
|
}
|
|
/*
|
|
// lazy modifier is required!
|
|
private lazy val in =
|
|
try {
|
|
new CustomObjectInputStream(socket.getInputStream)
|
|
}
|
|
catch {
|
|
case e: IOException =>
|
|
error("Input stream undefined: "+e.getMessage+" ("+this+")")
|
|
null
|
|
}
|
|
private lazy val out =
|
|
try {
|
|
new ObjectOutputStream(socket.getOutputStream)
|
|
}
|
|
catch {
|
|
case e: IOException =>
|
|
error("Output stream undefined: "+e.getMessage+" ("+this+")")
|
|
null
|
|
}
|
|
*/
|
|
/** <code>receive<primtype></code> methods may throw an
|
|
* <code>IOException</code>.
|
|
*/
|
|
def receiveUnit = receive[Unit]
|
|
def receiveBoolean = receive[Boolean]
|
|
def receiveByte = receive[Byte]
|
|
def receiveChar = receive[Char]
|
|
def receiveShort = receive[Short]
|
|
def receiveInt = receive[Int]
|
|
def receiveLong = receive[Long]
|
|
def receiveFloat = receive[Float]
|
|
def receiveDouble = receive[Double]
|
|
def receiveString = receive[String]
|
|
|
|
/** <code>receive</code> method may throw either an
|
|
* <code>ClassNotFoundException</code> or an <code>IOException</code>.
|
|
*
|
|
* @throw <code>ChannelException</code> if received value has not
|
|
* the expected type.
|
|
*/
|
|
@throws(classOf[ChannelException])
|
|
def receive[T](implicit expected: reflect.Manifest[T]): T = {
|
|
val in = new CustomObjectInputStream(socket.getInputStream)
|
|
val found = in.readObject().asInstanceOf[reflect.Manifest[_]]
|
|
info("receive: found="+found+", expected="+expected)
|
|
import scala.reflect.Manifest
|
|
val x = found match {
|
|
case Manifest.Unit => ()
|
|
case Manifest.Boolean => in.readBoolean()
|
|
case Manifest.Byte => in.readByte()
|
|
case Manifest.Char => in.readChar()
|
|
case Manifest.Short => in.readShort()
|
|
case Manifest.Int => in.readInt()
|
|
case Manifest.Long => in.readLong()
|
|
case Manifest.Float => in.readFloat()
|
|
case Manifest.Double => in.readDouble()
|
|
case _ => in.readObject()
|
|
}
|
|
val res = if (found <:< expected)
|
|
x.asInstanceOf[T]
|
|
else
|
|
throw new ChannelException(
|
|
"\n\tfound \""+found+"\"\n\texpected \""+expected+"\"")
|
|
info("received "+res+" (available="+in.available+")")
|
|
res
|
|
}
|
|
|
|
/** <code>?</code> method may throw either an
|
|
* <code>ClassNotFoundException</code> or an <code>IOException</code>.
|
|
*/
|
|
def ?[T](implicit m: reflect.Manifest[T]): T = receive[T](m)
|
|
|
|
/** <code>send</code> method may throw an <code>IOException</code>.
|
|
*/
|
|
def send[T](x: T)(implicit m: reflect.Manifest[T]) {
|
|
val out = new ObjectOutputStream(socket.getOutputStream)
|
|
out writeObject m
|
|
x match {
|
|
case x: Unit => // nop
|
|
case x: Boolean => out writeBoolean x
|
|
case x: Byte => out writeByte x
|
|
case x: Char => out writeChar x
|
|
case x: Short => out writeShort x
|
|
case x: Int => out writeInt x
|
|
case x: Long => out writeLong x
|
|
case x: Float => out writeFloat x
|
|
case x: Double => out writeDouble x
|
|
case x => out writeObject x
|
|
}
|
|
out.flush()
|
|
info("sent "+x)
|
|
}
|
|
|
|
/** <code>!</code> method may throw an <code>IOException</code>.
|
|
*/
|
|
def (implicit m: reflect.Manifest[T]) { send(x)(m) }
|
|
|
|
def close() {
|
|
try { socket.close() }
|
|
catch { case e: IOException => }
|
|
info(this+" closed")
|
|
}
|
|
|
|
override def toString: String = socket.toString
|
|
|
|
private def info(msg: String) {
|
|
runtime.remoting.Debug.info("[Channel] "+msg)
|
|
}
|
|
}
|
|
|
|
/** <code>ChannelException</code> may be thrown by the operation
|
|
* <code>receive</code> when the received data has not the expected type.
|
|
*/
|
|
case class ChannelException(msg: String) extends IOException(msg)
|
|
|