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;
031import java.util.concurrent.TimeUnit;                                                                                   // 8.0.0.0 (2021/07/31) Add
032
033// 8.0.0.0 (2021/07/31) httpclient-4.5.5.jar → httpclient5-5.1.jar
034// org.apache.http → org.apache.hc.core5.http or org.apache.hc.client5.http
035import org.apache.hc.core5.http.Header;
036import org.apache.hc.core5.http.HttpEntity;
037import org.apache.hc.core5.http.HttpHost;
038import org.apache.hc.core5.http.ClassicHttpRequest;
039import org.apache.hc.core5.http.ContentType;
040import org.apache.hc.core5.http.NameValuePair;
041import org.apache.hc.core5.http.ParseException;                                                                 // 8.0.0.0 (2021/07/31) Add
042import org.apache.hc.core5.http.io.entity.EntityUtils;
043import org.apache.hc.core5.http.io.entity.StringEntity;
044import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
045import org.apache.hc.core5.http.message.BasicHeader;
046import org.apache.hc.core5.http.message.BasicNameValuePair;
047// import org.apache.hc.core5.http.message.StatusLine;                                                  // 8.0.0.0 (2021/07/31) Delete
048import org.apache.hc.client5.http.auth.AuthScope;
049import org.apache.hc.client5.http.auth.Credentials;
050import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
051import org.apache.hc.client5.http.config.RequestConfig;
052// import org.apache.http.client.config.CookieSpecs;                                                    // 8.0.0.0 (2021/07/31) Delete
053// import org.apache.hc.client5.http.auth.CredentialsProvider;                                  // 8.0.0.0 (2021/07/31) Delete
054import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
055import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
056// import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;                             // 8.0.0.0 (2021/07/31) Delete
057import org.apache.hc.client5.http.classic.methods.HttpGet;
058import org.apache.hc.client5.http.classic.methods.HttpPost;
059//import org.apache.http.client.methods.HttpUriRequest;                                                 // 8.0.0.0 (2021/07/31)
060import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
061import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
062import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
063import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
064// import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;                             // 8.0.0.0 (2021/08/31)
065// import org.apache.hc.client5.http.cookie.Cookie;                                                             // 8.0.0.0 (2021/08/31)
066// import org.apache.hc.client5.http.impl.classic.HttpClients;
067// import org.apache.hc.client5.http.cookie.CookieStore;
068// import org.apache.http.impl.client.DefaultHttpClient;;
069import org.apache.hc.client5.http.cookie.BasicCookieStore;
070// import org.apache.hc.client5.http.cookie.Cookie;
071import org.apache.hc.client5.http.protocol.HttpClientContext;
072// import java.io.UnsupportedEncodingException;                                                                 // 8.0.0.0 (2021/07/31) Delete
073
074import java.net.URI;
075import java.net.URISyntaxException;
076// import java.util.Calendar;                                                                                                           // 8.0.0.0 (2021/08/31)
077import java.util.Map;
078// import java.util.Map.Entry;
079import java.util.HashMap;
080
081// import org.apache.http.impl.client.DefaultRedirectStrategy;
082// import org.apache.http.HttpRequest;
083// import org.apache.http.HttpResponse;
084// import org.apache.http.protocol.HttpContext;
085// import org.apache.http.ProtocolException;
086// import org.apache.http.impl.client.LaxRedirectStrategy;                                              // 8.0.0.0 (2021/07/31) Delete
087
088// import org.opengion.fukurou.system.Closer;
089import org.opengion.fukurou.system.LogWriter;
090import org.opengion.fukurou.system.OgRuntimeException ;
091
092import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
093import static org.opengion.fukurou.system.HybsConst.CR;
094
095/**
096 * HttpConnect は、指定のURL にアクセスして、データを取得します。
097 * URL へのアクセスにより、エンジンでは各種処理を実行させることが可能になります。
098 * 例えば、帳票デーモンの起動や、長時間かかる処理の実行などです。
099 * なお、URLに引数が付く場合は、ダブルコーテーションで括って下さい。
100 * URL の指定は、先頭に何もつけませ。指定の順番も関係ありません。
101 * - 付き引数は、指定順番は、関係ありません。
102 * 先頭が # の引数は、コメントと判断します。
103 *
104 * <pre>
105 * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
106 *   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです
107 *   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)
108 *   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)
109 *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
110 *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
111 *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
112 *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
113 *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
114 *   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします
115 *   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします
116 *   args[*] : [-postRedirect=true]    POST時に強制的にリダイレクトを行います(GET時は自動でリダイレクトします)(初期値:false) 7.2.5.0 (2020/06/01)
117 *   args[*] : [-usePost=true]         POSTを強制的に使用する場合にセットします(初期値:false)
118 *   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
119 *   args[*] : [-authJson=JSONコード]  JSONコードで認証する場合に使用します。8.0.0.0 (2021/08/31)
120 *   args[*] : [-authURL=認証用URL]    JSONコードで認証するURLを指定します。8.0.0.0 (2021/08/31)
121 *   args[*] : [-reqJson=JSONコード]   パラメータをJSONで指定する場合に使用します。8.0.0.0 (2021/08/31)
122 *   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)
123 *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
124 * </pre>
125 *
126 * ※ URLConnect との違い。
127 *    -info/-data 等の区別の廃止。(実質、-info がなくなる。)
128 *    setDownloadFile(String) 追加(-binaryの代用)
129 *    setUploadFile(String) 追加
130 *    proxy 設定の変更
131 *
132 * @og.rev 6.9.0.0 (2018/01/31) 新規作成
133 * @og.rev 8.0.0.0 (2021/08/31) httpclient5 対応
134 *
135 * https://hc.apache.org/httpcomponents-core-5.1.x/current/httpcore5/apidocs/
136 * https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/
137 *
138 * @version  8.0.0.0
139 * @author   Kazuhiko Hasegawa
140 * @since    JDK11.0,
141 */
142public class HttpConnect {
143        /** エンコードの初期値  {@value} */
144        public static final String DEFAULT_CHARSET = "UTF-8" ;
145        /** 言語の初期値  {@value} */
146        public static final String DEFAULT_LANG = "ja-JP" ;
147        /** User-Agentの初期値  {@value} */
148        public static final String DEFAULT_AGENT = "openGion with Apache HttpClient" ;
149        /** GETで指定するときのURLの長さ制限  {@value}  (IEの場合は、2,083文字) */
150        public static final int MAX_GET_LENGTH = 2000 ;
151
152        private final String urlStr ;
153        private final String user ;
154        private final String pass ;
155
156        private final Map<String,String> jsonMap = new  HashMap<>();
157
158        private int                     rpsCode         = -1;
159        private String          rpsMessage      ;
160        private String          charset         = DEFAULT_CHARSET ;
161        private String          upldFile        ;
162        private String          dwldFile        ;                               // バイナリファイルとして受け取る場合のファイル名
163        private int                     timeout         = -1;
164        private boolean         isPost          ;
165        private boolean         postRedirect ;                          // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
166        private boolean         isDebug         ;
167
168        private String          authJson        ;                               // -authJson            // 8.0.0.0 (2021/08/31)
169        private String          authURL         ;                               // -authURL                     // 8.0.0.0 (2021/08/31)
170        private String          reqJson         ;                               // -reqJson                     // 8.0.0.0 (2021/08/31)
171
172        private HttpHost        proxy           ;
173
174        // 初期ヘッダー情報
175        private static final List<Header> INIT_HEADER =
176                                                        Arrays.asList(
177                                                                new BasicHeader( "Accept-Charset"       , DEFAULT_CHARSET ) ,
178                                                                new BasicHeader( "Accept-Language"      , DEFAULT_LANG  ) ,
179                                                                new BasicHeader( "User-Agent"           , DEFAULT_AGENT )
180                                                        );
181
182        private final List<NameValuePair>       reqParamList = new ArrayList<NameValuePair>();  // リクエストパラメーター(主にPOST時)
183        private final List<Header>                      headers          = new ArrayList<>( INIT_HEADER );      // ヘッダーパラメーター
184
185        // GET でのパラメータのマージ。きちんとした方法がわかるまでの暫定処置
186        private final StringBuilder                     reqParamBuf  = new StringBuilder( BUFFER_MIDDLE );
187
188        /**
189         * 接続先URLと、認証用ユーザー:パスワードを指定する、コンストラクター
190         *
191         * 認証が必要ない場合は、userPass は、null でかまいません。
192         * 接続先URLは、HttpConnect で、urlEncode しますので、そのままの文字列でかまいません。
193         *
194         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
195         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
196         *
197         * @param       url                     接続するアドレスを指定します。(http://server:port/dir/file.html)
198         * @param       userPass        ユーザー:パスワード(認証接続が必要な場合)
199         */
200        public HttpConnect( final String url, final String userPass ) {
201                urlStr = StringUtil.urlEncode2( url );
202
203                if( StringUtil.isNull( userPass ) ) {
204                        user = null;
205                        pass = null;
206                }
207                else {
208                        final String[] prm = StringUtil.csv2Array( userPass , ':' , 2 );
209                        user = prm[0];
210                        pass = prm[1];
211                }
212        }
213
214//      /**
215//       * Form認証でのログイン
216//       *
217//       * https://hc.apache.org/httpcomponents-client-5.1.x/examples.html
218//       * https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientFormLogin.java
219//       *
220//       * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
221//       */
222//      private String formLogin() {
223//              String body = null;
224//              String secCheckURL = null;
225//
226//              final BasicCookieStore cookieStore = new BasicCookieStore();
227//              try (final CloseableHttpClient httpclient = HttpClients.custom()
228//                              .setDefaultCookieStore(cookieStore)
229//                              .build()) {
230////                    final HttpGet httpget = new HttpGet(urlStr);
231//                      final URI uri = new URI(urlStr);                                        // j_security_check のアドレスを取るために。
232//                      final HttpGet httpget = new HttpGet(uri);
233//
234//                      try (final CloseableHttpResponse response1 = httpclient.execute(httpget)) {
235//                              final HttpEntity entity = response1.getEntity();
236//
237//                              System.out.println("Login form get: " + response1.getCode() + " " + response1.getReasonPhrase());
238//                              final String loginBody = EntityUtils.toString( entity, charset );
239//                              // 一旦Form認証画面のHTMLが返ってくるので、その中から j_security_check を探してくる。
240//                              final int ed = loginBody.indexOf( "j_security_check" );
241//                              if( ed >= 0 ) {
242//                                      final int st = loginBody.lastIndexOf( '"',ed );         // 逆順に探す。
243//                                      if( st >= 0 ) {
244//                                              secCheckURL = uri.getScheme() + "://" + uri.getHost() + ':' + uri.getPort()
245//                                                                                      + loginBody.substring( st+1,ed ) + "j_security_check" ;
246//                                              System.out.println(secCheckURL);
247//                                      }
248//                              }
249//
250//                              EntityUtils.consume(entity);            // リソースを解放
251//
252//      //                      System.out.println("Initial set of cookies:");
253//      //                      final List<Cookie> cookies = cookieStore.getCookies();
254//      //                      if (cookies.isEmpty()) {
255//      //                              System.out.println("None");
256//      //                      } else {
257//      //                              for (int i = 0; i < cookies.size(); i++) {
258//      //                                      System.out.println("- " + cookies.get(i));
259//      //                              }
260//      //                      }
261//                      }
262//
263//                      final ClassicHttpRequest login = ClassicRequestBuilder.post()
264//      //                              .setUri(new URI("http://localhost:8828/gf/jsp/j_security_check"))
265//                                      .setUri(new URI( secCheckURL ))
266//                                      .addParameter("j_username", user)
267//                                      .addParameter("j_password", pass)
268//                                      .addParameter("j_security_check", "login")
269//                                      .build();
270//                      try (final CloseableHttpResponse response2 = httpclient.execute(login)) {
271//                              final HttpEntity entity = response2.getEntity();
272//
273//                              System.out.println("Login form get: " + response2.getCode() + " " + response2.getReasonPhrase());
274//                              body = EntityUtils.toString( entity, charset );
275//
276//                              EntityUtils.consume(entity);            // リソースを解放
277//
278//      //                      System.out.println("Post logon cookies:");
279//      //                      final List<Cookie> cookies = cookieStore.getCookies();
280//      //                      if (cookies.isEmpty()) {
281//      //                              System.out.println("None");
282//      //                      } else {
283//      //                              for (int i = 0; i < cookies.size(); i++) {
284//      //                                      System.out.println("- " + cookies.get(i));
285//      //                              }
286//      //                      }
287//                      }
288//              }
289//              catch( IOException ex ) { ex.printStackTrace(); }
290//              catch( URISyntaxException ex ) { ex.printStackTrace(); }
291//              catch( final ParseException ex ) {ex.printStackTrace();}
292//
293//              return body ;
294//      }
295
296        /**
297         * Form認証でのログイン
298         *
299         * https://hc.apache.org/httpcomponents-client-5.1.x/examples.html
300         * https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientFormLogin.java
301         *
302         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
303         */
304        private String formLogin( final CloseableHttpClient client , final URI uri ) {
305                String body = null;
306
307                try {
308                        final URI secCheckURI = uri.resolve( "j_security_check" );
309                        if( isDebug ) { System.out.println( "security URI=" + secCheckURI ); }
310
311                        final ClassicHttpRequest login = ClassicRequestBuilder.post()
312        //                              .setUri(new URI("http://localhost:8828/gf/jsp/j_security_check"))
313                                        .setUri( secCheckURI )
314                                        .addParameter("j_username", user)
315                                        .addParameter("j_password", pass)
316                                        .addParameter("j_security_check", "login")
317                                        .build();
318
319                        try( CloseableHttpResponse response = client.execute(login) ) {
320                                final HttpEntity entity = response.getEntity();
321
322                                rpsCode    = response.getCode();                                                                        // 8.0.0.0 (2021/07/31)
323                                rpsMessage = code2Message( rpsCode ).trim();                                            // 8.0.0.0 (2021/07/31)
324
325                                // Form認証の場合、バイナリ処理は、formLogin 内で行います。
326                                // バイナリファイルとして受け取る場合。成功(200番台)のみ処理します。
327                                if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
328                                        Files.write( Paths.get( dwldFile ) , EntityUtils.toByteArray( entity ) );
329                                        body = dwldFile;
330                                }
331                                else {
332                                        body = EntityUtils.toString( entity, charset );
333                                }
334                                EntityUtils.consume(entity);            // リソースを解放
335                        }
336                }
337                catch( final IOException | ParseException ex ) {
338                        throw new OgRuntimeException( ex );
339                }
340
341                return body ;
342        }
343
344        /**
345         * 特別版ログイン
346         *
347         * @og.rev 8.0.0.0 (2021/08/31) httpclient4 → httpclient5 対応
348         * @og.rev 8.0.2.0 (2021/11/30) PMD:Avoid instantiating new objects inside loops
349         */
350        private String jsonLogin( final CloseableHttpClient client ) {
351                String body = null;
352
353                try {
354                        final ClassicHttpRequest login = new HttpPost( authURL );
355
356                        // 8.0.0.0 (2021/08/31)
357                        login.setHeader("Content-type", "application/json");
358                        final HttpEntity stringEntity = new StringEntity(authJson,ContentType.APPLICATION_JSON);
359                        login.setEntity( stringEntity );
360
361                        try( CloseableHttpResponse response = client.execute(login) ) {
362                                final HttpEntity entity = response.getEntity();
363
364                                rpsCode    = response.getCode();                                                                        // 8.0.0.0 (2021/07/31)
365                                rpsMessage = code2Message( rpsCode ).trim();                                            // 8.0.0.0 (2021/07/31)
366                                if( isDebug ) { System.out.println( "jsonLogin=" + rpsCode ); }
367
368                                body = EntityUtils.toString( entity, charset );
369
370                                if( isDebug ) { System.out.println( "Login=" + body ); }
371
372                                // ログイン時のリターンJSONから、パラメータを取得するMapを作成します。
373                                if( !jsonMap.isEmpty() ) {
374                                        final StringBuilder buf = new StringBuilder();          // 8.0.2.0 (2021/11/30)
375                                        for( final String key : jsonMap.keySet() ) {
376                                                final int st = body.indexOf( key );
377                                                if( st >= 0 ) {
378                                                        final int ed = body.indexOf( ',',st+key.length() );
379//                                                      final StringBuilder buf = new StringBuilder();
380                                                        buf.setLength(0);                                                       // 8.0.2.0 (2021/11/30) StringBuilder の初期化
381                                                        if( ed > 0 ) {
382                                                                for( int i=st+key.length(); i<ed; i++ ) {
383                                                                        final char ch = body.charAt(i);
384                                                                        if( ch != '"' && ch != ':' && ch != ' ' ) {
385                                                                                buf.append( ch );
386                                                                        }
387                                                                }
388                                                        }
389                                                        jsonMap.put( key,buf.toString() );
390                                                        if( isDebug ) { System.out.println( "Map=" +  key + ":" + buf.toString() ); }
391                                                }
392                                        }
393                                }
394
395                                EntityUtils.consume(entity);            // リソースを解放
396                        }
397                }
398                catch( final IOException | ParseException ex ) {
399                        throw new OgRuntimeException( ex );
400                }
401
402                return body ;
403        }
404
405        /**
406         * 特別版ログイン
407         *
408         * @og.rev 8.0.0.0 (2021/08/31) httpclient4 → httpclient5 対応
409         */
410        private String jsonDataget( final CloseableHttpClient client , final URI uri ) {
411                String body = null;
412
413                try {
414                        final ClassicHttpRequest httpReq ;
415
416                        if( reqJson == null ) {
417                                httpReq = new HttpGet( uri );   // public/html/reportMain.html の時
418                        }
419                        else {
420                                httpReq = new HttpPost( uri );
421                                httpReq.setHeader("Content-type", "application/json");
422
423                                String tempJson = reqJson;
424                                if( !jsonMap.isEmpty() ) {
425                                        for( final Map.Entry<String,String> entry : jsonMap.entrySet() ) {
426                                                tempJson = tempJson.replace( '$'+entry.getKey()+'$' , entry.getValue() );
427                                        }
428                                }
429
430                                if( isDebug ) { System.out.println( "reqJson=" + tempJson ); }
431
432                                final HttpEntity stringEntity = new StringEntity(tempJson,ContentType.APPLICATION_JSON);
433                                httpReq.setEntity( stringEntity );
434                        }
435
436                        try( CloseableHttpResponse response = client.execute(httpReq) ) {
437                                final HttpEntity entity = response.getEntity();
438
439                                rpsCode    = response.getCode();                                                                        // 8.0.0.0 (2021/07/31)
440                                rpsMessage = code2Message( rpsCode ).trim();                                            // 8.0.0.0 (2021/07/31)
441                                if( isDebug ) { System.out.println( "jsonDataget=" + rpsCode ); }
442
443                                body = EntityUtils.toString( entity, charset );
444                                EntityUtils.consume(entity);            // リソースを解放
445                        }
446                }
447                catch( final IOException | ParseException ex ) {
448                        throw new OgRuntimeException( ex );
449                }
450
451                return body ;
452        }
453
454        /**
455         * URL接続先のデータを取得します。
456         *
457         * この処理の前に、必要な情報を設定して置いてください。
458         * また、code や message は、このメソッドを実行しないと取得できませんのでご注意ください。
459         *
460         * 取得したデータは、指定のURL へのアクセスのみです。
461         * 通常のWebブラウザは、イメージや、JavaScriptファイル、CSSファイルなど、
462         * 各種ファイル毎にHTTP接続を行い、取得して、レンダリングします。
463         * このメソッドでの処理では、それらのファイル内に指定されているURLの
464         * 再帰的な取得は行いません。
465         * よって、フレーム処理なども行いません。
466         *
467         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
468         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
469         *
470         * @return      接続結果
471         * @og.rtnNotNull
472         * @throws  IOException 入出力エラーが発生したとき
473         * @throws  MalformedURLException URLの形式が間違っている場合
474         */
475        public String readData() throws IOException , MalformedURLException {
476                final ClassicHttpRequest method ;                                                                                               // 8.0.0.0 (2021/07/31)
477                if( isPost ) {
478                        if( isDebug ) { System.out.println( "POST URL=" + urlStr ); }
479                        method = new HttpPost( urlStr );
480
481                        if( !reqParamList.isEmpty() ) {
482                                method.setEntity( new UrlEncodedFormEntity( reqParamList ) );                                           // 8.0.0.0 (2021/07/31)
483                                if( isDebug ) { reqParamList.forEach( v -> System.out.println( "PARAM KEY=" + v.getName() + " , VAL=" + v.getValue() ) ); }
484                        }
485
486                        if( !StringUtil.isNull( upldFile ) ) {
487                                final File file = new File( upldFile );
488                                if( isDebug ) { System.out.println( "  MULTI FILE=" + file ); }
489                                final HttpEntity entity = MultipartEntityBuilder.create()
490                                                                                .setCharset( StandardCharsets.UTF_8 )   // ファイル名の文字化け対策
491                                                                                .addBinaryBody( "upload" ,
492                                                                                                                file ,
493                                                                                                                ContentType.DEFAULT_BINARY ,
494                                                                                                                file.getName() )
495                                                                                .build();
496                                method.setEntity( entity );
497                        }
498                }
499                else {
500                        // GET でのパラメータのマージ。きちんとした方法がわかるまでの暫定処置
501                        final String getStr = reqParamBuf.length() == 0
502                                                                        ? urlStr
503                                                                        : reqParamBuf.toString() ;
504
505                        if( isDebug ) { System.out.println( "GET URL=" + getStr ); }
506
507                        method = new HttpGet( getStr );
508                }
509
510                final HttpClientContext context = HttpClientContext.create();
511//              if( ckStore != null ) {                                 // 8.0.0.0 (2021/07/31) 未使用
512//                      context.setCookieStore(ckStore);
513//              }
514
515                String body = null;
516                try( CloseableHttpClient client = getClient() ;
517                         CloseableHttpResponse response = client.execute(method,context) ) {
518
519                        rpsCode    = response.getCode();                                                                        // 8.0.0.0 (2021/07/31)
520                        if( isDebug ) { System.out.println( "readData=" + rpsCode ); }
521                        rpsMessage = code2Message( rpsCode ).trim();                                            // 8.0.0.0 (2021/07/31)
522
523                        final HttpEntity entity = response.getEntity();
524
525                        if( entity == null ) {
526                                body = rpsMessage;                      // HttpEntity が受け取れなかった場合は、メッセージを表示します。
527                        }
528                        else {
529                                // body は一度しか処理できない。EntityUtils.toByteArray( entity ) か、EntityUtils.toString( entity, charset );
530
531                                final URI uri = method.getUri();
532
533                                // バイナリファイルとして受け取る場合。成功(200番台)のみ処理します。
534                                if( !StringUtil.isNull( dwldFile ) && rpsCode >= 200 && rpsCode < 300 ) {
535                                        final byte[] dwnBody = EntityUtils.toByteArray( entity );
536                                        final String text = new String( dwnBody,charset );
537
538                                        // 一旦Form認証画面のHTMLが返ってくるので、その中から j_security_check を探してくる。
539                                        if( text.contains( "j_security_check" ) ) {
540                                                body = formLogin( client,uri );         // Form認証時の再接続処理
541                                        }
542                                        else {
543                                                Files.write( Paths.get( dwldFile ) , dwnBody );
544                                                body = dwldFile;
545                                        }
546                                }
547                                else {
548                                        // form認証チェックが必要なので、バイナリでも文字列で受け取る。
549                                        body = EntityUtils.toString( entity, charset );
550                                        // 一旦Form認証画面のHTMLが返ってくるので、その中から j_security_check を探してくる。
551                                        if( body.contains( "j_security_check" ) ) {
552                                                body = formLogin( client,uri );         // Form認証時の再接続処理
553                                        }
554                                        else if( authJson != null ) {                           // 8.0.0.0 (2021/08/31)
555                                                body = jsonLogin( client );                     // 認証時の再接続処理
556                                                body = jsonDataget( client,uri );       // 認証後の再接続処理
557                                        }
558                                }
559                        }
560                        EntityUtils.consume(entity);                    // リソースを解放
561                }
562                // 8.0.0.0 (2021/07/31) Add
563                catch( final ParseException | URISyntaxException ex ) {
564                        throw new OgRuntimeException( ex );
565                }
566
567                return body;
568        }
569
570        /**
571         * 接続先の HttpClient オブジェクトを作成します。
572         *
573         * 接続に必要な情報を、設定します。
574         * CloseableHttpClient は、AutoCloseable を継承しています。
575         *
576         * 7.2.5.0 (2020/06/01)
577         *   通常、HttpClientはGETの場合は自動でリダイレクト処理しますが、
578         *   POSTの場合は、302が返るだけでリダイレクト処理しません。
579         *   http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3[HTTP RFC 2616]で規定されています。
580         *   ここでは、ダウンロードファイルがあり、POSTの場合だけ強制的に
581         *
582         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
583         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
584         * @og.rev 8.0.0.0 (2021/07/31) httpclient4 → httpclient5 対応
585         *
586         * @return  HttpConnectionオブジェクト
587         * @throws  IOException 入出力エラーが発生したとき
588         */
589        private CloseableHttpClient getClient() throws MalformedURLException {
590                final HttpClientBuilder clBuild = HttpClientBuilder.create();
591
592                final BasicCookieStore cookieStore = new BasicCookieStore();
593                clBuild.setDefaultCookieStore(cookieStore) ;
594
595                if( timeout >= 0 ) {
596                        final RequestConfig.Builder reqConfig = RequestConfig.custom();
597                        reqConfig.setConnectTimeout( timeout ,TimeUnit.SECONDS );               // 8.0.0.0 (2021/07/31) timeoutの単位は(秒)
598
599                        clBuild.setDefaultRequestConfig( reqConfig.build() );
600                }
601
602                // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
603                // 8.0.0.0 (2021/07/31) httpclient5-5.1対応により LaxRedirectStrategy 廃止の為一旦削除
604        //      if( postRedirect ) {
605        //              clBuild.setRedirectStrategy( new LaxRedirectStrategy() );
606        //      }
607
608                // headers (初期設定も入っているので、通常は、empty にはならない。)
609                if( !headers.isEmpty() ) {
610                        clBuild.setDefaultHeaders( headers );
611                }
612
613                // Proxy
614                if( proxy != null ) {
615                        clBuild.setProxy( proxy );
616                }
617
618                // Auth
619                // https://github.com/apache/httpcomponents-client/blob/5.1.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientAuthentication.java
620                if( !StringUtil.isNull( user ) ) {
621                        final URL url = new URL( urlStr );
622                        final AuthScope   scope = new AuthScope( url.getHost(), url.getPort() );
623                        final Credentials cred  = new UsernamePasswordCredentials( user ,pass.toCharArray() );  // 8.0.0.0 (2021/07/31)
624
625                        final BasicCredentialsProvider credProvider = new BasicCredentialsProvider();                   // 8.0.0.0 (2021/07/31)
626                        credProvider.setCredentials( scope,cred );
627                        clBuild.setDefaultCredentialsProvider( credProvider );
628                }
629
630        //      // (デフォルトのHttpClientは、最新のRFC準拠ヘッダーを理解するのが困難です。)
631        //  // RequestConfig に、CookieSpecs.STANDARD を設定しているが、効果なければ、使わなくしてしまう。
632        //      clBuild.disableCookieManagement();
633
634                return clBuild.build();         // HttpClient httpClient  = HttpClientBuilder.create().*****.build();
635        }
636
637        /**
638         * 接続先に使用する引数(パラメータ)を追加します。
639         *
640         * これは、POSTでも、GETでも使用できます。
641         * POSTの場合は、NameValuePair として、HttpPost に、Entity としてセットするデータを設定します。
642         * GET の場合は、既存の接続先URLに、&amp;キー=値・・・・ で、追記します。
643         * すでに、パラメータが指定済みの場合は、&amp; で、そうでなければ、? で連結します。
644         * ここで指定するパラメータは、内部で、urlEncode しますので、そのままの文字列でかまいません。
645         *
646         * デフォルトは、GETですが、Internet Explorer では URL に最大 2,083 文字しか指定できないため、
647         * それ以上の場合は、POST に自動で切り替えます。
648         *
649         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
650         *
651         * @param       key     パラメータキー(nullの場合は、登録しません)
652         * @param       val     パラメータ値
653         */
654        public void addRequestProperty( final String key, final String val ) {
655                if( !StringUtil.isNull( key ) ) {
656                        reqParamList.add( new BasicNameValuePair( key,val ) );                          // POST のときのパラメータ。(GETでも使えるはず?)
657
658                        if( !isPost ) {                                                                                                         // 明らかに、GET でない場合は、この処理を行わない。
659                                if( reqParamBuf.length() == 0 ) {                                                               // 初めての場合
660                                        reqParamBuf.append( urlStr )
661                                                                .append( urlStr.indexOf( '?' ) > 0 ? '&' : '?' )
662                                                                .append( StringUtil.urlEncode2( key ) )
663                                                                .append( '=' )
664                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
665                                }
666                                else if( reqParamBuf.length() > MAX_GET_LENGTH ) {
667                                        if( isDebug ) { System.out.println( "GET → POST変更: URLの長さ制限<" + reqParamBuf.length() ); }
668                                        isPost = true;                                                                                          // GETで送れるURLの長さ制限を超えた場合は、POSTにする。
669                                }
670                                else {
671                                        reqParamBuf.append( '&' )
672                                                                .append( StringUtil.urlEncode2( key ) )
673                                                                .append( '=' )
674                                                                .append( StringUtil.urlEncode2( val ) );                // null のときは、長さゼロ文字列になる。
675                                }
676                        }
677                }
678        }
679
680        /**
681         * setRequestPropertyでセットするデータを設定します。
682         *
683         * keys,vals各々、カンマ区切りで分解します。
684         *
685         * @og.rev 5.10.16.0 (2019/10/04) 追加
686         *
687         * @param       keys    パラメータキー(カンマ区切り)
688         * @param       vals    パラメータ(カンマ区切り)
689         */
690        public void setRequestProperty( final String keys, final String vals ) {
691                if( keys != null && keys.length() > 0 && vals != null && vals.length() > 0 ){
692                        final String[]  propKeys = StringUtil.csv2Array( keys );
693                        final String[]  propVals = StringUtil.csv2Array( vals );
694
695                        if( propKeys.length == propVals.length && propKeys.length > 0 ) {
696                                for( int i=0; i<propKeys.length; i++ ) {
697                                        addRequestProperty( propKeys[i], propVals[i] );
698                                }
699                        }
700                        else {
701                                final String errMsg = "パラメータのキーと、値の数が一致しません。"   + CR
702                                                        + " key=[" + keys + "]"                                                                 + CR
703                                                        + " val=[" + vals + "]" ;
704                                throw new IllegalArgumentException( errMsg );
705                        }
706                }
707        }
708
709        /**
710         * 指定のURLに対して、コネクトするのに使用するプロキシ設定を行います。
711         * このときに、ヘッダー情報を内部変数に設定しておきます。
712         *
713         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
714         *
715         * @param       host    接続するプロキシのホスト名(nullの場合は、登録しません)
716         * @param       port    接続するプロキシのポート番号
717         */
718        public void setProxy( final String host,final int port ) {
719                if( !StringUtil.isNull( host ) ) {
720                        proxy = new HttpHost( host , port );
721                }
722        }
723
724        /**
725         * Header として、HttpClient にセットするデータを設定します。
726         *
727         * 例えばJSON形式でPOSTする場合は通常"Content-Type", "application/json"を指定します。
728         *
729         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
730         *
731         * @param       key     パラメータキー(nullの場合は、登録しません)
732         * @param       val     パラメータ値(nullの場合は、登録しません)
733         */
734        public void addHeaderProperty( final String key, final String val ) {
735                if( !StringUtil.isNull( key ) && !StringUtil.isNull( val ) ) {
736                        headers.add( new BasicHeader( key,val ) );
737                }
738        }
739
740        /**
741         * URL接続先のバイナリファイルをダウンロード取得します。
742         *
743         * 取得したファイルは、dwldFile にバイナリのまま書き込まれます。
744         * よって、エンコードの指定は不要です。
745         *
746         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
747         *
748         * @param       dwldFile ダウンロードするファイル名。
749         * @throws  IOException 入出力エラーが発生したとき
750         */
751        public void setDownloadFile( final String dwldFile ) throws IOException {
752                this.dwldFile = dwldFile;
753        }
754
755        /**
756         * URL接続先のバイナリファイルをアップロードします。
757         *
758         * 取得したファイルは、upldFile にバイナリのまま書き込まれます。
759         * よって、エンコードの指定は不要です。
760         * アップロード は、multipart/form-data で送信するため、isPost = true を
761         * 内部的に設定しておきます。
762         *
763         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
764         * @og.rev 7.2.5.0 (2020/06/01) upldFileのnull判定を入れます。
765         *
766         * @param       upldFile アップロードするファイル名。
767         * @throws  IOException 入出力エラーが発生したとき
768         */
769        public void setUploadFile( final String upldFile ) throws IOException {
770                if( upldFile != null ) {
771                        this.upldFile = upldFile;
772                        isPost = true;
773                }
774        }
775
776        /**
777         * エンコード情報を設定します。
778         *
779         * 初期値は、UTF-8 です。
780         *
781         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
782         *
783         * @param  chset エンコード情報(nullの場合は、初期値:UTF-8 になります)
784         */
785        public void setCharset( final String chset ) {
786                if( !StringUtil.isNull( chset ) ) {
787                        charset = chset;
788                }
789        }
790
791        /**
792         * 接続タイムアウト時間を(秒)で指定します
793         *
794         * 実際には、org.apache.http.client.config.RequestConfig に対して、
795         *       .setConnectTimeout( timeout * 1000 )
796         *       .setSocketTimeout(  timeout * 1000 )
797         * のように、 1000倍して設定しています。
798         * 0 は、無限のタイムアウト、マイナスは、設定しません。(つまりJavaの初期値のまま)
799         *
800         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
801         *
802         * @param       tout    タイムアウト時間(秒) (ゼロは、無制限)
803         */
804        public void setTimeout( final int tout ) {
805                timeout = tout;
806        }
807
808        /**
809         * trueの場合、POSTを使用して接続します(初期値:false)。
810         *
811         * 通常はGETですが、外部から強制的に、POSTで送信したい場合に、
812         * 設定します。
813         * ただし、バイナリファイルをアップロードか、URLの長さ制限が、
814         * {@value #MAX_GET_LENGTH} を超えた場合は、内部で自動的に、post にします。
815         *
816         * @og.rev 6.9.0.1 (2018/02/05) 新規作成
817         *
818         * @param       usePost true:POST使用/false:通常(GET)
819         */
820        public void usePost( final boolean usePost ) {
821                isPost = usePost;
822        }
823
824        /**
825         * jsonによる2フェーズ認証を行う場合の設定。
826         *
827         * 認証用のデータをJSON形式の文字列で設定します。
828         * urlは、認証するためのURLを指定します。
829         *
830         * @og.rev 8.0.0.0 (2021/08/31) 新規作成
831         *
832         * @param       json    認証用のデータを設定するJSON文字列
833         * @param       url             JSON文字列で認証を行うURL(無ければnull)
834         */
835        public void setAuthJson( final String json,final String url ) {
836                if( json != null && url != null ) {
837                        authJson = json;
838                        authURL  = url;
839                }
840        // null 設定も可能とする。
841        //      else {
842        //              final String errMsg = "setAuthJson を使用する場合は、authJsonとauthURLの両方を設定してください。" + CR
843        //                                      + " authJson=[" + json + "]"    + CR
844        //                                      + " authURL =[" + url + "]" ;
845        //              throw new IllegalArgumentException( errMsg );
846        //      }
847        }
848
849        /**
850         * パラメータをJSONで指定する場合に使用します。
851         *
852         * JSON形式でパラメータを指定する場合に使用します。
853         * $XXXX$ 文字列は、先のjson認証で取得した各種パラメータを割り当てます。
854         *
855         * @og.rev 8.0.0.0 (2021/08/31) 新規作成
856         *
857         * @param       json    JSON文字列のパラメータ
858         */
859        public void setReqJson( final String json ) {
860                if( json == null ) { return; }
861
862                reqJson = json;
863
864                // $xxxxx$ 文字列を見つけて、Mapのキーとして登録しておく
865                int st = reqJson.indexOf( '$' );
866                while( st >= 0 ) {
867                        final int ed = reqJson.indexOf( '$',st+1 );
868                        if( ed > 0 ) {
869                                jsonMap.put( reqJson.substring( st+1,ed ) , "" );       // $ は含めず
870                        }
871                        st = reqJson.indexOf( '$',ed+1 );
872                }
873        }
874
875        /**
876         * trueの場合、POST時に強制的にリダイレクトを行います(初期値:false)。
877         *
878         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
879         * @og.rev 8.0.0.0 (2021/07/31) httpclient5-5.1対応により LaxRedirectStrategy 廃止の為一旦削除
880         *
881         * @param       useRedirect     true:POST時に強制的にリダイレクト/false:通常
882         */
883        public void setPostRedirect( final boolean useRedirect ) {
884                postRedirect = useRedirect;
885        }
886
887        /**
888         * trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)。
889         *
890         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
891         *
892         * @param       isDebug true:デバッグ用のメッセージを出力/false:通常
893         */
894        public void setDebug( final boolean isDebug ) {
895                this.isDebug = isDebug;
896        }
897
898        /**
899         * 実行結果のステータスコード 情報を取得します。
900         *
901         * 結果は、#readData() メソッドをコールしないと取れません。
902         * 未実行の場合は、-1 がセットされています。
903         *
904         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
905         *
906         * @return      結果コード 情報
907         * @see         #readData()
908         */
909        public int getCode() { return rpsCode; }
910
911        /**
912         * メッセージ 情報を取得します。
913         *
914         * 結果は、#readData() メソッドをコールしないと取れません。
915         * 未実行の場合は、null がセットされています。
916         *
917         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
918         *
919         * @return      メッセージ 情報
920         */
921        public String getMessage() { return rpsMessage; }
922
923        /**
924         * HttpURLConnection のレスポンスコードに対応するメッセージ文字列を返します。
925         *
926         * HttpURLConnection の getResponseCode() メソッドにより取得された、HTTPレスポンスコード
927         * に対応する文字列を返します。この文字列は、HttpURLConnection で定義された
928         * static 定数のコメントを、定義しています。
929         *
930         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
931         *
932         * @param       code    HTTPレスポンスコード
933         *
934         * @return      レスポンスコードに対応する文字列
935         * @og.rtnNotNull
936         * @see HttpURLConnection#HTTP_ACCEPTED
937         */
938        public static String code2Message( final int code ) {
939                final String msg ;
940                switch( code ) {
941                        case 100                                                                                : msg = "100: 要求は続行可能です。"                                               ;       break;
942                        case 101                                                                                : msg = "101: プロトコルを切り替えます。"                            ;       break;
943                        case HttpURLConnection.HTTP_OK                                  : msg = "200: OK です。"                                                           ;       break;
944                        case HttpURLConnection.HTTP_CREATED                     : msg = "201: 作成されました。"                                                 ;       break;
945                        case HttpURLConnection.HTTP_ACCEPTED                    : msg = "202: 受け入れられました。"                                               ;       break;
946                        case HttpURLConnection.HTTP_NOT_AUTHORITATIVE   : msg = "203: 信頼できない情報です。"                                      ;       break;
947                        case HttpURLConnection.HTTP_NO_CONTENT                  : msg = "204: コンテンツがありません。"                                     ;       break;
948                        case HttpURLConnection.HTTP_RESET                               : msg = "205: コンテンツをリセットします。"                           ;       break;
949                        case HttpURLConnection.HTTP_PARTIAL                     : msg = "206: 部分的なコンテンツです。"                                     ;       break;
950                        case HttpURLConnection.HTTP_MULT_CHOICE                 : msg = "300: 複数の選択肢があります。"                                     ;       break;
951                        case HttpURLConnection.HTTP_MOVED_PERM                  : msg = "301: 永続的に移動されました。"                                     ;       break;
952                        case HttpURLConnection.HTTP_MOVED_TEMP                  : msg = "302: 一時的なリダイレクト。"                                      ;       break;
953                        case HttpURLConnection.HTTP_SEE_OTHER                   : msg = "303: ほかを参照してください。"                                     ;       break;
954                        case HttpURLConnection.HTTP_NOT_MODIFIED                : msg = "304: 変更されていません。"                                               ;       break;
955                        case HttpURLConnection.HTTP_USE_PROXY                   : msg = "305: プロキシを使用します。"                                      ;       break;
956                        case 306                                                                                : msg = "306: 仕様の拡張案です。"                                                ;       break;
957                        case 307                                                                                : msg = "307: 一時的なリダイレクトです。"                            ;       break;
958                        case HttpURLConnection.HTTP_BAD_REQUEST                 : msg = "400: 不当な要求です。"                                                 ;       break;
959                        case HttpURLConnection.HTTP_UNAUTHORIZED                : msg = "401: 認証されませんでした。"                                      ;       break;
960                        case HttpURLConnection.HTTP_PAYMENT_REQUIRED    : msg = "402: 支払いが必要です。"                                                ;       break;
961                        case HttpURLConnection.HTTP_FORBIDDEN                   : msg = "403: 禁止されています。"                                                ;       break;
962                        case HttpURLConnection.HTTP_NOT_FOUND                   : msg = "404: 見つかりませんでした。"                                      ;       break;
963                        case HttpURLConnection.HTTP_BAD_METHOD                  : msg = "405: メソッドは許可されません。"                            ;       break;
964                        case HttpURLConnection.HTTP_NOT_ACCEPTABLE              : msg = "406: 受け入れられません。"                                               ;       break;
965                        case HttpURLConnection.HTTP_PROXY_AUTH                  : msg = "407: プロキシの認証が必要です。"                            ;       break;
966                        case HttpURLConnection.HTTP_CLIENT_TIMEOUT              : msg = "408: 要求がタイムアウトしました。"                           ;       break;
967                        case HttpURLConnection.HTTP_CONFLICT                    : msg = "409: 重複しています。"                                                 ;       break;
968                        case HttpURLConnection.HTTP_GONE                                : msg = "410: 存在しません。"                                                  ;       break;
969                        case HttpURLConnection.HTTP_LENGTH_REQUIRED     : msg = "411: 長さが必要です。"                                                 ;       break;
970                        case HttpURLConnection.HTTP_PRECON_FAILED               : msg = "412: 前提条件が満たされていません。"                  ;       break;
971                        case HttpURLConnection.HTTP_ENTITY_TOO_LARGE    : msg = "413: 要求のエンティティが大きすぎます。"                ;       break;
972                        case HttpURLConnection.HTTP_REQ_TOO_LONG                : msg = "414: 要求のURIが大きすぎます。"                           ;       break;
973                        case HttpURLConnection.HTTP_UNSUPPORTED_TYPE    : msg = "415: サポートされないメディアタイプです。"               ;       break;
974                        case 416                                                                                : msg = "416: 要求された範囲は不十分です。"                           ;       break;
975                        case 417                                                                                : msg = "417: 要求どおりの処理が不可能です。"                  ;       break;
976                        case HttpURLConnection.HTTP_INTERNAL_ERROR              : msg = "500: 内部サーバエラーです。"                                      ;       break;
977                        case HttpURLConnection.HTTP_NOT_IMPLEMENTED     : msg = "501: 実装されていません。"                                               ;       break;
978                        case HttpURLConnection.HTTP_BAD_GATEWAY                 : msg = "502: 誤ったゲートウェイです。"                                     ;       break;
979                        case HttpURLConnection.HTTP_UNAVAILABLE                 : msg = "503: サービスが利用できません。"                            ;       break;
980                        case HttpURLConnection.HTTP_GATEWAY_TIMEOUT     : msg = "504: ゲートウェイがタイムアウトしました。"               ;       break;
981                        case HttpURLConnection.HTTP_VERSION                     : msg = "505: サポートされていないHTTPバージョンです。"   ;       break;
982                        default                                                                                 : msg = code + ": 未定義"                                                          ;       break;
983                }
984                return msg ;
985        }
986
987        /**
988         * サンプル実行用のメインメソッド
989         *
990         * <pre>
991         * Usage: java org.opengion.fukurou.util.HttpConnect [-post=キー:ファイル名] … url [user:passwd]
992         *   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです
993         *   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)
994         *   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)
995         *   args[*] : [-auth=user:pass]       BASIC認証のエリアへのアクセス時のユーザーとパスワードを指定します
996         *   args[*] : [-useForm=true/false]   認証方式にFORM認証を指定する場合、trueをセットします(初期値:false) 8.0.0.0 (2021/08/20)
997         *   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。
998         *   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)
999         *   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)
1000         *   args[*] : [-out=ファイル名]       結果をファイルに出力します。初期値は標準出力です
1001         *   args[*] : [-download=ファイル名]  ファイル名を指定して、ダウンロードします
1002         *   args[*] : [-upload=ファイル名]    ファイル名を指定して、multipart/form-dataでファイルアップロードします
1003         *   args[*] : [-postRedirect=true]    POST時に強制的にリダイレクトを行います(GET時は自動でリダイレクトします)(初期値:false) 7.2.5.0 (2020/06/01)
1004         *   args[*] : [-usePost=true]         POSTを強制的に使用する場合にセットします(初期値:false) 8.0.0.0 (2021/08/20)
1005         *   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)
1006         *   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)
1007         *   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)
1008         * </pre>
1009         *
1010         * @og.rev 6.9.0.0 (2018/01/31) 新規作成
1011         * @og.rev 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)引数を追加
1012         *
1013         * @param       args    コマンド引数配列
1014         * @throws IOException 入出力エラーが発生したとき
1015         */
1016        public static void main( final String[] args ) throws IOException {
1017                if( args.length < 2 ) {
1018                        LogWriter.log( "Usage: java org.opengion.fukurou.util.HttpConnect [-data/-binary] … url"                                                                                                );
1019                        LogWriter.log( "   args[A] : url                     URLを指定します。GETの場合、パラメータは ?KEY=VALです"                                                                       );
1020                        LogWriter.log( "   args[*] : [-param=key:value]      POST/GET時のパラメータのキーと値を:で区切って指定します。(複数回指定可)"                                        );
1021                        LogWriter.log( "   args[*] : [-header=key:value]     ヘッダーに設定するパラメータのキーと値を:で区切って指定します。(複数回指定可)"                                );
1022                        LogWriter.log( "   args[*] : [-auth=user:pass]       BASIC認証/FORM認証のエリアへのアクセス時のユーザーとパスワードを指定します"                                             );
1023//                      LogWriter.log( "   args[*] : [-useForm=true/false]   認証方式にFORM認証を指定する場合、trueをセットします(初期値:false)"                                 );
1024                        LogWriter.log( "   args[*] : [-proxy=host:port]      proxy を使用する場合のホストとポートを指定します。"                                                                             );
1025                        LogWriter.log( "   args[*] : [-timeout=3]            接続タイムアウト時間を(秒)で指定します(初期値:無指定)"                                                                     );
1026                        LogWriter.log( "   args[*] : [-encode=UTF-8]         エンコードを指定します。(初期値は UTF-8)"                                                                                         );
1027                        LogWriter.log( "   args[*] : [-out=ファイル名]           結果をファイルに出力します。初期値は標準出力です"                                                                           );
1028                        LogWriter.log( "   args[*] : [-download=ファイル名]      ファイル名を指定して、ダウンロードします"                                                                                                     );
1029                        LogWriter.log( "   args[*] : [-upload=ファイル名]        ファイル名を指定して、multipart/form-dataでファイルアップロードします"                                                     );
1030                        LogWriter.log( "   args[*] : [-postRedirect=true]    POST時に強制的にリダイレクトを行います(GET時は自動でリダイレクトします)(初期値:false)"             );
1031                        LogWriter.log( "   args[*] : [-usePost=true]         POSTを強制的に使用する場合にセットします(初期値:false)"                                                         );
1032                        LogWriter.log( "   args[*] : [-errEx=true/false]     trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます(初期値:false)" );
1033                        LogWriter.log( "   args[*] : [-authJson=JSONコード]  JSONコードで認証する場合に使用します。8.0.0.0 (2021/08/31)"                                            );
1034                        LogWriter.log( "   args[*] : [-authURL=認証用URL]    JSONコードで認証するURLを指定します。8.0.0.0 (2021/08/31)"                                                   );
1035                        LogWriter.log( "   args[*] : [#・・・・]                 コメント引数。(BATファイル上に残しておきたいが、使用したくない場合など)"                                            );
1036                        LogWriter.log( "   args[*] : [-debug=true/false]     trueの場合、適度にデバッグ用のメッセージを出力します(初期値:false)"                                               );
1037                        return;
1038                }
1039
1040                String  urlStr                  = null ;
1041                final List<String> paramKey  = new ArrayList<>();                                               // パラメーターキー
1042                final List<String> paramVal  = new ArrayList<>();                                               // パラメーター値
1043                final List<String> headerKey = new ArrayList<>();                                               // パラメーターキー
1044                final List<String> headerVal = new ArrayList<>();                                               // パラメーター値
1045
1046                String  userPass                = null ;                                        // -auth
1047//              boolean useForm                 = false ;                                       // -useForm                     // 8.0.0.0 (2021/08/20)
1048                String  proxy                   = null ;                                        // -proxy
1049                int             timeout                 = -1 ;                                          // -timeout
1050                String  encode                  = DEFAULT_CHARSET ;                     // -encode
1051                String  outFile                 = null ;                                        // -out
1052                String  dwldFile                = null ;                                        // -download
1053                String  upldFile                = null ;                                        // -upload
1054                boolean isEx                    = false ;                                       // -errEx
1055                boolean isDebug                 = false ;                                       // -debug
1056                boolean postRedirect    = false ;                                       // -postRedirect        // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
1057                boolean nonWriter               = false ;                                       // -out 指定で見つからない場合
1058                boolean isPost                  = false ;                                       // -usePost
1059                String  authJson                = null ;                                        // -authJson            // 8.0.0.0 (2021/08/31)
1060                String  authURL                 = null ;                                        // -authURL                     // 8.0.0.0 (2021/08/31)
1061                String  reqJson                 = null ;                                        // -reqJson                     // 8.0.0.0 (2021/08/31)
1062
1063//              int             code                    = -1;
1064
1065                for( final String arg : args ) {
1066                        if( arg.startsWith( "-param=" ) ) {
1067                                final String[] prm = StringUtil.csv2Array( arg.substring( "-param=".length() ) , '=' , 2 );
1068                                paramKey.add( prm[0] );
1069                                paramVal.add( prm[1] );
1070                        }
1071                        else if( arg.startsWith( "-header=" ) ) {
1072                                final String[] prm = StringUtil.csv2Array( arg.substring( "-header=".length() ) , '=' , 2 );
1073                                headerKey.add( prm[0] );
1074                                headerVal.add( prm[1] );
1075                        }
1076                        else if( arg.startsWith( "-auth=" ) ) {
1077                                userPass = arg.substring( "-auth=".length() );
1078                                if( StringUtil.isNull( userPass ) ) {
1079                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1080                                }
1081                        }
1082//                      else if( arg.startsWith( "-useForm=" ) ) {
1083//                              useForm = "true".equalsIgnoreCase( arg.substring( "-useForm=".length() ) );
1084//                      }
1085                        else if( arg.startsWith( "-proxy=" ) ) {
1086                                proxy = arg.substring( "-proxy=".length() );
1087                                if( StringUtil.isNull( proxy ) ) {
1088                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1089                                }
1090                        }
1091                        else if( arg.startsWith( "-timeout=" ) ) {
1092                                timeout = Integer.parseInt( arg.substring( "-timeout=".length() ) );
1093                        }
1094                        else if( arg.startsWith( "-encode=" ) ) {
1095                                encode = arg.substring( "-encode=".length() );
1096                                if( StringUtil.isNull( encode ) ) {
1097                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1098                                }
1099                        }
1100                        else if( arg.startsWith( "-out=" ) ) {
1101                                outFile = arg.substring( "-out=".length() );
1102                                if( StringUtil.isNull( outFile ) ) {
1103                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1104                                }
1105                                else {
1106                                        if( "null".equalsIgnoreCase( outFile ) || "none".equalsIgnoreCase( outFile ) ) {
1107                                                outFile   = null;
1108                                                nonWriter = true;
1109                                        }
1110                                }
1111                        }
1112                        else if( arg.startsWith( "-download=" ) ) {
1113                                dwldFile = arg.substring( "-download=".length() );
1114                                if( StringUtil.isNull( dwldFile ) ) {
1115                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1116                                }
1117                        }
1118                        else if( arg.startsWith( "-upload=" ) ) {
1119                                upldFile = arg.substring( "-upload=".length() );
1120                                if( StringUtil.isNull( upldFile ) ) {
1121                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1122                                }
1123                        }
1124                        else if( arg.startsWith( "-errEx=" ) ) {
1125                                isEx = "true".equalsIgnoreCase( arg.substring( "-errEx=".length() ) );
1126                        }
1127                        // 7.2.5.0 (2020/06/01) postRedirect(POST時に強制的にリダイレクト)
1128                        else if( arg.startsWith( "-postRedirect=" ) ) {
1129                                postRedirect = "true".equalsIgnoreCase( arg.substring( "-postRedirect=".length() ) );
1130                        }
1131                        else if( arg.startsWith( "-debug=" ) ) {
1132                                isDebug = "true".equalsIgnoreCase( arg.substring( "-debug=".length() ) );
1133                        }
1134                        else if( arg.startsWith( "-usePost=" ) ) {
1135                                isPost = "true".equalsIgnoreCase( arg.substring( "-usePost=".length() ) );
1136                        }
1137                        else if( arg.startsWith( "-authJson=" ) ) {                             // 8.0.0.0 (2021/08/31)
1138                                authJson = arg.substring( "-authJson=".length() );
1139                                if( StringUtil.isNull( authJson ) ) {
1140                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1141                                }
1142                        }
1143                        else if( arg.startsWith( "-authURL=" ) ) {                              // 8.0.0.0 (2021/08/31)
1144                                authURL = arg.substring( "-authURL=".length() );
1145                                if( StringUtil.isNull( authURL ) ) {
1146                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1147                                }
1148                        }
1149                        else if( arg.startsWith( "-reqJson=" ) ) {                              // 8.0.0.0 (2021/08/31)
1150                                reqJson = arg.substring( "-reqJson=".length() );
1151                                if( StringUtil.isNull( reqJson ) ) {
1152                                        System.err.println( arg + "指定した場合は、引数を設定してください。" );
1153                                }
1154                        }
1155                        else if( StringUtil.startsChar( arg , '-' ) ) {                 // 引数が未定義(処理は継続させます。)
1156                                System.err.println( "Error Argment:" + arg );
1157                        }
1158                        else if( StringUtil.startsChar( arg , '#' ) ) {                 // 引数がコメント
1159                                continue;
1160                        }
1161                        else {
1162                                urlStr = arg;
1163                        }
1164                }
1165
1166                try {                                                                   // try catch を入れます。
1167                        final HttpConnect conn = new HttpConnect( urlStr,userPass );
1168                        conn.usePost( isPost );
1169                        conn.setDebug( isDebug );                       // 最初に入れておけば、それ以降、有効になります。
1170
1171                        for( int i=0; i<paramKey.size(); i++ ) {
1172                                conn.addRequestProperty( paramKey.get(i) , paramVal.get(i) );
1173                        }
1174
1175                        for( int i=0; i<headerKey.size(); i++ ) {
1176                                conn.addHeaderProperty( headerKey.get(i) , headerVal.get(i) );
1177                        }
1178
1179                        // 6.8.1.3 (2017/08/04) proxy の設定
1180                        if( !StringUtil.isNull( proxy ) ) {
1181                                final String[] prm = StringUtil.csv2Array( proxy , ':' , 2 );
1182                                final String host = prm[0];
1183                                final int    port = Integer.parseInt( prm[1] );
1184                                conn.setProxy( host , port );
1185                        }
1186
1187                        conn.setCharset(                encode );                               // encode 指定
1188                        conn.setTimeout(                timeout );                              // timeout属性追加
1189                        conn.setUploadFile(             upldFile );
1190                        conn.setDownloadFile(   dwldFile );
1191                        conn.setPostRedirect(   postRedirect );                 // 7.2.5.0 (2020/06/01)
1192                        conn.setAuthJson(               authJson,authURL );             // 8.0.0.0 (2021/08/31)
1193                        conn.setReqJson(                reqJson );                              // 8.0.0.0 (2021/08/31)
1194
1195                        final String outData = conn.readData();                 // 8.0.0.0 (2021/08/20) テスト用
1196
1197                        try( PrintWriter writer = StringUtil.isNull( outFile )
1198                                                                                                ? FileUtil.getLogWriter( "System.out" )
1199                                                                                                : FileUtil.getPrintWriter( new File( outFile ),encode ) ) {
1200                                if( !nonWriter ) {
1201                                        writer.println( outData );
1202                                }
1203                                final int code = conn.getCode();
1204
1205                                // isEx=trueの場合、レスポンスコードが、4XX,5XX の時に RuntimeException を投げます
1206                                if( code >= 400 ) {
1207                                        final String errMsg = conn.getMessage();
1208                                        writer.println( errMsg );
1209                                        if( isEx ) {
1210                                                throw new OgRuntimeException( errMsg );
1211                                        }
1212                                        else {
1213                                                System.exit( code );
1214                                        }
1215                                }
1216                        }
1217                }
1218                catch( final Throwable th ) {
1219        //              throw new OgRuntimeException( th );
1220                        System.err.println( th.getMessage() );
1221                }
1222        }
1223}