diff options
author | Paul Phillips <paulp@improving.org> | 2009-05-26 18:12:53 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2009-05-26 18:12:53 +0000 |
commit | 103c97f7deef02d81d6d87c21f751899c63683b1 (patch) | |
tree | 50680eb96e39e016f28df03582164cce1b73d5e2 /src/library | |
parent | 5e12bab4777dc63711834cd39bf8514fb7e8da40 (diff) | |
download | scala-103c97f7deef02d81d6d87c21f751899c63683b1.tar.gz scala-103c97f7deef02d81d6d87c21f751899c63683b1.tar.bz2 scala-103c97f7deef02d81d6d87c21f751899c63683b1.zip |
A big yet interim patch emerging from my attemp...
A big yet interim patch emerging from my attempts to centralize common
classloader-related code. As it turns out, not that much of the
patch is directly associated with that. Most of it is cleanup in the
neighborhoods I was visiting, but there are a few new library files
about which I'm open to feedback:
scala/util/control/Exception - lots of exception handling code.
scala/net/Utility - what would be the first file in scala.net.*,
more code to follow if that sounds like a good package idea.
scala/util/ScalaClassLoader - mostly convenience methods right
now, more sophistication to come
Also, this adds a :jar command to the repl which adds a jar to your
classpath and replays the session.
Diffstat (limited to 'src/library')
-rw-r--r-- | src/library/scala/net/Utility.scala | 19 | ||||
-rw-r--r-- | src/library/scala/util/ClassLoader.scala | 109 | ||||
-rw-r--r-- | src/library/scala/util/control/Exception.scala | 166 |
3 files changed, 294 insertions, 0 deletions
diff --git a/src/library/scala/net/Utility.scala b/src/library/scala/net/Utility.scala new file mode 100644 index 0000000000..033dcc86a3 --- /dev/null +++ b/src/library/scala/net/Utility.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.net + +import java.net.{ URL, MalformedURLException } +import scala.util.control.Exception._ + +/** Skeleton in anticipation of more convenience methods. */ +object Utility +{ + def parseURL(s: String): Option[URL] = + catching(classOf[MalformedURLException]) opt new URL(s) +}
\ No newline at end of file diff --git a/src/library/scala/util/ClassLoader.scala b/src/library/scala/util/ClassLoader.scala new file mode 100644 index 0000000000..503fdc7202 --- /dev/null +++ b/src/library/scala/util/ClassLoader.scala @@ -0,0 +1,109 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2009 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.util + +import java.lang.{ ClassLoader => JavaClassLoader } +import java.lang.reflect.{ Modifier, Method } +import java.net.URL +import ScalaClassLoader._ +import scala.util.control.Exception.{ catching } + +trait ScalaClassLoader extends JavaClassLoader +{ + /** Executing an action with this classloader as context classloader */ + def asContext[T](action: => T): T = { + val oldLoader = getContextLoader + try { + setContextLoader(this) + action + } + finally setContextLoader(oldLoader) + } + def setAsContext() { setContextLoader(this) } + + /** Load and link a class with this classloader */ + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, false) + /** Load, link and initialize a class with this classloader */ + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = tryClass(path, true) + + private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] = + catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt + Class.forName(path, initialize, this).asInstanceOf[Class[T]] + + /** Create an instance of a class with this classloader */ + def create(path: String): AnyRef = { + tryToInitializeClass(path) match { + case Some(clazz) => clazz.newInstance() + case None => null + } + } + + /** Run the main method of a class to be loaded by this classloader */ + def run(objectName: String, arguments: Seq[String]) { + val clsToRun = tryToInitializeClass(objectName) getOrElse ( + throw new ClassNotFoundException(objectName) + ) + + val method = clsToRun.getMethod("main", classOf[Array[String]]) + if (!Modifier.isStatic(method.getModifiers)) + throw new NoSuchMethodException(objectName + ".main is not static") + + asContext(method.invoke(null, Array(arguments.toArray): _*)) + } +} + +class URLClassLoader(urls: List[URL], parent: JavaClassLoader) + extends java.net.URLClassLoader(urls.toArray, parent) + with ScalaClassLoader +{ + /** Override to widen to public */ + override def addURL(url: URL) = super.addURL(url) +} + +object ScalaClassLoader { + def setContextLoader(cl: JavaClassLoader) = Thread.currentThread.setContextClassLoader(cl) + def getContextLoader() = Thread.currentThread.getContextClassLoader() + def getSystemLoader() = JavaClassLoader.getSystemClassLoader() + def defaultParentClassLoader() = findExtClassLoader() + + /** XXX move this to RichClass. */ + def callReflectively[T](clazz: Class[_], obj: String, method: String, args: Any*): Option[T] = { + val exceptions = List( + classOf[ClassNotFoundException], + classOf[NoSuchMethodException], + classOf[SecurityException], + classOf[NullPointerException], + classOf[ClassCastException] + ) + + catching(exceptions: _*) opt { + val o: Class[_] = clazz.getClassLoader loadClass obj + val m: Method = o getDeclaredMethod method + m.invoke(o, args map (_.asInstanceOf[AnyRef]) : _*).asInstanceOf[T] + } + } + + def fromURLs(urls: Seq[URL]): URLClassLoader = + new URLClassLoader(urls.toList, defaultParentClassLoader()) + + /** True if supplied class exists in supplied path */ + def classExists(urls: Seq[URL], name: String): Boolean = + (fromURLs(urls) tryToLoadClass name).isDefined + + // we cannot use the app classloader here or we get what looks to + // be classloader deadlock, but if we pass null we bypass the extension + // classloader and our extensions, so we search the hierarchy to find + // the classloader whose parent is null. Resolves bug #857. + def findExtClassLoader(): JavaClassLoader = { + def search(cl: JavaClassLoader): JavaClassLoader = { + if (cl == null) null + else if (cl.getParent == null) cl + else search(cl.getParent) + } + + search(getContextLoader()) + } +}
\ No newline at end of file diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala new file mode 100644 index 0000000000..bd01886c12 --- /dev/null +++ b/src/library/scala/util/control/Exception.scala @@ -0,0 +1,166 @@ +package scala.util.control + +/** Classes representing the components of exception handling. + * Each class is independently composable. Some common uses: + * + * <pre> + * <b>import</b> scala.util.control.Exception._ + * <b>import</b> java.net._ + * + * <b>val</b> x1 = catching(classOf[MalformedURLException]) opt new URL(s) + * <b>val</b> x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) + * </pre> + * @author Paul Phillips + */ + +import java.lang.reflect.InvocationTargetException + +object Exception +{ + trait Described { + protected val name: String + private var _desc: String = "" + def desc = _desc + def withDesc(s: String): this.type = { + _desc = s + this + } + override def toString() = name + "(" + desc + ")" + } + + /** A container class for finally code. */ + class Finally(fin: => Unit) extends Described { + protected val name = "Finally" + + def butFirst(fin2: => Unit) = new Finally({ fin2 ; fin }) + def andAlso(fin2: => Unit) = new Finally({ fin ; fin2 }) + def invoke() { fin } + } + + /** A container class for catch logic. */ + class Catch[+T](val pf: PartialFunction[Throwable, T]) extends Described { + protected val name = "Catch" + + /** Create a new Catch with additional exception handling logic. */ + def orElse[U >: T](pf2: PartialFunction[Throwable, U]): Catch[U] = new Catch(pf orElse pf2) + def orElse[U >: T](catch2: Catch[U]): Catch[U] = orElse(catch2.pf) + def orElse[U >: T](res: U, exes: Class[_ <: Throwable]*): Catch[U] = + orElse(pfFromExceptionsWithResult(_ => res, exes : _*)) + + /** Invoke this catch logic upon the supplied body. */ + def invokeOn[U >: T](body: => U): U = + try { body } + catch { case e if pf.isDefinedAt(e) => pf(e) } + + /** Invoke this catch logic upon the supplied body, mapping the result + * into Option[T] - None if any exception was caught, Some(T) otherwise. + */ + def opt[U >: T](body: => U): Option[U] = toOption invokeOn Some(body) + + /** Invoke this catch logic upon the supplied body, mapping the result + * into Either[Throwable, T] - Left(exception) if an exception was caught, + * Right(T) otherwise. + */ + def either[U >: T](body: => U): Either[Throwable, U] = toEither invokeOn Right(body) + + /** Create a new Catch with the same isDefinedAt logic as this one, + * but with the supplied apply method replacing the current one. */ + def withApply[U](f: (Throwable) => U): Catch[U] = { + val pf2 = new PartialFunction[Throwable, U] { + def isDefinedAt(x: Throwable) = pf isDefinedAt x + def apply(x: Throwable) = f(x) + } + new Catch(pf2) + } + + /** Convenience methods. */ + def toOption: Catch[Option[T]] = withApply(_ => None) + def toEither: Catch[Either[Throwable, T]] = withApply(Left(_)) + } + + /** A container class for try/catch/finally logic. */ + class Try[+T](body: => T, val catcher: Catch[T], val fin: Finally) { + /** Invoke "body" using catch logic "catcher" and finally "fin" */ + def invoke(): T = withFin { catcher invokeOn body } + + /** As invoke, but map caught exceptions to None and success to Some(T) */ + def opt(): Option[T] = withFin { catcher opt body } + + /** As invoke, but map caught exceptions to Left(ex) and success to Right(x) */ + def either(): Either[Throwable, T] = withFin { catcher either body } + + /** Create a new Try with the supplied body replacing the current body */ + def tryInstead[U >: T](body2: => U) = new Try(body2, catcher, fin) + + /** Create a new Try with the supplied Catch replacing the current Catch */ + def catchInstead[U >: T](catcher2: Catch[U]) = new Try(body, catcher2, fin) + + /** Create a new Try with the supplied Finally replacing the current Finally */ + def finInstead(fin2: Finally) = new Try(body, catcher, fin2) + + /** Create a new Try with the supplied logic appended to the existing Catch logic. */ + def catchAlso[U >: T](pf2: PartialFunction[Throwable, U]) = + new Try(body, catcher orElse pf2, fin) + + /** Create a new Try with the supplied code appended to the existing Finally. */ + def finAlso(fin2: => Unit) = new Try(body, catcher, fin andAlso fin2) + + override def toString() = List("Try(<body>)", catcher.toString, fin.toString) mkString " " + private def withFin[T](f: => T) = + try { f } + finally { fin.invoke() } + } + + /** The empty Catch object. */ + final val noCatch = new Catch( + new PartialFunction[Throwable, Nothing] { + def isDefinedAt(x: Throwable) = false + def apply(x: Throwable) = throw x + } + ) withDesc "<nothing>" + + /** The empty Finally object. */ + final val noFinally = new Finally(()) withDesc "()" + + /** Creates a Catch object which will catch any of the supplied exceptions. + * Since the returned Catch object has no specific logic defined and will simply + * rethrow the exceptions it catches, you will typically want to call "opt" or + * "either" on the return value, or assign custom logic by calling "withApply". + */ + def catching[T](exceptions: Class[_ <: Throwable]*): Catch[T] = + new Catch(pfFromExceptions(exceptions : _*)) withDesc exceptions.map(_.getName).mkString(", ") + + /** Create a Try object with the supplied body and Catch logic. */ + def tryCatch[T](body: => T)(pf: PartialFunction[Throwable, T]) = + new Try(body, new Catch(pf), noFinally) + + /** Create a Try object with the supplied body and Finally code. */ + def tryFin[T](body: => T)(fin: => Unit) = + new Try(body, noCatch, new Finally(fin)) + + /** Create a Try object with the supplied body, Catch logic, and Finally code. */ + def tryCatchFin[T](body: => T)(pf: PartialFunction[Throwable, T])(fin: => Unit) = + new Try(body, new Catch(pf), new Finally(fin)) + + val reflectionUnwrapper: Catch[Nothing] = { + // unwrap unhelpful nested exceptions so the most interesting one is reported + def unwrap(e: Throwable): Throwable = e match { + case (_: InvocationTargetException | _: ExceptionInInitializerError) if e.getCause ne null => + unwrap(e.getCause) + case _ => e + } + + new Catch({ case e => throw unwrap(e) }) + } + + /** Private **/ + + private def pfFromExceptionsWithResult[T](f: (Throwable) => T, exceptions: Class[_ <: Throwable]*) = + new PartialFunction[Throwable, T] { + def apply(ex: Throwable) = f(ex) + def isDefinedAt(x: Throwable) = exceptions exists (_ isAssignableFrom x.getClass) + } + + private def pfFromExceptions(exceptions: Class[_ <: Throwable]*) = + pfFromExceptionsWithResult[Nothing](throw _, exceptions : _*) +} |