diff --git a/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala b/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala index b49b934b5..c8c3064f7 100644 --- a/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala +++ b/core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala @@ -357,7 +357,7 @@ abstract class ResolverFunctions { def mavenStyleBasePattern = "[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]" def localBasePattern = - "[organisation]/[module]/" + PluginPattern + "(/[branch])/[revision]/[type]s/[artifact](-[classifier]).[ext]" + "[organisation]/[module]/" + PluginPattern + "([branch]/)[revision]/[type]s/[artifact](-[classifier]).[ext]" def defaultRetrievePattern = "[type]s/[organisation]/[module]/" + PluginPattern + "[artifact](-[revision])(-[classifier]).[ext]" final val PluginPattern = "(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala index aa88f6af4..cc13ea745 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/ConvertResolver.scala @@ -367,7 +367,7 @@ private[sbt] object ConvertResolver { // Here we duplicate the put method for files so we don't just bail on trying ot use Http handler val resource = getResource(destination) if (!overwrite && resource.exists()) { - throw new IOException("destination file exists and overwrite == false"); + throw new IOException(s"Destination file $destination exists and overwrite == false"); } fireTransferInitiated(resource, TransferEvent.REQUEST_PUT); try { diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala index c2b5b6cfb..cdbfd2ee3 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/Ivy.scala @@ -80,16 +80,30 @@ final class IvySbt(val configuration: IvyConfiguration) { self => } private lazy val basicUrlHandler: URLHandler = new BasicURLHandler - private lazy val gigahorseUrlHandler: URLHandler = { - val dispatcher = new URLHandlerDispatcher - val handler = new GigahorseUrlHandler - dispatcher.setDownloader("http", handler) - dispatcher.setDownloader("https", handler) - dispatcher - } + private lazy val gigahorseUrlHandler: URLHandler = new GigahorseUrlHandler + private lazy val settings: IvySettings = { - if (configuration.updateOptions.gigahorse) URLHandlerRegistry.setDefault(gigahorseUrlHandler) - else URLHandlerRegistry.setDefault(basicUrlHandler) + val dispatcher: URLHandlerDispatcher = URLHandlerRegistry.getDefault match { + // If the default is already a URLHandlerDispatcher then just use that + case disp: URLHandlerDispatcher => disp + + // Otherwise wrap the existing URLHandler in a URLHandlerDispatcher + // while retaining the existing URLHandler as the default. + case default => + val disp: URLHandlerDispatcher = new URLHandlerDispatcher() + disp.setDefault(default) + URLHandlerRegistry.setDefault(disp) + disp + } + + val urlHandler: URLHandler = + if (configuration.updateOptions.gigahorse) gigahorseUrlHandler else basicUrlHandler + + // Only set the urlHandler for the http/https protocols so we do not conflict with any other plugins + // that might register other protocol handlers. + // For example https://github.com/frugalmechanic/fm-sbt-s3-resolver registers "s3" + dispatcher.setDownloader("http", urlHandler) + dispatcher.setDownloader("https", urlHandler) val is = new IvySettings is.setCircularDependencyStrategy( diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala index 78e1154dc..1e842c458 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ErrorMessageAuthenticator.scala @@ -1,6 +1,7 @@ package sbt.internal.librarymanagement package ivyint +import java.lang.reflect.InvocationTargetException import java.net.{ Authenticator, PasswordAuthentication } import org.apache.ivy.util.Message @@ -14,18 +15,41 @@ object ErrorMessageAuthenticator { private var securityWarningLogged = false private def originalAuthenticator: Option[Authenticator] = { - try { + if (isJavaVersion9Plus) getDefaultAuthenticator else getTheAuthenticator + } + + private[this] def getTheAuthenticator: Option[Authenticator] = { + withJavaReflectErrorHandling { val field = classOf[Authenticator].getDeclaredField("theAuthenticator") field.setAccessible(true) Option(field.get(null).asInstanceOf[Authenticator]) - } catch { - // TODO - Catch more specific errors. - case t: Throwable => - Message.debug("Error occurred while getting the original authenticator: " + t.getMessage) - None } } + private[this] def getDefaultAuthenticator: Option[Authenticator] = + withJavaReflectErrorHandling { + val method = classOf[Authenticator].getDeclaredMethod("getDefault") + Option(method.invoke(null).asInstanceOf[Authenticator]) + } + + private[this] def withJavaReflectErrorHandling[A](t: => Option[A]): Option[A] = { + try t + catch { + case e: ReflectiveOperationException => handleReflectionException(e) + case e: SecurityException => handleReflectionException(e) + case e: InvocationTargetException => handleReflectionException(e) + case e: ExceptionInInitializerError => handleReflectionException(e) + case e: IllegalArgumentException => handleReflectionException(e) + case e: NullPointerException => handleReflectionException(e) + case e: ClassCastException => handleReflectionException(e) + } + } + + private[this] def handleReflectionException(t: Throwable) = { + Message.debug("Error occurred while getting the original authenticator: " + t.getMessage) + None + } + private lazy val ivyOriginalField = { val field = classOf[IvyAuthenticator].getDeclaredField("original") field.setAccessible(true) @@ -76,6 +100,15 @@ object ErrorMessageAuthenticator { } doInstallIfIvy(originalAuthenticator) } + + private[this] def isJavaVersion9Plus = javaVersion > 8 + private[this] def javaVersion = { + // See Oracle section 1.5.3 at: + // https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html + val version = sys.props("java.specification.version").split("\\.").map(_.toInt) + if (version(0) == 1) version(1) else version(0) + } + } /** diff --git a/project/Dependencies.scala b/project/Dependencies.scala index fae131ba4..6a7a0f7b6 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,11 +3,11 @@ import Keys._ import sbt.contraband.ContrabandPlugin.autoImport._ object Dependencies { - val scala211 = "2.11.11" - val scala212 = "2.12.3" + val scala211 = "2.11.12" + val scala212 = "2.12.4" - private val ioVersion = "1.0.0" - private val utilVersion = "1.0.0" + private val ioVersion = "1.0.2" + private val utilVersion = "1.0.3" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion @@ -40,7 +40,7 @@ object Dependencies { def addSbtUtilCache(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCache", utilCache) val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0" - val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-a3314352b638afbf0dca19f127e8263ed6f898bd" + val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-b18f59ea3bc914a297bb6f1a4f7fb0ace399e310" val jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive () val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value } val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value }