Tomcat 7から、組み込み版のTomcat APIができていたのは知っていましたが、なんとなく気にはなるものの、触ってこなかったのでちょっと試してみました。
技術者が知っておきたいTomcat 7の新機能20連発 (2/3)
http://www.atmarkit.co.jp/ait/articles/1106/24/news113_2.html
http://dev-blog-eq-diary.blogspot.jp/2012/04/tomcat-jetty.html
http://www.copperykeenclaws.com/embedding-tomcat-7/
アプリケーションサーバとしてのTomcatを用意しなくても、簡単にサーブレットが使えるときっと便利だよね!という発想から始めたのですが、思った通りにいかない部分も…。
で、今回は、Groovy+Gradleでやりました。
build.gradle
apply plugin: 'groovy' apply plugin: 'application' mainClassName = 'EmbeddedTomcatServer' ext { tomcatVersion = '7.0.42' } repositories { mavenCentral() } dependencies { compile "org.codehaus.groovy:groovy:2.1.6" compile "org.apache.tomcat:tomcat-catalina:$tomcatVersion" compile "org.apache.tomcat.embed:tomcat-embed-core:$tomcatVersion" compile "org.apache.tomcat:tomcat-jasper:$tomcatVersion" }
依存関係の定義は、こんな感じです。
src/main/groovy/EmbeddedTomcatServer.groovy
import javax.servlet.http.HttpServlet import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse import org.apache.catalina.startup.Tomcat class EmbeddedTomcatServer extends HttpServlet { public static void main(String[] args) { def tomcat = new Tomcat() tomcat.port = 8080 // tomcat.connector.useBodyEncodingForURI = true def context = tomcat.addWebapp('/', '.') Tomcat.addServlet(context, 'simpleServlet', new SimpleServlet()) context.addServletMapping('/simpleServlet', 'simpleServlet') tomcat.start() tomcat.server.await() } } class SimpleServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) { response.writer.write("""|ServletName: [${this.class.simpleName}] | |RequestURL: [${request.requestURL}] |QueryString: [${request.queryString}] |RequestParameters: ${request.parameterMap} |""".stripMargin()) } }
def tomcat = new Tomcat()
これに対して、設定をいくつかしていきます。
今回は、リッスンポートを8080にして
*デフォルトポートなので、この場合は別に指定しなくても大丈夫ですが
tomcat.port = 8080
その他、Connectorの設定をすることもできます。
// tomcat.connector.useBodyEncodingForURI = true
useBodyEncodingForURIとか設定した方がいいかなーと思ったのですが、動かしてみたらなんか設定しなくても大丈夫でした…。
続いて、Webアプリケーションの設定をします。
今回は、コンテキストパスを「/」にして、Webアプリケーションのディレクトリは「.」にしています。
def context = tomcat.addWebapp('/', '.')
サーブレットは、ひとつ登録。web.xmlでいうservlet-nameは「simpleServlet」に。
Tomcat.addServlet(context, 'simpleServlet', new SimpleServlet())
servlet-mappingのurl-patternは、「/simpleServlet」にしました。
context.addServletMapping('/simpleServlet', 'simpleServlet')
ちなみに、この2つの指定ですが
tomcat.port = [ポート番号] def context = tomcat.addWebapp('/', 'ベースディレクトリ')
カレントディレクトリ配下に
tomcat.[ポート番号]/webapps/[ベースディレクトリ]
がないとWebアプリケーションの起動に失敗しますので、ご注意を。
Webアプリケーションの登録方法はいくつかあるみたいですが、その他はAPIドキュメントを。
http://tomcat.apache.org/tomcat-7.0-doc/api/index.html
あとは、Tomcatを起動して待ち状態にします。
tomcat.start() tomcat.server.await()
Servet#awaitを入れておかないと、すぐに終了してしまいます。この場合は、Ctrl-Cとかで止めてください。
その他、シャットダウンを受け付けるようなコードを書いて、
tomcat.stop()
を書いてもOKです。
サンプルのサーブレットは、単に受け取ったリクエストの内容をそのまま出力するだけなので、端折ります。
では、起動。
$ gradle --daemon run :compileJava UP-TO-DATE :compileGroovy :processResources UP-TO-DATE :classes :run 7 20, 2013 5:08:41 午後 org.apache.coyote.AbstractProtocol init 情報: Initializing ProtocolHandler ["http-bio-8080"] 7 20, 2013 5:08:41 午後 org.apache.catalina.core.StandardService startInternal 情報: Starting service Tomcat 7 20, 2013 5:08:41 午後 org.apache.catalina.core.StandardEngine startInternal 情報: Starting Servlet Engine: Apache Tomcat/7.0.42 7 20, 2013 5:08:42 午後 org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment 情報: No global web.xml found 7 20, 2013 5:08:44 午後 org.apache.coyote.AbstractProtocol start 情報: Starting ProtocolHandler ["http-bio-8080"] > Building > :run
確認。
$ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /simpleServlet?param=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E3%80%81%E4%B8%96%E7%95%8C HTTP/1.0 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 228 Date: Sat, 20 Jul 2013 08:10:18 GMT Connection: close ServletName: [SimpleServlet] RequestURL: [http://localhost:8080/simpleServlet] QueryString: [param=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E3%80%81%E4%B8%96%E7%95%8C] RequestParameters: [param:[こんにちは、世界]] Connection closed by foreign host.
OKそうです。
なお、普段の自分ならこういうのはGroovy+Grapeで書くところなのですが、これで同じようなコードを書いて実行すると
Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig; at org.apache.catalina.deploy.WebXml.configureContext(WebXml.java:1374) at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1346) at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:878) at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:376) at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119) at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5322) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) ... 7 more
と、どうもServlet API 2.5以降で追加されたメソッドが見えていないみたいだったので…ちょっと考えてGroovyにServlet APIがいるんじゃあ?と思って確認してみたら
$ ll ~/.gvm/groovy/current/lib/servlet-api-2.4.jar -rw-r--r-- 1 xxxxx xxxxx 97693 Feb 18 13:55 /xxxxx/.gvm/groovy/current/lib/servlet-api-2.4.jar
まあ、いますよね…。
念のため、ServletContextがどこからロードされたか確認してみると
println(ServletContext.class.getResource('/javax/servlet/ServletContext.class'))
あ、やっぱり?
jar:file:/xxxxx/.gvm/groovy/2.1.6/lib/servlet-api-2.4.jar!/javax/servlet/ServletContext.class
う〜ん、できればこういうのは、Grapeでやりたかったのですが…環境変えたりして頑張れば動きそうですけど、サクッと動かしたり人にお手軽スクリプトとして紹介…なんて使い方だと、そういうのは微妙。
となると、JDK 6から入っていたJava HTTP Serverの方がいいのかなぁー。
あ、組み込みTomcatは、単に使ってみたかっただけなので自分の目標は達成しました。