1. sbt 启动器的入门指南

sbt 启动器的入门指南 

sbt 启动器提供两个部分

  1. 一个接口,用于启动的应用程序与启动器代码进行交互
  2. 一个最小的 sbt-launch.jar,可以通过 ivy 解析启动应用程序。

sbt 启动器组件是一个自包含的 jar,它可以在系统上没有 Scala 或应用程序的情况下启动 Scala 应用程序或服务器。唯一的前提条件是启动器 jar 本身、一个可选的配置文件和 Java 运行时版本 1.6 或更高版本。

概述 

用户下载启动器 jar 并创建一个脚本以运行它。在本文档中,将假定脚本名为 launch。对于 Unix,脚本看起来像这样:java -jar sbt-launcher.jar "$@"

用户现在可以启动提供 sbt 启动器配置的服务器和应用程序。

或者,您可以将启动器与启动器配置文件重新打包。例如,sbt/sbt 拉取原始 JAR 并 为 sbt 注入相应的 boot.properties 文件.

应用程序 

要启动应用程序,用户然后下载应用程序的配置文件(称为 my.app.configuration)并创建一个脚本以启动它(称为 myapp

launch @my.app.configuration "$@"

用户然后可以使用 myapp arg1 arg2 ... 启动应用程序。

有关启动器配置的更多信息,请参见 启动器配置

服务器 

sbt 启动器可用于启动和发现系统上正在运行的服务器。启动器可以用来启动服务器,类似于应用程序。但是,如果需要,启动器也可以用来确保一次只有一个服务器实例正在运行。这是通过让客户端始终使用启动器作为服务定位器来实现的。

要发现服务器在哪里运行(或者如果它没有运行则启动它),用户下载服务器的配置文件(称为 my.server.configuration)并创建一个脚本以发现服务器(称为 find-myserver

launch --locate @my.server.properties.

此命令将打印出一行字符串,即用于访问服务器的 URI,例如 sbt://127.0.0.1:65501。客户端应使用 IP/端口连接到服务器并启动其连接。

在使用 locate 功能时,sbt 启动器对服务器做出了以下限制

  • 服务器必须有一个起始类,该类扩展 xsbti.ServerMain
  • 服务器必须有一个入口点(URI),客户端可以使用该入口点来检测服务器
  • 服务器必须定义一个锁定文件,启动器可以使用它来确保一次只有一个实例正在运行
  • 锁定文件所在的系统必须支持锁定。
  • 服务器必须允许启动器在不发送任何数据的情况下打开一个套接字到端口。这用于检查以前的服务器是否仍然存活。

解析应用程序/服务器 

与用于分发 sbt 的启动器一样,下载的启动器 jar 将根据提供的配置文件检索 Scala 和应用程序。版本可以是固定的,也可以从另一个配置文件中读取(其位置也是可配置的)。Scala 和应用程序 jar 下载到的位置也是可配置的。搜索的仓库是可配置的。启动时可选地初始化属性文件是可配置的。

启动器下载完必要的 jar 后,它将加载应用程序/服务器并调用其入口点。应用程序将接收有关其调用方式的信息:命令行参数、当前工作目录、Scala 版本和应用程序 ID(组织、名称、版本)。此外,应用程序可以请求启动器执行操作,例如获取 Scala jar 和任何版本的 Scala 的 ClassLoader,这些 Scala 可以从配置文件中指定的仓库中检索。它可以请求下载和运行其他应用程序。当应用程序完成时,它可以告诉启动器使用特定的退出代码退出或使用不同的 Scala 版本、不同的应用程序版本或不同的参数重新加载应用程序。

还有一些其他设置选项,例如将配置文件放入启动器 jar 中并将该文件作为单个下载分发。本文档的其余部分描述了配置、编写、分发和运行应用程序的详细信息。

创建启动的应用程序 

本节演示如何创建由该启动器启动的应用程序。首先,声明对 launcher-interface 的依赖关系。不要声明对启动器本身的依赖关系。启动器接口仅由 Java 接口组成,以避免在用于编译启动器的 Scala 版本和用于编译应用程序的 Scala 版本之间出现二进制不兼容问题。启动器接口类将由启动器提供,因此它只是一个编译时依赖项。如果您使用 sbt 构建,则依赖项定义将是

libraryDependencies += "org.scala-sbt" % "launcher-interface" % "1.0.0" % "provided"

resolvers += sbtResolver.value

使您的类的入口点实现 xsbti.AppMain。使用部分信息的示例

package com.acme.launcherapp

class Main extends xsbti.AppMain
{
    def run(configuration: xsbti.AppConfiguration) =
    {
        // get the version of Scala used to launch the application
        val scalaVersion = configuration.provider.scalaProvider.version

        // Print a message and the arguments to the application
        println("Hello world!  Running Scala " + scalaVersion)
        configuration.arguments.foreach(println)

        // demonstrate the ability to reboot the application into different versions of Scala
        // and how to return the code to exit with
        scalaVersion match
        {
            case "2.10.6" =>
                new xsbti.Reboot {
                    def arguments = configuration.arguments
                    def baseDirectory = configuration.baseDirectory
                    def scalaVersion = "2.11.8"
                    def app = configuration.provider.id
                }
            case "2.11.8" => new Exit(1)
            case _ => new Exit(0)
        }
    }
    class Exit(val code: Int) extends xsbti.Exit
}

接下来,为启动器定义一个配置文件。对于上面的类,它可能看起来像

[scala]
  version: 2.11.8
[app]
  org: com.acme
  name: launcherapp
  version: 0.0.1
  class: com.acme.launcherapp.Main
  cross-versioned: true
[repositories]
  local
  maven-central
[boot]
 directory: ${user.home}/.myapp/boot

然后,在 sbt 的 shell 中 publishLocal+publishLocal 应用程序以使其可用。有关更多信息,请参见 启动器配置.

运行应用程序 

如上所述,实际上运行应用程序有几个选项。第一个涉及提供一个修改后的 jar 用于下载。后两个需要提供一个配置文件用于下载。

  • 替换启动器 jar 中的 /sbt/sbt.boot.properties 文件并分发修改后的 jar。用户需要一个脚本运行 java -jar your-launcher.jar arg1 arg2 ....
  • 用户下载启动器 jar,您提供配置文件。

    • 用户需要运行 java -Dsbt.boot.properties=your.boot.properties -jar launcher.jar
    • 用户已经有一个脚本用来运行启动器(称为‘launch’)。用户需要运行 launch @your.boot.properties your-arg-1 your-arg-2

执行 

让我们回顾一下启动器启动您的应用程序时发生了什么。

启动时,启动器搜索其配置,然后解析它。一旦最终配置得到解析,启动器将继续获取启动应用程序所需的 jar。boot.directory 属性用作检索 jar 的基本目录。在目录上进行锁定,因此它可以在系统范围内共享。启动器检索请求的 Scala 版本以

${boot.directory}/${scala.version}/lib/

如果此目录已经存在,则启动器将采用启动性能的捷径,并假设 jar 已经下载。如果目录不存在,则启动器将使用 Apache Ivy 解析并检索 jar。类似的流程也发生在应用程序本身。它及其依赖项将被检索到

${boot.directory}/${scala.version}/${app.org}/${app.name}/.

一旦所有必需的代码下载完毕,类加载器就会被设置。启动器为请求的 Scala 版本创建一个类加载器。然后,它创建一个子类加载器,其中包含请求的 app.components 的 jar,以及 app.resources 中指定的路径。不使用组件的应用程序将把所有 jar 放入此类加载器中。

应用程序的主类随后被实例化。它必须是一个公共类,具有一个公共无参构造函数,并且必须符合xsbti.AppMainrun方法被调用,执行传递给应用程序。'run'方法的参数提供配置信息和一个回调,用于获取来自[repositories]中存储库的任何版本的Scala的类加载器。run方法的返回值决定了应用程序执行后会发生什么。它可以指定启动器应该重新启动应用程序,或者它应该使用提供的退出代码退出。