/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.components.source.impl; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Properties; import java.util.Vector; import javax.xml.transform.OutputKeys; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.logger.Logger; import org.apache.cocoon.caching.validity.EventValidity; import org.apache.cocoon.components.source.InspectableSource; import org.apache.cocoon.components.source.helpers.SourceProperty; import org.apache.cocoon.components.webdav.WebDAVEventFactory; import org.apache.cocoon.xml.XMLUtils; 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.MoveableSource; 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.SourceUtil; 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.Property; import org.apache.webdav.lib.PropertyName; import org.apache.webdav.lib.ResponseEntity; 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. * * <h2>Protocol syntax</h2> * <p><code>webdav://[user[: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> * * @version $Id$ */ public class WebDAVSource extends AbstractLogEnabled implements Source, TraversableSource, ModifiableSource, ModifiableTraversableSource, InspectableSource, MoveableSource { 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"; // 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 event factory to get the Event objects from for event caching private WebDAVEventFactory eventfactory = null; // the SWCL resource private WebdavResource resource = null; // current resource initialization values private int depth = -1; private int action = -1; /** * Default constructor. */ private WebDAVSource(HttpURL url, String protocol) throws URIException { this.protocol = protocol; this.url = url; 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.NOACTION); // [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; } private void setWebDAVEventFactory(WebDAVEventFactory factory) { eventfactory = factory; } /** * 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 != WebdavResource.NOACTION) { if (action > this.action) { this.action = action; update = true; } else { action = this.action; } } if (depth > this.depth) { this.depth = depth; update = true; } else { depth = this.depth; } if (this.resource == null) { this.resource = new WebdavResource(this.url, action, depth); } else if (update) { this.resource.setProperties(action, 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); } if (e.getReasonCode() == HttpStatus.SC_BAD_REQUEST) { throw new SourceException("Server doesn't appear to understand WebDAV: "+getSecureURI(), e); } final String msg = "Could not initialize webdav resource at " + getSecureURI() + ". 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(HttpURL url, String protocol, Logger logger, WebDAVEventFactory eventfactory) throws URIException { final WebDAVSource source = new WebDAVSource(url, protocol); source.enableLogging(logger); source.setWebDAVEventFactory(eventfactory); return source; } /** * Static factory method to obtain a Source. */ private static WebDAVSource newWebDAVSource(WebdavResource resource, HttpURL url, String protocol, Logger logger, WebDAVEventFactory eventfactory) throws URIException { final WebDAVSource source = new WebDAVSource(resource, url, protocol); source.enableLogging(logger); source.setWebDAVEventFactory(eventfactory); return source; } // ---------------------------------------------------- Source implementation /** * Get the scheme for this Source. */ public String getScheme() { return this.protocol; } /** * Return the unique identifer for this source */ public String getURI() { 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() { 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... * If it is currently not possible to calculate such an information * <code>null</code> is returned. */ public SourceValidity getValidity() { SourceValidity validity = null; if(eventfactory!=null) { try { validity = new EventValidity(eventfactory.createEvent(this.url)); if(getLogger().isDebugEnabled()) getLogger().debug("Created EventValidity for source: "+validity); } catch (Exception e) { if(getLogger().isErrorEnabled()) getLogger().error("could not create EventValidity!",e); } } if( validity == null ) { if(getLogger().isDebugEnabled()) getLogger().debug("Falling back to TimeStampValidity!"); final long lm = getLastModified(); if (lm > 0) { validity = new TimeStampValidity(lm); } } return validity; } /** * Refresh the content of this object after the underlying data * content has changed. */ public void refresh() { this.resource = null; } /** * 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 SourceException("Could not get WebDAV resource " + getSecureURI(), he); } catch (Exception e) { throw new SourceException("Could not get WebDAV resource" + getSecureURI(), e); } } /** * The mime-type of the content described by this object. * If the source is not able to determine the mime-type by itself * 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(); } /** * Return the content length of the content or -1 if the length is * unknown */ public long getContentLength() { try { initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0); } catch(IOException e) { return -1; } if (this.resource.isCollection()) { return -1; } return this.resource.getGetContentLength(); } /** * Get the last modification date. * @return The last modification in milliseconds since January 1, 1970 GMT * 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(); } /** * Does this source actually exist ? * * @return true if the resource exists. */ public boolean exists() { try { initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0); } catch (SourceNotFoundException e) { return false; } catch(IOException e) { return true; } return this.resource.getExistence(); } private InputStream resourcesToXml(WebdavResource[] resources) throws Exception { TransformerFactory tf = TransformerFactory.newInstance(); TransformerHandler th = ((SAXTransformerFactory) tf).newTransformerHandler(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); StreamResult result = new StreamResult(bOut); th.setResult(result); th.startDocument(); th.startPrefixMapping(PREFIX, NAMESPACE); th.startElement(NAMESPACE, COLLECTION_NAME, PREFIX + ":" + COLLECTION_NAME, XMLUtils.EMPTY_ATTRIBUTES); resourcesToSax(resources, th); th.endElement(NAMESPACE, COLLECTION_NAME, PREFIX + ":" + COLLECTION_NAME); th.endPrefixMapping(PREFIX); th.endDocument(); return new ByteArrayInputStream(bOut.toByteArray()); } private void resourcesToSax( WebdavResource[] resources, ContentHandler handler) throws SAXException { for (int i = 0; i < resources.length; i++) { if (getLogger().isDebugEnabled()) { final String message = "RESOURCE: " + resources[i].getDisplayName(); getLogger().debug(message); } if (resources[i].isCollection()) { try { WebdavResource[] childs = resources[i].listWebdavResources(); AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute( NAMESPACE, COLLECTION_NAME, PREFIX + ":name", "CDATA", resources[i].getDisplayName()); handler.startElement( NAMESPACE, COLLECTION_NAME, PREFIX + ":" + COLLECTION_NAME, attrs); this.resourcesToSax(childs, handler); handler.endElement( NAMESPACE, COLLECTION_NAME, PREFIX + ":" + COLLECTION_NAME); } catch (HttpException e) { if (getLogger().isDebugEnabled()) { final String message = "Unable to get WebDAV children. Server responded " + e.getReasonCode() + " (" + e.getReason() + ") - " + e.getMessage(); getLogger().debug(message); } } catch (SAXException e) { if (getLogger().isDebugEnabled()) { final String message = "Unable to get WebDAV children: " + e.getMessage(); getLogger().debug(message,e); } } catch (IOException e) { if (getLogger().isDebugEnabled()) { final String message = "Unable to get WebDAV children: " + e.getMessage(); getLogger().debug(message,e); } } catch (Exception e) { if (getLogger().isDebugEnabled()) { final String message = "Unable to get WebDAV children: " + e.getMessage(); getLogger().debug(message,e); } } } else { AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute( NAMESPACE, "name", PREFIX + ":name", "CDATA", resources[i].getDisplayName()); handler.startElement( NAMESPACE, RESOURCE_NAME, PREFIX + ":" + RESOURCE_NAME, attrs); handler.endElement( NAMESPACE, RESOURCE_NAME, PREFIX + ":" + RESOURCE_NAME); } } } // ---------------------------------------------------- TraversableSource implementation /** * Get a collection child. * * @see org.apache.excalibur.source.TraversableSource#getChild(java.lang.String) */ public Source getChild(String childName) throws SourceException { if (!isCollection()) { throw new SourceException(getSecureURI() + " is not a collection."); } 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(), eventfactory); } catch (URIException e) { throw new SourceException("Failed to create child", e); } } /** * Get the collection children. * * @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++) { 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(), this.eventfactory); src.enableLogging(getLogger()); children.add(src); } } catch (HttpException e) { if (getLogger().isDebugEnabled()) { final String message = "Unable to get WebDAV children. Server responded " + e.getReasonCode() + " (" + e.getReason() + ") - " + e.getMessage(); getLogger().debug(message); } throw new SourceException("Failed to get WebDAV collection children.", e); } catch (SourceException e) { throw e; } catch (IOException e) { throw new SourceException("Failed to get WebDAV collection children.", e); } return children; } /** * Get the name of this resource. * @see org.apache.excalibur.source.TraversableSource#getName() */ public String getName() { 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 { String path; if (this.url.getEscapedPath().endsWith("/")) { path = ".."; } else { path = "."; } try { HttpURL parentURL; if (url instanceof HttpsURL) { parentURL = new HttpsURL((HttpsURL) this.url, path); } else { parentURL = new HttpURL(this.url, path); } return WebDAVSource.newWebDAVSource(parentURL, this.protocol, getLogger(), eventfactory); } catch (URIException e) { throw new SourceException("Failed to create parent", e); } } /** * Check if this source is a collection. * @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 { initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0); try { this.resource.deleteMethod(); } catch (HttpException e) { throw new SourceException("Unable to delete source: " + getSecureURI(), e); } catch (IOException e) { throw new SourceException("Unable to delete source: " + getSecureURI(), e); } } 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: outputstream 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 { initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0); if (this.resource.exists()) return; try { if (!this.resource.mkcolMethod()) { int status = this.resource.getStatusCode(); if (status == 409) { // parent does not exist, create it and try again ((ModifiableTraversableSource) getParent()).makeCollection(); makeCollection(); } else if (status == 404) { // apparently mod_dav_svn wrongly returns 404 // on MKCOL when parent does not exist ((ModifiableTraversableSource) getParent()).makeCollection(); makeCollection(); } // Ignore status 405 - Not allowed: collection already exists else if (status != 405) { final String msg = "Unable to create collection " + getSecureURI() + ". Server responded " + this.resource.getStatusCode() + " (" + this.resource.getStatusMessage() + ")"; throw new SourceException(msg); } } } catch (HttpException e) { throw new SourceException("Unable to create collection(s) " + getSecureURI(), e); } catch (SourceException e) { throw e; } catch (IOException e) { throw new SourceException("Unable to create collection(s)" + getSecureURI(), e); } } // ---------------------------------------------------- InspectableSource implementation /** * Returns a enumeration of the properties * * @return Enumeration of SourceProperty * * @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()) { ResponseEntity response = (ResponseEntity)responses.nextElement(); props = response.getProperties(); while (props.hasMoreElements()) { prop = (Property) props.nextElement(); SourceProperty srcProperty = new SourceProperty(prop.getElement()); sourceproperties.addElement(srcProperty); } } } catch (Exception e) { throw new SourceException("Error getting properties", e); } SourceProperty[] sourcepropertiesArray = new SourceProperty[sourceproperties.size()]; for (int i = 0; i<sourceproperties.size(); i++) { sourcepropertiesArray[i] = (SourceProperty) sourceproperties.elementAt(i); } return sourcepropertiesArray; } /** * Returns a property from a source. * * @param namespace Namespace of the property * @param name Name of the property * * @return Property of the source. * * @throws SourceException If an exception occurs. */ 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; } /** * Remove a specified source property. * * @param namespace Namespace of the property. * @param name Name of the property. * * @throws SourceException If an exception occurs. */ 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) { throw new SourceException("Could not remove property ", e); } } /** * Sets a property for a source. * * @param sourceproperty Property of the source * * @throws SourceException If an exception occurs during this operation */ public void setSourceProperty(SourceProperty sourceproperty) throws SourceException { initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0); try { Node node = null; NodeList list = sourceproperty.getValue().getChildNodes(); for (int i=0; i<list.getLength(); i++) { if ((list.item(i) instanceof Text && !"".equals(list.item(i).getNodeValue())) || list.item(i) instanceof Element) { node = list.item(i); break; } } Properties format = new Properties(); format.put(OutputKeys.METHOD, "xml"); format.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); String prop = XMLUtils.serializeNode(node, format); this.resource.proppatchMethod( new PropertyName(sourceproperty.getNamespace(),sourceproperty.getName()), prop, true); } catch(HttpException e) { final String message = "Unable to set property. Server responded " + e.getReasonCode() + " (" + e.getReason() + ") - " + e.getMessage(); getLogger().debug(message); throw new SourceException("Could not set property ", e); } catch (Exception e) { 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(); // } // } // ---------------------------------------------------- MoveableSource /** * Move the current source to a specified destination. * * @param source * * @throws SourceException If an exception occurs during the move. */ public void moveTo(Source source) throws SourceException { if (source instanceof WebDAVSource) { initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0); WebDAVSource destination = (WebDAVSource)source; destination.initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0); try { this.resource.moveMethod(destination.resource.getHttpURL().getPath()); } catch (HttpException e) { throw new SourceException("Cannot move source '"+getSecureURI()+"'", e); } catch (IOException e) { throw new SourceException("Cannot move source '"+getSecureURI()+"'", e); } } else { SourceUtil.move(this,source); } } /** * Copy the current source to a specified destination. * * @param source * * @throws SourceException If an exception occurs during the copy. */ public void copyTo(Source source) throws SourceException { if (source instanceof WebDAVSource) { initResource(WebdavResource.BASIC, DepthSupport.DEPTH_0); WebDAVSource destination = (WebDAVSource)source; destination.initResource(WebdavResource.NOACTION, DepthSupport.DEPTH_0); try { this.resource.copyMethod(destination.resource.getHttpURL().getPath()); } catch (HttpException e) { throw new SourceException("Cannot copy source '"+getSecureURI()+"'", e); } catch (IOException e) { throw new SourceException("Cannot copy source '"+getSecureURI()+"'", e); } } else { SourceUtil.copy(this,source); } } }