トップ 履歴 一覧 カテゴリ ソース 検索 ヘルプ RSS ログイン

Source/Java/ObjSend

INDEX

Javaオブジェクト双方向通信

HttpURLConnection と HttpServlet を利用した、オブジェクトの双方向通信例です。

サーブレット側

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * $Id: InteractiveCommunicationServlet.java,v 0.0 2008/10/25 11:31:41 t-imamura Exp $
 *
 * Copyright (c) 2008 t-imamura, All rights reserved.
 */
package objsend;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * オブジェクト双方向通信 サーバ(HttpServlet)サイド.
 */
public class InteractiveCommunicationServlet extends HttpServlet {

    /** Serial Version */
    private static final long serialVersionUID = 1L;

    /**
     * コンストラクタ.
     */
    public InteractiveCommunicationServlet() {
    }

    /**
     * サーブレットを初期化します。
     * @see javax.servlet.GenericServlet#init()
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig SC) throws ServletException {
        super.init(SC);
    }

    /**
     * サーブレットを破棄します。
     * @see javax.servlet.GenericServlet#destroy()
     */
    @Override
    public void destroy() {
        super.destroy();
    }

    /**
     * オブジェクトの送受信を行う。
     * @param   req HTTPリクエスト
     * @param   res HTTPレスポンス
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

        Object reqData = null;
        Object resData = null;

        // オブジェクトストリームから要求オブジェクトの受信
        ObjectInputStream in = null;
        try {
            String encoding = req.getHeader("Content-Encoding");
            if("gzip".equals( encoding ) ){
                // リクエストヘッダの "Content-Encoding" が "gzip" だったらGZIP圧縮されている
                in = new ObjectInputStream(new GZIPInputStream(new BufferedInputStream(req.getInputStream())));
            }else{
                in = new ObjectInputStream(new BufferedInputStream(req.getInputStream()));
            }
            reqData = (Object)in.readObject();
        } catch (ClassNotFoundException e) {
            // オブジェクトストリームから要求オブジェクトが復元できない
            throw new ServletException("a request object of input stream is not found.", e);
        } catch (ObjectStreamException e) {
            // オブジェクトストリームから要求オブジェクトが復元できない
            throw new ServletException("a request object of input stream is not restorable.", e);
        } catch (IOException e) {
            throw e; // IOException はそのままサーブレットに通知
        } finally {
            // オブジェクトストリームのクローズ
            if (in  != null) {
                in.close();
                in  = null;
            }
        }

        // 要求の実行と結果の取得
        resData = execute(reqData);

        // オブジェクトストリームへ応答オブジェクトの送信
        ObjectOutputStream out = null;
        try {
            res.setContentType("application/octet-stream");

            String encoding = req.getHeader("Accept-Encoding");
            if( encoding != null && encoding.indexOf("gzip") != -1 ){
                res.setHeader("Content-Encoding", "gzip");
                out = new ObjectOutputStream(new GZIPOutputStream(new BufferedOutputStream(res.getOutputStream())));
            }else{
                out = new ObjectOutputStream(new BufferedOutputStream(res.getOutputStream()));
            }

            out.writeObject(resData);
            out.flush();

        } catch (ObjectStreamException e) {
            // 応答オブジェクトを直列化してオブジェクトストリームに渡せない
            throw new ServletException("a response object of output stream is not serialize.", e);
        } catch (IOException e) {
            throw e; // IOException はそのままサーブレットに通知
        } finally {
            // オブジェクトストリームのクローズ
            if (out != null) {
                out.close();
                out = null;
            }
        }

    }

    /**
     * 処理を行います。
     * @param reqData 要求オブジェクト
     * @return 応答オブジェクト
     */
    protected Object execute(Object reqData) {
        return reqData;
    }

}

クライアント側

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/*
 * $Id: InteractiveCommunicationClient.java,v 0.0 2008/10/25 11:31:41 t-imamura Exp $
 *
 * Copyright (c) 2008 t-imamura, All rights reserved.
 */
package objsend;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

/**
 * オブジェクト双方向通信 クライアント(HttpURLConnection)サイド.
 */
public class InteractiveCommunicationClient {

    /** サーブレットURL */
    private URL servlet;
    /** GZIP圧縮を行うか */
    private boolean compress = true;

    /** ホスト名検証 */
    private HostnameVerifier hostVerifier = null;
    /** SSLソケットの作成 */
    private SSLSocketFactory sslFactory = null;
    /** 信頼マネージャ */
    private TrustManager[] trustManager = null;
    /** 鍵マネージャ */
    private KeyManager[] keyManager = null;

    /**
     * コンストラクタ.
     * @param servlet サーブレットURL
     * @param compress データの圧縮をするか
     */
    public InteractiveCommunicationClient(URL servlet, boolean compress) {
        this.servlet = servlet;
        this.compress = compress;
    }

    /**
     * セキュア通信を行うための証明書のロード.<br>
     * キーストアが<code>null</code>、ロードしない場合は、Javaのデフォルトを使用
     * @param store キーストアのストリーム
     * @param storePass キーストアのパスワード
     */
    public void loadTrusts(InputStream store, String storePass) {
        if(store == null){
            setTrustManagers(null);
            return;
            //throw new IllegalArgumentException("InputStream of KeyStore is null.");
        }
        if(storePass == null){
            throw new IllegalArgumentException("KeyStore password is null.");
        }

        // 信用する証明書のロード
        try{
            // キーストアのインスタンスを作成
            KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
            // キーストアをロードする
            ks.load(store, storePass.toCharArray());
            // 信頼マネージャのファクトリを作成 (DefaultAlgorithm=PKIX)
            TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
            // 信頼マネージャのファクトリの初期化
            tmf.init(ks);
            // 信頼マネージャをセット
            setTrustManagers(tmf.getTrustManagers());
        } catch (GeneralSecurityException e) {
            throw new RuntimeException("A certificate cannot be loaded.", e);
        } catch (IOException e) {
            throw new RuntimeException("A certificate cannot be loaded.", e);
        }
    }

    /**
     * 信頼マネージャを設定します。
     * @param trustManager 信頼マネージャ
     */
    public void setTrustManagers(TrustManager[] trustManager) {
        this.trustManager = trustManager;
        this.sslFactory = null;
    }

    /**
     * セキュア通信を行うための鍵のロード.<br>
     * キーストアが<code>null</code>、ロードしない場合は、Javaのデフォルトを使用
     * @param store キーストアのストリーム
     * @param storePass キーストアのパスワード
     * @param keyAlias 使用する秘密鍵の別名 ※将来拡張用 現状では使用されません。
     * @param keyPass 鍵のパスワード
     */
    public void loadKeys(InputStream store, String storePass, String keyAlias, String keyPass) {
        if(store == null){
            setKeyManagers(null);
            return;
            //throw new IllegalArgumentException("InputStream of KeyStore is null.");
        }
        if(storePass == null){
            throw new IllegalArgumentException("KeyStore password is null.");
        }
        if(keyPass == null){
            throw new IllegalArgumentException("Key password is null.");
        }

        // 認証用の鍵のロード
        try {
            // キーストアのインスタンスを作成
            KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
            // キーストアをロードする
            ks.load(store, storePass.toCharArray());
            // 鍵マネージャのファクトリを作成 (DefaultAlgorithm=SunX509)
            KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
            // 鍵マネージャのファクトリの初期化
            kmf.init(ks, keyPass.toCharArray());
            // 鍵マネージャをセット
            setKeyManagers(kmf.getKeyManagers());
        } catch (GeneralSecurityException e) {
            throw new RuntimeException("A private key cannot be loaded.", e);
        } catch (IOException e) {
            throw new RuntimeException("A private key cannot be loaded.", e);
        }
    }

    /**
     * 鍵マネージャを設定します。
     * @param keyManager 鍵マネージャ
     */
    public void setKeyManagers(KeyManager[] keyManager) {
        this.keyManager = keyManager;
        this.sslFactory = null;
    }

    /**
     * SSLソケットファクトリーを設定します。
     * @param sslFactory SSLソケットファクトリー
     */
    public void setSocketFactory(SSLSocketFactory sslFactory) {
        this.sslFactory = sslFactory;
    }

    /**
     * SSLソケットの作成を取得します。
     * @return SSLSocketFactory
     */
    private SSLSocketFactory getSocketFactory(){
        if(this.sslFactory != null) return this.sslFactory;

        // https接続認証設定処理
        try {
            // SSLプロトコルの SSLContext を作成
            SSLContext sc = SSLContext.getInstance("SSL");
            // 認証キー, 信頼判断, 乱数 のソースをセットし初期化する
            sc.init(this.keyManager, this.trustManager, new SecureRandom());
            // SSLソケットの作成(SSLSocketFactory)
            this.sslFactory = sc.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            // SSLContext を指定したプロトコル,プロバイダーで作成できなかった
            throw new RuntimeException("SSLContext cannot be made.", e);
        } catch (KeyManagementException e) {
            // SSLContext 初期化できなかった
            throw new RuntimeException("SSLContext cannot be initialized.", e);
        }
        return this.sslFactory;
    }

    /**
     * ホスト名検証クラスを設定します。
     * @param hostVerifier ホスト名検証クラス
     */
    public void setHostnameVerifier(HostnameVerifier hostVerifier) {
        this.hostVerifier = hostVerifier;
    }

    /**
     * ホスト名検証クラスを取得します。
     * @return HostnameVerifier
     */
    private HostnameVerifier getHostnameVerifier() {
        if(this.hostVerifier == null){
            this.hostVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
        }
        return this.hostVerifier;
    }

    // -----=====-----=====-----=====-----=====-----=====-----=====-----=====-----=====-----

    /**
     * サーブレットとHTTP/HTTPS通信を行います。
     * @param reqData 要求オブジェクト
     * @return 応答オブジェクト
     * @throws IllegalArgumentException リクエストモデルの指定がなかった場合
     */
    public Object execute(Object reqData) {
        if( reqData == null ){
            throw new IllegalArgumentException("A request object is not specified.");
        }
        Object resData = null; // 応答オブジェクト

        // サーブレットに接続
        HttpURLConnection conn = null;
        try {
            // コネクションを開く
            conn = (HttpURLConnection)this.servlet.openConnection();
            // 接続の設定
            //conn.setConnectTimeout(0);    // 接続タイムアウト
            //conn.setReadTimeout(0);       // 応答タイムアウト
            conn.setUseCaches(false);
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-type", "application/octet-stream");
            // データの圧縮をする時は、リクエストヘッダ追加
            if( this.compress ){
                conn.setRequestProperty("Content-Encoding", "gzip");
                conn.setRequestProperty("Accept-Encoding", "gzip");
            }
            // SSLコネクションの場合の追加設定
            if (conn instanceof HttpsURLConnection) {
                HttpsURLConnection sslConn = (HttpsURLConnection)conn;
                sslConn.setSSLSocketFactory(getSocketFactory());
                sslConn.setHostnameVerifier(getHostnameVerifier());
            }
            // 接続
            conn.connect();
        } catch (SocketTimeoutException e) {
            // サーブレット connect タイムアウト
            throw new RuntimeException("Servlet connect timed out.", e);
        } catch (SSLException e) {
            // SSLが利用できない
            throw new RuntimeException("cannot open ssl connection of servlet.", e);
        } catch (IOException e) {
            // サーバのコネクションが開けない
            throw new RuntimeException("cannot open connection of servlet.", e);
        }

        // データ(オブジェクト)の送信
        ObjectOutputStream oos = null;  // 出力ストリーム
        try {
            // オブジェクトストリームに変換して送信
            OutputStream os = conn.getOutputStream();
            if( this.compress ){
                oos = new ObjectOutputStream(new GZIPOutputStream(new BufferedOutputStream(os)));
            }else{
                oos = new ObjectOutputStream(new BufferedOutputStream(os));
            }
            oos.writeObject(reqData);
            oos.flush();
        } catch (SocketTimeoutException e) {
            // Appサーバ connect/read タイムアウト
            throw new RuntimeException("Servlet timed out.", e);
        } catch (ObjectStreamException e) {
            // 応答オブジェクトを直列化してオブジェクトストリームに渡せない
            throw new RuntimeException("a request object of output stream is not serialize.", e);
        } catch (IOException e) {
            // Appサーバ通信エラー
            throw new RuntimeException("Servlet access error.", e);
        } finally {
            try {
                // 開いているストリームを閉じる
                if (oos != null) {
                    oos.close();
                    oos = null;
                }
            } catch (IOException e) {
                throw new RuntimeException("A object stream cannot be closed.", e);
            }
        }

        // データ(オブジェクト)の送受信
        ObjectInputStream ois = null;   // 入力ストリーム
        try {
            // オブジェクトストリームの受信して復元
            InputStream is = conn.getInputStream();
            String encoding = conn.getRequestProperty("Content-Encoding");
            if( this.compress && "gzip".equals(encoding) ){
                // レスポンスヘッダの "Content-Encoding" が "gzip" だったらGZIP圧縮されている
                ois = new ObjectInputStream(new GZIPInputStream(new BufferedInputStream(is)));
            }else{
                ois = new ObjectInputStream(new BufferedInputStream(is));
            }
            resData = ois.readObject();
        } catch (SocketTimeoutException e) {
            // Appサーバ connect/read タイムアウト
            throw new RuntimeException("Server timed out.", e);
        } catch (ClassNotFoundException e) {
            // オブジェクトストリームから応答オブジェクトが復元できない
            throw new RuntimeException("A response object of input stream is not found.", e);
        } catch (ObjectStreamException e) {
            // オブジェクトストリームから応答オブジェクトが復元できない
            throw new RuntimeException("A response object of input stream is not restorable.", e);
        } catch (IOException e) {
            // Appサーバ通信エラー
            throw new RuntimeException("Server access error.", e);
        } finally {
            // 開いているストリームを閉じる
            try {
                if (ois != null) {
                    ois.close();
                    ois = null;
                }
            } catch (IOException e) {
                throw new RuntimeException("A object stream cannot be closed.", e);
            }
        }

        return resData;
    }

}

呼び出し例

    /**
     * アプリケーションメイン.
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://127.0.0.1:8080/ServletTest/ObjSend");
        //URL url = new URL("https://127.0.0.1:8443/ServletTest/ObjSend");
        //String keystore = "C:/ServletTest/keystore";
        //String storepass = "password";

        InteractiveCommunicationClient client = new InteractiveCommunicationClient(url, true);
        //client.loadTrusts(new java.io.FileInputStream(keystore), storepass);
        
        String str = (String)client.execute("オブジェクト双方向通信");
        System.out.println("result:" + str);
    }

最終更新時間:2009年10月24日 16時05分57秒 指摘や意見などあればSandBoxのBBSへ。

InteractiveCommunicationClient.java InteractiveCommunicationServlet.java