CLOVER🍀

That was when it all began.

WARファイルの中にある、MANIFEST.MFファイルの内容を読む

こちらのエントリを見て面白そうだなぁと思い、断念されていた箇所の続きをやってみました。

Mavenのpom.xmlにあるプロジェクト情報をJSFで表示する
http://kikutaro777.hatenablog.com/entry/2014/01/31/232721

てっきり、pom.xmlの中の依存関係とかの話なのかなと思いましたが、pom.xmlに書かれている自身のアーティファクトのバージョンが欲しいということらしいです。

で、続きをやるのはこの部分。

相談にきた後輩はStackOverflowにあるMETA-INFフォルダにMANIFEST.MFを吐かせてImplementation-Versionを取得する流れで試してうまくいかなかったとのこと。

ちょっと思い当たるところがあるので、やってみようかと。

先のブログの例に習い、このようにpom.xmlを定義します。

バージョン情報。

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.littlewings</groupId>
  <artifactId>war-manifest</artifactId>
  <packaging>war</packaging>
  <version>0.1.1</version>

あえて、1.0.0ではなく0.1.1とかいう中途半端な値にしておきました。

プラグインも設定し、MANIFEST.MFにバージョン情報を書き出すようにしておきます。

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.4</version>
                
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
          <archive>                   
            <manifest>
              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
          </archive>
          <archiveClasses>true</archiveClasses>
        </configuration>
      </plugin>

作成されたMANIFEST.MFは、このようになります。
META-INF/MANIFEST.MF

Manifest-Version: 1.0
Implementation-Title: war-manifest
Implementation-Version: 0.1.1
Implementation-Vendor-Id: org.littlewings
Built-By: xxxxx
Build-Jdk: 1.7.0_51
Specification-Title: war-manifest
Created-By: Apache Maven 3.1.1
Specification-Version: 0.1.1
Archiver-Version: Plexus Archiver

これを、WARファイルから読もうという試みです。

ClassLoaderから取得しようとすると、うまくいきません。今回は、ServletContextを使用しました。
src/main/java/org/littlewings/manifest/ShowManifestServlet.java

package org.littlewings.manifest;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/show")
public class ShowManifestServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        PrintWriter writer = res.getWriter();

        // MANIFEST.MFを読み出すコード
    }
}

まずは、普通に読んでみます。

        StringBuilder builder = new StringBuilder();
        try (InputStream is = getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF");
             InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
             BufferedReader reader = new BufferedReader(isr)) {
            int c;
            while ((c = reader.read()) != -1) {
                builder.append((char) c);
            }
        }

        writer.println(builder.toString());

ServletContext#getResourceAsStreamで、読み出しています。あとは、普通にReader系のクラスで中身を読みます。

続いて、境界を挟みまして。

        writer.println("===========================");

続いては、java.util.jar.Manifestを使用する例。

        try (InputStream is = getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF")) {
            Manifest manifest = new Manifest(is);
            Attributes attributes = manifest.getMainAttributes();
            // Attributes#getValueで取得
            writer.println("Implementation-Version => " +
                           attributes.getValue("Implementation-Version"));
            writer.println("Specification-Version => " +
                           attributes.getValue("Specification-Version"));

            // Attributes#getで取得その1
            writer.println("Implementation-Version => " +
                           attributes.get(Attributes.Name.IMPLEMENTATION_VERSION));
            writer.println("Specification-Version => " +
                           attributes.get(Attributes.Name.SPECIFICATION_VERSION));

            // Attributes#getで取得その2
            writer.println("Implementation-Version => " +
                           attributes.get(new Attributes.Name("Implementation-Version")));
            writer.println("Specification-Version => " +
                           attributes.get(new Attributes.Name("Specification-Version")));
        }

あとは、これをアプリケーションサーバにデプロイします。自分は、JBoss AS 7.1.1にデプロイしました。

今回、WARファイルの名前は「javaee6-web.war」としましたので、以下のURLにアクセスすると

$ curl http://localhost:8080/javaee6-web/show

このような結果になります。

Manifest-Version: 1.0
Implementation-Title: war-manifest
Implementation-Version: 0.1.1
Implementation-Vendor-Id: org.littlewings
Built-By: xxxxx
Build-Jdk: 1.7.0_51
Specification-Title: war-manifest
Created-By: Apache Maven 3.1.1
Specification-Version: 0.1.1
Archiver-Version: Plexus Archiver


===========================
Implementation-Version => 0.1.1
Specification-Version => 0.1.1
Implementation-Version => 0.1.1
Specification-Version => 0.1.1
Implementation-Version => 0.1.1
Specification-Version => 0.1.1

最初が、普通にReaderで読んだもの、次がManifestクラスを使用したものですね。

こういうのを使うのはあまり機会がないので、たまにはよいでしょう。