manholeのおきらくごくらく日記

SPAM対策中です。コメントが反映されない方はリンク先をご覧ください。
[ Maven2 | Maven | Axis2 | testing ]
オススメ: xUnit Test Patterns | JUnit Recipes | パターン指向リファクタリング入門 | Ant第2版


2012-01-25 (水) [長年日記] この日を編集

java.beans.XMLEncoderでTimestampを扱う

XMLEncoder#setPersistenceDelegateへjava.sql.Timestamp.classのPersistenceDelegateをセットする。
(セットしないと例外になります)

final XMLEncoder encoder = open();
encoder.setPersistenceDelegate(Timestamp.class, TimestampPersistenceDelegate.getInstance());
encoder.writeObject(obj);
close(encoder);

PersistenceDelegate実装はこんなふう。

private static class TimestampPersistenceDelegate extends PersistenceDelegate {

    private static PersistenceDelegate INSTANCE = new TimestampPersistenceDelegate();

    public static PersistenceDelegate getInstance() {
      return INSTANCE;
    }

    @Override
    protected Expression instantiate(final Object oldInstance, final Encoder out) {
        final String s = oldInstance.toString();
        // Timestamp.valueOf(String s) を示す
        return new Expression(oldInstance, oldInstance.getClass(), "valueOf", new Object[] { s });
    }

}

出力されるxmlはこのようになります。

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0_09" class="java.beans.XMLDecoder">
 <object class="java.sql.Timestamp" method="valueOf">
  <string>2012-01-24 11:22:33.456789</string>
 </object>
</java>

この対策をしない場合の動作

<その1>

そもそも encoder.setPersistenceDelegate(Timestamp.class, xxxx); をしない場合は、java.lang.InstantiationExceptionになります。
Timestampがデフォルトコンストラクタを持たないためでしょう。

java.lang.InstantiationException: java.sql.Timestamp
    at java.lang.Class.newInstance0(Class.java:335)
    at java.lang.Class.newInstance(Class.java:303)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:239)
    at java.beans.Statement.invoke(Statement.java:215)
    at java.beans.Expression.getValue(Expression.java:98)
    at java.beans.Encoder.getValue(Encoder.java:85)
    at java.beans.Encoder.get(Encoder.java:180)
    at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:94)
    at java.beans.Encoder.writeObject(Encoder.java:54)
    at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254)
    at java.beans.Encoder.writeExpression(Encoder.java:259)
    at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:369)
    at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:97)
    at java.beans.Encoder.writeObject(Encoder.java:54)
    at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254)
    at java.beans.Encoder.writeObject1(Encoder.java:186)
    at java.beans.Encoder.cloneStatement(Encoder.java:199)
    at java.beans.Encoder.writeStatement(Encoder.java:230)
    at java.beans.XMLEncoder.writeStatement(XMLEncoder.java:328)
    at java.beans.XMLEncoder.writeObject(XMLEncoder.java:257)
    ...

<その2>

java.util.Dateに設定されているPersistenceDelegateを使用する場合。

final PersistenceDelegate pd = encoder.getPersistenceDelegate(Date.class);
encoder.setPersistenceDelegate(Timestamp.class, pd);

Timestampがnano部分を持つ場合に無限ループしてStackOverflowErrorになります。
ミリ秒部分までなら問題なく動作します。

java.lang.StackOverflowError
    at jp.co.hitachi_kenki.XmlObjectCodec$1.exceptionThrown(XmlObjectCodec.java:25)
    at java.beans.Encoder.getValue(Encoder.java:88)
    at java.beans.Encoder.get(Encoder.java:180)
    at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:94)
    at java.beans.Encoder.writeObject(Encoder.java:54)
    at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254)
    at java.beans.Encoder.writeExpression(Encoder.java:259)
    at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:369)
    at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:97)
    at java.beans.Encoder.writeObject(Encoder.java:54)
    at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254)
    at java.beans.Encoder.writeObject1(Encoder.java:186)
    at java.beans.Encoder.cloneStatement(Encoder.java:199)
    at java.beans.Encoder.writeExpression(Encoder.java:258)
    at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:369)
    at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:97)
    at java.beans.Encoder.writeObject(Encoder.java:54)
    at java.beans.XMLEncoder.writeObject(XMLEncoder.java:254)
    at java.beans.Encoder.writeExpression(Encoder.java:259)
    at java.beans.XMLEncoder.writeExpression(XMLEncoder.java:369)
    at java.beans.PersistenceDelegate.writeObject(PersistenceDelegate.java:97)
    ...

2011-12-15 (木) [長年日記] この日を編集

[git] svn間のマージにgit svnを利用する

svnを2つ用意。

(svnレポジトリ)
$ mkdir -p "/home/manhole/svnrepo/"
$ svnadmin create "/home/manhole/svnrepo/repo1"
$ svnadmin create "/home/manhole/svnrepo/repo2"

(svn work)
$ mkdir -p "/home/manhole/svnwork/work1"
$ mkdir -p "/home/manhole/svnwork/work2"

(workへそれぞれチェックアウトして、)
$ svn checkout "file:///home/manhole/svnrepo/repo1" "/home/manhole/svnwork/work1"
Checked out revision 0.
$ svn checkout "file:///home/manhole/svnrepo/repo2" "/home/manhole/svnwork/work2"
Checked out revision 0.

(それぞれのworkへ初期コミット。※コミットがないとgit svnで取得できない)
$ cd /home/manhole/svnwork/work1
$ touch readme.txt
$ svn add readme.txt
A         readme.txt
$ svn commit -m "initial work1"
Adding         readme.txt
Transmitting file data .
Committed revision 1.

cd /home/manhole/svnwork/work2
touch readme.txt
$ svn add readme.txt
A         readme.txt
$ svn commit -m "initial work2"
Adding         readme.txt
Transmitting file data .
Committed revision 1.

両方を同期するための作業用gitを用意

$ mkdir -p /home/manhole/gitwork
$ cd /home/manhole/gitwork

(-Rと--prefixがキモ)
$ git svn init -R svn1 --prefix svn1/ "file:///home/manhole/svnrepo/repo1"
Initialized empty Git repository in /home/manhole/gitwork/.git/
$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
[svn-remote "svn1"]
        url = file:///home/manhole/svnrepo/repo1
        fetch = :refs/remotes/svn1/git-svn

(1つ目とは異なる-Rと--prefixを指定する)
$ git svn init -R svn2 --prefix svn2/ "file:///home/manhole/svnrepo/repo2"
$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
[svn-remote "svn1"]
        url = file:///home/manhole/svnrepo/repo1
        fetch = :refs/remotes/svn1/git-svn
[svn-remote "svn2"]
        url = file:///home/manhole/svnrepo/repo2
        fetch = :refs/remotes/svn2/git-svn

それぞれのsvnからfetchする。

$ cd /home/manhole/gitwork
$ git svn fetch svn1
        A       readme.txt
r1 = 4b125ddaa2608230293af3296e7bce21f22e5b02 (refs/remotes/svn1/git-svn)
Checked out HEAD:
  file:///home/manhole/svnrepo/repo1 r1
$ git svn fetch svn2
        A       readme.txt
r1 = ff7d3a46dddfedf20abf8bb4b8b1f543d37f4e67 (refs/remotes/svn2/git-svn)

$ git branch -a
 * master
   remotes/svn1/git-svn
   remotes/svn2/git-svn

fetchしたsvnを、手元のbranchにそれぞれ持つ。

$ git checkout -b svn1 svn1/git-svn
Switched to a new branch 'svn1'
$ git checkout -b svn2 svn2/git-svn
Switched to a new branch 'svn2'
$ git branch -a
   master
   svn1
 * svn2
   remotes/svn1/git-svn
   remotes/svn2/git-svn

これで、2つのsvnレポジトリと、それぞれを手元で作業するためのworkができた。

svn1レポジトリ側へ色々変更が入ったとして...

cd /home/manhole/svnwork/work1
echo "aaa" > file1.txt

$ svn add file1.txt
A         file1.txt
$ svn commit -m "file1.txtを追加"
Adding         file1.txt
Transmitting file data .
Committed revision 2.

$ echo "bbb" > file2.txt
$ svn add file2.txt
A         file2.txt
$ svn commit -m "file2.txtを追加"
Adding         file2.txt
Transmitting file data .
Committed revision 3.

svn1側の変更をsvn2へ反映するため、gitへ取得する。

$ cd /home/manhole/gitwork
$ git checkout svn1
Switched to branch 'svn1'
$ git svn rebase
        A       file1.txt
r2 = 7c5288c257c8565495dfc3b78cc6aec97886e154 (refs/remotes/svn1/git-svn)
        A       file2.txt
r3 = 79b5fb01fb74585a507d941c6b270a2d8a8d4dad (refs/remotes/svn1/git-svn)
First, rewinding head to replay your work on top of it...
Fast-forwarded svn1 to refs/remotes/svn1/git-svn.

svn1側の変更をsvn2ブランチへmergeする。
※コミットを1つにまとめたくない場合は、svn mergeではなくコミットを1つずつ適用する手段にすること。

$ git checkout svn2
Switched to branch 'svn2'
$ git merge svn1
Merge made by recursive.
 file1.txt |    1 +
 file2.txt |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)
 create mode 100644 file1.txt
 create mode 100644 file2.txt

svn2側をsvnレポジトリへcommitする。

(svn2ブランチにいる状態で)
$ git svn dcommit
Committing to file:///home/manhole/svnrepo/repo2 ...
        A       file1.txt
        A       file2.txt
Committed r2
        A       file2.txt
        A       file1.txt
r2 = d5ac5e543e1f0fac9a809c62b13c69f6c45c8d1b (refs/remotes/svn2/git-svn)
No changes between current HEAD and refs/remotes/svn2/git-svn
Resetting to the latest refs/remotes/svn2/git-svn

この時点でのgitの状態は...

(alias.graph=log --graph --date-order -C -M --pretty=format:"<%h> %ad [%an] %Cgreen%d%Creset %s" --all --date=short)
$ git graph
 *   <d5ac5e5> 2011-12-16 [manhole]  (HEAD, svn2/git-svn, svn2) Merge branch 'svn1' into svn2
 |\
 | * <79b5fb0> 2011-12-16 [manhole]  (svn1/git-svn, svn1) file2.txtを追加
 | * <7c5288c> 2011-12-16 [manhole]  file1.txtを追加
 * | <ff7d3a4> 2011-12-16 [manhole]  initial work2
  /
 * <4b125dd> 2011-12-16 [manhole]  (master) initial work1

$ git diff svn1 svn2
$ (差分なし)

これをsvn2レポジトリへ反映する。

$ cd /home/manhole/svnwork/work2
$
$ svn update
Updating '.':
A    file2.txt
A    file1.txt
Updated to revision 2.

$ svn log
------------------------------------------------------------------------
r2 | manhole | 2011-12-16 14:58:34 +0900 (Fri, 16 Dec 2011) | 1 line

Merge branch 'svn1' into svn2
------------------------------------------------------------------------
r1 | manhole | 2011-12-16 14:42:43 +0900 (Fri, 16 Dec 2011) | 1 line

initial work2
------------------------------------------------------------------------

2011-12-07 (水) [長年日記] この日を編集

[Java]サロゲートペアを含むStringでのsubstring

メモ。

public static String substring(final String str, final int beginIndex, final int endIndex) {
    final int actualBegin = str.offsetByCodePoints(0, beginIndex);
    final int actualEnd = str.offsetByCodePoints(0, endIndex);
    final String s = str.substring(actualBegin, actualEnd);
    return s;
}

参照:


2011-10-06 (木) [長年日記] この日を編集

[groovy] ".svn"ディレクトリだけが存在するディレクトリを削除する

import java.io.File;

// closure
deleteClosure = { dir ->
    //println dir
    if (dir.name == ".svn") {
        return;
    }
    dir.eachDir(deleteClosure)
    def files = dir.listFiles();
    if (files.length == 1 && files[0].directory && files[0].name == ".svn") {
        println "削除します  : ${dir}"
        dir.deleteDir();
    } else {
        println "削除しません: ${dir}"
    }
}

if (args.length != 1) {
    println "対象ディレクトリを指定してください"
    return
}
println Arrays.asList(args)

deleteClosure(new File(args[0]))

バージョン1.7.5で確認。

D:\project\groovy-script\src\main\groovy\file>groovy -v
Groovy Version: 1.7.5 JVM: 1.6.0_20

2011-09-28 (水) [長年日記] この日を編集

[Oracle] SCOTT/TIGERのEMPとDEPTはどこへ?

Oracle Databaseに付属するdemobld.sql(Oracle Database 10g以降ではutlsampl.sql)を実行すると「EMP」「DEPT」というふたつのテーブルと「SCOTT/TIGER」というスキーマよりなる伝統的なデモ環境が構築される。

Oracle 10g XEでは、このSQLファイルですね。
/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/rdbms/admin/utlsampl.sql


1999|01|02|
2004|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|07|08|09|10|11|12|
2010|01|04|07|
2011|04|05|06|09|10|12|
2012|01|
今日: , 昨日: , 合計: