001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.fukurou.util;
017
018import java.io.BufferedInputStream;
019import java.io.BufferedReader;
020import java.io.File;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.InputStreamReader;
024import java.io.OutputStream;
025import java.io.PrintStream;
026import java.io.PrintWriter;
027import java.io.UnsupportedEncodingException;
028import java.net.HttpURLConnection;
029import java.net.InetSocketAddress;
030import java.net.Proxy;
031import java.net.SocketAddress;
032import java.net.URL;
033import java.net.URLConnection;
034import java.security.KeyManagementException;
035import java.security.NoSuchAlgorithmException;
036import java.security.cert.CertificateException;
037import java.security.cert.X509Certificate;
038
039import javax.net.ssl.HostnameVerifier;
040import javax.net.ssl.HttpsURLConnection;
041import javax.net.ssl.SSLContext;
042import javax.net.ssl.SSLSession;
043import javax.net.ssl.TrustManager;
044import javax.net.ssl.X509TrustManager;
045
046import org.apache.commons.codec.binary.Base64;
047
048/**
049 * URLConnect は、指定のURL にアクセスして、情報/データを取得します。
050 * setMethodOverrideでX-HTTP-Method-Overrideの設定が可能です。
051 * URL へのアクセスにより、エンジンでは各種処理を実行させることが可能になります。
052 * 例えば、帳票デーモンの起動や、長時間かかる処理の実行などです。
053 * なお、URLに引数が付く場合は、ダブルコーテーションで括って下さい。
054 * - 付き引数は、指定順番は、関係ありません。- 無し引数(url,user:passwd)は、
055 * 順番があります。
056 *
057 * Usage: java org.opengion.fukurou.util.URLConnect [-info/-data] … url [user:passwd]
058 *
059 *   args[*] : [-info/-data]       情報の取得か、データの取得かを指定します(初期値:-data)。
060 *   args[*] : [-post=ファイル名]  POSTメソッドを指定して、ファイルデータを送信します(初期値:-get)。
061 *   args[*] : [-encode=UTF-8]     エンコードを指定します(通常は接続先のencodeを使用)。
062 *   args[*] : [-out=ファイル名]   結果を指定されたファイルエンコードでファイルに出力します。
063 *   args[*] : [-errEx=true/false] trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)。
064 *   args[A] : url                 URLを指定します。GETの場合、パラメータは ?KEY=VALです。
065 *   args[B] : [user:passwd]       BASIC認証のエリアへのアクセス時に指定します。
066 *
067 * ※ プロキシ設定の3つの方法
068 * プロキシ設定には、3つの方法があります。
069 *   1.
070 *     URL url = URL( "http",proxyHost,proxyPort, url );
071 *     URLConnection urlConn = url.openConnection();
072 *   2.
073 *     SocketAddress scaddr = new InetSocketAddress( proxyHost, proxyPort );
074 *     Proxy proxy = new Proxy( Proxy.Type.HTTP, scaddr );
075 *     URL url = new Url( url );
076 *     URLConnection urlConn = url.openConnection( proxy );
077 *   3.
078 *     System.setProperty( "http.proxyHost",host );
079 *     System.setProperty( "http.proxyPort",String.valueOf( port ) );
080 *     URL url = new Url( url );
081 *     URLConnection urlConn = url.openConnection();
082 *     System.clearProperty( "http.proxyHost" );
083 *     System.clearProperty( "http.proxyPort" );
084 *
085 * 1. 、2. の方法は、urlConn.getContentType() を実行すると、エラーになります。(原因不明)
086 * 3. の方法では、マルチスレッドで実行する場合に、問題が発生します。
087 * 本クラスでは、方法2 を使用しています。
088 *
089 * @version  4.0
090 * @author   Kazuhiko Hasegawa
091 * @since    JDK5.0,
092 */
093public class URLConnect {
094        private static final String CR = System.getProperty("line.separator");
095
096//      private static final String     ENCODE  = "UTF-8";
097
098        private final String urlStr ;
099        private final String userPass ;
100
101        private int                             rpsCode         = -1;
102        private String                  rpsMethod       = null;
103        private String                  rpsMessage      = null;
104        private String                  type            = null;
105        private String                  charset         = null;
106        private String                  postData        = null;
107        private int                             timeout         = -1;           // 5.8.8.1 (2015/06/12) timeout属性追加
108        private long                    length          = -1;
109        private long                    date            = -1;
110        private long                    modified        = -1;
111        private boolean                 isPost          = false;
112        private URLConnection   conn            = null;
113        private Proxy                   proxy           = Proxy.NO_PROXY;
114
115        // 5.8.3.0 (2015/01/09) 追加
116        private String[]                propKeys;
117        private String[]                propVals;
118
119        // 5.10.10.0 (2019/03/29)
120        private String                  methodOver = null; // 上書き送信メソッドの指定
121
122        // 5.10.10.2 (2019/04/12)
123        private String                  contentType     = null;
124        /**
125         * コンストラクター
126         *
127         * @param       url     接続するアドレスを指定します。(http://server:port/dir/file.html)
128         * @param       pass    ユーザー:パスワード(認証接続が必要な場合)
129         */
130        public URLConnect( final String url, final String pass ) {
131                urlStr = url;
132                userPass = pass;
133        }
134
135        /**
136         * 指定のURLに対して、コネクトするのに使用するプロキシ設定を行います。
137         * このときに、ヘッダー情報を内部変数に設定しておきます。
138         *
139         * @param       host    接続するプロキシのホスト名
140         * @param       port    接続するプロキシのポート番号
141         */
142        public void setProxy( final String host,final int port ) {
143                // 方法2.
144                SocketAddress scaddr = new InetSocketAddress( host, port );
145                proxy = new Proxy( Proxy.Type.HTTP, scaddr );
146        }
147
148        /**
149         * 指定のURLに対して、コネクトします。
150         * このときに、ヘッダー情報を内部変数に設定しておきます。
151         *
152         * @og.rev 4.0.1.0 (2007/12/12) Postで複数キーを使えるように修正
153         * @og.rev 5.1.6.0 (2010/05/01) charsetを指定できるようにする
154         * @throws  IOException 入出力エラーが発生したとき
155         */
156        public void connect() throws IOException {
157                conn = getConnection();
158
159                if( isPost ) {
160                        conn.setDoOutput( true );                       // POST可能にする
161
162                        OutputStream os = null;                         // POST用のOutputStream
163                        PrintStream  ps = null;
164                        try {
165                                os = conn.getOutputStream();    // POST用のOutputStreamを取得
166                                // 5.1.6.0 (2010/05/01)
167                                if( charset != null ) {
168                                        ps = new PrintStream( os, false, charset );
169                                }
170                                else {
171                                        ps = new PrintStream( os );
172                                }
173                                ps.print( postData );                   // 4.1.0.0 (2007/12/22)
174                        }
175                        finally {
176                                Closer.ioClose( ps );           // close 処理時の IOException を無視
177                                Closer.ioClose( os );           // close 処理時の IOException を無視
178                        }
179                }
180                else {
181                        // GET 時のコネクション接続
182                        conn.connect();
183                }
184
185                setInfo( conn );
186        }
187
188        /**
189         * 接続先のデータを取得します。
190         *
191         * この処理の前に、connect() 処理を実行しておく必要があります。
192         * 取得したデータは、指定のURL へのアクセスのみです。
193         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
194         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
195         * このメソッドでの処理では、それらのファイル内に指定されているURLの
196         * 再帰的な取得は行いません。
197         * よって、フレーム処理なども行いません。
198         * 本来は、Stream のまま処理することで、バイナリデータも扱えますが、ここでは、
199         * テキストデータ(String)に変換して使用できるデータのみ扱えます。
200         *
201         * @return      接続結果
202         * @throws  IOException 入出力エラーが発生したとき
203         */
204        public String readData() throws IOException {
205                if( conn == null ) {
206                        String errMsg = "connect() されていません。データ取得前にconnect()してください。";
207                        throw new RuntimeException( errMsg );
208                }
209
210                BufferedReader reader = null;
211                StringBuilder buf = new StringBuilder();
212                try {
213                        reader = getReader();
214
215                        String line ;
216                        while( (line = reader.readLine()) != null ) {
217                                buf.append( line ).append( CR );
218                        }
219                }
220                catch( UnsupportedEncodingException ex ) {
221                        String errMsg = "指定された文字エンコーディングがサポートされていません。" + CR
222                                                + " url=[" + urlStr + "]"
223                                                + " charset=[" + charset + "]" ;
224                        throw new RuntimeException( errMsg,ex );
225                }
226                finally {
227                        Closer.ioClose( reader );
228                        disconnect();
229                }
230
231                return buf.toString();
232        }
233
234        /**
235         * サーバへのほかの要求が今後発生しそうにないことを示します。
236         *
237         */
238        public void disconnect() {
239                if( conn instanceof HttpURLConnection ) {
240                        ((HttpURLConnection)conn).disconnect() ;
241                }
242        }
243
244        /**
245         * URL と ユーザー:パスワードを与えて、URLConnectionを返します。
246         *
247         * ユーザー:パスワード が null でない場合は、BASCI認証エリアへのアクセスの為、
248         * BASE64Encoder を行って、Authorization プロパティーを設定します。
249         * ここで返す URLConnection は、すでに、connect() メソッド実行済みの
250         * リモート接続が完了した状態のオブジェクトです。
251         *
252         * @og.rev 5.8.3.0 (2015/01/09) ヘッダ等指定のためにsetRequestPropertyの値を指定できるようにします。
253         * @og.rev 5.8.8.1 (2015/06/12) timeout属性追加
254         * @og.rev 5.10.10.0 (2019/03/29) methodOverride指定
255         * @og.rev 5.10.10.2 (2019/04/12) contentType追加
256         * @og.rev 5.10.19.0 (2019/12/27) https対応
257         *
258         * @return  URLConnectionオブジェクト
259         * @throws  IOException 入出力エラーが発生したとき
260         */
261        // 5.1.5.0 (2010/04/01) SOAP対応により、PROTECTED化
262        protected URLConnection getConnection() throws IOException {
263                final URL url = new URL( urlStr );
264
265                // 方法2.
266                // 5.10.19.0 (2019/12/27) https接続の処理を追加
267//              URLConnection urlConn = url.openConnection( proxy );
268                URLConnection urlConn = null;
269                if(urlStr.startsWith("https://")) {
270                        urlConn = openHttpsConnection( url, proxy );
271                }else {
272                        urlConn = url.openConnection( proxy );
273                }
274
275                if( userPass != null ) {
276//                      byte[] encoded = Base64.encodeBase64( userPass.getBytes() );
277//                      String userPassEnc = new String( encoded );
278                        byte[] encoded = Base64.encodeBase64( userPass.getBytes( StringUtil.DEFAULT_CHARSET ) );        // 5.5.2.6 (2012/05/25) findbugs対応
279                        String userPassEnc = new String( encoded,StringUtil.DEFAULT_CHARSET );          // 5.5.2.6 (2012/05/25) findbugs対応
280                        urlConn.setRequestProperty( "Authorization","Basic " + userPassEnc );
281                }
282
283                // 5.8.3.0 (2015/01/09) RequestPropertyのセット
284                if( propKeys != null && propKeys.length > 0 ){
285                        for(int i = 0; i < propKeys.length; i++){
286                                urlConn.setRequestProperty( propKeys[i], propVals[i] );
287                        }
288                }
289
290                // 5.8.8.1 (2015/06/12) timeout属性追加
291                if( timeout >= 0 ) {
292                        urlConn.setConnectTimeout( timeout * 1000 );    // 引数は(秒)、設定は(ミリ秒)
293                }
294
295                // 5.10.10.0 (2019/03/29) method overrideの指定
296                if( methodOver != null && methodOver.length() > 0 ) {
297                        urlConn.setRequestProperty("X-HTTP-Method-Override", methodOver);
298                }
299
300                // 5.10.10.2 (2019/04/12)
301                if( contentType != null ) {
302                        urlConn.addRequestProperty("Content-Type",  contentType);
303                }
304                return urlConn ;
305        }
306
307        /**
308         *https接続
309         *
310         *指定のurlに対して、https接続を行います。
311         *自己証明書でも接続可能にするために、
312         *証明書のチェックを無効にしています。
313         *
314         *@og.rev 5.10.19.0 (2019/12/27) 新規追加
315         *@param url 接続先
316         *@param proxy プロキシ設定
317         *
318         *@returns HttpsURLConnection
319         *@throws IOExeption
320         */
321        protected HttpsURLConnection openHttpsConnection(URL url, Proxy proxy) throws IOException{
322
323                //証明書チェックの無効設定
324                TrustManager[] trustManagers = { new X509TrustManager() {
325                        public X509Certificate[] getAcceptedIssuers() {
326                                return null;
327                        };
328
329                        @Override
330                        public void checkClientTrusted(X509Certificate[] chain,
331                                        String authType) throws CertificateException {
332                        }
333
334                        @Override
335                        public void checkServerTrusted(X509Certificate[] chain,
336                                        String authType) throws CertificateException {
337                        }
338                } };
339
340                HttpsURLConnection httpsUrlConn = null;
341
342                try {
343                        SSLContext sslcontext = SSLContext.getInstance("SSL");
344                        // 証明書チェックを無効化
345                        sslcontext.init(null, trustManagers, null);
346
347                        //ホスト名の検証ルール 何が来てもtrueを返します。(dns名の検証を無効化)
348                        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
349                                @Override
350                                public boolean verify(String hostname,
351                                                SSLSession session) {
352                                        return true;
353                                }
354                        });
355
356                        httpsUrlConn = (HttpsURLConnection) url.openConnection(proxy);
357                        httpsUrlConn.setSSLSocketFactory(sslcontext.getSocketFactory());
358
359                } catch (KeyManagementException ke) {
360                        String errMsg = "https通信に失敗しました。" + ke.getMessage();
361                        throw new RuntimeException(errMsg);
362                } catch (NoSuchAlgorithmException ne) {
363                        String errMsg = "https通信の設定に失敗しました。" + ne.getMessage();
364                        throw new RuntimeException(errMsg);
365                }
366                return httpsUrlConn;
367        }
368
369
370        /**
371         * 接続先の情報を内部変数に設定します。
372         *
373         * ここでは、タイプ,エンコード,レスポンスコード,レスポンスメッセージ を設定します。
374         * レスポンスコード,レスポンスメッセージは、接続コネクションが、HttpURLConnection の
375         * 場合のみセットされます。
376         * 途中でエラーが発生した場合でも、継続処理できるようにします。これは、プロキシ
377         * 設定の方法により、conn.getContentType()  でエラーが発生する場合があるためです。
378         *
379         * @og.rev 5.5.9.1 (2012/12/07) charsetは、null の場合のみ設定します。
380         *
381         * @param   conn 接続先のコネクション
382         */
383        private void setInfo( final URLConnection conn ) {
384                try {
385                        // 5.5.9.1 (2012/12/07) charsetは、null の場合のみ設定します。
386                        if( charset == null ) { charset = conn.getContentEncoding(); }
387                        type    = conn.getContentType() ;
388                        length  = conn.getContentLength();
389                        date    = conn.getDate();
390                        modified= conn.getLastModified();
391
392                        if( charset == null && type != null ) {
393                                int adrs = type.indexOf( "charset" );
394                                int adrs2 = type.indexOf( '=',adrs );
395                                if( adrs > 0 && adrs2 > adrs ) {
396                                        charset = type.substring( adrs2+1 ).trim();
397                                }
398                        }
399
400                        if( conn instanceof HttpURLConnection ) {
401                                HttpURLConnection httpConn = (HttpURLConnection) conn;
402                                rpsCode         = httpConn.getResponseCode();
403                                rpsMethod       = httpConn.getRequestMethod();
404                                rpsMessage      = httpConn.getResponseMessage() + code2Message( rpsCode );
405                        }
406                }
407                // 4.0.0.0 (2007/11/29) Exception から、IOException と RuntimeException に変更
408                catch( IOException ex ) {
409                        System.out.println( ex.getMessage() );
410                }
411                catch( RuntimeException ex ) {
412                        System.out.println( ex.getMessage() );
413                }
414        }
415
416        /**
417         * URL 情報を取得します。
418         *
419         * @og.rev 4.3.4.4 (2009/01/01) メソッド名変更
420         *
421         * @return      URL情報
422         */
423        public String getUrl() { return urlStr; }
424
425        /**
426         * setRequestPropertyでセットするデータを設定します。
427         *
428         * keys,vals各々、カンマ区切りで分解します。
429         *
430         * @og.rev 5.8.3.0 (2007/12/22) 追加
431         * @param       keys    パラメータキー(カンマ区切り)
432         * @param       vals    パラメータ(カンマ区切り)
433         */
434        public void setRequestProperty( final String keys, final String vals ) {
435                if( keys != null && keys.length() > 0 && vals != null && vals.length() > 0 ){
436                        propKeys = StringUtil.csv2Array( keys );
437                        propVals = StringUtil.csv2Array( vals );
438
439                        if( propKeys.length != propVals.length ) {
440                                final String errMsg = "パラメータのキーと、値の数が一致しません。"   + CR
441                                                        + " key=[" + keys + "]"                                                                 + CR
442                                                        + " val=[" + vals + "]" ;
443                                throw new IllegalArgumentException( errMsg );
444                        }
445                }
446        }
447
448        /**
449         * POSTするデータを設定します。
450         *
451         * POSTする場合は、connect() 処理を行う前に、データを設定しておく必要があります。
452         * PUT,DELETEの場合もこのメソッドでデータ設定を行います。
453         *
454         * @og.rev 4.1.0.0 (2007/12/22) キーと値のセットを取得するよう変更
455         * @param       data    POSTデータ
456         */
457        public void setPostData( final String data ) {
458                postData = data;
459                if( postData != null && "?".indexOf( postData ) == 0 ) { // 先頭の?を抜く
460                        postData = postData.substring(1);
461                }
462                if( postData != null ) { isPost = true; }
463        }
464
465        /**
466         * タイプ 情報を取得します。
467         *
468         * @return      タイプ 情報
469         */
470        public String getType() { return type; }
471
472        /**
473         * データ量 情報を取得します。
474         *
475         * @return      データ量 情報
476         */
477        public long getLength() { return length; }
478
479        /**
480         * 作成日時 情報を取得します。
481         *
482         * @return      作成日時
483         */
484        public long getDate() { return date; }
485
486        /**
487         * 更新日時 情報を取得します。
488         *
489         * @return      更新日時
490         */
491        public long getModified() { return modified; }
492
493        /**
494         * 結果コード 情報(HttpURLConnection)を取得します。
495         *
496         * @return      結果コード 情報
497         */
498        public int getCode() { return rpsCode; }
499
500        /**
501         * メソッド 情報(HttpURLConnection)を取得します。
502         *
503         * @return      メソッド 情報
504         */
505        public String getMethod() { return rpsMethod; }
506
507        /**
508         * メッセージ 情報(HttpURLConnection)を取得します。
509         *
510         * @return      メッセージ 情報
511         */
512        public String getMessage() { return rpsMessage; }
513
514        /**
515         * キャラクタ 情報を取得します。
516         *
517         * @return      キャラクタ 情報
518         */
519        public String getCharset() { return charset; }
520
521        /**
522         * キャラクタ 情報を設定します。
523         *
524         * @param  chset キャラクタ 情報
525         */
526        public void setCharset( final String chset ) { charset = chset; }
527
528        /**
529         * 接続タイムアウト時間を(秒)で指定します
530         *
531         * 実際には、java.net.URLConnection#setConnectTimeout(int) に 1000倍して設定されます。
532         * 0 は、無限のタイムアウト、マイナスは、設定しません。(つまりJavaの初期値のまま)
533         *
534         * @og.rev 5.8.8.1 (2015/06/12) timeout属性追加
535         *
536         * @param       tout    タイムアウト時間(秒) (ゼロは、無制限)
537         * @see         java.net.URLConnection#setConnectTimeout(int)
538         */
539        public void setTimeout( final int tout ) {
540                timeout = tout;
541        }
542
543        /**
544         * 送信するためのメソッドを上書き指定します。
545         *
546         * RESTでPUTやDELETE等を指定する必要がある場合に利用します。
547         * メソッドそのものはPOSTですが、ヘッダのX-HTTP-Method-Overrideに追加指定します。
548         * 送信先サーバがOverrideに対応している必要があります。
549         *
550         * @og.rev 5.10.10.0 (2019/03/29)
551         *
552         * @param mtd 送信メソッド
553         */
554        public void setMethodOverride( final String mtd ) {
555                methodOver = mtd;
556        }
557
558        /**
559         * 送信ヘッダのContent-Typeを設定します。
560         *
561         * 送信先で指定がない場合は特にセットする必要はありません。
562         *
563         * @param ctype コンテントタイプ
564         */
565        public void setConentType(final String ctype) {
566                        contentType = ctype;
567        }
568
569        /**
570         * 接続先のデータのリーダーを取得します。
571         *
572         * この処理の前に、connect() 処理を実行しておく必要があります。
573         * 取得したデータは、指定のURL へのアクセスのみです。
574         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
575         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
576         * このメソッドでの処理では、それらのファイル内に指定されているURLの
577         * 再帰的な取得は行いません。
578         * よって、フレーム処理なども行いません。
579         *
580         * @return      接続結果のリーダー
581         * @throws  IOException 入出力エラーが発生したとき
582         */
583        public BufferedReader getReader() throws IOException {
584                InputStream in = conn.getInputStream();
585
586                final BufferedReader reader ;
587                if( charset != null ) {
588                        reader = new BufferedReader( new InputStreamReader( in,charset ) );
589                }
590                else {
591//                      reader = new BufferedReader( new InputStreamReader( in ) );
592                        reader = new BufferedReader( new InputStreamReader( in,StringUtil.DEFAULT_CHARSET ) );          // 5.5.2.6 (2012/05/25) findbugs対応
593                }
594
595                return reader;
596        }
597
598        /**
599         * 接続先のデータの入力ストリームを取得します。
600         *
601         * この処理の前に、connect() 処理を実行しておく必要があります。
602         * 取得したデータは、指定のURL へのアクセスのみです。
603         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
604         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
605         * このメソッドでの処理では、それらのファイル内に指定されているURLの
606         * 再帰的な取得は行いません。
607         * よって、フレーム処理なども行いません。
608         *
609         * @og.rev 5.4.2.0 (2011/12/01) 新規追加
610         *
611         * @return      接続結果の入力を出力します。
612         * @throws  IOException 入出力エラーが発生したとき
613         */
614        public InputStream getInputStream() throws IOException {
615//              InputStream in = new BufferedInputStream( conn.getInputStream() );
616//              return in;
617                return new BufferedInputStream( conn.getInputStream() );                // 5.5.2.4 (2012/05/16)
618        }
619
620        /**
621         * HttpURLConnection のレスポンスコードに対応するメッセージ文字列を返します。
622         *
623         * HttpURLConnection の getResponseCode() メソッドにより取得された、HTTPレスポンスコード
624         * に対応する文字列を返します。この文字列は、HttpURLConnection で定義された
625         * static 定数のコメントを、定義しています。
626         *
627         * @og.rev 5.6.7.0 (2013/07/27) レスポンスコード例 追加
628         *
629         * @param       code    HTTPレスポンスコード
630         *
631         * @return      レスポンスコードに対応する文字列
632         * @see HttpURLConnection#HTTP_ACCEPTED
633         */
634        public static String code2Message( final int code ) {
635                final String msg ;
636                switch( code ) {
637                        case 100                                                                                : msg = "100: 要求は続行可能です。"                                               ;       break;  // 5.6.7.0 (2013/07/27)
638                        case 101                                                                                : msg = "101: プロトコルを切り替えます。"                            ;       break;  // 5.6.7.0 (2013/07/27)
639                        case HttpURLConnection.HTTP_OK                                  : msg = "200: OK です。"                                                           ;       break;
640                        case HttpURLConnection.HTTP_CREATED                     : msg = "201: 作成されました。"                                                 ;       break;
641                        case HttpURLConnection.HTTP_ACCEPTED                    : msg = "202: 許可されました。"                                                 ;       break;
642                        case HttpURLConnection.HTTP_NOT_AUTHORITATIVE   : msg = "203: 不当な情報です。"                                                 ;       break;
643                        case HttpURLConnection.HTTP_NO_CONTENT                  : msg = "204: コンテンツがありません。"                                     ;       break;
644                        case HttpURLConnection.HTTP_RESET                               : msg = "205: コンテンツをリセットします。"                           ;       break;
645                        case HttpURLConnection.HTTP_PARTIAL                     : msg = "206: 部分的なコンテンツです。"                                     ;       break;
646                        case HttpURLConnection.HTTP_MULT_CHOICE                 : msg = "300: 複数選択されています。"                                      ;       break;
647                        case HttpURLConnection.HTTP_MOVED_PERM                  : msg = "301: 永続的に移動されました。"                                     ;       break;
648                        case HttpURLConnection.HTTP_MOVED_TEMP                  : msg = "302: 一時的に切り替えます。"                                      ;       break;
649                        case HttpURLConnection.HTTP_SEE_OTHER                   : msg = "303: 他を参照してください。"                                      ;       break;
650                        case HttpURLConnection.HTTP_NOT_MODIFIED                : msg = "304: 修正されませんでした。"                                      ;       break;
651                        case HttpURLConnection.HTTP_USE_PROXY                   : msg = "305: プロキシを使用してください。"                           ;       break;
652                        case 306                                                                                : msg = "306: 仕様の拡張案です。"                                                ;       break;  // 5.6.7.0 (2013/07/27)
653                        case 307                                                                                : msg = "307: 一時的なリダイレクトです。"                            ;       break;  // 5.6.7.0 (2013/07/27)
654                        case HttpURLConnection.HTTP_BAD_REQUEST                 : msg = "400: 不当な要求です。"                                                 ;       break;
655                        case HttpURLConnection.HTTP_UNAUTHORIZED                : msg = "401: 認証されませんでした。"                                      ;       break;
656                        case HttpURLConnection.HTTP_PAYMENT_REQUIRED    : msg = "402: 支払いが必要です。"                                                ;       break;
657                        case HttpURLConnection.HTTP_FORBIDDEN                   : msg = "403: 禁止されています。"                                                ;       break;
658                        case HttpURLConnection.HTTP_NOT_FOUND                   : msg = "404: 見つかりませんでした。"                                      ;       break;
659                        case HttpURLConnection.HTTP_BAD_METHOD                  : msg = "405: メソッドは許可されません。"                            ;       break;
660                        case HttpURLConnection.HTTP_NOT_ACCEPTABLE              : msg = "406: 許容されません。"                                                 ;       break;
661                        case HttpURLConnection.HTTP_PROXY_AUTH                  : msg = "407: プロキシの認証が必要です。"                            ;       break;
662                        case HttpURLConnection.HTTP_CLIENT_TIMEOUT              : msg = "408: 要求が時間切れです。"                                               ;       break;
663                        case HttpURLConnection.HTTP_CONFLICT                    : msg = "409: 重複しています。"                                                 ;       break;
664                        case HttpURLConnection.HTTP_GONE                                : msg = "410: 存在しません。"                                                  ;       break;
665                        case HttpURLConnection.HTTP_LENGTH_REQUIRED     : msg = "411: 長さが必要です。"                                                 ;       break;
666                        case HttpURLConnection.HTTP_PRECON_FAILED               : msg = "412: 前提条件が正しくありません。"                           ;       break;
667                        case HttpURLConnection.HTTP_ENTITY_TOO_LARGE    : msg = "413: 要求エンティティが長すぎます。"                  ;       break;
668                        case HttpURLConnection.HTTP_REQ_TOO_LONG                : msg = "414: 要求 URL が長すぎます。"                                   ;       break;
669                        case HttpURLConnection.HTTP_UNSUPPORTED_TYPE    : msg = "415: サポートされないメディアタイプです。"               ;       break;
670                        case 416                                                                                : msg = "416: 要求された範囲は不十分です。"                           ;       break;  // 5.6.7.0 (2013/07/27)
671                        case 417                                                                                : msg = "417: 要求どおりの処理が不可能です。"                  ;       break;  // 5.6.7.0 (2013/07/27)
672                        case HttpURLConnection.HTTP_INTERNAL_ERROR              : msg = "500: 内部サーバエラーです。"                                      ;       break;
673                        case HttpURLConnection.HTTP_NOT_IMPLEMENTED     : msg = "501: 実装されていません。"                                               ;       break;
674                        case HttpURLConnection.HTTP_BAD_GATEWAY                 : msg = "502: 誤ったゲートウェイです。"                                     ;       break;
675                        case HttpURLConnection.HTTP_UNAVAILABLE                 : msg = "503: サービスが利用できません。"                            ;       break;
676                        case HttpURLConnection.HTTP_GATEWAY_TIMEOUT     : msg = "504: ゲートウェイが時間切れです。"                           ;       break;
677                        case HttpURLConnection.HTTP_VERSION                     : msg = "505: HTTP バージョンがサポートされていません。"; break;
678//                      default : msg = "-1: 未定義" ;
679                        default : msg = code + ": 未定義" ;                // 5.6.7.0 (2013/07/27)
680                }
681                return msg ;
682        }
683
684        /**
685         * サンプル実行用のメインメソッド
686         *
687         * Usage: java org.opengion.fukurou.util.URLConnect [-info/-data] … url [user:passwd]
688         *
689         *   args[*] : [-info/-data]       情報の取得か、データの取得かを指定します(初期値:-data)。
690         *   args[*] : [-post=ファイル名]  POSTメソッドを指定して、ファイルデータを送信します(初期値:-get)。
691         *   args[*] : [-encode=UTF-8]     エンコードを指定します(通常は接続先のencodeを使用)
692         *   args[*] : [-out=ファイル名]   結果をファイルに出力します。ファイルエンコードも指定します。
693         *   args[*] : [-errEx=true/false] trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)。
694         *   args[A] : url                 URLを指定します。GETの場合、パラメータは ?KEY=VALです。
695         *   args[B] : [user:passwd]       BASIC認証のエリアへのアクセス時に指定します。
696         *
697         * @og.rev 5.6.7.0 (2013/07/27) -errEx 追加
698         *
699         * @param       args    コマンド引数配列
700         * @throws IOException 入出力エラーが発生したとき
701         */
702        public static void main( final String[] args ) throws IOException {
703                if( args.length < 3 ) {
704                        LogWriter.log( "Usage: java org.opengion.fukurou.util.URLConnect [-info/-data] … url [user:passwd]"                             );
705                        LogWriter.log( "   args[*] : [-info/-data]       情報の取得か、データの取得かを指定します(初期値:-data)"                       );
706                        LogWriter.log( "   args[*] : [-post=ファイル名]  POSTメソッドを指定して、ファイルデータを送信します(初期値:-get)"      );
707                        LogWriter.log( "   args[*] : [-encode=UTF-8]     エンコードを指定します。(通常は接続先のencodeを使用)"                                );
708                        LogWriter.log( "   args[*] : [-out=ファイル名]   結果をファイルに出力します。ファイルエンコードも指定します"              );
709                        LogWriter.log( "   args[*] : [-errEx=true/false] trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)" );
710                        LogWriter.log( "   args[A] : url                 URLを指定します。GETの場合、パラメータは ?KEY=VALです"                    );
711                        LogWriter.log( "   args[B] : [user:passwd]       BASIC認証のエリアへのアクセス時に指定します"                                              );
712                        return;
713                }
714
715                boolean isInfo  = false ;
716                boolean isPost  = false ;
717                String postKey  = null ;
718                String postFile = null ;
719                String encode   = null ;
720                String outFile  = null ;
721                boolean isEx    = false ;                               // 5.6.7.0 (2013/07/27) 追加
722                String[] vals   = new String[2];                // url,userPass の順に引数設定
723
724                int adrs = 0;
725                for( int i=0; i<args.length; i++ ) {
726                        String arg = args[i];
727                        if( arg.equalsIgnoreCase( "-info" ) ) {
728                                isInfo = true;
729                        }
730                        else if( arg.equalsIgnoreCase( "-data" ) ) {
731                                isInfo = false;
732                        }
733                        else if( arg.startsWith( "-post=" ) ) {
734                                isPost = true;
735                                int sepAdrs = arg.indexOf( ':',6 );
736                                postKey  = arg.substring( 6,sepAdrs );
737                                postFile = arg.substring( sepAdrs+1 );
738                        }
739                        else if( arg.startsWith( "-encode=" ) ) {
740                                encode = arg.substring( 8 );
741                        }
742                        else if( arg.startsWith( "-out=" ) ) {
743                                outFile = arg.substring( 5 );
744                        }
745                        else if( arg.startsWith( "-errEx=" ) ) {                                                        // 5.6.7.0 (2013/07/27) 追加
746                                isEx = "true".equalsIgnoreCase( arg.substring( 7 ) );
747                        }
748                        else if( arg.startsWith( "-" ) ) {
749                                System.out.println( "Error Argment:" + arg );
750                        }
751                        else {
752                                vals[adrs++] = arg;
753                        }
754                }
755
756                String urlStr   = vals[0] ;
757                String userPass = vals[1] ;
758
759                URLConnect conn = new URLConnect( urlStr,userPass );
760
761                // POST データは、connect() する前に、設定します。
762                if( isPost ) {
763                        FileString file = new FileString();
764                        file.setFilename( postFile );
765                        String postData = file.getValue();
766
767                        conn.setPostData( XHTMLTag.urlEncode(postKey, postData) );
768                }
769
770                conn.connect();
771                if( encode != null ) {
772                        conn.setCharset( encode );              // encode 指定
773                }
774                else {
775                        encode = conn.getCharset();             // 指定がなければ、接続先の charset を使用
776                }
777
778                final PrintWriter writer ;
779                if( outFile != null ) {
780                        writer = FileUtil.getPrintWriter( new File( outFile ),encode );
781                }
782                else {
783                        writer = FileUtil.getLogWriter( "System.out" );
784                }
785
786                int code = conn.getCode();              // 5.6.7.0 (2013/07/27) レスポンスコードは、常に拾っておきます。
787                if( isInfo ) {
788                        writer.println( "URL    :" + conn.getUrl() );
789                        writer.println( "Type   :" + conn.getType() );
790//                      writer.println( "Code   :" + conn.getCode() );
791                        writer.println( "Code   :" + code );                                    // 5.6.7.0 (2013/07/27) 取得済みの値を利用。
792                        writer.println( "Message:" + conn.getMessage() );
793                        writer.println( "Charset:" + conn.getCharset() );
794                }
795                else {
796                        writer.println( conn.readData() );
797                }
798
799                conn.disconnect();
800
801                Closer.ioClose( writer );
802
803                // 5.6.7.0 (2013/07/27) trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます
804                if( isEx && code >= 400 ) {
805                        String errMsg = URLConnect.code2Message( code );
806                        throw new RuntimeException( errMsg );
807                }
808        }
809}