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     *
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     * 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[*] : [<-methodName=メソ??] メソ?名を?します?"                                                                                        );
091     *   args[*] : [<-keys=キー?>]   メソ?のパラメーターのキー?を指定します?(dataを指定した?合?無視されま?" );
092     *   args[*] : [<-vals=値?>]     メソ?のパラメーターの値?を指定します?  (dataを指定した?合?無視されま?" );
093     *   args[A] : <url>                ???を指定します?GETの場合?パラメータは ?KEY=VALです?
094     *   args[B] : [<user:passwd>]      BASIC認証のエリアへのアクセス時に?します?
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             * 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             */
302            public static void main( final String[] args ) throws IOException {
303                    if( args.length < 3 ) {
304                            LogWriter.log( "Usage: java org.opengion.fukurou.util.SOAPConnect [<-info/-data>] <url> [user:passwd]"                                              );
305                            LogWriter.log( "   args[*] : [<-info/-data>]      ??の取得か、データの取得かを指定しま?初期値:-data)?                                        );
306                            LogWriter.log( "   args[*] : [<-data=ファイル?] 送信する??タが記述されたXMLファイルを指定します?"                                         );
307                            LogWriter.log( "   args[*] : [<-out=ファイル?]  結果をファイルに出力します?ファイルエンコードも?します?"                                );
308                            LogWriter.log( "   args[*] : [<-nameSpace=名前空?]    メソ?名及びパラメーターの名前空間を?します?"                                   );
309                            LogWriter.log( "   args[*] : [<-methodName=メソ??] メソ?名を?します?"                                                                                  );
310                            LogWriter.log( "   args[*] : [<-keys=キー?>]   メソ?のパラメーターのキー?を指定します?(dataを指定した?合?無視されま?" );
311                            LogWriter.log( "   args[*] : [<-vals=値?>]     メソ?のパラメーターの値?を指定します?  (dataを指定した?合?無視されま?" );
312                            LogWriter.log( "   args[A] : <url>                ???を指定します?GETの場合?パラメータは ?KEY=VALです?"                           );
313                            LogWriter.log( "   args[B] : [<user:passwd>]      BASIC認証のエリアへのアクセス時に?します?"                                                     );
314                            return;
315                    }
316    
317                    boolean isInfo  = false ;
318                    String postFile = null ;
319                    String outFile  = null ;
320                    String ns               = null;
321                    String method   = null;
322                    String[] ks             = null;
323                    String[] vs             = null;
324                    String[] vals   = new String[2];                // url,userPass の?引数設?
325    
326                    int adrs = 0;
327                    for( int i=0; i<args.length; i++ ) {
328                            String arg = args[i];
329                            if( arg.equalsIgnoreCase( "-info" ) ) {
330                                    isInfo = true;
331                            }
332                            else if( arg.equalsIgnoreCase( "-data" ) ) {
333                                    isInfo = false;
334                            }
335                            else if( arg.startsWith( "-post=" ) ) {
336                                    postFile = arg.substring( 6 );
337                            }
338                            else if( arg.startsWith( "-out=" ) ) {
339                                    outFile = arg.substring( 5 );
340                            }
341                            else if( arg.startsWith( "-nameSpace=" ) ) {
342                                    ns = arg.substring( 11 );
343                            }
344                            else if( arg.startsWith( "-methodName=" ) ) {
345                                    method = arg.substring( 12 );
346                            }
347                            else if( arg.startsWith( "-keys=" ) ) {
348                                    ks = StringUtil.csv2Array( arg.substring( 6 ) );
349                            }
350                            else if( arg.startsWith( "-vals=" ) ) {
351                                    vs = StringUtil.csv2Array( arg.substring( 6 ) );
352                            }
353                            else if( arg.startsWith( "-" ) ) {
354                                    System.out.println( "Error Argment:" + arg );
355                            }
356                            else {
357                                    vals[adrs++] = arg;
358                            }
359                    }
360    
361                    String urlStr   = vals[0] ;
362                    String userPass = vals[1] ;
363    
364                    // POST ??タは、connect() する前に、設定します?
365                    URLConnect conn = null;
366                    if( postFile != null ) {
367                            FileString file = new FileString();
368                            file.setFilename( postFile );
369                            String postData = file.getValue();
370                            conn = new SOAPConnect( urlStr,userPass, ns, method, postData );
371                    }
372                    else {
373                            conn = new SOAPConnect( urlStr,userPass, ns, method, ks, vs );
374                    }
375    
376                    conn.connect();
377    
378                    final PrintWriter writer ;
379                    if( outFile != null ) {
380                            writer = FileUtil.getPrintWriter( new File( outFile ),"UTF-8" );
381                    }
382                    else {
383                            writer = FileUtil.getLogWriter( "System.out" );
384                    }
385    
386                    if( isInfo ) {
387                            writer.println( "URL    :" + conn.getUrl() );
388                            writer.println( "Type   :" + conn.getType() );
389                            writer.println( "Code   :" + conn.getCode() );
390                            writer.println( "Message:" + conn.getMessage() );
391                            writer.println( "Charset:" + conn.getCharset() );
392                    }
393                    else {
394                            writer.println( conn.readData() );
395                    }
396    
397                    conn.disconnect();
398    
399                    Closer.ioClose( writer );
400            }
401    }