CLOVER🍀

That was when it all began.

Apache Veloticy Tools 3.0がリリースされたよという話

去年、こんなエントリを書きました。

Apache Velocity 2.0がリリースされたよという話 - CLOVER🍀

去年の8月にApache Velocity(正確にはApache Velocity Engine)の2.0がリリースされた、という話でした。

ただ、その時はApache Veloticy Toolsはまだリリースされていませんでした。

それが、ようやく最近になってリリースされました。リリース日は、2018年10月9日。

Velocity Tools 3.0 released

http://velocity.apache.org/tools/3.0/

リリースノートは、こちら。

Velocity Tools 3.0 Release Notes

今回も、せっかくなのでちょっと見ていきたいと思います。

お断り

去年のエントリの時にも書いたのですが、同じようなことをまた書きます。

世間的には、Apache Velocityはすでに役目を終えた印象のテンプレートエンジンかと思います。Spring Frameworkからの
サポート対象外、Apache Velocity (Engine) 2.0がリリースされてから、Viewなどで使うのに必要となるApache Velocity Toolsは
未リリースという状態でしたが、それがリリースされるまで1年かかっていますしね。

そんなわけで去年のエントリからの繰り返しになりますが、ここから先は、それでもApache Velocityに興味のある方だけ
ご覧になればよろしいかなと思います。

Apache Velocity Tools 3.0

Apache Velocity Tools 3.0での変更内容や

http://velocity.apache.org/tools/3.0/

Velocity Tools 3.0 Release Notes

アップグレードガイドは、こちら。

Upgrading to 3.0

ざっくりまとめると

  • Velocity Engine 2.0に依存してるよ
  • CollectionToolとJsonToolが増えたよ
  • BrowserTool、ImportTool、XmlToolは書き直されたよ
  • groupIdは「org.apache.velocity.tools」になったよ
  • LinkToolは現在のリクエストパラメーターのコピーを作成するのが簡単になったよ
  • "input.encoding"をちゃんと使うようになったよ
  • JSPタグライブラリが使えるようになったよ
  • DateToolのローカライゼーションの改善
  • SortToolで任意のComparatorが指定できるようになったよ
  • テンプレートでログ出力する、LogToolを追加したよ
  • JsonToolは、GenericとView用の2つがあり、View用の方はHTTP POSTで送信されたJSONをパースできるよ

といったところです。

依存するApache Velocity Engineが2.0になったことにより、ログはSLF4Jを使って出力されるようになりました。

また、アーティファクトも分割され、

  • velocity-tools-generic
  • velocity-tools-view
  • velocity-tools-view-jsp

https://search.maven.org/search?q=g:org.apache.velocity.tools

となりました。viewが付く方はWeb用で、Servlet APIに依存しています。view-jspとなると、JSPにも依存します。
Apache Velocity Tools 2.0の頃は、まるっとひとつのアーティファクトでした。

サンプルなどは、こちらを見ると良いでしょう。

Apache Velocity Tools - Standalone Tools

https://github.com/apache/velocity-tools/tree/3.0/velocity-tools-examples/velocity-tools-examples-simple

https://github.com/apache/velocity-tools/tree/3.0/velocity-tools-examples/velocity-tools-examples-showcase

なんですが、なにか劇的に変わった感じはしませんね…。

JsonToolを試してみる

とはいえ、本当になにもしないのもなんなので、ここはひとつ、新しいVelocityToolでも試してみましょう。

ここでは、JsonTool(Genericの方)を試してみることにします(CollectionToolは文字列のsplitと、Collectionのソートが
できるくらいみたいなので)。

利用するMaven依存関係は、こちら。

        <dependency>
            <groupId>org.apache.velocity.tools</groupId>
            <artifactId>velocity-tools-generic</artifactId>
            <version>3.0</version>
        </dependency>

こんな感じのサンプルプログラムを用意。
src/main/java/org/littlewings/velocity/VelocityToolsExample.java

package org.littlewings.velocity;

import java.io.StringWriter;
import java.util.Properties;

import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.tools.ToolManager;

public class VelocityToolsExample {
    public static void main(String... args) {
        Properties properties = new Properties();
        properties.put("resource.loader", "classpath");
        properties.put("classpath.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");

        Velocity.init(properties);

        ToolManager toolManager = new ToolManager();
        toolManager.configure("tools.xml");

        jsonToolSample1(toolManager);
        jsonToolSample2(toolManager);
    }

    static void jsonToolSample1(ToolManager toolManager) {
        // あとで
    }

    static void jsonToolSample2(ToolManager toolManager) {
        // あとで
    }
}

2つ、サンプル的に用意しています。ここに、メソッド定義を埋めていきます。

設定ファイルは、このようにしました。
src/main/resources/tools.xml

<?xml version="1.0" encoding="UTF-8"?>
<tools>
    <toolbox scope="application">
        <tool key="json" class="org.apache.velocity.tools.generic.JsonTool"/>
    </toolbox>
</tools>

なお、JsonToolでできるのは、JSONデータのパース(文字列、リソース)のみで、オブジェクトをJSONに変換するような
ことはできません。

さて、どういうシーンに使うのでしょう…。リクエストデータのJSONを、そのままViewで使ったりする時でしょうか?
そんなこと、ある?

まあ、気を取り直して…まずは、こんな感じ。

    static void jsonToolSample1(ToolManager toolManager) {
        Context context = toolManager.createContext();

        StringWriter writer = new StringWriter();

        Velocity.mergeTemplate("templates/simple-json1.vm", "UTF-8", context, writer);

        System.out.println(writer.toString());
    }

テンプレート。 src/main/resources/templates/simple-json1.vm

===== json sample 1 =====

#set($jsonString = "
{
  ""string"": ""hello json"",
  ""number"": 100,
  ""array"": [1, 2, 3, 4, 5],
  ""object"": {
    ""key1"": ""value1"",
    ""key2"": ""value2"",
    ""object"": {
      ""foo"": ""bar""
    }
  }
}
")

class = $json.class.name

parse json literal = $json.parse($jsonString)

#set($parsedJson = $json.parse($jsonString))
class = $parsedJson.class.name

size = $parsedJson.size()

get string = $parsedJson.get("string")
get array index[0] = $parsedJson.get("array").get(0)

get nested object = $parsedJson.get("object")
class = $parsedJson.get("object").class.name

get direct nested value = $parsedJson.get("object.key1")

root = $parsedJson.root()
class = $parsedJson.root().class.name

JsonTool.parseで、JSON文字列をパースすることができます。

少しずつ、結果を載せていきましょう。

class = $json.class.name

parse json literal = $json.parse($jsonString)

結果。

class = org.apache.velocity.tools.generic.JsonTool

parse json literal = {number=100, string=hello json, array=[1, 2, 3, 4, 5], object={key1=value1, key2=value2, object={foo=bar}}}

JsonTool#paserした結果は、thisが返るようなのでJsonToolになります。sizeは、そのオブジェクトのトップレベル…というか
"現在の階層の"プロパティの数。各要素にアクセスするには、プロパティの場合は名前を、配列の場合はインデックスを
指定します。

#set($parsedJson = $json.parse($jsonString))
class = $parsedJson.class.name

size = $parsedJson.size()

get string = $parsedJson.get("string")
get array index[0] = $parsedJson.get("array").get(0)

結果。

class = org.apache.velocity.tools.generic.JsonTool

size = 4

get string = hello json
get array index[0] = 1

ネストしたオブジェクトは、1度取得してから操作することになります。

get nested object = $parsedJson.get("object")
class = $parsedJson.get("object").class.name

ネストしたオブジェクトを取得すると、JsonContentになります。トップレベルであっても、JsonToolの内部にはJsonContentを
保持しています。

get nested object = {key1=value1, key2=value2, object={foo=bar}}
class = org.apache.velocity.tools.generic.JsonContent

なお、JsonToolはネストしたオブジェクトのプロパティなどに直接アクセスすることはできません。

get direct nested value = $parsedJson.get("object.key1")

結果…です…。

get direct nested value = $parsedJson.get("object.key1")

JsonToolから、内部に持っているJsonContentを直接取り出すには、JsonTool#rootで。

root = $parsedJson.root()
class = $parsedJson.root().class.name

結果。

root = {number=100, string=hello json, array=[1, 2, 3, 4, 5], object={key1=value1, key2=value2, object={foo=bar}}}
class = org.apache.velocity.tools.generic.JsonContent

続いて、もうひとパターン。

        Context context = toolManager.createContext();

        context.put("jsonFile", "data.json");

        StringWriter writer = new StringWriter();

        Velocity.mergeTemplate("templates/simple-json2.vm", "UTF-8", context, writer);

        System.out.println(writer.toString());

今度は、テンプレート内でJSONファイルを読み出してみます。 src/main/resources/templates/simple-json2.vm

===== json sample 2 =====
class = $json.class.name

read & parsed json literal = $json.read($jsonFile)

#set($parsedJson = $json.read($jsonFile))
class = $parsedJson.class.name

size = $parsedJson.size()

get string = $parsedJson.get("string")
get array index[0] = $parsedJson.get("array").get(0)

get nested object = $parsedJson.get("object")
class = $parsedJson.get("object").class.name

get direct nested value = $parsedJson.get("object.key1")

root = $parsedJson.root()
class = $parsedJson.root().class.name

先ほどと異なるのは、JSONデータをStringで読むのではなく、JsonTool#readでファイルから読み出していることです。

read & parsed json literal = $json.read($jsonFile)

#set($parsedJson = $json.read($jsonFile))

src/main/resources/data.json

{
  "string": "hello json from file",
  "number": 100,
  "array": [1, 2, 3, 4, 5],
  "object": {
    "key1": "value1",
    "key2": "value2",
    "object": {
      "foo": "bar"
    }
  }
}

JsonTool#readを使うと、ファイルシステムまたはクラスパス上のファイルを読むことができます。また、似たようなものに
JsonTool#fetchがあり、こちらはURLを指定してローカルまたはリモートのファイルを読むことができます。

結果は、先ほどの例とそう変わらないので、説明は省略。

class = org.apache.velocity.tools.generic.JsonTool

read & parsed json literal = {number=100, string=hello json from file, array=[1, 2, 3, 4, 5], object={key1=value1, key2=value2, object={foo=bar}}}

class = org.apache.velocity.tools.generic.JsonTool

size = 4

get string = hello json from file
get array index[0] = 1

get nested object = {key1=value1, key2=value2, object={foo=bar}}
class = org.apache.velocity.tools.generic.JsonContent

get direct nested value = $parsedJson.get("object.key1")

root = {number=100, string=hello json from file, array=[1, 2, 3, 4, 5], object={key1=value1, key2=value2, object={foo=bar}}}
class = org.apache.velocity.tools.generic.JsonContent

こんな感じで。

まとめ

相変わらず、ひっそりとリリースされていたApache Velocity Tools 3.0を試してみました。

Apache Velocity Engine 2.0から1年、やっと出てきたApache Velocity Tools 3.0ですが…。まあ、汎用的なテンプレートエンジン
として、使いたい方にはどうぞ、と。

個人的には、割と好きなテンプレートエンジンではあるのですけどね。