source: release-kits/wirk3/ant-scripts/tasks/antelope/src/ise/antelope/tasks/PostTask.java@ 15023

Last change on this file since 15023 was 15023, checked in by oranfry, 16 years ago

did the bulk of the work on wirk3

File size: 31.6 KB
Line 
1/*
2* The Apache Software License, Version 1.1
3*
4* Copyright (c) 2001-2002 The Apache Software Foundation. All rights
5* reserved.
6*
7* Redistribution and use in source and binary forms, with or without
8* modification, are permitted provided that the following conditions
9* are met:
10*
11* 1. Redistributions of source code must retain the above copyright
12* notice, this list of conditions and the following disclaimer.
13*
14* 2. Redistributions in binary form must reprodconnectione the above copyright
15* notice, this list of conditions and the following disclaimer in
16* the documentation and/or other materials provided with the
17* distribution.
18*
19* 3. The end-user documentation included with the redistribution, if
20* any, must include the following acknowlegement:
21* "This prodconnectiont includes software developed by the
22* Apache Software Foundation (http://www.apache.org/)."
23* Alternately, this acknowlegement may appear in the software itself,
24* if and wherever sconnectionh third-party acknowlegements normally appear.
25*
26* 4. The names "The Jakarta Project", "Ant", and "Apache Software
27* Foundation" must not be used to endorse or promote prodconnectionts derived
28* from this software without prior written permission. For written
29* permission, please contact [email protected].
30*
31* 5. Prodconnectionts derived from this software may not be called "Apache"
32* nor may "Apache" appear in their names without prior written
33* permission of the Apache Group.
34*
35* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46* SUCH DAMAGE.
47* ====================================================================
48*
49* This software consists of voluntary contributions made by many
50* individuals on behalf of the Apache Software Foundation. For more
51* information on the Apache Software Foundation, please see
52* <http://www.apache.org/>.
53*/
54package ise.antelope.tasks;
55
56import java.io.BufferedInputStream;
57import java.io.BufferedReader;
58import java.io.DataOutputStream;
59
60import java.io.File;
61import java.io.FileInputStream;
62import java.io.FileOutputStream;
63import java.io.FileWriter;
64import java.io.IOException;
65import java.io.InputStreamReader;
66import java.io.PrintWriter;
67import java.io.StringWriter;
68import java.net.HttpURLConnection;
69
70import java.net.URL;
71import java.net.URLConnection;
72import java.net.URLDecoder;
73import java.net.URLEncoder;
74
75import java.rmi.server.UID;
76
77import java.util.*;
78
79import org.apache.tools.ant.BuildException;
80import org.apache.tools.ant.Project;
81import org.apache.tools.ant.ProjectHelper;
82import org.apache.tools.ant.Task;
83import org.apache.tools.ant.TaskContainer;
84
85import ise.antelope.tasks.typedefs.Prop;
86
87/**
88 * This task does an http post. Name/value pairs for the post can be set in
89 * either or both of two ways, by nested Prop elements and/or by a file
90 * containing properties. Nested Prop elements are automatically configured by
91 * Ant. Properties from a file are configured by code borrowed from Property so
92 * all Ant property constructs (like ${somename}) are resolved prior to the
93 * post. This means that a file can be set up in advance of running the build
94 * and the appropriate property values will be filled in at run time.
95 *
96 * @author Dale Anson, [email protected]
97 * @version $Revision: 1.11 $
98 */
99public class PostTask extends Task {
100
101 /** Storage for name/value pairs to send. */
102 private Hashtable props = new Hashtable();
103 /** URL to send the name/value pairs to. */
104 private URL to = null;
105 /** File to read name/value pairs from. */
106 private File propsFile = null;
107 /** storage for Ant properties */
108 private String textProps = null;
109 /** encoding to use for the name/value pairs */
110 private String encoding = "UTF-8";
111 /** where to store the server response */
112 private File log = null;
113 /** append to the log? */
114 private boolean append = true;
115 /** verbose? */
116 private boolean verbose = true;
117 /** want to keep the server response? */
118 private boolean wantResponse = true;
119 /** store output in this property */
120 private String property = null;
121 /** where to write downloads */
122 private File outdir = null;
123
124 /** how long to wait for a response from the server */
125 private long maxwait = 180000; // units for maxwait is milliseconds
126 /** fail on error? */
127 private boolean failOnError = false;
128
129 // storage for cookies, static so that cookies are shared between instances
130 // of Post. This allows a <post> to follow a previous <post> and use the
131 // cookies that may have been set in the first post.
132 private static Hashtable cookieStorage = new Hashtable();
133
134 /** connection to the server */
135 private URLConnection connection = null;
136
137 /** for thread handling */
138 private Thread currentRunner = null;
139
140
141
142 /**
143 * Set the url to post to. Required.
144 *
145 * @param name the url to post to.
146 */
147 public void setTo( URL name ) {
148 to = name;
149 }
150
151
152 /**
153 * Set a directory to store files that may be returned from the post.
154 * @param dir the directory
155 */
156 public void setOutdir( File dir) {
157 outdir = dir;
158 if (outdir != null && outdir.isFile()) {
159 outdir = outdir.getParentFile();
160 }
161 }
162
163
164 /**
165 * Set the name of a file to read a set of properties from.
166 *
167 * @param f the file
168 */
169 public void setFile( File f ) {
170 propsFile = f;
171 }
172
173
174 /**
175 * Set the name of a file to save the response to. Optional. Ignored if
176 * "want response" is false.
177 *
178 * @param f the file
179 */
180 public void setLogfile( File f ) {
181 log = f;
182 }
183
184
185 /**
186 * Should the log file be appended to or overwritten? Default is true,
187 * append to the file.
188 *
189 * @param b append or not
190 */
191 public void setAppend( boolean b ) {
192 append = b;
193 }
194
195
196 /**
197 * If true, progress messages and returned data from the post will be
198 * displayed. Default is true.
199 *
200 * @param b true = verbose
201 */
202 public void setVerbose( boolean b ) {
203 verbose = b;
204 }
205
206
207 /**
208 * Default is true, get the response from the post. Can be set to false for
209 * "fire and forget" messages.
210 *
211 * @param b print/log server response
212 */
213 public void setWantresponse( boolean b ) {
214 wantResponse = b;
215 }
216
217 /**
218 * Set the name of a property to save the response to. Optional. Ignored if
219 * "wantResponse" is false.
220 * @param name the name to use for the property
221 */
222 public void setProperty( String name ) {
223 property = name;
224 }
225
226 /**
227 * Sets the encoding of the outgoing properties, default is UTF-8.
228 *
229 * @param encoding The new encoding value
230 */
231 public void setEncoding( String encoding ) {
232 this.encoding = encoding;
233 }
234
235
236 /**
237 * How long to wait on the remote server. As a post is generally a two part
238 * process (sending and receiving), maxwait is applied separately to each
239 * part, that is, if 180 is passed as the wait parameter, this task will
240 * spend at most 3 minutes to connect to the remote server and at most
241 * another 3 minutes waiting on a response after the post has been sent.
242 * This means that the wait period could total as much as 6 minutes (or 360
243 * seconds). <p>
244 *
245 * The default wait period is 3 minutes (180 seconds).
246 *
247 * @param wait time to wait in seconds, set to 0 to wait forever.
248 */
249 public void setMaxwait( int wait ) {
250 maxwait = wait * 1000;
251 }
252
253
254 /**
255 * Should the build fail if the post fails?
256 *
257 * @param fail true = fail the build, default is false
258 */
259 public void setFailonerror( boolean fail ) {
260 failOnError = fail;
261 }
262
263 /**
264 * Adds a name/value pair to post. Optional.
265 *
266 * @param p A property pair to send as part of the post.
267 * @exception BuildException When name and/or value are missing.
268 */
269 public void addConfiguredProp( Prop p ) throws BuildException {
270 String name = p.getName();
271 if ( name == null ) {
272 throw new BuildException( "name is null", getLocation() );
273 }
274 String value = p.getValue();
275 if ( value == null ) {
276 value = getProject().getProperty( name );
277 }
278 if ( value == null ) {
279 throw new BuildException( "value is null", getLocation() );
280 }
281 props.put( name, value );
282 }
283
284
285 /**
286 * Adds a feature to the Text attribute of the PostTask object
287 *
288 * @param text The feature to be added to the Text attribute
289 */
290 public void addText( String text ) {
291 textProps = text;
292 }
293
294
295 /**
296 * Do the post.
297 *
298 * @exception BuildException On any error.
299 */
300 public void execute() throws BuildException {
301 if ( to == null ) {
302 throw new BuildException( "'to' attribute is required", getLocation() );
303 }
304 final String content = getContent();
305 try {
306 if ( verbose )
307 log( "Opening connection for post to " + to.toString() + "..." );
308
309 // do the POST
310 Thread runner =
311 new Thread() {
312 public void run() {
313 DataOutputStream out = null;
314 try {
315 // set the url connection properties
316 connection = to.openConnection();
317 connection.setDoInput( true );
318 connection.setDoOutput( true );
319 connection.setUseCaches( false );
320 connection.setRequestProperty(
321 "Content-Type",
322 "application/x-www-form-urlencoded" );
323
324 // check if there are cookies to be included
325 for ( Iterator it = cookieStorage.keySet().iterator(); it.hasNext(); ) {
326 StringBuffer sb = new StringBuffer();
327 Object name = it.next();
328 if ( name != null ) {
329 String key = name.toString();
330 Cookie cookie = ( Cookie ) cookieStorage.get( name );
331 if ( to.getPath().startsWith( cookie.getPath() ) ) {
332 connection.addRequestProperty( "Cookie", cookie.toString() );
333 if (verbose)
334 log("Added cookie: " + cookie.toString());
335 }
336 }
337 }
338
339 // do the post
340 if ( verbose ) {
341 log( "Connected, sending data..." );
342 }
343 out = new DataOutputStream( connection.getOutputStream() );
344 if ( verbose ) {
345 log( content );
346 }
347 out.writeBytes( content );
348 out.flush();
349 if ( verbose ) {
350 log( "Data sent." );
351 }
352 }
353 catch ( Exception e ) {
354 if ( failOnError ) {
355 throw new BuildException( e, getLocation() );
356 }
357 }
358 finally {
359 try {
360 out.close();
361 }
362 catch ( Exception e ) {
363 // ignored
364 }
365 }
366 }
367 }
368 ;
369 runner.start();
370 runner.join( maxwait );
371 if ( runner.isAlive() ) {
372 runner.interrupt();
373 if ( failOnError ) {
374 throw new BuildException( "maxwait exceeded, unable to send data", getLocation() );
375 }
376 return ;
377 }
378
379 // read the response, if any, optionally writing it to a file
380 if ( wantResponse ) {
381 if ( verbose ) {
382 log( "Waiting for response..." );
383 }
384 runner =
385 new Thread() {
386 public void run() {
387 PrintWriter fw = null;
388 StringWriter sw = null;
389 PrintWriter pw = null;
390 FileOutputStream dw = null;
391 BufferedInputStream in = null;
392 byte[] buffer = new byte[8192];
393 File downloadFile = null;
394 try {
395 if ( connection instanceof HttpURLConnection ) {
396 // read and store cookies
397 Map map = ( ( HttpURLConnection ) connection ).getHeaderFields();
398 for ( Iterator it = map.keySet().iterator(); it.hasNext(); ) {
399 String name = ( String ) it.next();
400 if ( name != null && name.equals( "Set-Cookie" ) ) {
401 List cookies = ( List ) map.get( name );
402 for ( Iterator c = cookies.iterator(); c.hasNext(); ) {
403 String raw = ( String ) c.next();
404 Cookie cookie = new Cookie( raw );
405 cookieStorage.put( cookie.getId(), cookie );
406 }
407 }
408 }
409
410 // maybe log response headers
411 if ( verbose ) {
412 log( String.valueOf( ( ( HttpURLConnection ) connection ).getResponseCode() ) );
413 log( ( ( HttpURLConnection ) connection ).getResponseMessage() );
414 map = ( ( HttpURLConnection ) connection ).getHeaderFields();
415 StringBuffer sb = new StringBuffer();
416 for ( Iterator it = map.keySet().iterator(); it.hasNext(); ) {
417 String name = ( String ) it.next();
418 sb.append( name ).append( "=" );
419 List values = ( List ) map.get( name );
420 if ( values != null ) {
421 if ( values.size() == 1 )
422 sb.append( values.get( 0 ) );
423 else if ( values.size() > 1 ) {
424 sb.append( "[" );
425 for ( Iterator v = values.iterator(); v.hasNext(); ) {
426 sb.append( v.next() ).append( "," );
427 }
428 sb.append( "]" );
429 }
430 }
431 sb.append( "\n" );
432 log( sb.toString() );
433 }
434 }
435
436 // might have a download
437 map = ( ( HttpURLConnection ) connection ).getHeaderFields();
438 List values = (List)map.get("Content-Type");
439 String content_type = null;
440 if (values != null)
441 content_type = (String)values.get(0);
442 if (content_type != null && content_type.equals("application/download")) {
443 if (outdir == null) {
444 outdir = new File(getProject().getProperty("basedir"));
445 }
446 String cd = (String)(((List)map.get("Content-Disposition")).get(0));
447 int index = cd.indexOf("filename=");
448 if (index >= 0) {
449 String filename = cd.substring(index + "filename=".length());
450 filename = filename.replaceAll("\"", "");
451 downloadFile = new File(outdir, filename);
452 }
453 }
454 }
455 in = new BufferedInputStream( connection.getInputStream() );
456 if ( log != null ) {
457 // user wants output stored to a file
458 fw = new PrintWriter( new FileWriter( log, append ) );
459 }
460 if ( property != null ) {
461 // user wants output stored in a property
462 sw = new StringWriter();
463 pw = new PrintWriter( sw );
464 }
465 if (downloadFile != null) {
466 dw = new FileOutputStream( downloadFile );
467 }
468 int byte_count = in.read(buffer, 0, buffer.length);
469 while ( byte_count > -1 ) {
470 String line = new String( buffer, 0, byte_count );
471 if ( currentRunner != this ) {
472 break;
473 }
474 if ( verbose ) {
475 log( line );
476 }
477 if ( fw != null ) {
478 // write response to a file
479 fw.print( line );
480 }
481 if ( pw != null ) {
482 // write response to a property
483 pw.print( line );
484 }
485 if (dw != null) {
486 dw.write( buffer, 0, byte_count );
487 }
488 byte_count = in.read(buffer, 0, buffer.length);
489 }
490 }
491 catch ( Exception e ) {
492 e.printStackTrace();
493 if ( failOnError ) {
494 throw new BuildException( e, getLocation() );
495 }
496 }
497 finally {
498 try {
499 in.close();
500 }
501 catch ( Exception e ) {
502 // ignored
503 }
504 try {
505 if ( fw != null ) {
506 fw.flush();
507 fw.close();
508 }
509 }
510 catch ( Exception e ) {
511 // ignored
512 }
513 }
514 if ( property != null && sw != null ) {
515 // save property
516 getProject().setProperty( property, sw.toString() );
517 }
518 }
519 };
520 currentRunner = runner;
521 runner.start();
522 runner.join( maxwait );
523 if ( runner.isAlive() ) {
524 currentRunner = null;
525 runner.interrupt();
526 if ( failOnError ) {
527 throw new BuildException( "maxwait exceeded, unable to receive data", getLocation() );
528 }
529 }
530 }
531 if ( verbose )
532 log( "Post complete." );
533 }
534 catch ( Exception e ) {
535 if ( failOnError ) {
536 throw new BuildException( e );
537 }
538 }
539 }
540
541
542 /**
543 * Borrowed from Property -- load variables from a file
544 *
545 * @param file file to load
546 * @exception BuildException Description of the Exception
547 */
548 private void loadFile( File file ) throws BuildException {
549 Properties fileprops = new Properties();
550 try {
551 if ( file.exists() ) {
552 FileInputStream fis = new FileInputStream( file );
553 try {
554 fileprops.load( fis );
555 }
556 finally {
557 if ( fis != null ) {
558 fis.close();
559 }
560 }
561 addProperties( fileprops );
562 }
563 else {
564 log( "Unable to find property file: " + file.getAbsolutePath(),
565 Project.MSG_VERBOSE );
566 }
567 log( "Post complete." );
568 }
569 catch ( Exception e ) {
570 if ( failOnError ) {
571 throw new BuildException( e );
572 }
573 }
574 }
575
576
577 /**
578 * Builds and formats the message to send to the server. Message is UTF-8
579 * encoded unless encoding has been explicitly set.
580 *
581 * @return the message to send to the server.
582 */
583 private String getContent() {
584 if ( propsFile != null ) {
585 loadFile( propsFile );
586 }
587
588 if ( textProps != null ) {
589 loadTextProps( textProps );
590 }
591
592 StringBuffer content = new StringBuffer();
593 try {
594 Enumeration en = props.keys();
595 while ( en.hasMoreElements() ) {
596 String name = ( String ) en.nextElement();
597 String value = ( String ) props.get( name );
598 content.append( URLEncoder.encode( name, encoding ) );
599 content.append( "=" );
600 content.append( URLEncoder.encode( value, encoding ) );
601 if ( en.hasMoreElements() ) {
602 content.append( "&" );
603 }
604 }
605 }
606 catch ( IOException ex ) {
607 if ( failOnError ) {
608 throw new BuildException( ex, getLocation() );
609 }
610 }
611 return content.toString();
612 }
613
614
615 /**
616 * Description of the Method
617 *
618 * @param tp
619 */
620 private void loadTextProps( String tp ) {
621 Properties p = new Properties();
622 Project project = getProject();
623 StringTokenizer st = new StringTokenizer( tp, "$" );
624 while ( st.hasMoreTokens() ) {
625 String token = st.nextToken();
626 int start = token.indexOf( "{" );
627 int end = token.indexOf( "}" );
628 if ( start > -1 && end > -1 && end > start ) {
629 String name = token.substring( start + 1, end - start );
630 String value = project.getProperty( name );
631 if ( value != null )
632 p.setProperty( name, value );
633 }
634 }
635 addProperties( p );
636 }
637
638
639 /**
640 * Borrowed from Property -- iterate through a set of properties, resolve
641 * them, then assign them
642 *
643 * @param fileprops The feature to be added to the Properties attribute
644 */
645 private void addProperties( Properties fileprops ) {
646 resolveAllProperties( fileprops );
647 Enumeration e = fileprops.keys();
648 while ( e.hasMoreElements() ) {
649 String name = ( String ) e.nextElement();
650 String value = fileprops.getProperty( name );
651 props.put( name, value );
652 }
653 }
654
655
656 /**
657 * Borrowed from Property -- resolve properties inside a properties
658 * hashtable
659 *
660 * @param fileprops Description of the Parameter
661 * @exception BuildException Description of the Exception
662 */
663 private void resolveAllProperties( Properties fileprops ) throws BuildException {
664 for ( Enumeration e = fileprops.keys(); e.hasMoreElements(); ) {
665 String name = ( String ) e.nextElement();
666 String value = fileprops.getProperty( name );
667
668 boolean resolved = false;
669 while ( !resolved ) {
670 Vector fragments = new Vector();
671 Vector propertyRefs = new Vector();
672 /// this is the Ant 1.5 way of doing it. The Ant 1.6 PropertyHelper
673 /// should be used -- eventually.
674 ProjectHelper.parsePropertyString( value, fragments,
675 propertyRefs );
676
677 resolved = true;
678 if ( propertyRefs.size() != 0 ) {
679 StringBuffer sb = new StringBuffer();
680 Enumeration i = fragments.elements();
681 Enumeration j = propertyRefs.elements();
682 while ( i.hasMoreElements() ) {
683 String fragment = ( String ) i.nextElement();
684 if ( fragment == null ) {
685 String propertyName = ( String ) j.nextElement();
686 if ( propertyName.equals( name ) ) {
687 throw new BuildException( "Property " + name
688 + " was circularly "
689 + "defined." );
690 }
691 fragment = getProject().getProperty( propertyName );
692 if ( fragment == null ) {
693 if ( fileprops.containsKey( propertyName ) ) {
694 fragment = fileprops.getProperty( propertyName );
695 resolved = false;
696 }
697 else {
698 fragment = "${" + propertyName + "}";
699 }
700 }
701 }
702 sb.append( fragment );
703 }
704 value = sb.toString();
705 fileprops.put( name, value );
706 }
707 }
708 }
709 }
710
711 /**
712 * Represents a cookie. See RFC 2109 and 2965.
713 */
714 public class Cookie {
715 private String name;
716 private String value;
717 private String domain;
718 private String path = "/";
719 private String id;
720
721 /**
722 * @param raw the raw string abstracted from the header of an http response
723 * for a single cookie.
724 */
725 public Cookie( String raw ) {
726 String[] args = raw.split( "[;]" );
727 for ( int i = 0; i < args.length; i++ ) {
728 String part = args[ i ];
729 int eq_index = part.indexOf("=");
730 if (eq_index == -1)
731 continue;
732 String first_part = part.substring(0, eq_index).trim();
733 String second_part = part.substring(eq_index + 1);
734 if ( i == 0 ) {
735 name = first_part;
736 value = second_part;
737 }
738 else if ( first_part.equalsIgnoreCase( "Path" ) )
739 path = second_part;
740 else if ( first_part.equalsIgnoreCase( "Domain" ) )
741 domain = second_part;
742 }
743 if (name == null)
744 throw new IllegalArgumentException("Raw cookie does not contain a cookie name.");
745 if (path == null)
746 path = "/";
747 setId(path, name);
748 }
749
750 /**
751 * @param name name of the cookie
752 * @param value the value of the cookie
753 */
754 public Cookie( String name, String value ) {
755 if (name == null)
756 throw new IllegalArgumentException("Cookie name may not be null.");
757
758 this.name = name;
759 this.value = value;
760 setId(name);
761 }
762
763 /**
764 * @return the id of the cookie, used internally by Post to store the cookie
765 * in a hashtable.
766 */
767 public String getId() {
768 if (id == null)
769 setId(path, name);
770 return id.toString();
771 }
772
773 private void setId(String name) {
774 setId(path, name);
775 }
776
777 private void setId(String path, String name) {
778 if (name == null)
779 name = "";
780 id = path + name;
781 }
782
783 /**
784 * @return the name of the cookie
785 */
786 public String getName() {
787 return name;
788 }
789
790 /**
791 * @return the value of the cookie
792 */
793 public String getValue() {
794 return value;
795 }
796
797 /**
798 * @param domain the domain of the cookie
799 */
800 public void setDomain( String domain ) {
801 this.domain = domain;
802 }
803
804 /**
805 * @return the domain of the cookie
806 */
807 public String getDomain() {
808 return domain;
809 }
810
811 /**
812 * @param path the path of the cookie
813 */
814 public void setPath( String path ) {
815 this.path = path;
816 }
817
818 /**
819 * @return the path of the cookie
820 */
821 public String getPath() {
822 return path;
823 }
824
825 /**
826 * @return a Cookie formatted as a Cookie Version 1 string. The returned
827 * string is suitable for including with an http request.
828 */
829 public String toString() {
830 StringBuffer sb = new StringBuffer();
831 sb.append( name ).append( "=" ).append( value ).append( ";" );
832 if ( domain != null )
833 sb.append( "Domain=" ).append( domain ).append( ";" );
834 if ( path != null )
835 sb.append( "Path=" ).append( path ).append( ";" );
836 sb.append( "Version=\"1\";" );
837 return sb.toString();
838 }
839 }
840}
841
Note: See TracBrowser for help on using the repository browser.