PDFBoxで取得したテキストの空白置換

SlideShareがやっているように、PDFスライドから抽出したしたテキストから、改行やタブを半角スペース1つに置換を試みた際にはまったのでメモ。

取得したいのはSlideShareのページ下部に表示されているこの文字列。
f:id:n_agetsuma:20141025142450p:plain

1. PDFBoxでテキスト抽出

PDFTextStripper.getText(PDDocument doc) がIOExceptionを投げるため、IntStreamでループを回すとforEachブロック内での例外処理が強制される。シンプルにするためにfor文で書いてみる。

PDDocument doc = PDDocument.load("/Path/PDFSlide.pdf");
List<PDPage> pages = doc.getDocumentCatalog().getAllPages();
PDFTextStripper stripper = new PDFTextStripper();

for (int i = 1; i <= doc.getNumberOfPages(); i++) {         
    stripper.setStartPage(i);
    stripper.setEndPage(i);
    System.out.println(stripper.getText(doc));
}

この時点では以下のようにタブや空白、改行が混じったテキストが抽出される。

【JJUG	
  CCC	
  2014	
  Spring	
  H-­‐2】	
Javaトラブルに備えよう	
日本Javaユーザグループ 上妻 宜人 (あげつま のりと)	
はてなブログ : n-agetsuma.hatenablog.com	
2. 複数の空白やタブ・改行を半角スペースに置換

Javaで空白を置換する場合、replaceAllに¥sを設定する例が非常に多く紹介されているが、これではうまくいかないことがある。

// タブや改行を空白に置換し、2つ以上スペースが連続する場合は1つに変換
// ¥t タブ, ¥r 復帰, ¥n 改行, ¥r¥n 改行, ¥f 改ページ, 全角スペース
String text = stripper.getText(doc).trim()
                 .replaceAll("\\t|\\r|\\n|\\r\\n|\f| ", " ");
                 .replaceAll("\\s{2,}", " ");

JJUG と CCC の間の連続したスペースが消えない。

【JJUG  CCC  2014  Spring  H-­‐2】 Javaトラブルに備えよう...

調べてみると、JJUG{¥u0020}{¥u00a0}CCCとなっており、¥u0020はおなじみの半角スペースだが、¥u00a0(No-Break Space)が途中に挟まっている。

¥u00a0(No-Break Space)は、wikipediaに解説があり、HTMLでいう& nbspで、行末にスペースがある場合においてスペースと前の語の間で改行させないために使う文字。スライドの文字列には改行のコントロールが含まれているため、テキスト抽出すると普通のスペースではなく、nbspに変換される場合があるようだ。

他のスライドには同様な文字 ¥u202F (Narrow No-Break Space) も含まれていた。

対処

¥u00a0 (No-Break Space)¥u202F(Narrow No-Break Space) もスペースへの置換対象に加えてみた。

String text = stripper.getText(doc).trim()
                 .replaceAll("\\t|\\r|\\n|\\r\\n|\f| |\u00a0|\u202F", " ")
                 .replaceAll("\\s{2,}", " ");

1つの空白区切りで抽出できるようになった。

// before
【JJUG  CCC  2014  Spring  H-­‐2】 Javaトラブルに備えよう...
// after
【JJUG CCC 2014 Spring H-­‐2】 Javaトラブルに備えよう ...

JavaOne2014 4日目メモ (10/1)

サンフランシスコ市街地のスーパーに水を買いにいったら、キャベツ太郎が売ってたので思わず写真を撮る。2.49ドル、日本でいうとポッキーよりも高級なお菓子になっている。

f:id:n_agetsuma:20141006190933p:plain,w400

以下のようなセッションに参加。

Everything You Wanted to Know About Writing Async, Concurrent HTTP Apps in Java [CON3712]

非同期でかつノンブロッキングI/Oを行うHTTPクライアントをどうやって書くか紹介するセッション。

非同期およびノンブロッキングに関する振り返り

  • 非同期
    • I/O中のスレッドはブロックされるが、完了前に応答を返してくれるので別の仕事が並行でできる
    • javaではFutureが帰ってくるような実装で実現
  • ノンブロッキング
    • I/O中にスレッドをブロックしない。selectorに登録し、データ利用可能時に通知をもらうモデル
    • javaではNIOパッケージで実装できる

今のところの実現手段

いろんなREST APIから情報を持ってきて組み立てるシステムでは、毎回同期でかつブロックして待ってたら遅いので、非同期かつノンブロッキングが比較的簡単に実装できるのはとても大切。

Principles of Evolutionary Architecture [CON4580]

継続的にアーキテクチャを進化させていく上での原理・原則を紹介するセッション。

進化するアーキテクチャの原則
  • 最終責任時点 (last responsible moment)
    • その時点で集まる最大限の情報を集め、複雑性による技術的負債は最小限に
    • 優先度を重視して、早めに決断する
  • 発展的なアーキテクトと開発
    • データのライフサイクルとオーナーを明確にする
    • 軽量なツールとドキュメント
    • ソフトウェアの内部品質は変更が容易かどうかで評価すべき
  • ポステルの法則
    • 送信側は慎重に、受信側は寛容に。バリデーションは必要な範囲で。
  • 設計に対するテスタビリティ
    • テスタビリティを向上させることで、より最適な設計に近づく
    • メッセージングシステムは、あくまで通信のために使い、ビジネスロジックとしては使わない
    • 振る舞いや性能だけでなく、規約もテストする
  • コンウェイの法則
    • それぞれの組織がデザインするシステムは、その組織間のコミュニケーション構造を反映する
    • コミュニケーションの欠落は、複雑な統合に繋がる
    • 製品は組織と同じ形になるので、変えたければ組織か製品自体を変える
実現のためのテクニック
  • データベースリファクタリング
    • 大きな変更を小さく分割して、バージョン管理もする
  • 継続的デリバリー
    • デプロイって作業は本来退屈じゃなきゃいけない!
  • システムインタフェース規約のテスト
    • 個々のシステムが独立して作業できるようなインタフェース規約
    • ポステルの法則も意識しながら
    • 古来よりアーキテクトが持つ役割の一つ

今後の仕事で判断に迷ったときの参考にしようと思う。

Plugging into the Java Compiler [CON4265]

Googleが作ってるValueObject実装のAutoValue、Squireが作ってるDIの実装daggerと、それぞれの技術の基となっているアノテーションプロセッサの話

AutoValue
  • イミュータブルなValueObjectのコード記述を簡潔にすることが目的
    • コンストラクタ/equals()/toString()/hashCode()を各クラス毎に書くのは冗長
    • @AutoValueを付与して、アノテーションプロセッサによりソースを生成
    • 抽象クラスにAutoValue_を付与したクラスのソースが生成される

pom.xmlには以下を追加

<dependency>
    <groupId>com.google.auto.value</groupId>
    <artifactId>auto-value</artifactId>
    <version>1.0-rc1</version>
    <scope>provided</scope>
</dependency>

@AutoValueを付与する

@AuthValue public abstract class Address {
    public abstract String streetAddress();
    public abstract int postCode();

    public static  Address create (String atreetAddress, int postCode) {
        return new AutoValue_Address(streetAddress, postCode);
    }
}

ValueObject生成時にはcreateメソッドを実行する

Address a = Address.create("some address", "xxx-xxxx");

lombokの@Value異なり、AutoValueではコンパイラの処理に割り込んでバイトコードを追加したりなんてことはせずに、シンプルにソースコードを生成する。実際にNetBeansで試してみると、以下のようにソースファイルが出てくる。
f:id:n_agetsuma:20141006172730p:plain

AutoValueを使うとあくまでソースを生成しているだけなので、仮にAutoValue自体にバグがあってもすぐわかる。lombokで得る効率よりも、バイトコード生成の黒魔術感への不安が勝るプロジェクトで使えそう。

dagger
  • AutoValueと同様にアノテーションプロセッサによるコード生成により静的にDIの解決を行う
  • なぜDIをアノテーションプロセッサで行うか?
    • 性能 : リフレクションを使わないので早い
    • 開発容易性 : 実行時ではなく、コンパイル時に依存性解決エラーがわかる
    • 設計の明瞭性 : コードをみれば依存性解決結果がわかる。DIコンテナによる連結は"magic map"でわかりにくい
  • 現在のリリースは1.xで、現在2.0の開発中

dagger1.0のコード例についてはマニュアルが詳しい。

アノテーションプロセッサでできること/できないこと
  • できる
    • アノテーションを読んで、ソースやメタファイル(XML, META-INF/services)などが出力可能
    • 間違ってたら、コンパイル時に指摘される。実行時まで引き延ばさない。
  • できない
    • 既存のクラス自体を変更することはできない
    • ソースを吐くのは、あくまで別のクラスとして出力が必要
  • アノテーションプロセッサの良いところ
    • エラーチェックが早い段階でできる
    • ランタイム時にリフレクション等の処理をしないので、性能が挙がる
    • 見れるソースコードが出力できる。黒魔術にならない

JJUG 2013 Springで@kimuchi583さんから紹介のあったType annotationの話と組み合わせて考えても、これからの品質・生産性向上ツールアノテーションを読み込んで何かするのが軽量で使いやすいと思った。

Debt and Deprecation [CON6377]

Java Day Tokyo 2014やJJUG CCC 2014 Springでもラムダ式やストリームAPIについて講演されたStuart Marksさんが、Dr.Deprecator(非推奨博士?)として登場。

内容はJavaの『@Deprecated - 非推奨API』に関する振り返りと、現在複数の意味合いを持つDeprecatedを新しい言葉に置き換える提案を紹介するセッション。

今までの非推奨の振り返り
  • javadocタグとして@deprecatedが登場したのはJDK1.1まで遡る
  • JDK1.1において古いAPIに取ってかわる、多くの新しいAPIが盛り込まれた
  • 古いAPIから新しいAPIへの移行を促進するために非推奨は誕生した
既存の『非推奨』の使い方
  • 一口に非推奨と言っても、それぞれ趣旨が異なる
    • APIの実行によりエラーを招く (Thread.stopなど)
    • 設計上の欠陥 (java.util.Dateの多くのメソッド)
    • philosophical (理念?) の変更 (JDK5からSystem.getenvが非推奨でなくなった)
非推奨を新しい言葉を使って再カテゴライズする提案

陳腐化(Obsolescence)したAPIは現在の非推奨API以外(例 Vector)にもたくさんある。非推奨と一つに丸めてしまうのではなく、適切な用語を再定義してカテゴライズする。

  • Condemned : 将来のリリースで削除や無効化するもの
  • Dangerous : バグやデータ損失を招くAPIであるもの
  • Superseded : 危険ではなく、削除される予定もないが、新しいコードでは新機能に置き換えた方が良いもの
  • Unimplemented : 実装されてないことを示す。UnsupportedOperationExceptionをランタイム例外としていきなり返さずに、ドキュメント上でわかりやすくする

例えば、Superseded(廃止)として現状は非推奨になっていないが、VectorやHashTableなどの古いコレクションクラスや、java.util.Timer/TimerTask(タスクが長時間かすると次の実行がずれるシングルスレッドモデルなので、ScheduledThreadPoolExecutorに置き換えた方が良い)がある。

Java9で削除検討中のAPI
  • java9でのjigsaw導入に伴い、java.beansモジュールと相互依存があるAPI
    • java.util.logging.LogManagerのaddPropertyChangeListener/removePropertyChangeListener
    • java.util.jar.Pack200.Packer/Unpackerの同上メソッド

これら提案はあくまで将来に向けたアイディアであり、Java9に向けて具体的な話はないが、ユーザとしてはすごく嬉しい提案。特にUnsupportedOperationExceptionは相当イラっとくるので、@Unimplementedによるコンパイル時警告が欲しい。

JavaOne2014 3日目メモ (9/30)

今日も晴れていて、過ごしやすい気候。3回目のJavaOneでも雨に振られたことがないので、サンフランシスコは雨が少ない地域なのだろうか。

Building a Distributed Application for the Cloud with Akka Clustering and Java 8 [TUT6483]

PlayフレームワークとAkkaを組み合わせて、最近話題のリアクティブなシステムを実現しましょうという話。

リアクティブ・マニフェストについて

詳細については原文を参照のこと。

f:id:n_agetsuma:20141002083537p:plain

  • Responsive (応答性)
    • システムは可能な限り最適なタイミングで応答すること。
  • Resilient (障害からの回復)
    • システムは処理が失敗した場合にも、失敗したことを応答すること。これは高可用性やミッションクリティカルシステムだけに求めるのではない - 処理の失敗時に何も応答できないシステムはResilientではない。
  • Elastic (柔軟性)
    • システムは大量のリクエスト下においても、応答すること。
  • Message Driven (メッセージ駆動)
    • リアクティブなシステムは、疎結合で独立性が高く、配置されたロケーションに依存しないコンポーネント間の非同期メッセージングのやり取りによって成り立つ。
実現手段

リアクティブなシステムを実現するためには『決してブロックしてはならない。常に非同期にすること。』が求められる。この実現手段としてPlay2Akkaの組み合わせが紹介された。

  • Play2
    • 非同期でノンブロックングな処理が実装可能
    • ステートレスなのでスケールする
    • RESTful & WebSocketのサポート

Playやakkaを使わなくても、分散システムでスケーリングさせるためには、以下のようなことに気をつける必要がある。

  • いかに軽量にコンポーネント間の通信を行うか (最近はRESTやMQTTなど)
  • 非同期でかつ並行処理でないと、いくらマシンを増やしてもスケールしない
  • クラスタ機能
    • 故障検知、ロードバランス機能

色々とデモコードが紹介されたがうまく理解できなかったため、akkaを実際にいじってみるのが宿題。javaでも動くらしい。

Vert.x + WebSocket + Cloud = Awesome Map Tracking [CON1695]

Vert.xでwebsocketを実装して、以下の画面のように常に動くバス情報をブラウザ上の地図画像にアイコンとして表示するデモのセッション。RedHatが提供しているPaaSであるOpenShiftがVert.xに対応しているので、合わせてOpenShiftの紹介も少し。スライドはこちらで公開中。

f:id:n_agetsuma:20141002093136p:plain

セッションの冒頭で早速『今までのTomcatJBossと何が違うの?』という質問が飛ぶ。分散環境でフレキシブルに重点を置いた新しいアーキテクチャと答える。

Vert.xの特徴
  • 多言語対応 : JVM言語で動く言語であれば言語を選ばない
  • 非同期処理
  • NettyをベースとすたノンブロッキングI/O
  • イベントバス
    • 同一VM/リモートVMに配置されたVerticle(モジュールのようなもの)の通信手段を提供

akka、Vert.xなど、非同期とノンブロッキング、スケールを意識した比較的新しいアプリケーション基盤がC10K問題にぶつかるような高アクセスシステムで使えそうなのは理解したが、身近なところで使えそうな領域を探すのが帰ってからの宿題。

Going Native: Bringing FFI to the JVM [CON3979]

従来のJNIに代えて、もっと簡易にネイティブコードが呼び出せる仕組み JNR (Java Native Runtime)に関するセッション。スピーカーはJRubyの開発者であるCharles Nutterさん。JNRについてはgithubにて公開中

JRubyを作っていて困った事

Javaで作られているJRubyを作るにあたって、OSレベルへのアクセス、例えばファイルシステムアクセスや、グラフィック/暗号化などのネイティブライブラリへのアクセスで困っている。しかし既存のJNIは使いにくい面も多い。もっと簡単にネイティブコードが呼び出せる仕組みがあると良い。

JNRのコード例

現在のプロセスIDを取得するlibcのgetpid関数を呼び出す例。とても直感的なコード例になっている。

import jnr.ffi.LibraryLoader;
import jnr.ffi.annotations.IgnoreError;

public class GetPidJNRExcample {
    public interface GetPid {
        @IgnoreError
        long getpid();
    }

    public static void main(String[] args) {
        GetPid getpid = LibraryLoader.create(GetPid.class)
                                     .load("c");
        getpid.getPid();
    }
}
JNRの構成要素

ユーザレベルの関数だけでなく、色々なネイティブ関数にアクセスできる。例えば以下のようなもの。

今後に向けて
  • この仕組みをJava標準のFFI(Foreign function interface)として実現するには、JVMの協力が不可欠
  • 現状のところ、JDKの改善提案であるJEP 191としてドラフトを提案
  • Project PanamaとしてJDKレベルでAPIの定義や、JVMレベルの最適化、セキュリティへの考慮などを検討中
  • 将来的にはJSR(Java Specification Requests)として検討したいが、現状は未定

The Modular Java Platform and Project Jigsaw [CON5435]

前々から検討されているJDKへのモジュールシステム導入 Project Jigsaw に関するセッション。

互換性の維持と新機能の要望により増え続けるJDKのコアAPI実装にモジュール化システムを導入して、使いたいモジュールのみをロードする仕組みを作る。デモではrt.jarがなくなったよとのアピールがあり。

jlinkと呼ばれるツールで依存性を管理するらしいが、詳細は理解できなかった。JDK9のEarly Accessには今のところJavaOneでデモがあったjlinkやrt.jarの削除が盛り込まれていないので、JDK9のEAにマージされるのが待ち遠しい。

JSON Pointer and JSON Patch: Updates to the Java API for JSON Processing [CON5742]

  • 後日追記予定

Troubleshooting with Serviceability and the New Runtime Monitoring Tool HeapStats [BOF3108]

NTT OSSセンタが開発したJavaトラブル解析支援ツール HeapStatsBOF。NTT OSSセンタはNTTグループ会社向けのOSSのテクニカルサポートを提供しており、頻出トラブルとトラブル解決の迅速化を目的に開発されたHeapStatsの紹介が行われた。

よくあるトラブルについて

OSSセンタに寄せられたサポート依頼では、ヒープ関連のトラブルシュート依頼が特に多かった。

  • Heap/Perm/GC 31.3%
  • API 20.1%
  • JavaVMのクラッシュ 19.3%
トラブルシュートの課題

情報がない
ヒープのトラブルが多いが、トラブル発生時のヒープダンプが残っていない事も多く、-XX:+HeapDumpOnOutOfmemoryErrorをトラブル発生後に追加してもリークスピードが遅く、再現に数ヶ月かかるケースもあった。

既存ツールは重い
ヒープダンプは取得に時間がかかるためサービス影響が大きく、ヒストグラムではオブジェクト間の参照が見れないため解析情報が不足する。

HeapStats

HeapStatsの特徴は以下の通り。

  • トラブルシュートに必要な情報を漏らさず収集
    • JVM動作中に定常的に少しずつヒープ情報を収集
  • 軽量
    • HeapStatsはメジャーGC契機で情報収集するので、新たなStop the worldを発生させない。
    • SPECjvm2008では、HeapStatsアタッチによるオーバヘッドは4.51%
  • 可視化
    • HeapStatsエージェントで情報収集したバイナリ形式のヒープ情報を、GUIツールのHeapStatsアナライザで可視化

クラスヒストグラムの推移を示したHeapStatsアナライザ表示例
f:id:n_agetsuma:20141003074328p:plain

オブジェクト間の参照関係を示した表示例
f:id:n_agetsuma:20141003074400p:plain

HeapStats、Javaトラブルシューティングに強力です。

JavaOne2014 2日目メモ (9/29)

2日目。朝は霧が出ていて少し寒いが、日中になると日差しが出て半袖でも良いくらい暑くなる。今日から本格的にセッションの開始。

Java EE 8 [CON3015]

Java EE8のスペックリードであるLinda DeMichielさんによる、Java EE 8の現状の方針を話すセッション。

Java EE 8の大きなテーマは3つ。

  • HTML5/Web層の改善
  • Easy of Development (かんたんに開発) / CDI Alignment
  • モダンインフラへの対応 (クラウド対応)
HTML5/Web層の改善
Easy of Development / CDI Alignment
  • セキュリティインターセプタ
  • コンテナ管理Beanのシンプルな一本化。EJBでしかできない"MDB"をCDIで実行。
  • EJB2.x のクライアント向けビューをプルーニング(オプション化)
    • EJBHomeやEJBObjectなどのEJB2.xがオプション仕様となる
モダンインフラへの対応
  • Java EE Management APIの導入
    • RESTベースのAPIでAPサーバの管理・監視を標準化する 
  • Java EE Security 1.0
    • 現状使いにくいセキュリティ仕様を見直して作り直す

Tutorial: JVM Platform as a Service [TUT3525]

スエーデンのCITERUSという企業の技術者が、2012年にJavaが使えるPaaSを使ったプロジェクトを経験談を交えながら、各PaaSプラットフォームの特徴やデモを示すセッション。

なぜPaaS?
  • 何よりもtime-to-marketの達成がPaaSを使う一番の目的。
  • 過去の事例では、顧客のコアドメインがITと関連が薄く、ITインフラ(サーバとか)を持ってなかったのがPaaS適用を推進した。
dotcloudを使ってみて
  • Java、MongoDB、PostgreSQL、RabbitMQが使いたかったため、テクノロジスタックが合った
  • 実際に運用してみたら色々問題も出てきた
    • ヨーロッパ - アメリカのレイテンシによる性能問題、価格の値上げ改定
  • 結果的にjerasticを使ってサービスを提供しているスエーデン国内のPaaSサービスに移行し、各々の問題に対処した。
どうやってPaaSを選ぶ?
  • 提供されているテクノロジスタック(言語、DB、Webコンテナ等)
  • 成熟しているサービスか?SLAおよび、サポート条件。価格。
  • ロックインされるか? (例えばGoogle BigtableをPaaSで使う等)
  • 価格は変動する。常に同じだと思わない方が良い。

PaaSは良い面ばかりがフォーカスされがちですが、歴史の浅いサービスだと価格改定が頻繁にあることが想像できてなかったので、良い勉強になりました。

HTTP 2.0 Comes to Java: What Servlet 4.0 Means to You [CON5989]

HTTP2.0(SPDY)の登場に伴い、ServletJava SE9のAPIへの機能追加について紹介するセッション。まだ JSR369 Servlet4.0 として検討がはじまったところで、いくつかのコード案が紹介されたが写真取れず。

Java SE 9側でのHTTP2.0クライアントのアイディアについては、以下のようなコード例が紹介された。

HttpRequestGroup group = HttpRequestGroup.create();
HttpRequest req = group.createRequest()
    .setRequestMethod("POST")
    .setRequestURI(new URI("http://www.foo.com/a/b"))
    .setRequestBody("param1=1,param2=2")
    .onResponseHeader("X-Foo", (request, name, value) -> {
        System.out.println("receive an X-Foo header")
     })
    .sendRequest()
    .waitForCompletion();

HTTP2.0では、1つのコネクションが1対1でのリクエスト/レスポンスモデルではなく、複数のリクエストが1コネクションで並行して送受信されるため、リクエストのグループ化(HttpRequestGroup)が導入し、その後のリクエスト送信にもメソッドチェーンや、リクエスト応答のヘッダ処理にラムダ式を書いたり、最近のJavaっぽいAPI案となっている。

Modular Architectures Using Microservices [CON6132]

モジュラアーキテクチャの考え方を、クラウド環境でOSGiを使うためにリモート呼び出しなどをサポートするAmdatuを使って実現する方法を紹介したセッション。

まず、amdatuによる実装を示す前に、ソフトウェアをなるべくモジュール化して連携させるモジュラアーキテクチャとマイクロサービスの考え方を紹介した。

モジュラアーキテクチャとは

原則として以下の2つを要件として紹介した。

  • Adaptivility (change ahead)
    • 適応性。ビジネス・環境の変化にソフトウェアの変更が追従できるか。
  • Reuse
    • 再利用。コピペしない。オブジェクト指向もコピペをなるべくなくして、コンポーネントを上手に区切り、なるべくコードを再利用しやすいようにするのが始まりだった。
マイクロサービスとは
  • マーチンファウラ氏のブログの内容を時折引用。
    • 個々のコンポーネントはそれぞれ自分のデータを持つ。小さなサービスごとにデータを分ける。
    • DDDでコンテキスト境界として紹介されている単位
    • 分散サービスの連携においては、トランザクションを使わずにeventual consistency(実質的なxxx)によって現実的な一貫性の保持を目指す
    • サービスは自らのライフサイクルを持つ
    • RESTやメッセージングなどの軽量なコミュニケーション手段を使う
      • 分散サービスの連携に、ESBのような複雑性を持ち込まない
  • 分散システム固有の設計には注意する必要
    • どれか1つが落ちていても、サービスが縮小継続できるように
    • モニタリングに手間が掛かるのは、分散の代償
OSGiについて
  • モジュラアーキテクチャを実現する上で、バージョンの管理とデプロイの分離が必要
  • OSGiでバンドルごとのバージョン管理、デプロイ分離を実現
  • MANIFEST.MFへのexport/import記述により、API実装の詳細を隠してモジュール公開することが可能
Amdatu

リモート分散環境でOSGiを使うためのソフトウェア。OSSとして公開。自分でリモート接続コードを書かなくても、内部的にうまくやってくれてユーザは各OSGiバンドルの開発に集中できるらしい。

まとめると、マイクロサービスの実現例の1つとして、OSGiを使って、リモート分散をAmdatuに使いましたよというセッションでした。

Banking on OpenJDK: How Goldman Sachs Is Using and Contributing to OpenJDK [CON5177]

Goldman Sachs社のOpenJDKへの取り組みに関するセッション。

Goldman SachsJava
  • 1998年頃からJavaを技術評価として利用開始
  • 2004年から自社コレクションフレームワークを作り始め(githubで公開中)
  • 2013年からOCAにサインして、OpenJDKへのコントリビュートを開始した
  • 190GB以上のヒープを持つシステム、大量の分岐があって、JITコードキャッシュに影響を与える特殊な環境が特徴的。
なぜOpenJDKに力を入れ始めたのか
  • ソースが見れるのが何よりも重要
    • 商用でクラッシュしても解析できる
    • HotSpotの内部で改善できそうな部分を自分たちで見つけることができる
  • パッチを投げることが、自分たちのためにも、コミュニティのためにもなる
どんな風にOpenJDKを使っている
  • トラブルシューティング、新規能の研究・検証に使用
    • JVMTIでアサーションエラーが発生したバグ解析とパッチ作成の事例を紹介
  • 今のところ、プロダクション環境では使っていない

OpenJDKしてのソースが公開により、筆者自身もAPIやHotspotでのトラブル時に大変助かっている。Redhat Enterprise Linuxを使っているシステムではOpenJDKがバンドルされていて気軽に使えるので、もっと使われるところが増えると嬉しい。

JavaOne2014 1日目メモ (9/28)

JavaOne2014の初日はコミュニティが主体のユーザグループフォーラム(UGF)と、キーノートがサンフランシスコのモスコーンセンタにて行われた。カメラとPCを繋ぐケーブルを家に忘れたので、写真は後日追加予定。

以下、荒い部分も数多くあるがセッション参加メモ。

Lambdas and Laughs [UGF9672]

プロジェクトラムダの振り返りに関するセッション。

ラムダ式とは?ストリームAPIとは?Optionalは?など、Java8に入った新機能についてコードを見ながら振り返り、JavaOneでの関連セッションを紹介する。ユーザグループフォーラムのセッションらしく、途中でウケ狙いのスライドがたくさん入っているが、何回もスベる。

内容については様々なJava8特集で既出のため、特記事項なし。

GlassFish Roadmap and Executive Panel [UGF9120]

GlassFishのロードマップと、今後の方針に関するパネルディスカッション。アジェンダについては、GlassFishのホームページにもまとめられている。

ユーザグループフォーラムにも関わらず、オラクルのJava EE関連のエヴァンジェリストReza Rahmanさんや、この後のストラテジキーノートでも講演するCameron Purdyさん、日本でもWebLogicの製品発表関連で見かけるMichael Lehmannさんなど、オラクルとしての方針を話そうな立場の人たちがパネラーに並ぶ。

GlassFish 製品の基本的な戦略 (アナウンス済み内容と同じ)
  • Oracle GlassFish Server(商用サポート)としては、以前アナウンスしたとおり今後収束され、最終的に2019年にExtended Supportも終了する
  • GlassFish OpenSource Editionとしては今後も継続する。ただし、商用サポートは提供しない。
GlassFish4.1の新機能
  • プラットフォームのアップデート
    • Java8対応、CDI1.2、WebSocket1.1
  • Tyrus(WebSocket実装)
    • セッションリミット、プロキシのサポート、クライアント再接続
  • Jersey
    • 新しい診断(diagnostics)APIの導入
    • ServerSentEventクライアント再接続機能
  • 品質の安定化
Java EE8の今のところの方針
  • HTML5サポート/Web層の改善
    • JSON-Binding、JSON-Processing、Server Sent Event(SSE)、Action-based MVC、HTTP2.0サポート
  • Easy Of Development & Simplification : 開発生産性向上
    • セキュリティインタセプタ、メッセージングのシンプル化、WebSocketスコープなど
  • Modernize the infrastructure : モダンインフラへの対応
    • RESTベースの管理・監視APIの導入
    • より簡易なセキュリティAPIの導入。ユーザ管理、ロール管理、認証・認可など
Java EE8ロードマップ
  • Java EE 8 JSR 366で公開されている内容と同じ
  • 2016/3にFinal releaseとなる予定

パネルディスカッション内で触れられていたが、GlassFishWebLogicと共用しているモジュールも多く、これからも開発の継続されることが改めて強調されていた。

The OpenJDK Project: Your Java. Our Java. [UGF9755]

OpenJDKの今後の方針とコントリビュート方法に関するセッション。JEP(JDK Enhancement Proposals)に挙がっているJDKの改善案の紹介が行われた。いずれもドラフト段階だが興味深い。

紹介されたキーワードを以下のまとめる。

  • Shenandoah : 新しいGCストラテジ
    • 100GBヒープのような巨大ヒープでも、10ms以下の停止を目指す
List<Integer> list = #[ 1, 2, 3 ];
  • Measurement API
    • 色々な物事の値を単なるintやdoubleのような数値ではなく、クラスとして定義して抽象化したもの
    • githubのコード例により何となくイメージできる。例えば以下は心拍数を示す。
public static void main(String[] args) {
    HeartRate rate =  HeartRateAmount.of(BigDecimal.valueOf(90), BPM);
    System.out.println(rate);
}
  • HTTP2.0 Client
  • 軽量なJSONパーサ などなど

Strategy keynote / Technical keynote

Strategy Keynote
  • キーノートの前日に子供たちにプログラミングを教えるイベントがあったらしく、キーノートは子供たちの成果発表から始まる。
  • 子供向けだけではなく、コミュニティやJCPを通じてコミュニケーションを取る姿勢をアピール
Java SE
  • Java 8 buzz
    • Java8は色々な記事にも取り上げられ、書籍もたくさん出版されて確実に広まっている。
  • OpenJDKへのコントリビュート
    • オラクル以外のコミュニティによる貢献が引き続き行われている。ダグ・リーさんによるコンカレントAPIへの継続的な貢献など。
  • Goldman sachsによるJava8の適用事例
    • 自社のコレクションフレームワークのテストコードに適用したところ、105kL→95kLとなった
    • 匿名内部クラスをラムダ式に書き直したのが主な内容
  • Java SEのロードマップ
    • JDK8u40 (2015年前半)
      • パフォーマンス改善、他言語サポートの改善、アクセシビリティの改善など
      • Java SE Advancedへの継続的な機能追加
    • JDK8u60 (2015年後半)
      • バグフィックスおよびJava SE Advancedへの継続的な機能追加
      • シンプルにいうと『未定』と感じた
    • JDK9 (2016年)
Java ME
  • 普段使ってないので聞いてもよくわからず。
Java EE
  • 基本的には先ほどのGlassFishユーザグループフォーラムの内容と同じ
  • GlassFish4.1の機能、今のところ決まっているJavaEE8の方針について

Technical Keynote

  • Javaは来年で20周年
    • 2004年のJava5、2014年のJava8の大幅な改訂を振り返る
    • Javaは2030年になっても継続的に使われることを考慮して設計を続けている
  • 言語仕様やJVMの改善を盛り込むProject Valhalla、ネイティブコード呼び出しを改善するProject Panamaについては時間切れで説明なし

今年のキーノートは過去の振り返りが中心で新しい発表はなく、OpenJDKコミュニティやJCPで流れているJava9やJavaEE8の内容はまだまだ議論の途中でオラクルによる公式な周知はまだない印象を受けました。

HeapStatsをChefでインストールする

HotSpotJVMの監視・解析OSSツールであるHeapStatsをChefでインストールできるようにクックブックを作ってみた。


n-agetsu/chef-heapstats · GitHub

HeapStatsとはなんぞやについては、JJUGナイトセミナーなどのスライドキャスレー技術ブログ JavaVM監視・解析ツール HeapStatsを使ってみた参照。特に後述のブログには公式のwikiよりも詳しくまとまっている。

簡単にいうと、ヒープダンプのように一気に負荷をかけなくても、FullGC契機に少しずつヒープ情報をロギングしてくれる優れものである。

HeapStatsはアタッチ先のJVMへのオーバーヘッドを抑えるため、対応しているマシンが対応しているCPU命令セット(sse4 or avx or いずれも未対応)に応じてインストールするrpmファイルが異なる。このクックブックを使うと、マシンに合ったrpmをダウンロードしてインストールする。

前提条件

HeapStatsの動作条件は以下の通り。

  • Linux x86/x86_64 (クックブックではCentOS or Fedoraのみ対応)
  • JDK6u18以上、JDK7、JDK8
  • OpenJDKを使うときにはシンボル情報が必須 (java-xxx-debuginfoパッケージ)
  • pcre 6.6 以上
  • Net-SNMP 5.3.1 以上

HeapStatsクックブックの使い方

JDK7、WildFly、HeapStats agentの3つをChefでインストールする例を以下に示す。

vagrantやchef、knife-solo、berkshelfのインストールについては、Web上の記事やChef 実践入門本に詳しく紹介されているので省略。vagrant-berkshelfはハマってうまくいかなかったので、素直にknife-soloでセットアップする。

1. Cookbookの作成

適当な作業ディレクトリにクックブックを生成する。

cd work
knife solo init .
2. Berksfileの編集

knife solo init .の実行により、カレントディレクトリにBerksfileファイルが作成されている。ここにjava/WildFly8/HeapStatsのクックブックへの依存を定義する。

$ vim Berksfile
source "https://supermarket.getchef.com"

cookbook 'java'
cookbook 'heapstats', git: 'git@github.com:n-agetsu/chef-heapstats.git'
cookbook 'wildfly', git: 'git@github.com:bdwyertech/chef-wildfly.git'
$ berks install

cookbook 'heapstats'で指定している部分が今回作ってみたHeapStatsクックブック。berks installにより、依存先クックブックは./cookbooksにダウンロードされる。

3. Nodeオブジェクトの作成

対象マシンに何をインストールするか定義するnodeファイルを作成する。ファイル名のcentos.jsoncentosの部分は、マシンのホスト名 or IPアドレスを指定。

run_listと各クックブックのattributeを定義する。作ってみたHeapStatsのクックブックの依存先はjavaのみで、内部的にinclude_recipe 'java'して呼び出している。*1

wildflyクックブックにおいて、HeapStatsの有効化に必要なJava起動オプション-agentlib:heapstatsを追加している。他のAPサーバの場合でも、Java起動オプションに-agentlib:heapstatsを追加できればHeapStatsは有効化される。

heapstatsのfile/heaplogfile/archivefileはHeapStatsが出力する各種ファイルの出力先を指定している。ログが一カ所にまとまるように、wildflyのログディレクトリを指定した。

$ vim nodes/centos.json
{
  "run_list": [
    "recipe[heapstats]",
    "recipe[wildfly]"
  ],
  "java": {
    "install_flavor":"openjdk",
    "jdk_version":"7",
    "openjdk_packages":["java-1.7.0-openjdk", "java-1.7.0-openjdk-devel", "java-1.7.0-openjdk-debuginfo"]
  },
  "wildfly": {
    "jpda": {
      "enabled":false
    },
    "java_opts": {
      "other": ["-agentlib:heapstats"]
    }
  },
  "heapstats": {
    "file":"/opt/wildfly/standalone/log/heapstats_snapshot.dat",
    "heaplogfile":"/opt/wildfly/standalone/log/heapstats_log.csv",
    "archivefile":"/opt/wildfly/standalone/log/heapstats_analyze.zip"
  }
}

HeapStatsクックブックで指定可能な属性についてはREADME.mdに記載している。

4. プロビジョニング

knife solo bootstrapで、対象サーバへのchefのセットアップknife solo prepareと、クックブックの実行knife solo cookの両方が動く。

$ knife solo bootstrap centos --bootstrap-version 11.12.0

centosの部分は、プロビジョニング対象マシンのホスト名を示す。IPアドレスでも可。
bootstrap-versionの指定がないとエラーになって動かない場合はがある。詳しくはstackoverflowに記載がある。

これでインストールは完了。JVMにHeapStatsがアタッチされた状態のWildFly8が起動している状態となる。

今後の課題

試しに作ってみたものの、以下のような課題があるので今後追加予定。

  • HeapStatsの各種出力ファイルへのログローテーション (logrorate.d) 対応
  • OpenJDKを使用する場合のdebuginfoインストール漏れのチェック

*1:HeapStatsにはOpenJDKのdebuginfoが必要になるが、javaコミュニティクックブックではpackageリソースにoptionsが設定できず、--enable-repo=debugが設定できなかったため、HeapStats側のレシピで/etc/yum.repos.d/CentOS-Debuginfo.repoを編集してdebuginfoのリポジトリを有効化している。fedora向けも同様。

JDK8からあるちょっと嬉しいGCログオプション

JDK8およびJDK8u20では、GCログに関連する2つの便利な機能が追加されている。いずれの機能も2014/8現在最新のJDK7 update 67 には含まれていないが、JDK7u80にてバックポートされる予定。

GCログにpidと日付を含める (JDK8より)

JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/wildfly/gc_%p_%t.log"
  => 実際のファイル名例 : gc_pid31455_2014-08-31_14-20-16.log.0

GCログのフォーマットに%pを入れるとpid形式のプロセスIDが付与される。また%tを付与すると"_2014-08-31_14-20-16"のようにGCログファイルを作成した日付時分秒が追加される。かつてGCログはJavaを再起動すると同じファイルが上書きされて消えてしまうため、出力先を-Xloggc:gc.log.`date +%Y%m%d%H%M%S`のようにOSコマンドによってファイル名に日付を付与していたが、この機能の追加によりOSコマンドへの依存がなくなるので便利。

jcmd GC.rotate_logによるローテーション (JDK8u20より)

JDK8u20よりjcmdにGCログをローテーションさせるコマンドが追加されている。

jcmd <pid> GC.rotate_log

以前よりGCログにはサイズローテーション機能が付与されていたが、Javaの場合ログのリロード機能がないため、logrotate.dなどによる日付ローテーションが困難であった。GC.rotate_logの導入により、以下の起動オプションと組み合わせると日付契機のログローテーションが実現できる。

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=0

UseGCLogFileRotationおよびNumberOfGCLogFilesが設定されていない状態でjcmd GC.rotate_logを実行すると、GCログローテーションが有効となっていないことを示すエラー(Target VM does not support GC log file rotation.)となる。また、GCLogFilzeSize=0を設定し、ファイルサイズ契機のローテーションを止めている。

NumberOfGCLogFilesの指定がGC.rotate_logの実行に必須であるため、 ログの世代数管理をlogrotate.dに任せるのは難しい。またファイルのコピーや世代数を超えたファイルの削除はJVMが行ってくれるため、logrotate.dを使わずにまずはお試しとして直接cron.dailyに入れてみた。

# touch /etc/cron.d/cron.daily/gclog_rotate
# vim /etc/cron.d/cron.daily/gclog_rotate

#!/bin/sh
sudo -u wildfly /usr/java/jdk1.8.0_20/bin/jcmd `cat /var/run/wildfly/wildfly.pid` GC.rotate_log

/var/run/wildfly/wildfly.pidより起動中のWildFlyのプロセスIDを確認している。WildFlyでなくても、/etc/rc.d/init.dに登録している場合は/var/run配下に現状のプロセスIDを示すファイルが出力されると思う。

まとめ

  • JDK8よりGCログフォーマットに%p(pid)や%t(時間)が含められる
  • JDK8u20よりjcmd GC.rotate_logGCログローテションできる
  • いずれも現状はJDK7に含まれていないが、JDK7u80 にバックポート予定