今回は、TERASOLUNA 5.x(=実体はSpring Framework)が提供しているブランクプロジェクトから生成した開発プロジェクトにて、WebJarsの仕組みを利用してクライアント系ライブラリ(CSSライブラリ、JSライブラリ)のファイルをアプリケーションに組み込む方法を紹介します(=メモしておきます)。
Waht's TERASOLUNA 5.x ?
TERASOLUNA 5.xが何者かについては、ここに記載があります。
コンフィギュレーション方法がXMLベースだったり、Spring Bootをサポートしていない点が残念ではありますが、Springを使用してWebアプリケーションを開発するためのノウハウが詰まっており、メンテナンスも継続的(年1回のマイナーバージョンアップ+α)に行われています。
検証バージョン
- TERASOLUNA Server Framework for Java (5.x) 5.3.0.RELEASE
- Spring Framework 4.3.5.RELEASE
開発プロジェクトの生成
TERASOLUNA 5.xでは「シングルプロジェクト構成」と「マルチプロジェクト(マルチモジュール)構成」の2種類のブランクプロジェクト(Mavne Archetype)を提供していますが、今回はシングルプロジェクト構成のプロジェクトを作ります。
$ mvn archetype:generate -B\
-DarchetypeGroupId=org.terasoluna.gfw.blank\
-DarchetypeArtifactId=terasoluna-gfw-web-blank-archetype\
-DarchetypeVersion=5.3.0.RELEASE\
-DgroupId=com.example\
-DartifactId=demo-webjars\
-Dversion=1.0.0-SNAPSHOT
$ mvn archetype:generate -B\
> -DarchetypeGroupId=org.terasoluna.gfw.blank\
> -DarchetypeArtifactId=terasoluna-gfw-web-blank-archetype\
> -DarchetypeVersion=5.3.0.RELEASE\
> -DgroupId=com.example\
> -DartifactId=demo-webjars\
> -Dversion=1.0.0-SNAPSHOT
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.3:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.3:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:2.3:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] Archetype repository not defined. Using the one from [org.terasoluna.gfw.blank:terasoluna-gfw-web-blank-archetype:5.3.0.RELEASE] found in catalog remote
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: terasoluna-gfw-web-blank-archetype:5.3.0.RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: demo-webjars
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: packageInPathFormat, Value: com/example
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: demo-webjars
[INFO] project created from Archetype in dir: /usr/local/apps/demo-webjars
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.617 s
[INFO] Finished at: 2017-09-03T13:01:31+09:00
[INFO] Final Memory: 16M/222M
[INFO] ------------------------------------------------------------------------
$
$ tree demo-webjars
demo-webjars
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ ├── app
│ │ │ └── welcome
│ │ │ └── HelloController.java
│ │ └── domain
│ │ ├── model
│ │ ├── repository
│ │ └── service
│ ├── resources
│ │ ├── META-INF
│ │ │ ├── dozer
│ │ │ └── spring
│ │ │ ├── applicationContext.xml
│ │ │ ├── demo-webjars-codelist.xml
│ │ │ ├── demo-webjars-domain.xml
│ │ │ ├── demo-webjars-infra.xml
│ │ │ ├── spring-mvc.xml
│ │ │ └── spring-security.xml
│ │ ├── ValidationMessages.properties
│ │ ├── dozer.properties
│ │ ├── i18n
│ │ │ └── application-messages.properties
│ │ └── logback.xml
│ └── webapp
│ ├── WEB-INF
│ │ ├── views
│ │ │ ├── common
│ │ │ │ ├── error
│ │ │ │ │ ├── accessDeniedError.jsp
│ │ │ │ │ ├── businessError.jsp
│ │ │ │ │ ├── dataAccessError.jsp
│ │ │ │ │ ├── invalidCsrfTokenError.jsp
│ │ │ │ │ ├── missingCsrfTokenError.jsp
│ │ │ │ │ ├── resourceNotFoundError.jsp
│ │ │ │ │ ├── systemError.jsp
│ │ │ │ │ ├── transactionTokenError.jsp
│ │ │ │ │ └── unhandledSystemError.html
│ │ │ │ └── include.jsp
│ │ │ └── welcome
│ │ │ └── home.jsp
│ │ └── web.xml
│ └── resources
│ └── app
│ └── css
│ └── styles.css
└── test
├── java
└── resources
28 directories, 25 files
$
プロジェクトのビルド
プロジェクトの作成が終わったら、Mavneコマンドを使用してwarファイルをビルドします。
$ cd demo-webjars
$ mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building TERASOLUNA Server Framework for Java (5.x) Web Blank Project 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ demo-webjars ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 11 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ demo-webjars ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /usr/local/apps/demo-webjars/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ demo-webjars ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ demo-webjars ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ demo-webjars ---
[INFO]
[INFO] --- maven-war-plugin:2.5:war (default-war) @ demo-webjars ---
[INFO] Packaging webapp
[INFO] Assembling webapp [demo-webjars] in [/usr/local/apps/demo-webjars/target/demo-webjars-1.0.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/usr/local/apps/demo-webjars/src/main/webapp]
[INFO] Webapp assembled in [324 msecs]
[INFO] Building war: /usr/local/apps/demo-webjars/target/demo-webjars.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.454 s
[INFO] Finished at: 2017-09-03T13:07:22+09:00
[INFO] Final Memory: 32M/258M
[INFO] ------------------------------------------------------------------------
$
Webアプリの起動
ビルドしたwarファイルを、Carog Mavne Pluginを使用してアプリケーションサーバ(Tomcat)にデプロイして起動します。
$ mvn cargo:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building TERASOLUNA Server Framework for Java (5.x) Web Blank Project 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- cargo-maven2-plugin:1.6.2:run (default-cli) @ demo-webjars ---
[INFO] [en2.ContainerRunMojo] Resolved container artifact org.codehaus.cargo:cargo-core-container-tomcat:jar:1.6.2 for container tomcat8x
[INFO] [talledLocalContainer] Tomcat 8.x starting...
[INFO] [stalledLocalDeployer] Deploying [/usr/local/apps/demo-webjars/target/demo-webjars.war] to [/usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps]...
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Server version: Apache Tomcat/8.0.39
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Server built: Nov 9 2016 08:48:39 UTC
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Server number: 8.0.39.0
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: OS Name: Mac OS X
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: OS Version: 10.10.5
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Architecture: x86_64
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Java Home: /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: JVM Version: 1.8.0_131-b11
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: JVM Vendor: Oracle Corporation
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: CATALINA_BASE: /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: CATALINA_HOME: /usr/local/apps/demo-webjars/target/cargo/installs/apache-tomcat-8.0.39/apache-tomcat-8.0.39
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Xms512m
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Xmx1024m
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Dcatalina.home=/usr/local/apps/demo-webjars/target/cargo/installs/apache-tomcat-8.0.39/apache-tomcat-8.0.39
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Dcatalina.base=/usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Djava.io.tmpdir=/usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/temp
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.VersionLoggerListener log
[INFO] [talledLocalContainer] 情報: Command line argument: -Djava.util.logging.config.file=/usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/conf/logging.properties
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
[INFO] [talledLocalContainer] 情報: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /Users/shimizukazuki/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.coyote.AbstractProtocol init
[INFO] [talledLocalContainer] 情報: Initializing ProtocolHandler ["http-nio-8080"]
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
[INFO] [talledLocalContainer] 情報: Using a shared selector for servlet write/read
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.coyote.AbstractProtocol init
[INFO] [talledLocalContainer] 情報: Initializing ProtocolHandler ["ajp-nio-8009"]
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
[INFO] [talledLocalContainer] 情報: Using a shared selector for servlet write/read
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.Catalina load
[INFO] [talledLocalContainer] 情報: Initialization processed in 595 ms
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.core.StandardService startInternal
[INFO] [talledLocalContainer] 情報: サービス Catalina を起動します
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.core.StandardEngine startInternal
[INFO] [talledLocalContainer] 情報: Starting Servlet Engine: Apache Tomcat/8.0.39
[INFO] [talledLocalContainer] 9 03, 2017 1:08:56 午後 org.apache.catalina.startup.HostConfig deployWAR
[INFO] [talledLocalContainer] 情報: Webアプリケーションアーカイブ /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/cargocpc.war を配備します
[INFO] [talledLocalContainer] 9 03, 2017 1:08:57 午後 org.apache.catalina.startup.HostConfig deployWAR
[INFO] [talledLocalContainer] 情報: Deployment of web application archive /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/cargocpc.war has finished in 295 ms
[INFO] [talledLocalContainer] 9 03, 2017 1:08:57 午後 org.apache.catalina.startup.HostConfig deployWAR
[INFO] [talledLocalContainer] 情報: Webアプリケーションアーカイブ /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/demo-webjars.war を配備します
[INFO] [talledLocalContainer] 9 03, 2017 1:08:58 午後 org.apache.jasper.servlet.TldScanner scanJars
[INFO] [talledLocalContainer] 情報: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[INFO] [talledLocalContainer] 9 03, 2017 1:08:58 午後 org.apache.catalina.core.ApplicationContext log
[INFO] [talledLocalContainer] 情報: No Spring WebApplicationInitializer types detected on classpath
[INFO] [talledLocalContainer] 9 03, 2017 1:08:59 午後 org.apache.catalina.core.ApplicationContext log
[INFO] [talledLocalContainer] 情報: Initializing Spring root WebApplicationContext
[INFO] [talledLocalContainer] 9 03, 2017 1:09:00 午後 org.apache.catalina.core.ApplicationContext log
[INFO] [talledLocalContainer] 情報: Initializing Spring FrameworkServlet 'appServlet'
[INFO] [talledLocalContainer] date:2017-09-03 13:09:00 thread:localhost-startStop-1 X-Track: level:INFO logger:o.springframework.web.servlet.DispatcherServlet message:FrameworkServlet 'appServlet': initialization started
[INFO] [talledLocalContainer] date:2017-09-03 13:09:00 thread:localhost-startStop-1 X-Track: level:INFO logger:o.s.w.s.m.m.a.RequestMappingHandlerMapping message:Mapped "{[/],methods=[GET || POST]}" onto public java.lang.String com.example.app.welcome.HelloController.home(java.util.Locale,org.springframework.ui.Model)
[INFO] [talledLocalContainer] date:2017-09-03 13:09:01 thread:localhost-startStop-1 X-Track: level:INFO logger:o.s.w.s.m.m.a.RequestMappingHandlerAdapter message:Looking for @ControllerAdvice: WebApplicationContext for namespace 'appServlet-servlet': startup date [Sun Sep 03 13:09:00 JST 2017]; parent: Root WebApplicationContext
[INFO] [talledLocalContainer] date:2017-09-03 13:09:01 thread:localhost-startStop-1 X-Track: level:INFO logger:o.s.w.s.m.m.a.RequestMappingHandlerAdapter message:Looking for @ControllerAdvice: WebApplicationContext for namespace 'appServlet-servlet': startup date [Sun Sep 03 13:09:00 JST 2017]; parent: Root WebApplicationContext
[INFO] [talledLocalContainer] date:2017-09-03 13:09:01 thread:localhost-startStop-1 X-Track: level:INFO logger:o.s.web.servlet.handler.SimpleUrlHandlerMapping message:Mapped URL path [/**] onto handler 'org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0'
[INFO] [talledLocalContainer] date:2017-09-03 13:09:01 thread:localhost-startStop-1 X-Track: level:INFO logger:o.s.web.servlet.handler.SimpleUrlHandlerMapping message:Mapped URL path [/resources/**] onto handler 'org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0'
[INFO] [talledLocalContainer] date:2017-09-03 13:09:01 thread:localhost-startStop-1 X-Track: level:INFO logger:o.springframework.web.servlet.DispatcherServlet message:FrameworkServlet 'appServlet': initialization completed in 1222 ms
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.catalina.startup.HostConfig deployWAR
[INFO] [talledLocalContainer] 情報: Deployment of web application archive /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/demo-webjars.war has finished in 4,615 ms
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.catalina.startup.HostConfig deployDirectory
[INFO] [talledLocalContainer] 情報: Webアプリケーションディレクトリ /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/host-manager を配備します
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.catalina.startup.HostConfig deployDirectory
[INFO] [talledLocalContainer] 情報: Deployment of web application directory /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/host-manager has finished in 19 ms
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.catalina.startup.HostConfig deployDirectory
[INFO] [talledLocalContainer] 情報: Webアプリケーションディレクトリ /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/manager を配備します
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.catalina.startup.HostConfig deployDirectory
[INFO] [talledLocalContainer] 情報: Deployment of web application directory /usr/local/apps/demo-webjars/target/cargo/configurations/tomcat8x/webapps/manager has finished in 12 ms
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.coyote.AbstractProtocol start
[INFO] [talledLocalContainer] 情報: Starting ProtocolHandler ["http-nio-8080"]
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.coyote.AbstractProtocol start
[INFO] [talledLocalContainer] 情報: Starting ProtocolHandler ["ajp-nio-8009"]
[INFO] [talledLocalContainer] 9 03, 2017 1:09:01 午後 org.apache.catalina.startup.Catalina start
[INFO] [talledLocalContainer] 情報: Server startup in 5012 ms
[INFO] [talledLocalContainer] Tomcat 8.x started on port [8080]
[INFO] Press Ctrl-C to stop the container...
アプリケーションルートにアクセスすると、以下のようなトップ画面(HTML)が応用されます。
$ curl -D - http://localhost:8080/demo-webjars/
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Track: c061e8853a3f49c2919c8ac6f46071cf
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: text/html;charset=UTF-8
Content-Language: ja-JP
Content-Length: 320
Date: Sun, 03 Sep 2017 04:11:03 GMT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet"
href="/demo-webjars/resources/app/css/styles.css">
</head>
<body>
<div id="wrapper">
<h1>Hello world!</h1>
<p>The time on the server is 2017/09/03 13:11:03 JST.</p>
</div>
</body>
</html>
$
WebJarsを使う
今回はCSSライブラリのBootstrapをWebJarsの仕組みを利用してアプリケーションに取り込んでみます。
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7-1</version>
</dependency>
warをビルドし直して再度デプロイ・起動します。
$ mvn package cargo:run
...
$
WebJarsのbootstrapのcssファイルにアクセスしてみます。
$ curl -D - http://localhost:8080/demo-webjars/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Track: cda63d914b0142c5bd36c0e85dd8e22d
Accept-Ranges: bytes
ETag: W/"121200-1469407916000"
Last-Modified: Mon, 25 Jul 2016 00:51:56 GMT
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: text/css;charset=UTF-8
Content-Length: 121200
Date: Sun, 03 Sep 2017 04:24:44 GMT
/*!
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{fo....
...
Servlet 3.0+をサポートしているアプリケーションサーバの場合は、クラスパス(WEB-INF/libなど)にWebJarsのjarファイルがあれば、特別な設定を行うことなくアクセスすることができます。
ということで、単純にWebJarsを使うだけなら、
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet"
href="${pageContext.request.contextPath}/resources/app/css/styles.css">
<link rel="stylesheet"
href="${pageContext.request.contextPath}/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css"> <!-- ★ 追加 -->
</head>
<body>
<div id="wrapper">
<h1>Hello world!</h1>
<p>The time on the server is ${serverTime}.</p>
</div>
</body>
</html>
とすればOKです。
webjar-locatorを使用してバージョン番号を排除する
おそらく・・・多くのエンジニアの方はcssファイルにアクセスする際のURLに、バージョン番号(本例だと3.3.7-1
)を含めたくないな〜と思うのではないでしょうか?
Spring MVCは、webjar-locator
というライブラリと連携することで、アプリケーションコード(JSPなど)からバージョン番号を排除する仕組みを提供しています。
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
</dependency>
<mvc:resources mapping="/webjars/**" location="/webjars/">
<mvc:resource-chain resource-cache="true"/> <!-- このタグを指定するとSpringがwebjars-locatorとの連携機能を自動で有効化してくれる -->
</mvc:resources>
warをビルドし直して再度デプロイ・起動し、バージョン番号を抜いてアクセスしてみます。
$ curl -D - http://localhost:8080/demo-webjars/webjars/bootstrap/css/bootstrap.min.css
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Track: adb21119739e422bb3faf020ff5e9e3f
Last-Modified: Sat, 17 Sep 2016 02:28:22 GMT
Accept-Ranges: bytes
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: text/css;charset=UTF-8
Content-Length: 121200
Date: Sun, 03 Sep 2017 04:53:18 GMT
/*!
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{
...
お〜アクセスできましたね。
ということで、読み込むcssファイルを指定する部分は・・・
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet"
href="${pageContext.request.contextPath}/resources/app/css/styles.css">
<link rel="stylesheet"
href="${pageContext.request.contextPath}/webjars/bootstrap/css/bootstrap.min.css"> <!-- ★ パスからバージョン番号を削除 -->
</head>
<body>
<div id="wrapper">
<h1>Hello world!</h1>
<p>The time on the server is ${serverTime}.</p>
</div>
</body>
</html>
とすればOKです。
バージョンアップ時のブラウザキャッシュ対策を行う
パスにバージョン番号を含めなくてOKということは、ライブラリのバージョンをあげてもパスが変わらないということです。
ということは、Cache-Controlの設定次第によっては、ブラウザがキャッシュしているファイル(バージョンアップ前のファイル)が使われてしまうことがあります。かといってこれらのファイルをキャッシュしないと、無駄なネットワークトラフィックやサーバ側でのリソースアクセスが発生するため、「システムへの負荷が高くなる」「画面の描画速度が遅くなる」ことが予想されます。
この問題を解決するには、
- アプリケーションコード上で指定するパスにはバージョンは含めない
- アプリケーションコードから生成するHTMLにはパスの中にバージョンを含める
ようにすればいいわけですが・・・なんと(ちゃんと)・・・Springがこの機能を提供してくれています!!
まず、Spring提供のResourceUrlEncodingFilter
をサーブレットフィルタとして登録します。
重要: サーブレットフィルタの定義順
Spring Securityのサーブレットフィルタ(springSecurityFilterChain)より後に定義してください。
<filter>
<filter-name>ResourceUrlEncodingFilter</filter-name>
<filter-class>org.springframework.web.servlet.resource.ResourceUrlEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ResourceUrlEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
でもって、JSPではパスを指定する際にJSTLまたはSpring提供のタグライブラリを使いようにします。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet"
href="${pageContext.request.contextPath}/resources/app/css/styles.css">
<link rel="stylesheet"
href="${pageContext.request.contextPath}/webjars/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet"
href="<c:url value='/webjars/bootstrap/css/bootstrap.min.css'/>"> <!-- ★ JSTLのタグライブラリを使う -->
<link rel="stylesheet"
href="<spring:url value='/webjars/bootstrap/css/bootstrap.min.css'/>"> <!-- ★ Springのタグライブラリを使う -->
</head>
<body>
<div id="wrapper">
<h1>Hello world!</h1>
<p>The time on the server is ${serverTime}.</p>
</div>
</body>
</html>
すると・・・
$ curl -D - http://localhost:8080/demo-webjars/
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Track: 5576191e3ed54e82a83b67236bc98b24
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: text/html;charset=UTF-8
Content-Language: ja-JP
Content-Length: 609
Date: Sun, 03 Sep 2017 05:28:08 GMT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet"
href="/demo-webjars/resources/app/css/styles.css">
<link rel="stylesheet"
href="/demo-webjars/webjars/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet"
href="/demo-webjars/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css">
<link rel="stylesheet"
href="/demo-webjars/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css">
</head>
<body>
<div id="wrapper">
<h1>Hello world!</h1>
<p>The time on the server is 2017/09/03 14:28:08 JST.</p>
</div>
</body>
</html>
$
JSPから生成されたHTMLには、パスの中にバージョン番号が含まれるようになりました。
ブラウザキャッシュ対策を行う
TERASOLUNA 5.xのブランクプロジェクトは、デフォルトでSpring Securityのサーブレットフィルタが提供されており、/resources/**
以外のパスへのアクセスがキャッシュされないように設定されています。つまり、/webjars/**
へのアクセスはキャッシュされないため、画面を描画するたびにCSSファイルを取得しなおすことになります。
この挙動をかえるためには、/webjars/**
へのアクセスをSpring Securityの処理対象から除外すればOKです。
<sec:http pattern="/resources/**" security="none"/>
<sec:http pattern="/webjars/**" security="none"/> <!-- ★ 追加 -->
<sec:http>
<!-- ... -->
</sec:http>
この状態でアクセスすると・・・
$ curl -D - http://localhost:8080/demo-webjars/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Track: a27dba531c8f4c79af0657ceaf5b430b
Last-Modified: Sat, 17 Sep 2016 02:28:22 GMT
Accept-Ranges: bytes
Content-Type: text/css;charset=UTF-8
Content-Length: 121200
Date: Sun, 03 Sep 2017 05:48:20 GMT
/*!
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{...
...
Spring Securityが付与していたCache-Controlのヘッダがなくなっていることが確認できます。
ChromeとSafariで確認したところ、
- アドレスバーにURLを入れた時は共にローカルキャッシュを使う
- ChromeはF5で再描画した時もサーバには問い合わせずにローカルキャッシュを使う
- SafariはF5で再描画した時はサーバに問い合わせて304が返却されてローカルキャッシュを使う
という動きになっている感じでした。(ちょっとしたらウソかもしれません・・・)
HanderInterceptorの処理対象から除外する
TERASOLUNA 5.xのブランクプロジェクトでは、以下のHandlerInterceptor
がデフォルトで適用されているので、WebJarsからリソースを取得する際に適用されないようにします。
TraceLoggingInterceptor
TransactionTokenInterceptor
CodeListInterceptor
-
OpenEntityManagerInViewInterceptor
(JPA用のブランクプロジェクトの場合のみ)
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/resources/**" />
<mvc:exclude-mapping path="/webjars/**" /> <!-- ★ 追加 -->
<mvc:exclude-mapping path="/**/*.html" />
<bean
class="org.terasoluna.gfw.web.logging.TraceLoggingInterceptor" />
</mvc:interceptor>
まとめ
WebJarsを使うと、
- Javaのライブラリと同じ方式(Maven)で依存関係+バージョンを管理できる
- Spring MVC提供の機能 +
webjars-locator
を使うことでアプリのコード(JSPやHTML)からライブラリのバージョン番号を意識する必要がなくなる
というメリットがあるので、積極的使いたいな〜と思います。