Oracleで「ORA-01882」が出たので対処した

障害内容

あるJavaアプリケーションを実行しようとしたら以下の例外が発生。

java.sql.SQLException: ORA-00604: 再帰SQLレベル1でエラーが発生しました。
ORA-01882: タイムゾーンのリージョンが見つかりません。

環境

OS
Red Hat Enterprise Linux Server release 5 (Tikanga)

先に結論

OSのタイムゾーンが変な値に設定されているので、「/usr/sbin/timeconfig」でタイムゾーンを変更。

結論に至るまでの過程。。。

まずはGoogle先生に聞いてみた。

共にオラクル社カスタマ・サポート・センターに問い合わせないと解決しないエラーです。
サポート契約してサポート・センターに連絡してください。

http://www.oracle.co.jp/forum/thread.jspa?threadID=3001706

連絡とかしてられないので自力で調べる。
とりあえず「date」コマンド実行

[yamap@xxxx ~]$ date
2012年  2月 29日 水曜日 08:49:26 SYOT

実行時の時間は14:49だったので6時間のずれ。
他のredhat環境でコマンド実行すると最後の文字列が「JST」となっていたので、
やっぱりOSのタイムゾーンがおかしい事が原因っぽい。

設定方法をGoogle先生に質問して実行。

Linux で、システムのタイムゾーンを設定する方法
http://perltips.twinkle.cc/server/linux_set_timezone.php

[root@xxxx ~]# mv /etc/localtime /etc/localtime.org
[root@xxxx ~]# ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

「date」コマンドの結果は直ってた。

[yamap@xxxx ~]$ date
2012年  2月 29日 水曜日 15:00:13 SYOT

が、javaから実行すると同じ障害発生。
javaだとタイムゾーンが変更されてない?って事で試す。

System.out.println(System.getProperty("user.timezone"));
[yamap@xxxx ~]# java Test
Antarctica/Syowa

残念。

更に検索したら「/usr/sbin/timeconfig」でタイムゾーンを変更できるとの記述を発見。

Q タイムゾーンを設定するには
http://tooljp.com/linux/faq/EC7B8AFE495087BE492575400061F3F9.html

設定後にjava、アプリケーションで確認したら例外でませんでした。

余談

直接は関係ないですがgroovyで実行した時とjavaで実行した時の結果が違ったのでだいぶはまった。

上記の流れで、javaで実行時に「Antarctica/Syowa」が出てた時に実行。

groovy:000> println System.getProperty("user.timezone")
Asia/Tokyo

groovyはどこからタイムゾーンを設定してるんだろう?
ちなみに、groovyのコードをコンパイルして実行すると空文字が表示されました。
(正しく動作していると思われる環境で実行しても空文字。)

Groovyがインストールされていない環境での実行ではまった

1.8から以下のように実行する事でGroovyがインストールされていない環境でもGroovyを直接実行することができるようになりました。
(1.7以下でもできたみたいですが、面倒だった?)

java -jar C:\groovy\groovy-1.8.6\embeddable\groovy-all-1.8.6.jar c:\work\test.groovy

先日「プログラミングGroovy」を読み直すまでこの事を気づかずに、毎回classにコンパイルしていた私は狂喜乱舞したんですが。。。

実は落とし穴があり、「CLASSPATH」の設定は無効になります。
勿論「-cp」で指定しても無効です。

どういうことかというと、こんな感じ。

C:\work\test.groovy

assert System.getProperty("java.class.path") == /C:\groovy\groovy-1.8.6\embeddable\groovy-all-1.8.6.jar/

こいつをクラスパスにその辺に転がっていたjarを指定して適当に実行してみると。。。

java -cp "C:\tomcat50\shared\lib\mysql-connector-java-5.1.7-bin.jar" -jar C:\groovy\groovy-1.8.6\embeddable\groovy-all-1.8.6.jar C:\work\test.groovy

C:\>java -cp "C:\mysql-connector-java-5.1.7-bin.jar" -jar C:\groovy\groovy-1.8.6\embeddable\groovy-all-1.8.6.jar C:\work\test.groovy

C:\>

正しく実行されちゃいます。(クラスパスに指定したjarが設定されていません)

これはjavaコマンド、「-jar」オプションの仕様のようです。

このオプションを使用すると、指定した JAR ファイルがすべてのユーザークラスのソースになり、ユーザークラスパスのほかの設定は無視されます。
http://java.sun.com/javase/ja/6/docs/ja/technotes/tools/windows/java.html

どうやら、OracleのBug Databaseにあがってるみたいです。
2001年にあがってますので、直す気ないんでしょうねぇ。。。

■まとめ
・外部jarが不要な場合
groovy-all-X.X.X.jarを使用して実行
例 : java -jar C:\groovy\groovy-1.8.6\embeddable\groovy-all-1.8.6.jar c:\work\test.groovy

・外部jarが必要な場合
コンパイルして実行

例 :

javac c:\work\test.groovy
java -cp "C:\mysql-connector-java-5.1.7-bin.jar" test

Groovyでインスタンス生成時のフィールドの初期化

Groovyではnewする際に、Mapを渡すことでフィールドに値を設定することができます。
当然渡されなかったフィールドは各型の初期値になります。
また、「TupleConstructor」というアノテーションを付けるとAST変換が行われ、コンストラクタのように引数を渡すことでフィールドに値を設定できます。(言い方変な気がする。。。)
尚、上から順に設定されているようで、型が異なると例外が発生しました。

※「ToString」アノテーションインスタンスを文字列に変換するための「toString」メソッドが追加されます。

Mapでフィールドに設定するというのは、コンストラクタを記述しなかった場合だったと思ったのですが、引数なしのコンストラクタだったら設定していても設定できちゃう。
追記:2012/02/19 19:58 ごめんなさい。マップでフィールドの初期化を行うコンストラクタを使用できるのは引数のないコンストラクタだけが定義されている場合のみです。(プログラミングGroovy P.49より)

ただ、引数なしのコンストラクタが定義されている場合にこのような形式でインスタンスを生成しようとすると、引数なしコンストラクタが呼ばれているようです。
引数のMapはHashMapを受け取るコンストラクタに渡されて、デフォルトコンストラクタが呼ばれた後にプロパティに設定されているんですかね?

id:uehaj が回答してくれました!
「マップを引数に取るコンストラクタ」は生成されず、引数なしのコンストラクタでインスタンスを生成したのちに、マップを使って個々のプロパティを初期化していくコードがItemの外側で実行されるようです。

リストの内容比較

しばらくGroovyをいじっていなかったのでリハビリがてらid:fumokmm さんのお題を解くよ!

お題:リストの内容比較
http://d.hatena.ne.jp/fumokmm/20101023/1287835025

二つのリスト(左・右)の内容を比較し、片方にしかないものと両方に在るものを出力するプログラムを書いて下さい。

条件
・リストの入力方法、出力方法は自由とします。
・入力されるリストの内容は未ソートかつ重複がある可能性を考慮して下さい。
・出力されるリストは、左のリストにのみある内容、右のリストにのみある内容、両方のリストにある内容の三種類とします。
・出力されるリストは辞書順ソートかつ重複は除去した形式として下さい。

サンプル入力リスト:
[listL] aaa bbb ccc bbb eee hhhh
[listR] bbb ddd eee fff ggg iiiii

サンプル出力リスト:
[left only] aaa ccc hhhh
[right only] ddd fff ggg iiiii
[both] bbb eee

また、余裕のある方は以下のように左右の内容を並べて出力して下さい。

サンプル出力リスト(余裕のある方用):
aaa  |
bbb  | bbb
ccc  |
     | ddd
     | eee
     | fff
     | ggg
hhhh |
     | iiiii


fumokmmさんの解答とほぼ同じ感じになったのは嬉しい!

ただ、おまけの解答を出すのにかなり時間がかかってしまったのが残念。

Listの範囲を指定する際に変数を使おうとしてしばらくハマったのでメモ

括弧です括弧!!

def list = [1,2,3,4,5]
def i = 2
def j = 3
assert list[(i)] == 3
assert list[(i)..(j)] == [3,4]

def map = [a:"aa", b:"bb", c:"cc"]
def s = "a"
assert map[(s)] == "aa"

前もmapの時にはまったんだよなぁ。。。括弧です括弧!!
GStringをよく使うせいか、map[${s}]みたいにやりたくなっちゃうのはなんでだろう。

文字列を先頭から見て同じところまで除去

ちょっと時間が開いてしまったけど、id:fumokmm さんが出題しているお題をこなしていくよ!

今回のお題はこちら↓
お題:文字列を先頭から見て同じところまで除去
http://d.hatena.ne.jp/fumokmm/20110812/1313138407

複数の文字列を受け取り、受け取った文字列をそれぞれ先頭から見てゆき、すべてが同じ内容であれば除去した内容の文字列を返却する関数を書いて下さい。
※関数の引数と戻り値については複数の文字列が受け渡しできれば型や方法は問いません*1。

例1)hoge("abcdef", "abc123")
戻り値 => "def" と "123" ("abc" が除去される)

例2)hoge("あいうえお", "あいさんさん", "あいどる")
戻り値 => "うえお", "さんさん", "どる" ("あい" が除去される)

例3)hoge("12345", "67890", "12abc")
戻り値 => "12345", "67890", "12abc" (一致なしのため、そのまま返却される)

最新の回答↓

↑がでるまでの経緯

とりあえず問いてみた。
(出題直後に問いてたんだけど、仕様を間違えて一旦置いといたのは秘密)

List method(String... strs) {
  def f = { list ->
      if (list.contains("") || list.collect{it[0]}.unique().size() != 1) {
        return list
      } else {
        return call(list.collect{it.substring(1)})
      }
  }
  f(strs as List)
}

assert method("abcdef", "abc123") == ["def", "123" ]
assert method("あいうえお", "あいさんさん", "あいどる") == ["うえお", "さんさん", "どる" ]
assert method("12345", "67890", "12abc") == ["12345", "67890", "12abc"]
// 追加したテスト
assert method("12345", "12", "12abc") == ["345", "", "abc"]

かなり冗長。
というか、無理やり感に溢れてるw
メソッドを呼び出してるのに中でクロージャー作ってる辺りとか。。。
(でもListにしないと色んなメソッド使えない。。。)
returnが2つもある所とか。。。

後、問題の例にはなかったですが全て除去される文字の場合も対処してます。
(list.contains("")の所)

元が元ですが、ワンライナーにしてみる。

def m(String... s){f={(it.contains("")||it.collect{it[0]}.unique().size()-1)?it:call(it.collect{it.substring(1)})};f(s as List)}

assert m("abcdef", "abc123") == ["def", "123" ]
assert m("あいうえお", "あいさんさん", "あいどる") == ["うえお", "さんさん", "どる" ]
assert m("12345", "67890", "12abc") == ["12345", "67890", "12abc"]
assert m("12345", "12", "12abc") == ["345", "", "abc"]

あれ?なんかそれっぽく見えるw
一応条件を「!=1」じゃなくて「-1」にして1バイト削ったり努力は認めて欲しい。
(0の場合はfalseと判断される)

さぁて、他の人の答え見てこよー♪

追記(2011/08/20 0:47)

色々な人の回答を見て気づく。

ループ使えよループwww
最初forを使ってた気がするんだけどなんでこうなったんだろうw

という事で修正したのが冒頭のコード。
かなりわかりやすく、綺麗になった感じ?

皆様方が使っていた「*.」も使ってみました。(collectと同じ)
が、やはり追加したテストの部分がネックで長くなってしまう。。。
明日、もう一回改良してみる予定。

ワンライナーにしてみようとしたけど、returnが途中に入ってるからかうまくできず。
こちらも明日やってみます。

追記(2011/08/20 11:52)

id:fumokmm さんのコードに載ってたテスト流したら、空文字入ってると例外発生。
後で修正!
というか、fumokmmさんのコードやっぱり凄いなぁ。。。