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