2012年4月12日木曜日

Playframeworkでコンソール文字化け

Windows + Playframeworkで開発しているともれなく、コマンドプロンプトでの日本語表示が化けると思うんだけど、何故か検索してもまったく情報がひっかからない。
なんでだろ???

皆Windowsで開発していないのかな?

とりあえず回避方法は以下です。

1、System.out(err).printlnの文字化け回避
OnApplicationStartのJobを作成してそこでSystem.outとerrを差し替えます。

@OnApplicationStart
public class ConsoleLog extends Job {

    private boolean consoleSetuped;

    public void doJob() {
        if (!consoleSetuped) {
            try {
                PrintStream ps = new PrintStream(System.out, true, "MS932");
                System.setOut(ps);
                System.setErr(ps);
            } catch (UnsupportedEncodingException e) {
                //not occur
            }
            consoleSetuped = true;
        }
    }
}

2、Loggerでの文字化け回避
log4j.propertiesに

log4j.appender.Console.encoding=MS932

を追加

いじょ。

2012年4月4日水曜日

Playframeworkでダウンロード後にファイル削除

playframeworkでバイナリのダウンロード機能を実装する際にはrenderBinaryを使用する。
この時にダウンロード完了したファイルを削除しようとしていくつか試行錯誤が必要だったのでメモとして残しておく。
やったのは次の順

1、普通にメソッド内のfinallyで削除

public static void download(...)  throws IOException {
    File file = getDownloadFile(...);
    String filename = ...;
    try {
        renderBinary(file, filename);
    } finally {
        file.delete();
    }
}

結果: Fileが見つからない
何故ならrenderXXXXは対応するExcetionをthrowしてそのcatch節で処理を行うから。
catch節での処理が始まるときにはすでにdownloadメソッド自体を抜けており、そのfinally節でのファイル削除が実行されてしまっている。

2、@FinallyとThreadLocalの合わせ技


private static ThreadLocal<File> deleteFile;

@Finally
static void doDeleteFile() {
    File f = deleteFile.get();
    if (f != null) {
         f.delete();
         deleteFile.remove();
    }
}

public static void download(...)  throws IOException {
    File file = getDownloadFile(...);
    String filename = ...;

    deleteFile.set(file);

    renderBinary(file, filename);
}

結果:responseのcontent-lengthが0になる
@FinallyはrenderXXの処理実行後に実行されるので一見うまくいきそうに見える。
しかしRenderBinaryでレスポンスへのwriteが遅延処理されており、やっぱりwriteの前にファイルが削除されてしまう。

3、FileInputStreamのclose時に削除
結局うまくいったのはFileInputStreamのサブクラスを作成してそのclose時にファイルを削除するという方法

class FileInputStreamWithDelete extends FileInputStream {
    
    private File file;
    
    public FileInputStreamWithDelete(File file) throws FileNotFoundException {
        super(file);
        this.file = file;
    }
    
    @Override
    public void close() throws IOException {
        super.close();
        this.file.delete();
    }
}

public static void download(...) throws IOException {
    File file = getDownloadFile(...);
    String filename = ...;
    renderBinary(new FileInputStreamWithDelete(file), filename, file.length());
}

ちょっとひねりが必要な感じですね。