7月 132012
 

Each class variable, instance variable, or array component is initialized with a default value when it is created (§15.9, §15.10):

For type byte, the default value is zero, that is, the value of (byte)0.

For type short, the default value is zero, that is, the value of (short)0.

For type int, the default value is zero, that is, 0.

For type long, the default value is zero, that is, 0L.

For type float, the default value is positive zero, that is, 0.0f.

For type double, the default value is positive zero, that is, 0.0d.

For type char, the default value is the null character, that is, ‘\u0000’.

For type boolean, the default value is false.

For all reference types (§4.3), the default value is null.

booleanはfalse、参照型はnull、その他プリミティブ型は0がデフォルト値。

7月 132012
 
  • JDBC APIではPreparedStatementインタフェースを用いてパラメタライズドクエリを実行する
  • OracleJDBCドライバではOraclePreparedStatementクラスを用いてパラメタライズドクエリを実行する
  • OraclePreparedStatementのsetStringメソッドおよびsetCharメソッドは空白埋めを行わない
  • OraclePreparedStatementのsetFixedCharメソッドは空白埋めを行う
  • JDBCのPreparedStatementインタフェースにはsetCharおよびsetFixedCharメソッドは定義されていない
    (これらはOracleのJDBC拡張機能を用いる事で実装できる)

要するに・・・

CHAR(5)型のCOL1列を持ったテーブルTABLE_Aに対して
[sql]SELECT * FROM TABLE_A WHERE COL1=?[/sql]
というSQLに比較文字列をバインドしてを発行する時、setString(1, "ABC")とするとCOL1='ABC'という比較がされ、確実に0件。
setString(1, "ABC ")としなければならない。

OracleParameterdStatement.setFixedChar(int, String)を使用することにより、空白埋め比較でバインドする事も出来るが、DBに依存したコードとなる。

ちなみにバインドをしないでリテラルで比較するとヒットする。
[sql]SELECT * FROM TABLE_A WHERE COL1=’ABC'[/sql]
これはOracleでは両辺にVARCHAR2型またはNVARCHAR2型が使用されている場合以外は、空白埋めで比較が行われるためである。

そもそも可変文字列の格納にCHAR型を使うのをやめればすむ話。。

参考:

7月 132012
 

※憶測入り。特にデシリアライズの時の判断基準とか適当。

結論から言うと、普通は指定しなくて大丈夫。
シリアライズする必要性のあるクラスの場合、指定したほうが環境問題に悩まされずにすむのでベター。

serialVersionUIDとわ

端的にいえば、シリアライズされたオブジェクトを送る側と受け取る側で、どのクラスのインスタンスなのかを識別するためのID。

いろいろ
  • どうやらクラス名とクラスメンバとserialVersionUID(以下SUID)で判別してるようだ。
  • 送られてきたクラス定義とSUIDが、受け取る側が参照できるクラスと同じなら、互換性があるクラスだと判断して、オブジェクトをそのクラスを使用して解析するんだと思う。
  • 定義とSUIDが同じだけど実は互換性がない(実装が違う)場合、変換時に例外が出る。
  • 指定しないとSHA-1でクラスのハッシュから計算されるっぽい。
  • SUID自動計算の方法は環境によって違うっぽいので自分で指定したほうがベター。
  • Serializableインタフェースだと勝手に計算してくれる。
  • Externalizableインタフェースだと自分で指定しないとだめぽい。
  • Exceptionの場合、ThrowableインタフェースがSerializableインタフェースを実装してるので、SUIDが求められる。
おまけ

serialverコマンドでSUID自動計算の結果が見れる。

ざっと試した感じ、以下の情報できまるっぽい。

  • クラス名
  • クラスのフィールド(プロパティ、メンバ変数)全部
  • private以外のメソッド

実装コードは関係なかった。
まぁシリアライズする必要性のあるクラスにはやはり自分で指定したほうがいいと思う。

参照
7月 132012
 
  • Oracle10gのJDBCは、DATE型項目の場合java.sql.Date型で返却するために時刻が欠落する。
  • Oracle9.0.1より前のJDBCではjava.sql.Timestamp型で返却していたため時刻は欠落しない。
  • Oracle11gもjava.sql.Timestamp型で返却する仕様をデフォルトとしているため時刻は欠落しない。

  • Oracle10gの場合、oracle.jdbc.V8Compatible=trueを指定することでjava.sql.Timestamp型で返却する動作となる。

参考:

Oracleオブジェクト型からSQL DATEデータ型へのマッピング

Oracle Database 8i以下のバージョンではTIMESTAMPデータがサポートされていませんでしたが、SQL標準への拡張機能として、Oracle DATEデータに時刻コンポーネントが含まれていました。このため、Oracle Database 8i以下のバージョンのJDBCドライバは、oracle.sql.DATEをjava.sql.Timestampにマップして時刻コンポーネントを保持していました。Oracle Database 9.0.1以上ではTIMESTAMPがサポートされ、9iのJDBCドライバがoracle.sql.DATEをjava.sql.Dateにマッピングするようになりました。このマッピングはOracle DATEデータの時刻コンポーネントを切り捨てたため、不正確でした。この問題を克服するために、Oracle Database 11.1では新しいフラグmapDateToTimestampが導入されました。このフラグのデフォルト値はtrueです。これは、デフォルトで、ドライバがoracle.sql.DATEをjava.sql.Timestampに正確にマップして時刻情報を保持することを意味します。不正確でも10gと互換性のあるoracle.sql.DATEからjava.sql.Dateへのマッピングが必要な場合は、mapDateToTimestampフラグの値をfalseに設定します。

注意:
oracle.sql.DATEからjava.sql.Dateへのマッピングの問題を克服するために、Oracle Database 9.2でフラグV8Compatibleが導入されました。このフラグのデフォルト値はfalseで、これはOracle DATEデータのjava.sql.Dateデータへのマッピングが許可されることを意味します。ただし、このフラグの値をtrueに設定することによって、ユーザーはOracle DATEデータの時刻コンポーネントを保持することができました。このフラグは11gでは非推奨です。これは、Oracle Database 8iとの互換性がサポートされなくなったためです。

Oracleからの回答:

QUESTION
======
oracle.jdbc.V8Compatible=trueにすることで getObject()の戻り値の型がDate→Timestamp と変更される以外に動作が変わることがあるか。

ANSWER
======
ございません。

oracle.jdbc.V8Compatible=true に設定することによる動作の変更点はgetObject()の戻り値の型が Date→Timestamp と変更されるだけでございます。

7月 132012
 

DBへの型のマッピングは気をつけないとダメだよという話。

予備知識とか
  • ANSI SQLのDATE型は日付のみ保持する。
  • OracleのDATE型は日付だけでなく時刻まで保持する。(秒未満の精度とタイムゾーンは保持しない)
DATE型@Java
  • java.sql.DateクラスはANSI準拠。日付までしか保持しない
  • java.util.Dateクラスはミリ秒まで保持する。
  • JDBCではjava.sql.DateクラスはDATE型(JDBC)にマッピングされる。
  • JDBCではjava.util.DateクラスはDATE、TIME、TIMESTAMP型(JDBC)にマッピングされる。
問題例とか
  • JavaでOracleに日付を登録する場合、やり方によっては時刻情報が落ちる。
  • java.sql.Dateのコンストラクタはlong型なミリ秒を受け取れるが、残るのは日付のみ。

参考:

7月 122012
 

RSAの話。

modulusをpublicKey.getModulus().toByteArray()で取得した際くっついてくる符号ビットが曲者。。
※BigInteger#toByteArrayは符号ビット付き2の補数表現となる
符号ビット1bit + モジュラス本体1024bit(鍵長1024bitの場合) → 129ByteのByte配列
となるため、C#のRSAParameters.Modulusに突っ込むには先頭の符号ビットに相当する1バイトを削って渡す必要がある。
ヘルプに何にも書いてないので謎だが、符号なしにしないとだめなようで。。
as3cryptoはそのまま突っ込んでも大丈夫なのに・・・

PrivateExponentはJavaでしか処理しないためそのままでも問題なく、PublicExponentについては17bit(65537固定)のデータのため、符号ビット”0″が付加されても無害。
(modulusは1024bit(8bitの倍数)なため符号ビットを付加するとあふれて129Byteになってしまうのが今回の場合問題となる)

キーペア作成@Java
[java]
//鍵作る
KeyPairGenerator generator = KeyPairGenerator.getInstance(“RSA”);
generator.initialize(2048);
KeyPair keyPair = generator.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate(); //Javaでとっとく
RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();

//modulusの先頭符号ビットを除去
byte[] modulusBytes = stripLeadingZeros(publicKey.getModulus().toByteArray());

//Bse64エンコードしてC#へわたす
BASE64Encoder encoder = new BASE64Encoder();
String modulus = encoder.encode(modulusBytes); //→C#に渡す
String publicExponent = encoder.encode(publicKey.getPublicExponent().toByteArray()); //→C#に渡す
[/java]

暗号化@C#
[csharp]
//鍵の取り込み
RSAParameters rsaParams = new RSAParameters();
rsaParams.Modulus = Convert.FromBase64String(modulus); //Javaから受け取る
rsaParams.Exponent = Convert.FromBase64String(exponent); //Javaから受け取る

RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider();
rsaProvider.ImportParameters(rsaParams);

//暗号化してJavaに渡す
byte[] targetBytes = new UTF8Encoding().GetBytes(target); //target:暗号化対象文字列。UTF-8でバイト配列化
//とりあえずPKCS#1パディング。第二引数をtrueにすればOAEPパディングも使える。
byte[] resultBytes = rsaProvider.Encrypt(targetBytes, false);
string result = Convert.ToBase64String(resultBytes); //Javaに返す
[/csharp]

復号化@Java
[java]
BASE64Decoder decoder = new BASE64Decoder();
byte[] src = decoder.decodeBuffer(cryptedData); //cryptedData:C#から受け取る

//ここでパディングや暗号化サービスプロバイダを指定できる。
//”RSA”は”RSA/ECB/PKCS1Padding”と同じ。
Cipher cipher = Cipher.getInstance(“RSA”);
cipher.init(Cipher.DECRYPT_MODE, privateKey); //とっておいた秘密鍵で初期化
byte[] resultBytes = cipher.doFinal(src);
String result = new String(resultBytes, “UTF-8”); //復号結果。UTF-8で復元
[/java]

7月 112012
 

※要参照:さくらのレンタルサーバーでのdaemon実行は禁止?

さくらのレンタルサーバーはJavaが入っていないが、自分で入れればJenkinsを動かす事が出来る。

1. Javaを入れる
  1. feebsdfoundationのJREをダウンロード(ちょっと古いので、OpenJDKとかを自分でビルドした方がいいかも?)
    FreeBSD Foundation Java Downloads
  2. 適当なディレクトリに解凍
[shell]
bzip2 -d diablo-latte-freebsd7-i386-1.6.0_07-b02.tar.bz2
tar xvf diablo-latte-freebsd7-i386-1.6.0_07-b02.tar
[/shell]
2. Jenkinsセットアップ
  1. war版Jenkinsをダウンロード
  2. 適当なディレクトリに配置
  3. 下記のような起動/終了スクリプト作成
  4. start-jenkins.shを実行して起動、stop-jenkins.shを実行して終了する事を確認
[shell title=”start-jenkins.sh”]
#!/usr/local/bin/bash
JENKINS_WAR=~/jenkins/jenkins.war
JENKINS_LOG=~/jenkins/jenkins.log
JAVA=~/java/diablo-jre1.6.0_07/bin/java
nohup $JAVA -jar $JENKINS_WAR > $JENKINS_LOG 2>&1 &
[/shell]
[shell title=”stop-jenkins.sh”]
#!/usr/local/bin/bash
kill `ps -ef | grep ‘[j]enkins.war’ | awk ‘{ print $1 }’`
[/shell]

※JENKINS_HOMEは~/.jenkinsとなる。

おまけ1. cron登録

マシンが再起動されても立ち上がるよう設定する。
効果があるかは試してないので不明…
[shell]
crontab -e
[/shell]

@reboot ~/jenkins/start-jenkins.sh >/dev/null 2>&1
おまけ2. Apacheからリダイレクトされるようにする

面倒だから試してないけど出来るかもしれない。
.htaccessでmod_rewiteが利用できるため、うまくルールを作ればリダイレクトできる可能性あり。

参考:
さくらレンタルサーバで java を利用する
configuring ProxyPass on .htaccess to show tomcat through apache http server – Stack Overflow
【さくら共用】RewriteRuleで静的URLにしようとしたら404エラー | Server Hosting Guide
システム起動時に特定のコマンドを実行するには - @IT
再起動時に一度だけ実行されるcron定義
Starting and Accessing Jenkins – 日本語 – Jenkins Wiki
webhtm.net日記: psをgrepした結果にgrep自身のプロセスを出力させない
プログラムを作ってます さくらのレンタルサーバでbashを使う

7月 062012
 

参照:weblogic.xmlデプロイメント記述子の要素

値-1の場合、ページのチェックは行われません。この値は、本番環境でのデフォルト値。

値0の場合、ページは常にチェックされます。

値1の場合、ページは毎秒チェックされます。この値は、開発環境でのデフォルト値。

デフォルト値1となっているが、環境によっては-1であることもある模様。
まぁ触った環境がそうだったから書いてるんだけど。。

以下のようなweblogic.xmlを記述し、デプロイすればOK。
[xml title=”weblogic.xml” mark=”4″]


1

[/xml]