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