2011年3月22日火曜日

[Scala] sbt jetty-runでhttpsを使用したい

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

方法1: カスタムjetty.xmlを使う

sbt標準だとjetty.xmlを1から書けばOK。
override def jettyConfiguration = new CustomJettyConfiguration {
  override def jettyConfigurationXML = List("/path/to/jetty.xml")
}

この方法だとjetty.xmlにConnectorとかHandlerとか全部自分で書かないといけないので面倒…
参考:jetty.xmlのリファレンス

方法2: sbtにちょっと手を入れてjetty設定用のhookを使う

sbtのソースをcheckoutしてhookが設定できるようにする


参考:Build - simple-build-tool -

sbtのソースをcheckout
$ git clone git://github.com/harrah/xsbt.git
$ cd xsbt

変更したのは下の2行だけ。jetty-runが呼ばれてデフォルトのhttpポートのConnectorの設定直後にcallbackで設定した関数が呼ばれるようにする。
diff --git a/sbt/src/main/scala/sbt/WebApp.scala b/sbt/src/main/scala/sbt/WebApp.scala
index f5a3d09..997294e 100644
--- a/sbt/src/main/scala/sbt/WebApp.scala
+++ b/sbt/src/main/scala/sbt/WebApp.scala
@@ -105,6 +105,8 @@ trait DefaultJettyConfiguration extends JettyConfiguration
        def parentLoader: ClassLoader
        def jettyEnv: Option[File]
        def webDefaultXml: Option[File]
+
+       def callback: Option[AnyRef => Any] = None
 }
 abstract class CustomJettyConfiguration extends JettyConfiguration
 {
diff --git a/sbt/src/main/scala/sbt/jetty/LazyJettyRun.scala.templ b/sbt/src/main/scala/sbt/jetty/LazyJettyRun.scala.templ
index 263afa6..4ca4b0e 100644
--- a/sbt/src/main/scala/sbt/jetty/LazyJettyRun.scala.templ
+++ b/sbt/src/main/scala/sbt/jetty/LazyJettyRun.scala.templ
@@ -54,6 +54,7 @@ private object LazyJettyRun${jetty.version} extends JettyRun
                                case c: DefaultJettyConfiguration =>
                                        import c._
                                        configureDefaultConnector(server, port)
+                                       c.callback.foreach(_(server))
                                        val webapp = new WebAppContext(war.absolutePath, contextPath)
                                        webDefaultXml.foreach{webDefaultXml:File => webapp.setDefaultsDescriptor(webDefaultXml.toString)}

sbtのビルド

$ sbt update generate-loader-compat proguard "project Simple Build Tool" publish-local
※generate-loader-compatしないとjetty6用のクラスが一部生成されなかった。(jettyは6->7でパッケージ名が変わったので、sbtのソースではテンプレート化して6,7,7.2用のscalaソースが吐かれるようになってる

ssl用証明書の準備

JDKのkeytoolを使用。とりあえずオレオレ証明書で。
$ keytool -keystore src/test/resources/keystore -alias jetty -genkey -keyalg RSA
$ keytool -selfcert -validity 1024 -keystore src/test/resources/keystore -alias jetty
※keystoreのパスワードを聞かれるので入力します

sbt プロジェクト側の準備

build.propertiesの sbt.version=0.7.5.RC0 にしてsbt実行、先に標準の0.7.5.RC0を入れておく。

ビルドしたsbt/target/scala_2.7.7/sbt_2.7.7-0.7.5.RC0.jar をプロジェクトのproject/boot/scala-2.7.7/org.scala-tools.sbt/sbt/0.7.5.RC0/sbt_2.7.7-0.7.5.RC0.jar に上書き。

project/build/MyProject.scala の設定。callbackでjettyのServerインスタンスにSslSocketConnectorを追加。ClassLoaderが違うので普通に書くとClassCastExceptionが発生するのでリフレクションで。
  override def jettyConfiguration: JettyConfiguration =
    new DefaultJettyConfiguration {
      def classpath = jettyRunClasspath
      def jettyClasspath = MyProject.this.jettyClasspath
      def war = jettyWebappPath
      def contextPath = jettyContextPath
      def classpathName = "test"
      def parentLoader = buildScalaInstance.loader
      def scanDirectories = Path.getFiles(MyProject.this.scanDirectories).toSeq
      def scanInterval = MyProject.this.scanInterval
      def port = jettyPort
      def log = MyProject.this.log
      def jettyEnv = jettyEnvXml
      def webDefaultXml = jettyWebDefaultXml
      override def callback = Some((server: AnyRef) =>  {
        val cl = server.getClass.getClassLoader
        val ssl = cl.loadClass("org.mortbay.jetty.security.SslSocketConnector").newInstance.asInstanceOf[{
          def setPort(p: Int): Unit
          def setMaxIdleTime(t: Int): Unit
          def setKeystore(s: String): Unit
          def setPassword(s: String): Unit
          def setKeyPassword(s: String): Unit
          def setTruststore(s: String): Unit
          def setTrustPassword(s: String): Unit
        }]
        val addConnector: java.lang.reflect.Method = server.getClass.getMethod("addConnector", cl.loadClass("org.mortbay.jetty.Connector"))

        ssl.setPort(8443)
        ssl.setMaxIdleTime(30000)
        ssl.setKeystore(info.projectPath + "/src/test/resources/keystore")
        ssl.setPassword("password")
        ssl.setKeyPassword("password")
        ssl.setTruststore(info.projectPath + "/src/test/resources/keystore")
        ssl.setTrustPassword("password")
        addConnector.invoke(server, ssl)
      })
  }
※上記はjetty6の場合で、jetty7の場合はパッケージ・クラス名がちょっと違います。

これで、jetty-runで8080でhttp、8443でhttpsが起動します。

0 件のコメント:

コメントを投稿