name見出し記法

name見出し記法を知った。各記事のパーマリンクのURLを、その記事の内容を表すような感じにできる。

こういうのは早いうちにやったほうがいい。さっそく、今まで書いた全部の記事の見出しを直した。

追記:失敗だった

失敗だった。この記法を使うと、記事を書いた時刻が失われてしまう。急いで元に戻した。一応バックアップ取っておいて良かった。

はてなダイアリーは、時刻かnameか、どっちかひとつしか持つことが出来ないようだ。うーん悩ましいなコレ。なんでこんな仕様なんだろ。ブログじゃなく日記だった頃の名残なのかな。

今やってること

ホントはWeb系の開発が好きなんだけど、仕事なんで他のこともやる。

今やってるのはJavaアプレットアプレットなんて流行らないと思ってあんまり勉強してなかったので、真面目にやるのは今回が初めて。

ってことで、しばらくはアプレット関係の話が多くなるかと。

アプレットで「ファイルを保存」ダイアログを出す

調べたらわかった。わかってみると割と簡単。

FileDialog dialog = new FileDialog(new JFrame(), "ファイルの保存", FileDialog.SAVE);
dialog.toFront();
dialog.setVisible(true); // ここでダイアログが出て、閉じるまで待つ。

System.out.println(dialog.getDirectory()); // パス
System.out.println(dialog.getFile()); // ファイル名
  • 上書きしますか?の確認は自動でやってくれる。
  • FileDialogのコンストラクタの第二引数をFileDialog.LOADにすると「ファイルを開く」ダイアログになる。

awtなので別にアプレットに限った事じゃないか。
ていうかアプレットでこれ使おうとすると、ローカルファイルに触れる権限が必要なのでむしろ面倒。

テストのため、オレオレ証明書でjarに署名する方法

ローカルファイルに触れるアプレットは、署名が無いとダメ。権限が無いって怒られる。でも開発中に本物の署名ができるケースもなかなか無い。

そこで、オレオレ証明書による署名をした。今回、Windowsコマンドプロンプトからやってみた。他のOSでも大差ないと思う。

鍵ペアの生成

C:\>c:\jdk1.5.0_12\bin\keytool -genkey -keyalg rsa -alias test

これで「test」っていう名前(エイリアス)の鍵ペアの生成が始まる。あとは画面に従って、パスワードやら姓名などの情報を入れる。テスト用のオレオレ証明書なので、パスワード以外の情報は適当でいい。パスワードはあとで署名するときに使うので覚えておくこと。

署名する

C:\>c:\jdk1.5.0_12\bin\jarsigner c:\dev\applet.jar test

jarsignerに、署名したいjarのパスと鍵ペアのエイリアスを指定する。

するとキーストアのパスワードを入れろと言われるので、さっきのパスワードを入れる。

終わり

  • 権限を与えたいすべてのjarを署名する必要がある。
  • このjarを使うと、当然ブラウザは警告を出す。オレオレだから。
  • これはあくまでテスト用。本番でオレオレ証明書は使ってはいけない。絶対に。手抜きはダメ。

アプレットに指定したURLの画像を表示

画像をWebから取得してアプレット内に表示したい。

なんか難しそうだと思ったら、めちゃくちゃ簡単だった。

URL url = new URL("http://example.com/image.gif");
ImageIcon icon = new ImageIcon(url);

JLabel label = new JLabel();
label.setIcon(icon);
label.setSize(icon.getIconWidth(), icon.getIconHeight());

どこにも通信とか描画の処理を書かなくておk。けっこう便利ー。

注意点

アプレットが置いてあるドメインと同じドメインの画像しか取って来れないかも。試してないけど、セキュリティ的にそんな制限があると思う。アプレットじゃなくローカルアプリなら大丈夫かな。

アプレットからWebサーバへファイルアップロード

アプレットからのファイルアップロード。これはなかなか大変だった。

具体的には、httpでmultipartのPOSTをしたい。複数のファイルや、一緒にテキストデータなんかも一緒にPOSTしたい。

Jakarta Commons HttpClientをアプレットで使うのは難しい

Jakarta Commons HttpClientというコンポーネントがあって、これを使うとhttp通信が手軽に使えるらしい。

これを使って実際に作ってみたけど、ダメだった。

  • 自分の作ったjarだけじゃなく、このHttpClientのjarにも署名が必要
  • 一緒に使う必要があるJakarta Commons Loggingのバグにより、権限エラーになる
  • 日本語ファイル名の文字化け

どれも回避しようと思えばできるんだけど、後でのメンテも含めて面倒になるため、あきらめた。

自分で通信部分を実装

Javaの標準機能だけを使って自分で通信部分を実装した。

String boundary = generateBoundary();

// 接続
URL url = new URL("http://example.com/upload.do"); // 送信先
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
DataOutputStream out = new DataOutputStream(conn.getOutputStream());

// テキストフィールド送信
out.writeBytes("--" + boundary + "\r\n");
out.writeBytes("Content-Disposition: form-data; name=\"text\"\r\n");
out.writeBytes("Content-Type: text/plain; charset=Shift_JIS\r\n\r\n");
out.write("テキスト".getBytes(this.charset));
out.writeBytes("\r\n");

// ファイルフィールド送信
File file = new File("c:\\files\\file.zip");
out.writeBytes("--" + boundary + "\r\n");
out.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"");
out.write(file.getName().getBytes("Shift_JIS"));
out.writeBytes("\"\r\n");
out.writeBytes("Content-Type: application/octet-stream\r\n\r\n");
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
int buff = 0;
while((buff = in.read()) != -1){
    out.write(buff);
}
out.writeBytes("\r\n");
in.close();

// 送信終わり
out.writeBytes("--" + boundary + "--");
out.flush();
out.close();

// レスポンスを受信 (これをやらないと通信が完了しない)
InputStream stream = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String responseData = null;
while((responseData = reader.readLine()) != null){
    System.out.print(responseData);
}
stream.close();

テキストフィールドとファイルフィールドを送信しているところは、連続して何回でも呼べる (multipart のパートとなる)。

あと、最初にboudaryを取得してるところ (generateBoundary) は、以下のような感じで書いた。

private String generateBoundary(){
    String chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
    Random rand = new Random();
    String boundary = "";
    for(int i = 0; i < 40; i++){
        int r = rand.nextInt(chars.length());
        boundary += chars.substring(r, r + 1);
    }
    return boundary;
}

boundaryっていうのは、multipartのデータを送信するときにデータ(各パート)の区切りを表す文字列。どんな文字列でも良いが、boundary以外のデータに同じ文字列が含まれると、そこで区切られたことになってしまって良くない。

今回は、ランダムに40文字の文字列を生成しただけ。このやり方は、ものすごく運が悪いと、ファイル内のデータに含まれる文字列と重複しちゃうかも知れない。

けど、たぶん相当なレアケースだと思う (確率は計算してないけど) ので、気にしないことにした。

URL#openConnectionによるhttp通信時にBasic認証

Javaの標準機能を使ってhttp通信するときは、URLクラスのopenConnectionメソッドを使って接続するわけだけど、そのときにBasic認証で認証したいときのやりかた。

final String username = "username";
final String password = "p@ssw0rd";
Authenticator.setDefault(new Authenticator(){
    @Override protected PasswordAuthentication getPasswordAuthentication(){
        return new PasswordAuthentication(username, password.toCharArray());
    }
});
  • AuthenticatorクラスのsetDefaultメソッドを呼ぶ。これはstaticメソッド。
  • 引数として、Authenticatorインタフェースを実装したクラスのインスタンスを渡す。
  • いちいちクラスを定義してインスタンスをnewして渡すと面倒なので、匿名クラス(無名インナークラス)で渡すのが簡単。
  • 匿名クラス内のコードから、外の変数を参照する場合、外の変数はfinalにしておくか、インスタンス変数にしておく必要がある。

これを、openConnectionメソッドを呼ぶ前にやっておく。

これでうまくいったから良いけど、なんでこんな仕様なの?あらかじめstaticメソッドを呼んでおくなんて、微妙すぎると思うんだけど。

アプレットからJavaScriptの関数を呼び出す

例えばアプレットで何かの処理をしていて、それが終わったタイミングで

  • JavaScriptのalertを呼んだり
  • ページを移動したり
  • その他もろもろ

といったような、何かしらのJavaScriptを動かしたいことがある。そんなときのやり方。

JSObject window = JSObject.getWindow(Applet.this);
window.eval("hoge()");

これで、アプレットが埋め込まれているhtml側に定義されてるJavaScripthoge関数が呼ばれる。いわゆるコールバック。

このhoge関数は、例えばこんな感じ。

function hoge(){
    alert('完了しました');
}

上記のようにすればalertされる。ページを移動したいなら、

function hoge(){
    location.href = 'http://example.com/complete.html';
}

こんな感じ。まあ普通のJavaScript。これをhtml側に用意しておけば良い。

plugin.jarをCLASS_PATHに追加

ここで注意点。JSObjectクラスは、そのままじゃ使えない。JREのlibディレクトリに入ってるplugin.jarをCLASS_PATHに追加するなりする必要がある。

例えばアプレットEclipseで作ってるなら、Javaのビルドパスに追加するとか。

ちなみにimport文は、

import netscape.javascript.JSObject;

こう。

アプレットを埋め込んでるタグにmayscriptを追加

さらにもう一点。アプレットを埋め込んでるhtmlタグにも定義が必要。

<param name="mayscript" value="true" />

これを、objectタグの中に書いておく。

デザインのカスタマイズ

とりあえず以下のcssだけ書いた。

.body h3{
  background: #9fbfbe;
  color: #ffffff;
  padding: 0.3em;
  font-size: 120%;
}
.body h3 a{
  color: #ffffff;
}
.body h3 a.sectioncategory{
  color: #ffffff;
}
.body h3 a .sanchor{
  color: #ffffff;
}
.body .section p {
  margin-bottom: 1.2em;
}
  • 記事の見出しを目立つようにした
  • 段落と段落の間のすきまを広くした

デザインをカスタマイズするのは楽しい。