MavenでHello Worldを出力するWeb Applicationを作成するチュートリアルです.数年前に書かれた本を参考にしたため素直に動かずトライアンドエラーで修正を繰り返してようやく動くようになりました.
Mavenプロジェクトを作成する
mkdir mvn-web-app
cd mvn-web-app
% mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-webapp
Interactiveセッションで色々聞かれるので,以下のように回答します.
Define value for property 'groupId': org.example
Define value for property 'artifactId': mvn-web-app
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' org.example: : org.example.web
Confirm properties configuration:
groupId: org.example
artifactId: mvn-web-app
version: 1.0-SNAPSHOT
package: org.example.web
パラメータを確認します.
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: maven-archetype-webapp:1.4
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.example
[INFO] Parameter: artifactId, Value: mvn-web-app
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.example.web
[INFO] Parameter: packageInPathFormat, Value: org/example/web
[INFO] Parameter: package, Value: org.example.web
[INFO] Parameter: groupId, Value: org.example
[INFO] Parameter: artifactId, Value: mvn-web-app
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Project created from Archetype in dir: /Users/[username]/Development/maven-practice/mvn-web-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
フォルダ構造を確認する
% tree
.
├── pom.xml
└── src
└── main
└── webapp
├── WEB-INF
│ └── web.xml
└── index.jsp
index.jspを修正する
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
web.xmlを修正する
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
pom.xmlを修正する
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mvn-web-app</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mvn-web-app Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>mvn-web-app</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
mvn:package
でパッケージを作成します.
mvn package
生成されたファイルを確認します.
% tree
.
├── pom.xml
├── src
│ └── main
│ └── webapp
│ ├── WEB-INF
│ │ └── web.xml
│ └── index.jsp
└── target
├── maven-archiver
│ └── pom.properties
├── mvn-web-app
│ ├── META-INF
│ ├── WEB-INF
│ │ ├── classes
│ │ └── web.xml
│ └── index.jsp
└── mvn-web-app.war
mvn-web-app.warが生成されています.mvn-web-app.warの中身を確認してみましょう.ただのzipなのでファイル名を変更してコピーし,展開してみます.
% cp ./target/mvn-web-app.war ./mvn-web-app.zip
% unzip mvn-web-app.zip -d ./mvn-web-app-extract
Archive: mvn-web-app.zip
inflating: ./mvn-web-app-extract/META-INF/MANIFEST.MF
creating: ./mvn-web-app-extract/WEB-INF/
creating: ./mvn-web-app-extract/WEB-INF/classes/
inflating: ./mvn-web-app-extract/index.jsp
inflating: ./mvn-web-app-extract/WEB-INF/web.xml
inflating: ./mvn-web-app-extract/META-INF/maven/org.example/mvn-web-app/pom.xml
inflating: ./mvn-web-app-extract/META-INF/maven/org.example/mvn-web-app/pom.properties
maven jetty pluginを追加する(試行錯誤)
pom.xmlにmaven-jetty-pluginを追加します.
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin<artifactId>
<version>6.1.0</version>
<configuration>
<scanIntervalSeconds>10</ScanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
</plugin>
mvn jetty:run
で実行します.以下のエラーが出ました.
% mvn jetty:run
[WARNING] failed SelectChannelConnector @ 0.0.0.0:8080
java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0 (Native Method)
at sun.nio.ch.Net.bind (Net.java:555)
at sun.nio.ch.ServerSocketChannelImpl.netBind (ServerSocketChannelImpl.java:337)
at sun.nio.ch.ServerSocketChannelImpl.bind (ServerSocketChannelImpl.java:294)
at sun.nio.ch.ServerSocketAdaptor.bind (ServerSocketAdaptor.java:89)
at org.mortbay.jetty.nio.SelectChannelConnector.open (SelectChannelConnector.java:198)
at org.mortbay.jetty.AbstractConnector.doStart (AbstractConnector.java:251)
原因はJettyプラグインで指定したPort 8080が他のアプリケーションで既に使われているからでした.lsof
コマンドで8080番を使っているアプリケーションを調べてみます.
% lsof -P -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
XXXXXXXX 84962 xxxxxx 9u IPv6 0x29cd8d412bd18e51 0t0 TCP *:8080 (LISTEN)
対象のアプリケーションを終了して,改めてlsof
コマンドを実行すると,今度は8080番を使用しているアプリケーションは何も表示されません.
% lsof -P -i:8080
もう一度実行してみます.
mvn jetty:run
[INFO] Configuring Jetty for project: mvn-web-app Maven Webapp
[INFO] Webapp source directory = /Users/[username]/Development/maven-practice/mvn-web-app/src/main/webapp
[INFO] web.xml file = /Users/[username]/Development/maven-practice/mvn-web-app/src/main/webapp/WEB-INF/web.xml
[INFO] Classes directory /Users/[username]/Development/maven-practice/mvn-web-app/target/classes does not exist
[INFO] Logging to org.slf4j.impl.MavenSimpleLogger(org.mortbay.log) via org.mortbay.log.Slf4jLog
[INFO] Context path = /mvn-web-app
[INFO] Tmp directory = /Users/[username]/Development/maven-practice/mvn-web-app/target/work
[INFO] Web defaults = jetty default
[INFO] Web overrides = none
[INFO] Webapp directory = /Users/[username]/Development/maven-practice/mvn-web-app/src/main/webapp
[INFO] Starting jetty 6.1.0 ...
[INFO] jetty-6.1.0
[INFO] Classpath = [file:/Users/[username]/Development/maven-practice/mvn-web-app/target/classes]
[INFO] Started SelectChannelConnector @ 0.0.0.0:8080
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 10 seconds.
エラーは出ませんでした.ブラウザからアクセスしてみます.
http://localhost:8080/mvn-web-app/
エラーメッセージが表示されました.
HTTP ERROR: 500
class [Ljava.lang.Object; cannot be cast to class [Lorg.apache.jasper.compiler.JavacErrorDetail; ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; [Lorg.apache.jasper.compiler.JavacErrorDetail; is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @58496dc)
RequestURI=/mvn-web-app/
Caused by:
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Lorg.apache.jasper.compiler.JavacErrorDetail; ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; [Lorg.apache.jasper.compiler.JavacErrorDetail; is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @58496dc)
at org.apache.jasper.compiler.JDTJavaCompiler.compile(JDTJavaCompiler.java:466)
at org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:317)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:364)
at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:581)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:344)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:464)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:358)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:491)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:367)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:185)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:689)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:391)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:268)
at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
at org.mortbay.jetty.servlet.DefaultServlet.doGet(DefaultServlet.java:414)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:491)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:367)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:185)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:689)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:391)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:146)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
at org.mortbay.jetty.Server.handle(Server.java:285)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:457)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:751)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:500)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:209)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:357)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:329)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:475)
指定したバージョン番号にミスがあったので修正します.
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin<artifactId>
<version>6.1.0</version>
->
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin<artifactId>
<version>6.1.10</version>
もう一度実行してブラウザからアクセスしてみます.
mvn jetty:run
別なエラーが発生しました.
HTTP ERROR: 500
PWC6033: Unable to compile class for JSP
PWC6199: Generated servlet error:
The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files
PWC6199: Generated servlet error:
java.util.Vector cannot be resolved to a type
PWC6199: Generated servlet error:
_jspx_dependants cannot be resolved
RequestURI=/mvn-web-app/
Caused by:
org.apache.jasper.JasperException: PWC6033: Unable to compile class for JSP
PWC6199: Generated servlet error:
The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files
PWC6199: Generated servlet error:
java.util.Vector cannot be resolved to a type
PWC6199: Generated servlet error:
_jspx_dependants cannot be resolved
at org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:107)
at org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:280)
at org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:350)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:411)
at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:592)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:344)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:470)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:364)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
pom.xmlのjetty-pluginのバージョンを修正してみます.
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin<artifactId>
<version>7.0.0.pre5</version>
エラーは変わりません.
HTTP ERROR: 500
PWC6033: Unable to compile class for JSP
PWC6199: Generated servlet error:
The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files
PWC6199: Generated servlet error:
java.util.Vector cannot be resolved to a type
PWC6199: Generated servlet error:
_jspx_dependants cannot be resolved
RequestURI=/mvn-web-app/
Caused by:
org.apache.jasper.JasperException: PWC6033: Unable to compile class for JSP
PWC6199: Generated servlet error:
The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files
色々調べてみるとjettyの開発元がorg.mortbay.jetty
からorg.eclipse.jetty
に移ったようです.それに合わせて正しいgroupIdを指定する必要があります.
pom.xmlを修正します.
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>7.0.0.pre5</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
</plugin>
↓
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
</plugin>
今度は以下にアクセスしてみます.
http://localhost:8080/mvn-web-app/
またエラーが出ました.
HTTP ERROR 404 Not Found
URI: /mvn-web-app/
STATUS: 404
MESSAGE: Not Found
SERVLET: default
以下にアクセスしてみます.
http://localhost:8080/
表示されました.
なお,以下のパートは削除しても動きます.デフォルト値を上書きする場合のみ必要なようです.
<!--
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
-->
プラグインの記述をpluginからdependencyに動かすと動かない
当たり前ですが,以下のようにpluginに書くべきものをdependencyに記述しても動きません.
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
<!--
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
-->
</plugin>
</plugins>
↓
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
<type>maven-plugin</type>
</dependency>
</dependencies>
以下のエラーが出ます.
[ERROR] No plugin found for prefix 'jetty' in the current project and in the plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from the repositories [local (/Users/[username]/.m2/repository), central (https://repo.maven.apache.org/maven2)] -> [Help 1]