Category Archives: Java

5年間Mayaaを使って思ったこと

関連記事はこちら:そろそろ2年間Mayaa使ってわかったことを書く

上記記事を書いたのが2011年ですので、あれから3年。まだMayaa使ってます(笑)

一つのプロダクトをずっとメンテし続けられるって、ビジネス的に成功した証拠ですから素晴らしいです!というわけで、10年の半分Mayaaを使い続けて思ったことを書いてみようと思います。

Mayaaは長期メンテに耐えるライブラリか

もちろんYesです。もう作られてから10年近い老舗プロダクトで、Javaは1.4準拠だったりしますが、それを加味しても十分現役で使えます。それは、私の勤務先でもずっと使い続けられていることが証拠です。

教育コスト・生産性は

当初とっつきにくい、教育コストがかかると思いましたが、大して問題になりませんでした。既に10人以上の開発者がMayaaを使った開発をしていますが教育コストが高いと思ったことはありません。

この辺りは、「2年間~」のときに書いたように開発ルールを定めたことが効いたのだと思います。はじめにビビってこれらのドキュメントを整えたことがかえって組織には良かったのかもしれません。

コードとデザインが分離するのでフロントのコードがスパゲッティにならないことも良かったです。最初のめんどくささは確実に後から利益となって返済されています。

デザイナーとの相性

あれから、想像した以上にデザイナーと開発者のすれ違いというものを多く目撃してきました。しかしそれはそもそも話せば分かる問題だったり、Javaプログラマーは業務システムを多く経験してきていてフロントに疎いことが問題だったりするためであり、むしろ、Mayaaを使うことで、一緒のプロダクトを同時開発することで発言した必然的なことであるような気がします。

多くの組織では、デザイナーと開発者の線を明確に切るようです。うちのように両者が入り乱れて開発できる現場というのはそんなに多くないでしょう。それができることは、お互いに、そしてプロダクトにとっても、ユーザーにとっても良いことだと思います。

パフォーマンス

今私が全力で取り組んでいる課題です。インフラがクラウド化してパフォーマンスとコストが直結したこと、Webの利用が広がりユーザトラフィックに対してサーバの余裕はもはやないこと、そしてWebブラウザの性能が向上したこともあり、低コストで、高パフォーマンスを実現することがWebアプリケーションに求められています。

そんな中で、Mayaaははっきり言ってパフォーマンスが良くありません。

Mayaaはかなり頑張っています。まず重要なのがキャッシュです。SpecificationCacheというオブジェクトに、ページのツリーデータがキャッシュされていきます。このため、普段のレンダリングは十分に高速です。ただし、キャッシュを行った結果は次の2つの問題点が発生します。

  • キャッシュ自体のメモリのオーバーヘッド
  • 更新直後などのキャッシュがない状態のパフォーマンス

キャッシュ自体のメモリオーバーヘッドは大きいです、たった数10KB程度のテンプレートファイルが1MBを超えたりします。特にJava6の古いマイナーバージョンや、Java5以前を使っているなら、即Java7以降に移行するべきです。Java6u14以降には圧縮ポインタという機能が搭載されています。詳しくはこちらに書きました。→64bit環境でなんかheapを多く消費するなあと思ったら

それでも、メモリを食うことは変わらないので、なるべく大きめにヒープを割り当てるようにしてください。トラフィックやサイトの種類にも依存しますが目安としては全テンプレートファイルの10から50倍程度用意するべきだと思います。

キャッシュがないときの問題も大きいです。そこで、Mayaaは可能な限り、キャッシュがない状態を防ごうとしています。ヒープがいっぱいになってキャッシュがGCによって消えてしまった場合や再起動した時も、ハードディスク上にserializeしておき、そこから復元することでXML解析の時間を短縮しようとする機能があります。この機能はデフォルトでOFFになっていますが、ONにしても問題が発生することはないので、プロジェクトの初期段階からONにすることをおすすめします。

キャッシュがない状態でのレンダリングで最も時間を食っている原因は、Rhinoスクリプトのコンパイル・最適化です。Rhinoスクリプトをそのまま実行すると遅いので、一旦Javaクラスとしてコンパイルするとう最適化を行っています。そして、Javaクラスは繰り返し実行するにつれてJITコンパイルされ次第にネイティブコードに近くなっていきます。なので、数回同じページを表示すると、JSPやServletで画面を構築した場合に劣らないくらい高速になります。VMを再起動したり、テンプレートを更新するとこれらキャッシュは消えてしまいます。コンパイルされたRhinoスクリプトはtransientなので、シリアライズ・デシリアライズされると最適化がはじめからやり直しです。この件については、今後Mayaa自体が効率化することを望みます。

究極のパフォーマンス改善は、そもそもRhinoScriptを使用しないことです。RhinoScriptを使用しないためには、独自のProcessorを作成する必要があります。Mayaaのソースを読んでいくことで、TextCompiledScriptが使用されている箇所を軒並み潰して、Reflectionなどを使用して直接Javaコードを呼び出すようにすることで、ページの処理速度が最大5倍程度速く出来ました。

拡張性

かなり良いです。5年間で本体をforkし独自のコードを挿入する必要があったことはありません。

Processor, InjectionResolver, Builderなどを独自化することで全く別のテンプレートエンジンに仕上げてしまうこともできます。

ただし、一方で、拡張するには非常に多くのコードを書かなければいけなかったり、キャッシュやMayaaツリーの解析など、あらゆる機構が独自で実装されているため、ソースコードのかなりの部分を精読しなければなりません。なんでもできるけど、その難易度は高いといったところです。

XPathMatchesInjectionResolverは無効にするべき

Mayaa User Mailing listに私が報告したとおり、こいつがあるとパフォーマンスが低下します。またメンテ性が悪いのではじめから使うべきではありません。

org.seasar.mayaa.provider.ServiceProvider
を編集し、下記のようにコメントアウトしてしまいましょう。

<!-- <resolver class="org.seasar.mayaa.impl.builder.injection.XPathMatchesInjectionResolver"/> -->

動的なページの構築、MayaaはSPAよりPJAXと相性が良い

最近はSPAといって静的なHTMLページ一枚でWebアプリを構成し、JavaScriptによる非同期リクエストにサーバはJSONを返すことで画面を構築していくというアプローチが普及しつつあります。AnqularJSなどがそのページの作り方をサポートするライブラリとして有力です。

この構成でサイトを作る場合は、そもそもMayaaの出る番がありません。しかし、もし作っているものが、Webサイトの延長で、全ての画面の状態にはURLがあるという状況においてはSPAにせず、Mayaaを使ってPJAXを実装するとスムーズです。

つまり、Ajaxに対してレスポンスするのはJSONではなく、普通のHTMLページであり、jQueryを使うなどしてここから必要な断片を画面に上書きするというアプローチです。Push Stateを使用してURLも書き換えてしまえばSEOもバッチリです!このURLが静的コンテンツとしてMayaaにリクエストした時のページのURLでもあり、静的ページと動的ページの両方をMayaaによって実装することができます。

2014年以降もMayaaは最強か?

これについては、なんとも言えないでしょう。
拡張しやすさを求めるならMixer2、生産性を求めるならThymeleafもあります。パフォーマンスを求めるならJSPで割り切るのも手です。

デフォルト状態でのデザイナーとの相性が非常に良いので、CMSのような物を作りたい場合は、Mayaaは未だにかなり良い選択肢です。この領域にはWordPressというそもそものデファクトスタンダードがあるので、わざわざJavaで作るという機会はあまりないかもしれません。でも、もしそのタイミングが訪れたなら、WordPressのテーマやプラグインよりもずっと良い基盤です。

パフォーマンスの問題がシビアな状況ではあまり適切とはいえません。あらゆる汎用エンジンは何かしらパフォーマンスとトレードオフの関係にあります。いっそ、独自でテンプレートエンジンを作るのも手だと思います。

Mayaaの柔軟性を取り入れつつ、パフォーマンスも良くしたい場合、私が業務で取り組んでいるRhinoを使わないしくみがもし汎用化できて、公開することができれば最高ですね。それは私の業務的にはプライオリティが高いことなので、案外実現してしまう可能性もあります。が、まだ約束できることではないことので、現時点ではまだ考慮にいれられないことが残念です。

WordPressに持ってきてしまった、はてなキーワードリンクを解除しました(XMLRPC + wordpress-javaを使用)

前回 はてなダイアリーから持ってきたはてなダイアリーの記事がはてなキーワードリンクを含んでしまっているので、解除するためにスクリプト組むと宣言していました。
今日そのためのスクリプトを書いてリンク解除に成功したので報告します。

変換方法

WordPressのXMLRPC機能を使ってAPI経由でデータ変換をしました。

イメージとしては一つ一つのページを手で開いて、エディタに記事を貼り付けて正規表現置換して保存を繰り返すのと同じです。

この方法のメリットは

  • インターネットに繋がっていればどの端末からも実行できる
  • 履歴データまでは置換しない(WordPressは記事を変更した履歴を保持しています)
  • 変更履歴が残る(その気になればいつでも変換前に戻せる)

デメリットは

  • 前記事分の通信が発生する
  • APIの仕様を調べなければならない
  • APIを有効にしなければならない

当初SQL一本流して変換しようとしましたがMySQLは正規表現で文字列置換をすることができないらしく、それならPHPでスクリプトを組もうとも思ったのですが、DB直接操作するより外部から触ったほうが安全だと考え、API方式を選択しました。

WordPressのAPIはXMLRPC方式と、AtomPub方式の二種類提供されていますが、後者はデフォルトで無効になっていたり、日本語の情報が皆無に近く、XMLRPC方式の場合は、もう少し状況がマシなようで、 wordpress-java というJavaクライアントライブラリもありましたので、今回はこちらを使用しました。

変換プログラム全文

public class App {
    static String user = "******";
    static String pass = "****************";
    static String url = "*************/xmlrpc.php";

    public static void main(String[] args) throws MalformedURLException, XmlRpcFault {
        WordPress wp = new WordPress(user, pass, url);
        List<Page> recentPosts = wp.getRecentPosts(1000);
        Pattern pattern = Pattern.compile("<a class=\"keyword\" href=\"[^\"]+\">([^<]+)</a>");
        for (Page page : recentPosts) {
            String contents = page.getDescription();
            if (contents.contains("keyword")) {
                Matcher m = pattern.matcher(page.getDescription());
                System.out.println(page.getPermaLink());
                while (m.find()) {
                    System.out.println("\t\t" + m.group(1) + "\t" + m.group());
                    contents = contents.replace(m.group(), m.group(1));
                }
                page.setDescription(contents);
                System.out.println("\tsaved status = " + page.getPost_status());
                wp.editPost(page.getPostid(), page, page.getPost_status());
            }
        }
    }
}

実行結果はこんなかんじです。

http://www.susumuis.info/entry/2014/03/31/103829
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        API <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>
        hoge    <a class="keyword" href="http://d.hatena.ne.jp/keyword/hoge">hoge</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        Java5   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Java5">Java5</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        GC  <a class="keyword" href="http://d.hatena.ne.jp/keyword/GC">GC</a>
    saved status = publish
http://www.susumuis.info/entry/2014/03/30/004202
        Mayaa   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Mayaa">Mayaa</a>
続く...

キーワードの変遷を見ると当時どんなことに注目していたのか見られて面白いです。

実行後、変換された記事のリビジョンが1増加しています。差分を表示すると、たしかにキーワードの箇所のみ置換されています。

キーワードだけ置換されている

プログラム解説

解説するまでもない短いプログラムですが、正規表現によって検索をし、置換のところは正規表現を使わずに完全一致で置換しています。こうすることで、どこが変わったのかログに出力することができます。

contents = contents.replace(m.group(), m.group(1));

の部分は記事本文をまるごと置換しているのでメモリ効率の点で言えば有利ではありません。が、以前業務で作った変換ツールはこの方式でトータル数十万行のファイル群を平気で置換できてしまっていたのでこの程度気にするほどではないでしょう。ただし、件数によっては時間がかかるかもしれません。

wp.editPost(page.getPostid(), page, page.getPost_status());

の部分は第三パラメータがJavaDocで

String pubulish : publish status

となっていて、それ以上の説明がありません。WordPressのAPIの仕様書を見てもよくわからなかったので、当てずっぽうでprivateまたはpublicを返すpost.getPost_status()にしたら、うまくいきました。

newPostの場合はbooleanなので、この辺り統一性がないなと思いました。

キーワードリンクの是非について

自分がはてなブログ内にいたら、キーワード被リンクでSEO効果もあったかもしれませんが、はてな外にいますからその恩恵はありません。じゃあ、はてなブログを使用しているならどうかといえば、最近の検索エンジンは外部リンクが多すぎることに対して厳しくなってきていますから、その効果も今はどれほどなのかとも思います。

ユーザーとしてページを見ると、リンクが多すぎるとうるさい感じがしますし、キーワードリンクをクリックして便利だと思ったことはあまりありません。分からない語句があったらはてな内を探すより「ぐぐる」のが自然だと思います。

今後はてなキーワードが終了したり、使用が変わったりすると、外部のブログからのキーワードリンクは全部リンク切れになってしまいますから、引っ越してきたサイトでキーワードリンクが残ってしまっている状況は百害あって一利なしと思います。

実行にJavaが必要ではありますが、同じ状況の方がいらっしゃったら使っていただいて構いません。ただし、事前にバックアップを取るなど自己責任にてお願いします。MySQLのバックアップは

mysqldump -u ユーザー名 -p DB名 > ファイル名.dump

にて取れます (^^;

Java8をインストールしたMacでもさくらVPSのVNCは使えましたという報告

今日のお題:

さくらVPSのVNC機能を使う場合はJava6を入れなきゃいけないという誤解→ウソ→Java8でも動いたよ!

筆者環境

MacBook Air (11-inch, Late 2010)
OSX 10.9.2 Maverics
Safari 7.0.3
java version "1.8.0"

経緯:

急にサーバー立てたくなりまして。世間ではDockerとか流行ってるんですが、ここ数年サーバーとは離れてしまってCUIコマンドに対しても自信がないLinux弱者と化しているので、まあ、ライト路線でさくらVPSでも契約して勉強し直そうかと思いたちますした。

東京リージョン、メモリ1GB、HDD 100GB、2コアという、最小構成で契約をし、さて、使うかと思ったら初期OSがCentOS、別になんでもいいんだけど、学生の頃Debian Etchと親しんだのでapt系がいいなと思い、さくっとOS再インストールボタンをポチ。

ブラウザーからVNCが立ち上がって簡単OSインストールが出来るのがVPSの売りでしたよね。って、あれ?VNCウィンドウが立ち上がりません。

Java6を入れなきゃいけないという誤解

おかしいなと思って、調べてみるとこんな記事が。。。

MacでさくらVPSのVNC - 一茶庵

さくらVPSのVNCコンソールはJava 7 で使用できない
Java 6にダウングレードするとVNCコンソールが動く

そうなのか?それを裏付ける情報続々

さくらVPSのVNCコンソールを mac maverics から起動する | 新なんでもありBLOG

Java を全てアンインストールし、Apple 社の Java-6 をインストールし、JavaAppletプラグインのシンボリック・リンクを張り直して、何とか maverics(10.9.1) からさくらVPSのVNCコンソールが動くようになった。

えーーー。

僕はJavaエンジニアだから、複数のバージョンのJDKをインストールすることは互換性検証のためにも構いませんが、自分がアプリを使う実行環境としては、最新のものでないとちょっと怖いです。

しぶしぶ Apple Developer Center から "Java for OS X 2013-005 Developer Package" をダウンロード、インストール。シンボリックリンク張り直しそしてブラウザとの関連付けをしようとしましたが、やっぱり怖いよう。ウィルスとか入るかもしれない。

Java7でも大丈夫だよという情報

そう思ってもう一度ググり直してみました。

ひとりぶろぐ » さくらVPSのVNCコンソールをJava 7で起動させる

危険を冒してJava 6にダウングレードしなければならない
的な認識で対処方法を書いているサイトも見かけましたが、それは誤解。ダウングレードの必要はありません。

そうでしょ!やっぱそうでしょ!

試しにリンク先の方法で試してみます。と言っても、簡単で、「システム環境設定」から「Java」を選んで、「Javaコントロールパネル」が開いたら「セキュリティ」のタブをクリックし、「例外サイト・リスト」欄の「サイトリスト編集」をクリック、「追加」をクリックして「https://secure.sakura.ad.jp/」を追加。

以上で、特にマシンの再起動とかもせず、ブラウザも再起動せず、別ウィンドウにVNCウィンドウが立ち上がりました。

めでたしめでたし。

OSインストールは後日

今日は遅くなっちゃったんで肝心のOSインストールは後日にします。
DebianとUbuntuどっちにしようかと見てみるとUbuntu 14.04がそろそろ出そうなので、ちょっとだけ待ってみますかね。*1

*1:毎回サーバー立てようとすると、Ubuntu ??.04 がもうすぐ出そうってなってる気がする。春は鯖立てしたくなる季節です