summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-05-26 18:12:53 +0000
committerPaul Phillips <paulp@improving.org>2009-05-26 18:12:53 +0000
commit103c97f7deef02d81d6d87c21f751899c63683b1 (patch)
tree50680eb96e39e016f28df03582164cce1b73d5e2 /src/library
parent5e12bab4777dc63711834cd39bf8514fb7e8da40 (diff)
downloadscala-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.scala19
-rw-r--r--src/library/scala/util/ClassLoader.scala109
-rw-r--r--src/library/scala/util/control/Exception.scala166
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 : _*)
+}