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 */
016 package org.opengion.fukurou.transfer;
017
018 import java.io.ByteArrayInputStream;
019 import java.io.IOException;
020 import java.util.ArrayList;
021 import java.util.List;
022
023 import javax.xml.parsers.DocumentBuilder;
024 import javax.xml.parsers.DocumentBuilderFactory;
025
026 import org.opengion.fukurou.db.Transaction;
027 import org.opengion.fukurou.util.ApplicationInfo;
028 import org.opengion.fukurou.util.StringUtil;
029 import org.opengion.fukurou.util.URLConnect;
030 import org.w3c.dom.Document;
031 import org.w3c.dom.Element;
032 import org.w3c.dom.Node;
033 import org.w3c.dom.NodeList;
034
035 /**
036 * 伝é?要求に対して、HTTP経由でãƒ??タを読取しますã?
037 *
038 * 読取方法により読み取ったデータをPOSTãƒ??タとして送信しã?リモートã?スト上で
039 * 伝é?処ç�?‚’実行しますã?
040 *
041 * 処ç�??流れとしては以下ã?ようになりますã?
042 * â‘?TTPで読取å?ç�?‚’実行するためã?サーブレãƒ?ƒˆを呼び出すã?
043 * ②â‘?�§呼び出しされたサーブレãƒ?ƒˆがã?読取å?ç�?‚’実行するためã?オブジェクトを生æ?しã?
044 * 読取å?ç�?‚’実行するã?
045 * ③読取した結果XMLをパースしã?そã?読取データを伝é?オブジェクトに渡し伝é?処ç�?‚’実行するã?
046 * ④伝é?処ç�?®Ÿ行後ã?③の結果XMLから得られる更新キー(配å?)をPostãƒ??タとして渡しã?
047 * HTTP経由で後å?ç�?完äº??ç�?�¾たã?エラー処ç�?を実行するã?
048 *
049 * まず①につã�?�¦、呼び出しされるサーブレãƒ?ƒˆはã€?
050 * [リモート接続å?URL]servlet/remoteControl?class=TransferReadWrapper&type=read になりますã?
051 * [リモート接続å?URL]は、http://[ホスト名]:[ポã?ト番号]/[コンãƒ?‚スト名]の形式になりますがã€?
052 * これにつã�?�¦は、読取対象でæŒ?®šしますã?
053 * 接続時にエラーが発生したå?合や、レスポンスãƒ??タに対して"row_error"とã�?�†
054 * æ–?—å?が存在する場合ã?、エラーとして処ç�?�—ますã?
055 * それ以外ã?場合ã?、正常終äº?�¨して処ç�?�—ますã?
056 * 次に②につã�?�¦、サーブレãƒ?ƒˆ経由で行われる伝é?処ç�?�«つã�?�¦、その読取方法ã?ã€?
057 * こã?クラスを継承したå�?‚µブクラスのクラス名により決定されますã?
058 * 具体的には、サブクラスのクラス名に対して、このクラス(親クラス)のクラスå�?"_" を除外した部åˆ?�Œ
059 * 読取方法として認識されますã?
060 * 例として、サブクラス名がTransferRead_HTTP_CB01の場合ã?接続å?における読取方法ã?
061 * 旧伝é?DB読å�?CB01)となりますã?
062 * またã?リモートã?スト上で実行される伝é?処ç�??[リモート読取対象]は、å?の読取対象で設定しますã?
063 * こã?ことから、HTTP経由で読取å?ç�?‚’行う場合ã?å…??読取対象には、[リモート接続å?URL]と
064 * [リモート読取対象]の2つを設定するå¿?¦�がありますã?
065 * 具体的な設定方法につã�?�¦はå�?‚µブクラスのJavaDocを参照して下さã�??
066 * ③につã�?�¦、返されるXMLãƒ??タは[レスポンスãƒ??タのXML構é?]の形式になりますã?
067 * ここで、dataListは、伝é?実行オブジェクトに渡されるデータになりますã?
068 * またã?keyListにつã�?�¦は、伝é?実行終äº?¾Œã?HTTP経由で完äº??ç�?‚’行うための更新キーになりますã?
069 * (ローカルの伝é?読取オブジェクトã?トランザクションに対して同ä¸?‚ªブジェクトになりますがã€?
070 * HTTP接続å?でサーブレãƒ?ƒˆ経由で生æ?されるã?リモートã?伝é?読取オブジェクトã?ã€?
071 * 読取å?ç�?�¨完äº?エラー処ç�?�§異なりますã?こã?ため、読取したデータのキーをローカルに保持しã?
072 * æœ?¾Œã?完äº?エラー処ç�??際に、読取したデータのキー(更新キー)をPostDataとして渡すことでã€?
073 * 正しく完äº?エラー処ç�?�Œ行われるように対応してã�?�¾ã�?
074 * æœ?¾Œに④につã�?�¦、呼び出しされるサーブレãƒ?ƒˆはã€?
075 * [リモート接続å?URL]servlet/remoteControl?class=TransferReadWrapper&type=complete (正常終äº?™‚)
076 * [リモート接続å?URL]servlet/remoteControl?class=TransferReadWrapper&type=error (エラー発生時)
077 * となりますã?
078 *
079 * HTTP接続時には、以下ã?ポストデータがé?信されますã?
080 * [ポストデータ]
081 * ・KBREAD (読取方æ³? ※サブクラスのæœ?¾Œã?"_"(アンãƒ??バã?)以降ã?æ–?—å?
082 * ・READOBJ (リモート読取対象) ※ローカルの読取対象からリモート接続å?URLを除ã�?�Ÿæ–?—å?
083 * ・READPRM (読取パラメーター)
084 * ・KBEXEC (実行方æ³?
085 * ・EXECDBID (実行接続å?DBID)
086 * ・EXECOBJ (実行対象)
087 * ・EXECPRM (実行パラメーター)
088 * ・ERROR_SENDTO (読み取りå…??ストコーãƒ?
089 * ・HFROM (読み取りå…??ストコーãƒ?
090 * ・n (キー件数)
091 * ・k1?žkn (キー)
092 *
093 * またã?ãƒ??タ読取時に返されるXMLは以下ã?構é?をしてã�?�¾すã?
094 * [レスポンスãƒ??タのXML構é?]
095 * <root>
096 * <dataList>
097 * <data>aaa</data>
098 * <data>bbb</data>
099 * <data>ccc</data>
100 * <data>ddd</data>
101 * <data>eee</data>
102 * </dataList>
103 * <keyList>
104 * <key>KEY1</key>
105 * <key>KEY2</key>
106 * <key>KEY3</key>
107 * </keyList>
108 * </root>
109 *
110 * @og.group 伝é?シスãƒ?ƒ
111 *
112 * @version 5.0
113 * @author Hiroki.Nakamura
114 * @since JDK1.6
115 */
116 public abstract class TransferRead_HTTP implements TransferRead {
117
118 // リモート制御サーブレãƒ?ƒˆå�?
119 private static final String REMOTE_SERVLET = "servlet/remoteControl?class=TransferReadWrapper";
120
121 // 更新対象のキー
122 private String[] keys = null;
123
124 /**
125 * URL接続を行いãƒ??タを読み取りますã?
126 * 接続パラメータにはã€?type=read"が付加されますã?
127 *
128 * @param config 伝é?設定オブジェクãƒ?
129 * @param tran トランザクションオブジェクãƒ?
130 *
131 * @return 読み取りしたãƒ??タ(配å?)
132 */
133 @Override
134 public String[] read( final TransferConfig config, final Transaction tran ) {
135 splitReadObj( config.getReadObj() );
136 String url = getRemoteHost() + REMOTE_SERVLET+ "&type=read";
137 String postData = getPostData( keys, config );
138 URLConnect conn = null;
139 String data = null;
140 try {
141 conn = connect( url, postData, config );
142 data = readData( conn );
143 }
144 catch( IOException ex ) {
145 String errMsg = "URL接続時に例外が発生しましたã€?URL=" + url + "]";
146 throw new RuntimeException( errMsg, ex );
147 }
148 finally {
149 if( conn != null ) { conn.disconnect(); }
150 }
151
152 List<String> valList = new ArrayList<String>();
153 List<String> keyList = new ArrayList<String>();
154
155 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
156 Document doc = null;
157 try {
158 DocumentBuilder builder = dbfactory.newDocumentBuilder();
159 doc = builder.parse( new ByteArrayInputStream( data.getBytes( "UTF-8" ) ) );
160 }
161 catch( Exception ex ) {
162 String errMsg = "XMLパã?ス時にエラーが発生しましたã€?;
163 throw new RuntimeException( errMsg, ex );
164 }
165 Element root = doc.getDocumentElement();
166
167 // ãƒ??タ部åˆ?‚’取得しますã?
168 NodeList dataChilds = root.getElementsByTagName( "dataList" ).item(0).getChildNodes();
169 int numDataChild = dataChilds.getLength();
170 for( int i=0; i<numDataChild; i++ ) {
171 Node nd = dataChilds.item(i);
172 if( nd.getNodeType() == Node.ELEMENT_NODE ) {
173 if( "data".equals( ((Element)nd).getTagName() ) ) {
174 valList.add( nd.getTextContent() );
175 }
176 }
177 }
178
179 // 以降ã?処ç�?�§ãƒ??タを更新するためのキーを取得しますã?
180 NodeList keyChilds = root.getElementsByTagName( "keyList" ).item(0).getChildNodes();
181 int numKeyChild = keyChilds.getLength();
182 for( int i=0; i<numKeyChild; i++ ) {
183 Node nd = keyChilds.item(i);
184 if( nd.getNodeType() == Node.ELEMENT_NODE ) {
185 if( "key".equals( ((Element)nd).getTagName() ) ) {
186 keyList.add( nd.getTextContent() );
187 }
188 }
189 }
190 // keys = keyList.toArray( new String[0] );
191 keys = keyList.toArray( new String[keyList.size()] );
192
193 // return valList.toArray( new String[0] );
194 return valList.toArray( new String[valList.size()] );
195 }
196
197 /**
198 * 読取したデータに対して完äº??ç�?‚’行いますã?
199 * 接続パラメータにはã€?type=complete"が付加されますã?
200 *
201 * @param config 伝é?設定オブジェクãƒ?
202 * @param tran トランザクションオブジェクãƒ?
203 */
204 @Override
205 public void complete( final TransferConfig config, final Transaction tran ) {
206 splitReadObj( config.getReadObj() );
207 String url = getRemoteHost() + REMOTE_SERVLET + "&type=complete";
208 String postData = getPostData( keys, config );
209 URLConnect conn = null;
210 try {
211 conn = connect( url, postData, config );
212 }
213 catch( IOException ex ) {
214 String errMsg = "URL接続時に例外が発生しましたã€?URL=" + url + "]";
215 throw new RuntimeException( errMsg, ex );
216 }
217 finally {
218 if( conn != null ) { conn.disconnect(); }
219 }
220 }
221
222 /**
223 * 読取したデータに対してエラー処ç�?‚’行いますã?
224 * 接続パラメータにはã€?type=error"が付加されますã?
225 *
226 * @param config 伝é?設定オブジェクãƒ?
227 * @param appInfo DB接続情報
228 */
229 @Override
230 public void error( final TransferConfig config, final ApplicationInfo appInfo ) {
231 splitReadObj( config.getReadObj() );
232 String url = getRemoteHost() + REMOTE_SERVLET + "&type=error";
233 String postData = getPostData( keys, config );
234 URLConnect conn = null;
235 try {
236 conn = connect( url, postData, config );
237 }
238 catch( IOException ex ) {
239 String errMsg = "URL接続時に例外が発生しましたã€?URL=" + url + "]";
240 throw new RuntimeException( errMsg, ex );
241 }
242 finally {
243 if( conn != null ) { conn.disconnect(); }
244 }
245 }
246
247 /**
248 * (こã?クラスでは、サポã?トされてませんã€?
249 *
250 * @return 更新キー(配å?)
251 */
252 @Override
253 public String[] getKeys() {
254 String errMsg = "こã?クラスでは、サポã?トされてませんã€?;
255 throw new RuntimeException( errMsg );
256 }
257
258 /**
259 * (こã?クラスでは、サポã?トされてませんã€?
260 *
261 * @param keys 更新キー(配å?)
262 */
263 @Override
264 public void setKeys( final String[] keys ) {
265 String errMsg = "こã?クラスでは、サポã?トされてませんã€?;
266 throw new RuntimeException( errMsg );
267 }
268
269 /**
270 * ローカルの読取対象をã?リモート接続å?の読取対象とリモート接続å?URLにåˆ?§£しますã?
271 *
272 * @param localReadObj ローカルの読取対象
273 */
274 protected abstract void splitReadObj( final String localReadObj );
275
276 /**
277 * リモート接続å?URLを返しますã?
278 * こã?メソãƒ?ƒ‰は、{@link #splitReadObj(String)}の後に呼び出しするå¿?¦�がありますã?
279 *
280 * @return リモート接続å?URL
281 */
282 protected abstract String getRemoteHost();
283
284 /**
285 * リモート接続å?の読取対象を返しますã?
286 * こã?メソãƒ?ƒ‰は、{@link #splitReadObj(String)}の後に呼び出しするå¿?¦�がありますã?
287 *
288 * @return 接続URL
289 */
290 protected abstract String getRemoteReadObj();
291
292 /**
293 * æŒ?®šã?URLに接続しますã?
294 *
295 * @param url 接続URL
296 * @param postData POSTするãƒ??タ
297 * @param config 伝é?設定オブジェクãƒ?
298 *
299 * @return 接続オブジェクãƒ?
300 * @throws IOException なんらかã?エラーが発生したå?合ã?
301 */
302 protected URLConnect connect( final String url, final String postData, final TransferConfig config ) throws IOException {
303 URLConnect conn = new URLConnect( url, TransferConfig.HTTP_AUTH_USER_PASS );
304 if( config.getProxyHost() != null && config.getProxyHost().length() > 0 ) {
305 conn.setProxy( config.getProxyHost(),config.getProxyPort() );
306 }
307 conn.setCharset( "UTF-8" );
308 conn.setPostData( postData );
309 conn.connect();
310 return conn;
311 }
312
313 /**
314 * æŒ?®šã?URLに接続しレスポンスãƒ??タを返しますã?
315 *
316 * @param conn URL接続オブジェクãƒ?
317 *
318 * @return レスポンスãƒ??タ
319 */
320 private String readData( final URLConnect conn ) throws IOException {
321 String readData = conn.readData();
322 // 返されたãƒ??タ中に"row_error"が存在する場合ã?エラーとして処ç�?�—ますã?
323 if( readData != null && readData.indexOf( "row_error" ) >= 0 ) {
324 throw new RuntimeException( readData );
325 }
326 return readData;
327 }
328
329 /**
330 * 伝é?設定オブジェクトをURLパラメーターに変換しますã?
331 *
332 * @param keys 更新キー(配å?)
333 * @param config 伝é?設定オブジェクãƒ?
334 *
335 * @return URLパラメーター
336 */
337 protected String getPostData( final String[] keys, final TransferConfig config ) {
338 // サブクラス名から親クラスå�?"_"を除ã�?�Ÿ部åˆ?‚’読取方法とするã€?
339 String kbRead = getClass().getName().replace( getClass().getSuperclass().getName() + "_", "" );
340
341 StringBuilder buf = new StringBuilder();
342 buf.append( "KBREAD=" ).append( StringUtil.urlEncode( kbRead ) );
343 buf.append( "&READOBJ=" ).append( StringUtil.urlEncode( getRemoteReadObj() ) );
344 buf.append( "&READPRM=" ).append( StringUtil.urlEncode( config.getReadPrm() ) );
345 buf.append( "&KBEXEC=" ).append( StringUtil.urlEncode( config.getKbExec() ) );
346 buf.append( "&EXECDBID=" ).append( StringUtil.urlEncode( config.getExecDbid() ) );
347 buf.append( "&EXECOBJ=" ).append( StringUtil.urlEncode( config.getExecObj() ) );
348 buf.append( "&EXECPRM=" ).append( StringUtil.urlEncode( config.getExecPrm() ) );
349 buf.append( "&ERROR_SENDTO=").append( StringUtil.urlEncode( config.getErrorSendto() ) );
350 buf.append( "&HFROM=" ).append( StringUtil.urlEncode( config.getHfrom() ) );
351
352 if( keys != null && keys.length > 0 ) {
353 buf.append( "&n=" ).append( keys.length );
354 for( int i=0; i<keys.length; i++ ) {
355 buf.append( "&k" ).append( i ).append( "=" );
356 buf.append( StringUtil.urlEncode( keys[i] ) );
357 }
358 }
359 else {
360 buf.append( "&n=0" );
361 }
362
363 return buf.toString();
364 }
365 }