tokuhirom's blog

servlet 3.0 の multipart 関連処理を有効にする方法

Java の Servlet API では 3.0 以後、multipart 関連の処理が可能だが、デフォルトでは利用不可となっている。

これを有効にするには web.xml に以下のように記述すればよろしい。

最新の avans ではデフォルトで有効になっている。

<servlet>
    <servlet-name>main</servlet-name>
    <servlet-class>me.geso.avans.AvansServlet</servlet-class>
    <init-param>
        <param-name>package</param-name>
        <param-value>me.geso.sample.controller</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <multipart-config>
        <max-file-size>5242880</max-file-size>
        <max-request-size>27262976</max-request-size>
        <file-size-threshold>32768</file-size-threshold>
    </multipart-config>
</servlet>
Created: 2015-03-03 15:56:06
Updated: 2015-03-03 15:56:06

JMX にアクセスして値を取得したい

Java で何かを実装したい場合、Tomcat ちゃんやらなんやらの remote のプロセスからデータを取得したいという欲求を覚えることでしょう。

そういう時の取得方法についてサンプルプログラムを以下に貼ります。

以下のような感じで書くと、オブジェクトのリストを得ることができます。 remote.queryMBeans(null, null) と呼ぶとオブジェクトリストがとれるんですね。このへんはググれば出てきます。

import javax.management.remote.{JMXConnectorFactory, JMXServiceURL}

import scala.collection.JavaConverters._

object JMXListItems {
    def main(argv: Array[String]) = {
        if (argv.length != 1) {
            println("Options: jmx_url")
            System.exit(0)
        }

        val url = argv(0)
        val target = new JMXServiceURL(url)
        val connector = JMXConnectorFactory.connect(target)
        val remote = connector.getMBeanServerConnection
        val objectNames = remote.queryMBeans(null, null)
        objectNames.asScala foreach { it =>
            printf("%s %s\n", it.getClassName, it.getObjectName.getCanonicalName)
        }
    }
}

ほんで、実際の値の取得はこんな感じです。 MBeanServerConnection#getAttributes() というメソッドがあるのでこれを利用すればよいように見えるが、一部の MBean は Serialize 不能な値を返してくる……。そういう場合には getAttributes メソッドが例外を上げてくるので、まったく値を取れない。 そこで、現実的には一個一個例外をキャッチしつつ getAttribute するしかないのかな、と思っている。 が、そんなバカバカしい状態が Java SE でずっと放置されているわけもないと思うので、実はもっとちゃんとした方法があるのかもしれない!

import javax.management.ObjectName
import javax.management.remote.{JMXConnectorFactory, JMXServiceURL}

object JMXGetAttributes {
    def main(argv: Array[String]): Unit = {
        if (argv.length != 2) {
            println("Options: jmx_url object_canonical_name")
            System.exit(0)
        }

        val url = argv(0)
        val objectCanonicalName = argv(1)
        val target = new JMXServiceURL(url)
        val connector = JMXConnectorFactory.connect(target)
        try {
            val remote = connector.getMBeanServerConnection
            val name = ObjectName.getInstance(objectCanonicalName)
            remote.getMBeanInfo(name).getAttributes foreach { attribute =>
                val value = try {
                    remote.getAttribute(name, attribute.getName).toString
                } catch {
                    case e:Exception => "Unavailable"
                }
                printf("%s %s\n", attribute.getName, value)
            }
        } finally {
            connector.close()
        }
    }
}

ちなみに MBeanServerConnection は内部でスレッドを上げてくるので、ちゃんと close しないとすぐに Out Of Memory になるので注意が必要。

あと、サンプルコードは scala です。

Created: 2015-02-26 14:56:37
Updated: 2015-02-26 14:56:37

Content-Security-Policy と nonce の話

Content-Security-Policy の nonce を利用すると、XSS の脅威をかなり軽減できます。

そこで、Web Application Framework ではデフォルトで対応したほうがよいのではないか、という旨を @hasegawayosuke さんから教えて頂いたので、実装について考えてみました。

とりあえず CSP の nonce はどういうものなのかを考慮するために、コード例を探していたのですが、実際に動くサンプルというものが nonce 関連のもので見当たりませんでした。

そこで、実際に動くサンプルを用意しました。

https://github.com/tokuhirom/csp-nonce-sample

以下は Sinatra で書かれたサンプルコードです。

require 'sinatra'
require 'securerandom'

get '/' do
    @nonce = SecureRandom.base64()
    headers 'Content-Security-Policy' => "script-src 'nonce-#{@nonce}' 'unsafe-inline'"
    erb :index
end

テンプレートファイルは以下のようになります。

<!doctype html>
<html>
    <body>
        <h1>CSP の nonce のサンプルです。</h1>

        <script type="text/javascript" nonce="<%= @nonce %>">
            alert("これは信用されてる");
        </script>
        <script type="text/javascript">
            alert("こんにちは!こんにちは!");
        </script>
        ↑これは、攻撃者が送り込んだメッセージだと思ってください。これは実際にはサポートされているブラウザでは実行されません。
    </body>
</html>

"Content-Security-Policy: unsafe-inline; script-src 'nonce-${nonce}'" 形式で書いたら、${nonce} の部分を script タグの属性にいれないと実行されなくなります。

nonce の値は、リクエストごとにランダムな文字列が生成されるので、クライアント側からは想像もつかず、攻撃することが困難になります。

これはもう、どんどん nonce 使って行った方がいいのでしょうかね!

Web application framework でのサポートについて

Web Application Framework でこの機能をがっつりサポートするのはどうなのでしょうか?

現実的には HTML をパースして nonce を埋め込むアプローチも考えられますが、それでは攻撃者が送ってきた script タグに nonce をつけてしまう可能性もあって効果が薄れます。

理想的には、やはり手作業で一つ一つ nonce 値を埋めていって上げるのがよいでしょう。

Web Application Framework では csp header の送出とテンプレートエンジンへの nonce 値の受け渡しだけを担当し、HTML の書き換えはユーザーに任せたほうがよいように思います。

(追記) unsafe-inline をつけよう

ということなので、unsafe-inline もつけたほうがよいようです。

(追記) nonce つけても XSS される場合。

<script nonce="...">document.write(location.hashref)</script>

のような場合、ダメとのこと。

Created: 2015-02-24 15:08:33
Updated: 2015-02-24 15:08:33

avans での Freemarker の取り扱いを変えた。

既存コードには関係ない話ですが、今朝リリースした 1.5.3 のスケルトンジェネレータでの変更についてです。

avans ではいままで avans-freemarker を利用していましたが、これが将来的に Deprecate されて、新しい方法が採用されます。

今までのスケルトンでは Freemarker 関連のコードがゴチャゴチャしがちでしたが、これらを src/main/java/${pkg}/view/ 以下に配置するように変更。view 関連のコードをこのあたりにまとめるようになっています。

  • me.geso.sample.view.FreemarkerView
  • me.geso.sample.view.FreemarkerViewFactory
  • me.geso.sample.view.Helper
  • me.geso.sample.view.HtmlTemplateLoader

のようなクラスが生成されるようになっています。

この変更により、以下のようにすんなりとコントローラコードを記述することが可能になっています。またビュー関連のコードが一箇所にまとまって管理しやすくあんっております。

public class RootController extends BaseController {
    @GET("/")
    public WebResponse index() throws IOException, TemplateException {
        return this.freemarker("index.html.ftl")
                .param("name", "John<>")
                .render();
    }
}

以前のバージョンでは ImmutableMap を使ったりしていてダルい感じだったけど、イカス感じになった!!

また、Helper クラスが最初から用意されるようになっています。これは Amon2 で言うところの ViewFunctions にあたるものです。 これを定義しておけば、テンプレートから呼び出したい関数を好き勝手に呼べるようになっています。便利。

まあそういう感じですのでよろしくお願いします。

Created: 2015-02-24 12:20:40
Updated: 2015-02-24 12:20:40

Embedded Tomcat の起動が遅いんですけど~ って人

最近は Embedded Tomcat をテスト時に利用しているが、こちら極めて起動が遅い。

と思って調べていたところ、 metadata-complete="true" を設定すればいいとの情報を発見。

公式の wiki にめっちゃ書いてあった。以下のように metadata-complete 属性を web-app タグに記述しつつ、absolute-ordering タグを入れると、起動時のスキャンがとまって速くなる。

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    metadata-complete="true">
    <absolute-ordering />
    <!-- なにか書く -->
</web-app>

インターネッツを見ていると、これは Java をちゃんとやってる人たちの間では常識的なことらしいけど、えてしてこういう情報って本とかにはのっていないものですね。

http://wiki.apache.org/tomcat/HowTo/FasterStartUp

Created: 2015-02-19 17:48:55
Updated: 2015-02-19 17:48:55

[java] web.xml から tomcat-embed をランダムポートで起動し、起動したポートを得る方法、または Java webapp のテスト手法について

一般的に web application のテストをするには application server を別スレッドで起動してそこにアクセスする方法と、http request を mocking して application code を直接呼ぶ方法があります。前者の方法は、app server の挙動を完全に再現できるし、http client も使い慣れたものを利用できるというメリットがあります。後者の場合には実行速度が速いというメリットがあります。

で、いろいろなメリット・デメリットを考慮した結果、今回は前者の方法でいこうとおもったわけです。

ここでは、maven 構成な一般的な webapp を tomcat で運用する場合のテストを前提とします。

とりあえず、pom.xml に以下のように記述しましょう。tomcat は 7 を使う無難さよ。

    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>7.0.56</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>7.0.56</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jasper</artifactId>
        <version>7.0.56</version>
        <scope>test</scope>
    </dependency>

以下のように、tomcat-embed を起動すればいいだけです。めっちゃ簡単ですね。

Tomcat tomcat = new Tomcat();
tomcat.setPort(0);
org.apache.catalina.Context webContext = tomcat.addWebapp("/", new File("src/main/webapp").getAbsolutePath());
webContext.getServletContext().setAttribute(Globals.ALT_DD_ATTR, "src/main/webapp/WEB-INF/web.xml");
webContext.setJarScanner(new JarScanner() {
    @Override
    public void scan(JarScanType scanType, ServletContext context,
            JarScannerCallback callback) {
        // do nothing.
    }

    @Override
    public JarScanFilter getJarScanFilter() {
        return null;
    }

    @Override
    public void setJarScanFilter(final JarScanFilter jarScanFilter) {
        // do nothing.
    }
});
tomcat.start();

int port = tomcat.getConnector().getLocalPort();
String url = "http://127.0.0.1:" + port;
// url をつかってなにか処理する

めっちゃ簡単ですね!! Embedded tomcat 遅いと思うかもしれないですが、意外と起動はやいです。

出来てみると簡単ですが、意外とこういうことしてるコード見当たらなかったんで、書いてみた次第。

もっといいテスト方法あるよ! なんて方いたら教えてくだされ~

Created: 2015-02-19 16:28:13
Updated: 2015-02-19 16:28:13

jdbcutils と tinyorm に問題が見つかったのでメジャーバージョンアップした

jdbcutils 2.0.0 と tinyorm 1.0.0 を出しました。

ざっくり言うと、getColumnLabel() を呼ぶべきところで getColumnName() を呼んでいたので、正しい方に変えた、という話。

getColumnLabel()getColumnLabel() の違いというのはつまり以下のような状況です。x がカラム名で y がカラムラベル。

    SELECT x AS y FROM z;
           ^                    x: getColumnName()
                ^               y: getColumnLabel()

よって、AS を使うなりなんなりして別名でラベルを貼っていない限りは関係ないです。社内のコードを確認した範囲だと影響あるコードはなさそうでした。

変更をいれた箇所は二箇所で

* JDBCUtils#executeQueryMapList()
* TinyORM#searchBySQL, TinyORM#searchBySQLWithPager

です。

searchBySQL で AS を元の名前から別の名前つけてるケースで問題が起きそうに見えますが、実際には AS で別名をつけててもその名前で取り出せていなかったので、バグっていたコードがたまたま動いていたのが動かなくなるというケースが考えられる程度です。

つまり以下のようなコードは今までうっかり動いていたけど、動かなくなるよ、と(正しくは x.getExtraColumn("y"))。

    List<X> xs = orm.searchBySQL(X.class, "SELECT x AS y FROM z");
    for (X x:xs) {
        System.out.println(x.getExtraColumn("x"));
    }

ただ、この挙動になったら普通は「あれれ~おかしいぞ~」となって気づくはずなので、発生していないはずです。 (というか実際自分はそのケースになって気づいた)

ref.

Created: 2015-02-18 15:42:32
Updated: 2015-02-18 15:42:32

jdbcutils 1.1.0 を出した

jdbcutils の 1.1.0 が出ました。jdbcutils は JDBC を便利に使うためのユーティリティ集です。

今回追加になったのは JDBCUtils#executeQueryForBean() メソッドです。 以下のような形式で、Bean にマッピングできます。

List<Bean> beans = JDBCUtils.executeQueryForBean(connection,
                "SELECT * FROM bean ORDER BY id", Collections.emptyList(),
                Bean.class);

HashMap なりなんなりにマッピングすればいいという話ではありますが、Bean にマッピングすると IntelliJ IDEA で Freemarker を書いている時にメソッド名の補完が効くようになり、開発効率が向上するので Bean にマッピングしたほうがよい気がしています。 (Bean を定義する手間と Freemarker の方をコード見ないでサクサク書けるようになるメリットとのトレードオフで、サクサク書けることのメリットのほうが大きいと見ています)


ちなみに TinyORM は Teng と同様に extra column を扱えるので、一覧画面に他のテーブルの情報をちょっと混ぜ込みたい程度ならその機能を使ってもいいのであった。

つまり、以下のようなことができる。

List<Entry> entries = orm.searchBySQL(Entry.class, "SELECT blog.title blogTitle, entry.title title FROM entry INNER JOIN blog ON (blog.id=entry.blogId)", Collections.emptyList());
System.out.println(
    entries.map(e -> e.getExtraColumn('blogTitle') + " : " + entry.getTitle())
        .collect(Collectors.joining(", ")));

どれかのテーブルが主体になって一カラムだけ他のテーブルからJOINしたものを出したいなんてときにはこちらが便利。

Created: 2015-02-17 06:32:04
Updated: 2015-02-17 06:32:04

vue.js の Object に対する v-repeat って使いにくいのでは?

ざっくり言うと: 具体的には jolokia の返す API response を元に vue.js でテンプレート書くの辛い

vue.js の 0.11.0 を使ってて思ったことのメモ。

vue.js の v-repeat って、たとえば以下のように書けるけど、$key なり $value になるのでちょっと使いにくい気がした。

<ul id="repeat-object">
    <li v-repeat="primitiveValues">{{$key}} : {{$value}}</li>
    <li>===</li>
    <li v-repeat="objectValues">{{$key}} : {{msg}}</li>
</ul>

angular.js の ng-repeat だと以下のように書けるので、Hash がネストした値を返してくる API でもそこそこ読みやすいテンプレートを記述しやすい。。

ng-repeat="(key, value) in expression"
Created: 2015-02-16 11:33:33
Updated: 2015-02-16 11:33:33

apimock 0.2.0 を出した

https://github.com/tokuhirom/apimock/

外部の API を呼ぶときにそのモックをつくるやつの 0.2.0 を出した。

外部の API を呼ぶようなコードを、呼び出し部分のクラスをモッキングしてもいいのだが、それよりも API サーバーそのものをモッキングしたほうが、より広い範囲のテストを同時に行うことができて良い。

というわけで、最近は Embedded Tomcat を使って実際にサーバーをたちあげてテストしている。 想像されるほど遅くはないのでよい。

基本的な使い方は以下のようになっており、mock#get と mock#post が用意されていて、ここにエンドポイントを登録してモッキングする。webscrew のレスポンス型以外のオブジェクトを返すと Jackson で適当に JSON にしてくれるので便利だ。

try (APIMockTomcat mock = new APIMockTomcat()) {
    mock.get("/", c -> Collections.emptyMap());
    mock.start();
    URI uri = mock.getURI();

    // Your test code here.
    HttpResponse resp = Request.Get(uri.resolve("/"))
            .execute()
            .returnResponse();
    assertEquals(200, resp.getStatusLine().getStatusCode());
    assertEquals("{}", EntityUtils.toString(resp.getEntity()));
}

で、便利に使っていたのだが、今までは APIMockContext#readJSON のようなメソッドがなくて、出力はいいけど入力が辛い、ということになっていたので直した。

mock.get("/", c -> {
    V v = c.readJSON(V.class);
    return ImmutableMap.builder()
        .put("x", v.getX()*2)
        .build();
});
mock.start();

まあ、それだけですが。

最近の Web 開発は、外部 API との通信を行うケースが増えているので、こういうライブラリの需要が高まっていると思う今日このごろです(Perl でやってると fork しなきゃいけないのでちょっと管理が面倒だったけど Java だと thread なので楽でいい)。

ちなみに、実際にはこういうふうなのを毎回書くわけではなくて以下の様なヘルパメソッドを用意している。

protected void mockTwitter(Consumer<APIMockTomcat> server, Consumer<Mech2WithURI> client) {
    try (APIMockTomcat mock = new APIMockTomcat()) {
        server.accept(mock);
        mock.start();
        URI uri = mock.getURI();

        Mech2WithURI mech2 = new Mech2WithURI(new Mech2(), uri);
        client.accept(mech2);
    }
}
Created: 2015-02-15 10:07:11
Updated: 2015-02-15 10:07:11