    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.transfer;
    017
    018import java.io.IOException;
    019
    020import org.opengion.fukurou.db.Transaction;
    021import org.opengion.fukurou.util.StringUtil;
    022import org.opengion.fukurou.util.URLConnect;
    023
    024/**
    025 * 伝送要求に対してのHTTP経由で伝送処理を実行します。
    026 *
    027 * 読取方法により読み取ったデータをPOSTデータとして送信し、リモートホスト上で
    028 * 伝送処理を実行します。
    029 *
    030 * 処理の流れとしては以下のようになります。
    031 * ①HTTPで伝送処理を実行するためのサーブレットを呼び出す。
    032 * ②①で呼び出しされたサーブレットが、伝送処理を実行するためのオブジェクトを生成し、
    033 *   伝送処理を実行する。
    034 * ③処理終了後、伝送プロセスに制御が戻り、読取方法に基づく後処理(完了処理または
    035 *   エラー処理)が実行されます。
    036 *
    037 * まず①について、呼び出しされるサーブレットは、
    038 *   [リモート接続先URL]servlet/remoteControl?class=TransferExecWrapper になります。
    039 *   [リモート接続先URL]は、http://[ホスト名]:[ポート番号]/[コンテキスト名]の形式になりますが、
    040 *   これについては、実行対象で指定します。
    041 * 次に②について、サーブレット経由で実行される伝送処理について、その実行方法は、
    042 *   具体的には、サブクラスのクラス名に対して、このクラス(親クラス)のクラス名+"_" を除外した部分が
    043 *   実行方法として認識されます。
    044 *   具体的には、クラス名の最後の"_"(アンダーバー)以降が実行方法として認識され、
    045 *   例として、サブクラス名がTransferExec_HTTP_CB01の場合、接続先における実行方法は
    046 *   旧伝送DB登録(CB01)となります。
    047 *   また、リモートホスト上で実行される伝送処理の[リモート実行対象]は、元の実行対象で設定します。
    048 *   このことから、HTTP経由で伝送処理を実行する場合、元の実行対象には、[リモート接続先URL]と
    049 *   [リモート実行対象]の2つを設定する必要があります。
    050 *   具体的な設定方法については各サブクラスのJavaDocを参照して下さい。
    051 * 最後に③について、接続時にエラーが発生した場合や、レスポンスデータに対して"row_error"という
    052 *   文字列が存在する場合は、エラーとして処理します。
    053 *   それ以外の場合は、正常終了として処理します。
    054 *
    055 * HTTP接続時には、以下のポストデータが送信されます。
    056 * [ポストデータ]
    057 * ・KBREAD                      (読取方法)
    058 * ・READOBJ             (読取対象)
    059 * ・READPRM             (読取パラメーター)
    060 * ・KBEXEC                      (実行方法) ※サブクラスの最後の"_"(アンダーバー)以降の文字列
    061 * ・EXECDBID            (実行接続先DBID)
    062 * ・EXECOBJ             (リモート実行対象) ※ローカルの実行対象からリモート接続先URLを除いた文字列
    063 * ・EXECPRM             (実行パラメーター)
    064 * ・ERROR_SENDTO        (読み取り元ホストコード)
    065 * ・HFROM (読み取り元ホストコード)
    066 * ・n (データ件数)
    067 * ・v1～vn (データ)
    068 *
    069 * @og.group 伝送システム
    070 *
    071 * @version  5.0
    072 * @author   Hiroki.Nakamura
    073 * @since    JDK1.6
    074 */
    075public abstract class TransferExec_HTTP implements TransferExec {
    076
    077        // リモート制御サーブレット名
    078        private static final String REMOTE_SERVLET = "servlet/remoteControl?class=TransferExecWrapper";
    079
    080        /**
    081         * URL接続を実行します。
    082         *
    083         * @param vals 伝送データ(配列)
    084         * @param config 伝送設定オブジェクト
    085         * @param tran トランザクションオブジェクト
    086         */
    087        @Override
    088        public void execute( final String[] vals, final TransferConfig config, final Transaction tran ) {
    089                URLConnect conn = null;
    090                try {
    091                        splitExecObj( config.getExecObj() );
    092                        conn = new URLConnect( getRemoteHost() + REMOTE_SERVLET, TransferConfig.HTTP_AUTH_USER_PASS );
    093                        if( config.getProxyHost() != null && config.getProxyHost().length() > 0 ) {
    094                                conn.setProxy( config.getProxyHost(), config.getProxyPort() );
    095                        }
    096
    097                        conn.setCharset( "UTF-8" );
    098                        // ポストデータを生成します。
    099                        conn.setPostData( getPostData( vals, config ) );
    100
    101                        conn.connect();
    102
    103                        String readData = conn.readData();
    104                        // 返されたデータ中に"row_error"が存在する場合はエラーとして処理します。
    105                        if( readData != null && readData.indexOf( "row_error" ) >= 0 ) {
    106                                throw new RuntimeException( readData );
    107                        }
    108                }
    109                catch( IOException ex ) {
    110                        String errMsg = "URL接続時に例外が発生しました。";
    111                        throw new RuntimeException( errMsg, ex );
    112                }
    113                finally {
    114                        if( conn != null ) { conn.disconnect(); }
    115                }
    116        }
    117
    118        /**
    119         * ローカルの実行対象を、リモート接続先の実行対象とリモート接続先URLに分解します。
    120         *
    121         * @param localExecObj ローカルの実行対象
    122         */
    123        protected abstract void splitExecObj( final String localExecObj );
    124
    125        /**
    126         * リモート接続先URLを返します。
    127         * このメソッドは、{@link #splitExecObj(String)}の後に呼び出しする必要があります。
    128         *
    129         * @return リモート接続先URL
    130         */
    131        protected abstract String getRemoteHost();
    132
    133        /**
    134         * リモート接続先の実行対象を返します。
    135         * このメソッドは、{@link #splitExecObj(String)}の後に呼び出しする必要があります。
    136         *
    137         * @return 接続URL
    138         */
    139        protected abstract String getRemoteExecObj();
    140
    141        /**
    142         * 伝送データ及び伝送設定をPOSTデータとしてシリアライズします。
    143         *
    144         * @param vals 伝送データ(配列)
    145         * @param config 伝送設定オブジェクト
    146         *
    147         * @return ポストデータ
    148         */
    149        private String getPostData( final String[] vals, final TransferConfig config ) {
    150                // サブクラス名から親クラス名+"_"を除いた部分を実行方法とする。
    151                String kbExec = getClass().getName().replace( getClass().getSuperclass().getName() + "_", "" );
    152
    153                StringBuilder buf = new StringBuilder();
    154                buf.append( "KBREAD="           ).append( StringUtil.urlEncode( config.getKbRead() ) );
    155                buf.append( "&READOBJ="             ).append( StringUtil.urlEncode( config.getReadObj() ) );
    156                buf.append( "&READPRM="             ).append( StringUtil.urlEncode( config.getReadPrm() ) );
    157                buf.append( "&KBEXEC="              ).append( StringUtil.urlEncode( kbExec ) );
    158                buf.append( "&EXECDBID="    ).append( StringUtil.urlEncode( config.getExecDbid() )  );
    159                buf.append( "&EXECOBJ="             ).append( StringUtil.urlEncode( getRemoteExecObj() ) );
    160                buf.append( "&EXECPRM="             ).append( StringUtil.urlEncode( config.getExecPrm() ) );
    161                buf.append( "&ERROR_SENDTO=").append( StringUtil.urlEncode( config.getErrorSendto() ) );
    162                buf.append( "&HFROM="               ).append( StringUtil.urlEncode( config.getHfrom() ) ) ;
    163
    164                if( vals != null && vals.length > 0 ) {
    165                        buf.append( "&n=" ).append( vals.length );
    166                        for( int i=0; i<vals.length; i++ ) {
    167                                buf.append( "&v" ).append( i ).append( "=" );
    168                                buf.append( StringUtil.urlEncode( vals[i] ) );
    169                        }
    170                }
    171                else {
    172                        buf.append( "&n=0" );
    173                }
    174
    175                return buf.toString();
    176        }
    177}