おがさわらなるひこのオープンソースとかプログラミングとか印刷技術とか

おがさわらなるひこ @naru0ga が技術系で興味を持ったりなんだりしたことをたまーに書くブログです。最近はてなダイアリー放置しすぎて記事書くたびにはてな記法忘れるのではてなブログに移行しました。

クリエイティブ・コモンズ・ライセンス
特に断りがない場合は、本ブログの筆者によるコンテンツは クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。

jOpenDocumentを2021年にリブートしてみる その1:Maven化

LibreOfficeの標準フォーマットであるOpenDocument Format(以下ODF)*1 は、XMLの標準化団体OASISのOpenDocument TCが標準化しISO標準にもなっている公開された文書フォーマットです。アプリケーションの進化に伴い標準が改訂されていく「真の国際標準」である唯一のオフィス向けドキュメント形式であるとともに、スキーマの構造が透明で見通しがよく、なおかつLibreOfficeの出力するODFは意味が明瞭(機械も人間も読みやすい)という特徴があります。

そのため操作・加工ライブラリも豊富にあり、さまざまな言語でODFを簡単に、しかも結構複雑な操作を行うことができます。

さて、私の勤務先はとあるセキュリティベンダーでございまして、日々顧客のためにシステムやアプリを診断して、それを報告書にして提出するということやっております。で、スプレッドシートの形式になった診断結果から見やすいPDF形式の報告書を作成するために、社内製のツールにてODFファイル(Writer向けのODT)を生成して、それに考察などを記入してWriterでPDFエクスポートして提出するという作業を日々行っております。

このために使っているライブラリが

www.jopendocument.org

といいまして、専用の拡張機能で埋め込んだタグをプログラムで置換したり表形式で埋め込んだりするというなかなか使いやすいライブラリであります。こいつ便利ですよという発表は去年の台湾のOSSイベントCOSCUPでしました。

speakerdeck.com

で、この資料でも書いたんですが、jOpenDocumentいいんですけどなにせ最後に出たバージョン1.4-rc2が 2014年 でして、java8で動作させると「この言語機能はdeprecatedだから使うのやめた方がいいよ」という警告がバリバリ出る。それはまあいいのですが、ODF 1.2 extended までしかサポートしてないので、今のLibreOffice(7.0以降)で普通に生成したODF(1.3 extended)だと正しく扱えない。 しかし今更これらの問題が治るとはとても思えない。思えないんだったら、自分で直せばいいじゃん! と、トライしてみることにしました。

ちなみにわたくし齢50にして細々と今でもコードを書いてますが(会社で書くのは ↑ のスライドでも述べた通りScala)、職業プログラマーとしての黄金期?はC/C++(言語仕様的には98)、C++は組み込み用途だったのでメモリフットプリントを抑えるためにRTTI禁止、という制限があり、Javaを書くようになったのは前職のソフトウェア第三者検証会社(今の所属の親会社)に入ってからで、世の中にはGradleとIntellij IDEAがすでに存在し、クラスパスがどーとか javac でビルドして *.class から JAR 作るとかそういう経験はゼロなのであります。ゼロ。antも知らない。mavenもほぼわからない。

そんなレベルの人間が、2014年で開発が止まっちゃった、まったくの他人が書いたライブラリをモダンにしてみようという、まあうまく行くんだか行かないんだかという試みを紹介してみたく。 意味も分からず試行錯誤で進めてるところがあるので、「そんなやり方はよくないよ!」という突っ込みは大募集なのです。

もとのソースを取ってくる

ソースは公式のダウンロードページ にてZIPファイルでおいてあるので、まずはこれを取ってきて手元に展開します。

まずは最初にライセンスを確認。LICENSE.txt を見たら GPL v3 だったので大手を振ってフォークできます。オープンソース万歳。

で、中身を見てみたら、 build.xml というファイルと libs というフォルダに JAR ファイルがゴロゴロ転がっています。さっき言った通り私はJavaのビルドシステムはさっぱりわからんのですがきっとこれはantなんだろうなと。で、さすがに今リブートのであればせめてビルドシステムはモダンにしたいよねということで、Maven化することにしました。

これも繰り返しになりますが、わたくし別にMavenにも詳しいわけではありません。なので、何も考えずにIntellij IDEAの新規プロジェクトでMavenを選んで、こんな感じの pom.xml をスクラッチで作りました。

<?xml version="1.0" encoding="UTF-8"?> 4.0.0

<groupId>jp.shiftsecurity.tech</groupId>
<artifactId>jOpenDocunentNg</artifactId>
<version>2.0-SNAPSHOT</version>

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

依存関係の解決

わからないなりに、たぶん元プロジェクトの lib にあるJARをMaven Centralで探してその設定を pom に書いていけばいいんでしょ、ということにしてやってみることにしました。なにせ2014年のソースなので依存関係も2014年で止まっておりますが、そこは何も考えずに最新化して、動かなかったら考えようと……。

探し方としてはまあひたすら https://search.maven.org/ で検索するだけなんですが、

ファイル名でartifactが想像できるものはそれで いまいちわからないものは jar を unzip してパッケージ名から推察 って感じです。結果以下の通りになりました。

元のjar maven central で見つけたバージョン 備考
commons-collections-3.2.1.jar org.apache.commons:commons-collections4:4.4 この名前なら多分Apache Commonsでしょう。3と4で非互換あるかもだけどなんとかしましょう
fb-annotations-2.0.0.jar com.github.spotbugs:spotbugs:4.3.0 もとのはfindbugsらしいけど死んだので後継プロジェクトのspotbugsに
isorelax-jaxp-bridge-ILM.jar org.jopendocument:isorelax-jaxp-bridge-ILM:1.1 どうもjOpenDocumentの一部を切り出しただけで凍結されてるけど、動かなかったら考えることでいったんはそのまま取り込み(実際問題あったんだけど)
jaxen-1.1.6.jar jaxen:jaxen:1.2.0
jcip-annotations.jar net.jcip:jcip-annotations:1.0 いろいろforkがあるけどいったんは公式っぽいもので
jdom-1.1.1.jar org.jdom:jdom:1.1.3 たぶん1系の最新を選んどけばよいのだろうと
jdom-2.0.5.jar org.jdom:jdom2:2.0.6 同名のJARのバージョン違いがあるのがなんだけど、まあ2系の最新を選べばよかろうと
js-1.7R1.jar org.mozilla:rhino:1.7.13 ZIPファイルをほどいたパッケージ名から判断
ognl-2.6.9.jar ognl:ognl:3.2.21 これは素直に名前から。
ognl-engine.jar org.jopendocument:ognl-engine:2.6.9 これも名前から

で、これらのdependencyをpom.xmlに追加してビルド……するも、jaxenがmaven centralに見つからないと怒られてしまいました。 よくわからないし、必要なら依存関係で引っ張ってこられたりするかな……と、えいっと指定自体はずしてしまいました。 いまのところ特に何も起きてない、ような……。

ソースコードの場所を移動

Mavenなのでソースコードの位置を src/ 直下から src/main/java 以下に丸っと引っ越しました。 テストコードも混じってるんですがそれはあとで……。

ライブラリ更新に伴うソースコードの修正

ライブラリのバージョンをばしばし上げたので、当然非互換な部分も出てきます。 動作の部分はあとで考えるとして、とりあえずコンパイルエラーをつぶしていきます。 細かに書いても退屈なだけでしょうから下記リンクからdiffを見てください。

https://github.com/naruoga/jOpenDocument/compare/af77a4b09e7fea72ceff9f325919bc463ddc7414..92982f10d37a9cf230720246fa2d23692cdd8e3f

コミットコメントから引用しておきます。英語へぼいのは勘弁してね。

commit 92982f10d37a9cf230720246fa2d23692cdd8e3f
Author: Naruhiko Ogasawara <ogasawara@shiftsecurity.jp>
Date:   Fri May 7 21:08:00 2021 +0900

    avoid to use deprecated PdfTemplate#createPrinterGraphics

    the implementation is just call PdfPrinterGraphics2D constructor,
    so it could be migrate easily

commit 75caf0495319ab02c60d0e5b7524d85808b31893
Author: Naruhiko Ogasawara <ogasawara@shiftsecurity.jp>
Date:   Fri May 7 21:05:28 2021 +0900

    OGNLDataModel: remove previous code comment, because we use Git

commit d2855abc7f01d43b8b0dd30b33bb1d4b96c655ed
Author: Naruhiko Ogasawara <ogasawara@shiftsecurity.jp>
Date:   Fri May 7 20:48:49 2021 +0900

    follow updated APIs of libraries

    general: avoid useless FQCN

    several: migrate to org.apache.commons.collections4
      - CollectionUtils
      - ExnTransformer
      - ICache
      - IPredicate
      - ITransformerWrapper
      - ODSingleXMLDocument
      - Transformer

    CollectionMap2Itf:
      remove() should have Object args, not type parameters to override Map

    OGNLDataModel:
      Ognl.getMemberAccess() has been removed, then change
      Ognl.addDefaultContext() without it

    SimplePDFGenerator:
      migrate to com.itextpdf.text from com.lowagie.text (this was obsoleted)

pom.xml にビルドの設定とプラグインの設定を追加

これでビルドできるようになっただろ! と IntellijMaven画面から build を叩いても何も起きない。ふむぅ。

これは私が素人だからあんまり意味も分からずに書いてますが、以下のようなビルド用の設定書かないとだめっぽいですね。

        <dependency>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <version>3.2.1</version>
            <type>maven-plugin</type>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>${project.basedir}/src/main/resources</directory>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>${project.basedir}/src/test/resources</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

maven-resource-pluginはJARの中にリソース取り込むためのプラグインmaven-soruce-pluginはsource jarを作るときのプラグイン(これがないとJARで取り込んだプロジェクト側でソースコードレベルでバッグができない)、あとビルド時にどこからリソース参照するかの設定を追加。この時点でのpom.xmlはこんな感じです。

jOpenDocument/pom.xml at 6497e9f279bb8d6184b8994a452865e040b0bc5d · naruoga/jOpenDocument · GitHub

これで、IntellijMaven 機能でbuildすると依存関係をちゃんと抱いたjarと、source jarの両方ができるようになりました。よかったよかった。

取り込んで基本的な動作確認

前述のCOSCUPでデモしたときのサンプルにできたJARを食わせて、ちゃんと動くことを確認しました。 おお、ここまでは結構順調。

github.com

jitpackにて仮公開

毎回毎回JAR差し替えるのは面倒くさいので、みんな大好きjitpackにて公開することにしました。

jitpack.io

こんな風にGitHubの公開リポジトリ登録するだけで、Mavenなどで参照できる形で公開してくれる上に、それを各バージョン管理システムmaven, gradle, sbtなど)でどう設定するかまで教えてくれる超便利サイトです。

f:id:naruoga:20210711171733p:plain
Jitpackでの公開画面。ブランチやタグ、コミットハッシュでも参照できるのが嬉しい

これで公開して、先のデモプログラムもそっちを参照するように(ローカルで)変えて、ちゃんと動くじゃーん、、と確認して、いい気持になって放置してたのが、ここまでのいきさつです。

ほぼ1か月の放置の末、大急ぎで続きをやろうというのが次の記事になります。あまり期待せずにお待ちあれ。結論を言うと、サンプル動いたくらいで安心してちゃいけなかったですw

*1:公式の宣伝サイト http://opendocumentformat.org が死んでいるっぽい……。