JavaScriptによるEtupirkaCMSのアップデートプログラム

EtupirkaCMS絡みでちょっとネタになりそうなプログラムを作ったので紹介します。内容としては、JSR223(スクリプト言語サポート)を使いJavaScriptでランチャープログラム(Swingアプリ)の更新を行う仕組みの解説です。

動機

EtupirkaCMSのウリとしてJavaだとかGAEだとかを意識せずに使って貰えるようなユーザビリティを目指しています。昨今のアプリでは自動アップデートなんて当たり前ですから、EtupirkaCMSも自動アップデートが出来る必要があります。しかし、Javaスタンドアローンプログラムなど珍しいこの時代、WindowsアプリやWebStartのようにちょとしたAPIを使えば自動アップデートが出来るというわけにはいきません。そこでほとんどを自分で実装するわけですが、色々と課題もあります。

ランチャーの起動中にランチャーの使っているjarは(簡単には)更新できない

最初の問題として、ランチャーが起動しているときにそのjarは触れません。なので、元からあるetupirka.jarに加え、updater.jarを配置します。ランチャー起動時に更新が必要と判断すると、updater.jarを外部プロセスとして起動し、etupirka.jarは終了します。

更新内容は毎回異なる

当然ですが更新内容は毎回異なります。従って、更新プログラムをダウンロードしてローカルで実行するのがセオリーかと思われます。しかし、仕組みとしてやや煩雑になってしまうでしょう。もっとシンプルにできないのかと考え、更新用のプログラムをスクリプトとしてダウンロードし実行する方法を思いつきました。JSR223の出番です、JavaScriptです*1

ファイルのコピーとか削除をJavaScriptで行う

・・・ことは出来ません。ところが、JSR223の特徴としてスクリプト側からJavaのクラスを使えますJavaで書くのをやめてスクリプトで書いているというのに、Javaを使いたいとは意味不明とも思えますが、JavaScriptJRubyでSwingのコードを書けるなどやっぱり意味が夢があります。例えばjava.io.Fileを使ったコードは次のようになります。

var dir = new java.io.File("foo");
dir.mkdir();

完全修飾名で書くのが嫌ならImportしましょう。

importPackage(java.io);
var dir = new File("foo");
dir.mkdir();

実例

実際のアップデートに関するコードはこんな形になっています(抜粋)。

// 共通部
importPackage(org.etupirkacms.updater);
var sys = {
  baseDir: Commands.baseDir(),
  baseUrl: "http://etupirka.googlecode.com/files/",
  // file
  file: function(filename) {
    return Commands.newFile(this.baseDir, filename.split('/'));
  },
  // mkdir
  mkdir: function(pathname) {
    Commands.mkdir(this.file(pathname));
  },
  // rm
  rm: function(filename) {
    Commands.rmr(this.file(filename));
  },
  // mv
  mv: function(srcfilename, destfilename) {
    Commands.mv(this.file(srcfilename), this.file(destfilename));
  },
  // cp
  cp: function(src, dest) {
    Commands.cpr(this.file(src), this.file(dest));
  },
  // download
  download: function(filename, dirname) {
    Commands.downloadAndUnzip(this.baseUrl + "/" + filename, this.file(dirname));
  },
};
function update(func) {
  func.apply(sys);
}
// バージョン毎の更新スクリプト
update(function() {
  // download new version
  this.mkdir("../etupirkacms/0.2.0");
  this.download("EtupirkaCMS-0.2.0.zip", "../etupirkacms/0.2.0");
  // replace _mng directory
  this.rm("war/_mng");
  this.cp("../etupirkacms/0.2.0/EtupirkaCMS/your-site/war/_mng", "war/_mng");
  // replace static/_mng directory
  this.rm("war/static/_mng");
  this.cp("../etupirkacms/0.2.0/EtupirkaCMS/your-site/war/static/_mng", "war/static/_mng");
  // replace lib
  this.rm("war/WEB-INF/lib/etupirka-0.1.0.jar");
  this.cp("../etupirkacms/0.2.0/EtupirkaCMS/your-site/war/WEB-INF/lib/etupirka-0.2.0.jar", "war/WEB-INF/lib/etupirka-0.2.0.jar");
  this.rm("etupirka.jar");
  this.cp("../etupirkacms/0.2.0/EtupirkaCMS/your-site/etupirka.jar", "etupirka.jar");
});

これでupdate.jarは更新が不要になったのです。

全体のソースは、こちらを参照のこと。ちなみに、このJavaScriptのコードはJUnitユニットテストしています。

まとめ

JSR223を使う事で処理の一部を外部リソースとして定義することが出来ます。そこではJavaではなくJavaScriptなどスクリプト言語で書くことが可能です。必要があれば、Javaのクラスにアクセスできます。

*1:他の言語も使えるが、あくまで追加ライブラリが必要なので現実的でない