This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-vfs.git
The following commit(s) were added to refs/heads/master by this push: new 4434be76 Use for-each loop 4434be76 is described below commit 4434be760962847d554db7f850b59132706619a5 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sat Jul 9 09:42:14 2022 -0400 Use for-each loop --- .../vfs2/provider/webdav4/Webdav4FileObject.java | 1263 ++++++++++---------- 1 file changed, 629 insertions(+), 634 deletions(-) diff --git a/commons-vfs2-jackrabbit2/src/main/java/org/apache/commons/vfs2/provider/webdav4/Webdav4FileObject.java b/commons-vfs2-jackrabbit2/src/main/java/org/apache/commons/vfs2/provider/webdav4/Webdav4FileObject.java index acc33516..094d0756 100644 --- a/commons-vfs2-jackrabbit2/src/main/java/org/apache/commons/vfs2/provider/webdav4/Webdav4FileObject.java +++ b/commons-vfs2-jackrabbit2/src/main/java/org/apache/commons/vfs2/provider/webdav4/Webdav4FileObject.java @@ -1,634 +1,629 @@ -/* - * 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.vfs2.provider.webdav4; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.vfs2.FileContentInfoFactory; -import org.apache.commons.vfs2.FileNotFolderException; -import org.apache.commons.vfs2.FileNotFoundException; -import org.apache.commons.vfs2.FileObject; -import org.apache.commons.vfs2.FileSystemException; -import org.apache.commons.vfs2.FileType; -import org.apache.commons.vfs2.NameScope; -import org.apache.commons.vfs2.provider.AbstractFileName; -import org.apache.commons.vfs2.provider.DefaultFileContent; -import org.apache.commons.vfs2.provider.GenericURLFileName; -import org.apache.commons.vfs2.provider.http4.Http4FileObject; -import org.apache.commons.vfs2.util.FileObjectUtils; -import org.apache.commons.vfs2.util.MonitorOutputStream; -import org.apache.commons.vfs2.util.URIUtils; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.utils.DateUtils; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.ContentType; -import org.apache.jackrabbit.webdav.DavConstants; -import org.apache.jackrabbit.webdav.DavException; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.MultiStatusResponse; -import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest; -import org.apache.jackrabbit.webdav.client.methods.HttpCheckin; -import org.apache.jackrabbit.webdav.client.methods.HttpCheckout; -import org.apache.jackrabbit.webdav.client.methods.HttpDelete; -import org.apache.jackrabbit.webdav.client.methods.HttpMkcol; -import org.apache.jackrabbit.webdav.client.methods.HttpMove; -import org.apache.jackrabbit.webdav.client.methods.HttpPropfind; -import org.apache.jackrabbit.webdav.client.methods.HttpProppatch; -import org.apache.jackrabbit.webdav.client.methods.HttpVersionControl; -import org.apache.jackrabbit.webdav.property.DavProperty; -import org.apache.jackrabbit.webdav.property.DavPropertyIterator; -import org.apache.jackrabbit.webdav.property.DavPropertyName; -import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; -import org.apache.jackrabbit.webdav.property.DavPropertySet; -import org.apache.jackrabbit.webdav.property.DefaultDavProperty; -import org.apache.jackrabbit.webdav.version.DeltaVConstants; -import org.apache.jackrabbit.webdav.version.VersionControlledResource; -import org.apache.jackrabbit.webdav.xml.Namespace; -import org.w3c.dom.Node; - -/** - * A WebDAV file. - * - * @since 2.5.0 - */ -public class Webdav4FileObject extends Http4FileObject<Webdav4FileSystem> { - - /** - * An OutputStream that writes to a Webdav resource. - * <p> - * TODO - Use piped stream to avoid temporary file. - */ - private class WebdavOutputStream extends MonitorOutputStream { - private final Webdav4FileObject file; - - WebdavOutputStream(final Webdav4FileObject file) { - super(new ByteArrayOutputStream()); - this.file = file; - } - - private boolean createVersion(final String urlStr) { - try { - final HttpVersionControl request = new HttpVersionControl(urlStr); - setupRequest(request); - executeRequest(request); - return true; - } catch (final Exception ex) { - return false; - } - } - - /** - * Called after this stream is closed. - */ - @Override - protected void onClose() throws IOException { - final HttpEntity entity = new ByteArrayEntity(((ByteArrayOutputStream) out).toByteArray()); - final GenericURLFileName fileName = (GenericURLFileName) getName(); - final String urlStr = toUrlString(fileName); - if (builder.isVersioning(getFileSystem().getFileSystemOptions())) { - DavPropertySet set = null; - boolean fileExists = true; - boolean isCheckedIn = true; - try { - set = getPropertyNames(fileName); - } catch (final 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 { - final HttpCheckout request = new HttpCheckout(urlStr); - setupRequest(request); - executeRequest(request); - isCheckedIn = false; - } catch (final FileSystemException ex) { - log(ex); - } - } - - try { - final HttpPut request = new HttpPut(urlStr); - request.setEntity(entity); - setupRequest(request); - executeRequest(request); - setUserName(fileName, urlStr); - } catch (final FileSystemException ex) { - if (!isCheckedIn) { - try { - final HttpCheckin request = new HttpCheckin(urlStr); - setupRequest(request); - executeRequest(request); - isCheckedIn = true; - } catch (final Exception e) { - // Going to throw original. - log(e); - } - throw ex; - } - } - if (!fileExists) { - createVersion(urlStr); - try { - final DavPropertySet props = getPropertyNames(fileName); - isCheckedIn = !props.contains(VersionControlledResource.CHECKED_OUT); - } catch (final FileNotFoundException fnfe) { - log(fnfe); - } - } - if (!isCheckedIn) { - final HttpCheckin request = new HttpCheckin(urlStr); - setupRequest(request); - executeRequest(request); - } - } else { - final HttpPut request = new HttpPut(urlStr); - request.setEntity(entity); - setupRequest(request); - executeRequest(request); - try { - setUserName(fileName, urlStr); - } catch (final IOException e) { - // Unable to set the user name. - log(e); - } - } - ((DefaultFileContent) this.file.getContent()).resetAttributes(); - } - - private void setUserName(final GenericURLFileName fileName, final String urlStr) throws IOException { - final DavPropertySet setProperties = new DavPropertySet(); - final DavPropertyNameSet removeProperties = new DavPropertyNameSet(); - String name = builder.getCreatorName(getFileSystem().getFileSystemOptions()); - final String userName = fileName.getUserName(); - if (name == null) { - name = userName; - } else if (userName != null) { - final String comment = "Modified by user " + userName; - setProperties.add(new DefaultDavProperty<>(DeltaVConstants.COMMENT, comment)); - } - setProperties.add(new DefaultDavProperty<>(DeltaVConstants.CREATOR_DISPLAYNAME, name)); - final HttpProppatch request = new HttpProppatch(urlStr, setProperties, removeProperties); - setupRequest(request); - executeRequest(request); - } - } - - /** The character set property name. */ - public static final DavPropertyName RESPONSE_CHARSET = DavPropertyName.create("response-charset"); - - /** - * An empty immutable {@code Webdav4FileObject} array. - */ - private static final Webdav4FileObject[] EMPTY_ARRAY = {}; - - /** The FileSystemConfigBuilder */ - private final Webdav4FileSystemConfigBuilder builder; - - - protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem) - throws FileSystemException { - this(name, fileSystem, Webdav4FileSystemConfigBuilder.getInstance()); - } - - protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem, - final Webdav4FileSystemConfigBuilder builder) throws FileSystemException { - super(name, fileSystem, builder); - this.builder = builder; - } - - /** - * Creates this file as a folder. - */ - @Override - protected void doCreateFolder() throws Exception { - final HttpMkcol request = new HttpMkcol(toUrlString((GenericURLFileName) getName())); - setupRequest(request); - try { - executeRequest(request); - } catch (final FileSystemException fse) { - throw new FileSystemException("vfs.provider.webdav/create-collection.error", getName(), fse); - } - } - - /** - * Deletes the file. - */ - @Override - protected void doDelete() throws Exception { - final HttpDelete request = new HttpDelete(toUrlString((GenericURLFileName) getName())); - setupRequest(request); - executeRequest(request); - } - - /** - * Returns the properties of the Webdav resource. - */ - @Override - protected Map<String, Object> doGetAttributes() throws Exception { - final Map<String, Object> attributes = new HashMap<>(); - try { - final GenericURLFileName fileName = (GenericURLFileName) getName(); - DavPropertySet properties = getProperties(fileName, DavConstants.PROPFIND_ALL_PROP, - new DavPropertyNameSet(), false); - final DavPropertyIterator iter = properties.iterator(); - while (iter.hasNext()) { - final DavProperty<?> property = iter.nextProperty(); - attributes.put(property.getName().toString(), property.getValue()); - } - properties = getPropertyNames(fileName); - final DavPropertyIterator iter2 = properties.iterator(); - while (iter2.hasNext()) { - DavProperty<?> property = iter2.nextProperty(); - if (!attributes.containsKey(property.getName().getName())) { - property = getProperty(fileName, property.getName()); - if (property != null) { - final Object name = property.getName(); - final Object value = property.getValue(); - if (name != null && value != null) { - attributes.put(name.toString(), value); - } - } - } - } - return attributes; - } catch (final Exception e) { - throw new FileSystemException("vfs.provider.webdav/get-attributes.error", getName(), e); - } - } - - /** - * Returns the size of the file content (in bytes). - */ - @Override - protected long doGetContentSize() throws Exception { - final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETCONTENTLENGTH); - if (property != null) { - final 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}. - */ - @Override - protected long doGetLastModifiedTime() throws Exception { - final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETLASTMODIFIED); - if (property != null) { - final String value = (String) property.getValue(); - return DateUtils.parseDate(value).getTime(); - } - return 0; - } - - @Override - protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception { - return new WebdavOutputStream(this); - } - - /** - * Determines the type of this file. Must not return null. The return value of this method is cached, so the - * implementation can be expensive. - */ - @Override - protected FileType doGetType() throws Exception { - try { - return isDirectory((GenericURLFileName) getName()) ? FileType.FOLDER : FileType.FILE; - } catch (final FileNotFolderException | FileNotFoundException fnfe) { - return FileType.IMAGINARY; - } - - } - - /** - * Determines if this file can be written to. Is only called if {@link #doGetType} does not return - * {@link FileType#IMAGINARY}. - * <p> - * This implementation always returns true. - * - * @return true if the file is writable. - * @throws Exception if an error occurs. - */ - @Override - protected boolean doIsWriteable() throws Exception { - return true; - } - - /** - * Lists the children of the file. - */ - @Override - protected String[] doListChildren() throws Exception { - // use doListChildrenResolved for performance - return null; - } - - /** - * Lists the children of the file. - */ - @Override - protected FileObject[] doListChildrenResolved() throws Exception { - HttpPropfind request = null; - try { - final GenericURLFileName name = (GenericURLFileName) getName(); - if (isDirectory(name)) { - final DavPropertyNameSet nameSet = new DavPropertyNameSet(); - nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_DISPLAYNAME)); - - request = new HttpPropfind(toUrlString(name), nameSet, DavConstants.DEPTH_1); - - final HttpResponse res = executeRequest(request); - final List<Webdav4FileObject> vfs = new ArrayList<>(); - if (request.succeeded(res)) { - final MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(res).getResponses(); - - for (final MultiStatusResponse response : responses) { - if (isCurrentFile(response.getHref(), name)) { - continue; - } - final String resourceName = resourceName(response.getHref()); - if (!resourceName.isEmpty()) { - final Webdav4FileObject fo = (Webdav4FileObject) FileObjectUtils.getAbstractFileObject( - getFileSystem().resolveFile(getFileSystem().getFileSystemManager() - .resolveName(getName(), resourceName, NameScope.CHILD))); - vfs.add(fo); - } - } - } - return vfs.toArray(EMPTY_ARRAY); - } - throw new FileNotFolderException(getName()); - } catch (final FileNotFolderException fnfe) { - throw fnfe; - } catch (final DavException | IOException e) { - throw new FileSystemException(e.getMessage(), e); - } finally { - if (request != null) { - request.releaseConnection(); - } - } - } - - /** - * Rename the file. - */ - @Override - protected void doRename(final FileObject newFile) throws Exception { - final String url = URIUtils.encodePath(toUrlString((GenericURLFileName) getName())); - final String dest = toUrlString((GenericURLFileName) newFile.getName(), false); - final HttpMove request = new HttpMove(url, dest, false); - setupRequest(request); - executeRequest(request); - } - - /** - * Sets an attribute of this file. Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}. - */ - @Override - protected void doSetAttribute(final String attrName, final Object value) throws Exception { - try { - final GenericURLFileName fileName = (GenericURLFileName) getName(); - final String urlStr = toUrlString(fileName); - final DavPropertySet properties = new DavPropertySet(); - final DavPropertyNameSet propertyNameSet = new DavPropertyNameSet(); - final DavProperty<Object> property = new DefaultDavProperty<>(attrName, value, Namespace.EMPTY_NAMESPACE); - if (value != null) { - properties.add(property); - } else { - propertyNameSet.add(property.getName()); // remove property - } - - final HttpProppatch request = new HttpProppatch(urlStr, properties, propertyNameSet); - setupRequest(request); - final HttpResponse response = executeRequest(request); - if (!request.succeeded(response)) { - throw new FileSystemException("Property '" + attrName + "' could not be set."); - } - } catch (final FileSystemException fse) { - throw fse; - } catch (final Exception e) { - throw new FileSystemException("vfs.provider.webdav/set-attributes", e, getName(), attrName); - } - } - - private HttpResponse executeRequest(final HttpUriRequest request) throws FileSystemException { - final HttpResponse response; - - try { - response = executeHttpUriRequest(request); - final int status = response.getStatusLine().getStatusCode(); - if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) { - throw new FileNotFoundException(request.getURI()); - } - - if (request instanceof BaseDavRequest) { - ((BaseDavRequest) request).checkSuccess(response); - } - - return response; - } catch (final FileSystemException fse) { - throw fse; - } catch (final IOException e) { - throw new FileSystemException(e); - } catch (final DavException e) { - throw ExceptionConverter.generate(e); - } finally { - if (request instanceof HttpRequestBase) { - ((HttpRequestBase) request).releaseConnection(); - } - } - } - - @Override - protected FileContentInfoFactory getFileContentInfoFactory() { - return new Webdav4FileContentInfoFactory(); - } - - DavPropertySet getProperties(final GenericURLFileName name) throws FileSystemException { - return getProperties(name, DavConstants.PROPFIND_ALL_PROP, new DavPropertyNameSet(), false); - } - - DavPropertySet getProperties(final GenericURLFileName name, final DavPropertyNameSet nameSet, final boolean addEncoding) - throws FileSystemException { - return getProperties(name, DavConstants.PROPFIND_BY_PROPERTY, nameSet, addEncoding); - } - - DavPropertySet getProperties(final GenericURLFileName name, final int type, final DavPropertyNameSet nameSet, - final boolean addEncoding) throws FileSystemException { - try { - final String urlStr = toUrlString(name); - final HttpPropfind request = new HttpPropfind(urlStr, type, nameSet, DavConstants.DEPTH_0); - setupRequest(request); - final HttpResponse res = executeRequest(request); - if (request.succeeded(res)) { - final MultiStatus multiStatus = request.getResponseBodyAsMultiStatus(res); - final MultiStatusResponse response = multiStatus.getResponses()[0]; - final DavPropertySet props = response.getProperties(HttpStatus.SC_OK); - if (addEncoding) { - final ContentType resContentType = ContentType.getOrDefault(res.getEntity()); - final DavProperty<String> prop = new DefaultDavProperty<>(RESPONSE_CHARSET, - resContentType.getCharset().name()); - props.add(prop); - } - return props; - } - return new DavPropertySet(); - } catch (final FileSystemException fse) { - throw fse; - } catch (final Exception e) { - throw new FileSystemException("vfs.provider.webdav/get-property.error", e, getName(), name, type, - nameSet.getContent(), addEncoding); - } - } - - DavProperty<?> getProperty(final GenericURLFileName fileName, final DavPropertyName name) throws FileSystemException { - final DavPropertyNameSet nameSet = new DavPropertyNameSet(); - nameSet.add(name); - final DavPropertySet propertySet = getProperties(fileName, nameSet, false); - return propertySet.get(name); - } - - DavProperty<?> getProperty(final GenericURLFileName fileName, final String property) throws FileSystemException { - return getProperty(fileName, DavPropertyName.create(property)); - } - - DavPropertySet getPropertyNames(final GenericURLFileName name) throws FileSystemException { - return getProperties(name, DavConstants.PROPFIND_PROPERTY_NAMES, new DavPropertyNameSet(), false); - } - - /** - * Convert the FileName to an encoded url String. - * - * @param name The FileName. - * @return The encoded URL String. - */ - private String hrefString(final GenericURLFileName name) { - try { - final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(), - null, null, name.getPath(), name.getType(), name.getQueryString()); - return newFile.getURIEncoded(this.getUrlCharset()); - } catch (final Exception e) { - return name.getURI(); - } - } - - private boolean isCurrentFile(final String href, final GenericURLFileName fileName) { - String name = hrefString(fileName); - if (href.endsWith("/") && !name.endsWith("/")) { - name += "/"; - } - return href.equals(name) || href.equals(fileName.getPath()); - } - - private boolean isDirectory(final GenericURLFileName name) throws IOException { - try { - final DavProperty<?> property = getProperty(name, DavConstants.PROPERTY_RESOURCETYPE); - final Node node; - if (property != null && (node = (Node) property.getValue()) != null) { - return node.getLocalName().equals(DavConstants.XML_COLLECTION); - } - return false; - } catch (final FileNotFoundException fse) { - throw new FileNotFolderException(name); - } - } - - void log(final Exception ex) { - // TODO Consider logging - } - - /** - * 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 void setupRequest(final HttpUriRequest request) { - // NOTE: *FileSystemConfigBuilder takes care of redirect option and user agent. - request.addHeader("Cache-control", "no-cache"); - request.addHeader("Cache-store", "no-store"); - request.addHeader("Pragma", "no-cache"); - request.addHeader("Expires", "0"); - } - - /** - * Converts the given URLFileName to an encoded URL String to internally use in real DAV operations. - * - * @param name The FileName. - * @return The encoded URL String. - */ - String toUrlString(final GenericURLFileName name) { - return toUrlString(name, true); - } - - /** - * Converts the given URLFileName to an encoded URL String to internally use in real DAV operations. - * - * @param name The FileName. - * @param includeUserInfo true if user information should be included. - * @return The encoded URL String. - */ - private String toUrlString(final GenericURLFileName name, final boolean includeUserInfo) { - String user = null; - String password = null; - if (includeUserInfo) { - user = name.getUserName(); - password = name.getPassword(); - } - try { - final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(), - user, password, name.getPath(), name.getType(), name.getQueryString()); - return newFile.getURIEncoded(this.getUrlCharset()); - } catch (final Exception e) { - return name.getURI(); - } - } -} +/* + * 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.vfs2.provider.webdav4; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.vfs2.FileContentInfoFactory; +import org.apache.commons.vfs2.FileNotFolderException; +import org.apache.commons.vfs2.FileNotFoundException; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileType; +import org.apache.commons.vfs2.NameScope; +import org.apache.commons.vfs2.provider.AbstractFileName; +import org.apache.commons.vfs2.provider.DefaultFileContent; +import org.apache.commons.vfs2.provider.GenericURLFileName; +import org.apache.commons.vfs2.provider.http4.Http4FileObject; +import org.apache.commons.vfs2.util.FileObjectUtils; +import org.apache.commons.vfs2.util.MonitorOutputStream; +import org.apache.commons.vfs2.util.URIUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.utils.DateUtils; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.jackrabbit.webdav.DavConstants; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.MultiStatus; +import org.apache.jackrabbit.webdav.MultiStatusResponse; +import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest; +import org.apache.jackrabbit.webdav.client.methods.HttpCheckin; +import org.apache.jackrabbit.webdav.client.methods.HttpCheckout; +import org.apache.jackrabbit.webdav.client.methods.HttpDelete; +import org.apache.jackrabbit.webdav.client.methods.HttpMkcol; +import org.apache.jackrabbit.webdav.client.methods.HttpMove; +import org.apache.jackrabbit.webdav.client.methods.HttpPropfind; +import org.apache.jackrabbit.webdav.client.methods.HttpProppatch; +import org.apache.jackrabbit.webdav.client.methods.HttpVersionControl; +import org.apache.jackrabbit.webdav.property.DavProperty; +import org.apache.jackrabbit.webdav.property.DavPropertyName; +import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; +import org.apache.jackrabbit.webdav.property.DavPropertySet; +import org.apache.jackrabbit.webdav.property.DefaultDavProperty; +import org.apache.jackrabbit.webdav.version.DeltaVConstants; +import org.apache.jackrabbit.webdav.version.VersionControlledResource; +import org.apache.jackrabbit.webdav.xml.Namespace; +import org.w3c.dom.Node; + +/** + * A WebDAV file. + * + * @since 2.5.0 + */ +public class Webdav4FileObject extends Http4FileObject<Webdav4FileSystem> { + + /** + * An OutputStream that writes to a Webdav resource. + * <p> + * TODO - Use piped stream to avoid temporary file. + */ + private class WebdavOutputStream extends MonitorOutputStream { + private final Webdav4FileObject file; + + WebdavOutputStream(final Webdav4FileObject file) { + super(new ByteArrayOutputStream()); + this.file = file; + } + + private boolean createVersion(final String urlStr) { + try { + final HttpVersionControl request = new HttpVersionControl(urlStr); + setupRequest(request); + executeRequest(request); + return true; + } catch (final Exception ex) { + return false; + } + } + + /** + * Called after this stream is closed. + */ + @Override + protected void onClose() throws IOException { + final HttpEntity entity = new ByteArrayEntity(((ByteArrayOutputStream) out).toByteArray()); + final GenericURLFileName fileName = (GenericURLFileName) getName(); + final String urlStr = toUrlString(fileName); + if (builder.isVersioning(getFileSystem().getFileSystemOptions())) { + DavPropertySet set = null; + boolean fileExists = true; + boolean isCheckedIn = true; + try { + set = getPropertyNames(fileName); + } catch (final 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 { + final HttpCheckout request = new HttpCheckout(urlStr); + setupRequest(request); + executeRequest(request); + isCheckedIn = false; + } catch (final FileSystemException ex) { + log(ex); + } + } + + try { + final HttpPut request = new HttpPut(urlStr); + request.setEntity(entity); + setupRequest(request); + executeRequest(request); + setUserName(fileName, urlStr); + } catch (final FileSystemException ex) { + if (!isCheckedIn) { + try { + final HttpCheckin request = new HttpCheckin(urlStr); + setupRequest(request); + executeRequest(request); + isCheckedIn = true; + } catch (final Exception e) { + // Going to throw original. + log(e); + } + throw ex; + } + } + if (!fileExists) { + createVersion(urlStr); + try { + final DavPropertySet props = getPropertyNames(fileName); + isCheckedIn = !props.contains(VersionControlledResource.CHECKED_OUT); + } catch (final FileNotFoundException fnfe) { + log(fnfe); + } + } + if (!isCheckedIn) { + final HttpCheckin request = new HttpCheckin(urlStr); + setupRequest(request); + executeRequest(request); + } + } else { + final HttpPut request = new HttpPut(urlStr); + request.setEntity(entity); + setupRequest(request); + executeRequest(request); + try { + setUserName(fileName, urlStr); + } catch (final IOException e) { + // Unable to set the user name. + log(e); + } + } + ((DefaultFileContent) this.file.getContent()).resetAttributes(); + } + + private void setUserName(final GenericURLFileName fileName, final String urlStr) throws IOException { + final DavPropertySet setProperties = new DavPropertySet(); + final DavPropertyNameSet removeProperties = new DavPropertyNameSet(); + String name = builder.getCreatorName(getFileSystem().getFileSystemOptions()); + final String userName = fileName.getUserName(); + if (name == null) { + name = userName; + } else if (userName != null) { + final String comment = "Modified by user " + userName; + setProperties.add(new DefaultDavProperty<>(DeltaVConstants.COMMENT, comment)); + } + setProperties.add(new DefaultDavProperty<>(DeltaVConstants.CREATOR_DISPLAYNAME, name)); + final HttpProppatch request = new HttpProppatch(urlStr, setProperties, removeProperties); + setupRequest(request); + executeRequest(request); + } + } + + /** The character set property name. */ + public static final DavPropertyName RESPONSE_CHARSET = DavPropertyName.create("response-charset"); + + /** + * An empty immutable {@code Webdav4FileObject} array. + */ + private static final Webdav4FileObject[] EMPTY_ARRAY = {}; + + /** The FileSystemConfigBuilder */ + private final Webdav4FileSystemConfigBuilder builder; + + + protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem) + throws FileSystemException { + this(name, fileSystem, Webdav4FileSystemConfigBuilder.getInstance()); + } + + protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem, + final Webdav4FileSystemConfigBuilder builder) throws FileSystemException { + super(name, fileSystem, builder); + this.builder = builder; + } + + /** + * Creates this file as a folder. + */ + @Override + protected void doCreateFolder() throws Exception { + final HttpMkcol request = new HttpMkcol(toUrlString((GenericURLFileName) getName())); + setupRequest(request); + try { + executeRequest(request); + } catch (final FileSystemException fse) { + throw new FileSystemException("vfs.provider.webdav/create-collection.error", getName(), fse); + } + } + + /** + * Deletes the file. + */ + @Override + protected void doDelete() throws Exception { + final HttpDelete request = new HttpDelete(toUrlString((GenericURLFileName) getName())); + setupRequest(request); + executeRequest(request); + } + + /** + * Returns the properties of the Webdav resource. + */ + @Override + protected Map<String, Object> doGetAttributes() throws Exception { + final Map<String, Object> attributes = new HashMap<>(); + try { + final GenericURLFileName fileName = (GenericURLFileName) getName(); + DavPropertySet properties = getProperties(fileName, DavConstants.PROPFIND_ALL_PROP, + new DavPropertyNameSet(), false); + for (final DavProperty<?> property : properties) { + attributes.put(property.getName().toString(), property.getValue()); + } + properties = getPropertyNames(fileName); + for (DavProperty<?> property : properties) { + if (!attributes.containsKey(property.getName().getName())) { + property = getProperty(fileName, property.getName()); + if (property != null) { + final Object name = property.getName(); + final Object value = property.getValue(); + if (name != null && value != null) { + attributes.put(name.toString(), value); + } + } + } + } + return attributes; + } catch (final Exception e) { + throw new FileSystemException("vfs.provider.webdav/get-attributes.error", getName(), e); + } + } + + /** + * Returns the size of the file content (in bytes). + */ + @Override + protected long doGetContentSize() throws Exception { + final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETCONTENTLENGTH); + if (property != null) { + final 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}. + */ + @Override + protected long doGetLastModifiedTime() throws Exception { + final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETLASTMODIFIED); + if (property != null) { + final String value = (String) property.getValue(); + return DateUtils.parseDate(value).getTime(); + } + return 0; + } + + @Override + protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception { + return new WebdavOutputStream(this); + } + + /** + * Determines the type of this file. Must not return null. The return value of this method is cached, so the + * implementation can be expensive. + */ + @Override + protected FileType doGetType() throws Exception { + try { + return isDirectory((GenericURLFileName) getName()) ? FileType.FOLDER : FileType.FILE; + } catch (final FileNotFolderException | FileNotFoundException fnfe) { + return FileType.IMAGINARY; + } + + } + + /** + * Determines if this file can be written to. Is only called if {@link #doGetType} does not return + * {@link FileType#IMAGINARY}. + * <p> + * This implementation always returns true. + * + * @return true if the file is writable. + * @throws Exception if an error occurs. + */ + @Override + protected boolean doIsWriteable() throws Exception { + return true; + } + + /** + * Lists the children of the file. + */ + @Override + protected String[] doListChildren() throws Exception { + // use doListChildrenResolved for performance + return null; + } + + /** + * Lists the children of the file. + */ + @Override + protected FileObject[] doListChildrenResolved() throws Exception { + HttpPropfind request = null; + try { + final GenericURLFileName name = (GenericURLFileName) getName(); + if (isDirectory(name)) { + final DavPropertyNameSet nameSet = new DavPropertyNameSet(); + nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_DISPLAYNAME)); + + request = new HttpPropfind(toUrlString(name), nameSet, DavConstants.DEPTH_1); + + final HttpResponse res = executeRequest(request); + final List<Webdav4FileObject> vfs = new ArrayList<>(); + if (request.succeeded(res)) { + final MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(res).getResponses(); + + for (final MultiStatusResponse response : responses) { + if (isCurrentFile(response.getHref(), name)) { + continue; + } + final String resourceName = resourceName(response.getHref()); + if (!resourceName.isEmpty()) { + final Webdav4FileObject fo = (Webdav4FileObject) FileObjectUtils.getAbstractFileObject( + getFileSystem().resolveFile(getFileSystem().getFileSystemManager() + .resolveName(getName(), resourceName, NameScope.CHILD))); + vfs.add(fo); + } + } + } + return vfs.toArray(EMPTY_ARRAY); + } + throw new FileNotFolderException(getName()); + } catch (final FileNotFolderException fnfe) { + throw fnfe; + } catch (final DavException | IOException e) { + throw new FileSystemException(e.getMessage(), e); + } finally { + if (request != null) { + request.releaseConnection(); + } + } + } + + /** + * Rename the file. + */ + @Override + protected void doRename(final FileObject newFile) throws Exception { + final String url = URIUtils.encodePath(toUrlString((GenericURLFileName) getName())); + final String dest = toUrlString((GenericURLFileName) newFile.getName(), false); + final HttpMove request = new HttpMove(url, dest, false); + setupRequest(request); + executeRequest(request); + } + + /** + * Sets an attribute of this file. Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}. + */ + @Override + protected void doSetAttribute(final String attrName, final Object value) throws Exception { + try { + final GenericURLFileName fileName = (GenericURLFileName) getName(); + final String urlStr = toUrlString(fileName); + final DavPropertySet properties = new DavPropertySet(); + final DavPropertyNameSet propertyNameSet = new DavPropertyNameSet(); + final DavProperty<Object> property = new DefaultDavProperty<>(attrName, value, Namespace.EMPTY_NAMESPACE); + if (value != null) { + properties.add(property); + } else { + propertyNameSet.add(property.getName()); // remove property + } + + final HttpProppatch request = new HttpProppatch(urlStr, properties, propertyNameSet); + setupRequest(request); + final HttpResponse response = executeRequest(request); + if (!request.succeeded(response)) { + throw new FileSystemException("Property '" + attrName + "' could not be set."); + } + } catch (final FileSystemException fse) { + throw fse; + } catch (final Exception e) { + throw new FileSystemException("vfs.provider.webdav/set-attributes", e, getName(), attrName); + } + } + + private HttpResponse executeRequest(final HttpUriRequest request) throws FileSystemException { + final HttpResponse response; + + try { + response = executeHttpUriRequest(request); + final int status = response.getStatusLine().getStatusCode(); + if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) { + throw new FileNotFoundException(request.getURI()); + } + + if (request instanceof BaseDavRequest) { + ((BaseDavRequest) request).checkSuccess(response); + } + + return response; + } catch (final FileSystemException fse) { + throw fse; + } catch (final IOException e) { + throw new FileSystemException(e); + } catch (final DavException e) { + throw ExceptionConverter.generate(e); + } finally { + if (request instanceof HttpRequestBase) { + ((HttpRequestBase) request).releaseConnection(); + } + } + } + + @Override + protected FileContentInfoFactory getFileContentInfoFactory() { + return new Webdav4FileContentInfoFactory(); + } + + DavPropertySet getProperties(final GenericURLFileName name) throws FileSystemException { + return getProperties(name, DavConstants.PROPFIND_ALL_PROP, new DavPropertyNameSet(), false); + } + + DavPropertySet getProperties(final GenericURLFileName name, final DavPropertyNameSet nameSet, final boolean addEncoding) + throws FileSystemException { + return getProperties(name, DavConstants.PROPFIND_BY_PROPERTY, nameSet, addEncoding); + } + + DavPropertySet getProperties(final GenericURLFileName name, final int type, final DavPropertyNameSet nameSet, + final boolean addEncoding) throws FileSystemException { + try { + final String urlStr = toUrlString(name); + final HttpPropfind request = new HttpPropfind(urlStr, type, nameSet, DavConstants.DEPTH_0); + setupRequest(request); + final HttpResponse res = executeRequest(request); + if (request.succeeded(res)) { + final MultiStatus multiStatus = request.getResponseBodyAsMultiStatus(res); + final MultiStatusResponse response = multiStatus.getResponses()[0]; + final DavPropertySet props = response.getProperties(HttpStatus.SC_OK); + if (addEncoding) { + final ContentType resContentType = ContentType.getOrDefault(res.getEntity()); + final DavProperty<String> prop = new DefaultDavProperty<>(RESPONSE_CHARSET, + resContentType.getCharset().name()); + props.add(prop); + } + return props; + } + return new DavPropertySet(); + } catch (final FileSystemException fse) { + throw fse; + } catch (final Exception e) { + throw new FileSystemException("vfs.provider.webdav/get-property.error", e, getName(), name, type, + nameSet.getContent(), addEncoding); + } + } + + DavProperty<?> getProperty(final GenericURLFileName fileName, final DavPropertyName name) throws FileSystemException { + final DavPropertyNameSet nameSet = new DavPropertyNameSet(); + nameSet.add(name); + final DavPropertySet propertySet = getProperties(fileName, nameSet, false); + return propertySet.get(name); + } + + DavProperty<?> getProperty(final GenericURLFileName fileName, final String property) throws FileSystemException { + return getProperty(fileName, DavPropertyName.create(property)); + } + + DavPropertySet getPropertyNames(final GenericURLFileName name) throws FileSystemException { + return getProperties(name, DavConstants.PROPFIND_PROPERTY_NAMES, new DavPropertyNameSet(), false); + } + + /** + * Convert the FileName to an encoded url String. + * + * @param name The FileName. + * @return The encoded URL String. + */ + private String hrefString(final GenericURLFileName name) { + try { + final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(), + null, null, name.getPath(), name.getType(), name.getQueryString()); + return newFile.getURIEncoded(this.getUrlCharset()); + } catch (final Exception e) { + return name.getURI(); + } + } + + private boolean isCurrentFile(final String href, final GenericURLFileName fileName) { + String name = hrefString(fileName); + if (href.endsWith("/") && !name.endsWith("/")) { + name += "/"; + } + return href.equals(name) || href.equals(fileName.getPath()); + } + + private boolean isDirectory(final GenericURLFileName name) throws IOException { + try { + final DavProperty<?> property = getProperty(name, DavConstants.PROPERTY_RESOURCETYPE); + final Node node; + if (property != null && (node = (Node) property.getValue()) != null) { + return node.getLocalName().equals(DavConstants.XML_COLLECTION); + } + return false; + } catch (final FileNotFoundException fse) { + throw new FileNotFolderException(name); + } + } + + void log(final Exception ex) { + // TODO Consider logging + } + + /** + * 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 void setupRequest(final HttpUriRequest request) { + // NOTE: *FileSystemConfigBuilder takes care of redirect option and user agent. + request.addHeader("Cache-control", "no-cache"); + request.addHeader("Cache-store", "no-store"); + request.addHeader("Pragma", "no-cache"); + request.addHeader("Expires", "0"); + } + + /** + * Converts the given URLFileName to an encoded URL String to internally use in real DAV operations. + * + * @param name The FileName. + * @return The encoded URL String. + */ + String toUrlString(final GenericURLFileName name) { + return toUrlString(name, true); + } + + /** + * Converts the given URLFileName to an encoded URL String to internally use in real DAV operations. + * + * @param name The FileName. + * @param includeUserInfo true if user information should be included. + * @return The encoded URL String. + */ + private String toUrlString(final GenericURLFileName name, final boolean includeUserInfo) { + String user = null; + String password = null; + if (includeUserInfo) { + user = name.getUserName(); + password = name.getPassword(); + } + try { + final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(), + user, password, name.getPath(), name.getType(), name.getQueryString()); + return newFile.getURIEncoded(this.getUrlCharset()); + } catch (final Exception e) { + return name.getURI(); + } + } +}