1 17 package org.apache.excalibur.source.impl; 18 19 import java.io.File ; 20 import java.io.FileInputStream ; 21 import java.io.FileOutputStream ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.OutputStream ; 25 import java.util.Collections ; 26 import java.util.Date ; 27 import java.util.Iterator ; 28 import java.util.Map ; 29 30 import org.apache.avalon.framework.activity.Initializable; 31 import org.apache.avalon.framework.logger.AbstractLogEnabled; 32 import org.apache.avalon.framework.logger.Logger; 33 import org.apache.avalon.framework.parameters.ParameterException; 34 import org.apache.avalon.framework.parameters.Parameterizable; 35 import org.apache.avalon.framework.parameters.Parameters; 36 import org.apache.commons.httpclient.Header; 37 import org.apache.commons.httpclient.HttpClient; 38 import org.apache.commons.httpclient.HttpMethod; 39 import org.apache.commons.httpclient.HttpStatus; 40 import org.apache.commons.httpclient.NameValuePair; 41 import org.apache.commons.httpclient.methods.DeleteMethod; 42 import org.apache.commons.httpclient.methods.GetMethod; 43 import org.apache.commons.httpclient.methods.HeadMethod; 44 import org.apache.commons.httpclient.methods.PostMethod; 45 import org.apache.commons.httpclient.methods.PutMethod; 46 import org.apache.excalibur.source.*; 47 import org.apache.excalibur.source.impl.validity.TimeStampValidity; 48 49 57 public class HTTPClientSource extends AbstractLogEnabled 58 implements ModifiableSource, Initializable, Parameterizable 59 { 60 63 public static final String POST = "POST"; 64 65 68 public static final String GET = "GET"; 69 70 73 public static final String PROXY_HOST = "proxy.host"; 74 75 78 public static final String PROXY_PORT = "proxy.port"; 79 80 83 public static final String CONTENT_TYPE = "Content-Type"; 84 85 88 public static final String CONTENT_LENGTH = "Content-Length"; 89 90 93 public static final String LAST_MODIFIED = "Last-Modified"; 94 95 98 private final String m_uri; 99 100 103 private final Map m_parameters; 104 105 108 private HttpClient m_client; 109 110 113 private int m_proxyPort; 114 115 118 private String m_proxyHost; 119 120 123 private boolean m_dataValid; 124 125 128 private boolean m_exists; 129 130 133 private String m_mimeType; 134 135 138 private long m_contentLength; 139 140 143 private long m_lastModified; 144 145 148 private SourceValidity m_cachedValidity; 149 150 153 private long m_cachedLastModificationDate; 154 155 162 public HTTPClientSource( final String uri, final Map parameters ) 163 throws Exception 164 { 165 m_uri = uri; 166 m_parameters = 167 parameters == null ? Collections.EMPTY_MAP : parameters; 168 } 169 170 176 public void parameterize( final Parameters params ) 177 throws ParameterException 178 { 179 m_proxyHost = params.getParameter( PROXY_HOST, null ); 180 m_proxyPort = params.getParameterAsInteger( PROXY_PORT, -1 ); 181 182 if ( getLogger().isDebugEnabled() ) 183 { 184 final String message = 185 m_proxyHost == null || m_proxyPort == -1 186 ? "No proxy configured" 187 : "Configured with proxy host " 188 + m_proxyHost + " port " + m_proxyPort; 189 190 getLogger().debug( message ); 191 } 192 } 193 194 199 public void initialize() throws Exception 200 { 201 m_client = new HttpClient(); 202 203 if ( m_proxyHost != null && m_proxyPort != -1 ) 204 { 205 m_client.getHostConfiguration().setProxy( m_proxyHost, m_proxyPort ); 206 } 207 208 m_dataValid = false; 209 } 210 211 218 private String findMethodType() 219 { 220 final String method = 221 (String ) m_parameters.get( SourceResolver.METHOD ); 222 return method == null ? GET : method; 223 } 224 225 231 private HttpMethod getMethod() 232 { 233 final String method = findMethodType(); 234 235 if ( POST.equals( method ) ) 237 { 238 return createPostMethod( 239 m_uri, 240 (SourceParameters) m_parameters.get( SourceResolver.URI_PARAMETERS ) 241 ); 242 } 243 244 return createGetMethod( m_uri ); 246 } 247 248 256 private PostMethod createPostMethod( 257 final String uri, final SourceParameters params 258 ) 259 { 260 final PostMethod post = new PostMethod( uri ); 261 262 if ( params == null ) 263 { 264 return post; 265 } 266 267 for ( final Iterator names = params.getParameterNames(); 268 names.hasNext(); 269 ) 270 { 271 final String name = (String ) names.next(); 272 273 for ( final Iterator values = params.getParameterValues( name ); 274 values.hasNext(); 275 ) 276 { 277 final String value = (String ) values.next(); 278 post.addParameter( new NameValuePair( name, value ) ); 279 } 280 } 281 282 return post; 283 } 284 285 291 private GetMethod createGetMethod( final String uri ) 292 { 293 final GetMethod method = new GetMethod( uri ); 294 295 for ( final Iterator i = m_parameters.keySet().iterator(); i.hasNext(); ) 297 { 298 final String key = (String ) i.next(); 299 final String value = (String ) m_parameters.get( key ); 300 301 if ( getLogger().isDebugEnabled() ) 302 { 303 getLogger().debug( 304 "Adding header '" + key + "', with value '" + value + "'" 305 ); 306 } 307 308 method.setRequestHeader( key, value ); 309 } 310 311 return method; 312 } 313 314 320 private HeadMethod createHeadMethod( final String uri ) 321 { 322 return new HeadMethod( uri ); 323 } 324 325 333 private PutMethod createPutMethod( 334 final String uri, final File uploadFile 335 ) 336 throws IOException 337 { 338 final PutMethod put = new PutMethod( uri ); 339 put.setRequestBody( 340 new FileInputStream ( uploadFile.getAbsolutePath() ) 341 ); 342 return put; 343 } 344 345 351 private DeleteMethod createDeleteMethod( final String uri ) 352 { 353 return new DeleteMethod( uri ); 354 } 355 356 360 private void updateData() 361 { 362 if ( !m_dataValid ) 364 { 365 if ( GET.equals( findMethodType() ) ) 366 { 367 try 368 { 369 final HttpMethod head = createHeadMethod( m_uri ); 370 executeMethod( head ); 371 head.releaseConnection(); 372 return; 373 } 374 catch ( final IOException e ) 375 { 376 if ( getLogger().isDebugEnabled() ) 377 { 378 getLogger().debug( 379 "Unable to determine response data, using defaults", e 380 ); 381 } 382 } 383 } 384 385 m_exists = false; 387 m_mimeType = null; 388 m_contentLength = -1; 389 m_lastModified = 0; 390 m_dataValid = true; 391 } 392 } 393 394 402 private int executeMethod( final HttpMethod method ) 403 throws IOException 404 { 405 final int response = m_client.executeMethod( method ); 406 407 updateExists( method ); 408 updateMimeType( method ); 409 updateContentLength( method ); 410 updateLastModified( method ); 411 412 return response; 414 } 415 416 425 private void updateExists( final HttpMethod method ) 426 { 427 final int response = method.getStatusCode(); 428 429 433 436 m_exists = (response == HttpStatus.SC_OK || 438 response == HttpStatus.SC_CREATED || 439 response == HttpStatus.SC_PARTIAL_CONTENT); 440 } 441 442 449 public boolean exists() 450 { 451 updateData(); 452 return m_exists; 453 } 454 455 463 public InputStream getInputStream() 464 throws IOException , SourceNotFoundException 465 { 466 final HttpMethod method = getMethod(); 467 final int response = executeMethod( method ); 468 m_dataValid = true; 469 470 if ( !exists() ) 473 { 474 final StringBuffer error = new StringBuffer (); 475 error.append( "Unable to retrieve URI: " ); 476 error.append( m_uri ); 477 error.append( " (" ); 478 error.append( response ); 479 error.append( ")" ); 480 481 throw new SourceNotFoundException( error.toString() ); 482 } 483 484 return method.getResponseBodyAsStream(); 485 } 486 487 492 public String getURI() 493 { 494 return m_uri; 495 } 496 497 503 public String getScheme() 504 { 505 return SourceUtil.getScheme( m_uri ); 506 } 507 508 514 public SourceValidity getValidity() 515 { 516 518 final long lm = getLastModified(); 519 520 if ( lm > 0 ) 521 { 522 if ( lm == m_cachedLastModificationDate ) 523 { 524 return m_cachedValidity; 525 } 526 527 m_cachedLastModificationDate = lm; 528 m_cachedValidity = new TimeStampValidity( lm ); 529 return m_cachedValidity; 530 } 531 532 return null; 533 } 534 535 538 public void refresh() 539 { 540 recycle(); 541 } 542 543 549 private void updateMimeType( final HttpMethod method ) 550 { 551 final Header header = method.getResponseHeader( CONTENT_TYPE ); 554 m_mimeType = header == null ? null : header.getValue(); 555 } 556 557 562 public String getMimeType() 563 { 564 updateData(); 565 return m_mimeType; 566 } 567 568 574 private void updateContentLength( final HttpMethod method ) 575 { 576 try 577 { 578 final Header length = 579 method.getResponseHeader( CONTENT_LENGTH ); 580 m_contentLength = 581 length == null ? -1 : Long.parseLong( length.getValue() ); 582 } 583 catch ( final NumberFormatException e ) 584 { 585 if ( getLogger().isDebugEnabled() ) 586 { 587 getLogger().debug( 588 "Unable to determine content length, returning -1", e 589 ); 590 } 591 592 m_contentLength = -1; 593 } 594 } 595 596 602 public long getContentLength() 603 { 604 updateData(); 605 return m_contentLength; 606 } 607 608 614 private void updateLastModified( final HttpMethod method ) 615 { 616 final Header lastModified = method.getResponseHeader( LAST_MODIFIED ); 617 m_lastModified = 618 lastModified == null ? 0 : Date.parse( lastModified.getValue() ); 619 } 620 621 627 public long getLastModified() 628 { 629 updateData(); 630 return m_lastModified; 631 } 632 633 637 private void recycle() 638 { 639 m_dataValid = false; 640 } 641 642 644 652 public OutputStream getOutputStream() throws IOException 653 { 654 final File tempFile = File.createTempFile("httpclient", "tmp"); 655 return new WrappedFileOutputStream( tempFile, getLogger() ); 656 } 657 658 663 private class WrappedFileOutputStream extends FileOutputStream 664 { 665 668 private File m_file; 669 670 673 private final Logger m_logger; 674 675 683 public WrappedFileOutputStream( final File file, final Logger logger ) 684 throws IOException 685 { 686 super( file ); 687 m_file = file; 688 m_logger = logger; 689 } 690 691 697 public void close() throws IOException 698 { 699 super.close(); 700 701 if ( m_file != null ) 702 { 703 upload(); 704 m_file.delete(); 705 m_file = null; 706 } 707 } 708 709 714 public boolean canCancel() 715 { 716 return m_file != null; 717 } 718 719 724 public void cancel() throws IOException 725 { 726 if ( m_file == null ) 727 { 728 throw new IOException ( "Stream already closed" ); 729 } 730 731 super.close(); 732 m_file.delete(); 733 m_file = null; 734 } 735 736 742 private void upload() 743 throws IOException 744 { 745 HttpMethod uploader = null; 746 747 if ( m_logger.isDebugEnabled() ) 748 { 749 m_logger.debug( "Stream closed, writing data to " + m_uri ); 750 } 751 752 try 753 { 754 uploader = createPutMethod( m_uri, m_file ); 755 final int response = executeMethod( uploader ); 756 757 if ( !successfulUpload( response ) ) 758 { 759 throw new SourceException( 760 "Write to " + m_uri + " failed (" + response + ")" 761 ); 762 } 763 764 if ( m_logger.isDebugEnabled() ) 765 { 766 m_logger.debug( 767 "Write to " + m_uri + " succeeded (" + response + ")" 768 ); 769 } 770 } 771 finally 772 { 773 if ( uploader != null ) 774 { 775 uploader.releaseConnection(); 776 } 777 } 778 } 779 780 787 private boolean successfulUpload( final int response ) 788 { 789 return response == HttpStatus.SC_OK 790 || response == HttpStatus.SC_CREATED 791 || response == HttpStatus.SC_NO_CONTENT; 792 } 793 } 794 795 800 public void delete() throws SourceException 801 { 802 try 803 { 804 final int response = 805 executeMethod( createDeleteMethod( m_uri ) ); 806 807 if ( !deleteSuccessful( response ) ) 808 { 809 throw new SourceException( 810 "Failed to delete " + m_uri + " (" + response + ")" 811 ); 812 } 813 814 if ( getLogger().isDebugEnabled() ) 815 { 816 getLogger().debug( m_uri + " deleted (" + response + ")"); 817 } 818 } 819 catch ( final IOException e ) 820 { 821 throw new SourceException( 822 "IOException thrown during delete", e 823 ); 824 } 825 } 826 827 834 private boolean deleteSuccessful( final int response ) 835 { 836 return response == HttpStatus.SC_OK 837 || response == HttpStatus.SC_ACCEPTED 838 || response == HttpStatus.SC_NO_CONTENT; 839 } 840 841 849 public boolean canCancel( final OutputStream stream ) 850 { 851 853 if ( stream instanceof WrappedFileOutputStream ) 854 { 855 return ((WrappedFileOutputStream) stream).canCancel(); 856 } 857 858 throw new IllegalArgumentException ( 859 "Output stream supplied was not created by this class" 860 ); 861 } 862 863 870 public void cancel( final OutputStream stream ) throws IOException 871 { 872 if ( stream instanceof WrappedFileOutputStream ) 873 { 874 ((WrappedFileOutputStream) stream).cancel(); 875 } 876 else 877 { 878 throw new IllegalArgumentException ( 879 "Output stream supplied was not created by this class" 880 ); 881 } 882 } 883 } 884 | Popular Tags |