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.File;
019import java.io.IOException;
020import java.io.PrintWriter;
021import java.net.HttpURLConnection;
022import java.net.MalformedURLException;
023import java.net.URL;
024import java.util.Arrays;
025import java.util.ArrayList;
026import java.util.List;
027import java.nio.file.Files;
028import java.nio.file.Paths;
029        // import java.nio.charset.Charset;
030import java.nio.charset.StandardCharsets;
031
032import org.apache.http.Header;
033import org.apache.http.HttpEntity;
034import org.apache.http.HttpHost;
035import org.apache.http.StatusLine;
036import org.apache.http.auth.AuthScope;
037import org.apache.http.auth.Credentials;
038import org.apache.http.auth.UsernamePasswordCredentials;
039import org.apache.http.client.config.RequestConfig;
040import org.apache.http.client.config.CookieSpecs;
041import org.apache.http.client.CredentialsProvider;
042import org.apache.http.client.entity.UrlEncodedFormEntity;
043import org.apache.http.client.methods.CloseableHttpResponse;
044import org.apache.http.client.methods.HttpGet;
045import org.apache.http.client.methods.HttpPost;
046import org.apache.http.client.methods.HttpUriRequest;
047import org.apache.http.entity.ContentType;
048import org.apache.http.entity.mime.MultipartEntityBuilder;
049import org.apache.http.entity.mime.HttpMultipartMode;
050import org.apache.http.impl.client.BasicCredentialsProvider;
051import org.apache.http.impl.client.CloseableHttpClient;
052import org.apache.http.impl.client.HttpClientBuilder;
053import org.apache.http.message.BasicHeader;
054import org.apache.http.message.BasicNameValuePair;
055import org.apache.http.NameValuePair;
056import org.apache.http.util.EntityUtils;
057
058// import org.opengion.fukurou.system.Closer;
059import org.opengion.fukurou.system.LogWriter;
060import org.opengion.fukurou.system.OgRuntimeException ;
061
062import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
063import static org.opengion.fukurou.system.HybsConst.CR;
064
065/**
066 * HttpConnect は、指定のURL にアクセスして、データを取得します。
067 * URL へのアクセスにより、エンジンでは各種処理を実行させることが可能になります。
068 * 例えば、帳票デーモンの起動や、長時間かかる処理の実行などです。
069 * なお、URLに引数が付く場合は、ダブルコーテーションで括って下さい。
070 * URL の指定は、先頭に何もつけませ。指定の順番も関係ありません。
071 * - 付き引数は、指定順番は、関係ありません。
072 * 先頭が # の引数は、コメントと判断します。
073 *
074 * <pre>
075 * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
076 *   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです
077 *   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)
078 *   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)
079 *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
080 *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
081 *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
082 *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
083 *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
084 *   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします
085 *   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします
086 *   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
087 *   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)
088 *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
089 * </pre>
090 *
091 * ※ URLConnect との違い。
092 *    -info/-data 等の区別の廃止。(実質、-info がなくなる。)
093 *    setDownloadFile(String) 追加(-binaryの代用)
094 *    setUploadFile(String) 追加
095 *    proxy 設定の変更
096 *
097 * @og.rev 6.9.0.0 (2018/01/31) 新規作成
098 *
099 * @version  6.9.0.0
100 * @author   Kazuhiko Hasegawa
101 * @since    JDK8.0,
102 */
103public class HttpConnect {
104        /** エンコードの初期値  {@value} */
105        public static final String DEFAULT_CHARSET = "UTF-8" ;
106        /** 言語の初期値  {@value} */
107        public static final String DEFAULT_LANG = "ja-JP" ;
108        /** User-Agentの初期値  {@value} */
109        public static final String DEFAULT_AGENT = "openGion with Apache HttpClient" ;
110        /** GETで指定するときのURLの長さ制限  {@value}  (IEの場合は、2,083文字) */
111        public static final int MAX_GET_LENGTH = 2000 ;
112
113        private final String urlStr ;
114        private final String user ;
115        private final String pass ;
116
117        private int                     rpsCode         = -1;
118        private String          rpsMessage      ;
119        private String          charset         = DEFAULT_CHARSET ;
120        private String          upldFile        ;
121        private String          dwldFile        ;                                               // バイナリファイルとして受け取る場合のファイル名
122        private int                     timeout         = -1;
123        private boolean         isPost          ;
124        private boolean         isDebug         ;
125
126        private HttpHost        proxy           ;
127
128        // 初期ヘッダー情報
129        private static final List<Header> INIT_HEADER =
130                                                                                        Arrays.asList(
131                                                                                                new BasicHeader( "Accept-Charset"       , DEFAULT_CHARSET ) ,
132                                                                                                new BasicHeader( "Accept-Language"      , DEFAULT_LANG  ) ,
133                                                                                                new BasicHeader( "User-Agent"           , DEFAULT_AGENT )
134                                                                                        );
135
136        private final List<NameValuePair>       reqParamList = new ArrayList<NameValuePair>();  // リクエストパラメーター(主にPOST時)
137        private final List<Header>                      headers          = new ArrayList<>( INIT_HEADER );      // ヘッダーパラメーター
138
139        // GET でのパラメータのマージ。きちんとした方法がわかるまでの暫定処置
140        private final StringBuilder                     reqParamBuf  = new StringBuilder( BUFFER_MIDDLE );
141
142        /**
143         * 接続先URLと、認証用ユーザー:パスワードを指定する、コンストラクター
144         *
145         * 認証が必要ない場合は、userPass は、null でかまいません。
146         * 接続先URLは、HttpConnect で、urlEncode しますので、そのままの文字列でかまいません。
147         *
148         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
149         *
150         * @param       url                     接続するアドレスを指定します。(http://server:port/dir/file.html)
151         * @param       userPass        ユーザー:パスワード(認証接続が必要な場合)
152         */
153        public HttpConnect( final String url, final String userPass ) {
154                urlStr = StringUtil.urlEncode2( url );
155
156                if( StringUtil.isNull( userPass ) ) {
157                        user = null;
158                        pass = null;
159                }
160                else {
161                        final String[] prm = StringUtil.csv2Array( userPass , ':' , 2 );
162                        user = prm[0];
163                        pass = prm[1];
164                }
165        }
166
167        /**
168         * URL接続先のデータを取得します。
169         *
170         * この処理の前に、必要な情報を設定して置いてください。
171         * また、code や message は、このメソッドを実行しないと取得できませんのでご注意ください。
172         * 
173         * 取得したデータは、指定のURL へのアクセスのみです。
174         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
175         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
176         * このメソッドでの処理では、それらのファイル内に指定されているURLの
177         * 再帰的な取得は行いません。
178         * よって、フレーム処理なども行いません。
179         *
180         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
181         *
182         * @return      接続結果
183         * @og.rtnNotNull
184         * @throws  IOException 入出力エラーが発生したとき
185         * @throws  MalformedURLException URLの形式が間違っている場合
186         */
187        public String readData() throws IOException , MalformedURLException {
188                HttpUriRequest method ;
189                if( isPost ) {
190                        if( isDebug ) { System.out.println( "POST URL=" + urlStr ); }
191                        method = new HttpPost( urlStr );
192
193                        if( !reqParamList.isEmpty() ) {
194                                ((HttpPost)method).setEntity( new UrlEncodedFormEntity( reqParamList , DEFAULT_CHARSET ) );
195                                if( isDebug ) { reqParamList.forEach( v -> System.out.println( "PARAM KEY=" + v.getName() + " , VAL=" + v.getValue() ) ); }
196                        }
197
198                        if( !StringUtil.isNull( upldFile ) ) {
199                                final File file = new File( upldFile );
200                                if( isDebug ) { System.out.println( "  MULTI FILE=" + file ); }
201                                final HttpEntity entity = MultipartEntityBuilder.create()
202                                                                                .setMode( HttpMultipartMode.BROWSER_COMPATIBLE )
203                                                                                .setCharset( StandardCharsets.UTF_8 )   // ファイル名の文字化け対策
204                                                                                .addBinaryBody( "upload" ,
205                                                                                                                file ,
206                                                                                                                ContentType.DEFAULT_BINARY ,
207                                                                                                                file.getName() )
208                                                                                .build();
209                                ((HttpPost)method).setEntity( entity );
210                        }
211                }
212                else {
213                        // GET でのパラメータのマージ。きちんとした方法がわかるまでの暫定処置
214                        final String getStr = reqParamBuf.length() == 0
215                                                                        ? urlStr
216                                                                        : reqParamBuf.toString() ;
217
218                        if( isDebug ) { System.out.println( "GET URL=" + getStr ); }
219
220                        method = new HttpGet( getStr );
221                }
222
223                String body = null;
224                try( CloseableHttpClient client = getClient() ;
225                         CloseableHttpResponse response = client.execute(method) ) {
226
227                        final StatusLine status = response.getStatusLine();
228                        final HttpEntity entity = response.getEntity();
229
230                        rpsCode    = status.getStatusCode();
231                        rpsMessage = ( code2Message( rpsCode ) + CR + status.getReasonPhrase() ).trim();
232
233                        // バイナリファイルとして受け取る場合。正常時のみ処理します。
234                        if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
235                                Files.write( Paths.get( dwldFile ) , EntityUtils.toByteArray( entity ) );
236                                body = dwldFile;
237                        }
238                        else {
239                                if( entity == null ) {
240                                        body = rpsMessage;              // HttpEntity が受け取れなかった場合は、メッセージを表示します。
241                                }
242                                else {
243                                        body = EntityUtils.toString( entity, charset );
244                                }
245                        }
246                }
247
248                return body;
249        }
250
251        /**
252         * 接続先の HttpClient オブジェクトを作成します。
253         *
254         * 接続に必要な情報を、設定します。
255         * CloseableHttpClient は、AutoCloseable を継承しています。
256         *
257         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
258         *
259         * @return  HttpConnectionオブジェクト
260         * @throws  IOException 入出力エラーが発生したとき
261         */
262        private CloseableHttpClient getClient() throws MalformedURLException {
263
264                final HttpClientBuilder clBuild = HttpClientBuilder.create();
265
266                // request configuration
267                final RequestConfig.Builder reqConfig = RequestConfig.custom()
268                        .setCookieSpec( CookieSpecs.STANDARD );                                 // 最新のRFC準拠ヘッダーを理解するのが困難なので。
269
270                if( timeout >= 0 ) {
271                        reqConfig.setConnectTimeout( timeout * 1000 )                   // timeoutの単位は(秒)、設定値は、ミリ秒
272                                         .setSocketTimeout(  timeout * 1000 );
273                }
274
275                clBuild.setDefaultRequestConfig( reqConfig.build() );
276
277                // headers (初期設定も入っているので、通常は、empty にはならない。)
278                if( !headers.isEmpty() ) {
279                        clBuild.setDefaultHeaders( headers );
280                }
281
282                // Auth
283                if( !StringUtil.isNull( user ) ) {
284                        final URL url = new URL( urlStr );
285                        final AuthScope   scope = new AuthScope( url.getHost(), url.getPort() );
286                        final Credentials cred  = new UsernamePasswordCredentials( user ,pass );
287
288                        final CredentialsProvider credProvider = new BasicCredentialsProvider();
289                        credProvider.setCredentials( scope,cred );
290
291                        clBuild.setDefaultCredentialsProvider( credProvider );
292                }
293
294                // Proxy
295                if( proxy != null ) {
296                        clBuild.setProxy( proxy );
297                }
298
299        //      // (デフォルトのHttpClientは、最新のRFC準拠ヘッダーを理解するのが困難です。)
300        //  // RequestConfig に、CookieSpecs.STANDARD を設定しているが、効果なければ、使わなくしてしまう。
301        //      clBuild.disableCookieManagement();
302
303                return clBuild.build();         // HttpClient httpClient  = HttpClientBuilder.create().*****.build();
304        }
305
306        /**
307         * 接続先に使用する引数(パラメータ)を追加します。
308         *
309         * これは、POSTでも、GETでも使用できます。
310         * POSTの場合は、NameValuePair として、HttpPost に、Entity としてセットするデータを設定します。
311         * GET の場合は、既存の接続先URLに、&amp;キー=値・・・・ で、追記します。
312         * すでに、パラメータが指定済みの場合は、&amp; で、そうでなければ、? で連結します。
313         * ここで指定するパラメータは、内部で、urlEncode しますので、そのままの文字列でかまいません。
314         *
315         * デフォルトは、GETですが、Internet Explorer では URL に最大 2,083 文字しか指定できないため、
316         * それ以上の場合は、POST に自動で切り替えます。
317         *
318         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
319         *
320         * @param       key     パラメータキー(nullの場合は、登録しません)
321         * @param       val     パラメータ値
322         */
323        public void addRequestProperty( final String key, final String val ) {
324                if( !StringUtil.isNull( key ) ) {
325                        reqParamList.add( new BasicNameValuePair( key,val ) );                          // POST のときのパラメータ。(GETでも使えるはず?)
326
327                        if( !isPost ) {                                                                                                         // 明らかに、GET でない場合は、この処理を行わない。
328                                if( reqParamBuf.length() == 0 ) {                                                               // 初めての場合
329                                        reqParamBuf.append( urlStr )
330                                                                .append( urlStr.indexOf( '?' ) > 0 ? '&' : '?' )
331                                                                .append( StringUtil.urlEncode2( key ) )
332                                                                .append( '=' )
333                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
334                                }
335                                else if( reqParamBuf.length() > MAX_GET_LENGTH ) {
336                                        isPost = true;                                                                                          // GETで送れるURLの長さ制限を超えた場合は、POSTにする。
337                                }
338                                else {
339                                        reqParamBuf.append( '&' )
340                                                                .append( StringUtil.urlEncode2( key ) )
341                                                                .append( '=' )
342                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
343                                }
344                        }
345                }
346        }
347
348        /**
349         * 指定のURLに対して、コネクトするのに使用するプロキシ設定を行います。
350         * このときに、ヘッダー情報を内部変数に設定しておきます。
351         *
352         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
353         *
354         * @param       host    接続するプロキシのホスト名(nullの場合は、登録しません)
355         * @param       port    接続するプロキシのポート番号
356         */
357        public void setProxy( final String host,final int port ) {
358                if( !StringUtil.isNull( host ) ) {
359                        proxy = new HttpHost( host , port );
360                }
361        }
362
363        /**
364         * Header として、HttpClient にセットするデータを設定します。
365         *
366         * 例えばJSON形式でPOSTする場合は通常"Content-Type", "application/json"を指定します。
367         *
368         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
369         *
370         * @param       key     パラメータキー(nullの場合は、登録しません)
371         * @param       val     パラメータ値(nullの場合は、登録しません)
372         */
373        public void addHeaderProperty( final String key, final String val ) {
374                if( !StringUtil.isNull( key ) && !StringUtil.isNull( val ) ) {
375                        headers.add( new BasicHeader( key,val ) );
376                }
377        }
378
379        /**
380         * URL接続先のバイナリファイルをダウンロード取得します。
381         *
382         * 取得したファイルは、dwldFile にバイナリのまま書き込まれます。
383         * よって、エンコードの指定は不要です。
384         *
385         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
386         *
387         * @param       dwldFile ダウンロードするファイル名。
388         * @throws  IOException 入出力エラーが発生したとき
389         */
390        public void setDownloadFile( final String dwldFile ) throws IOException {
391                this.dwldFile = dwldFile;
392        }
393
394        /**
395         * URL接続先のバイナリファイルをアップロードします。
396         *
397         * 取得したファイルは、upldFile にバイナリのまま書き込まれます。
398         * よって、エンコードの指定は不要です。
399         * アップロード は、multipart/form-data で送信するため、isPost = true を
400         * 内部的に設定しておきます。
401         *
402         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
403         *
404         * @param       upldFile アップロードするファイル名。
405         * @throws  IOException 入出力エラーが発生したとき
406         */
407        public void setUploadFile( final String upldFile ) throws IOException {
408                this.upldFile = upldFile;
409                isPost = true;
410        }
411
412        /**
413         * エンコード情報を設定します。
414         *
415         * 初期値は、UTF-8 です。
416         *
417         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
418         *
419         * @param  chset エンコード情報(nullの場合は、初期値:UTF-8 になります)
420         */
421        public void setCharset( final String chset ) {
422                if( !StringUtil.isNull( chset ) ) {
423                        charset = chset;
424                }
425        }
426
427        /**
428         * 接続タイムアウト時間を(秒)で指定します
429         *
430         * 実際には、org.apache.http.client.config.RequestConfig に対して、
431         *       .setConnectTimeout( timeout * 1000 )
432         *       .setSocketTimeout(  timeout * 1000 )
433         * のように、 1000倍して設定しています。
434         * 0 は、無限のタイムアウト、マイナスは、設定しません。(つまりJavaの初期値のまま)
435         *
436         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
437         *
438         * @param       tout    タイムアウト時間(秒) (ゼロは、無制限)
439         */
440        public void setTimeout( final int tout ) {
441                timeout = tout;
442        }
443
444        /**
445         * trueの場合、POSTを使用して接続します(初期値:false)。
446         *
447         * 通常はGETですが、外部から強制的に、POSTで送信したい場合に、
448         * 設定します。
449         * ただし、バイナリファイルをアップロードか、URLの長さ制限が、
450         * {@value #MAX_GET_LENGTH} を超えた場合は、内部で自動的に、post にします。
451         *
452         * @og.rev 6.9.0.1 (2018/02/05) 新規作成
453         *
454         * @param       usePost true:POST使用/false:通常(GET)
455         */
456        public void usePost( final boolean usePost ) {
457                isPost = usePost;
458        }
459
460        /**
461         * trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)。
462         *
463         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
464         *
465         * @param       isDebug true:デバッグ用のメッセージを出力/false:通常
466         */
467        public void setDebug( final boolean isDebug ) {
468                this.isDebug = isDebug;
469        }
470
471        /**
472         * 実行結果のステータスコード 情報を取得します。
473         *
474         * 結果は、#readData() メソッドをコールしないと取れません。
475         * 未実行の場合は、-1 がセットされています。
476         *
477         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
478         *
479         * @return      結果コード 情報
480         * @see         #readData()
481         */
482        public int getCode() { return rpsCode; }
483
484        /**
485         * メッセージ 情報を取得します。
486         *
487         * 結果は、#readData() メソッドをコールしないと取れません。
488         * 未実行の場合は、null がセットされています。
489         *
490         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
491         *
492         * @return      メッセージ 情報
493         */
494        public String getMessage() { return rpsMessage; }
495
496        /**
497         * HttpURLConnection のレスポンスコードに対応するメッセージ文字列を返します。
498         *
499         * HttpURLConnection の getResponseCode() メソッドにより取得された、HTTPレスポンスコード
500         * に対応する文字列を返します。この文字列は、HttpURLConnection で定義された
501         * static 定数のコメントを、定義しています。
502         *
503         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
504         *
505         * @param       code    HTTPレスポンスコード
506         *
507         * @return      レスポンスコードに対応する文字列
508         * @og.rtnNotNull
509         * @see HttpURLConnection#HTTP_ACCEPTED
510         */
511        public static String code2Message( final int code ) {
512                final String msg ;
513                switch( code ) {
514                        case 100                                                                                : msg = "100: 要求は続行可能です。"                                               ;       break;
515                        case 101                                                                                : msg = "101: プロトコルを切り替えます。"                            ;       break;
516                        case HttpURLConnection.HTTP_OK                                  : msg = "200: OK です。"                                                           ;       break;
517                        case HttpURLConnection.HTTP_CREATED                     : msg = "201: 作成されました。"                                                 ;       break;
518                        case HttpURLConnection.HTTP_ACCEPTED                    : msg = "202: 受け入れられました。"                                               ;       break;
519                        case HttpURLConnection.HTTP_NOT_AUTHORITATIVE   : msg = "203: 信頼できない情報です。"                                      ;       break;
520                        case HttpURLConnection.HTTP_NO_CONTENT                  : msg = "204: コンテンツがありません。"                                     ;       break;
521                        case HttpURLConnection.HTTP_RESET                               : msg = "205: コンテンツをリセットします。"                           ;       break;
522                        case HttpURLConnection.HTTP_PARTIAL                     : msg = "206: 部分的なコンテンツです。"                                     ;       break;
523                        case HttpURLConnection.HTTP_MULT_CHOICE                 : msg = "300: 複数の選択肢があります。"                                     ;       break;
524                        case HttpURLConnection.HTTP_MOVED_PERM                  : msg = "301: 永続的に移動されました。"                                     ;       break;
525                        case HttpURLConnection.HTTP_MOVED_TEMP                  : msg = "302: 一時的なリダイレクト。"                                      ;       break;
526                        case HttpURLConnection.HTTP_SEE_OTHER                   : msg = "303: ほかを参照してください。"                                     ;       break;
527                        case HttpURLConnection.HTTP_NOT_MODIFIED                : msg = "304: 変更されていません。"                                               ;       break;
528                        case HttpURLConnection.HTTP_USE_PROXY                   : msg = "305: プロキシを使用します。"                                      ;       break;
529                        case 306                                                                                : msg = "306: 仕様の拡張案です。"                                                ;       break;
530                        case 307                                                                                : msg = "307: 一時的なリダイレクトです。"                            ;       break;
531                        case HttpURLConnection.HTTP_BAD_REQUEST                 : msg = "400: 不当な要求です。"                                                 ;       break;
532                        case HttpURLConnection.HTTP_UNAUTHORIZED                : msg = "401: 認証されませんでした。"                                      ;       break;
533                        case HttpURLConnection.HTTP_PAYMENT_REQUIRED    : msg = "402: 支払いが必要です。"                                                ;       break;
534                        case HttpURLConnection.HTTP_FORBIDDEN                   : msg = "403: 禁止されています。"                                                ;       break;
535                        case HttpURLConnection.HTTP_NOT_FOUND                   : msg = "404: 見つかりませんでした。"                                      ;       break;
536                        case HttpURLConnection.HTTP_BAD_METHOD                  : msg = "405: メソッドは許可されません。"                            ;       break;
537                        case HttpURLConnection.HTTP_NOT_ACCEPTABLE              : msg = "406: 受け入れられません。"                                               ;       break;
538                        case HttpURLConnection.HTTP_PROXY_AUTH                  : msg = "407: プロキシの認証が必要です。"                            ;       break;
539                        case HttpURLConnection.HTTP_CLIENT_TIMEOUT              : msg = "408: 要求がタイムアウトしました。"                           ;       break;
540                        case HttpURLConnection.HTTP_CONFLICT                    : msg = "409: 重複しています。"                                                 ;       break;
541                        case HttpURLConnection.HTTP_GONE                                : msg = "410: 存在しません。"                                                  ;       break;
542                        case HttpURLConnection.HTTP_LENGTH_REQUIRED     : msg = "411: 長さが必要です。"                                                 ;       break;
543                        case HttpURLConnection.HTTP_PRECON_FAILED               : msg = "412: 前提条件が満たされていません。"                  ;       break;
544                        case HttpURLConnection.HTTP_ENTITY_TOO_LARGE    : msg = "413: 要求のエンティティが大きすぎます。"                ;       break;
545                        case HttpURLConnection.HTTP_REQ_TOO_LONG                : msg = "414: 要求のURIが大きすぎます。"                           ;       break;
546                        case HttpURLConnection.HTTP_UNSUPPORTED_TYPE    : msg = "415: サポートされないメディアタイプです。"               ;       break;
547                        case 416                                                                                : msg = "416: 要求された範囲は不十分です。"                           ;       break;
548                        case 417                                                                                : msg = "417: 要求どおりの処理が不可能です。"                  ;       break;
549                        case HttpURLConnection.HTTP_INTERNAL_ERROR              : msg = "500: 内部サーバエラーです。"                                      ;       break;
550                        case HttpURLConnection.HTTP_NOT_IMPLEMENTED     : msg = "501: 実装されていません。"                                               ;       break;
551                        case HttpURLConnection.HTTP_BAD_GATEWAY                 : msg = "502: 誤ったゲートウェイです。"                                     ;       break;
552                        case HttpURLConnection.HTTP_UNAVAILABLE                 : msg = "503: サービスが利用できません。"                            ;       break;
553                        case HttpURLConnection.HTTP_GATEWAY_TIMEOUT     : msg = "504: ゲートウェイがタイムアウトしました。"               ;       break;
554                        case HttpURLConnection.HTTP_VERSION                     : msg = "505: サポートされていないHTTPバージョンです。"   ;       break;
555                        default                                                                                 : msg = code + ": 未定義"                                                          ;       break;
556                }
557                return msg ;
558        }
559
560        /**
561         * サンプル実行用のメインメソッド
562         *
563         * <pre>
564         * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
565         *   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです
566         *   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)
567         *   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)
568         *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
569         *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
570         *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
571         *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
572         *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
573         *   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします
574         *   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします
575         *   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
576         *   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)
577         *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
578         * </pre>
579         *
580         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
581         *
582         * @param       args    コマンド引数配列
583         * @throws IOException 入出力エラーが発生したとき
584         */
585        public static void main( final String[] args ) throws IOException {
586                if( args.length < 2 ) {
587                        LogWriter.log( "Usage: java org.opengion.fukurou.util.HttpConnect [-data/-binary] … url"                                                                                                        );
588                        LogWriter.log( "   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです"                                                                );
589                        LogWriter.log( "   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)"                                                );
590                        LogWriter.log( "   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)"                                        );
591                        LogWriter.log( "   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します"                                               );
592                        LogWriter.log( "   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。"                                                                      );
593                        LogWriter.log( "   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)"                                                                     );
594                        LogWriter.log( "   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)"                                                                                          );
595                        LogWriter.log( "   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です"                                                                               );
596                        LogWriter.log( "   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします"                                                                                                         );
597                        LogWriter.log( "   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします"                                                         );
598                        LogWriter.log( "   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)" );
599                        LogWriter.log( "   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)"                                            );
600                        LogWriter.log( "   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)"                                   );
601                        return;
602                }
603
604                String  urlStr          = null ;
605                final List<String> paramKey  = new ArrayList<>();       // パラメーターキー
606                final List<String> paramVal  = new ArrayList<>();       // パラメーター値
607                final List<String> headerKey = new ArrayList<>();       // パラメーターキー
608                final List<String> headerVal = new ArrayList<>();       // パラメーター値
609
610                String  userPass        = null ;
611                String  proxy           = null ;
612                int             timeout         = -1 ;
613                String  encode          = DEFAULT_CHARSET ;
614                String  outFile         = null ;
615                String  dwldFile        = null ;
616                String  upldFile        = null ;
617                boolean isEx            = false ;
618                boolean isDebug         = false ;
619                boolean nonWriter       = false ;
620
621//              int             code            = -1;
622
623                for( final String arg : args ) {
624                        if( arg.startsWith( "-param=" ) ) {
625                                final String[] prm = StringUtil.csv2Array( arg.substring( "-param=".length() ) , '=' , 2 );
626                                paramKey.add( prm[0] );
627                                paramVal.add( prm[1] );
628                        }
629                        else if( arg.startsWith( "-header=" ) ) {
630                                final String[] prm = StringUtil.csv2Array( arg.substring( "-header=".length() ) , '=' , 2 );
631                                headerKey.add( prm[0] );
632                                headerVal.add( prm[1] );
633                        }
634                        else if( arg.startsWith( "-auth=" ) ) {
635                                userPass = arg.substring( "-auth=".length() );
636                                if( StringUtil.isNull( userPass ) ) {
637                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
638                                }
639                        }
640                        else if( arg.startsWith( "-proxy=" ) ) {
641                                proxy = arg.substring( "-proxy=".length() );
642                                if( StringUtil.isNull( proxy ) ) {
643                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
644                                }
645                        }
646                        else if( arg.startsWith( "-timeout=" ) ) {
647                                timeout = Integer.parseInt( arg.substring( "-timeout=".length() ) );
648                        }
649                        else if( arg.startsWith( "-encode=" ) ) {
650                                encode = arg.substring( "-encode=".length() );
651                                if( StringUtil.isNull( encode ) ) {
652                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
653                                }
654                        }
655                        else if( arg.startsWith( "-out=" ) ) {
656                                outFile = arg.substring( "-out=".length() );
657                                if( StringUtil.isNull( outFile ) ) {
658                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
659                                }
660                                else {
661                                        if( "null".equalsIgnoreCase( outFile ) || "none".equalsIgnoreCase( outFile ) ) {
662                                                outFile   = null;
663                                                nonWriter = true;
664                                        }
665                                }
666                        }
667                        else if( arg.startsWith( "-download=" ) ) {
668                                dwldFile = arg.substring( "-download=".length() );
669                                if( StringUtil.isNull( dwldFile ) ) {
670                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
671                                }
672                        }
673                        else if( arg.startsWith( "-upload=" ) ) {
674                                upldFile = arg.substring( "-upload=".length() );
675                                if( StringUtil.isNull( upldFile ) ) {
676                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
677                                }
678                        }
679                        else if( arg.startsWith( "-errEx=" ) ) {
680                                isEx = "true".equalsIgnoreCase( arg.substring( "-errEx=".length() ) );
681                        }
682                        else if( arg.startsWith( "-debug=" ) ) {
683                                isDebug = "true".equalsIgnoreCase( arg.substring( "-debug=".length() ) );
684                        }
685                        else if( StringUtil.startsChar( arg , '-' ) ) {                 // 引数が未定義(処理は継続させます。)
686                                System.err.println( "Error Argment:" + arg );
687                        }
688                        else if( StringUtil.startsChar( arg , '#' ) ) {                 // 引数がコメント
689                                continue;
690                        }
691                        else {
692                                urlStr = arg;
693                        }
694                }
695
696                try {                                                                   // try catch を入れます。
697                        final HttpConnect conn = new HttpConnect( urlStr,userPass );
698                        conn.setDebug( isDebug );                       // 最初に入れておけば、それ以降、有効になります。
699
700                        for( int i=0; i<paramKey.size(); i++ ) {
701                                conn.addRequestProperty( paramKey.get(i) , paramVal.get(i) );
702                        }
703
704                        for( int i=0; i<headerKey.size(); i++ ) {
705                                conn.addHeaderProperty( headerKey.get(i) , headerVal.get(i) );
706                        }
707
708                        // 6.8.1.3 (2017/08/04) proxy の設定
709                        if( !StringUtil.isNull( proxy ) ) {
710                                final String[] prm = StringUtil.csv2Array( proxy , ':' , 2 );
711                                final String host = prm[0];
712                                final int    port = Integer.parseInt( prm[1] );
713                                conn.setProxy( host , port );
714                        }
715
716                        conn.setCharset(                encode );               // encode 指定
717                        conn.setTimeout(                timeout );              // timeout属性追加
718                        conn.setUploadFile(             upldFile );
719                        conn.setDownloadFile(   dwldFile );
720
721                        final String outData = conn.readData();
722
723                        try( final PrintWriter writer = StringUtil.isNull( outFile )
724                                                                                                ? FileUtil.getLogWriter( "System.out" )
725                                                                                                : FileUtil.getPrintWriter( new File( outFile ),encode ) ) {
726                                if( !nonWriter ) {
727                                        writer.println( outData );
728                                }
729                                final int code = conn.getCode();
730
731                                // isEx=trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます
732                                if( code >= 400 ) {
733                                        final String errMsg = conn.getMessage();
734                                        writer.println( errMsg );
735                                        if( isEx ) {
736                                                throw new OgRuntimeException( errMsg );
737                                        }
738                                        else {
739                                                System.exit( code );
740                                        }
741                                }
742                        }
743                }
744                catch( final Throwable th ) {
745                        throw new OgRuntimeException( th );
746                }
747        }
748}