You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by cr...@apache.org on 2003/07/02 15:24:12 UTC

cvs commit: avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl HTTPClientSourceFactory.java HTTPClientSource.java

crafterm    2003/07/02 06:24:10

  Modified:    sourceresolve build.xml default.properties
  Added:       sourceresolve/src/java/org/apache/excalibur/source/impl
                        HTTPClientSourceFactory.java HTTPClientSource.java
  Log:
  Initial cut at a Source implementation that uses the Jakarta Commons
  HttpClient library.
  
  To use this Source, make sure you have the commons-httpclient jar and the
  commons-logging jar (required by httpclient) available in your classpath
  somewhere.
  
  Revision  Changes    Path
  1.44      +19 -1     avalon-excalibur/sourceresolve/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/sourceresolve/build.xml,v
  retrieving revision 1.43
  retrieving revision 1.44
  diff -u -r1.43 -r1.44
  --- build.xml	15 Jun 2003 16:08:53 -0000	1.43
  +++ build.xml	2 Jul 2003 13:23:45 -0000	1.44
  @@ -15,6 +15,7 @@
           <pathelement location="${build.classes}"/>
           <pathelement location="${avalon-framework.jar}"/>
           <pathelement location="${checkstyle.jar}"/>
  +        <pathelement location="${commons-httpclient.jar}"/>
           <pathelement path="${java.class.path}"/>
       </path>
   
  @@ -28,6 +29,7 @@
       <path id="test.class.path">
           <pathelement location="${build.testclasses}"/>
           <pathelement location="${junit.jar}"/>
  +        <pathelement location="${commons-logging.jar}"/>	
           <path refid="project.class.path"/>
       </path>
       <property name="cp" refid="test.class.path"/>
  @@ -35,6 +37,16 @@
       <target name="main" depends="test,jar" description="Build the project"/>
       <target name="rebuild" depends="clean,main" description="Rebuild the project"/>
   
  +    <!-- Checks the environment for existing resources -->
  +    <target name="check-environment" description="Checks environment">
  +        <available property="commons-httpclient.present" classname="org.apache.commons.httpclient.HttpClient">
  +            <classpath refid="project.class.path"/>
  +        </available>
  +        <available property="commons-logging.present" classname="org.apache.commons.logging.LogSource">
  +            <classpath refid="test.class.path"/>
  +        </available>
  +    </target>
  +
       <target name="dependencies" description="Check dependencies" unless="skip.dependencies">
           <ant antfile="${depchecker.prefix}/depchecker.xml" target="checkCommon"/>
           <ant antfile="${depchecker.prefix}/depchecker.xml" target="checkFramework"/>
  @@ -49,7 +61,7 @@
   
   
       <!-- Compiles the source code -->
  -    <target name="compile" depends="dependencies" description="Compiles the source code">
  +    <target name="compile" depends="dependencies, check-environment" description="Compiles the source code">
   
           <mkdir dir="${build.classes}"/>
   
  @@ -62,6 +74,8 @@
               target="1.2">
               <classpath refid="project.class.path" />
               <include name="**/*.java"/>
  +	    <exclude name="**/HTTPClient*"
  +	            unless="commons-httpclient.present"/>
           </javac>
   
           <!-- copy resources to same location as .class files -->
  @@ -96,6 +110,8 @@
               target="1.2">
               <classpath refid="test.class.path" />
               <include name="**/*.java"/>
  +	    <exclude name="**/HTTPClient*"
  +	            unless="commons-httpclient.present"/>
           </javac>
   
           <copy todir="${build.testclasses}">
  @@ -188,6 +204,8 @@
                   <fileset dir="${build.testclasses}">
                       <include name="**/test/*TestCase.class"/>
                       <exclude name="**/Abstract*"/>
  +		    <exclude name="**/test/HTTPClient*"
  +		             unless="commons-httpclient.present"/>
                   </fileset>
               </batchtest>
   
  
  
  
  1.16      +6 -0      avalon-excalibur/sourceresolve/default.properties
  
  Index: default.properties
  ===================================================================
  RCS file: /home/cvs/avalon-excalibur/sourceresolve/default.properties,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- default.properties	2 Jul 2003 10:56:42 -0000	1.15
  +++ default.properties	2 Jul 2003 13:23:45 -0000	1.16
  @@ -77,5 +77,11 @@
   #  property indicating directory where all distribution archives are placed
   dist.base = distributions
   
  +# commons http client
  +commons-httpclient.jar = ../lib/commons-httpclient-2.0-beta1.jar
  +
  +# commons logging (required by commons http client)
  +commons-logging.jar = ../lib/commons-logging-1.0.3.jar
  +
   depchecker.prefix=.
   
  
  
  
  1.1                  avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/HTTPClientSourceFactory.java
  
  Index: HTTPClientSourceFactory.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software
   *    itself, if and wherever such third-party acknowledgments
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.excalibur.source.impl;
  
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.Map;
  
  import org.apache.avalon.framework.container.ContainerUtil;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceException;
  import org.apache.excalibur.source.SourceFactory;
  
  /**
   * {@link HTTPClientSource} Factory class.
   *
   * @avalon.component
   * @avalon.service type=SourceFactory
   * @x-avalon.info name=httpclient-source
   * @x-avalon.lifestyle type=singleton
   *
   * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
   * @version CVS $Id: HTTPClientSourceFactory.java,v 1.1 2003/07/02 13:23:58 crafterm Exp $
   */
  public final class HTTPClientSourceFactory extends AbstractLogEnabled
      implements SourceFactory, ThreadSafe
  {
      /**
       * Creates a {@link HTTPClientSource} instance.
       */
      public Source getSource( final String uri, final Map parameters )
          throws MalformedURLException, IOException
      {
          try
          {
              final HTTPClientSource source = 
                  new HTTPClientSource( uri, parameters );
              ContainerUtil.enableLogging( source, getLogger() );
              ContainerUtil.initialize( source );
              return source;
          }
          catch ( final MalformedURLException e )
          {
              throw e;
          }
          catch ( final IOException e ) 
          {
              throw e;
          }
          catch ( final Exception e )
          {
              final StringBuffer message = new StringBuffer();
              message.append( "Exception thrown while creating " );
              message.append( HTTPClientSource.class.getName() );
  
              throw new SourceException( message.toString(), e );
          }
      }
  
      /**
       * Releases the given {@link Source} object.
       *
       * @param source {@link Source} object to be released
       */
      public void release( final Source source )
      {
          if ( source instanceof HTTPClientSource )
          {
              HTTPClientSource src = (HTTPClientSource) source;
  
              ContainerUtil.dispose( src );
          }
          else
          {
              if ( getLogger().isDebugEnabled() )
              {
                  getLogger().debug( 
                      "Ignoring request to release non-HTTPClientSource object" 
                  );
              }
          }
      }
  }
  
  
  
  1.1                  avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl/HTTPClientSource.java
  
  Index: HTTPClientSource.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 The Apache Software Foundation. All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *    "This product includes software developed by the
   *    Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software
   *    itself, if and wherever such third-party acknowledgments
   *    normally appear.
   *
   * 4. The names "Jakarta", "Avalon", and "Apache Software Foundation"
   *    must not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation. For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.excalibur.source.impl;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.Date;
  import java.util.Map;
  
  import org.apache.avalon.framework.CascadingRuntimeException;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.commons.httpclient.Header;
  import org.apache.commons.httpclient.HttpClient;
  import org.apache.commons.httpclient.HttpStatus;
  import org.apache.commons.httpclient.URIException;
  import org.apache.commons.httpclient.methods.GetMethod;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceNotFoundException;
  import org.apache.excalibur.source.SourceValidity;
  import org.apache.excalibur.source.impl.validity.TimeStampValidity;
  
  /**
   * HTTP URL Source object, based on the Jakarta Commons
   * <a href="http://jakarta.apache.org/commons/httpclient/">HttpClient</a>
   * project.
   *
   * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
   * @version CVS $Id: HTTPClientSource.java,v 1.1 2003/07/02 13:23:58 crafterm Exp $
   */
  public class HTTPClientSource extends AbstractLogEnabled 
      implements Source, Initializable, Disposable
  {
      /**
       * Constant used when obtaining the Content-Type from HTTP Headers
       */
      public static final String CONTENT_TYPE   = "Content-Type";
  
      /**
       * Constant used when obtaining the Content-Length from HTTP Headers
       */
      public static final String CONTENT_LENGTH = "Content-Length";
  
      /**
       * Constant used when obtaining the Last-Modified date from HTTP Headers
       */
      public static final String LAST_MODIFIED  = "Last-Modified";
  
      /**
       * The URI being accessed.
       */
      private final String m_uri;
  
      /**
       * Contextual parameters passed via the {@link SourceFactory}.
       */
      private final Map m_parameters;
  
      /**
       * The {@link HttpClient} object.
       */
      private HttpClient m_client;
  
      /**
       * The {@link HttpMethod} being performed on the {@link HttpClient}.
       */
      private GetMethod m_method;
  
      /**
       * HTTP response returned from server after the {@link HttpMethod}
       * has been performed.
       */
      private int m_response;
  
      /**
       * Stored {@link SourceValidity} object.
       */
      private  SourceValidity m_cachedValidity;
  
      /**
       * Cached last modification date.
       */
      private long m_cachedLastModificationDate;
  
      /**
       * Constructor, creates a new {@link HTTPClientSource} instance.
       *
       * @param uri URI
       * @param parameters contextual parameters passed to this instance
       * @exception Exception if an error occurs
       */
      public HTTPClientSource( final String uri, final Map parameters )
          throws Exception
      {
          m_uri = uri;
          m_parameters = parameters;
      }
  
      /**
       * Initializes this {@link HTTPClientSource} instance.
       *
       * @exception Exception if an error occurs
       */
      public void initialize() throws Exception
      {
          m_client = new HttpClient();
  
          // REVISIT(MC): assume HTTP GET for the moment
          m_method = new GetMethod( m_uri );
          m_response = m_client.executeMethod( m_method );
      }
  
      /**
       * Method to ascertain whether the given resource actually exists.
       *
       * @return <code>true</code> if the resource pointed to by the 
       *         URI during construction exists, <code>false</code> 
       *         otherwise.
       */
      public boolean exists()
      {
          // REVISIT(MC): should this return true if the server does not return
          // a 404, or a 410, or should it only return true if the user can
          // successfully get an InputStream from it without getting errors.
  
          // resource does not exist if HttpClient returns a 404 or a 410
          return !( m_response == HttpStatus.SC_GONE || 
                    m_response == HttpStatus.SC_NOT_FOUND );
      }
      
      /**
       * Method to obtain an {@link InputStream} to read the response
       * from the server.
       *
       * @return {@link InputStream} containing data sent from the server.
       * @throws IOException if some I/O problem occurs.
       * @throws SourceNotFoundException if the source doesn't exist.
       */
      public InputStream getInputStream()
          throws IOException, SourceNotFoundException
      {
          // throw SourceNotFoundException - according to Source API we
          // need to throw this if the source doesn't exist.
          if ( !exists() )
          {
              final StringBuffer error = new StringBuffer();
              error.append( "Unable to retrieve URI: " );
              error.append( m_method.getURI() );
              error.append( " (" );
              error.append( m_response );
              error.append( ")" );
  
              throw new SourceNotFoundException( error.toString() );
          }
  
          return m_method.getResponseBodyAsStream();
      }
  
      /**
       * Obtain the absolute URI this {@link Source} object references.
       * 
       * @return the absolute URI this {@link String} object references.
       */
      public String getURI()
      {
          try
          {
              return m_method.getURI().getURI();
          }
          catch ( final URIException e )
          {
              throw new CascadingRuntimeException( "Unable to determine URI", e );
          }
      }
  
      /**
       * Return the URI scheme identifier, ie.  the part preceding the fist ':' 
       * in the URI (see <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>).
       * 
       * @return the URI scheme identifier
       */
      public String getScheme()
      {
          try
          {
              return m_method.getURI().getScheme();
          }
          catch ( final URIException e )
          {
              throw new CascadingRuntimeException( "Unable to determine URI Scheme", e );
          }
      }
      
      /**
       * Obtain a {@link SourceValidity} object.
       * 
       * @return a {@link SourceValidity} object, or 
       *         <code>null</code> if this is not possible.
       */
      public SourceValidity getValidity()
      {
          // Implementation taken from URLSource.java, Kudos :)
  
          final long lm = getLastModified();
  
          if ( lm > 0 )
          {
              if ( lm == m_cachedLastModificationDate )
              {
                  return m_cachedValidity;
              }
  
              m_cachedLastModificationDate = lm;
              m_cachedValidity = new TimeStampValidity( lm );
              return m_cachedValidity;
          }
          return null;
      }
  
      /**
       * Refreshes this {@link Source} object.
       */
      public void refresh()
      {
          try
          {
              recycle();
              initialize();
          }
          catch ( final Exception e )
          {
              final StringBuffer buf = new StringBuffer();
              buf.append( "Refresh on " );
              buf.append( m_uri );
              buf.append( " failed" );
  
              if ( getLogger().isWarnEnabled() )
              {
                  getLogger().warn( buf.toString(), e );
              }
          }
      }
  
      /**
       * Obtain the mime-type for the referenced resource.
       * 
       * @return mime-type for the referenced resource.
       */
      public String getMimeType()
      {
          // REVISIT: should this be the mime-type, or the content-type -> URLSource
          // returns the Content-Type, so we'll follow that for now.
          return m_method.getResponseHeader( CONTENT_TYPE ).getValue();
      }
  
      /**
       * Obtain the content length of the referenced resource.
       * 
       * @return content length of the referenced resource, or 
       *         -1 if unknown/uncalculatable
       */
      public long getContentLength()
      {
          try
          {
              final Header length = 
                  m_method.getResponseHeader( CONTENT_LENGTH );
              return length == null ? -1 : Long.parseLong( length.getValue() );
          }
          catch ( final NumberFormatException e )
          {
              if ( getLogger().isDebugEnabled() )
              {
                  getLogger().debug(
                      "Unable to determine content length, returning -1", e 
                  );
              }
  
              return -1;
          }
      }
  
      /**
       * Get the last modification date of this source. This date is
       * measured in milliseconds since the Epoch (00:00:00 GMT, January 1, 1970).
       * 
       * @return the last modification date or <code>0</code> if unknown.
       */
      public long getLastModified()
      {
          final Header lastModified = m_method.getResponseHeader( LAST_MODIFIED );
          return lastModified == null ? 0 : Date.parse( lastModified.getValue() );
      }
  
      /**
       * Disposes this {@link HTTPClientSource} instance.
       */
      public void dispose()
      {
          if ( m_method != null )
          {
              m_method.releaseConnection();
          }
      }
  
      /**
       * Recycles this {@link HTTPClientSource} object so that it may be reused
       * to refresh it's content. Note, after this method is called,
       * {@link HTTPClientSource}.invoke should be invoked.
       */
      private void recycle()
      {
          m_method.recycle();
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org


Re: cvs commit: avalon-excalibur/sourceresolve/src/java/org/apache/excalibur/source/impl HTTPClientSourceFactory.java HTTPClientSource.java

Posted by Anton Tagunov <at...@mail.cnt.ru>.
Hello, Marcus!

cao>       public boolean exists()
cao>       {
cao>           // REVISIT(MC): should this return true if the server does not return
cao>           // a 404, or a 410, or should it only return true if the user can
cao>           // successfully get an InputStream from it without getting errors.

Not sure I will ever use this code, so my opinion may be weak here,
just theoretically I would prefer to get true from exists iff I can
get an InputStream from it without getting errors.

But then probably we should do in FileSource the following change?

      public SourceValidity getValidity()
      {
-         if (m_file.exists())
+         if (m_file.canRead())
          {
              return new FileTimeStampValidity(m_file);
          }

          
      public boolean exists()
      {
-         return getFile().exists();
+         return getFile().canRead();
      }

Must confess I am sort of at a loss here too.
On the one hand, what is the user of knowing a file
exist without me being able to read from it.

On the other hand the method should have been called
canRead() then to avoid confusion.

:-/

  
cao>           // resource does not exist if HttpClient returns a 404 or a 410
cao>           return !( m_response == HttpStatus.SC_GONE || 
cao>                     m_response == HttpStatus.SC_NOT_FOUND );
cao>       }

- Anton


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org