001    package org.rakeshv.utils;
002    
003    import java.io.BufferedReader;
004    import java.io.BufferedWriter;
005    import java.io.FileInputStream;
006    import java.io.FileOutputStream;
007    import java.io.FileReader;
008    import java.io.FileWriter;
009    import java.io.FileNotFoundException;
010    import java.io.InputStream;
011    import java.io.InputStreamReader;
012    import java.io.OutputStreamWriter;
013    import java.io.IOException;
014    import java.io.PrintWriter;
015    import java.io.UnsupportedEncodingException;
016    import java.net.Authenticator;
017    import java.net.HttpURLConnection;
018    import java.net.MalformedURLException;
019    import java.net.PasswordAuthentication;
020    import java.net.URL;
021    import java.util.Enumeration;
022    import java.util.Properties;
023    import java.util.zip.GZIPInputStream;
024    
025    /**
026     * Provides utility methods to return <code>String</code> objects
027     * from commonly used resources.
028     *
029     * <p>Copyright 2004-2006 Rakesh Vidyadharan</p>
030     * @author Rakesh Vidyadharan 2004 August 27
031     * @version $Id: StringUtilities.java,v 1.15 2006/03/14 22:55:36 rakesh Exp $
032     */
033    public final class StringUtilities extends Object
034    {
035      /**
036       * The <code>end of line</code> characters to use in the returned
037       * String objects.
038       */
039      public static final String endOfLine = 
040        System.getProperty( "line.separator" );
041    
042      /**
043       * Default constructor.  Cannot be instantiated.
044       */
045      private StringUtilities()
046      {
047        super();
048      }
049    
050      /**
051       * Return the response body from the specified <code>HTTP URL</code>
052       * as a <code>String</code> using the encoding specified by the
053       * <code>web server</code>.
054       *
055       * <p>The following code snippet shows how to use this method:</p>
056       * <pre>
057       *    import org.rakeshv.utils.StringUtilities;
058       *
059       *        try
060       *        {
061       *          String result = StringUtilities.fromUrl( "http://www.w3.org/" );
062       *        }
063       *        catch ( Throwable t )
064       *        {
065       *          // Exception handling
066       *        }
067       * </pre>
068       *
069       * @see #readResponseFromUrl( HttpURLConnection )
070       * @param url The <code>URL</code> from which the contents are to be
071       *   retrieved.
072       * @throws MalformedURLException If the specified <code>url</code>
073       *   is improperly formatted.
074       * @throws IOException If errors are encountered while reading the
075       *   data.  An IOException is thrown with the error response from the
076       *   server if a HTTP error is encountered.
077       */
078      public static final String fromUrl( String url )
079        throws MalformedURLException, IOException
080      {
081        String result = null;
082        HttpURLConnection httpConnection = null;
083    
084        try
085        {
086          httpConnection =
087            (HttpURLConnection) ( new URL( url ) ).openConnection();
088          httpConnection.setRequestProperty( "Accept-Encoding", "gzip" ); 
089          result = readResponseFromUrl( httpConnection );
090        }
091        finally
092        {
093          httpConnection.disconnect();
094        }
095    
096        return result;
097      }
098    
099      /**
100       * Return the response body from the specified <code>HTTP URL</code>
101       * as a <code>String</code> using the encoding specified by the 
102       * <code>web server</code>.  The response is obtained by 
103       * <code>POST</code>'ing the data specified in the 
104       * <code>Properties</code> object.
105       *
106       * <p>The following code snippet shows how to use this method:</p>
107       * <pre>
108       *    import java.util.Properties;
109       *    import org.rakeshv.utils.StringUtilities;
110       *
111       *        Properties properties = new Properties();
112       *        properties.setProperty( "page", "2" );
113       *        properties.setProperty( "user", "scott" );
114       *        properties.setProperty( "password", "tiger" );
115       *        try
116       *        {
117       *          String result = StringUtilities.fromUrl( 
118       *            "http://www.w3.org/", properties );
119       *        }
120       *        catch ( Throwable t )
121       *        {
122       *          // Exception handling
123       *        }
124       * </pre>
125       *
126       * @see #readResponseFromUrl( HttpURLConnection )
127       * @param url The <code>URL</code> from which the contents are to be
128       *   retrieved.
129       * @param properties A <code>Properties</code> object with the
130       *   name-value pairs that are to be submitted to the server.  A
131       *   Properties object is used to ensure that the keys and values
132       *   are <code>String</code> objects.
133       * @throws MalformedURLException If the specified <code>url</code>
134       *   is improperly formatted.
135       * @throws IOException If errors are encountered while reading the
136       *   data.  An IOException is thrown with the error response from the
137       *   server if a HTTP error is encountered.
138       */
139      public static final String fromUrl( String url, 
140          Properties properties ) throws MalformedURLException, IOException
141      {
142        String result = null;
143        HttpURLConnection httpConnection = null;
144        StringBuilder body = new StringBuilder();
145    
146        if ( properties != null )
147        {
148          for ( Enumeration enumeration = properties.propertyNames();
149              enumeration.hasMoreElements(); )
150          {
151            String name = (String) enumeration.nextElement();
152            body.append( name );
153            body.append( "=" );
154            body.append( properties.getProperty( name ) );
155            body.append( "&" );
156          }
157        }
158    
159        try
160        {
161          httpConnection =
162            (HttpURLConnection) ( new URL( url ) ).openConnection();
163          httpConnection.setRequestMethod( "POST" );
164          httpConnection.setAllowUserInteraction( false );
165          httpConnection.setDoOutput( true );
166          httpConnection.setRequestProperty( "Accept-Encoding", "gzip" ); 
167          httpConnection.setRequestProperty( 
168              "Content-type", "application/x-www-form-urlencoded" );
169          httpConnection.setRequestProperty( "Content-length", 
170              String.valueOf( body.length() - 1 ) );
171    
172          if ( body.length() > 0 )
173          {
174            PrintWriter writer = 
175              new PrintWriter( httpConnection.getOutputStream() );
176            writer.print( body.substring( 0, body.length() - 1 ) );
177            writer.flush();
178            writer.close();
179          }
180    
181          result = readResponseFromUrl( httpConnection );
182        }
183        finally
184        {
185          httpConnection.disconnect();
186        }
187    
188        return result;
189      }
190    
191      /**
192       * Return the response body from the specified <code>HTTP URL</code>
193       * as a <code>String</code> using the encoding specified by the
194       * <code>web server</code>.  Uses the specified credentials to
195       * authenticate the request with the server.
196       *
197       * <p>The following code snippet shows how to use this method:</p>
198       * <pre>
199       *    import org.rakeshv.utils.StringUtilities;
200       *
201       *        try
202       *        {
203       *          String url = "http://www.w3.org/";
204       *          String userName = "scott";
205       *          String password = "tiger";
206       *          String result = StringUtilities.fromUrl( url, userName, password );
207       *        }
208       *        catch ( Throwable t )
209       *        {
210       *          // Exception handling
211       *        }
212       * </pre>
213       *
214       * @see #setAuthenticator( String, String )
215       * @see #fromUrl( String )
216       * @param url The <code>URL</code> from which the contents are to be
217       *   retrieved.
218       * @param userName The user name to use for authentication with the
219       *   server.
220       * @param password The password to use for authentication with the
221       *   server.
222       * @throws MalformedURLException If the specified <code>url</code>
223       *   is improperly formatted.
224       * @throws IOException If errors are encountered while reading the
225       *   data.  An IOException is thrown with the error response from the
226       *   server if a HTTP error is encountered.
227       */
228      public static final String fromUrl( String url, String userName,
229          String password ) throws MalformedURLException, IOException
230      {
231        setAuthenticator( userName, password );
232        return fromUrl( url );
233      }
234    
235      /**
236       * Return the response body from the specified <code>HTTP URL</code>
237       * as a <code>String</code> using the encoding specified by the 
238       * <code>web server</code>.  The response is obtained by 
239       * <code>POST</code>'ing the data specified in the 
240       * <code>Properties</code> object.  The <code>userName</code> and
241       * <code>password</code> values specified are used to authenticate
242       * the request with the server.
243       *
244       * <p>The following code snippet shows how to use this method:</p>
245       * <pre>
246       *    import java.util.Properties;
247       *    import org.rakeshv.utils.StringUtilities;
248       *
249       *        String url = "http://www.w3.org/";
250       *        String user = "scott";
251       *        String password = "tiger";
252       *        Properties properties = new Properties();
253       *        properties.setProperty( "page", "2" );
254       *        properties.setProperty( "content", "xml" );
255       *        properties.setProperty( "details", "heavy" );
256       *        try
257       *        {
258       *          String result = StringUtilities.fromUrl( 
259       *            url, properties, user, password );
260       *        }
261       *        catch ( Throwable t )
262       *        {
263       *          // Exception handling
264       *        }
265       * </pre>
266       *
267       * @see #setAuthenticator( String, String )
268       * @see #fromUrl( String, Properties )
269       * @param url The <code>URL</code> from which the contents are to be
270       *   retrieved.
271       * @param properties A <code>Properties</code> object with the
272       *   name-value pairs that are to be submitted to the server.  A
273       *   Properties object is used to ensure that the keys and values
274       *   are <code>String</code> objects.
275       * @param userName The user name to use for authentication with the
276       *   server.
277       * @param password The password to use for authentication with the
278       *   server.
279       * @throws MalformedURLException If the specified <code>url</code>
280       *   is improperly formatted.
281       * @throws IOException If errors are encountered while reading the
282       *   data.  An IOException is thrown with the error response from the
283       *   server if a HTTP error is encountered.
284       */
285      public static final String fromUrl( String url, 
286          Properties properties, String userName, String password ) 
287        throws MalformedURLException, IOException
288      {
289        setAuthenticator( userName, password );
290        return fromUrl( url, properties );
291      }
292    
293      /**
294       * Parse the <code>charset</code> from the <code>content-type</code>
295       * HTTP header if it was specified.
296       *
297       * @param type The <code>content-type</code> header value.
298       * @return String - The encoding value if specified.  If it was not
299       *   specified, this method returns <code>null</code>.
300       */
301      public static final String parseEncoding( String type )
302      {
303        if ( type == null ) return type;
304    
305        StringBuilder builder = new StringBuilder();
306        int length = type.length();
307        int index = type.indexOf( "charset" );
308        if ( index != -1 )
309        {
310          index += 8; // Advance to after charset=
311          char currentChar = type.charAt( index );
312          
313          // Skip leading quotes if any
314          if ( currentChar == '"' || currentChar == '\'' )
315          {
316            ++index;
317          }
318    
319          currentChar = type.charAt( index );
320          while ( currentChar != '\r' && 
321              currentChar != '\n' && 
322              currentChar != ' ' && 
323              currentChar != ';' && 
324              currentChar != '\'' && 
325              currentChar != '"' && 
326              ++index < length )
327          {
328            builder.append( currentChar );
329            currentChar = type.charAt( index );
330          }
331    
332          // Add last character if there were no quotes or such like
333          if ( index == length )
334          {
335            builder.append( currentChar );
336          }
337        }
338    
339        String result = null;
340        if ( builder.length() > 0 )
341        {
342          result = builder.toString().toUpperCase();
343        }
344    
345        return result;
346      }
347    
348      /**
349       * Return the contents of the specified <code>file</code> as a 
350       * <code>String</code> using the system default encoding.
351       *
352       * <p>The following code snippet shows how to use this method:</p>
353       * <pre>
354       *    import org.rakeshv.utils.StringUtilities;
355       *
356       *        try
357       *        {
358       *          String result = StringUtilities.fromFile( "/tmp/test.java" );
359       *        }
360       *        catch ( Throwable t )
361       *        {
362       *          // Exception handling
363       *        }
364       * </pre>
365       *
366       * @param file The <code>file</code> from which the contents are to 
367       *   be read.
368       * @throws FileNotFoundException If the specified <code>file</code>
369       *   could not be found.
370       * @throws IOException If errors are encountered while reading the
371       *   data.
372       */
373      public static final String fromFile( String file )
374        throws FileNotFoundException, IOException
375      {
376        BufferedReader reader = new BufferedReader(
377            new FileReader( file ) );
378        String result = readData( reader );
379        reader.close();
380    
381        return result;
382      }
383    
384      /**
385       * Return the contents of the specified <code>file</code> as a 
386       * <code>String</code> using the character encoding specified.
387       *
388       * <p>The following code snippet shows how to use this method:</p>
389       * <pre>
390       *    import org.rakeshv.utils.StringUtilities;
391       *
392       *        try
393       *        {
394       *          String result = StringUtilities.fromFile( "/tmp/test.java", "UTF-8" );
395       *        }
396       *        catch ( Throwable t )
397       *        {
398       *          // Exception handling
399       *        }
400       * </pre>
401       *
402       * @param file The <code>file</code> from which the contents are to 
403       *   be read.
404       * @param encoding The <code>character encoding</code> scheme to use
405       *   to read the file contents.
406       * @throws FileNotFoundException If the specified <code>file</code>
407       *   could not be found.
408       * @throws UnsupportedEncodingException If the specified character
409       *   encoding is invalid.
410       * @throws IOException If errors are encountered while reading the
411       *   data.
412       */
413      public static final String fromFile( String file, String encoding )
414        throws FileNotFoundException, IOException
415      {
416        BufferedReader reader = new BufferedReader( 
417            new InputStreamReader( 
418              new FileInputStream( file ), encoding ) );
419        String result = readData( reader );
420        reader.close();
421    
422        return result;
423      }
424    
425      /**
426       * Write the contents of the specified <code>String</code> to the
427       * <code>file</code> specified using the system default encoding.
428       *
429       * <p>The following code snippet shows how to use this method:</p>
430       * <pre>
431       *    import org.rakeshv.utils.StringUtilities;
432       *
433       *        try
434       *        {
435       *          String content = "blah .... blah";
436       *          StringUtilities.toFile( content, "/tmp/test.txt" );
437       *        }
438       *        catch ( Throwable t )
439       *        {
440       *          // Exception handling
441       *        }
442       * </pre>
443       *
444       * @since version 1.16
445       * @param content The content that is to be written to file.
446       * @param file The <code>fully qualified file name</code> to which 
447       *   the contents are to be written.
448       * @throws IOException If errors are encountered while writing the
449       *   data.
450       */
451      public static final void toFile( String content, String file )
452        throws IOException
453      {
454        BufferedWriter writer = new BufferedWriter(
455            new FileWriter( file ) );
456        writer.write( content, 0, content.length() );
457        if ( ! content.endsWith( "\n" ) )
458        {
459          writer.newLine();
460        }
461        writer.close();
462      }
463    
464      /**
465       * Write the contents of the specified <code>String</code> to the
466       * <code>file</code> specified using the character encoding specified.
467       *
468       * <p>The following code snippet shows how to use this method:</p>
469       * <pre>
470       *    import org.rakeshv.utils.StringUtilities;
471       *
472       *        try
473       *        {
474       *          String content = "blah ... blah";
475       *          StringUtilities.toFile( content, "/tmp/test.txt", "UTF-8" );
476       *        }
477       *        catch ( Throwable t )
478       *        {
479       *          // Exception handling
480       *        }
481       * </pre>
482       *
483       * @since version 1.16
484       * @param content The content that is to be written to file.
485       * @param file The <code>file</code> from which the contents are to 
486       *   be read.
487       * @param encoding The <code>character encoding</code> scheme to use
488       *   to read the file contents.
489       * @throws UnsupportedEncodingException If the specified character
490       *   encoding is invalid.
491       * @throws IOException If errors are encountered while reading the
492       *   data.
493       */
494      public static final void toFile( String content, String file, 
495          String encoding )
496        throws UnsupportedEncodingException, IOException
497      {
498        BufferedWriter writer = new BufferedWriter( 
499            new OutputStreamWriter( 
500              new FileOutputStream( file ), encoding ) );
501        writer.write( content, 0, content.length() );
502        if ( ! content.endsWith( "\n" ) )
503        {
504          writer.newLine();
505        }
506        writer.close();
507      }
508    
509      /**
510       * Returns the <code>StackTrace</code> for the specified instance
511       * of <code>Throwable</code>.
512       *
513       * @param throwable The instance whose stack trace is desired.
514       * @return String - The stack trace for the throwable instance.
515       */
516      public static final String stackTrace( Throwable throwable )
517      {
518        StringBuilder builder = new StringBuilder( 1024 );
519    
520        builder.append( throwable.getMessage() );
521        builder.append( endOfLine );
522        printTrace( throwable, builder );
523    
524        // Loop through the causes if they exist
525        Throwable throwable1 = throwable.getCause();
526        while ( throwable1 != null )
527        {
528          builder.append( "Caused by: " );
529          builder.append( throwable1.toString() );
530          builder.append( endOfLine );
531          printTrace( throwable1, builder );
532          throwable1 = throwable1.getCause();
533        }
534    
535        return builder.toString();
536      }
537    
538      /**
539       * Replace XML/HTML special characters (&amp;, &lt;, ...) in the 
540       * String specified with their escaped equivalents.
541       *
542       * @param content - The <code>String</code> that
543       *   is to be processed.
544       * @return String - The modified <code>String</code> object.
545       */
546      public static final String toXML( String content )
547      {
548        if ( content == null ) return content;
549    
550        char contentChars[] = content.toCharArray();
551        StringBuilder builder = new StringBuilder( contentChars.length + 256 );
552        for ( int i = 0; i < contentChars.length; ++i )
553        {
554          switch( contentChars[i] )
555          {
556            case '&':
557              // Check to see if the ampersand has already been escaped
558              if ( i + 4 < contentChars.length )
559              {
560                if ( contentChars[i + 1] == 'a' &&
561                    contentChars[i + 2] == 'm' &&
562                    contentChars[i + 3] == 'p' &&
563                    contentChars[i + 4] == ';' )
564                {
565                  builder.append( contentChars[i] );
566                }
567                else
568                {
569                  builder.append( "&amp;" );
570                }
571              }
572              else
573              {
574                builder.append( "&amp;" );
575              }
576              break;
577            case '<':
578              builder.append("&lt;");
579              break;
580            case '>':
581              builder.append("&gt;");
582              break;
583            case '\'':
584            case '`':
585              builder.append("&apos;");
586              break;
587            case '"':
588              builder.append("&quot;");
589              break;
590            case '\\':
591              builder.append("&#92;");
592              break;
593            case (char)133:
594              builder.append("&#133;");
595              break;
596            default:
597              builder.append( contentChars[i] );
598              break;
599          }
600        }
601      
602        return builder.toString();
603      }
604    
605      /**
606       * Replace escaped XML/HTML special characters (&amp;, &lt;, ...) in 
607       * the String specified with their original equivalents.
608       *
609       * @since version 1.16
610       * @param content - The <code>String</code> that
611       *   is to be processed.
612       * @return String - The modified <code>String</code> object.
613       */
614      public static final String fromXML( String content )
615      {
616        if ( content == null ) return content;
617    
618        char contentChars[] = content.toCharArray();
619        StringBuilder builder = new StringBuilder( contentChars.length );
620        for ( int i = 0; i < contentChars.length; ++i )
621        {
622          boolean processed = false;
623          if ( contentChars[i] == '&' )
624          {
625            // Check for escaped ampersand and backslash
626            if ( i + 5 < contentChars.length )
627            {
628              // Check for escaped apostrophe and quote and 133
629              if ( contentChars[i + 1] == 'a' &&
630                  contentChars[i + 2] == 'p' &&
631                  contentChars[i + 3] == 'o' &&
632                  contentChars[i + 4] == 's' &&
633                  contentChars[i + 5] == ';' )
634              {
635                builder.append( '\'' );
636                processed = true;
637                i += 5;
638              } 
639              else if ( contentChars[i + 1] == 'q' &&
640                  contentChars[i + 2] == 'u' &&
641                  contentChars[i + 3] == 'o' &&
642                  contentChars[i + 4] == 't' &&
643                  contentChars[i + 5] == ';' )
644              {
645                builder.append( '"' );
646                processed = true;
647                i += 5;
648              } 
649              else if ( contentChars[i + 1] == '#' &&
650                  contentChars[i + 2] == '1' &&
651                  contentChars[i + 3] == '3' &&
652                  contentChars[i + 4] == '3' &&
653                  contentChars[i + 5] == ';' )
654              {
655                builder.append( (char) 133 );
656                processed = true;
657                i += 5;
658              }
659            }
660            if ( i + 4 < contentChars.length && ! processed )
661            {
662              if ( contentChars[i + 1] == 'a' &&
663                  contentChars[i + 2] == 'm' &&
664                  contentChars[i + 3] == 'p' &&
665                  contentChars[i + 4] == ';' )
666              {
667                builder.append( contentChars[i] );
668                processed = true;
669                i += 4;
670              } 
671              else if ( contentChars[i + 1] == '#' &&
672                  contentChars[i + 2] == '9' &&
673                  contentChars[i + 3] == '2' &&
674                  contentChars[i + 4] == ';' )
675              {
676                builder.append( '\\' );
677                processed = true;
678                i += 4;
679              }
680            }
681            if ( i + 3 < contentChars.length && ! processed )
682            {
683              // Check for escaped less than or greater than
684              if ( i + 3 < contentChars.length )
685              {
686                if ( contentChars[i + 1] == 'l' &&
687                    contentChars[i + 2] == 't' &&
688                    contentChars[i + 3] == ';' )
689                {
690                  builder.append( '<' );
691                  processed = true;
692                  i += 3;
693                }
694                else if ( contentChars[i + 1] == 'g' &&
695                    contentChars[i + 2] == 't' &&
696                    contentChars[i + 3] == ';' )
697                {
698                  builder.append( '>' );
699                  processed = true;
700                  i += 3;
701                }
702              }
703            }
704            if ( ! processed )
705            {
706              builder.append( contentChars[i] );
707            }
708          }
709          else
710          {
711            builder.append( contentChars[i] );
712          }
713        }
714      
715        return builder.toString();
716      }
717    
718      /**
719       * Read the response from the <code>HttpURLConnection</code>.  Check
720       * the <code>Content-encoding</code> response header to see if the
721       * response is <code>gzip</code> encoded.  If it is, use a
722       * <code>GZIPInputStream</code> to read the response.
723       *
724       * @param httpConnection The <code>HttpURLConnection</code> from
725       *   which the response is to be read.
726       * @throws IOException If errors are encountered while reading the
727       *   response from the server.
728       */
729      private static final String readResponseFromUrl( 
730          HttpURLConnection httpConnection ) throws IOException
731      {
732        String result = null;
733        BufferedReader reader = null;
734        String encoding = parseEncoding( httpConnection.getContentType() );
735    
736        boolean gzipped = false;
737        String value = httpConnection.getHeaderField( "Content-Encoding" );
738        if ( value == null )
739        {
740          value = httpConnection.getHeaderField( "Content-encoding" );
741        }
742        if ( value != null && value.equals( "gzip" ) )
743        {
744          gzipped = true;
745        }
746    
747        if ( httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK )
748        {
749    
750          if ( encoding == null )
751          {
752            if ( gzipped )
753            {
754              reader = new BufferedReader( 
755                  new InputStreamReader( 
756                    new GZIPInputStream( 
757                      httpConnection.getInputStream() ) ) );
758            }
759            else
760            {
761              reader = new BufferedReader( 
762                  new InputStreamReader( 
763                    httpConnection.getInputStream() ) );
764            }
765          }
766          else
767          {
768            if ( gzipped )
769            {
770              reader = new BufferedReader( 
771                  new InputStreamReader( 
772                    new GZIPInputStream( httpConnection.getInputStream() ), 
773                    encoding.toUpperCase() ) );
774            }
775            else
776            {
777              reader = new BufferedReader( 
778                  new InputStreamReader( 
779                    httpConnection.getInputStream(), 
780                    encoding.toUpperCase() ) );
781            }
782          }
783    
784          result = readData( reader );
785          reader.close();
786        }
787        else
788        {
789          InputStream inputStream = httpConnection.getErrorStream();
790          if ( inputStream != null )
791          {
792            if ( gzipped )
793            {
794              reader = new BufferedReader( 
795                  new InputStreamReader( 
796                    new GZIPInputStream( inputStream ) ) );
797            }
798            else
799            {
800              reader = new BufferedReader( 
801                  new InputStreamReader( inputStream ) );
802            }
803            String error = readData( reader );
804            reader.close();
805            throw new IOException( error );
806          }
807          else
808          {
809            throw new IOException( "HTTP Error " + httpConnection.getResponseCode() );
810          }
811        }
812    
813        return result;
814      }
815    
816      /**
817       * Create a new instance of <code>Authenticator</code> using the
818       * specified credentials.  Create an anonymous sub-class of
819       * <code>Authenticator</code> that implements the <code>
820       * getPasswordAuthentication</code> method by returning a new 
821       * instance of <code>PasswordAuthentication</code> using the
822       * <code>userName</code> and <code>password</code> specified.
823       *
824       * @param userName The user name to use for authentication.
825       * @param password The password to use for authentication.
826       */
827      private static final void setAuthenticator( final String userName,
828          final String password )
829      {
830        Authenticator.setDefault(
831            new Authenticator()
832            {
833              protected PasswordAuthentication getPasswordAuthentication()
834              {
835                return new PasswordAuthentication( 
836                  userName, password.toCharArray() );
837              }
838            }
839            );
840      }
841    
842      /**
843       * Returns all the data from the specified <code>BufferedReader</code>.
844       *
845       * @param reader The <code>BufferedReader</code> from which the
846       *   data is to be read.
847       * @return String - The data read from the specified BufferedReader.
848       * @throws IOException If errors are encountered while reading the
849       *   data.
850       */
851      private static String readData( BufferedReader reader )
852        throws IOException
853      {
854        StringBuilder builder = new StringBuilder( 8192 );
855        String line = null;
856        while ( ( line = reader.readLine() ) != null )
857        {
858          builder.append( line );
859          builder.append( endOfLine );
860        }
861    
862        return builder.toString();
863      }
864    
865      /**
866       * Return the <code>stack trace</code> for the specified instance
867       * of <code>Throwable</code>.
868       *
869       * @param throwable The instance of <code>Throwable</code>
870       *   whose <code>stack trace</code> is to be retrieved.
871       * @param builder The StringBuilder to which the stack trace is to
872       *   be appended.
873       * @return StringBuilder - The reference to the StringBuilder that 
874       *   was passed in for convenience.
875       */
876      private static final StringBuilder printTrace( Throwable throwable, 
877          StringBuilder builder )
878      {
879        StackTraceElement [] ste = throwable.getStackTrace();
880    
881        for ( int i = 0; i < ste.length; ++i )
882        {
883          builder.append( "\tat " );
884          builder.append( ste[i].getClassName() );
885          builder.append( "." );
886          builder.append( ste[i].getMethodName() );
887          builder.append( "(" );
888          builder.append( ste[i].getFileName() );
889          builder.append( ":" );
890          builder.append( ste[i].getLineNumber() );
891          builder.append( ")" );
892          builder.append( endOfLine );
893        }
894    
895        return builder;
896      }
897    }