You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by un...@apache.org on 2004/03/27 16:49:42 UTC

cvs commit: cocoon-2.1/src/blocks/webdav/java/org/apache/cocoon/components/source/impl WebDAVSourceFactory.java WebDAVSource.java

unico       2004/03/27 07:49:42

  Modified:    src/blocks/webdav/java/org/apache/cocoon/components/source/impl
                        WebDAVSourceFactory.java WebDAVSource.java
  Log:
  WebDAVSource refactorings:
  - lazy initialization of SWCL in order to work with CachingSource
  - more control over initialization action and depth parameters
  - cleanup url parsing
  - add ablility to use ssl
  - better doco and add some FIXME's
  - remove unimplemented methods and interfaces
  - properly implement cancel and canCancel methods
  
  Revision  Changes    Path
  1.8       +39 -26    cocoon-2.1/src/blocks/webdav/java/org/apache/cocoon/components/source/impl/WebDAVSourceFactory.java
  
  Index: WebDAVSourceFactory.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/webdav/java/org/apache/cocoon/components/source/impl/WebDAVSourceFactory.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- WebDAVSourceFactory.java	5 Mar 2004 13:02:26 -0000	1.7
  +++ WebDAVSourceFactory.java	27 Mar 2004 15:49:41 -0000	1.8
  @@ -19,13 +19,17 @@
   import java.net.MalformedURLException;
   import java.util.Map;
   
  +import org.apache.avalon.framework.configuration.Configurable;
  +import org.apache.avalon.framework.configuration.Configuration;
  +import org.apache.avalon.framework.configuration.ConfigurationException;
   import org.apache.avalon.framework.logger.AbstractLogEnabled;
  +import org.apache.avalon.framework.parameters.Parameters;
   import org.apache.avalon.framework.thread.ThreadSafe;
   import org.apache.commons.httpclient.HttpURL;
  +import org.apache.commons.httpclient.HttpsURL;
   import org.apache.excalibur.source.Source;
   import org.apache.excalibur.source.SourceException;
   import org.apache.excalibur.source.SourceFactory;
  -import org.apache.excalibur.source.SourceParameters;
   
   /**
    *  A factory for WebDAV sources
  @@ -35,44 +39,53 @@
    *  @author <a href="mailto:d.madama@pro-netics.com">Daniele Madama</a>
    *  @version $Id$
   */
  -public class WebDAVSourceFactory
  -    extends AbstractLogEnabled
  -    implements SourceFactory, ThreadSafe {
  +public class WebDAVSourceFactory extends AbstractLogEnabled 
  +implements SourceFactory, Configurable, ThreadSafe {
   
  +    private String protocol;
  +    private boolean secure;
  +    
  +    /**
  +     * Read the scheme name.
  +     */
  +    public void configure(Configuration configuration) throws ConfigurationException {
  +        this.protocol = configuration.getAttribute("name");
  +        
  +        // parse parameters
  +        Parameters parameters = Parameters.fromConfiguration(configuration);
  +        this.secure = parameters.getParameterAsBoolean("secure", false);
  +    }
  +    
       /**
        * Get a <code>Source</code> object.
        * @param parameters This is optional.
        */
       public Source getSource(String location, Map parameters)
           throws MalformedURLException, IOException, SourceException {
  -        if ((this.getLogger() != null)
  -            && (this.getLogger().isDebugEnabled())) {
  +        
  +        if (this.getLogger().isDebugEnabled()) {
               this.getLogger().debug("Creating source object for " + location);
           }
  -
  -        final String protocol = location.substring(0, location.indexOf(':'));
  -
  -		HttpURL url = new HttpURL("http://" + location.substring(location.indexOf(':')+3));       
  -		String principal = url.getUser();
  -		String password = url.getPassword();
  -		location = url.getHost() + ":" + url.getPort();
  -		if(url.getPathQuery() != null) location += url.getPathQuery();
  -
  -        if(principal == null || password == null) {
  -			String queryString = url.getQuery();
  -			SourceParameters locationParameters = new SourceParameters(queryString);
  -			principal = locationParameters.getParameter("principal", principal);
  -			password = locationParameters.getParameter("password", password);
  +        
  +        int index = location.indexOf(':');
  +        if (index != -1) {
  +            location = location.substring(index+3);
           }
  -
  -        WebDAVSource source =
  -            WebDAVSource.newWebDAVSource(location, principal, password, protocol,getLogger());
  -            
  -        return source;
  +        
  +		HttpURL url;
  +		if (this.secure) {
  +		    url = new HttpsURL("https://" + location);
  +		}
  +		else {
  +		    url = new HttpURL("http://" + location);
  +		}
  +        
  +        return WebDAVSource.newWebDAVSource(url, this.protocol, getLogger());
       }
   
       public void release(Source source) {
           // do nothing
       }
  +
   
   }
  
  
  
  1.23      +484 -377  cocoon-2.1/src/blocks/webdav/java/org/apache/cocoon/components/source/impl/WebDAVSource.java
  
  Index: WebDAVSource.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/webdav/java/org/apache/cocoon/components/source/impl/WebDAVSource.java,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- WebDAVSource.java	22 Mar 2004 17:09:39 -0000	1.22
  +++ WebDAVSource.java	27 Mar 2004 15:49:41 -0000	1.23
  @@ -21,12 +21,11 @@
   import java.io.IOException;
   import java.io.InputStream;
   import java.io.OutputStream;
  +import java.util.ArrayList;
   import java.util.Collection;
  -import java.util.Iterator;
  +import java.util.Enumeration;
   import java.util.Properties;
   import java.util.Vector;
  -import java.util.Enumeration;
  -import java.util.ArrayList;
   
   import javax.xml.transform.OutputKeys;
   import javax.xml.transform.TransformerFactory;
  @@ -36,207 +35,273 @@
   
   import org.apache.avalon.framework.logger.AbstractLogEnabled;
   import org.apache.avalon.framework.logger.Logger;
  -import org.apache.cocoon.components.source.RestrictableSource;
  -import org.apache.cocoon.components.source.helpers.SourceCredential;
  -import org.apache.cocoon.components.source.helpers.SourcePermission;
  -import org.apache.cocoon.components.source.helpers.SourceProperty;
   import org.apache.cocoon.components.source.InspectableSource;
  +import org.apache.cocoon.components.source.helpers.SourceProperty;
   import org.apache.cocoon.xml.XMLUtils;
  -import org.apache.commons.httpclient.HttpURL;
   import org.apache.commons.httpclient.HttpException;
  +import org.apache.commons.httpclient.HttpStatus;
  +import org.apache.commons.httpclient.HttpURL;
  +import org.apache.commons.httpclient.HttpsURL;
  +import org.apache.commons.httpclient.URIException;
  +import org.apache.excalibur.source.ModifiableSource;
   import org.apache.excalibur.source.ModifiableTraversableSource;
   import org.apache.excalibur.source.Source;
   import org.apache.excalibur.source.SourceException;
  +import org.apache.excalibur.source.SourceNotFoundException;
  +import org.apache.excalibur.source.SourceParameters;
   import org.apache.excalibur.source.SourceValidity;
  +import org.apache.excalibur.source.TraversableSource;
   import org.apache.excalibur.source.impl.validity.TimeStampValidity;
  -import org.apache.webdav.lib.WebdavResource;
  -import org.apache.webdav.lib.methods.DepthSupport;
   import org.apache.webdav.lib.Property;
   import org.apache.webdav.lib.PropertyName;
   import org.apache.webdav.lib.ResponseEntity;
  -import org.xml.sax.ContentHandler;
  -import org.xml.sax.SAXException;
  -import org.xml.sax.helpers.AttributesImpl;
  -
  +import org.apache.webdav.lib.WebdavResource;
  +import org.apache.webdav.lib.methods.DepthSupport;
   import org.w3c.dom.Element;
   import org.w3c.dom.Node;
   import org.w3c.dom.NodeList;
   import org.w3c.dom.Text;
  +import org.xml.sax.ContentHandler;
  +import org.xml.sax.SAXException;
  +import org.xml.sax.helpers.AttributesImpl;
   
   /**
  - *  A source implementation to get access to WebDAV repositories. Use it
  - *  as webdav://[usr]:[password]@[host][:port]/path.
  - *
  - *  @author <a href="mailto:g.casper@s-und-n.de">Guido Casper</a>
  - *  @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
  - *  @author <a href="mailto:d.madama@pro-netics.com">Daniele Madama</a>
  - *  @version $Id$
  + * A source implementation to get access to WebDAV repositories.
  + * 
  + * <h2>Protocol syntax</h2>
  + * <p><code>webdav://[usr[:password]@]host[:port][/path][?cocoon:webdav-depth][&cocoon:webdav-action]</code></p>
  + * <p>
  + *  <ul>
  + *   <li>
  + *    <code>cocoon:webdav-depth</code> allows to specify the default depth
  + *    to use during initialization of the webdav resource.
  + *   </li>
  + *   <li>
  + *    <code>cocoon:webdav-action</code> allows to specify a default action
  + *    to take upon initialization of the webdav resource.
  + *   </li>
  + *  </ul>
  + * <p>
  + * 
  + * @author <a href="mailto:g.casper@s-und-n.de">Guido Casper</a>
  + * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
  + * @author <a href="mailto:d.madama@pro-netics.com">Daniele Madama</a>
  + * @version $Id$
   */
  -public class WebDAVSource extends AbstractLogEnabled implements Source,
  -    RestrictableSource, ModifiableTraversableSource, InspectableSource {
  -
  +public class WebDAVSource extends AbstractLogEnabled 
  +implements Source, TraversableSource, ModifiableSource, ModifiableTraversableSource, InspectableSource {
   
       private static final String NAMESPACE = "http://apache.org/cocoon/webdav/1.0";
   
       private static final String PREFIX = "webdav";
  -
       private static final String RESOURCE_NAME = "resource";
  -
       private static final String COLLECTION_NAME = "collection";
  -
  -    private String systemId;
       
  -    private String location;
  -    private String principal;
  -    private String password;
  -
  -    private SourceValidity validity = null;
  -    private long cachedLastModificationDate;
  -    private SourceCredential sourcecredential = null;
  -
  +    // the http url
  +    private final HttpURL url;
  +    
  +    // the scheme name
  +    private final String protocol;
  +    
  +    // cached uri and secureUri values
  +    private String uri;
  +    private String secureUri;
  +    
  +    // the SWCL resource
       private WebdavResource resource = null;
  -    private String protocol;
  +    
  +    // current resource initialization values
  +    private int depth = -1;
  +    private int action = -1;
   
  -    private WebDAVSource(
  -        String location,
  -        String principal,
  -        String password,
  -        String protocol,
  -        boolean createNew)
  -        throws HttpException, IOException {
  -            
  -        this.location = location;
  -        this.principal = principal;
  -        this.password = password;
  +    /**
  +     * Default constructor.
  +     */
  +    private WebDAVSource(HttpURL url, String protocol) throws URIException {
           this.protocol = protocol;
  +        this.url = url;
           
  -        this.systemId = "http://" + location;
  -        
  -        HttpURL httpURL = new HttpURL(this.systemId);
  -        httpURL.setUserinfo(principal, password);
  -        
  -        if (createNew) {
  -            this.resource = new WebdavResource(httpURL, 
  -                WebdavResource.NOACTION, 
  -                DepthSupport.DEPTH_1);
  -        }
  -        else {
  -            this.resource = new WebdavResource(httpURL);
  +        String qs = url.getQuery();
  +        if (qs != null) {
  +            final SourceParameters sp = new SourceParameters(qs);
  +            
  +            // parse optional start depth and start action qs parameters
  +            this.depth = sp.getParameterAsInteger("cocoon:webdav-depth", DepthSupport.DEPTH_1);
  +            this.action = sp.getParameterAsInteger("cocoon:webdav-action", WebdavResource.BASIC);
  +            
  +            // [UH] FIXME: Why this alternative way of passing in credentials?
  +            String principal = url.getUser();
  +            String password = url.getPassword();
  +            if (principal == null || password == null) {
  +                principal = sp.getParameter("cocoon:webdav-principal", principal);
  +                password = sp.getParameter("cocoon:webdav-password", password);
  +                if (principal != null) {
  +                    url.setUser(principal);
  +                    url.setPassword(password);
  +                }
  +            }
  +
  +            sp.removeParameter("cocoon:webdav-depth");
  +            sp.removeParameter("cocoon:webdav-action");
  +            sp.removeParameter("cocoon:webdav-principal");
  +            sp.removeParameter("cocoon:webdav-password");
  +            
  +            // set the qs without WebdavSource specific parameters
  +            url.setQuery(sp.getQueryString());
           }
  -        
  +    }
  +
  +    /**
  +     * Constructor used by getChildren() method.
  +     */
  +    private WebDAVSource (WebdavResource resource, HttpURL url, String protocol) 
  +    throws URIException {
  +        this(url, protocol);
  +        this.resource = resource;
  +    }
  +    
  +    /**
  +     * Initialize the SWCL WebdavResource.
  +     * <p>
  +     * The action argument specifies a set of properties to load during initialization. 
  +     * Its value is one of WebdavResource.NOACTION, WebdavResource.NAME,
  +     * WebdavResource.BASIC, WebdavResource.DEFAULT, WebdavResource.ALL.
  +     * Similarly the depth argument specifies the depth header of the PROPFIND 
  +     * method that is executed upon initialization.
  +     * </p>
  +     * <p>
  +     * The different methods of this Source implementation call this method to 
  +     * initialize the resource using their minimal action and depth requirements.
  +     * For instance the WebDAVSource.getMimeType() method requires WebdavResource.BASIC
  +     * properties and a search depth of 0 is sufficient.
  +     * </p>
  +     * <p>
  +     * However it may be that a later call (eg. WebDAVSource.getChildren()) requires more
  +     * information. In that case the WebdavResource would have to make another call to the Server.
  +     * It would be more efficient if previous initialization had been done using depth 1 instead.
  +     * In order give the user more control over this the WebDAVSource can be passed a minimal
  +     * action and depth using cocoon:webdav-depth and cocoon:webdav-action query string parameters.
  +     * By default the mimimum action is WebdavResource.BASIC (which loads all the following basic 
  +     * webdav properties: DAV:displayname, DAV:getcontentlength, DAV:getcontenttype DAV:resourcetype,
  +     * DAV:getlastmodified and DAV:lockdiscovery). The default minimum depth is 1.
  +     * </p>
  +     * 
  +     * @param action  the set of propterties the WebdavResource should load.
  +     * @param depth  the webdav depth.
  +     * @throws SourceException
  +     * @throws SourceNotFoundException
  +     */
  +    private void initResource(int action, int depth) throws SourceException, SourceNotFoundException {
  +		try {
  +            boolean update = false;
  +            if (action > this.action) {
  +                this.action = action;
  +                update = true;
  +            }
  +            if (depth > this.depth) {
  +                this.depth = depth;
  +                update = true;
  +            }
  +			if (this.resource == null) {
  +			    this.resource = new WebdavResource(this.url, this.action, this.depth);
  +			}
  +			else if (update) {
  +			    this.resource.setProperties(this.action, this.depth);
  +			}
  +            if (this.action > WebdavResource.NOACTION) {
  +                if (this.resource.isCollection()) {
  +                    String path = this.url.getPath();
  +                    if (path.charAt(path.length()-1) != '/') {
  +                        this.url.setPath(path + "/");
  +                    }
  +                }
  +            }
  + 		}
  +		catch (HttpException e) {
  +            if (e.getReasonCode() == HttpStatus.SC_NOT_FOUND) {
  +                throw new SourceNotFoundException("Not found: " + getSecureURI(), e);
  +            }
  +            final String msg = "Could not initialize webdav resource. Server responded " 
  +                + e.getReasonCode() + " (" + e.getReason() + ") - " + e.getMessage();
  +            throw new SourceException(msg, e);
  +		}
  +		catch (IOException e) {
  +            throw new SourceException("Could not initialize webdav resource", e);
  +		}
       }
   
       /**
        * Static factory method to obtain a Source.
        */
  -    public static WebDAVSource newWebDAVSource(String location,
  -                                               String principal,
  -                                               String password,
  +    public static WebDAVSource newWebDAVSource(HttpURL url,
                                                  String protocol,
  -                                               Logger logger) throws SourceException {
  -        // FIXME: wild hack needed for writing to a new resource.
  -        // if a resource doesn't exist, an exception
  -        // will be thrown, unless such resource isn't created with the
  -        // WebdavResouce.NOACTION flag. However, such flag cannot be
  -        // used by default, since it won't allow to discover the
  -        // properties of an exixting resource. So either we do this
  -        // hack here or we fill properties on the fly when requested.
  -        // This "solution" is scary, but the SWCL is pretty dumb.
  -        WebDAVSource source;
  -        try {
  -            source = new WebDAVSource(location, principal, password, protocol, false);
  -        }  catch (HttpException he) {
  -            try {
  -                source = new WebDAVSource(location, principal, password, protocol, true);
  -            } catch (HttpException finalHe) {
  -                final String message = "Error creating the source.";
  -                throw new SourceException(message, finalHe);
  -            } catch (IOException e) {
  -                final String message = "Error creating the source.";
  -                throw new SourceException(message, e);
  -            }
  -        } catch (IOException e) {
  -            final String message = "Error creating the source.";
  -            throw new SourceException(message, e);
  -        }
  +                                               Logger logger) 
  +    throws URIException {
  +        final WebDAVSource source = new WebDAVSource(url, protocol);
           source.enableLogging(logger);
           return source;
       }
  -
  +    
       /**
  -     * Constructor used by the Traversable methods to build children.
  +     * Static factory method to obtain a Source.
        */
  -    private WebDAVSource (WebdavResource source, String principal, String password)
  -    throws HttpException, IOException {
  -        this.resource = source;
  -        this.systemId = source.getHttpURL().getURI();
  -        source.getHttpURL().setUserinfo(principal, password);
  -
  -        //fix trailing slash
  -        if (this.resource.isCollection() && (this.systemId.endsWith("/") == false)) {
  -            this.systemId = this.systemId+"/";
  -            HttpURL httpURL = new HttpURL(this.systemId);
  -            httpURL.setUserinfo(principal, password);
  -            this.resource.setHttpURL(httpURL);
  -        }
  +    private static WebDAVSource newWebDAVSource(WebdavResource resource,
  +                                                HttpURL url,
  +                                                String protocol,
  +                                                Logger logger) 
  +    throws URIException {
  +        final WebDAVSource source = new WebDAVSource(resource, url, protocol);
  +        source.enableLogging(logger);
  +        return source;
       }
  -
  +    
  +    // ---------------------------------------------------- Source implementation
  +    
       /**
  -     * Get the scheme for this Source (webdav://).
  +     * Get the scheme for this Source.
        */
  -
       public String getScheme() {
           return this.protocol;
       }
   
       /**
  -     * Return an <code>InputStream</code> object to read from the source.
  -     * This is the data at the point of invocation of this method,
  -     * so if this is Modifiable, you might get different content
  -     * from two different invocations.
  -     */
  -    public InputStream getInputStream() throws IOException, SourceException {
  -        try {
  -            if (this.resource.isCollection()) {
  -                WebdavResource[] resources =
  -                    this.resource.listWebdavResources();
  -                return resourcesToXml(resources);
  -            } else {
  -                BufferedInputStream bi = null;
  -                bi = new BufferedInputStream(this.resource.getMethodData());
  -                if (!this.resource.exists()) {
  -                    throw new HttpException(this.systemId + " does not exist");
  -                }
  -                return bi;
  -            }
  -        } catch (HttpException he) {
  -            throw new SourceException("Could not get WebDAV resource " + getSecureURI(), he);
  -        } catch (Exception e) {
  -            throw new SourceException("Could not get WebDAV resource" + getSecureURI(), e);
  -        }
  -    }
  -
  -    /**
        * Return the unique identifer for this source
        */
       public String getURI() {
  -        // Change http: to webdav:
  -        //return "webdav:" + this.systemId.substring(5);
  -        //add Source credentials
  -        if (principal != null)
  -            return "webdav://" + this.principal + ":" + this.password + "@" +  this.systemId.substring(7);
  -        else
  -            return "webdav://"  +  this.systemId.substring(7);
  +        if (this.uri == null) {
  +            String uri = this.url.toString();
  +            final int index = uri.indexOf("://");
  +            if (index != -1) {
  +                uri = uri.substring(index+3);
  +            }
  +            final String userinfo = this.url.getEscapedUserinfo();
  +            if (userinfo != null) {
  +                uri = this.protocol + "://" + userinfo + "@" + uri;
  +            }
  +            else {
  +                uri = this.protocol + "://" + uri;
  +            }
  +            this.uri = uri;
  +        }
  +        return this.uri;
       }
       
       /**
        * Return the URI securely, without username and password
  -     * 
        */
       protected String getSecureURI() {
  -		return "webdav://"  +  this.systemId.substring(7);    	
  +        if (this.secureUri == null) {
  +            String uri = this.url.toString();
  +            int index = uri.indexOf("://");
  +            if (index != -1) {
  +                uri = uri.substring(index+3);
  +            }
  +            uri = this.protocol + "://" + uri;
  +            this.secureUri = uri;
  +        }
  +        return this.secureUri;
       }
  -
  +    
       /**
        *  Get the Validity object. This can either wrap the last modification
        *  date or the expires information or...
  @@ -244,21 +309,11 @@
        *  <code>null</code> is returned.
        */
       public SourceValidity getValidity() {
  -    	// TODO: Implementation taken from HttpClientSource, who took it from URLSource: time for a separate impl?
  -		final long lm = getLastModified();
  -
  -		if ( lm > 0 )
  -		{
  -			if ( lm == cachedLastModificationDate )
  -			{
  -				return validity;
  -			}
  -
  -			cachedLastModificationDate = lm;
  -			validity = new TimeStampValidity( lm );
  -			return validity;
  -		}
  -		return null;
  +        final long lm = getLastModified();
  +        if (lm > 0) {
  +            return new TimeStampValidity(lm);
  +        }
  +        return null;
       }
   
       /**
  @@ -266,20 +321,36 @@
        * content has changed.
        */
       public void refresh() {
  -        try {
  -            this.resource = new WebdavResource(this.systemId);
  +        this.resource = null;
  +    }
   
  -            if (sourcecredential != null)
  -                resource.setUserInfo(
  -                    sourcecredential.getPrincipal(),
  -                    sourcecredential.getPassword());
  +    /**
  +     * Return an <code>InputStream</code> object to read from the source.
  +     * This is the data at the point of invocation of this method,
  +     * so if this is Modifiable, you might get different content
  +     * from two different invocations.
  +     */
  +    public InputStream getInputStream() throws IOException, SourceException {
  +        initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
  +        try {
  +            if (this.resource.isCollection()) {
  +                // [UH] FIXME: why list collection as XML here?
  +                // I think its a concern for the TraversableGenerator.
  +                WebdavResource[] resources = this.resource.listWebdavResources();
  +                return resourcesToXml(resources);
  +            } else {
  +                BufferedInputStream bi = null;
  +                bi = new BufferedInputStream(this.resource.getMethodData());
  +                if (!this.resource.exists()) {
  +                    throw new HttpException(getSecureURI() + " does not exist");
  +                }
  +                return bi;
  +            }
           } catch (HttpException he) {
  -            throw new IllegalStateException(he.getMessage());
  -        } catch (IOException ioe) {
  -            throw new IllegalStateException(ioe.getMessage());
  +            throw new SourceException("Could not get WebDAV resource " + getSecureURI(), he);
  +        } catch (Exception e) {
  +            throw new SourceException("Could not get WebDAV resource" + getSecureURI(), e);
           }
  -
  -        this.validity = null;
       }
   
       /**
  @@ -288,6 +359,12 @@
        * this can be <code>null</code>.
        */
       public String getMimeType() {
  +        try {
  +            initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
  +        }
  +        catch (IOException e) {
  +            return null;
  +        }
           return this.resource.getGetContentType();
       }
   
  @@ -296,6 +373,12 @@
        * unknown
        */
       public long getContentLength() {
  +        try {
  +            initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
  +        }
  +        catch(IOException e) {
  +            return -1;
  +        }
           if (this.resource.isCollection()) {
               return -1;
           }
  @@ -308,171 +391,31 @@
        *         or 0 if it is unknown
        */
       public long getLastModified() {
  +        try {
  +            initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
  +        }
  +        catch(IOException e) {
  +            return 0;
  +        }
           return this.resource.getGetLastModified();
       }
   
       /**
  -     * Get the value of a parameter.
  -     * Using this it is possible to get custom information provided by the
  -     * source implementation, like an expires date, HTTP headers etc.
  -     */
  -    public String getParameter(String name) {
  -        return null;
  -    }
  -
  -    /**
  -     * Get the value of a parameter.
  -     * Using this it is possible to get custom information provided by the
  -     * source implementation, like an expires date, HTTP headers etc.
  -     */
  -    public long getParameterAsLong(String name) {
  -        return -1;
  -    }
  -
  -    /**
  -     * Get parameter names
  -     * Using this it is possible to get custom information provided by the
  -     * source implementation, like an expires date, HTTP headers etc.
  -     */
  -    public Iterator getParameterNames() {
  -        return null;
  -    }
  -
  -    /**
        * Does this source actually exist ?
        *
        * @return true if the resource exists.
        */
       public boolean exists() {
  -        return this.resource.getExistence();
  -    }
  -
  -    /**
  -     * Get an <code>InputStream</code> where raw bytes can be written to.
  -     * The signification of these bytes is implementation-dependent and
  -     * is not restricted to a serialized XML document.
  -     *
  -     * @return a stream to write to
  -     */
  -    public OutputStream getOutputStream() throws IOException {
  -        return new WebDAVSourceOutputStream(this.resource);
  -    }
  -
  -    /**
  -     * Can the data sent to an <code>OutputStream</code> returned by
  -     * {@link #getOutputStream()} be cancelled ?
  -     *
  -     * @return true if the stream can be cancelled
  -     */
  -    public boolean canCancel(OutputStream stream) {
  -        return true;
  -    }
  -
  -    /**
  -     * Cancel the data sent to an <code>OutputStream</code> returned by
  -     * {@link #getOutputStream()}.
  -     * <p>
  -     * After cancel, the stream should no more be used.
  -     */
  -    public void cancel(OutputStream stream) throws SourceException {
  -        // The content will only be send, if outputstream.close() executed.
  -    }
  -
  -    /** 
  -     * Get the current credential for the source
  -     */
  -    public SourceCredential getSourceCredential() throws SourceException {
  -        return this.sourcecredential;
  -    }
  -
  -    /** 
  -     * Set the credential for the source
  -     */
  -    public void setSourceCredential(SourceCredential sourcecredential)
  -        throws SourceException {
  -        this.sourcecredential = sourcecredential;
  -        if (sourcecredential == null) return;
  -        try {
  -            HttpURL httpURL = new HttpURL(this.systemId);
  -            httpURL.setUserinfo(
  -                sourcecredential.getPrincipal(),
  -                sourcecredential.getPassword());
  -            this.resource = new WebdavResource(httpURL);
  -        } catch (HttpException he) {
  -            throw new SourceException("Could not set credentials", he);
  -        } catch (IOException ioe) {
  -            throw new IllegalStateException(ioe.getMessage());
  +        try {
  +            initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
           }
  -    }
  -
  -    /**
  -     * Set a permission to this source
  -     *
  -     * @param sourcepermission Permission, which should be set
  -     *
  -     * @throws SourceException If an exception occurs during this operation
  -     */
  -    public void setSourcePermission(SourcePermission sourcepermission)
  -        throws SourceException {
  -        //FIXME
  -    }
  -
  -    /**
  -     * Returns a list of the existing permissions
  -     *
  -     * @return Array of SourcePermission
  -     */
  -    public SourcePermission[] getSourcePermissions() throws SourceException {
  -        //FIXME
  -        return null;
  -    }
  -
  -    public class WebDAVSourceOutputStream extends ByteArrayOutputStream {
  -
  -        private WebdavResource resource = null;
  -
  -        protected WebDAVSourceOutputStream(WebdavResource resource) {
  -            this.resource = resource;
  +        catch (SourceNotFoundException e) {
  +            return false;
           }
  -
  -        public void close() throws IOException {
  -            super.close();
  -
  -            try {
  -                this.resource.putMethod(toByteArray());
  -            } catch (HttpException he) {
  -                final String message =
  -                    "Unable to close output stream. Server responded " +
  -                    he.getReasonCode() + " (" + he.getReason() + ") - " 
  -                    + he.getMessage();
  -                getLogger().debug(message);
  -                throw new IOException(he.getMessage());
  -            }
  +        catch(IOException e) {
  +            return true;
           }
  -    }
  -
  -    /**
  -     * Add a permission to this source
  -     *
  -     * @param sourcepermission Permission, which should be set
  -     *
  -     * @throws SourceException If an exception occurs during this operation
  -     **/
  -    public void addSourcePermission(SourcePermission sourcepermission)
  -        throws SourceException {
  -        // FIXME
  -    }
  -
  -    /**
  -     * Remove a permission from this source
  -     *
  -     * @param sourcepermission Permission, which should be removed
  -     *
  -     * @throws SourceException If an exception occurs during this operation
  -     **/
  -    public void removeSourcePermission(SourcePermission sourcepermission)
  -        throws SourceException {
  -        // FIXME
  +        return this.resource.getExistence();
       }
   
       private InputStream resourcesToXml(WebdavResource[] resources)
  @@ -577,21 +520,27 @@
           }
       }
   
  +    // ---------------------------------------------------- TraversableSource implementation
  +    
       /**
        * Get a collection child.
        *
        * @see org.apache.excalibur.source.TraversableSource#getChild(java.lang.String)
        */
  -    public Source getChild(String childName) throws SourceException {    	
  -        String childLocation = this.location + "/" + childName;
  -        WebDAVSource source = WebDAVSource.newWebDAVSource(
  -            childLocation, 
  -            this.principal, 
  -            this.password, 
  -            this.protocol, 
  -            this.getLogger());
  -        source.setSourceCredential(this.getSourceCredential());
  -        return source;
  +    public Source getChild(String childName) throws SourceException {
  +        try {
  +            HttpURL childURL;
  +            if (this.url instanceof HttpsURL) {
  +                childURL = new HttpsURL((HttpsURL) this.url, childName);
  +            }
  +            else {
  +                childURL = new HttpURL(this.url, childName);
  +            }
  +            return WebDAVSource.newWebDAVSource(childURL, this.protocol, getLogger());
  +        }
  +        catch (URIException e) {
  +            throw new SourceException("Failure creating child", e);
  +        }        
       }
   
       /**
  @@ -600,14 +549,27 @@
        * @see org.apache.excalibur.source.TraversableSource#getChildren()
        */
       public Collection getChildren() throws SourceException {
  +        initResource(WebdavResource.BASIC, DepthSupport.DEPTH_1);
           ArrayList children = new ArrayList();
           try {
               WebdavResource[] resources = this.resource.listWebdavResources();
               for (int i = 0; i < resources.length; i++) {
  -                WebDAVSource src = new WebDAVSource(resources[i], this.principal, this.password);
  +                HttpURL childURL;
  +                if (this.url instanceof HttpsURL) {
  +                    childURL = new HttpsURL((HttpsURL) this.url,resources[i].getName());
  +                }
  +                else {
  +                    childURL = new HttpURL(this.url,resources[i].getName());
  +                }
  +                WebDAVSource src = WebDAVSource.newWebDAVSource(resources[i],
  +                                                                childURL,
  +                                                                this.protocol,
  +                                                                getLogger());
  +                src.enableLogging(getLogger());
                   children.add(src);
               }
  -        } catch (HttpException e) {
  +        }
  +        catch (HttpException e) {
               if (getLogger().isDebugEnabled()) {
                   final String message =
                       "Unable to get WebDAV children. Server responded " +
  @@ -616,7 +578,11 @@
                   getLogger().debug(message);
               }
               throw new SourceException("Failed to get WebDAV collection children.", e);
  -        } catch (IOException e) {
  +        } 
  +        catch (SourceException e) {
  +            throw e;
  +        }
  +        catch (IOException e) {
               throw new SourceException("Failed to get WebDAV collection children.", e);
           }
           return children;
  @@ -627,24 +593,34 @@
        * @see org.apache.excalibur.source.TraversableSource#getName()
        */
       public String getName() {
  -        return this.resource.getDisplayName();
  +        try {
  +            initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +        }
  +        catch (IOException e) {
  +            return "";
  +        }
  +        return this.resource.getName();
       }
   
       /**
        * Get the parent.
  +     * 
        * @see org.apache.excalibur.source.TraversableSource#getParent()
        */
       public Source getParent() throws SourceException {
  -        int last = this.location.lastIndexOf("/");
  -        String myLocation = this.location.substring(0, last);
  -        WebDAVSource wds = WebDAVSource.newWebDAVSource(
  -                myLocation,
  -                this.principal,
  -                this.password,
  -                this.protocol,
  -                this.getLogger());
  -        wds.setSourceCredential(this.getSourceCredential());
  -        return wds;
  +        try {
  +            HttpURL parentURL;
  +            if (url instanceof HttpsURL) {
  +                parentURL = new HttpsURL((HttpsURL) this.url, ".");
  +            }
  +            else {
  +                parentURL = new HttpURL(this.url, ".");
  +            }
  +            return WebDAVSource.newWebDAVSource(parentURL, this.protocol, getLogger());
  +        }
  +        catch (URIException e) {
  +            throw new SourceException("Failure creating parent", e);
  +        }
       }
   
       /**
  @@ -652,15 +628,72 @@
        * @see org.apache.excalibur.source.TraversableSource#isCollection()
        */
       public boolean isCollection() {
  +        try {
  +            initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0);
  +        }
  +        catch (IOException e) {
  +            return false;
  +        }
           return this.resource.isCollection();
       }
  +    
  +    // ---------------------------------------------------- ModifiableSource implementation
  +    
  +    /**
  +     * Get an <code>OutputStream</code> where raw bytes can be written to.
  +     * The signification of these bytes is implementation-dependent and
  +     * is not restricted to a serialized XML document.
  +     *
  +     * @return a stream to write to
  +     */
  +    public OutputStream getOutputStream() throws IOException {
  +        return new WebDAVSourceOutputStream(this);
  +    }
  +
  +    /**
  +     * Can the data sent to an <code>OutputStream</code> returned by
  +     * {@link #getOutputStream()} be cancelled ?
  +     *
  +     * @return true if the stream can be cancelled
  +     */
  +    public boolean canCancel(OutputStream stream) {
  +        if (stream instanceof WebDAVSourceOutputStream) {
  +            WebDAVSourceOutputStream wsos = (WebDAVSourceOutputStream) stream;
  +            if (wsos.source == this) {
  +                return wsos.canCancel();
  +            }
  +        }
  +        throw new IllegalArgumentException("The stream is not associated to this source");
  +    }
  +
  +    /**
  +     * Cancel the data sent to an <code>OutputStream</code> returned by
  +     * {@link #getOutputStream()}.
  +     * <p>
  +     * After cancel, the stream should no more be used.
  +     */
  +    public void cancel(OutputStream stream) throws SourceException {
  +        if (stream instanceof WebDAVSourceOutputStream) {
  +            WebDAVSourceOutputStream wsos = (WebDAVSourceOutputStream) stream;
  +            if (wsos.source == this) {
  +                try {
  +                    wsos.cancel();
  +                }
  +                catch (Exception e) {
  +                    throw new SourceException("Failure cancelling Source", e);
  +                }
  +            }
  +        }
  +        throw new IllegalArgumentException("The stream is not associated to this source");
  +    }
   
       /** 
        * Delete this source (unimplemented).
        * @see org.apache.excalibur.source.ModifiableSource#delete()
        */
       public void delete() throws SourceException {
  -    	try {
  +    	initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +        try {
               this.resource.deleteMethod();
           } catch (HttpException e) {
           	throw new SourceException("Unable to delete source: " + getSecureURI(), e);
  @@ -669,12 +702,56 @@
           }
       }
   
  +    private static class WebDAVSourceOutputStream extends ByteArrayOutputStream {
  +
  +        private WebDAVSource source = null;
  +        private boolean isClosed = false;
  +
  +        private WebDAVSourceOutputStream(WebDAVSource source) {
  +            this.source = source;
  +        }
  +
  +        public void close() throws IOException {
  +            if (!isClosed) {
  +                try {
  +                    super.close();
  +                    this.source.initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +                    this.source.resource.putMethod(toByteArray());
  +                } catch (HttpException he) {
  +                    final String message =
  +                        "Unable to close output stream. Server responded " +
  +                        he.getReasonCode() + " (" + he.getReason() + ") - " 
  +                        + he.getMessage();
  +                    this.source.getLogger().debug(message);
  +                    throw new IOException(he.getMessage());
  +                }
  +                finally {
  +                    this.isClosed = true;
  +                }
  +            }
  +        }
  +        
  +        private boolean canCancel() {
  +            return !isClosed;
  +        }
  +        
  +        private void cancel() {
  +            if (isClosed) {
  +                throw new IllegalStateException("Cannot cancel: outputstrem is already closed");
  +            }
  +            this.isClosed = true;
  +        }
  +    }
  +    
  +    // ---------------------------------------------------- ModifiableTraversableSource implementation
  +    
       /**
        * Create the collection, if it doesn't exist.
        * @see org.apache.excalibur.source.ModifiableTraversableSource#makeCollection()
        */
       public void makeCollection() throws SourceException {
  -    	if (resource.exists()) return;
  +        initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +        if (resource.exists()) return;
       	try {
               resource.mkcolMethod();
           } catch (HttpException e) {
  @@ -684,6 +761,8 @@
           }
       }
       
  +    // ---------------------------------------------------- InspectableSource implementation
  +    
       /**
        * Returns a enumeration of the properties
        *
  @@ -692,11 +771,13 @@
        * @throws SourceException If an exception occurs.
        */
        public SourceProperty[] getSourceProperties() throws SourceException {
  -
  +         
  +         initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +         
            Vector sourceproperties = new Vector();
            Enumeration props= null;
            org.apache.webdav.lib.Property prop = null;
  -        
  +         
            try {
                Enumeration responses = this.resource.propfindMethod(0);
                while (responses.hasMoreElements()) {
  @@ -730,27 +811,28 @@
        *
        * @throws SourceException If an exception occurs.
        */
  -    public SourceProperty getSourceProperty (String namespace, String name)
  -    throws SourceException {
  -
  -          Vector propNames = new Vector(1);
  -          propNames.add(new PropertyName(namespace,name));
  -          Enumeration props= null;
  -          org.apache.webdav.lib.Property prop = null;
  -          try {
  -              Enumeration responses = this.resource.propfindMethod(0, propNames);
  -              while (responses.hasMoreElements()) {
  -                  ResponseEntity response = (ResponseEntity)responses.nextElement();
  -                  props = response.getProperties();
  -                  if (props.hasMoreElements()) {
  -                      prop = (Property) props.nextElement();
  -                      return new SourceProperty(prop.getElement());
  -                  }
  -              }
  -          } catch (Exception e) {
  -              throw new SourceException("Error getting property: "+name, e);
  -          }
  -          return null;
  +    public SourceProperty getSourceProperty (String namespace, String name) throws SourceException {
  +        
  +        initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +        
  +        Vector propNames = new Vector(1);
  +        propNames.add(new PropertyName(namespace,name));
  +        Enumeration props= null;
  +        org.apache.webdav.lib.Property prop = null;
  +        try {
  +            Enumeration responses = this.resource.propfindMethod(0, propNames);
  +            while (responses.hasMoreElements()) {
  +                ResponseEntity response = (ResponseEntity) responses.nextElement();
  +                props = response.getProperties();
  +                if (props.hasMoreElements()) {
  +                    prop = (Property) props.nextElement();
  +                    return new SourceProperty(prop.getElement());
  +                }
  +            }
  +        } catch (Exception e) {
  +            throw new SourceException("Error getting property: "+name, e);
  +        }
  +        return null;
       }
   
       /**
  @@ -763,7 +845,9 @@
        */
       public void removeSourceProperty(String namespace, String name)
       throws SourceException {
  -
  +        
  +        initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +        
           try {
               this.resource.proppatchMethod(new PropertyName(namespace, name), "", false);
           } catch (Exception e) {
  @@ -778,9 +862,10 @@
        *
        * @throws SourceException If an exception occurs during this operation
        */
  -    public void setSourceProperty(SourceProperty sourceproperty)
  -    throws SourceException {
  -
  +    public void setSourceProperty(SourceProperty sourceproperty) throws SourceException {
  +        
  +        initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0);
  +        
           try {
   			Node node = null;
               NodeList list = sourceproperty.getValue().getChildNodes();
  @@ -813,4 +898,26 @@
               throw new SourceException("Could not set property ", e);
           }
       }
  +    
  +    /** 
  +     * Get the current credential for the source
  +     */
  +//    public SourceCredential getSourceCredential() throws SourceException {
  +//        if (this.principal != null) {
  +//            return new SourceCredential(this.principal, this.password);
  +//        }
  +//        return null;
  +//    }
  +
  +    /** 
  +     * Set the credential for the source
  +     */
  +//    public void setSourceCredential(SourceCredential sourcecredential)
  +//        throws SourceException {
  +//        if (sourcecredential != null) {
  +//            this.password = sourcecredential.getPassword();
  +//            this.principal = sourcecredential.getPrincipal();
  +//            refresh();
  +//        }
  +//    }
   }