2015年5月26日火曜日

Java1.7 Compile で検知できないキャストエラーについて

Java1.6 で動作しているJenkins で、ビルドがコケた。
理由は、キャストエラー。これをJava1.7 のデフォルトではコンパイルエラーとして表示してくれないため、開発環境ではエラーがでないが、Java1.6 でBuild したらコケる。


以下、テストコード
package test;

import java.util.Map;
import java.util.HashMap;

public class Test {
    public static void main(String args[]) {
     Map result = new HashMap();
     result.put("key",new Integer(2));
     int errorCode = (int)result.get( "key" );
     System.err.println("errorCode: " + errorCode);
    }
}

で、問題は以下の部分。
int errorCode = (int)result.get( "key" );

以下のようにキャストすべき。
int errorCode = (Integer)result.get( "key" );

-source option に1.6 を指定すると、キャスト失敗。
javac -source 1.6 -bootclasspath "C:\Program Files\Java\jdk1.7.0_67\jre\lib\rt.jar" Test.java
Test.java:11: エラー: 変換できない型
        int errorCode = (int)result.get( "key" );
                                       ^
  期待値: int
  検出値:    Object
エラー1個

しかし、source を1.7 に指定すれば、コンパイルに問題は起きない。
javac -source 1.7 -bootclasspath "C:\Program Files\Java\jdk1.7.0_67\jre\lib\rt.jar" Test.java

で、開発中にどうやってこれを検知するか(コンパイルエラーとして表示させるか)という点ですが、 Eclipse の以下の設定で、実行環境をJava1.7 から、1.6 に変更すれば検知可能です。
Right click on the project > Build Path > Java Build Path > Libraries

2015年5月18日月曜日

XMLStreamReader が持つXML をファイル出力する場合

[ 解決課題 ]
XML に問題があり、ファイル退避、かつ、問題箇所以降のXML を読み込みたい。

[ 検討結果 ]
ファイル退避、もしくはXML の読み込みのどちらかを選択する必要がある。 両方実施したい場合は、自前パーサーを作成する必要がある。

[ 処理内容 ]
前提として、reader は元から存在するものとし、XML 情報を持つとする。
 StringWriter sw と、XMLStreamWriter writer を準備して、Transformer class を利用して、 transform method をCall するとsw にXML 情報が入力される。
これをtoString() してあげれば、XML の出力が可能。

 しかし、問題点があり、transform() をCall すると、XMLStreamReader reader の情報がすべて失われてしまう。
そのため、XML のある1箇所に問題があり、すべてのXML を出力、かつ、問題の箇所以降のXML を再読み込みすることができない。
 DeepCopy もできないため(*1)、2回XML の読み込みをしたい場合は、StringWriter にすべて書きだした後に、 自前のパーサーを作って処理を実行しないといけない。

*1) http://stackoverflow.com/questions/9837861/how-to-remember-xmlstreamreader-position-in-xml-with-stax

public void parse(XMLStreamReader reader){
 StringWriter sw = new StringWriter();
 StAXSource source = new StAXSource( reader );
 XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
 XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
 XMLStreamWriter writer;
 String rawxml = null;
 try {
     XMLStreamReader re = xmlInputFactory.( reader );
     writer = xmlOutputFactory.createXMLStreamWriter( sw );
     TransformerFactory tf = TransformerFactory.newInstance();
     Transformer t = tf.newTransformer();
     StAXResult result = new StAXResult( writer );
     t.transform( source, result );
     rawxml = sw.toString();
    } catch ( XMLStreamException e ) {
     e.printStackTrace();
    } catch ( TransformerConfigurationException e ) {
     e.printStackTrace();
    } catch ( TransformerException e ) {
     e.printStackTrace();
    }
} 

2015年5月8日金曜日

Java でClose したPort にアクセスした場合について

閉じているポートにアクセスすると、以下のException がでる。
java.net.ConnectException: Connection refused: connect
 at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
 at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
 at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
 at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
 at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
 at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
 at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
 at java.net.Socket.connect(Socket.java:579)

では、これはなぜおこるのか、というと以下の記載がありました。
http://docs.oracle.com/cd/E19455-01/806-2720/msgs-175/index.html

[ 抜粋 ]
[ 原因 ]
対象のマシンが拒否したため、接続できません。
アクティブでないサービスに接続しようとした場合、または要求したアドレスにサービスプロセスが存在しなかった場合に起こります。

[ 対処方法 ]
対象のマシン上のサービスをアクティブにするか、またはサービスがなくなっていた場合は再度起動します。
セキュリティ上の理由からこのサービスを提供したくない場合は、ユーザーグループにそのことを伝え、できれば代替サービスを提供します。

では、なぜConnection refused になるのでしょうか?
その答えは以下に有りました。
http://www5d.biglobe.ne.jp/stssk/rfc/rfc793j.html

[ 抜粋 ]
1.  If the connection does not exist (CLOSED) then a reset is sent
    in response to any incoming segment except another reset.  In
    particular, SYNs addressed to a non-existent connection are rejected
    by this means.

つまり、CLOSED の状態のポートにアクセスすると、リセットがレスポンスとしてアクセス元に送られるそうです。

で、リセットって何?とググると、以下が出てきました。
http://e-words.jp/w/RST%E3%83%91%E3%82%B1%E3%83%83%E3%83%88.html

[ 抜粋 ]
TCPで接続を中断・拒否する際に送られるパケット。
TCPヘッダの制御フラグでRSTフィールドがセットされたパケットのこと。
接続要求を拒絶したり、確立された接続を一方的に切断する際に送られるもの。

最後に、ポートを閉じている、とはどういう状態か、という点に関しては、RFC793 ではCLOSED の状態である、ということでした。