| /* |
| * 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.commons.vfs.provider.webdav; |
| |
| import org.apache.commons.httpclient.HttpMethodBase; |
| import org.apache.commons.httpclient.HttpStatus; |
| import org.apache.commons.httpclient.HttpMethod; |
| import org.apache.commons.httpclient.URIException; |
| import org.apache.commons.httpclient.methods.RequestEntity; |
| import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; |
| import org.apache.commons.httpclient.util.DateUtil; |
| import org.apache.commons.vfs.provider.URLFileName; |
| import org.apache.commons.vfs.provider.DefaultFileContent; |
| import org.apache.commons.vfs.provider.http.HttpFileObject; |
| import org.apache.commons.vfs.util.FileObjectUtils; |
| import org.apache.commons.vfs.util.MonitorOutputStream; |
| import org.apache.commons.vfs.FileObject; |
| import org.apache.commons.vfs.FileSystemException; |
| import org.apache.commons.vfs.FileType; |
| import org.apache.commons.vfs.NameScope; |
| import org.apache.commons.vfs.FileNotFolderException; |
| import org.apache.commons.vfs.FileName; |
| import org.apache.commons.vfs.FileNotFoundException; |
| import org.apache.commons.vfs.FileContentInfoFactory; |
| import org.apache.jackrabbit.webdav.client.methods.DavMethod; |
| import org.apache.jackrabbit.webdav.client.methods.MkColMethod; |
| import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; |
| import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; |
| import org.apache.jackrabbit.webdav.client.methods.MoveMethod; |
| import org.apache.jackrabbit.webdav.client.methods.PutMethod; |
| import org.apache.jackrabbit.webdav.client.methods.CheckoutMethod; |
| import org.apache.jackrabbit.webdav.client.methods.CheckinMethod; |
| import org.apache.jackrabbit.webdav.client.methods.UncheckoutMethod; |
| import org.apache.jackrabbit.webdav.client.methods.VersionControlMethod; |
| import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod; |
| import org.apache.jackrabbit.webdav.DavException; |
| import org.apache.jackrabbit.webdav.DavConstants; |
| import org.apache.jackrabbit.webdav.MultiStatus; |
| import org.apache.jackrabbit.webdav.MultiStatusResponse; |
| import org.apache.jackrabbit.webdav.version.DeltaVConstants; |
| import org.apache.jackrabbit.webdav.version.VersionControlledResource; |
| import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; |
| import org.apache.jackrabbit.webdav.property.DavPropertyName; |
| import org.apache.jackrabbit.webdav.property.DavPropertySet; |
| import org.apache.jackrabbit.webdav.property.DavProperty; |
| import org.apache.jackrabbit.webdav.property.DefaultDavProperty; |
| import org.w3c.dom.Node; |
| |
| import java.io.IOException; |
| import java.io.ByteArrayOutputStream; |
| import java.io.OutputStream; |
| import java.net.HttpURLConnection; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.List; |
| import java.util.Iterator; |
| |
| /** |
| * A WebDAV file. |
| * |
| * @author <a href="http://commons.apache.org/vfs/team-list.html">Commons VFS team</a> |
| * @version $Revision$ |
| */ |
| public class WebdavFileObject extends HttpFileObject implements FileObject |
| { |
| /** The character set property name. */ |
| public static final DavPropertyName RESPONSE_CHARSET = DavPropertyName.create( |
| "response-charset"); |
| |
| private final WebdavFileSystem fileSystem; |
| private final String urlCharset; |
| |
| protected WebdavFileObject(final FileName name, final WebdavFileSystem fileSystem) |
| { |
| super(name, fileSystem); |
| this.fileSystem = fileSystem; |
| this.urlCharset = fileSystem.getFileSystemOptions().getUrlCharset(); |
| } |
| |
| protected void configureMethod(HttpMethodBase httpMethod) |
| { |
| httpMethod.setMethodRetryHandler(WebdavMethodRetryHandler.getInstance()); |
| } |
| |
| /** |
| * Determines the type of this file. Must not return null. The return |
| * value of this method is cached, so the implementation can be expensive. |
| */ |
| protected FileType doGetType() throws Exception |
| { |
| try |
| { |
| return isDirectory((URLFileName) getName()) ? FileType.FOLDER : FileType.FILE; |
| } |
| catch (FileNotFolderException fnfe) |
| { |
| return FileType.IMAGINARY; |
| } |
| catch (FileNotFoundException fnfe) |
| { |
| return FileType.IMAGINARY; |
| } |
| |
| } |
| |
| /** |
| * Lists the children of the file. |
| */ |
| protected String[] doListChildren() throws Exception |
| { |
| // use doListChildrenResolved for performance |
| return null; |
| } |
| |
| /** |
| * Lists the children of the file. |
| */ |
| protected FileObject[] doListChildrenResolved() throws Exception |
| { |
| PropFindMethod method = null; |
| try |
| { |
| URLFileName name = (URLFileName) getName(); |
| if (isDirectory(name)) |
| { |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_DISPLAYNAME)); |
| |
| method = new PropFindMethod(urlString(name), nameSet, |
| DavConstants.DEPTH_1); |
| |
| execute(method); |
| List vfs = new ArrayList(); |
| if (method.succeeded()) |
| { |
| MultiStatusResponse[] responses = |
| method.getResponseBodyAsMultiStatus().getResponses(); |
| |
| for (int i = 0; i < responses.length; ++i) |
| { |
| MultiStatusResponse response = responses[i]; |
| if (isCurrentFile(response.getHref(), name)) |
| { |
| continue; |
| } |
| String resourceName = resourceName(response.getHref()); |
| if (resourceName != null && resourceName.length() > 0) |
| { |
| WebdavFileObject fo = (WebdavFileObject) FileObjectUtils. |
| getAbstractFileObject(getFileSystem().resolveFile( |
| getFileSystem().getFileSystemManager(). |
| resolveName(getName(), resourceName, |
| NameScope.CHILD))); |
| vfs.add(fo); |
| } |
| } |
| } |
| return (WebdavFileObject[]) vfs.toArray(new WebdavFileObject[vfs.size()]); |
| } |
| throw new FileNotFolderException(getName()); |
| } |
| catch (FileNotFolderException fnfe) |
| { |
| throw fnfe; |
| } |
| catch (DavException e) |
| { |
| throw new FileSystemException(e.getMessage(), e); |
| } |
| catch (IOException e) |
| { |
| throw new FileSystemException(e.getMessage(), e); |
| } |
| finally |
| { |
| if (method != null) |
| { |
| method.releaseConnection(); |
| } |
| } |
| } |
| |
| /** |
| * Creates this file as a folder. |
| */ |
| protected void doCreateFolder() throws Exception |
| { |
| DavMethod method = new MkColMethod(urlString((URLFileName) getName())); |
| setupMethod(method); |
| try |
| { |
| execute(method); |
| } |
| catch (FileSystemException fse) |
| { |
| throw new FileSystemException("vfs.provider.webdav/create-collection.error", getName(), |
| fse); |
| } |
| } |
| |
| /** |
| * Deletes the file. |
| */ |
| protected void doDelete() throws Exception |
| { |
| DavMethod method = new DeleteMethod(urlString((URLFileName) getName())); |
| setupMethod(method); |
| execute(method); |
| } |
| |
| /** |
| * Rename the file. |
| */ |
| protected void doRename(FileObject newfile) throws Exception |
| { |
| String url = encodePath(urlString((URLFileName) getName())); |
| String dest = urlString((URLFileName) newfile.getName(), false); |
| DavMethod method = new MoveMethod(url, dest, false); |
| setupMethod(method); |
| execute(method); |
| } |
| |
| /** |
| * Returns the size of the file content (in bytes). |
| */ |
| protected long doGetContentSize() throws Exception |
| { |
| DavProperty property = getProperty((URLFileName) getName(), |
| DavConstants.PROPERTY_GETCONTENTLENGTH); |
| if (property != null) |
| { |
| String value = (String) property.getValue(); |
| return Long.parseLong(value); |
| } |
| return 0; |
| } |
| |
| /** |
| * Returns the last modified time of this file. Is only called if |
| * {@link #doGetType} does not return {@link FileType#IMAGINARY}. |
| */ |
| protected long doGetLastModifiedTime() throws Exception |
| { |
| DavProperty property = getProperty((URLFileName) getName(), |
| DavConstants.PROPERTY_GETLASTMODIFIED); |
| if (property != null) |
| { |
| String value = (String) property.getValue(); |
| return DateUtil.parseDate(value).getTime(); |
| } |
| return 0; |
| } |
| |
| /** |
| * Returns the properties of the Webdav resource. |
| */ |
| protected Map doGetAttributes() throws Exception |
| { |
| final Map attributes = new HashMap(); |
| try |
| { |
| URLFileName fileName = (URLFileName) getName(); |
| DavPropertySet properties = getProperties(fileName, PropFindMethod.PROPFIND_ALL_PROP, |
| new DavPropertyNameSet(), false); |
| Iterator iter = properties.iterator(); |
| while (iter.hasNext()) |
| { |
| DavProperty property = (DavProperty) iter.next(); |
| attributes.put(property.getName().toString(), property.getValue()); |
| } |
| properties = getPropertyNames(fileName); |
| iter = properties.iterator(); |
| while (iter.hasNext()) |
| { |
| DavProperty property = (DavProperty) iter.next(); |
| if (!attributes.containsKey(property.getName())) |
| { |
| property = getProperty(fileName, property.getName()); |
| attributes.put(property.getName().toString(), property.getValue()); |
| } |
| } |
| return attributes; |
| } |
| catch (Exception e) |
| { |
| throw new FileSystemException("vfs.provider.webdav/propfind.error", getName(), e); |
| } |
| } |
| |
| protected OutputStream doGetOutputStream(boolean bAppend) throws Exception |
| { |
| return new WebdavOutputStream(this); |
| } |
| |
| protected FileContentInfoFactory getFileContentInfoFactory() |
| { |
| return new WebdavFileContentInfoFactory(); |
| } |
| |
| /** |
| * Prepares a Method object. |
| * |
| * @param method the HttpMethod. |
| * @throws FileSystemException if an error occurs encoding the uri. |
| * @throws URIException if the URI is in error. |
| */ |
| protected void setupMethod(final HttpMethod method) throws FileSystemException, URIException |
| { |
| String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(urlCharset); |
| method.setPath(pathEncoded); |
| // All the WebDav methods are EntityEnclosingMethods and are not allowed to redirect. |
| method.setFollowRedirects(false); |
| method.setRequestHeader("User-Agent", "Jakarta-Commons-VFS"); |
| method.addRequestHeader("Cache-control", "no-cache"); |
| method.addRequestHeader("Cache-store", "no-store"); |
| method.addRequestHeader("Pragma", "no-cache"); |
| method.addRequestHeader("Expires", "0"); |
| } |
| |
| /** |
| * Execute a 'Workspace' operation. |
| * |
| * @param method The DavMethod to invoke. |
| * @throws FileSystemException If an error occurs. |
| */ |
| private void execute(DavMethod method) throws FileSystemException |
| { |
| try |
| { |
| int status = fileSystem.getClient().executeMethod(method); |
| if (status == HttpURLConnection.HTTP_NOT_FOUND |
| || status == HttpURLConnection.HTTP_GONE) |
| { |
| throw new FileNotFoundException(method.getURI()); |
| } |
| method.checkSuccess(); |
| } |
| catch (FileSystemException fse) |
| { |
| throw fse; |
| } |
| catch (IOException e) |
| { |
| throw new FileSystemException(e); |
| } |
| catch (DavException e) |
| { |
| throw ExceptionConverter.generate(e); |
| } |
| finally |
| { |
| if (method != null) |
| { |
| method.releaseConnection(); |
| } |
| } |
| } |
| |
| private boolean isDirectory(URLFileName name) throws IOException, DavException |
| { |
| try |
| { |
| DavProperty property = getProperty(name, DavConstants.PROPERTY_RESOURCETYPE); |
| Node node; |
| if (property != null && (node = (Node) property.getValue()) != null) |
| { |
| return node.getLocalName().equals(DavConstants.XML_COLLECTION); |
| } |
| else |
| { |
| return false; |
| } |
| } |
| catch (FileNotFoundException fse) |
| { |
| throw new FileNotFolderException(name); |
| } |
| } |
| |
| DavProperty getProperty(URLFileName fileName, String property) |
| throws FileSystemException |
| { |
| return getProperty(fileName, DavPropertyName.create(property)); |
| } |
| |
| DavProperty getProperty(URLFileName fileName, DavPropertyName name) |
| throws FileSystemException |
| { |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(name); |
| DavPropertySet propertySet = getProperties(fileName, nameSet, false); |
| return propertySet.get(name); |
| } |
| |
| DavPropertySet getProperties(URLFileName name, DavPropertyNameSet nameSet, boolean addEncoding) |
| throws FileSystemException |
| { |
| return getProperties(name, PropFindMethod.PROPFIND_BY_PROPERTY, nameSet, addEncoding); |
| } |
| |
| DavPropertySet getProperties(URLFileName name) throws FileSystemException |
| { |
| return getProperties(name, PropFindMethod.PROPFIND_ALL_PROP, new DavPropertyNameSet(), |
| false); |
| } |
| |
| |
| DavPropertySet getPropertyNames(URLFileName name) throws FileSystemException |
| { |
| return getProperties(name, PropFindMethod.PROPFIND_PROPERTY_NAMES, |
| new DavPropertyNameSet(), false); |
| } |
| |
| DavPropertySet getProperties(URLFileName name, int type, DavPropertyNameSet nameSet, |
| boolean addEncoding) |
| throws FileSystemException |
| { |
| try |
| { |
| String urlStr = urlString(name); |
| PropFindMethod method = new PropFindMethod(urlStr, type, nameSet, DavConstants.DEPTH_0); |
| setupMethod(method); |
| execute(method); |
| if (method.succeeded()) |
| { |
| MultiStatus multiStatus = method.getResponseBodyAsMultiStatus(); |
| MultiStatusResponse response = multiStatus.getResponses()[0]; |
| DavPropertySet props = response.getProperties(HttpStatus.SC_OK); |
| if (addEncoding) |
| { |
| DavProperty prop = new DefaultDavProperty(RESPONSE_CHARSET, |
| method.getResponseCharSet()); |
| props.add(prop); |
| } |
| return props; |
| } |
| return new DavPropertySet(); |
| } |
| catch (FileSystemException fse) |
| { |
| throw fse; |
| } |
| catch (Exception e) |
| { |
| throw new FileSystemException("vfs.provider.webdav/propfind.error", getName(), e); |
| } |
| } |
| |
| /** |
| * Returns the resource name from the path. |
| * |
| * @param path the path to the file. |
| * @return The resource name |
| */ |
| private String resourceName(String path) |
| { |
| if (path.endsWith("/")) |
| { |
| path = path.substring(0, path.length() - 1); |
| } |
| final int i = path.lastIndexOf("/"); |
| return (i >= 0) ? path.substring(i + 1) : path; |
| } |
| |
| private String urlString(URLFileName name) |
| { |
| return urlString(name, true); |
| } |
| |
| /** |
| * Convert the FileName to an encoded url String. |
| * |
| * @param name The FileName. |
| * @param includeUserInfo true if user information should be included. |
| * @return The encoded URL String. |
| */ |
| private String urlString(URLFileName name, boolean includeUserInfo) |
| { |
| String user = null; |
| String password = null; |
| if (includeUserInfo) |
| { |
| user = name.getUserName(); |
| password = name.getPassword(); |
| } |
| URLFileName newFile = new URLFileName("http", name.getHostName(), name.getPort(), |
| name.getDefaultPort(), user, password, |
| name.getPath(), name.getType(), name.getQueryString()); |
| try |
| { |
| return newFile.getURIEncoded(urlCharset); |
| } |
| catch (Exception e) |
| { |
| return name.getURI(); |
| } |
| } |
| |
| private boolean isCurrentFile(String href, URLFileName fileName) |
| { |
| String name = hrefString(fileName); |
| if (href.endsWith("/") && !name.endsWith("/")) |
| { |
| name += "/"; |
| } |
| return href.equals(name); |
| } |
| |
| /** |
| * Convert the FileName to an encoded url String. |
| * |
| * @param name The FileName. |
| * @return The encoded URL String. |
| */ |
| private String hrefString(URLFileName name) |
| { |
| URLFileName newFile = new URLFileName("http", name.getHostName(), name.getPort(), |
| name.getDefaultPort(), null, null, |
| name.getPath(), name.getType(), name.getQueryString()); |
| try |
| { |
| return newFile.getURIEncoded(urlCharset); |
| } |
| catch (Exception e) |
| { |
| return name.getURI(); |
| } |
| } |
| |
| /** |
| * An OutputStream that writes to a Webdav resource. |
| * |
| * @todo Use piped stream to avoid temporary file |
| */ |
| private class WebdavOutputStream extends MonitorOutputStream |
| { |
| private WebdavFileObject file; |
| |
| public WebdavOutputStream(WebdavFileObject file) throws FileSystemException |
| { |
| super(new ByteArrayOutputStream()); |
| this.file = file; |
| } |
| |
| /** |
| * Called after this stream is closed. |
| */ |
| protected void onClose() throws IOException |
| { |
| RequestEntity entity = new ByteArrayRequestEntity(((ByteArrayOutputStream) out).toByteArray()); |
| URLFileName fileName = (URLFileName) getName(); |
| String urlStr = urlString(fileName); |
| WebdavFileSystemOptions opts = fileSystem.getFileSystemOptions(); |
| if (opts.isVersioning()) |
| { |
| DavPropertySet set = null; |
| boolean fileExists = true; |
| boolean isCheckedIn = true; |
| try |
| { |
| set = getPropertyNames(fileName); |
| } |
| catch (FileNotFoundException fnfe) |
| { |
| fileExists = false; |
| } |
| if (fileExists && set != null) |
| { |
| if (set.contains(VersionControlledResource.CHECKED_OUT)) |
| { |
| isCheckedIn = false; |
| } |
| else if (!set.contains(VersionControlledResource.CHECKED_IN)) |
| { |
| DavProperty prop = set.get(VersionControlledResource.AUTO_VERSION); |
| if (prop != null) |
| { |
| prop = getProperty(fileName, VersionControlledResource.AUTO_VERSION); |
| if (DeltaVConstants.XML_CHECKOUT_CHECKIN.equals(prop.getValue())) |
| { |
| createVersion(urlStr); |
| } |
| } |
| } |
| } |
| if (fileExists && isCheckedIn) |
| { |
| try |
| { |
| CheckoutMethod checkout = new CheckoutMethod(urlStr); |
| setupMethod(checkout); |
| execute(checkout); |
| isCheckedIn = false; |
| } |
| catch (FileSystemException ex) |
| { |
| // Ignore the exception checking out. |
| } |
| } |
| |
| try |
| { |
| PutMethod method = new PutMethod(urlStr); |
| method.setRequestEntity(entity); |
| setupMethod(method); |
| execute(method); |
| setUserName(fileName, urlStr, opts); |
| } |
| catch (FileSystemException ex) |
| { |
| if (!isCheckedIn) |
| { |
| try |
| { |
| UncheckoutMethod method = new UncheckoutMethod(urlStr); |
| setupMethod(method); |
| execute(method); |
| isCheckedIn = true; |
| } |
| catch (Exception e) |
| { |
| // Ignore the exception. Going to throw original. |
| } |
| throw ex; |
| } |
| } |
| if (!fileExists) |
| { |
| createVersion(urlStr); |
| try |
| { |
| DavPropertySet props = getPropertyNames(fileName); |
| isCheckedIn = !props.contains(VersionControlledResource.CHECKED_OUT); |
| } |
| catch (FileNotFoundException fnfe) |
| { |
| // Ignore the error |
| } |
| } |
| if (!isCheckedIn) |
| { |
| CheckinMethod checkin = new CheckinMethod(urlStr); |
| setupMethod(checkin); |
| execute(checkin); |
| } |
| } |
| else |
| { |
| PutMethod method = new PutMethod(urlStr); |
| method.setRequestEntity(entity); |
| setupMethod(method); |
| execute(method); |
| try |
| { |
| setUserName(fileName, urlStr, opts); |
| } |
| catch (IOException e) |
| { |
| // Ignore the exception if unable to set the user name. |
| } |
| } |
| ((DefaultFileContent) this.file.getContent()).resetAttributes(); |
| } |
| |
| private void setUserName(URLFileName fileName, String urlStr, WebdavFileSystemOptions opts) |
| throws IOException |
| { |
| List list = new ArrayList(); |
| |
| String name = opts.getCreatorName(); |
| String userName = fileName.getUserName(); |
| if (name == null) |
| { |
| name = userName; |
| } |
| else |
| { |
| if (userName != null) |
| { |
| String comment = "Modified by user " + userName; |
| list.add(new DefaultDavProperty(DeltaVConstants.COMMENT, comment)); |
| } |
| } |
| list.add(new DefaultDavProperty(DeltaVConstants.CREATOR_DISPLAYNAME, name)); |
| PropPatchMethod method = new PropPatchMethod(urlStr, list); |
| setupMethod(method); |
| execute(method); |
| } |
| |
| private boolean createVersion(String urlStr) |
| { |
| try |
| { |
| VersionControlMethod method = new VersionControlMethod(urlStr); |
| setupMethod(method); |
| execute(method); |
| return true; |
| } |
| catch (Exception ex) |
| { |
| return false; |
| } |
| } |
| } |
| } |