/*
* 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);
}
}
}