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.util;
017
018 import java.io.File;
019 import java.io.IOException;
020 import java.io.PrintWriter;
021 import java.net.URLConnection;
022
023 /**
024 * SOAPConnect は、URLConnectクラスの拡張版で、SOAP接続を行うための機?を追?て?す?
025 * 基本?機?は、{@link org.opengion.fukurou.util.URLConnect}を参照して下さ??
026 *
027 * SOAP対応?追??としては、以下?2点があります?
028 * <pre>
029 * ???ー??の変更
030 * SOAP接続では??常のURLConnectに?て、以下?ヘッ????を付加して?す?
031 *
032 * 1.Content = text/xml;charset=UTF-8"
033 * 2.Accept = text/xml, multipart/related, text/html, image/gif, image/jpeg,
034 * 3.Soapaction = "[NameSpace][SOAPMethodName]"
035 *
036 * ②SOAPメ?ージの生?機?
037 * SOAPメ?ージは、以下?ようなXML構?で定義されます?
038 *
039 * <env:Envelope env:xmlns="..." xsi:xmlns="...">
040 * <env:Body>
041 * <tns:[methodName] xmlns:tns="[nameSpace]">
042 * [methodParameter(XML)]
043 * </tns:[methodName]>
044 * </env:Body>
045 * </env:Envelope>
046 *
047 * SOAPConnectクラスでは、[methodParameter(メソ?パラメーター)]の定義方法に2種類?方法があります?
048 *
049 * (a)keys,valsによる??
050 * keys,valsを指定することで、これらを?部?XML??タに変換し?パラメーター部??XML
051 * ??を生成します?
052 *
053 * ?
054 * keys="param0>AAA,param0>BBB,param1>CCC,DDD"
055 * vals="v1,v2,v3,v4"
056 * [変換結果]
057 * <param0>
058 * <AAA>v1</AAA>
059 * <BBB>v2</BBB>
060 * </param0>
061 * <param1>
062 * <CCC>v3</CCC>
063 * </param1>
064 * <DDD>v4</DDD>
065 *
066 * こ?定義方法では??目の値?null"とすることで、XMLで?ところの
067 * 「xsi:nil="true"」???タを表現することもできます?
068 *
069 * また?キー名?先??@'にすることで??目名に名前空間?PREFIXを付加することができます?
070 * ??は、JavaやRubyで実?れたWebサービスを呼び出しする?合?、?ありませんが?
071 * .NETで実?れたWebサービスを呼び出しする?合?、各?にPREFIXを付与しな?、正しく
072 * パラメーターを渡すことができません?
073 *
074 * ※現時点では、keysの階層定義は?階層まで対応して?す?
075 * 3階層以上?XML構?を定義する場合?、postFile属?によるファイル?又は、Body部?直接
076 * XML??タを記述して下さ??
077 *
078 * (b)XML??タを直接??
079 * メソ?パラメーターの部??XML??タを直接?します?
080 * こ?場合?(a)のように、xsi:nil="true"??パラメーターキーへのPREFIXの付加は、予め行って
081 * おく?があります?
082 * なお?パラメーターキーのPREFIXは?tns:"です?
083 *
084 * Usage: java org.opengion.fukurou.util.SOAPConnect [-info/-data] … url [user:passwd]
085 *
086 * args[*] : [-info/-data] ??の取得か、データの取得かを指定しま?初期値:-data)?
087 * args[*] : [-data=ファイル名] メソ?のパラメーターが記述されたXMLファイルを指定します?
088 * args[*] : [-out=ファイル名] 結果をファイルに出力します?ファイルエンコードも?します?
089 * args[*] : [-nameSpace=名前空間] メソ?名及びパラメータ
090 * args[*] : [-keys=キー?] メソ?のパラメーターのキー?を指定します?(dataを指定した?合?無視されま?
091 * args[*] : [-vals=値?] メソ?のパラメーターの値?を指定します? (dataを指定した?合?無視されま?
092 * args[A] : url ???を指定します?GETの場合?パラメータは ?KEY=VALです?
093 * args[B] : [user:passwd] BASIC認証のエリアへのアクセス時に?します?
094 * </pre>
095 *
096 * @version 4.0
097 * @author Hiroki Nakamura
098 * @since JDK5.0,
099 */
100 public class SOAPConnect extends URLConnect {
101
102 private static final String CONTENT_TYPE = "text/xml;charset=UTF-8";
103 private static final String ACCEPT = "text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
104
105 final private String nameSpace;
106 final private String methodName;
107
108 /**
109 * コンストラクター
110 *
111 * ここでは??信するXML??タをキー及?値の?から生?するためのコンストラクターを定義して?す?
112 *
113 * @param url 接続するアドレスを指定します?(http://server:port/dir/file.html)
114 * @param pass ユーザー?パスワー?認証接続が?な場?
115 * @param ns 名前空間を?します?
116 * @param method メソ?名を?します?
117 * @param ks 送信するメソ?パラメーターのキー?を指定します?
118 * @param vs 送信するパラメーターの値?を指定します?
119 */
120 public SOAPConnect( final String url, final String pass, final String ns, final String method, final String[] ks, final String[] vs ) {
121 this( url, pass, ns, method, makeParamData( ks, vs ) );
122 }
123
124 /**
125 * コンストラクター
126 *
127 * ここでは??信するXML??タを直接?するため?コンストラクターを定義して?す?
128 *
129 * @param url 接続するアドレスを指定します?(http://server:port/dir/file.html)
130 * @param pass ユーザー?パスワー?認証接続が?な場?
131 * @param ns 名前空間を?します?
132 * @param method メソ?名を?します?
133 * @param xmlData パラメーターのXML??タ
134 */
135 public SOAPConnect( final String url, final String pass, final String ns, final String method, final String xmlData ) {
136 super( url, pass );
137 nameSpace = ns;
138 methodName = method;
139 setPostData( getSoapEnvelop( xmlData ) );
140 }
141
142 /**
143 * URL と ユーザー?パスワードを与えて、URLConnectionを返します?
144 *
145 * SOAP接続では??常のURLConnectに?て、以下?ヘッ????を付加して?す?
146 *
147 * 1.Content = text/xml;charset=UTF-8"
148 * 2.Accept = text/xml, multipart/related, text/html, image/gif, image/jpeg,
149 * 3.Soapaction = "[NameSpace][SOAPMethodName]"
150 *
151 * @return URLConnectionオブジェク?
152 * @throws IOException 入出力エラーが発生したと?
153 * @see URLConnect#getConnection()
154 */
155 @Override
156 protected URLConnection getConnection() throws IOException {
157 URLConnection urlConn = super.getConnection();
158 urlConn.setRequestProperty( "Content-Type", CONTENT_TYPE );
159 urlConn.setRequestProperty( "Accept", ACCEPT );
160 urlConn.setRequestProperty( "Soapaction", "\"" + nameSpace + methodName + "\"" );
161 return urlConn;
162 }
163
164 /**
165 * メソ?パラメーターのXML??タに、Envelop??を付加し?SOAP通信を行うための
166 * 完?なXML??を生成します?
167 *
168 * @param data メソ?のパラメーターとなるXML??タ
169 *
170 * @return SOAPで送信される?体?XML??
171 */
172 private String getSoapEnvelop( final String data ) {
173 StringBuilder buf = new StringBuilder();
174
175 buf.append( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" );
176 buf.append( "<env:Envelope " );
177 buf.append( " xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\" " );
178 buf.append( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><env:Body>" );
179 buf.append( "<tns:" ).append( methodName ).append( " xmlns:tns=\"" ).append( nameSpace ).append( "\">" );
180 buf.append( data );
181 buf.append( "</tns:" ).append( methodName ).append( ">" );
182 buf.append( "</env:Body></env:Envelope>" );
183
184 return buf.toString();
185 }
186
187 /**
188 * キー?と値?から、メソ?パラメータのXML??タを生成します?
189 *
190 * @param keys キー?
191 * @param vals 値?
192 *
193 * @return メソ?パラメーターのXML??タ
194 */
195 private static final String makeParamData( final String[] keys, final String[] vals ) {
196 StringBuilder buf = new StringBuilder();
197
198 String preParentKey = null;
199 String key = null;
200 for( int i = 0; i < keys.length; i++ ) {
201 String[] keyTags = StringUtil.csv2Array( keys[i], '>' );
202
203 if( keyTags.length > 2 ) {
204 String errMsg = "keys,vals形式では2階層までの??タのみを定義することができます?"
205 + "3階層以上???タに関しては、直接XMLでメソ?パラメーターを指定して下さ??";
206 throw new RuntimeException( errMsg );
207 }
208
209 // 親の終?グを??
210 if( preParentKey != null
211 && ( keyTags.length == 1 || !preParentKey.equals( keyTags[0] ) ) ) {
212 buf.append( getKeyTag( preParentKey, true ) );
213 }
214
215 if( keyTags.length > 1 ) {
216 // 親の開始タグを??
217 if( preParentKey == null || !preParentKey.equals( keyTags[0] ) ) {
218 buf.append( getKeyTag( keyTags[0], false ) );
219 }
220 key = keyTags[1];
221 preParentKey = keyTags[0];
222 }
223 else {
224 key = keyTags[0];
225 preParentKey = null;
226 }
227
228 // 値の出?
229 if( "null".equals( vals[i] ) ) {
230 buf.append( getKeyTag( key, false, "xsi:nil=\"true\"" ) );
231 }
232 else {
233 buf.append( getKeyTag( key, false ) ).append( vals[i] );
234 }
235 buf.append( getKeyTag( key, true ) );
236 }
237
238 // 親の終?グの出?
239 if( preParentKey != null ) {
240 buf.append( getKeyTag( preParentKey, true ) );
241 }
242
243 return buf.toString();
244 }
245
246 /**
247 * タグキーからタグ??を生成します?
248 *
249 * @param key タグキー
250 * @param isEnd 終?グ or 開始タグ
251 *
252 * @return タグ??
253 */
254 private static final String getKeyTag( final String key, final boolean isEnd ) {
255 return getKeyTag( key, isEnd, null );
256 }
257
258 /**
259 * タグキーからタグ??を生成します?
260 *
261 * @param key タグキー
262 * @param isEnd 終?グ or 開始タグ
263 * @param attr 属?値
264 *
265 * @return タグ??
266 */
267 private static final String getKeyTag( final String key, final boolean isEnd, final String attr ) {
268 StringBuilder buf = new StringBuilder();
269 buf.append( '<' );
270 if( isEnd ) { buf.append( '/' ); }
271
272 boolean isQualified = ( key.charAt( 0 ) == '@' );
273 String tmp = ( isQualified ? key.substring( 1 ) : key );
274 if( isQualified ) {
275 buf.append( "tns:" );
276 }
277 buf.append( tmp );
278 if( attr != null && attr.length() > 0 ) {
279 buf.append( " " ).append( attr );
280 }
281 buf.append( '>' );
282 return buf.toString();
283 }
284
285 /**
286 * サンプル実行用のメインメソ?
287 *
288 * Usage: java org.opengion.fukurou.util.SOAPConnect [-info/-data] … url [user:passwd]
289 *
290 * args[*] : [-info/-data] ??の取得か、データの取得かを指定しま?初期値:-data)?
291 * args[*] : [-data=ファイル名] 送信する??タが記述されたXMLファイルを指定します?
292 * args[*] : [-out=ファイル名] 結果をファイルに出力します?ファイルエンコードも?します?
293 * args[*] : [-nameSpace=名前空間] メソ?名及びパラメーターの名前空間を?します?
294 * args[*] : [-methodName=メソ?名] メソ?名を?します?
295 * args[*] : [-keys=キー?] メソ?のパラメーターのキー?を指定します?(dataを指定した?合?無視されま?
296 * args[*] : [-vals=値?] メソ?のパラメーターの値?を指定します? (dataを指定した?合?無視されま?
297 * args[A] : url ???を指定します?GETの場合?パラメータは ?KEY=VALです?
298 * args[B] : [user:passwd] BASIC認証のエリアへのアクセス時に?します?
299 *
300 * @param args コマンド引数配?
301 * @throws IOException 入出力エラーが発生した??
302 */
303 public static void main( final String[] args ) throws IOException {
304 if( args.length < 3 ) {
305 LogWriter.log( "Usage: java org.opengion.fukurou.util.SOAPConnect [-info/-data] … url [user:passwd]" );
306 LogWriter.log( " args[*] : [-info/-data] ??の取得か、データの取得かを指定しま?初期値:-data)? );
307 LogWriter.log( " args[*] : [-data=ファイル名] 送信する??タが記述されたXMLファイルを指定します?" );
308 LogWriter.log( " args[*] : [-out=ファイル名] 結果をファイルに出力します?ファイルエンコードも?します?" );
309 LogWriter.log( " args[*] : [-nameSpace=名前空間] メソ?名及びパラメーターの名前空間を?します?" );
310 LogWriter.log( " args[*] : [-methodName=メソ?名] メソ?名を?します?" );
311 LogWriter.log( " args[*] : [-keys=キー?] メソ?のパラメーターのキー?を指定します?(dataを指定した?合?無視されま?" );
312 LogWriter.log( " args[*] : [-vals=値?] メソ?のパラメーターの値?を指定します? (dataを指定した?合?無視されま?" );
313 LogWriter.log( " args[A] : url ???を指定します?GETの場合?パラメータは ?KEY=VALです?" );
314 LogWriter.log( " args[B] : [user:passwd] BASIC認証のエリアへのアクセス時に?します?" );
315 return;
316 }
317
318 boolean isInfo = false ;
319 String postFile = null ;
320 String outFile = null ;
321 String ns = null;
322 String method = null;
323 String[] ks = null;
324 String[] vs = null;
325 String[] vals = new String[2]; // url,userPass の?引数設?
326
327 int adrs = 0;
328 for( int i=0; i<args.length; i++ ) {
329 String arg = args[i];
330 if( arg.equalsIgnoreCase( "-info" ) ) {
331 isInfo = true;
332 }
333 else if( arg.equalsIgnoreCase( "-data" ) ) {
334 isInfo = false;
335 }
336 else if( arg.startsWith( "-post=" ) ) {
337 postFile = arg.substring( 6 );
338 }
339 else if( arg.startsWith( "-out=" ) ) {
340 outFile = arg.substring( 5 );
341 }
342 else if( arg.startsWith( "-nameSpace=" ) ) {
343 ns = arg.substring( 11 );
344 }
345 else if( arg.startsWith( "-methodName=" ) ) {
346 method = arg.substring( 12 );
347 }
348 else if( arg.startsWith( "-keys=" ) ) {
349 ks = StringUtil.csv2Array( arg.substring( 6 ) );
350 }
351 else if( arg.startsWith( "-vals=" ) ) {
352 vs = StringUtil.csv2Array( arg.substring( 6 ) );
353 }
354 else if( arg.startsWith( "-" ) ) {
355 System.out.println( "Error Argment:" + arg );
356 }
357 else {
358 vals[adrs++] = arg;
359 }
360 }
361
362 String urlStr = vals[0] ;
363 String userPass = vals[1] ;
364
365 // POST ??タは、connect() する前に、設定します?
366 URLConnect conn = null;
367 if( postFile != null ) {
368 FileString file = new FileString();
369 file.setFilename( postFile );
370 String postData = file.getValue();
371 conn = new SOAPConnect( urlStr,userPass, ns, method, postData );
372 }
373 else {
374 conn = new SOAPConnect( urlStr,userPass, ns, method, ks, vs );
375 }
376
377 conn.connect();
378
379 final PrintWriter writer ;
380 if( outFile != null ) {
381 writer = FileUtil.getPrintWriter( new File( outFile ),"UTF-8" );
382 }
383 else {
384 writer = FileUtil.getLogWriter( "System.out" );
385 }
386
387 if( isInfo ) {
388 writer.println( "URL :" + conn.getUrl() );
389 writer.println( "Type :" + conn.getType() );
390 writer.println( "Code :" + conn.getCode() );
391 writer.println( "Message:" + conn.getMessage() );
392 writer.println( "Charset:" + conn.getCharset() );
393 }
394 else {
395 writer.println( conn.readData() );
396 }
397
398 conn.disconnect();
399
400 Closer.ioClose( writer );
401 }
402 }