You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/08/16 20:15:43 UTC
[commons-vfs] 03/03: [VFS-726]getInputStream(int bufferSize) on
SftpFileObject effectively ig nores buffer size.
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
commit 953aa407df78f0d10da54e29323d404cda3e4f6b
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Aug 16 13:15:34 2019 -0700
[VFS-726]getInputStream(int bufferSize) on SftpFileObject effectively ig
nores buffer size.
---
.../commons/vfs2/provider/mime/MimeFileObject.java | 520 +--
.../commons/vfs2/provider/smb/SmbFileObject.java | 450 +--
.../commons/vfs2/provider/AbstractFileObject.java | 3759 ++++++++++----------
.../commons/vfs2/provider/DefaultFileContent.java | 1625 +++++----
.../commons/vfs2/provider/DelegateFileObject.java | 830 ++---
.../vfs2/provider/bzip2/Bzip2FileObject.java | 4 +-
.../vfs2/provider/ftp/FTPClientWrapper.java | 19 +
.../commons/vfs2/provider/ftp/FtpClient.java | 9 +
.../commons/vfs2/provider/ftp/FtpFileObject.java | 1259 +++----
.../commons/vfs2/provider/gzip/GzipFileObject.java | 4 +-
.../commons/vfs2/provider/hdfs/HdfsFileObject.java | 522 +--
.../commons/vfs2/provider/http/HttpFileObject.java | 499 +--
.../vfs2/provider/http4/Http4FileObject.java | 460 +--
.../MonitoredHttpResponseContentInputStream.java | 93 +-
.../commons/vfs2/provider/local/LocalFile.java | 2 +-
.../commons/vfs2/provider/ram/RamFileObject.java | 508 +--
.../commons/vfs2/provider/sftp/SftpFileObject.java | 1042 +++---
.../commons/vfs2/provider/tar/TarFileObject.java | 2 +-
.../commons/vfs2/provider/url/UrlFileObject.java | 284 +-
.../commons/vfs2/provider/zip/ZipFileObject.java | 324 +-
src/changes/changes.xml | 3 +
21 files changed, 6149 insertions(+), 6069 deletions(-)
diff --git a/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/mime/MimeFileObject.java b/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/mime/MimeFileObject.java
index 8a91e72..64314b7 100644
--- a/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/mime/MimeFileObject.java
+++ b/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/mime/MimeFileObject.java
@@ -1,260 +1,260 @@
-/*
- * 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.mime;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-
-import javax.mail.Header;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.Multipart;
-import javax.mail.Part;
-import javax.mail.internet.MimeMultipart;
-
-import org.apache.commons.vfs2.FileContentInfoFactory;
-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.AbstractFileObject;
-import org.apache.commons.vfs2.provider.UriParser;
-import org.apache.commons.vfs2.util.FileObjectUtils;
-
-/**
- * A part of a MIME message.
- */
-public class MimeFileObject extends AbstractFileObject<MimeFileSystem> implements FileObject {
- private Part part;
- private Map<String, Object> attributeMap;
-
- protected MimeFileObject(final AbstractFileName name, final Part part, final MimeFileSystem fileSystem)
- throws FileSystemException {
- super(name, fileSystem);
- setPart(part);
- }
-
- /**
- * Attaches this file object to its file resource.
- */
- @Override
- protected void doAttach() throws Exception {
- if (part == null) {
- if (!getName().equals(getFileSystem().getRootName())) {
- final MimeFileObject foParent = (MimeFileObject) FileObjectUtils.getAbstractFileObject(getParent());
- setPart(foParent.findPart(getName().getBaseName()));
- return;
- }
-
- setPart(((MimeFileSystem) getFileSystem()).createCommunicationLink());
- }
- }
-
- private Part findPart(final String partName) throws Exception {
- if (getType() == FileType.IMAGINARY) {
- // not existent
- return null;
- }
-
- if (isMultipart()) {
- final Multipart multipart = (Multipart) part.getContent();
- if (partName.startsWith(MimeFileSystem.NULL_BP_NAME)) {
- final int partNumber = Integer.parseInt(partName.substring(MimeFileSystem.NULL_BP_NAME.length()), 10);
- if (partNumber < 0 || partNumber + 1 > multipart.getCount()) {
- // non existent
- return null;
- }
-
- return multipart.getBodyPart(partNumber);
- }
-
- for (int i = 0; i < multipart.getCount(); i++) {
- final Part childPart = multipart.getBodyPart(i);
- if (partName.equals(childPart.getFileName())) {
- return childPart;
- }
- }
- }
-
- return null;
- }
-
- @Override
- protected void doDetach() throws Exception {
- }
-
- /**
- * Determines the type of the file, returns null if the file does not exist.
- */
- @Override
- protected FileType doGetType() throws Exception {
- if (part == null) {
- return FileType.IMAGINARY;
- }
-
- if (isMultipart()) {
- // we cant have children ...
- return FileType.FILE_OR_FOLDER;
- }
-
- return FileType.FILE;
- }
-
- @Override
- protected String[] doListChildren() throws Exception {
- return null;
- }
-
- /**
- * Lists the children of the file. Is only called if {@link #doGetType} returns
- * {@link org.apache.commons.vfs2.FileType#FOLDER}.
- */
- @Override
- protected FileObject[] doListChildrenResolved() throws Exception {
- if (part == null) {
- return null;
- }
-
- final List<MimeFileObject> vfs = new ArrayList<>();
- if (isMultipart()) {
- final Object container = part.getContent();
- if (container instanceof Multipart) {
- final Multipart multipart = (Multipart) container;
-
- for (int i = 0; i < multipart.getCount(); i++) {
- final Part part = multipart.getBodyPart(i);
-
- String filename = UriParser.encode(part.getFileName());
- if (filename == null) {
- filename = MimeFileSystem.NULL_BP_NAME + i;
- }
-
- final MimeFileObject fo = (MimeFileObject) FileObjectUtils
- .getAbstractFileObject(getFileSystem().resolveFile(getFileSystem().getFileSystemManager()
- .resolveName(getName(), filename, NameScope.CHILD)));
- fo.setPart(part);
- vfs.add(fo);
- }
- }
- }
-
- return vfs.toArray(new MimeFileObject[vfs.size()]);
- }
-
- private void setPart(final Part part) {
- this.part = part;
- this.attributeMap = null;
- }
-
- /**
- * Returns the size of the file content (in bytes).
- */
- @Override
- protected long doGetContentSize() throws Exception {
- return part.getSize();
- }
-
- /**
- * Returns the last modified time of this file.
- */
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- final Message mm = getMessage();
- if (mm == null) {
- return -1;
- }
- if (mm.getSentDate() != null) {
- return mm.getSentDate().getTime();
- }
- if (mm.getReceivedDate() != null) {
- mm.getReceivedDate();
- }
- return 0;
- }
-
- private Message getMessage() throws FileSystemException {
- if (part instanceof Message) {
- return (Message) part;
- }
-
- return ((MimeFileObject) FileObjectUtils.getAbstractFileObject(getParent())).getMessage();
- }
-
- /**
- * Creates an input stream to read the file content from.
- */
- @Override
- protected InputStream doGetInputStream() throws Exception {
- if (isMultipart()) {
- // deliver the preamble as the only content
-
- final String preamble = ((MimeMultipart) part.getContent()).getPreamble();
- if (preamble == null) {
- return new ByteArrayInputStream(new byte[] {});
- }
- return new ByteArrayInputStream(preamble.getBytes(MimeFileSystem.PREAMBLE_CHARSET));
- }
-
- return part.getInputStream();
- }
-
- boolean isMultipart() throws MessagingException {
- return part.getContentType() != null && part.getContentType().startsWith("multipart/");
- }
-
- @Override
- protected FileContentInfoFactory getFileContentInfoFactory() {
- return new MimeFileContentInfoFactory();
- }
-
- protected Part getPart() {
- return part;
- }
-
- /**
- * Returns all headers of this part.
- * <p>
- * The map key is a java.lang.String and the value is a:
- * <ul>
- * <li>{@code java.lang.Strings} for single entries or a</li>
- * <li>{@code java.utils.List<java.lang.Strings>} for entries with multiple values</li>
- * </ul>
- */
- @Override
- protected Map<String, Object> doGetAttributes() throws Exception {
- if (attributeMap == null) {
- if (part != null) {
- attributeMap = new MimeAttributesMap(part);
- } else {
- attributeMap = Collections.emptyMap();
- }
- }
-
- return attributeMap;
- }
-
- @SuppressWarnings("unchecked") // Javadoc says Part returns Header
- protected Enumeration<Header> getAllHeaders() throws MessagingException {
- return part.getAllHeaders();
- }
-}
+/*
+ * 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.mime;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import javax.mail.Header;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Part;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.commons.vfs2.FileContentInfoFactory;
+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.AbstractFileObject;
+import org.apache.commons.vfs2.provider.UriParser;
+import org.apache.commons.vfs2.util.FileObjectUtils;
+
+/**
+ * A part of a MIME message.
+ */
+public class MimeFileObject extends AbstractFileObject<MimeFileSystem> implements FileObject {
+ private Part part;
+ private Map<String, Object> attributeMap;
+
+ protected MimeFileObject(final AbstractFileName name, final Part part, final MimeFileSystem fileSystem)
+ throws FileSystemException {
+ super(name, fileSystem);
+ setPart(part);
+ }
+
+ /**
+ * Attaches this file object to its file resource.
+ */
+ @Override
+ protected void doAttach() throws Exception {
+ if (part == null) {
+ if (!getName().equals(getFileSystem().getRootName())) {
+ final MimeFileObject foParent = (MimeFileObject) FileObjectUtils.getAbstractFileObject(getParent());
+ setPart(foParent.findPart(getName().getBaseName()));
+ return;
+ }
+
+ setPart(((MimeFileSystem) getFileSystem()).createCommunicationLink());
+ }
+ }
+
+ private Part findPart(final String partName) throws Exception {
+ if (getType() == FileType.IMAGINARY) {
+ // not existent
+ return null;
+ }
+
+ if (isMultipart()) {
+ final Multipart multipart = (Multipart) part.getContent();
+ if (partName.startsWith(MimeFileSystem.NULL_BP_NAME)) {
+ final int partNumber = Integer.parseInt(partName.substring(MimeFileSystem.NULL_BP_NAME.length()), 10);
+ if (partNumber < 0 || partNumber + 1 > multipart.getCount()) {
+ // non existent
+ return null;
+ }
+
+ return multipart.getBodyPart(partNumber);
+ }
+
+ for (int i = 0; i < multipart.getCount(); i++) {
+ final Part childPart = multipart.getBodyPart(i);
+ if (partName.equals(childPart.getFileName())) {
+ return childPart;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void doDetach() throws Exception {
+ }
+
+ /**
+ * Determines the type of the file, returns null if the file does not exist.
+ */
+ @Override
+ protected FileType doGetType() throws Exception {
+ if (part == null) {
+ return FileType.IMAGINARY;
+ }
+
+ if (isMultipart()) {
+ // we cant have children ...
+ return FileType.FILE_OR_FOLDER;
+ }
+
+ return FileType.FILE;
+ }
+
+ @Override
+ protected String[] doListChildren() throws Exception {
+ return null;
+ }
+
+ /**
+ * Lists the children of the file. Is only called if {@link #doGetType} returns
+ * {@link org.apache.commons.vfs2.FileType#FOLDER}.
+ */
+ @Override
+ protected FileObject[] doListChildrenResolved() throws Exception {
+ if (part == null) {
+ return null;
+ }
+
+ final List<MimeFileObject> vfs = new ArrayList<>();
+ if (isMultipart()) {
+ final Object container = part.getContent();
+ if (container instanceof Multipart) {
+ final Multipart multipart = (Multipart) container;
+
+ for (int i = 0; i < multipart.getCount(); i++) {
+ final Part part = multipart.getBodyPart(i);
+
+ String filename = UriParser.encode(part.getFileName());
+ if (filename == null) {
+ filename = MimeFileSystem.NULL_BP_NAME + i;
+ }
+
+ final MimeFileObject fo = (MimeFileObject) FileObjectUtils
+ .getAbstractFileObject(getFileSystem().resolveFile(getFileSystem().getFileSystemManager()
+ .resolveName(getName(), filename, NameScope.CHILD)));
+ fo.setPart(part);
+ vfs.add(fo);
+ }
+ }
+ }
+
+ return vfs.toArray(new MimeFileObject[vfs.size()]);
+ }
+
+ private void setPart(final Part part) {
+ this.part = part;
+ this.attributeMap = null;
+ }
+
+ /**
+ * Returns the size of the file content (in bytes).
+ */
+ @Override
+ protected long doGetContentSize() throws Exception {
+ return part.getSize();
+ }
+
+ /**
+ * Returns the last modified time of this file.
+ */
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ final Message mm = getMessage();
+ if (mm == null) {
+ return -1;
+ }
+ if (mm.getSentDate() != null) {
+ return mm.getSentDate().getTime();
+ }
+ if (mm.getReceivedDate() != null) {
+ mm.getReceivedDate();
+ }
+ return 0;
+ }
+
+ private Message getMessage() throws FileSystemException {
+ if (part instanceof Message) {
+ return (Message) part;
+ }
+
+ return ((MimeFileObject) FileObjectUtils.getAbstractFileObject(getParent())).getMessage();
+ }
+
+ /**
+ * Creates an input stream to read the file content from.
+ */
+ @Override
+ protected InputStream doGetInputStream(int bufferSize) throws Exception {
+ if (isMultipart()) {
+ // deliver the preamble as the only content
+
+ final String preamble = ((MimeMultipart) part.getContent()).getPreamble();
+ if (preamble == null) {
+ return new ByteArrayInputStream(new byte[] {});
+ }
+ return new ByteArrayInputStream(preamble.getBytes(MimeFileSystem.PREAMBLE_CHARSET));
+ }
+
+ return part.getInputStream();
+ }
+
+ boolean isMultipart() throws MessagingException {
+ return part.getContentType() != null && part.getContentType().startsWith("multipart/");
+ }
+
+ @Override
+ protected FileContentInfoFactory getFileContentInfoFactory() {
+ return new MimeFileContentInfoFactory();
+ }
+
+ protected Part getPart() {
+ return part;
+ }
+
+ /**
+ * Returns all headers of this part.
+ * <p>
+ * The map key is a java.lang.String and the value is a:
+ * <ul>
+ * <li>{@code java.lang.Strings} for single entries or a</li>
+ * <li>{@code java.utils.List<java.lang.Strings>} for entries with multiple values</li>
+ * </ul>
+ */
+ @Override
+ protected Map<String, Object> doGetAttributes() throws Exception {
+ if (attributeMap == null) {
+ if (part != null) {
+ attributeMap = new MimeAttributesMap(part);
+ } else {
+ attributeMap = Collections.emptyMap();
+ }
+ }
+
+ return attributeMap;
+ }
+
+ @SuppressWarnings("unchecked") // Javadoc says Part returns Header
+ protected Enumeration<Header> getAllHeaders() throws MessagingException {
+ return part.getAllHeaders();
+ }
+}
diff --git a/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/smb/SmbFileObject.java b/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/smb/SmbFileObject.java
index 7a9dc40..88d488f 100644
--- a/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/smb/SmbFileObject.java
+++ b/commons-vfs2-sandbox/src/main/java/org/apache/commons/vfs2/provider/smb/SmbFileObject.java
@@ -1,225 +1,225 @@
-/*
- * 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.smb;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.MalformedURLException;
-
-import jcifs.smb.NtlmPasswordAuthentication;
-import jcifs.smb.SmbException;
-import jcifs.smb.SmbFile;
-import jcifs.smb.SmbFileInputStream;
-import jcifs.smb.SmbFileOutputStream;
-
-import org.apache.commons.vfs2.FileName;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.FileTypeHasNoContentException;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.UserAuthenticationData;
-import org.apache.commons.vfs2.provider.AbstractFileName;
-import org.apache.commons.vfs2.provider.AbstractFileObject;
-import org.apache.commons.vfs2.provider.UriParser;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
-
-/**
- * A file in an SMB file system.
- */
-public class SmbFileObject extends AbstractFileObject<SmbFileSystem> {
- // private final String fileName;
- private SmbFile file;
-
- protected SmbFileObject(final AbstractFileName name, final SmbFileSystem fileSystem) throws FileSystemException {
- super(name, fileSystem);
- // this.fileName = UriParser.decode(name.getURI());
- }
-
- /**
- * Attaches this file object to its file resource.
- */
- @Override
- protected void doAttach() throws Exception {
- // Defer creation of the SmbFile to here
- if (file == null) {
- file = createSmbFile(getName());
- }
- }
-
- @Override
- protected void doDetach() throws Exception {
- // file closed through content-streams
- file = null;
- }
-
- private SmbFile createSmbFile(final FileName fileName)
- throws MalformedURLException, SmbException, FileSystemException {
- final SmbFileName smbFileName = (SmbFileName) fileName;
-
- final String path = smbFileName.getUriWithoutAuth();
-
- UserAuthenticationData authData = null;
- SmbFile file;
- try {
- authData = UserAuthenticatorUtils.authenticate(getFileSystem().getFileSystemOptions(),
- SmbFileProvider.AUTHENTICATOR_TYPES);
-
- NtlmPasswordAuthentication auth = null;
- if (authData != null) {
- auth = new NtlmPasswordAuthentication(
- UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData,
- UserAuthenticationData.DOMAIN, UserAuthenticatorUtils.toChar(smbFileName.getDomain()))),
- UserAuthenticatorUtils
- .toString(UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME,
- UserAuthenticatorUtils.toChar(smbFileName.getUserName()))),
- UserAuthenticatorUtils
- .toString(UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD,
- UserAuthenticatorUtils.toChar(smbFileName.getPassword()))));
- }
-
- // if auth == null SmbFile uses default credentials
- // ("jcifs.smb.client.domain", "?"), ("jcifs.smb.client.username", "GUEST"),
- // ("jcifs.smb.client.password", BLANK);
- // ANONYMOUS=("","","")
- file = new SmbFile(path, auth);
-
- if (file.isDirectory() && !file.toString().endsWith("/")) {
- file = new SmbFile(path + "/", auth);
- }
- return file;
- } finally {
- UserAuthenticatorUtils.cleanup(authData); // might be null
- }
- }
-
- /**
- * Determines the type of the file, returns null if the file does not exist.
- */
- @Override
- protected FileType doGetType() throws Exception {
- if (!file.exists()) {
- return FileType.IMAGINARY;
- } else if (file.isDirectory()) {
- return FileType.FOLDER;
- } else if (file.isFile()) {
- return FileType.FILE;
- }
-
- throw new FileSystemException("vfs.provider.smb/get-type.error", getName());
- }
-
- /**
- * Lists the children of the file. Is only called if {@link #doGetType} returns {@link FileType#FOLDER}.
- */
- @Override
- protected String[] doListChildren() throws Exception {
- // VFS-210: do not try to get listing for anything else than directories
- if (!file.isDirectory()) {
- return null;
- }
-
- return UriParser.encode(file.list());
- }
-
- /**
- * Determines if this file is hidden.
- */
- @Override
- protected boolean doIsHidden() throws Exception {
- return file.isHidden();
- }
-
- /**
- * Deletes the file.
- */
- @Override
- protected void doDelete() throws Exception {
- file.delete();
- }
-
- @Override
- protected void doRename(final FileObject newfile) throws Exception {
- file.renameTo(createSmbFile(newfile.getName()));
- }
-
- /**
- * Creates this file as a folder.
- */
- @Override
- protected void doCreateFolder() throws Exception {
- file.mkdir();
- file = createSmbFile(getName());
- }
-
- /**
- * Returns the size of the file content (in bytes).
- */
- @Override
- protected long doGetContentSize() throws Exception {
- return file.length();
- }
-
- /**
- * Returns the last modified time of this file.
- */
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- return file.getLastModified();
- }
-
- /**
- * Creates an input stream to read the file content from.
- */
- @Override
- protected InputStream doGetInputStream() throws Exception {
- try {
- return new SmbFileInputStream(file);
- } catch (final SmbException e) {
- if (e.getNtStatus() == SmbException.NT_STATUS_NO_SUCH_FILE) {
- throw new org.apache.commons.vfs2.FileNotFoundException(getName());
- } else if (file.isDirectory()) {
- throw new FileTypeHasNoContentException(getName());
- }
-
- throw e;
- }
- }
-
- /**
- * Creates an output stream to write the file content to.
- */
- @Override
- protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
- return new SmbFileOutputStream(file, bAppend);
- }
-
- /**
- * random access
- */
- @Override
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- return new SmbFileRandomAccessContent(file, mode);
- }
-
- @Override
- protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
- file.setLastModified(modtime);
- return true;
- }
-}
+/*
+ * 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.smb;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+
+import jcifs.smb.NtlmPasswordAuthentication;
+import jcifs.smb.SmbException;
+import jcifs.smb.SmbFile;
+import jcifs.smb.SmbFileInputStream;
+import jcifs.smb.SmbFileOutputStream;
+
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.FileTypeHasNoContentException;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.UserAuthenticationData;
+import org.apache.commons.vfs2.provider.AbstractFileName;
+import org.apache.commons.vfs2.provider.AbstractFileObject;
+import org.apache.commons.vfs2.provider.UriParser;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+import org.apache.commons.vfs2.util.UserAuthenticatorUtils;
+
+/**
+ * A file in an SMB file system.
+ */
+public class SmbFileObject extends AbstractFileObject<SmbFileSystem> {
+ // private final String fileName;
+ private SmbFile file;
+
+ protected SmbFileObject(final AbstractFileName name, final SmbFileSystem fileSystem) throws FileSystemException {
+ super(name, fileSystem);
+ // this.fileName = UriParser.decode(name.getURI());
+ }
+
+ /**
+ * Attaches this file object to its file resource.
+ */
+ @Override
+ protected void doAttach() throws Exception {
+ // Defer creation of the SmbFile to here
+ if (file == null) {
+ file = createSmbFile(getName());
+ }
+ }
+
+ @Override
+ protected void doDetach() throws Exception {
+ // file closed through content-streams
+ file = null;
+ }
+
+ private SmbFile createSmbFile(final FileName fileName)
+ throws MalformedURLException, SmbException, FileSystemException {
+ final SmbFileName smbFileName = (SmbFileName) fileName;
+
+ final String path = smbFileName.getUriWithoutAuth();
+
+ UserAuthenticationData authData = null;
+ SmbFile file;
+ try {
+ authData = UserAuthenticatorUtils.authenticate(getFileSystem().getFileSystemOptions(),
+ SmbFileProvider.AUTHENTICATOR_TYPES);
+
+ NtlmPasswordAuthentication auth = null;
+ if (authData != null) {
+ auth = new NtlmPasswordAuthentication(
+ UserAuthenticatorUtils.toString(UserAuthenticatorUtils.getData(authData,
+ UserAuthenticationData.DOMAIN, UserAuthenticatorUtils.toChar(smbFileName.getDomain()))),
+ UserAuthenticatorUtils
+ .toString(UserAuthenticatorUtils.getData(authData, UserAuthenticationData.USERNAME,
+ UserAuthenticatorUtils.toChar(smbFileName.getUserName()))),
+ UserAuthenticatorUtils
+ .toString(UserAuthenticatorUtils.getData(authData, UserAuthenticationData.PASSWORD,
+ UserAuthenticatorUtils.toChar(smbFileName.getPassword()))));
+ }
+
+ // if auth == null SmbFile uses default credentials
+ // ("jcifs.smb.client.domain", "?"), ("jcifs.smb.client.username", "GUEST"),
+ // ("jcifs.smb.client.password", BLANK);
+ // ANONYMOUS=("","","")
+ file = new SmbFile(path, auth);
+
+ if (file.isDirectory() && !file.toString().endsWith("/")) {
+ file = new SmbFile(path + "/", auth);
+ }
+ return file;
+ } finally {
+ UserAuthenticatorUtils.cleanup(authData); // might be null
+ }
+ }
+
+ /**
+ * Determines the type of the file, returns null if the file does not exist.
+ */
+ @Override
+ protected FileType doGetType() throws Exception {
+ if (!file.exists()) {
+ return FileType.IMAGINARY;
+ } else if (file.isDirectory()) {
+ return FileType.FOLDER;
+ } else if (file.isFile()) {
+ return FileType.FILE;
+ }
+
+ throw new FileSystemException("vfs.provider.smb/get-type.error", getName());
+ }
+
+ /**
+ * Lists the children of the file. Is only called if {@link #doGetType} returns {@link FileType#FOLDER}.
+ */
+ @Override
+ protected String[] doListChildren() throws Exception {
+ // VFS-210: do not try to get listing for anything else than directories
+ if (!file.isDirectory()) {
+ return null;
+ }
+
+ return UriParser.encode(file.list());
+ }
+
+ /**
+ * Determines if this file is hidden.
+ */
+ @Override
+ protected boolean doIsHidden() throws Exception {
+ return file.isHidden();
+ }
+
+ /**
+ * Deletes the file.
+ */
+ @Override
+ protected void doDelete() throws Exception {
+ file.delete();
+ }
+
+ @Override
+ protected void doRename(final FileObject newfile) throws Exception {
+ file.renameTo(createSmbFile(newfile.getName()));
+ }
+
+ /**
+ * Creates this file as a folder.
+ */
+ @Override
+ protected void doCreateFolder() throws Exception {
+ file.mkdir();
+ file = createSmbFile(getName());
+ }
+
+ /**
+ * Returns the size of the file content (in bytes).
+ */
+ @Override
+ protected long doGetContentSize() throws Exception {
+ return file.length();
+ }
+
+ /**
+ * Returns the last modified time of this file.
+ */
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ return file.getLastModified();
+ }
+
+ /**
+ * Creates an input stream to read the file content from.
+ */
+ @Override
+ protected InputStream doGetInputStream(int bufferSize) throws Exception {
+ try {
+ return new SmbFileInputStream(file);
+ } catch (final SmbException e) {
+ if (e.getNtStatus() == SmbException.NT_STATUS_NO_SUCH_FILE) {
+ throw new org.apache.commons.vfs2.FileNotFoundException(getName());
+ } else if (file.isDirectory()) {
+ throw new FileTypeHasNoContentException(getName());
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ * Creates an output stream to write the file content to.
+ */
+ @Override
+ protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
+ return new SmbFileOutputStream(file, bAppend);
+ }
+
+ /**
+ * random access
+ */
+ @Override
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
+ return new SmbFileRandomAccessContent(file, mode);
+ }
+
+ @Override
+ protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
+ file.setLastModified(modtime);
+ return true;
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
index 2515849..bad8896 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
@@ -1,1865 +1,1894 @@
-/*
- * 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;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.security.cert.Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.vfs2.Capability;
-import org.apache.commons.vfs2.FileContent;
-import org.apache.commons.vfs2.FileContentInfoFactory;
-import org.apache.commons.vfs2.FileName;
-import org.apache.commons.vfs2.FileNotFolderException;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSelector;
-import org.apache.commons.vfs2.FileSystem;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.FileUtil;
-import org.apache.commons.vfs2.NameScope;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.Selectors;
-import org.apache.commons.vfs2.VFS;
-import org.apache.commons.vfs2.operations.DefaultFileOperations;
-import org.apache.commons.vfs2.operations.FileOperations;
-import org.apache.commons.vfs2.util.FileObjectUtils;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-
-/**
- * A partial file object implementation.
- *
- * @param <AFS> An AbstractFileSystem subclass
- */
-public abstract class AbstractFileObject<AFS extends AbstractFileSystem> implements FileObject {
-
- /*
- * TODO - Chop this class up - move all the protected methods to several interfaces, so that structure and content
- * can be separately overridden.
- *
- * <p>
- * TODO - Check caps in methods like getChildren(), etc, and give better error messages (eg 'this file type does not
- * support listing children', vs 'this is not a folder')
- * </p>
- */
-
- private static final FileName[] EMPTY_FILE_ARRAY = {};
-
- private static final int INITIAL_LIST_SIZE = 5;
-
- /**
- * Traverses a file.
- */
- private static void traverse(final DefaultFileSelectorInfo fileInfo, final FileSelector selector,
- final boolean depthwise, final List<FileObject> selected) throws Exception {
- // Check the file itself
- final FileObject file = fileInfo.getFile();
- final int index = selected.size();
-
- // If the file is a folder, traverse it
- if (file.getType().hasChildren() && selector.traverseDescendents(fileInfo)) {
- final int curDepth = fileInfo.getDepth();
- fileInfo.setDepth(curDepth + 1);
-
- // Traverse the children
- final FileObject[] children = file.getChildren();
- for (final FileObject child : children) {
- fileInfo.setFile(child);
- traverse(fileInfo, selector, depthwise, selected);
- }
-
- fileInfo.setFile(file);
- fileInfo.setDepth(curDepth);
- }
-
- // Add the file if doing depthwise traversal
- if (selector.includeFile(fileInfo)) {
- if (depthwise) {
- // Add this file after its descendants
- selected.add(file);
- } else {
- // Add this file before its descendants
- selected.add(index, file);
- }
- }
- }
- private final AbstractFileName fileName;
-
- private final AFS fileSystem;
- private FileContent content;
- // Cached info
- private boolean attached;
-
- private FileType type;
- private FileObject parent;
-
- // Changed to hold only the name of the children and let the object
- // go into the global files cache
- // private FileObject[] children;
- private FileName[] children;
-
- private List<Object> objects;
-
- /**
- * FileServices instance.
- */
- private FileOperations operations;
-
- /**
- *
- * @param name the file name - muse be an instance of {@link AbstractFileName}
- * @param fileSystem the file system
- * @throws ClassCastException if {@code name} is not an instance of {@link AbstractFileName}
- */
- protected AbstractFileObject(final AbstractFileName name, final AFS fileSystem) {
- this.fileName = name;
- this.fileSystem = fileSystem;
- fileSystem.fileObjectHanded(this);
- }
-
- /**
- * Attaches to the file.
- *
- * @throws FileSystemException if an error occurs.
- */
- private void attach() throws FileSystemException {
- synchronized (fileSystem) {
- if (attached) {
- return;
- }
-
- try {
- // Attach and determine the file type
- doAttach();
- attached = true;
- // now the type could already be injected by doAttach (e.g from parent to child)
-
- /*
- * VFS-210: determine the type when really asked fore if (type == null) { setFileType(doGetType()); } if
- * (type == null) { setFileType(FileType.IMAGINARY); }
- */
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/get-type.error", exc, fileName);
- }
-
- // fs.fileAttached(this);
- }
- }
-
- /**
- * Queries the object if a simple rename to the file name of {@code newfile} is possible.
- *
- * @param newfile the new file name
- * @return true if rename is possible
- */
- @Override
- public boolean canRenameTo(final FileObject newfile) {
- return fileSystem == newfile.getFileSystem();
- }
-
- /**
- * Notifies the file that its children have changed.
- *
- * @param childName The name of the child.
- * @param newType The type of the child.
- * @throws Exception if an error occurs.
- */
- protected void childrenChanged(final FileName childName, final FileType newType) throws Exception {
- // TODO - this may be called when not attached
-
- if (children != null && childName != null && newType != null) {
- // TODO - figure out if children[] can be replaced by list
- final ArrayList<FileName> list = new ArrayList<>(Arrays.asList(children));
- if (newType.equals(FileType.IMAGINARY)) {
- list.remove(childName);
- } else {
- list.add(childName);
- }
- children = new FileName[list.size()];
- list.toArray(children);
- }
-
- // removeChildrenCache();
- onChildrenChanged(childName, newType);
- }
-
- /**
- * Closes this file, and its content.
- *
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void close() throws FileSystemException {
- FileSystemException exc = null;
-
- synchronized (fileSystem) {
- // Close the content
- if (content != null) {
- try {
- content.close();
- content = null;
- } catch (final FileSystemException e) {
- exc = e;
- }
- }
-
- // Detach from the file
- try {
- detach();
- } catch (final Exception e) {
- exc = new FileSystemException("vfs.provider/close.error", fileName, e);
- }
-
- if (exc != null) {
- throw exc;
- }
- }
- }
-
- /**
- * Compares two FileObjects (ignores case).
- *
- * @param file the object to compare.
- * @return a negative integer, zero, or a positive integer when this object is less than, equal to, or greater than
- * the given object.
- */
- @Override
- public int compareTo(final FileObject file) {
- if (file == null) {
- return 1;
- }
- return this.toString().compareToIgnoreCase(file.toString());
- }
-
- /**
- * Copies another file to this file.
- *
- * @param file The FileObject to copy.
- * @param selector The FileSelector.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void copyFrom(final FileObject file, final FileSelector selector) throws FileSystemException {
- if (!FileObjectUtils.exists(file)) {
- throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
- }
-
- // Locate the files to copy across
- final ArrayList<FileObject> files = new ArrayList<>();
- file.findFiles(selector, false, files);
-
- // Copy everything across
- for (final FileObject srcFile : files) {
- // Determine the destination file
- final String relPath = file.getName().getRelativeName(srcFile.getName());
- final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);
-
- // Clean up the destination file, if necessary
- if (FileObjectUtils.exists(destFile) && destFile.getType() != srcFile.getType()) {
- // The destination file exists, and is not of the same type,
- // so delete it
- // TODO - add a pluggable policy for deleting and overwriting existing files
- destFile.deleteAll();
- }
-
- // Copy across
- try {
- if (srcFile.getType().hasContent()) {
- FileUtil.copyContent(srcFile, destFile);
- } else if (srcFile.getType().hasChildren()) {
- destFile.createFolder();
- }
- } catch (final IOException e) {
- throw new FileSystemException("vfs.provider/copy-file.error", e, srcFile, destFile);
- }
- }
- }
-
- /**
- * Creates this file, if it does not exist.
- *
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void createFile() throws FileSystemException {
- synchronized (fileSystem) {
- try {
- // VFS-210: We do not want to trunc any existing file, checking for its existence is
- // still required
- if (exists() && !isFile()) {
- throw new FileSystemException("vfs.provider/create-file.error", fileName);
- }
-
- if (!exists()) {
- getOutputStream().close();
- endOutput();
- }
- } catch (final RuntimeException re) {
- throw re;
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/create-file.error", fileName, e);
- }
- }
- }
-
- /**
- * Creates this folder, if it does not exist. Also creates any ancestor files which do not exist.
- *
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void createFolder() throws FileSystemException {
- synchronized (fileSystem) {
- // VFS-210: we create a folder only if it does not already exist. So this check should be safe.
- if (getType().hasChildren()) {
- // Already exists as correct type
- return;
- }
- if (getType() != FileType.IMAGINARY) {
- throw new FileSystemException("vfs.provider/create-folder-mismatched-type.error", fileName);
- }
- /*
- * VFS-210: checking for writeable is not always possible as the security constraint might be more complex
- * if (!isWriteable()) { throw new FileSystemException("vfs.provider/create-folder-read-only.error", name);
- * }
- */
-
- // Traverse up the hierarchy and make sure everything is a folder
- final FileObject parent = getParent();
- if (parent != null) {
- parent.createFolder();
- }
-
- try {
- // Create the folder
- doCreateFolder();
-
- // Update cached info
- handleCreate(FileType.FOLDER);
- } catch (final RuntimeException re) {
- throw re;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/create-folder.error", fileName, exc);
- }
- }
- }
-
- /**
- * Deletes this file.
- * <p>
- * TODO - This will not fail if this is a non-empty folder.
- * </p>
- *
- * @return true if this object has been deleted
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public boolean delete() throws FileSystemException {
- return delete(Selectors.SELECT_SELF) > 0;
- }
-
- /**
- * Deletes this file, and all children matching the {@code selector}.
- *
- * @param selector The FileSelector.
- * @return the number of deleted files.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public int delete(final FileSelector selector) throws FileSystemException {
- int nuofDeleted = 0;
-
- /*
- * VFS-210 if (getType() == FileType.IMAGINARY) { // File does not exist return nuofDeleted; }
- */
-
- // Locate all the files to delete
- final ArrayList<FileObject> files = new ArrayList<>();
- findFiles(selector, true, files);
-
- // Delete 'em
- final int count = files.size();
- for (int i = 0; i < count; i++) {
- final AbstractFileObject file = FileObjectUtils.getAbstractFileObject(files.get(i));
- // file.attach();
-
- // VFS-210: It seems impossible to me that findFiles will return a list with hidden files/directories
- // in it, else it would not be hidden. Checking for the file-type seems ok in this case
- // If the file is a folder, make sure all its children have been deleted
- if (file.getType().hasChildren() && file.getChildren().length != 0) {
- // Skip - as the selector forced us not to delete all files
- continue;
- }
-
- // Delete the file
- if (file.deleteSelf()) {
- nuofDeleted++;
- }
- }
-
- return nuofDeleted;
- }
-
- /**
- * Deletes this file and all children. Shorthand for {@code delete(Selectors.SELECT_ALL)}
- *
- * @return the number of deleted files.
- * @throws FileSystemException if an error occurs.
- * @see #delete(FileSelector)
- * @see Selectors#SELECT_ALL
- */
- @Override
- public int deleteAll() throws FileSystemException {
- return this.delete(Selectors.SELECT_ALL);
- }
-
- /**
- * Deletes this file, once all its children have been deleted
- *
- * @return true if this file has been deleted
- * @throws FileSystemException if an error occurs.
- */
- private boolean deleteSelf() throws FileSystemException {
- synchronized (fileSystem) {
- // Its possible to delete a read-only file if you have write-execute access to the directory
-
- /*
- * VFS-210 if (getType() == FileType.IMAGINARY) { // File does not exist return false; }
- */
-
- try {
- // Delete the file
- doDelete();
-
- // Update cached info
- handleDelete();
- } catch (final RuntimeException re) {
- throw re;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/delete.error", exc, fileName);
- }
-
- return true;
- }
- }
-
- /**
- * Detaches this file, invalidating all cached info. This will force a call to {@link #doAttach} next time this file
- * is used.
- *
- * @throws Exception if an error occurs.
- */
- private void detach() throws Exception {
- synchronized (fileSystem) {
- if (attached) {
- try {
- doDetach();
- } finally {
- attached = false;
- setFileType(null);
- parent = null;
-
- // fs.fileDetached(this);
-
- removeChildrenCache();
- // children = null;
- }
- }
- }
- }
-
- /**
- * Attaches this file object to its file resource.
- * <p>
- * This method is called before any of the doBlah() or onBlah() methods. Sub-classes can use this method to perform
- * lazy initialisation.
- * </p>
- * <p>
- * This implementation does nothing.
- * </p>
- *
- * @throws Exception if an error occurs.
- */
- protected void doAttach() throws Exception {
- // noop
- }
-
- /**
- * Create a FileContent implementation.
- *
- * @return The FileContent.
- * @throws FileSystemException if an error occurs.
- * @since 2.0
- */
- protected FileContent doCreateFileContent() throws FileSystemException {
- return new DefaultFileContent(this, getFileContentInfoFactory());
- }
-
- /**
- * Creates this file as a folder. Is only called when:
- * <ul>
- * <li>{@link #doGetType} returns {@link FileType#IMAGINARY}.</li>
- * <li>The parent folder exists and is writeable, or this file is the root of the file system.</li>
- * </ul>
- * This implementation throws an exception.
- *
- * @throws Exception if an error occurs.
- */
- protected void doCreateFolder() throws Exception {
- throw new FileSystemException("vfs.provider/create-folder-not-supported.error");
- }
-
- /**
- * Deletes the file. Is only called when:
- * <ul>
- * <li>{@link #doGetType} does not return {@link FileType#IMAGINARY}.</li>
- * <li>{@link #doIsWriteable} returns true.</li>
- * <li>This file has no children, if a folder.</li>
- * </ul>
- * This implementation throws an exception.
- *
- * @throws Exception if an error occurs.
- */
- protected void doDelete() throws Exception {
- throw new FileSystemException("vfs.provider/delete-not-supported.error");
- }
-
- /**
- * Detaches this file object from its file resource.
- * <p>
- * Called when this file is closed. Note that the file object may be reused later, so should be able to be
- * reattached.
- * </p>
- * <p>
- * This implementation does nothing.
- * </p>
- *
- * @throws Exception if an error occurs.
- */
- protected void doDetach() throws Exception {
- // noop
- }
-
- /**
- * Returns the attributes of this file. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation always returns an empty map.
- * </p>
- *
- * @return The attributes of the file.
- * @throws Exception if an error occurs.
- */
- protected Map<String, Object> doGetAttributes() throws Exception {
- return Collections.emptyMap();
- }
-
- /**
- * Returns the certificates used to sign this file. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation always returns null.
- * </p>
- *
- * @return The certificates used to sign the file.
- * @throws Exception if an error occurs.
- */
- protected Certificate[] doGetCertificates() throws Exception {
- return null;
- }
-
- /**
- * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
- * {@link FileType#FILE}.
- *
- * @return The size of the file in bytes.
- * @throws Exception if an error occurs.
- */
- protected abstract long doGetContentSize() throws Exception;
-
- /**
- * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
- * {@link FileType#FILE}.
- * <p>
- * It is guaranteed that there are no open output streams for this file when this method is called.
- * </p>
- * <p>
- * The returned stream does not have to be buffered.
- * </p>
- *
- * @return An InputStream to read the file content.
- * @throws Exception if an error occurs.
- */
- protected abstract InputStream doGetInputStream() throws Exception;
-
- /**
- * Returns the last modified time of this file. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation throws an exception.
- * </p>
- *
- * @return The last modification time.
- * @throws Exception if an error occurs.
- */
- protected long doGetLastModifiedTime() throws Exception {
- throw new FileSystemException("vfs.provider/get-last-modified-not-supported.error");
- }
-
- /**
- * Creates an output stream to write the file content to. Is only called if:
- * <ul>
- * <li>{@link #doIsWriteable} returns true.
- * <li>{@link #doGetType} returns {@link FileType#FILE}, or {@link #doGetType} returns {@link FileType#IMAGINARY},
- * and the file's parent exists and is a folder.
- * </ul>
- * It is guaranteed that there are no open stream (input or output) for this file when this method is called.
- * <p>
- * The returned stream does not have to be buffered.
- * </p>
- * <p>
- * This implementation throws an exception.
- * </p>
- *
- * @param bAppend true if the file should be appended to, false if it should be overwritten.
- * @return An OutputStream to write to the file.
- * @throws Exception if an error occurs.
- */
- protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
- throw new FileSystemException("vfs.provider/write-not-supported.error");
- }
-
- /**
- * Creates access to the file for random i/o. Is only called if {@link #doGetType} returns {@link FileType#FILE}.
- * <p>
- * It is guaranteed that there are no open output streams for this file when this method is called.
- * </p>
- *
- * @param mode The mode to access the file.
- * @return The RandomAccessContext.
- * @throws Exception if an error occurs.
- */
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- throw new FileSystemException("vfs.provider/random-access-not-supported.error");
- }
-
- /**
- * Determines the type of this file. Must not return null. The return value of this method is cached, so the
- * implementation can be expensive.
- *
- * @return the type of the file.
- * @throws Exception if an error occurs.
- */
- protected abstract FileType doGetType() throws Exception;
-
- /**
- * Determines if this file is executable. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation always returns false.
- * </p>
- *
- * @return true if the file is executable, false otherwise.
- * @throws Exception if an error occurs.
- */
- protected boolean doIsExecutable() throws Exception {
- return false;
- }
-
- /**
- * Determines if this file is hidden. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation always returns false.
- * </p>
- *
- * @return true if the file is hidden, false otherwise.
- * @throws Exception if an error occurs.
- */
- protected boolean doIsHidden() throws Exception {
- return false;
- }
-
- /**
- * Determines if this file can be read. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation always returns true.
- * </p>
- *
- * @return true if the file is readable, false otherwise.
- * @throws Exception if an error occurs.
- */
- protected boolean doIsReadable() throws Exception {
- return true;
- }
-
- /**
- * Checks if this fileObject is the same file as {@code destFile} just with a different name. E.g. for case
- * insensitive file systems like windows.
- *
- * @param destFile The file to compare to.
- * @return true if the FileObjects are the same.
- * @throws FileSystemException if an error occurs.
- */
- protected boolean doIsSameFile(final FileObject destFile) throws FileSystemException {
- return false;
- }
-
- /**
- * Determines if this file is a symbolic link. Is only called if {@link #doGetType} does not return
- * {@link FileType#IMAGINARY}.
- * <p>
- * This implementation always returns false.
- * </p>
- *
- * @return true if the file is readable, false otherwise.
- * @throws Exception if an error occurs.
- * @since 2.4
- */
- protected boolean doIsSymbolicLink() throws Exception {
- return false;
- }
-
- /**
- * 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.
- * </p>
- *
- * @return true if the file is writable.
- * @throws Exception if an error occurs.
- */
- protected boolean doIsWriteable() throws Exception {
- return true;
- }
-
- /**
- * Lists the children of this file. Is only called if {@link #doGetType} returns {@link FileType#FOLDER}. The return
- * value of this method is cached, so the implementation can be expensive.
- *
- * @return a possible empty String array if the file is a directory or null or an exception if the file is not a
- * directory or can't be read.
- * @throws Exception if an error occurs.
- */
- protected abstract String[] doListChildren() throws Exception;
-
- /**
- * Lists the children of this file.
- * <p>
- * Is only called if {@link #doGetType} returns {@link FileType#FOLDER}.
- * </p>
- * <p>
- * The return value of this method is cached, so the implementation can be expensive.
- * Other than {@code doListChildren} you could return FileObject's to e.g. reinitialize the type of the file.
- * </p>
- * <p>
- * (Introduced for Webdav: "permission denied on resource" during getType())
- * </p>
- *
- * @return The children of this FileObject.
- * @throws Exception if an error occurs.
- */
- protected FileObject[] doListChildrenResolved() throws Exception {
- return null;
- }
-
- /**
- * Removes an attribute of this file.
- * <p>
- * Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * </p>
- * <p>
- * This implementation throws an exception.
- * </p>
- *
- * @param attrName The name of the attribute to remove.
- * @throws Exception if an error occurs.
- * @since 2.0
- */
- protected void doRemoveAttribute(final String attrName) throws Exception {
- throw new FileSystemException("vfs.provider/remove-attribute-not-supported.error");
- }
-
- /**
- * Renames the file.
- * <p>
- * Is only called when:
- * </p>
- * <ul>
- * <li>{@link #doIsWriteable} returns true.</li>
- * </ul>
- * <p>
- * This implementation throws an exception.
- * </p>
- *
- * @param newFile A FileObject with the new file name.
- * @throws Exception if an error occurs.
- */
- protected void doRename(final FileObject newFile) throws Exception {
- throw new FileSystemException("vfs.provider/rename-not-supported.error");
- }
-
- /**
- * Sets an attribute of this file.
- * <p>
- * Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * </p>
- * <p>
- * This implementation throws an exception.
- * </p>
- *
- * @param attrName The attribute name.
- * @param value The value to be associated with the attribute name.
- * @throws Exception if an error occurs.
- */
- protected void doSetAttribute(final String attrName, final Object value) throws Exception {
- throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
- }
-
- /**
- * Make the file executable.
- * <p>
- * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * </p>
- * <p>
- * This implementation returns false.
- * </p>
- *
- * @param executable True to allow access, false to disallow.
- * @param ownerOnly If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
- * @return true if the operation succeeded.
- * @throws Exception Any Exception thrown is wrapped in FileSystemException.
- * @see #setExecutable(boolean, boolean)
- * @since 2.1
- */
- protected boolean doSetExecutable(final boolean executable, final boolean ownerOnly) throws Exception {
- return false;
- }
-
- /**
- * Sets the last modified time of this file.
- * <p>
- * Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * </p>
- * <p>
- * This implementation throws an exception.
- * </p>
- *
- * @param modtime The last modification time.
- * @return true if the time was set.
- * @throws Exception Any Exception thrown is wrapped in FileSystemException.
- */
- protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
- throw new FileSystemException("vfs.provider/set-last-modified-not-supported.error");
- }
-
- /**
- * Make the file or folder readable.
- * <p>
- * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * </p>
- * <p>
- * This implementation returns false.
- * </p>
- *
- * @param readable True to allow access, false to disallow
- * @param ownerOnly If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
- * @return true if the operation succeeded
- * @throws Exception Any Exception thrown is wrapped in FileSystemException.
- * @see #setReadable(boolean, boolean)
- * @since 2.1
- */
- protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception {
- return false;
- }
-
- /**
- * Make the file or folder writeable.
- * <p>
- * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
- * </p>
- *
- * @param writable True to allow access, false to disallow
- * @param ownerOnly If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
- * @return true if the operation succeeded
- * @throws Exception Any Exception thrown is wrapped in FileSystemException.
- * @see #setWritable(boolean, boolean)
- * @since 2.1
- */
- protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception {
- return false;
- }
-
- /**
- * Called when the output stream for this file is closed.
- *
- * @throws Exception if an error occurs.
- */
- protected void endOutput() throws Exception {
- if (getType() == FileType.IMAGINARY) {
- // File was created
- handleCreate(FileType.FILE);
- } else {
- // File has changed
- onChange();
- }
- }
-
- /**
- * Determines if the file exists.
- *
- * @return true if the file exists, false otherwise,
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public boolean exists() throws FileSystemException {
- return getType() != FileType.IMAGINARY;
- }
-
- private FileName[] extractNames(final FileObject[] objects) {
- if (objects == null) {
- return null;
- }
-
- final FileName[] names = new FileName[objects.length];
- for (int iterObjects = 0; iterObjects < objects.length; iterObjects++) {
- names[iterObjects] = objects[iterObjects].getName();
- }
-
- return names;
- }
-
- @Override
- protected void finalize() throws Throwable {
- fileSystem.fileObjectDestroyed(this);
-
- super.finalize();
- }
-
- /**
- * Finds the set of matching descendants of this file, in depthwise order.
- *
- * @param selector The FileSelector.
- * @return list of files or null if the base file (this object) do not exist
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject[] findFiles(final FileSelector selector) throws FileSystemException {
- final List<FileObject> list = this.listFiles(selector);
- return list == null ? null : list.toArray(new FileObject[list.size()]);
- }
-
- /**
- * Traverses the descendants of this file, and builds a list of selected files.
- *
- * @param selector The FileSelector.
- * @param depthwise if true files are added after their descendants, before otherwise.
- * @param selected A List of the located FileObjects.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void findFiles(final FileSelector selector, final boolean depthwise, final List<FileObject> selected)
- throws FileSystemException {
- try {
- if (exists()) {
- // Traverse starting at this file
- final DefaultFileSelectorInfo info = new DefaultFileSelectorInfo();
- info.setBaseFolder(this);
- info.setDepth(0);
- info.setFile(this);
- traverse(info, selector, depthwise, selected);
- }
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/find-files.error", fileName, e);
- }
- }
-
- /**
- * Returns the file system this file belongs to.
- *
- * @return The FileSystem this file is associated with.
- */
- protected AFS getAbstractFileSystem() {
- return fileSystem;
- }
-
- /**
- * Returns a child of this file.
- *
- * @param name The name of the child to locate.
- * @return The FileObject for the file or null if the child does not exist.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject getChild(final String name) throws FileSystemException {
- // TODO - use a hashtable when there are a large number of children
- final FileObject[] children = getChildren();
- for (final FileObject element : children) {
- final FileName child = element.getName();
- // TODO - use a comparator to compare names
- if (child.getBaseName().equals(name)) {
- return resolveFile(child);
- }
- }
- return null;
- }
-
- /**
- * Returns the children of the file.
- *
- * @return an array of FileObjects, one per child.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject[] getChildren() throws FileSystemException {
- synchronized (fileSystem) {
- // VFS-210
- if (!fileSystem.hasCapability(Capability.LIST_CHILDREN)) {
- throw new FileNotFolderException(fileName);
- }
-
- /*
- * VFS-210 if (!getType().hasChildren()) { throw new
- * FileSystemException("vfs.provider/list-children-not-folder.error", name); }
- */
- attach();
-
- // Use cached info, if present
- if (children != null) {
- return resolveFiles(children);
- }
-
- // allow the filesystem to return resolved children. e.g. prefill type for webdav
- FileObject[] childrenObjects;
- try {
- childrenObjects = doListChildrenResolved();
- children = extractNames(childrenObjects);
- } catch (final FileSystemException exc) {
- // VFS-210
- throw exc;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
- }
-
- if (childrenObjects != null) {
- return childrenObjects;
- }
-
- // List the children
- final String[] files;
- try {
- files = doListChildren();
- } catch (final FileSystemException exc) {
- // VFS-210
- throw exc;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
- }
-
- if (files == null) {
- // VFS-210
- // honor the new doListChildren contract
- // return null;
- throw new FileNotFolderException(fileName);
- } else if (files.length == 0) {
- // No children
- children = EMPTY_FILE_ARRAY;
- } else {
- // Create file objects for the children
- final FileName[] cache = new FileName[files.length];
- for (int i = 0; i < files.length; i++) {
- final String file = files[i];
- cache[i] = fileSystem.getFileSystemManager().resolveName(fileName, file, NameScope.CHILD);
- }
- // VFS-285: only assign the children file names after all of them have been
- // resolved successfully to prevent an inconsistent internal state
- children = cache;
- }
-
- return resolveFiles(children);
- }
- }
-
- /**
- * Returns the file's content.
- *
- * @return the FileContent for this FileObject.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileContent getContent() throws FileSystemException {
- synchronized (fileSystem) {
- attach();
- if (content == null) {
- content = doCreateFileContent();
- }
- return content;
- }
- }
-
- /**
- * Creates the FileContentInfo factory.
- *
- * @return The FileContentInfoFactory.
- */
- protected FileContentInfoFactory getFileContentInfoFactory() {
- return fileSystem.getFileSystemManager().getFileContentInfoFactory();
- }
-
- /**
- * @return FileOperations interface that provides access to the operations API.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileOperations getFileOperations() throws FileSystemException {
- if (operations == null) {
- operations = new DefaultFileOperations(this);
- }
-
- return operations;
- }
-
- /**
- * Returns the file system this file belongs to.
- *
- * @return The FileSystem this file is associated with.
- */
- @Override
- public FileSystem getFileSystem() {
- return fileSystem;
- }
-
- /**
- * Returns an input stream to use to read the content of the file.
- *
- * @return The InputStream to access this file's content.
- * @throws FileSystemException if an error occurs.
- */
- public InputStream getInputStream() throws FileSystemException {
- /*
- * VFS-210 if (!getType().hasContent()) { throw new FileSystemException("vfs.provider/read-not-file.error",
- * name); } if (!isReadable()) { throw new FileSystemException("vfs.provider/read-not-readable.error", name); }
- */
-
- // Get the raw input stream
- try {
- return doGetInputStream();
- } catch (final org.apache.commons.vfs2.FileNotFoundException exc) {
- throw new org.apache.commons.vfs2.FileNotFoundException(fileName, exc);
- } catch (final FileNotFoundException exc) {
- throw new org.apache.commons.vfs2.FileNotFoundException(fileName, exc);
- } catch (final FileSystemException exc) {
- throw exc;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/read.error", fileName, exc);
- }
- }
-
- /**
- * Returns the name of the file.
- *
- * @return The FileName, never {@code null}.
- */
- @Override
- public FileName getName() {
- return fileName;
- }
-
- /**
- * Prepares this file for writing. Makes sure it is either a file, or its parent folder exists. Returns an output
- * stream to use to write the content of the file to.
- *
- * @return An OutputStream where the new contents of the file can be written.
- * @throws FileSystemException if an error occurs.
- */
- public OutputStream getOutputStream() throws FileSystemException {
- return getOutputStream(false);
- }
-
- /**
- * Prepares this file for writing. Makes sure it is either a file, or its parent folder exists. Returns an output
- * stream to use to write the content of the file to.
- *
- * @param bAppend true when append to the file.
- * Note: If the underlying file system does not support appending, a FileSystemException is thrown.
- * @return An OutputStream where the new contents of the file can be written.
- * @throws FileSystemException if an error occurs; for example:
- * bAppend is true, and the underlying FileSystem does not support it
- */
- public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException {
- /*
- * VFS-210 if (getType() != FileType.IMAGINARY && !getType().hasContent()) { throw new
- * FileSystemException("vfs.provider/write-not-file.error", name); } if (!isWriteable()) { throw new
- * FileSystemException("vfs.provider/write-read-only.error", name); }
- */
-
- if (bAppend && !fileSystem.hasCapability(Capability.APPEND_CONTENT)) {
- throw new FileSystemException("vfs.provider/write-append-not-supported.error", fileName);
- }
-
- if (getType() == FileType.IMAGINARY) {
- // Does not exist - make sure parent does
- final FileObject parent = getParent();
- if (parent != null) {
- parent.createFolder();
- }
- }
-
- // Get the raw output stream
- try {
- return doGetOutputStream(bAppend);
- } catch (final RuntimeException re) {
- throw re;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/write.error", exc, fileName);
- }
- }
-
- /**
- * Returns the parent of the file.
- *
- * @return the parent FileObject.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject getParent() throws FileSystemException {
- if (this.compareTo(fileSystem.getRoot()) == 0) // equals is not implemented :-/
- {
- if (fileSystem.getParentLayer() == null) {
- // Root file has no parent
- return null;
- }
- // Return the parent of the parent layer
- return fileSystem.getParentLayer().getParent();
- }
-
- synchronized (fileSystem) {
- // Locate the parent of this file
- if (parent == null) {
- final FileName name = fileName.getParent();
- if (name == null) {
- return null;
- }
- parent = fileSystem.resolveFile(name);
- }
- return parent;
- }
- }
-
- /**
- * Returns the receiver as a URI String for public display, like, without a password.
- *
- * @return A URI String without a password, never {@code null}.
- */
- @Override
- public String getPublicURIString() {
- return fileName.getFriendlyURI();
- }
-
- /**
- * Returns an input/output stream to use to read and write the content of the file in and random manner.
- *
- * @param mode The RandomAccessMode.
- * @return The RandomAccessContent.
- * @throws FileSystemException if an error occurs.
- */
- public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException {
- /*
- * VFS-210 if (!getType().hasContent()) { throw new FileSystemException("vfs.provider/read-not-file.error",
- * name); }
- */
-
- if (mode.requestRead()) {
- if (!fileSystem.hasCapability(Capability.RANDOM_ACCESS_READ)) {
- throw new FileSystemException("vfs.provider/random-access-read-not-supported.error");
- }
- if (!isReadable()) {
- throw new FileSystemException("vfs.provider/read-not-readable.error", fileName);
- }
- }
-
- if (mode.requestWrite()) {
- if (!fileSystem.hasCapability(Capability.RANDOM_ACCESS_WRITE)) {
- throw new FileSystemException("vfs.provider/random-access-write-not-supported.error");
- }
- if (!isWriteable()) {
- throw new FileSystemException("vfs.provider/write-read-only.error", fileName);
- }
- }
-
- // Get the raw input stream
- try {
- return doGetRandomAccessContent(mode);
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/random-access.error", fileName, exc);
- }
- }
-
- /**
- * Returns the file's type.
- *
- * @return The FileType.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileType getType() throws FileSystemException {
- synchronized (fileSystem) {
- attach();
-
- // VFS-210: get the type only if requested for
- try {
- if (type == null) {
- setFileType(doGetType());
- }
- if (type == null) {
- setFileType(FileType.IMAGINARY);
- }
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/get-type.error", e, fileName);
- }
-
- return type;
- }
- }
-
- /**
- * Returns a URL representation of the file.
- *
- * @return The URL representation of the file.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public URL getURL() throws FileSystemException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<URL>() {
- @Override
- public URL run() throws MalformedURLException, FileSystemException {
- final StringBuilder buf = new StringBuilder();
- final String scheme = UriParser.extractScheme(VFS.getManager().getSchemes(), fileName.getURI(), buf);
- return new URL(scheme, "", -1, buf.toString(),
- new DefaultURLStreamHandler(fileSystem.getContext(), fileSystem.getFileSystemOptions()));
- }
- });
- } catch (final PrivilegedActionException e) {
- throw new FileSystemException("vfs.provider/get-url.error", fileName, e.getException());
- }
- }
-
- /**
- * Called when this file is changed.
- * <p>
- * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}.
- * </p>
- *
- * @throws Exception if an error occurs.
- */
- protected void handleChanged() throws Exception {
- // Notify the file system
- fileSystem.fireFileChanged(this);
- }
-
- /**
- * Called when this file is created. Updates cached info and notifies the parent and file system.
- *
- * @param newType The type of the file.
- * @throws Exception if an error occurs.
- */
- protected void handleCreate(final FileType newType) throws Exception {
- synchronized (fileSystem) {
- if (attached) {
- // Fix up state
- injectType(newType);
-
- removeChildrenCache();
-
- // Notify subclass
- onChange();
- }
-
- // Notify parent that its child list may no longer be valid
- notifyParent(this.getName(), newType);
-
- // Notify the file system
- fileSystem.fireFileCreated(this);
- }
- }
-
- /**
- * Called when this file is deleted. Updates cached info and notifies subclasses, parent and file system.
- *
- * @throws Exception if an error occurs.
- */
- protected void handleDelete() throws Exception {
- synchronized (fileSystem) {
- if (attached) {
- // Fix up state
- injectType(FileType.IMAGINARY);
- removeChildrenCache();
-
- // Notify subclass
- onChange();
- }
-
- // Notify parent that its child list may no longer be valid
- notifyParent(this.getName(), FileType.IMAGINARY);
-
- // Notify the file system
- fileSystem.fireFileDeleted(this);
- }
- }
-
- /**
- * This method is meant to add an object where this object holds a strong reference then. E.g. a archive-file system
- * creates a list of all children and they shouldn't get garbage collected until the container is garbage collected
- *
- * @param strongRef The Object to add.
- */
- // TODO should this be a FileObject?
- public void holdObject(final Object strongRef) {
- if (objects == null) {
- objects = new ArrayList<>(INITIAL_LIST_SIZE);
- }
- objects.add(strongRef);
- }
-
- protected void injectType(final FileType fileType) {
- setFileType(fileType);
- }
-
- /**
- * Check if the internal state is "attached".
- *
- * @return true if this is the case
- */
- @Override
- public boolean isAttached() {
- return attached;
- }
-
- /**
- * Check if the content stream is open.
- *
- * @return true if this is the case
- */
- @Override
- public boolean isContentOpen() {
- if (content == null) {
- return false;
- }
-
- return content.isOpen();
- }
-
- /**
- * Determines if this file is executable.
- *
- * @return {@code true} if this file is executable, {@code false} if not.
- * @throws FileSystemException On error determining if this file exists.
- */
- @Override
- public boolean isExecutable() throws FileSystemException {
- try {
- return exists() ? doIsExecutable() : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/check-is-executable.error", fileName, exc);
- }
- }
-
- /**
- * Checks if this file is a regular file by using its file type.
- *
- * @return true if this file is a regular file.
- * @throws FileSystemException if an error occurs.
- * @see #getType()
- * @see FileType#FILE
- */
- @Override
- public boolean isFile() throws FileSystemException {
- // Use equals instead of == to avoid any class loader worries.
- return FileType.FILE.equals(this.getType());
- }
-
- /**
- * Checks if this file is a folder by using its file type.
- *
- * @return true if this file is a regular file.
- * @throws FileSystemException if an error occurs.
- * @see #getType()
- * @see FileType#FOLDER
- */
- @Override
- public boolean isFolder() throws FileSystemException {
- // Use equals instead of == to avoid any class loader worries.
- return FileType.FOLDER.equals(this.getType());
- }
-
- /**
- * Determines if this file can be read.
- *
- * @return true if the file is a hidden file, false otherwise.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public boolean isHidden() throws FileSystemException {
- try {
- return exists() ? doIsHidden() : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/check-is-hidden.error", fileName, exc);
- }
- }
-
- /**
- * Determines if this file can be read.
- *
- * @return true if the file can be read, false otherwise.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public boolean isReadable() throws FileSystemException {
- try {
- return exists() ? doIsReadable() : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/check-is-readable.error", fileName, exc);
- }
- }
-
- /**
- * Checks if this fileObject is the same file as {@code destFile} just with a different name. E.g. for case
- * insensitive file systems like windows.
- *
- * @param destFile The file to compare to.
- * @return true if the FileObjects are the same.
- * @throws FileSystemException if an error occurs.
- */
- protected boolean isSameFile(final FileObject destFile) throws FileSystemException {
- attach();
- return doIsSameFile(destFile);
- }
-
- /**
- * Determines if this file can be read.
- *
- * @return true if the file can be read, false otherwise.
- * @throws FileSystemException if an error occurs.
- * @since 2.4
- */
- @Override
- public boolean isSymbolicLink() throws FileSystemException {
- try {
- return exists() ? doIsSymbolicLink() : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/check-is-symbolic-link.error", fileName, exc);
- }
- }
-
- /**
- * Determines if this file can be written to.
- *
- * @return true if the file can be written to, false otherwise.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public boolean isWriteable() throws FileSystemException {
- try {
- if (exists()) {
- return doIsWriteable();
- }
- final FileObject parent = getParent();
- if (parent != null) {
- return parent.isWriteable();
- }
- return true;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/check-is-writeable.error", fileName, exc);
- }
- }
-
- /**
- * Returns an iterator over a set of all FileObject in this file object.
- *
- * @return an Iterator.
- */
- @Override
- public Iterator<FileObject> iterator() {
- try {
- return listFiles(Selectors.SELECT_ALL).iterator();
- } catch (final FileSystemException e) {
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Lists the set of matching descendants of this file, in depthwise order.
- *
- * @param selector The FileSelector.
- * @return list of files or null if the base file (this object) do not exist or the {@code selector} is null
- * @throws FileSystemException if an error occurs.
- */
- public List<FileObject> listFiles(final FileSelector selector) throws FileSystemException {
- if (!exists() || selector == null) {
- return null;
- }
-
- final ArrayList<FileObject> list = new ArrayList<>();
- this.findFiles(selector, true, list);
- return list;
- }
-
- /**
- * Moves (rename) the file to another one.
- *
- * @param destFile The target FileObject.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void moveTo(final FileObject destFile) throws FileSystemException {
- if (canRenameTo(destFile)) {
- if (!getParent().isWriteable()) {
- throw new FileSystemException("vfs.provider/rename-parent-read-only.error", getName(),
- getParent().getName());
- }
- } else {
- if (!isWriteable()) {
- throw new FileSystemException("vfs.provider/rename-read-only.error", getName());
- }
- }
-
- if (destFile.exists() && !isSameFile(destFile)) {
- destFile.deleteAll();
- // throw new FileSystemException("vfs.provider/rename-dest-exists.error", destFile.getName());
- }
-
- if (canRenameTo(destFile)) {
- // issue rename on same filesystem
- try {
- attach();
- // remember type to avoid attach
- final FileType srcType = getType();
-
- doRename(destFile);
-
- FileObjectUtils.getAbstractFileObject(destFile).handleCreate(srcType);
- destFile.close(); // now the destFile is no longer imaginary. force reattach.
-
- handleDelete(); // fire delete-events. This file-object (src) is like deleted.
- } catch (final RuntimeException re) {
- throw re;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/rename.error", exc, getName(), destFile.getName());
- }
- } else {
- // different fs - do the copy/delete stuff
-
- destFile.copyFrom(this, Selectors.SELECT_SELF);
-
- if ((destFile.getType().hasContent()
- && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FILE)
- || destFile.getType().hasChildren()
- && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FOLDER))
- && fileSystem.hasCapability(Capability.GET_LAST_MODIFIED)) {
- destFile.getContent().setLastModifiedTime(this.getContent().getLastModifiedTime());
- }
-
- deleteSelf();
- }
-
- }
-
- /**
- * Clled after this file-object closed all its streams.
- */
- protected void notifyAllStreamsClosed() {
- // noop
- }
-
- /**
- * Notify the parent of a change to its children, when a child is created or deleted.
- *
- * @param childName The name of the child.
- * @param newType The type of the child.
- * @throws Exception if an error occurs.
- */
- private void notifyParent(final FileName childName, final FileType newType) throws Exception {
- if (parent == null) {
- final FileName parentName = fileName.getParent();
- if (parentName != null) {
- // Locate the parent, if it is cached
- parent = fileSystem.getFileFromCache(parentName);
- }
- }
-
- if (parent != null) {
- FileObjectUtils.getAbstractFileObject(parent).childrenChanged(childName, newType);
- }
- }
-
- /**
- * Called when the type or content of this file changes.
- * <p>
- * This implementation does nothing.
- * </p>
- *
- * @throws Exception if an error occurs.
- */
- protected void onChange() throws Exception {
- // noop
- }
-
- /**
- * Called when the children of this file change. Allows subclasses to refresh any cached information about the
- * children of this file.
- * <p>
- * This implementation does nothing.
- * </p>
- *
- * @param child The name of the child that changed.
- * @param newType The type of the file.
- * @throws Exception if an error occurs.
- */
- protected void onChildrenChanged(final FileName child, final FileType newType) throws Exception {
- // noop
- }
-
- /**
- * This will prepare the fileObject to get resynchronized with the underlying file system if required.
- *
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void refresh() throws FileSystemException {
- // Detach from the file
- try {
- detach();
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/resync.error", fileName, e);
- }
- }
-
- private void removeChildrenCache() {
- children = null;
- }
-
- private FileObject resolveFile(final FileName child) throws FileSystemException {
- return fileSystem.resolveFile(child);
- }
-
- /**
- * Finds a file, relative to this file.
- *
- * @param path The path of the file to locate. Can either be a relative path, which is resolved relative to this
- * file, or an absolute path, which is resolved relative to the file system that contains this file.
- * @return The FileObject.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject resolveFile(final String path) throws FileSystemException {
- final FileName otherName = fileSystem.getFileSystemManager().resolveName(fileName, path);
- return fileSystem.resolveFile(otherName);
- }
-
- /**
- * Returns a child by name.
- *
- * @param name The name of the child to locate.
- * @param scope the NameScope.
- * @return The FileObject for the file or null if the child does not exist.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileObject resolveFile(final String name, final NameScope scope) throws FileSystemException {
- // return fs.resolveFile(this.name.resolveName(name, scope));
- return fileSystem.resolveFile(fileSystem.getFileSystemManager().resolveName(this.fileName, name, scope));
- }
-
- private FileObject[] resolveFiles(final FileName[] children) throws FileSystemException {
- if (children == null) {
- return null;
- }
-
- final FileObject[] objects = new FileObject[children.length];
- for (int iterChildren = 0; iterChildren < children.length; iterChildren++) {
- objects[iterChildren] = resolveFile(children[iterChildren]);
- }
-
- return objects;
- }
-
- @Override
- public boolean setExecutable(final boolean readable, final boolean ownerOnly) throws FileSystemException {
- try {
- return exists() ? doSetExecutable(readable, ownerOnly) : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/set-executable.error", fileName, exc);
- }
- }
-
- private void setFileType(final FileType type) {
- if (type != null && type != FileType.IMAGINARY) {
- try {
- fileName.setType(type);
- } catch (final FileSystemException e) {
- throw new RuntimeException(e.getMessage());
- }
- }
- this.type = type;
- }
-
- @Override
- public boolean setReadable(final boolean readable, final boolean ownerOnly) throws FileSystemException {
- try {
- return exists() ? doSetReadable(readable, ownerOnly) : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/set-readable.error", fileName, exc);
- }
- }
-
- // --- OPERATIONS ---
-
- @Override
- public boolean setWritable(final boolean readable, final boolean ownerOnly) throws FileSystemException {
- try {
- return exists() ? doSetWritable(readable, ownerOnly) : false;
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/set-writeable.error", fileName, exc);
- }
- }
-
- /**
- * Returns the URI as a String.
- *
- * @return Returns the URI as a String.
- */
- @Override
- public String toString() {
- return fileName.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;
+
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.vfs2.Capability;
+import org.apache.commons.vfs2.FileContent;
+import org.apache.commons.vfs2.FileContentInfoFactory;
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileNotFolderException;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSelector;
+import org.apache.commons.vfs2.FileSystem;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.FileUtil;
+import org.apache.commons.vfs2.NameScope;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.Selectors;
+import org.apache.commons.vfs2.VFS;
+import org.apache.commons.vfs2.operations.DefaultFileOperations;
+import org.apache.commons.vfs2.operations.FileOperations;
+import org.apache.commons.vfs2.util.FileObjectUtils;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+
+/**
+ * A partial file object implementation.
+ *
+ * TODO - Chop this class up - move all the protected methods to several interfaces, so that structure and content can
+ * be separately overridden.
+ *
+ * <p>
+ * TODO - Check caps in methods like getChildren(), etc, and give better error messages (eg 'this file type does not
+ * support listing children', vs 'this is not a folder')
+ * </p>
+ *
+ * @param <AFS> An AbstractFileSystem subclass
+ */
+public abstract class AbstractFileObject<AFS extends AbstractFileSystem> implements FileObject {
+
+ /**
+ * Same as {@link BufferedInputStream}.
+ */
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+ private static final FileName[] EMPTY_FILE_ARRAY = {};
+
+ private static final int INITIAL_LIST_SIZE = 5;
+
+ /**
+ * Traverses a file.
+ */
+ private static void traverse(final DefaultFileSelectorInfo fileInfo, final FileSelector selector,
+ final boolean depthwise, final List<FileObject> selected) throws Exception {
+ // Check the file itself
+ final FileObject file = fileInfo.getFile();
+ final int index = selected.size();
+
+ // If the file is a folder, traverse it
+ if (file.getType().hasChildren() && selector.traverseDescendents(fileInfo)) {
+ final int curDepth = fileInfo.getDepth();
+ fileInfo.setDepth(curDepth + 1);
+
+ // Traverse the children
+ final FileObject[] children = file.getChildren();
+ for (final FileObject child : children) {
+ fileInfo.setFile(child);
+ traverse(fileInfo, selector, depthwise, selected);
+ }
+
+ fileInfo.setFile(file);
+ fileInfo.setDepth(curDepth);
+ }
+
+ // Add the file if doing depthwise traversal
+ if (selector.includeFile(fileInfo)) {
+ if (depthwise) {
+ // Add this file after its descendants
+ selected.add(file);
+ } else {
+ // Add this file before its descendants
+ selected.add(index, file);
+ }
+ }
+ }
+ private final AbstractFileName fileName;
+
+ private final AFS fileSystem;
+ private FileContent content;
+ // Cached info
+ private boolean attached;
+
+ private FileType type;
+ private FileObject parent;
+
+ // Changed to hold only the name of the children and let the object
+ // go into the global files cache
+ // private FileObject[] children;
+ private FileName[] children;
+
+ private List<Object> objects;
+
+ /**
+ * FileServices instance.
+ */
+ private FileOperations operations;
+
+ /**
+ *
+ * @param name the file name - muse be an instance of {@link AbstractFileName}
+ * @param fileSystem the file system
+ * @throws ClassCastException if {@code name} is not an instance of {@link AbstractFileName}
+ */
+ protected AbstractFileObject(final AbstractFileName name, final AFS fileSystem) {
+ this.fileName = name;
+ this.fileSystem = fileSystem;
+ fileSystem.fileObjectHanded(this);
+ }
+
+ /**
+ * Attaches to the file.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ private void attach() throws FileSystemException {
+ synchronized (fileSystem) {
+ if (attached) {
+ return;
+ }
+
+ try {
+ // Attach and determine the file type
+ doAttach();
+ attached = true;
+ // now the type could already be injected by doAttach (e.g from parent to child)
+
+ /*
+ * VFS-210: determine the type when really asked fore if (type == null) { setFileType(doGetType()); } if
+ * (type == null) { setFileType(FileType.IMAGINARY); }
+ */
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/get-type.error", exc, fileName);
+ }
+
+ // fs.fileAttached(this);
+ }
+ }
+
+ /**
+ * Queries the object if a simple rename to the file name of {@code newfile} is possible.
+ *
+ * @param newfile the new file name
+ * @return true if rename is possible
+ */
+ @Override
+ public boolean canRenameTo(final FileObject newfile) {
+ return fileSystem == newfile.getFileSystem();
+ }
+
+ /**
+ * Notifies the file that its children have changed.
+ *
+ * @param childName The name of the child.
+ * @param newType The type of the child.
+ * @throws Exception if an error occurs.
+ */
+ protected void childrenChanged(final FileName childName, final FileType newType) throws Exception {
+ // TODO - this may be called when not attached
+
+ if (children != null && childName != null && newType != null) {
+ // TODO - figure out if children[] can be replaced by list
+ final ArrayList<FileName> list = new ArrayList<>(Arrays.asList(children));
+ if (newType.equals(FileType.IMAGINARY)) {
+ list.remove(childName);
+ } else {
+ list.add(childName);
+ }
+ children = new FileName[list.size()];
+ list.toArray(children);
+ }
+
+ // removeChildrenCache();
+ onChildrenChanged(childName, newType);
+ }
+
+ /**
+ * Closes this file, and its content.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void close() throws FileSystemException {
+ FileSystemException exc = null;
+
+ synchronized (fileSystem) {
+ // Close the content
+ if (content != null) {
+ try {
+ content.close();
+ content = null;
+ } catch (final FileSystemException e) {
+ exc = e;
+ }
+ }
+
+ // Detach from the file
+ try {
+ detach();
+ } catch (final Exception e) {
+ exc = new FileSystemException("vfs.provider/close.error", fileName, e);
+ }
+
+ if (exc != null) {
+ throw exc;
+ }
+ }
+ }
+
+ /**
+ * Compares two FileObjects (ignores case).
+ *
+ * @param file the object to compare.
+ * @return a negative integer, zero, or a positive integer when this object is less than, equal to, or greater than
+ * the given object.
+ */
+ @Override
+ public int compareTo(final FileObject file) {
+ if (file == null) {
+ return 1;
+ }
+ return this.toString().compareToIgnoreCase(file.toString());
+ }
+
+ /**
+ * Copies another file to this file.
+ *
+ * @param file The FileObject to copy.
+ * @param selector The FileSelector.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void copyFrom(final FileObject file, final FileSelector selector) throws FileSystemException {
+ if (!FileObjectUtils.exists(file)) {
+ throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
+ }
+
+ // Locate the files to copy across
+ final ArrayList<FileObject> files = new ArrayList<>();
+ file.findFiles(selector, false, files);
+
+ // Copy everything across
+ for (final FileObject srcFile : files) {
+ // Determine the destination file
+ final String relPath = file.getName().getRelativeName(srcFile.getName());
+ final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);
+
+ // Clean up the destination file, if necessary
+ if (FileObjectUtils.exists(destFile) && destFile.getType() != srcFile.getType()) {
+ // The destination file exists, and is not of the same type,
+ // so delete it
+ // TODO - add a pluggable policy for deleting and overwriting existing files
+ destFile.deleteAll();
+ }
+
+ // Copy across
+ try {
+ if (srcFile.getType().hasContent()) {
+ FileUtil.copyContent(srcFile, destFile);
+ } else if (srcFile.getType().hasChildren()) {
+ destFile.createFolder();
+ }
+ } catch (final IOException e) {
+ throw new FileSystemException("vfs.provider/copy-file.error", e, srcFile, destFile);
+ }
+ }
+ }
+
+ /**
+ * Creates this file, if it does not exist.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void createFile() throws FileSystemException {
+ synchronized (fileSystem) {
+ try {
+ // VFS-210: We do not want to trunc any existing file, checking for its existence is
+ // still required
+ if (exists() && !isFile()) {
+ throw new FileSystemException("vfs.provider/create-file.error", fileName);
+ }
+
+ if (!exists()) {
+ getOutputStream().close();
+ endOutput();
+ }
+ } catch (final RuntimeException re) {
+ throw re;
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/create-file.error", fileName, e);
+ }
+ }
+ }
+
+ /**
+ * Creates this folder, if it does not exist. Also creates any ancestor files which do not exist.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void createFolder() throws FileSystemException {
+ synchronized (fileSystem) {
+ // VFS-210: we create a folder only if it does not already exist. So this check should be safe.
+ if (getType().hasChildren()) {
+ // Already exists as correct type
+ return;
+ }
+ if (getType() != FileType.IMAGINARY) {
+ throw new FileSystemException("vfs.provider/create-folder-mismatched-type.error", fileName);
+ }
+ /*
+ * VFS-210: checking for writeable is not always possible as the security constraint might be more complex
+ * if (!isWriteable()) { throw new FileSystemException("vfs.provider/create-folder-read-only.error", name);
+ * }
+ */
+
+ // Traverse up the hierarchy and make sure everything is a folder
+ final FileObject parent = getParent();
+ if (parent != null) {
+ parent.createFolder();
+ }
+
+ try {
+ // Create the folder
+ doCreateFolder();
+
+ // Update cached info
+ handleCreate(FileType.FOLDER);
+ } catch (final RuntimeException re) {
+ throw re;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/create-folder.error", fileName, exc);
+ }
+ }
+ }
+
+ /**
+ * Deletes this file.
+ * <p>
+ * TODO - This will not fail if this is a non-empty folder.
+ * </p>
+ *
+ * @return true if this object has been deleted
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public boolean delete() throws FileSystemException {
+ return delete(Selectors.SELECT_SELF) > 0;
+ }
+
+ /**
+ * Deletes this file, and all children matching the {@code selector}.
+ *
+ * @param selector The FileSelector.
+ * @return the number of deleted files.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public int delete(final FileSelector selector) throws FileSystemException {
+ int nuofDeleted = 0;
+
+ /*
+ * VFS-210 if (getType() == FileType.IMAGINARY) { // File does not exist return nuofDeleted; }
+ */
+
+ // Locate all the files to delete
+ final ArrayList<FileObject> files = new ArrayList<>();
+ findFiles(selector, true, files);
+
+ // Delete 'em
+ final int count = files.size();
+ for (int i = 0; i < count; i++) {
+ final AbstractFileObject file = FileObjectUtils.getAbstractFileObject(files.get(i));
+ // file.attach();
+
+ // VFS-210: It seems impossible to me that findFiles will return a list with hidden files/directories
+ // in it, else it would not be hidden. Checking for the file-type seems ok in this case
+ // If the file is a folder, make sure all its children have been deleted
+ if (file.getType().hasChildren() && file.getChildren().length != 0) {
+ // Skip - as the selector forced us not to delete all files
+ continue;
+ }
+
+ // Delete the file
+ if (file.deleteSelf()) {
+ nuofDeleted++;
+ }
+ }
+
+ return nuofDeleted;
+ }
+
+ /**
+ * Deletes this file and all children. Shorthand for {@code delete(Selectors.SELECT_ALL)}
+ *
+ * @return the number of deleted files.
+ * @throws FileSystemException if an error occurs.
+ * @see #delete(FileSelector)
+ * @see Selectors#SELECT_ALL
+ */
+ @Override
+ public int deleteAll() throws FileSystemException {
+ return this.delete(Selectors.SELECT_ALL);
+ }
+
+ /**
+ * Deletes this file, once all its children have been deleted
+ *
+ * @return true if this file has been deleted
+ * @throws FileSystemException if an error occurs.
+ */
+ private boolean deleteSelf() throws FileSystemException {
+ synchronized (fileSystem) {
+ // Its possible to delete a read-only file if you have write-execute access to the directory
+
+ /*
+ * VFS-210 if (getType() == FileType.IMAGINARY) { // File does not exist return false; }
+ */
+
+ try {
+ // Delete the file
+ doDelete();
+
+ // Update cached info
+ handleDelete();
+ } catch (final RuntimeException re) {
+ throw re;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/delete.error", exc, fileName);
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Detaches this file, invalidating all cached info. This will force a call to {@link #doAttach} next time this file
+ * is used.
+ *
+ * @throws Exception if an error occurs.
+ */
+ private void detach() throws Exception {
+ synchronized (fileSystem) {
+ if (attached) {
+ try {
+ doDetach();
+ } finally {
+ attached = false;
+ setFileType(null);
+ parent = null;
+
+ // fs.fileDetached(this);
+
+ removeChildrenCache();
+ // children = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Attaches this file object to its file resource.
+ * <p>
+ * This method is called before any of the doBlah() or onBlah() methods. Sub-classes can use this method to perform
+ * lazy initialisation.
+ * </p>
+ * <p>
+ * This implementation does nothing.
+ * </p>
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void doAttach() throws Exception {
+ // noop
+ }
+
+ /**
+ * Create a FileContent implementation.
+ *
+ * @return The FileContent.
+ * @throws FileSystemException if an error occurs.
+ * @since 2.0
+ */
+ protected FileContent doCreateFileContent() throws FileSystemException {
+ return new DefaultFileContent(this, getFileContentInfoFactory());
+ }
+
+ /**
+ * Creates this file as a folder. Is only called when:
+ * <ul>
+ * <li>{@link #doGetType} returns {@link FileType#IMAGINARY}.</li>
+ * <li>The parent folder exists and is writeable, or this file is the root of the file system.</li>
+ * </ul>
+ * This implementation throws an exception.
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void doCreateFolder() throws Exception {
+ throw new FileSystemException("vfs.provider/create-folder-not-supported.error");
+ }
+
+ /**
+ * Deletes the file. Is only called when:
+ * <ul>
+ * <li>{@link #doGetType} does not return {@link FileType#IMAGINARY}.</li>
+ * <li>{@link #doIsWriteable} returns true.</li>
+ * <li>This file has no children, if a folder.</li>
+ * </ul>
+ * This implementation throws an exception.
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void doDelete() throws Exception {
+ throw new FileSystemException("vfs.provider/delete-not-supported.error");
+ }
+
+ /**
+ * Detaches this file object from its file resource.
+ * <p>
+ * Called when this file is closed. Note that the file object may be reused later, so should be able to be
+ * reattached.
+ * </p>
+ * <p>
+ * This implementation does nothing.
+ * </p>
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void doDetach() throws Exception {
+ // noop
+ }
+
+ /**
+ * Returns the attributes of this file. Is only called if {@link #doGetType} does not return
+ * {@link FileType#IMAGINARY}.
+ * <p>
+ * This implementation always returns an empty map.
+ * </p>
+ *
+ * @return The attributes of the file.
+ * @throws Exception if an error occurs.
+ */
+ protected Map<String, Object> doGetAttributes() throws Exception {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns the certificates used to sign this file. Is only called if {@link #doGetType} does not return
+ * {@link FileType#IMAGINARY}.
+ * <p>
+ * This implementation always returns null.
+ * </p>
+ *
+ * @return The certificates used to sign the file.
+ * @throws Exception if an error occurs.
+ */
+ protected Certificate[] doGetCertificates() throws Exception {
+ return null;
+ }
+
+ /**
+ * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
+ * {@link FileType#FILE}.
+ *
+ * @return The size of the file in bytes.
+ * @throws Exception if an error occurs.
+ */
+ protected abstract long doGetContentSize() throws Exception;
+
+ /**
+ * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
+ * {@link FileType#FILE}.
+ * <p>
+ * It is guaranteed that there are no open output streams for this file when this method is called.
+ * </p>
+ * <p>
+ * The returned stream does not have to be buffered.
+ * </p>
+ *
+ * @return An InputStream to read the file content.
+ * @throws Exception if an error occurs.
+ */
+ protected InputStream doGetInputStream() throws Exception {
+ // Backward compatibility.
+ return doGetInputStream(DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
+ * {@link FileType#FILE}.
+ * <p>
+ * It is guaranteed that there are no open output streams for this file when this method is called.
+ * </p>
+ * <p>
+ * The returned stream does not have to be buffered.
+ * </p>
+ * @param bufferSize Buffer size hint.
+ * @return An InputStream to read the file content.
+ * @throws Exception if an error occurs.
+ */
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the last modified time of this file. Is only called if {@link #doGetType} does not return
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ *
+ * @return The last modification time.
+ * @throws Exception if an error occurs.
+ */
+ protected long doGetLastModifiedTime() throws Exception {
+ throw new FileSystemException("vfs.provider/get-last-modified-not-supported.error");
+ }
+
+ /**
+ * Creates an output stream to write the file content to. Is only called if:
+ * <ul>
+ * <li>{@link #doIsWriteable} returns true.
+ * <li>{@link #doGetType} returns {@link FileType#FILE}, or {@link #doGetType} returns {@link FileType#IMAGINARY},
+ * and the file's parent exists and is a folder.
+ * </ul>
+ * It is guaranteed that there are no open stream (input or output) for this file when this method is called.
+ * <p>
+ * The returned stream does not have to be buffered.
+ * </p>
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ *
+ * @param bAppend true if the file should be appended to, false if it should be overwritten.
+ * @return An OutputStream to write to the file.
+ * @throws Exception if an error occurs.
+ */
+ protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
+ throw new FileSystemException("vfs.provider/write-not-supported.error");
+ }
+
+ /**
+ * Creates access to the file for random i/o. Is only called if {@link #doGetType} returns {@link FileType#FILE}.
+ * <p>
+ * It is guaranteed that there are no open output streams for this file when this method is called.
+ * </p>
+ *
+ * @param mode The mode to access the file.
+ * @return The RandomAccessContext.
+ * @throws Exception if an error occurs.
+ */
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
+ throw new FileSystemException("vfs.provider/random-access-not-supported.error");
+ }
+
+ /**
+ * Determines the type of this file. Must not return null. The return value of this method is cached, so the
+ * implementation can be expensive.
+ *
+ * @return the type of the file.
+ * @throws Exception if an error occurs.
+ */
+ protected abstract FileType doGetType() throws Exception;
+
+ /**
+ * Determines if this file is executable. Is only called if {@link #doGetType} does not return
+ * {@link FileType#IMAGINARY}.
+ * <p>
+ * This implementation always returns false.
+ * </p>
+ *
+ * @return true if the file is executable, false otherwise.
+ * @throws Exception if an error occurs.
+ */
+ protected boolean doIsExecutable() throws Exception {
+ return false;
+ }
+
+ /**
+ * Determines if this file is hidden. Is only called if {@link #doGetType} does not return
+ * {@link FileType#IMAGINARY}.
+ * <p>
+ * This implementation always returns false.
+ * </p>
+ *
+ * @return true if the file is hidden, false otherwise.
+ * @throws Exception if an error occurs.
+ */
+ protected boolean doIsHidden() throws Exception {
+ return false;
+ }
+
+ /**
+ * Determines if this file can be read. Is only called if {@link #doGetType} does not return
+ * {@link FileType#IMAGINARY}.
+ * <p>
+ * This implementation always returns true.
+ * </p>
+ *
+ * @return true if the file is readable, false otherwise.
+ * @throws Exception if an error occurs.
+ */
+ protected boolean doIsReadable() throws Exception {
+ return true;
+ }
+
+ /**
+ * Checks if this fileObject is the same file as {@code destFile} just with a different name. E.g. for case
+ * insensitive file systems like windows.
+ *
+ * @param destFile The file to compare to.
+ * @return true if the FileObjects are the same.
+ * @throws FileSystemException if an error occurs.
+ */
+ protected boolean doIsSameFile(final FileObject destFile) throws FileSystemException {
+ return false;
+ }
+
+ /**
+ * Determines if this file is a symbolic link. Is only called if {@link #doGetType} does not return
+ * {@link FileType#IMAGINARY}.
+ * <p>
+ * This implementation always returns false.
+ * </p>
+ *
+ * @return true if the file is readable, false otherwise.
+ * @throws Exception if an error occurs.
+ * @since 2.4
+ */
+ protected boolean doIsSymbolicLink() throws Exception {
+ return false;
+ }
+
+ /**
+ * 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.
+ * </p>
+ *
+ * @return true if the file is writable.
+ * @throws Exception if an error occurs.
+ */
+ protected boolean doIsWriteable() throws Exception {
+ return true;
+ }
+
+ /**
+ * Lists the children of this file. Is only called if {@link #doGetType} returns {@link FileType#FOLDER}. The return
+ * value of this method is cached, so the implementation can be expensive.
+ *
+ * @return a possible empty String array if the file is a directory or null or an exception if the file is not a
+ * directory or can't be read.
+ * @throws Exception if an error occurs.
+ */
+ protected abstract String[] doListChildren() throws Exception;
+
+ /**
+ * Lists the children of this file.
+ * <p>
+ * Is only called if {@link #doGetType} returns {@link FileType#FOLDER}.
+ * </p>
+ * <p>
+ * The return value of this method is cached, so the implementation can be expensive.
+ * Other than {@code doListChildren} you could return FileObject's to e.g. reinitialize the type of the file.
+ * </p>
+ * <p>
+ * (Introduced for Webdav: "permission denied on resource" during getType())
+ * </p>
+ *
+ * @return The children of this FileObject.
+ * @throws Exception if an error occurs.
+ */
+ protected FileObject[] doListChildrenResolved() throws Exception {
+ return null;
+ }
+
+ /**
+ * Removes an attribute of this file.
+ * <p>
+ * Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * </p>
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ *
+ * @param attrName The name of the attribute to remove.
+ * @throws Exception if an error occurs.
+ * @since 2.0
+ */
+ protected void doRemoveAttribute(final String attrName) throws Exception {
+ throw new FileSystemException("vfs.provider/remove-attribute-not-supported.error");
+ }
+
+ /**
+ * Renames the file.
+ * <p>
+ * Is only called when:
+ * </p>
+ * <ul>
+ * <li>{@link #doIsWriteable} returns true.</li>
+ * </ul>
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ *
+ * @param newFile A FileObject with the new file name.
+ * @throws Exception if an error occurs.
+ */
+ protected void doRename(final FileObject newFile) throws Exception {
+ throw new FileSystemException("vfs.provider/rename-not-supported.error");
+ }
+
+ /**
+ * Sets an attribute of this file.
+ * <p>
+ * Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * </p>
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ *
+ * @param attrName The attribute name.
+ * @param value The value to be associated with the attribute name.
+ * @throws Exception if an error occurs.
+ */
+ protected void doSetAttribute(final String attrName, final Object value) throws Exception {
+ throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
+ }
+
+ /**
+ * Make the file executable.
+ * <p>
+ * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * </p>
+ * <p>
+ * This implementation returns false.
+ * </p>
+ *
+ * @param executable True to allow access, false to disallow.
+ * @param ownerOnly If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
+ * @return true if the operation succeeded.
+ * @throws Exception Any Exception thrown is wrapped in FileSystemException.
+ * @see #setExecutable(boolean, boolean)
+ * @since 2.1
+ */
+ protected boolean doSetExecutable(final boolean executable, final boolean ownerOnly) throws Exception {
+ return false;
+ }
+
+ /**
+ * Sets the last modified time of this file.
+ * <p>
+ * Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * </p>
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ *
+ * @param modtime The last modification time.
+ * @return true if the time was set.
+ * @throws Exception Any Exception thrown is wrapped in FileSystemException.
+ */
+ protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
+ throw new FileSystemException("vfs.provider/set-last-modified-not-supported.error");
+ }
+
+ /**
+ * Make the file or folder readable.
+ * <p>
+ * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * </p>
+ * <p>
+ * This implementation returns false.
+ * </p>
+ *
+ * @param readable True to allow access, false to disallow
+ * @param ownerOnly If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
+ * @return true if the operation succeeded
+ * @throws Exception Any Exception thrown is wrapped in FileSystemException.
+ * @see #setReadable(boolean, boolean)
+ * @since 2.1
+ */
+ protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception {
+ return false;
+ }
+
+ /**
+ * Make the file or folder writeable.
+ * <p>
+ * Only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+ * </p>
+ *
+ * @param writable True to allow access, false to disallow
+ * @param ownerOnly If {@code true}, the permission applies only to the owner; otherwise, it applies to everybody.
+ * @return true if the operation succeeded
+ * @throws Exception Any Exception thrown is wrapped in FileSystemException.
+ * @see #setWritable(boolean, boolean)
+ * @since 2.1
+ */
+ protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception {
+ return false;
+ }
+
+ /**
+ * Called when the output stream for this file is closed.
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void endOutput() throws Exception {
+ if (getType() == FileType.IMAGINARY) {
+ // File was created
+ handleCreate(FileType.FILE);
+ } else {
+ // File has changed
+ onChange();
+ }
+ }
+
+ /**
+ * Determines if the file exists.
+ *
+ * @return true if the file exists, false otherwise,
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public boolean exists() throws FileSystemException {
+ return getType() != FileType.IMAGINARY;
+ }
+
+ private FileName[] extractNames(final FileObject[] objects) {
+ if (objects == null) {
+ return null;
+ }
+
+ final FileName[] names = new FileName[objects.length];
+ for (int iterObjects = 0; iterObjects < objects.length; iterObjects++) {
+ names[iterObjects] = objects[iterObjects].getName();
+ }
+
+ return names;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ fileSystem.fileObjectDestroyed(this);
+
+ super.finalize();
+ }
+
+ /**
+ * Finds the set of matching descendants of this file, in depthwise order.
+ *
+ * @param selector The FileSelector.
+ * @return list of files or null if the base file (this object) do not exist
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject[] findFiles(final FileSelector selector) throws FileSystemException {
+ final List<FileObject> list = this.listFiles(selector);
+ return list == null ? null : list.toArray(new FileObject[list.size()]);
+ }
+
+ /**
+ * Traverses the descendants of this file, and builds a list of selected files.
+ *
+ * @param selector The FileSelector.
+ * @param depthwise if true files are added after their descendants, before otherwise.
+ * @param selected A List of the located FileObjects.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void findFiles(final FileSelector selector, final boolean depthwise, final List<FileObject> selected)
+ throws FileSystemException {
+ try {
+ if (exists()) {
+ // Traverse starting at this file
+ final DefaultFileSelectorInfo info = new DefaultFileSelectorInfo();
+ info.setBaseFolder(this);
+ info.setDepth(0);
+ info.setFile(this);
+ traverse(info, selector, depthwise, selected);
+ }
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/find-files.error", fileName, e);
+ }
+ }
+
+ /**
+ * Returns the file system this file belongs to.
+ *
+ * @return The FileSystem this file is associated with.
+ */
+ protected AFS getAbstractFileSystem() {
+ return fileSystem;
+ }
+
+ /**
+ * Returns a child of this file.
+ *
+ * @param name The name of the child to locate.
+ * @return The FileObject for the file or null if the child does not exist.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject getChild(final String name) throws FileSystemException {
+ // TODO - use a hashtable when there are a large number of children
+ final FileObject[] children = getChildren();
+ for (final FileObject element : children) {
+ final FileName child = element.getName();
+ // TODO - use a comparator to compare names
+ if (child.getBaseName().equals(name)) {
+ return resolveFile(child);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the children of the file.
+ *
+ * @return an array of FileObjects, one per child.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject[] getChildren() throws FileSystemException {
+ synchronized (fileSystem) {
+ // VFS-210
+ if (!fileSystem.hasCapability(Capability.LIST_CHILDREN)) {
+ throw new FileNotFolderException(fileName);
+ }
+
+ /*
+ * VFS-210 if (!getType().hasChildren()) { throw new
+ * FileSystemException("vfs.provider/list-children-not-folder.error", name); }
+ */
+ attach();
+
+ // Use cached info, if present
+ if (children != null) {
+ return resolveFiles(children);
+ }
+
+ // allow the filesystem to return resolved children. e.g. prefill type for webdav
+ FileObject[] childrenObjects;
+ try {
+ childrenObjects = doListChildrenResolved();
+ children = extractNames(childrenObjects);
+ } catch (final FileSystemException exc) {
+ // VFS-210
+ throw exc;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
+ }
+
+ if (childrenObjects != null) {
+ return childrenObjects;
+ }
+
+ // List the children
+ final String[] files;
+ try {
+ files = doListChildren();
+ } catch (final FileSystemException exc) {
+ // VFS-210
+ throw exc;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/list-children.error", exc, fileName);
+ }
+
+ if (files == null) {
+ // VFS-210
+ // honor the new doListChildren contract
+ // return null;
+ throw new FileNotFolderException(fileName);
+ } else if (files.length == 0) {
+ // No children
+ children = EMPTY_FILE_ARRAY;
+ } else {
+ // Create file objects for the children
+ final FileName[] cache = new FileName[files.length];
+ for (int i = 0; i < files.length; i++) {
+ final String file = files[i];
+ cache[i] = fileSystem.getFileSystemManager().resolveName(fileName, file, NameScope.CHILD);
+ }
+ // VFS-285: only assign the children file names after all of them have been
+ // resolved successfully to prevent an inconsistent internal state
+ children = cache;
+ }
+
+ return resolveFiles(children);
+ }
+ }
+
+ /**
+ * Returns the file's content.
+ *
+ * @return the FileContent for this FileObject.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileContent getContent() throws FileSystemException {
+ synchronized (fileSystem) {
+ attach();
+ if (content == null) {
+ content = doCreateFileContent();
+ }
+ return content;
+ }
+ }
+
+ /**
+ * Creates the FileContentInfo factory.
+ *
+ * @return The FileContentInfoFactory.
+ */
+ protected FileContentInfoFactory getFileContentInfoFactory() {
+ return fileSystem.getFileSystemManager().getFileContentInfoFactory();
+ }
+
+ /**
+ * @return FileOperations interface that provides access to the operations API.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileOperations getFileOperations() throws FileSystemException {
+ if (operations == null) {
+ operations = new DefaultFileOperations(this);
+ }
+
+ return operations;
+ }
+
+ /**
+ * Returns the file system this file belongs to.
+ *
+ * @return The FileSystem this file is associated with.
+ */
+ @Override
+ public FileSystem getFileSystem() {
+ return fileSystem;
+ }
+
+ /**
+ * Returns an input stream to use to read the content of the file.
+ *
+ * @return The InputStream to access this file's content.
+ * @throws FileSystemException if an error occurs.
+ */
+ public InputStream getInputStream() throws FileSystemException {
+ return getInputStream(DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Returns an input stream to use to read the content of the file.
+ *
+ * @param bufferSize buffer size hint.
+ * @return The InputStream to access this file's content.
+ * @throws FileSystemException if an error occurs.
+ */
+ public InputStream getInputStream(final int bufferSize) throws FileSystemException {
+ // Get the raw input stream
+ try {
+ return doGetInputStream(bufferSize);
+ } catch (final org.apache.commons.vfs2.FileNotFoundException exc) {
+ throw new org.apache.commons.vfs2.FileNotFoundException(fileName, exc);
+ } catch (final FileNotFoundException exc) {
+ throw new org.apache.commons.vfs2.FileNotFoundException(fileName, exc);
+ } catch (final FileSystemException exc) {
+ throw exc;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/read.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Returns the name of the file.
+ *
+ * @return The FileName, never {@code null}.
+ */
+ @Override
+ public FileName getName() {
+ return fileName;
+ }
+
+ /**
+ * Prepares this file for writing. Makes sure it is either a file, or its parent folder exists. Returns an output
+ * stream to use to write the content of the file to.
+ *
+ * @return An OutputStream where the new contents of the file can be written.
+ * @throws FileSystemException if an error occurs.
+ */
+ public OutputStream getOutputStream() throws FileSystemException {
+ return getOutputStream(false);
+ }
+
+ /**
+ * Prepares this file for writing. Makes sure it is either a file, or its parent folder exists. Returns an output
+ * stream to use to write the content of the file to.
+ *
+ * @param bAppend true when append to the file.
+ * Note: If the underlying file system does not support appending, a FileSystemException is thrown.
+ * @return An OutputStream where the new contents of the file can be written.
+ * @throws FileSystemException if an error occurs; for example:
+ * bAppend is true, and the underlying FileSystem does not support it
+ */
+ public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException {
+ /*
+ * VFS-210 if (getType() != FileType.IMAGINARY && !getType().hasContent()) { throw new
+ * FileSystemException("vfs.provider/write-not-file.error", name); } if (!isWriteable()) { throw new
+ * FileSystemException("vfs.provider/write-read-only.error", name); }
+ */
+
+ if (bAppend && !fileSystem.hasCapability(Capability.APPEND_CONTENT)) {
+ throw new FileSystemException("vfs.provider/write-append-not-supported.error", fileName);
+ }
+
+ if (getType() == FileType.IMAGINARY) {
+ // Does not exist - make sure parent does
+ final FileObject parent = getParent();
+ if (parent != null) {
+ parent.createFolder();
+ }
+ }
+
+ // Get the raw output stream
+ try {
+ return doGetOutputStream(bAppend);
+ } catch (final RuntimeException re) {
+ throw re;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/write.error", exc, fileName);
+ }
+ }
+
+ /**
+ * Returns the parent of the file.
+ *
+ * @return the parent FileObject.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject getParent() throws FileSystemException {
+ if (this.compareTo(fileSystem.getRoot()) == 0) // equals is not implemented :-/
+ {
+ if (fileSystem.getParentLayer() == null) {
+ // Root file has no parent
+ return null;
+ }
+ // Return the parent of the parent layer
+ return fileSystem.getParentLayer().getParent();
+ }
+
+ synchronized (fileSystem) {
+ // Locate the parent of this file
+ if (parent == null) {
+ final FileName name = fileName.getParent();
+ if (name == null) {
+ return null;
+ }
+ parent = fileSystem.resolveFile(name);
+ }
+ return parent;
+ }
+ }
+
+ /**
+ * Returns the receiver as a URI String for public display, like, without a password.
+ *
+ * @return A URI String without a password, never {@code null}.
+ */
+ @Override
+ public String getPublicURIString() {
+ return fileName.getFriendlyURI();
+ }
+
+ /**
+ * Returns an input/output stream to use to read and write the content of the file in and random manner.
+ *
+ * @param mode The RandomAccessMode.
+ * @return The RandomAccessContent.
+ * @throws FileSystemException if an error occurs.
+ */
+ public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException {
+ /*
+ * VFS-210 if (!getType().hasContent()) { throw new FileSystemException("vfs.provider/read-not-file.error",
+ * name); }
+ */
+
+ if (mode.requestRead()) {
+ if (!fileSystem.hasCapability(Capability.RANDOM_ACCESS_READ)) {
+ throw new FileSystemException("vfs.provider/random-access-read-not-supported.error");
+ }
+ if (!isReadable()) {
+ throw new FileSystemException("vfs.provider/read-not-readable.error", fileName);
+ }
+ }
+
+ if (mode.requestWrite()) {
+ if (!fileSystem.hasCapability(Capability.RANDOM_ACCESS_WRITE)) {
+ throw new FileSystemException("vfs.provider/random-access-write-not-supported.error");
+ }
+ if (!isWriteable()) {
+ throw new FileSystemException("vfs.provider/write-read-only.error", fileName);
+ }
+ }
+
+ // Get the raw input stream
+ try {
+ return doGetRandomAccessContent(mode);
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/random-access.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Returns the file's type.
+ *
+ * @return The FileType.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileType getType() throws FileSystemException {
+ synchronized (fileSystem) {
+ attach();
+
+ // VFS-210: get the type only if requested for
+ try {
+ if (type == null) {
+ setFileType(doGetType());
+ }
+ if (type == null) {
+ setFileType(FileType.IMAGINARY);
+ }
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/get-type.error", e, fileName);
+ }
+
+ return type;
+ }
+ }
+
+ /**
+ * Returns a URL representation of the file.
+ *
+ * @return The URL representation of the file.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public URL getURL() throws FileSystemException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<URL>() {
+ @Override
+ public URL run() throws MalformedURLException, FileSystemException {
+ final StringBuilder buf = new StringBuilder();
+ final String scheme = UriParser.extractScheme(VFS.getManager().getSchemes(), fileName.getURI(), buf);
+ return new URL(scheme, "", -1, buf.toString(),
+ new DefaultURLStreamHandler(fileSystem.getContext(), fileSystem.getFileSystemOptions()));
+ }
+ });
+ } catch (final PrivilegedActionException e) {
+ throw new FileSystemException("vfs.provider/get-url.error", fileName, e.getException());
+ }
+ }
+
+ /**
+ * Called when this file is changed.
+ * <p>
+ * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}.
+ * </p>
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void handleChanged() throws Exception {
+ // Notify the file system
+ fileSystem.fireFileChanged(this);
+ }
+
+ /**
+ * Called when this file is created. Updates cached info and notifies the parent and file system.
+ *
+ * @param newType The type of the file.
+ * @throws Exception if an error occurs.
+ */
+ protected void handleCreate(final FileType newType) throws Exception {
+ synchronized (fileSystem) {
+ if (attached) {
+ // Fix up state
+ injectType(newType);
+
+ removeChildrenCache();
+
+ // Notify subclass
+ onChange();
+ }
+
+ // Notify parent that its child list may no longer be valid
+ notifyParent(this.getName(), newType);
+
+ // Notify the file system
+ fileSystem.fireFileCreated(this);
+ }
+ }
+
+ /**
+ * Called when this file is deleted. Updates cached info and notifies subclasses, parent and file system.
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void handleDelete() throws Exception {
+ synchronized (fileSystem) {
+ if (attached) {
+ // Fix up state
+ injectType(FileType.IMAGINARY);
+ removeChildrenCache();
+
+ // Notify subclass
+ onChange();
+ }
+
+ // Notify parent that its child list may no longer be valid
+ notifyParent(this.getName(), FileType.IMAGINARY);
+
+ // Notify the file system
+ fileSystem.fireFileDeleted(this);
+ }
+ }
+
+ /**
+ * This method is meant to add an object where this object holds a strong reference then. E.g. a archive-file system
+ * creates a list of all children and they shouldn't get garbage collected until the container is garbage collected
+ *
+ * @param strongRef The Object to add.
+ */
+ // TODO should this be a FileObject?
+ public void holdObject(final Object strongRef) {
+ if (objects == null) {
+ objects = new ArrayList<>(INITIAL_LIST_SIZE);
+ }
+ objects.add(strongRef);
+ }
+
+ protected void injectType(final FileType fileType) {
+ setFileType(fileType);
+ }
+
+ /**
+ * Check if the internal state is "attached".
+ *
+ * @return true if this is the case
+ */
+ @Override
+ public boolean isAttached() {
+ return attached;
+ }
+
+ /**
+ * Check if the content stream is open.
+ *
+ * @return true if this is the case
+ */
+ @Override
+ public boolean isContentOpen() {
+ if (content == null) {
+ return false;
+ }
+
+ return content.isOpen();
+ }
+
+ /**
+ * Determines if this file is executable.
+ *
+ * @return {@code true} if this file is executable, {@code false} if not.
+ * @throws FileSystemException On error determining if this file exists.
+ */
+ @Override
+ public boolean isExecutable() throws FileSystemException {
+ try {
+ return exists() ? doIsExecutable() : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/check-is-executable.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Checks if this file is a regular file by using its file type.
+ *
+ * @return true if this file is a regular file.
+ * @throws FileSystemException if an error occurs.
+ * @see #getType()
+ * @see FileType#FILE
+ */
+ @Override
+ public boolean isFile() throws FileSystemException {
+ // Use equals instead of == to avoid any class loader worries.
+ return FileType.FILE.equals(this.getType());
+ }
+
+ /**
+ * Checks if this file is a folder by using its file type.
+ *
+ * @return true if this file is a regular file.
+ * @throws FileSystemException if an error occurs.
+ * @see #getType()
+ * @see FileType#FOLDER
+ */
+ @Override
+ public boolean isFolder() throws FileSystemException {
+ // Use equals instead of == to avoid any class loader worries.
+ return FileType.FOLDER.equals(this.getType());
+ }
+
+ /**
+ * Determines if this file can be read.
+ *
+ * @return true if the file is a hidden file, false otherwise.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public boolean isHidden() throws FileSystemException {
+ try {
+ return exists() ? doIsHidden() : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/check-is-hidden.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Determines if this file can be read.
+ *
+ * @return true if the file can be read, false otherwise.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public boolean isReadable() throws FileSystemException {
+ try {
+ return exists() ? doIsReadable() : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/check-is-readable.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Checks if this fileObject is the same file as {@code destFile} just with a different name. E.g. for case
+ * insensitive file systems like windows.
+ *
+ * @param destFile The file to compare to.
+ * @return true if the FileObjects are the same.
+ * @throws FileSystemException if an error occurs.
+ */
+ protected boolean isSameFile(final FileObject destFile) throws FileSystemException {
+ attach();
+ return doIsSameFile(destFile);
+ }
+
+ /**
+ * Determines if this file can be read.
+ *
+ * @return true if the file can be read, false otherwise.
+ * @throws FileSystemException if an error occurs.
+ * @since 2.4
+ */
+ @Override
+ public boolean isSymbolicLink() throws FileSystemException {
+ try {
+ return exists() ? doIsSymbolicLink() : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/check-is-symbolic-link.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Determines if this file can be written to.
+ *
+ * @return true if the file can be written to, false otherwise.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public boolean isWriteable() throws FileSystemException {
+ try {
+ if (exists()) {
+ return doIsWriteable();
+ }
+ final FileObject parent = getParent();
+ if (parent != null) {
+ return parent.isWriteable();
+ }
+ return true;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/check-is-writeable.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Returns an iterator over a set of all FileObject in this file object.
+ *
+ * @return an Iterator.
+ */
+ @Override
+ public Iterator<FileObject> iterator() {
+ try {
+ return listFiles(Selectors.SELECT_ALL).iterator();
+ } catch (final FileSystemException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Lists the set of matching descendants of this file, in depthwise order.
+ *
+ * @param selector The FileSelector.
+ * @return list of files or null if the base file (this object) do not exist or the {@code selector} is null
+ * @throws FileSystemException if an error occurs.
+ */
+ public List<FileObject> listFiles(final FileSelector selector) throws FileSystemException {
+ if (!exists() || selector == null) {
+ return null;
+ }
+
+ final ArrayList<FileObject> list = new ArrayList<>();
+ this.findFiles(selector, true, list);
+ return list;
+ }
+
+ /**
+ * Moves (rename) the file to another one.
+ *
+ * @param destFile The target FileObject.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void moveTo(final FileObject destFile) throws FileSystemException {
+ if (canRenameTo(destFile)) {
+ if (!getParent().isWriteable()) {
+ throw new FileSystemException("vfs.provider/rename-parent-read-only.error", getName(),
+ getParent().getName());
+ }
+ } else {
+ if (!isWriteable()) {
+ throw new FileSystemException("vfs.provider/rename-read-only.error", getName());
+ }
+ }
+
+ if (destFile.exists() && !isSameFile(destFile)) {
+ destFile.deleteAll();
+ // throw new FileSystemException("vfs.provider/rename-dest-exists.error", destFile.getName());
+ }
+
+ if (canRenameTo(destFile)) {
+ // issue rename on same filesystem
+ try {
+ attach();
+ // remember type to avoid attach
+ final FileType srcType = getType();
+
+ doRename(destFile);
+
+ FileObjectUtils.getAbstractFileObject(destFile).handleCreate(srcType);
+ destFile.close(); // now the destFile is no longer imaginary. force reattach.
+
+ handleDelete(); // fire delete-events. This file-object (src) is like deleted.
+ } catch (final RuntimeException re) {
+ throw re;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/rename.error", exc, getName(), destFile.getName());
+ }
+ } else {
+ // different fs - do the copy/delete stuff
+
+ destFile.copyFrom(this, Selectors.SELECT_SELF);
+
+ if ((destFile.getType().hasContent()
+ && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FILE)
+ || destFile.getType().hasChildren()
+ && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FOLDER))
+ && fileSystem.hasCapability(Capability.GET_LAST_MODIFIED)) {
+ destFile.getContent().setLastModifiedTime(this.getContent().getLastModifiedTime());
+ }
+
+ deleteSelf();
+ }
+
+ }
+
+ /**
+ * Clled after this file-object closed all its streams.
+ */
+ protected void notifyAllStreamsClosed() {
+ // noop
+ }
+
+ /**
+ * Notify the parent of a change to its children, when a child is created or deleted.
+ *
+ * @param childName The name of the child.
+ * @param newType The type of the child.
+ * @throws Exception if an error occurs.
+ */
+ private void notifyParent(final FileName childName, final FileType newType) throws Exception {
+ if (parent == null) {
+ final FileName parentName = fileName.getParent();
+ if (parentName != null) {
+ // Locate the parent, if it is cached
+ parent = fileSystem.getFileFromCache(parentName);
+ }
+ }
+
+ if (parent != null) {
+ FileObjectUtils.getAbstractFileObject(parent).childrenChanged(childName, newType);
+ }
+ }
+
+ /**
+ * Called when the type or content of this file changes.
+ * <p>
+ * This implementation does nothing.
+ * </p>
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void onChange() throws Exception {
+ // noop
+ }
+
+ /**
+ * Called when the children of this file change. Allows subclasses to refresh any cached information about the
+ * children of this file.
+ * <p>
+ * This implementation does nothing.
+ * </p>
+ *
+ * @param child The name of the child that changed.
+ * @param newType The type of the file.
+ * @throws Exception if an error occurs.
+ */
+ protected void onChildrenChanged(final FileName child, final FileType newType) throws Exception {
+ // noop
+ }
+
+ /**
+ * This will prepare the fileObject to get resynchronized with the underlying file system if required.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void refresh() throws FileSystemException {
+ // Detach from the file
+ try {
+ detach();
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/resync.error", fileName, e);
+ }
+ }
+
+ private void removeChildrenCache() {
+ children = null;
+ }
+
+ private FileObject resolveFile(final FileName child) throws FileSystemException {
+ return fileSystem.resolveFile(child);
+ }
+
+ /**
+ * Finds a file, relative to this file.
+ *
+ * @param path The path of the file to locate. Can either be a relative path, which is resolved relative to this
+ * file, or an absolute path, which is resolved relative to the file system that contains this file.
+ * @return The FileObject.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject resolveFile(final String path) throws FileSystemException {
+ final FileName otherName = fileSystem.getFileSystemManager().resolveName(fileName, path);
+ return fileSystem.resolveFile(otherName);
+ }
+
+ /**
+ * Returns a child by name.
+ *
+ * @param name The name of the child to locate.
+ * @param scope the NameScope.
+ * @return The FileObject for the file or null if the child does not exist.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileObject resolveFile(final String name, final NameScope scope) throws FileSystemException {
+ // return fs.resolveFile(this.name.resolveName(name, scope));
+ return fileSystem.resolveFile(fileSystem.getFileSystemManager().resolveName(this.fileName, name, scope));
+ }
+
+ private FileObject[] resolveFiles(final FileName[] children) throws FileSystemException {
+ if (children == null) {
+ return null;
+ }
+
+ final FileObject[] objects = new FileObject[children.length];
+ for (int iterChildren = 0; iterChildren < children.length; iterChildren++) {
+ objects[iterChildren] = resolveFile(children[iterChildren]);
+ }
+
+ return objects;
+ }
+
+ @Override
+ public boolean setExecutable(final boolean readable, final boolean ownerOnly) throws FileSystemException {
+ try {
+ return exists() ? doSetExecutable(readable, ownerOnly) : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/set-executable.error", fileName, exc);
+ }
+ }
+
+ private void setFileType(final FileType type) {
+ if (type != null && type != FileType.IMAGINARY) {
+ try {
+ fileName.setType(type);
+ } catch (final FileSystemException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ this.type = type;
+ }
+
+ @Override
+ public boolean setReadable(final boolean readable, final boolean ownerOnly) throws FileSystemException {
+ try {
+ return exists() ? doSetReadable(readable, ownerOnly) : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/set-readable.error", fileName, exc);
+ }
+ }
+
+ // --- OPERATIONS ---
+
+ @Override
+ public boolean setWritable(final boolean readable, final boolean ownerOnly) throws FileSystemException {
+ try {
+ return exists() ? doSetWritable(readable, ownerOnly) : false;
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/set-writeable.error", fileName, exc);
+ }
+ }
+
+ /**
+ * Returns the URI as a String.
+ *
+ * @return Returns the URI as a String.
+ */
+ @Override
+ public String toString() {
+ return fileName.getURI();
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DefaultFileContent.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DefaultFileContent.java
index 870f4ea..009a87a 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DefaultFileContent.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DefaultFileContent.java
@@ -1,813 +1,812 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.cert.Certificate;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.vfs2.FileContent;
-import org.apache.commons.vfs2.FileContentInfo;
-import org.apache.commons.vfs2.FileContentInfoFactory;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.util.MonitorInputStream;
-import org.apache.commons.vfs2.util.MonitorOutputStream;
-import org.apache.commons.vfs2.util.MonitorRandomAccessContent;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-
-/**
- * The content of a file.
- */
-public final class DefaultFileContent implements FileContent {
-
- /*
- * static final int STATE_NONE = 0; static final int STATE_READING = 1; static final int STATE_WRITING = 2; static
- * final int STATE_RANDOM_ACCESS = 3;
- */
-
- static final int STATE_CLOSED = 0;
- static final int STATE_OPENED = 1;
-
- /**
- * The default buffer size for {@link #write(OutputStream)}
- */
- private static final int WRITE_BUFFER_SIZE = 4096;
-
- private final AbstractFileObject fileObject;
- private Map<String, Object> attrs;
- private Map<String, Object> roAttrs;
- private FileContentInfo fileContentInfo;
- private final FileContentInfoFactory fileContentInfoFactory;
-
- private final ThreadLocal<FileContentThreadData> threadLocal = new ThreadLocal<>();
- private boolean resetAttributes;
-
- /**
- * Counts open streams for this file.
- */
- private int openStreams;
-
- public DefaultFileContent(final AbstractFileObject file, final FileContentInfoFactory fileContentInfoFactory) {
- this.fileObject = file;
- this.fileContentInfoFactory = fileContentInfoFactory;
- }
-
- private FileContentThreadData getOrCreateThreadData() {
- FileContentThreadData data = this.threadLocal.get();
- if (data == null) {
- data = new FileContentThreadData();
- this.threadLocal.set(data);
- }
- return data;
- }
-
- void streamOpened() {
- synchronized (this) {
- openStreams++;
- }
- ((AbstractFileSystem) fileObject.getFileSystem()).streamOpened();
- }
-
- void streamClosed() {
- synchronized (this) {
- if (openStreams > 0) {
- openStreams--;
- if (openStreams < 1) {
- fileObject.notifyAllStreamsClosed();
- }
- }
- }
- ((AbstractFileSystem) fileObject.getFileSystem()).streamClosed();
- }
-
- /**
- * Returns the file that this is the content of.
- *
- * @return the FileObject.
- */
- @Override
- public FileObject getFile() {
- return fileObject;
- }
-
- /**
- * Returns the size of the content (in bytes).
- *
- * @return The size of the content (in bytes).
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public long getSize() throws FileSystemException {
- // Do some checking
- if (!fileObject.getType().hasContent()) {
- throw new FileSystemException("vfs.provider/get-size-not-file.error", fileObject);
- }
- /*
- * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
- * new FileSystemException("vfs.provider/get-size-write.error", file); }
- */
-
- try {
- // Get the size
- return fileObject.doGetContentSize();
- } catch (final Exception exc) {
- throw new FileSystemException("vfs.provider/get-size.error", exc, fileObject);
- }
- }
-
- /**
- * Returns the last-modified timestamp.
- *
- * @return The last modified timestamp.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public long getLastModifiedTime() throws FileSystemException {
- /*
- * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
- * new FileSystemException("vfs.provider/get-last-modified-writing.error", file); }
- */
- if (!fileObject.getType().hasAttributes()) {
- throw new FileSystemException("vfs.provider/get-last-modified-no-exist.error", fileObject);
- }
- try {
- return fileObject.doGetLastModifiedTime();
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/get-last-modified.error", fileObject, e);
- }
- }
-
- /**
- * Sets the last-modified timestamp.
- *
- * @param modTime The last modified timestamp.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void setLastModifiedTime(final long modTime) throws FileSystemException {
- /*
- * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
- * new FileSystemException("vfs.provider/set-last-modified-writing.error", file); }
- */
- if (!fileObject.getType().hasAttributes()) {
- throw new FileSystemException("vfs.provider/set-last-modified-no-exist.error", fileObject);
- }
- try {
- if (!fileObject.doSetLastModifiedTime(modTime)) {
- throw new FileSystemException("vfs.provider/set-last-modified.error", fileObject);
- }
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/set-last-modified.error", fileObject, e);
- }
- }
-
- /**
- * Checks if an attribute exists.
- *
- * @param attrName The name of the attribute to check.
- * @return true if the attribute is associated with the file.
- * @throws FileSystemException if an error occurs.
- * @since 2.0
- */
- @Override
- public boolean hasAttribute(final String attrName) throws FileSystemException {
- if (!fileObject.getType().hasAttributes()) {
- throw new FileSystemException("vfs.provider/exists-attributes-no-exist.error", fileObject);
- }
- getAttributes();
- return attrs.containsKey(attrName);
- }
-
- /**
- * Returns a read-only map of this file's attributes.
- *
- * @return a Map of the file's attributes.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public Map<String, Object> getAttributes() throws FileSystemException {
- if (!fileObject.getType().hasAttributes()) {
- throw new FileSystemException("vfs.provider/get-attributes-no-exist.error", fileObject);
- }
- if (resetAttributes || roAttrs == null) {
- try {
- synchronized (this) {
- attrs = fileObject.doGetAttributes();
- roAttrs = Collections.unmodifiableMap(attrs);
- resetAttributes = false;
- }
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/get-attributes.error", fileObject, e);
- }
- }
- return roAttrs;
- }
-
- /**
- * Used internally to flag situations where the file attributes should be reretrieved.
- *
- * @since 2.0
- */
- public void resetAttributes() {
- resetAttributes = true;
- }
-
- /**
- * Lists the attributes of this file.
- *
- * @return An array of attribute names.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public String[] getAttributeNames() throws FileSystemException {
- getAttributes();
- final Set<String> names = attrs.keySet();
- return names.toArray(new String[names.size()]);
- }
-
- /**
- * Gets the value of an attribute.
- *
- * @param attrName The attribute name.
- * @return The value of the attribute or null.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public Object getAttribute(final String attrName) throws FileSystemException {
- getAttributes();
- return attrs.get(attrName);
- }
-
- /**
- * Sets the value of an attribute.
- *
- * @param attrName The name of the attribute to add.
- * @param value The value of the attribute.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void setAttribute(final String attrName, final Object value) throws FileSystemException {
- if (!fileObject.getType().hasAttributes()) {
- throw new FileSystemException("vfs.provider/set-attribute-no-exist.error", attrName, fileObject);
- }
- try {
- fileObject.doSetAttribute(attrName, value);
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/set-attribute.error", e, attrName, fileObject);
- }
-
- if (attrs != null) {
- attrs.put(attrName, value);
- }
- }
-
- /**
- * Removes an attribute.
- *
- * @param attrName The name of the attribute to remove.
- * @throws FileSystemException if an error occurs.
- * @since 2.0
- */
- @Override
- public void removeAttribute(final String attrName) throws FileSystemException {
- if (!fileObject.getType().hasAttributes()) {
- throw new FileSystemException("vfs.provider/remove-attribute-no-exist.error", fileObject);
- }
-
- try {
- fileObject.doRemoveAttribute(attrName);
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/remove-attribute.error", e, attrName, fileObject);
- }
-
- if (attrs != null) {
- attrs.remove(attrName);
- }
- }
-
- /**
- * Returns the certificates used to sign this file.
- *
- * @return An array of Certificates.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public Certificate[] getCertificates() throws FileSystemException {
- if (!fileObject.exists()) {
- throw new FileSystemException("vfs.provider/get-certificates-no-exist.error", fileObject);
- }
- /*
- * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
- * new FileSystemException("vfs.provider/get-certificates-writing.error", file); }
- */
-
- try {
- final Certificate[] certs = fileObject.doGetCertificates();
- if (certs != null) {
- return certs;
- }
- return new Certificate[0];
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/get-certificates.error", fileObject, e);
- }
- }
-
- /**
- * Returns an input stream for reading the content.
- *
- * @return The InputStream
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public InputStream getInputStream() throws FileSystemException {
- return buildInputStream(0);
- }
-
- /**
- * Returns an input stream for reading the content.
- *
- * @param bufferSize The buffer size to use.
- * @return The InputStream
- * @throws FileSystemException if an error occurs.
- * @since 2.4
- */
- @Override
- public InputStream getInputStream(final int bufferSize) throws FileSystemException {
- return buildInputStream(bufferSize);
- }
-
- /**
- * Returns an input/output stream to use to read and write the content of the file in an random manner.
- *
- * @param mode The RandomAccessMode.
- * @return A RandomAccessContent object to access the file.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException {
- /*
- * if (getThreadData().getState() != STATE_NONE) { throw new
- * FileSystemException("vfs.provider/read-in-use.error", file); }
- */
-
- // Get the content
- final RandomAccessContent rastr = fileObject.getRandomAccessContent(mode);
-
- final FileRandomAccessContent rac = new FileRandomAccessContent(fileObject, rastr);
-
- getOrCreateThreadData().addRastr(rac);
- streamOpened();
-
- return rac;
- }
-
- /**
- * Returns an output stream for writing the content.
- *
- * @return The OutputStream for the file.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public OutputStream getOutputStream() throws FileSystemException {
- return getOutputStream(false);
- }
-
- /**
- * Returns an output stream for writing the content in append mode.
- *
- * @param bAppend true if the data written should be appended.
- * @return The OutputStream for the file.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException {
- return buildOutputStream(bAppend, 0);
- }
-
- /**
- * Returns an output stream for writing the content.
- *
- * @param bufferSize The buffer size to use.
- * @return The OutputStream for the file.
- * @throws FileSystemException if an error occurs.
- * @since 2.4
- */
- @Override
- public OutputStream getOutputStream(final int bufferSize) throws FileSystemException {
- return buildOutputStream(false, bufferSize);
- }
-
- /**
- * Returns an output stream for writing the content in append mode.
- *
- * @param bAppend true if the data written should be appended.
- * @param bufferSize The buffer size to use.
- * @return The OutputStream for the file.
- * @throws FileSystemException if an error occurs.
- * @since 2.4
- */
- @Override
- public OutputStream getOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
- return buildOutputStream(bAppend, bufferSize);
- }
-
- /**
- * Closes all resources used by the content, including all streams, readers and writers.
- *
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void close() throws FileSystemException {
- FileSystemException caught = null;
- try {
- final FileContentThreadData fileContentThreadData = getOrCreateThreadData();
-
- // Close the input stream
- while (fileContentThreadData.getInstrsSize() > 0) {
- final FileContentInputStream inputStream = (FileContentInputStream) fileContentThreadData
- .removeInstr(0);
- try {
- inputStream.close();
- } catch (final FileSystemException ex) {
- caught = ex;
-
- }
- }
-
- // Close the randomAccess stream
- while (fileContentThreadData.getRastrsSize() > 0) {
- final FileRandomAccessContent randomAccessContent = (FileRandomAccessContent) fileContentThreadData
- .removeRastr(0);
- try {
- randomAccessContent.close();
- } catch (final FileSystemException ex) {
- caught = ex;
- }
- }
-
- // Close the output stream
- final FileContentOutputStream outputStream = fileContentThreadData.getOutstr();
- if (outputStream != null) {
- fileContentThreadData.setOutstr(null);
- try {
- outputStream.close();
- } catch (final FileSystemException ex) {
- caught = ex;
- }
- }
- } finally {
- threadLocal.remove();
- }
-
- // throw last error (out >> rac >> input) after all closes have been tried
- if (caught != null) {
- throw caught;
- }
- }
-
- private InputStream buildInputStream(final int bufferSize) throws FileSystemException {
- /*
- * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
- * new FileSystemException("vfs.provider/read-in-use.error", file); }
- */
-
- // Get the raw input stream
- final InputStream inputStream = fileObject.getInputStream();
-
- final InputStream wrappedInputStream = bufferSize == 0 ?
- new FileContentInputStream(fileObject, inputStream) :
- new FileContentInputStream(fileObject, inputStream, bufferSize);
-
- getOrCreateThreadData().addInstr(wrappedInputStream);
- streamOpened();
-
- return wrappedInputStream;
- }
-
- private OutputStream buildOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
- /*
- * if (getThreadData().getState() != STATE_NONE)
- */
- final FileContentThreadData streams = getOrCreateThreadData();
-
- if (streams.getOutstr() != null) {
- throw new FileSystemException("vfs.provider/write-in-use.error", fileObject);
- }
-
- // Get the raw output stream
- final OutputStream outstr = fileObject.getOutputStream(bAppend);
-
- // Create and set wrapper
- final FileContentOutputStream wrapped = bufferSize == 0 ?
- new FileContentOutputStream(fileObject, outstr) :
- new FileContentOutputStream(fileObject, outstr, bufferSize);
- streams.setOutstr(wrapped);
- streamOpened();
-
- return wrapped;
- }
-
- /**
- * Handles the end of input stream.
- */
- private void endInput(final FileContentInputStream instr) {
- final FileContentThreadData fileContentThreadData = threadLocal.get();
- if (fileContentThreadData != null) {
- fileContentThreadData.removeInstr(instr);
- }
- if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
- // remove even when no value is set to remove key
- threadLocal.remove();
- }
- streamClosed();
- }
-
- /**
- * Handles the end of random access.
- */
- private void endRandomAccess(final RandomAccessContent rac) {
- final FileContentThreadData fileContentThreadData = threadLocal.get();
- if (fileContentThreadData != null) {
- fileContentThreadData.removeRastr(rac);
- }
- if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
- // remove even when no value is set to remove key
- threadLocal.remove();
- }
- streamClosed();
- }
-
- /**
- * Handles the end of output stream.
- */
- private void endOutput() throws Exception {
- final FileContentThreadData fileContentThreadData = threadLocal.get();
- if (fileContentThreadData != null) {
- fileContentThreadData.setOutstr(null);
- }
- if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
- // remove even when no value is set to remove key
- threadLocal.remove();
- }
- streamClosed();
- fileObject.endOutput();
- }
-
- /**
- * Checks if a input and/or output stream is open.
- * <p>
- * This checks only the scope of the current thread.
- * </p>
- *
- * @return true if this is the case
- */
- @Override
- public boolean isOpen() {
- final FileContentThreadData fileContentThreadData = threadLocal.get();
- if (fileContentThreadData != null && fileContentThreadData.hasStreams()) {
- return true;
- }
- // threadData.get() created empty entry
- threadLocal.remove();
- return false;
- }
-
- /**
- * Checks if an input or output stream is open. This checks all threads.
- *
- * @return true if this is the case
- */
- public boolean isOpenGlobal() {
- synchronized (this) {
- return openStreams > 0;
- }
- }
-
- /**
- * An input stream for reading content. Provides buffering, and end-of-stream monitoring.
- */
- private final class FileContentInputStream extends MonitorInputStream {
- // avoid gc
- private final FileObject file;
-
- FileContentInputStream(final FileObject file, final InputStream instr) {
- super(instr);
- this.file = file;
- }
-
- FileContentInputStream(final FileObject file, final InputStream instr, final int bufferSize) {
- super(instr, bufferSize);
- this.file = file;
- }
-
- /**
- * Closes this input stream.
- */
- @Override
- public void close() throws FileSystemException {
- try {
- super.close();
- } catch (final IOException e) {
- throw new FileSystemException("vfs.provider/close-instr.error", file, e);
- }
- }
-
- /**
- * Called after the stream has been closed.
- */
- @Override
- protected void onClose() throws IOException {
- try {
- super.onClose();
- } finally {
- endInput(this);
- }
- }
- }
-
- /**
- * An input/output stream for reading/writing content on random positions
- */
- private final class FileRandomAccessContent extends MonitorRandomAccessContent {
- // also avoids gc
- private final FileObject file;
-
- FileRandomAccessContent(final FileObject file, final RandomAccessContent content) {
- super(content);
- this.file = file;
- }
-
- /**
- * Called after the stream has been closed.
- */
- @Override
- protected void onClose() throws IOException {
- try {
- super.onClose();
- } finally {
- endRandomAccess(this);
- }
- }
-
- @Override
- public void close() throws FileSystemException {
- try {
- super.close();
- } catch (final IOException e) {
- throw new FileSystemException("vfs.provider/close-rac.error", file, e);
- }
- }
- }
-
- /**
- * An output stream for writing content.
- */
- final class FileContentOutputStream extends MonitorOutputStream {
- // avoid gc
- private final FileObject file;
-
- FileContentOutputStream(final FileObject file, final OutputStream outstr) {
- super(outstr);
- this.file = file;
- }
-
- FileContentOutputStream(final FileObject file, final OutputStream outstr, final int bufferSize) {
- super(outstr, bufferSize);
- this.file = file;
- }
-
- /**
- * Closes this output stream.
- */
- @Override
- public void close() throws FileSystemException {
- try {
- super.close();
- } catch (final IOException e) {
- throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
- }
- }
-
- /**
- * Called after this stream is closed.
- */
- @Override
- protected void onClose() throws IOException {
- try {
- super.onClose();
- } finally {
- try {
- endOutput();
- } catch (final Exception e) {
- throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
- }
- }
- }
- }
-
- /**
- * Gets the FileContentInfo which describes the content-type, content-encoding
- *
- * @return The FileContentInfo.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public FileContentInfo getContentInfo() throws FileSystemException {
- if (fileContentInfo == null) {
- fileContentInfo = fileContentInfoFactory.create(this);
- }
-
- return fileContentInfo;
- }
-
- /**
- * Writes this content to another FileContent.
- *
- * @param fileContent The target FileContent.
- * @return the total number of bytes written
- * @throws IOException if an error occurs writing the content.
- * @since 2.1
- */
- @Override
- public long write(final FileContent fileContent) throws IOException {
- final OutputStream output = fileContent.getOutputStream();
- try {
- return this.write(output);
- } finally {
- output.close();
- }
- }
-
- /**
- * Writes this content to another FileObject.
- *
- * @param file The target FileObject.
- * @return the total number of bytes written
- * @throws IOException if an error occurs writing the content.
- * @since 2.1
- */
- @Override
- public long write(final FileObject file) throws IOException {
- return write(file.getContent());
- }
-
- /**
- * Writes this content to an OutputStream.
- *
- * @param output The target OutputStream.
- * @return the total number of bytes written
- * @throws IOException if an error occurs writing the content.
- * @since 2.1
- */
- @Override
- public long write(final OutputStream output) throws IOException {
- return write(output, WRITE_BUFFER_SIZE);
- }
-
- /**
- * Writes this content to an OutputStream.
- *
- * @param output The target OutputStream.
- * @param bufferSize The buffer size to write data chunks.
- * @return the total number of bytes written
- * @throws IOException if an error occurs writing the file.
- * @since 2.1
- */
- @Override
- public long write(final OutputStream output, final int bufferSize) throws IOException {
- final InputStream input = this.getInputStream();
- long count = 0;
- try {
- // This read/write code from Apache Commons IO
- final byte[] buffer = new byte[bufferSize];
- int n = 0;
- while (-1 != (n = input.read(buffer))) {
- output.write(buffer, 0, n);
- count += n;
- }
- } finally {
- input.close();
- }
- return count;
- }
-}
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.vfs2.FileContent;
+import org.apache.commons.vfs2.FileContentInfo;
+import org.apache.commons.vfs2.FileContentInfoFactory;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.util.MonitorInputStream;
+import org.apache.commons.vfs2.util.MonitorOutputStream;
+import org.apache.commons.vfs2.util.MonitorRandomAccessContent;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+
+/**
+ * The content of a file.
+ */
+public final class DefaultFileContent implements FileContent {
+
+ /*
+ * static final int STATE_NONE = 0; static final int STATE_READING = 1; static final int STATE_WRITING = 2; static
+ * final int STATE_RANDOM_ACCESS = 3;
+ */
+
+ static final int STATE_CLOSED = 0;
+ static final int STATE_OPENED = 1;
+
+ /**
+ * The default buffer size for {@link #write(OutputStream)}
+ */
+ private static final int WRITE_BUFFER_SIZE = 4096;
+
+ private final AbstractFileObject fileObject;
+ private Map<String, Object> attrs;
+ private Map<String, Object> roAttrs;
+ private FileContentInfo fileContentInfo;
+ private final FileContentInfoFactory fileContentInfoFactory;
+
+ private final ThreadLocal<FileContentThreadData> threadLocal = new ThreadLocal<>();
+ private boolean resetAttributes;
+
+ /**
+ * Counts open streams for this file.
+ */
+ private int openStreams;
+
+ public DefaultFileContent(final AbstractFileObject file, final FileContentInfoFactory fileContentInfoFactory) {
+ this.fileObject = file;
+ this.fileContentInfoFactory = fileContentInfoFactory;
+ }
+
+ private FileContentThreadData getOrCreateThreadData() {
+ FileContentThreadData data = this.threadLocal.get();
+ if (data == null) {
+ data = new FileContentThreadData();
+ this.threadLocal.set(data);
+ }
+ return data;
+ }
+
+ void streamOpened() {
+ synchronized (this) {
+ openStreams++;
+ }
+ ((AbstractFileSystem) fileObject.getFileSystem()).streamOpened();
+ }
+
+ void streamClosed() {
+ synchronized (this) {
+ if (openStreams > 0) {
+ openStreams--;
+ if (openStreams < 1) {
+ fileObject.notifyAllStreamsClosed();
+ }
+ }
+ }
+ ((AbstractFileSystem) fileObject.getFileSystem()).streamClosed();
+ }
+
+ /**
+ * Returns the file that this is the content of.
+ *
+ * @return the FileObject.
+ */
+ @Override
+ public FileObject getFile() {
+ return fileObject;
+ }
+
+ /**
+ * Returns the size of the content (in bytes).
+ *
+ * @return The size of the content (in bytes).
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public long getSize() throws FileSystemException {
+ // Do some checking
+ if (!fileObject.getType().hasContent()) {
+ throw new FileSystemException("vfs.provider/get-size-not-file.error", fileObject);
+ }
+ /*
+ * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
+ * new FileSystemException("vfs.provider/get-size-write.error", file); }
+ */
+
+ try {
+ // Get the size
+ return fileObject.doGetContentSize();
+ } catch (final Exception exc) {
+ throw new FileSystemException("vfs.provider/get-size.error", exc, fileObject);
+ }
+ }
+
+ /**
+ * Returns the last-modified timestamp.
+ *
+ * @return The last modified timestamp.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public long getLastModifiedTime() throws FileSystemException {
+ /*
+ * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
+ * new FileSystemException("vfs.provider/get-last-modified-writing.error", file); }
+ */
+ if (!fileObject.getType().hasAttributes()) {
+ throw new FileSystemException("vfs.provider/get-last-modified-no-exist.error", fileObject);
+ }
+ try {
+ return fileObject.doGetLastModifiedTime();
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/get-last-modified.error", fileObject, e);
+ }
+ }
+
+ /**
+ * Sets the last-modified timestamp.
+ *
+ * @param modTime The last modified timestamp.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void setLastModifiedTime(final long modTime) throws FileSystemException {
+ /*
+ * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
+ * new FileSystemException("vfs.provider/set-last-modified-writing.error", file); }
+ */
+ if (!fileObject.getType().hasAttributes()) {
+ throw new FileSystemException("vfs.provider/set-last-modified-no-exist.error", fileObject);
+ }
+ try {
+ if (!fileObject.doSetLastModifiedTime(modTime)) {
+ throw new FileSystemException("vfs.provider/set-last-modified.error", fileObject);
+ }
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/set-last-modified.error", fileObject, e);
+ }
+ }
+
+ /**
+ * Checks if an attribute exists.
+ *
+ * @param attrName The name of the attribute to check.
+ * @return true if the attribute is associated with the file.
+ * @throws FileSystemException if an error occurs.
+ * @since 2.0
+ */
+ @Override
+ public boolean hasAttribute(final String attrName) throws FileSystemException {
+ if (!fileObject.getType().hasAttributes()) {
+ throw new FileSystemException("vfs.provider/exists-attributes-no-exist.error", fileObject);
+ }
+ getAttributes();
+ return attrs.containsKey(attrName);
+ }
+
+ /**
+ * Returns a read-only map of this file's attributes.
+ *
+ * @return a Map of the file's attributes.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public Map<String, Object> getAttributes() throws FileSystemException {
+ if (!fileObject.getType().hasAttributes()) {
+ throw new FileSystemException("vfs.provider/get-attributes-no-exist.error", fileObject);
+ }
+ if (resetAttributes || roAttrs == null) {
+ try {
+ synchronized (this) {
+ attrs = fileObject.doGetAttributes();
+ roAttrs = Collections.unmodifiableMap(attrs);
+ resetAttributes = false;
+ }
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/get-attributes.error", fileObject, e);
+ }
+ }
+ return roAttrs;
+ }
+
+ /**
+ * Used internally to flag situations where the file attributes should be reretrieved.
+ *
+ * @since 2.0
+ */
+ public void resetAttributes() {
+ resetAttributes = true;
+ }
+
+ /**
+ * Lists the attributes of this file.
+ *
+ * @return An array of attribute names.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public String[] getAttributeNames() throws FileSystemException {
+ getAttributes();
+ final Set<String> names = attrs.keySet();
+ return names.toArray(new String[names.size()]);
+ }
+
+ /**
+ * Gets the value of an attribute.
+ *
+ * @param attrName The attribute name.
+ * @return The value of the attribute or null.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public Object getAttribute(final String attrName) throws FileSystemException {
+ getAttributes();
+ return attrs.get(attrName);
+ }
+
+ /**
+ * Sets the value of an attribute.
+ *
+ * @param attrName The name of the attribute to add.
+ * @param value The value of the attribute.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void setAttribute(final String attrName, final Object value) throws FileSystemException {
+ if (!fileObject.getType().hasAttributes()) {
+ throw new FileSystemException("vfs.provider/set-attribute-no-exist.error", attrName, fileObject);
+ }
+ try {
+ fileObject.doSetAttribute(attrName, value);
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/set-attribute.error", e, attrName, fileObject);
+ }
+
+ if (attrs != null) {
+ attrs.put(attrName, value);
+ }
+ }
+
+ /**
+ * Removes an attribute.
+ *
+ * @param attrName The name of the attribute to remove.
+ * @throws FileSystemException if an error occurs.
+ * @since 2.0
+ */
+ @Override
+ public void removeAttribute(final String attrName) throws FileSystemException {
+ if (!fileObject.getType().hasAttributes()) {
+ throw new FileSystemException("vfs.provider/remove-attribute-no-exist.error", fileObject);
+ }
+
+ try {
+ fileObject.doRemoveAttribute(attrName);
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/remove-attribute.error", e, attrName, fileObject);
+ }
+
+ if (attrs != null) {
+ attrs.remove(attrName);
+ }
+ }
+
+ /**
+ * Returns the certificates used to sign this file.
+ *
+ * @return An array of Certificates.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public Certificate[] getCertificates() throws FileSystemException {
+ if (!fileObject.exists()) {
+ throw new FileSystemException("vfs.provider/get-certificates-no-exist.error", fileObject);
+ }
+ /*
+ * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
+ * new FileSystemException("vfs.provider/get-certificates-writing.error", file); }
+ */
+
+ try {
+ final Certificate[] certs = fileObject.doGetCertificates();
+ if (certs != null) {
+ return certs;
+ }
+ return new Certificate[0];
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/get-certificates.error", fileObject, e);
+ }
+ }
+
+ /**
+ * Returns an input stream for reading the content.
+ *
+ * @return The InputStream
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public InputStream getInputStream() throws FileSystemException {
+ return buildInputStream(0);
+ }
+
+ /**
+ * Returns an input stream for reading the content.
+ *
+ * @param bufferSize The buffer size to use.
+ * @return The InputStream
+ * @throws FileSystemException if an error occurs.
+ * @since 2.4
+ */
+ @Override
+ public InputStream getInputStream(final int bufferSize) throws FileSystemException {
+ return buildInputStream(bufferSize);
+ }
+
+ /**
+ * Returns an input/output stream to use to read and write the content of the file in an random manner.
+ *
+ * @param mode The RandomAccessMode.
+ * @return A RandomAccessContent object to access the file.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException {
+ /*
+ * if (getThreadData().getState() != STATE_NONE) { throw new
+ * FileSystemException("vfs.provider/read-in-use.error", file); }
+ */
+
+ // Get the content
+ final RandomAccessContent rastr = fileObject.getRandomAccessContent(mode);
+
+ final FileRandomAccessContent rac = new FileRandomAccessContent(fileObject, rastr);
+
+ getOrCreateThreadData().addRastr(rac);
+ streamOpened();
+
+ return rac;
+ }
+
+ /**
+ * Returns an output stream for writing the content.
+ *
+ * @return The OutputStream for the file.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public OutputStream getOutputStream() throws FileSystemException {
+ return getOutputStream(false);
+ }
+
+ /**
+ * Returns an output stream for writing the content in append mode.
+ *
+ * @param bAppend true if the data written should be appended.
+ * @return The OutputStream for the file.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public OutputStream getOutputStream(final boolean bAppend) throws FileSystemException {
+ return buildOutputStream(bAppend, 0);
+ }
+
+ /**
+ * Returns an output stream for writing the content.
+ *
+ * @param bufferSize The buffer size to use.
+ * @return The OutputStream for the file.
+ * @throws FileSystemException if an error occurs.
+ * @since 2.4
+ */
+ @Override
+ public OutputStream getOutputStream(final int bufferSize) throws FileSystemException {
+ return buildOutputStream(false, bufferSize);
+ }
+
+ /**
+ * Returns an output stream for writing the content in append mode.
+ *
+ * @param bAppend true if the data written should be appended.
+ * @param bufferSize The buffer size to use.
+ * @return The OutputStream for the file.
+ * @throws FileSystemException if an error occurs.
+ * @since 2.4
+ */
+ @Override
+ public OutputStream getOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
+ return buildOutputStream(bAppend, bufferSize);
+ }
+
+ /**
+ * Closes all resources used by the content, including all streams, readers and writers.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void close() throws FileSystemException {
+ FileSystemException caught = null;
+ try {
+ final FileContentThreadData fileContentThreadData = getOrCreateThreadData();
+
+ // Close the input stream
+ while (fileContentThreadData.getInstrsSize() > 0) {
+ final FileContentInputStream inputStream = (FileContentInputStream) fileContentThreadData
+ .removeInstr(0);
+ try {
+ inputStream.close();
+ } catch (final FileSystemException ex) {
+ caught = ex;
+
+ }
+ }
+
+ // Close the randomAccess stream
+ while (fileContentThreadData.getRastrsSize() > 0) {
+ final FileRandomAccessContent randomAccessContent = (FileRandomAccessContent) fileContentThreadData
+ .removeRastr(0);
+ try {
+ randomAccessContent.close();
+ } catch (final FileSystemException ex) {
+ caught = ex;
+ }
+ }
+
+ // Close the output stream
+ final FileContentOutputStream outputStream = fileContentThreadData.getOutstr();
+ if (outputStream != null) {
+ fileContentThreadData.setOutstr(null);
+ try {
+ outputStream.close();
+ } catch (final FileSystemException ex) {
+ caught = ex;
+ }
+ }
+ } finally {
+ threadLocal.remove();
+ }
+
+ // throw last error (out >> rac >> input) after all closes have been tried
+ if (caught != null) {
+ throw caught;
+ }
+ }
+
+ private InputStream buildInputStream(final int bufferSize) throws FileSystemException {
+ /*
+ * if (getThreadData().getState() == STATE_WRITING || getThreadData().getState() == STATE_RANDOM_ACCESS) { throw
+ * new FileSystemException("vfs.provider/read-in-use.error", file); }
+ */
+
+ // Get the raw input stream
+ final InputStream inputStream = bufferSize == 0 ? fileObject.getInputStream()
+ : fileObject.getInputStream(bufferSize);
+ final InputStream wrappedInputStream = bufferSize == 0
+ ? new FileContentInputStream(fileObject, inputStream)
+ : new FileContentInputStream(fileObject, inputStream, bufferSize);
+ getOrCreateThreadData().addInstr(wrappedInputStream);
+ streamOpened();
+
+ return wrappedInputStream;
+ }
+
+ private OutputStream buildOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
+ /*
+ * if (getThreadData().getState() != STATE_NONE)
+ */
+ final FileContentThreadData streams = getOrCreateThreadData();
+
+ if (streams.getOutstr() != null) {
+ throw new FileSystemException("vfs.provider/write-in-use.error", fileObject);
+ }
+
+ // Get the raw output stream
+ final OutputStream outstr = fileObject.getOutputStream(bAppend);
+
+ // Create and set wrapper
+ final FileContentOutputStream wrapped = bufferSize == 0 ?
+ new FileContentOutputStream(fileObject, outstr) :
+ new FileContentOutputStream(fileObject, outstr, bufferSize);
+ streams.setOutstr(wrapped);
+ streamOpened();
+
+ return wrapped;
+ }
+
+ /**
+ * Handles the end of input stream.
+ */
+ private void endInput(final FileContentInputStream instr) {
+ final FileContentThreadData fileContentThreadData = threadLocal.get();
+ if (fileContentThreadData != null) {
+ fileContentThreadData.removeInstr(instr);
+ }
+ if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
+ // remove even when no value is set to remove key
+ threadLocal.remove();
+ }
+ streamClosed();
+ }
+
+ /**
+ * Handles the end of random access.
+ */
+ private void endRandomAccess(final RandomAccessContent rac) {
+ final FileContentThreadData fileContentThreadData = threadLocal.get();
+ if (fileContentThreadData != null) {
+ fileContentThreadData.removeRastr(rac);
+ }
+ if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
+ // remove even when no value is set to remove key
+ threadLocal.remove();
+ }
+ streamClosed();
+ }
+
+ /**
+ * Handles the end of output stream.
+ */
+ private void endOutput() throws Exception {
+ final FileContentThreadData fileContentThreadData = threadLocal.get();
+ if (fileContentThreadData != null) {
+ fileContentThreadData.setOutstr(null);
+ }
+ if (fileContentThreadData == null || !fileContentThreadData.hasStreams()) {
+ // remove even when no value is set to remove key
+ threadLocal.remove();
+ }
+ streamClosed();
+ fileObject.endOutput();
+ }
+
+ /**
+ * Checks if a input and/or output stream is open.
+ * <p>
+ * This checks only the scope of the current thread.
+ * </p>
+ *
+ * @return true if this is the case
+ */
+ @Override
+ public boolean isOpen() {
+ final FileContentThreadData fileContentThreadData = threadLocal.get();
+ if (fileContentThreadData != null && fileContentThreadData.hasStreams()) {
+ return true;
+ }
+ // threadData.get() created empty entry
+ threadLocal.remove();
+ return false;
+ }
+
+ /**
+ * Checks if an input or output stream is open. This checks all threads.
+ *
+ * @return true if this is the case
+ */
+ public boolean isOpenGlobal() {
+ synchronized (this) {
+ return openStreams > 0;
+ }
+ }
+
+ /**
+ * An input stream for reading content. Provides buffering, and end-of-stream monitoring.
+ */
+ private final class FileContentInputStream extends MonitorInputStream {
+ // avoid gc
+ private final FileObject file;
+
+ FileContentInputStream(final FileObject file, final InputStream instr) {
+ super(instr);
+ this.file = file;
+ }
+
+ FileContentInputStream(final FileObject file, final InputStream instr, final int bufferSize) {
+ super(instr, bufferSize);
+ this.file = file;
+ }
+
+ /**
+ * Closes this input stream.
+ */
+ @Override
+ public void close() throws FileSystemException {
+ try {
+ super.close();
+ } catch (final IOException e) {
+ throw new FileSystemException("vfs.provider/close-instr.error", file, e);
+ }
+ }
+
+ /**
+ * Called after the stream has been closed.
+ */
+ @Override
+ protected void onClose() throws IOException {
+ try {
+ super.onClose();
+ } finally {
+ endInput(this);
+ }
+ }
+ }
+
+ /**
+ * An input/output stream for reading/writing content on random positions
+ */
+ private final class FileRandomAccessContent extends MonitorRandomAccessContent {
+ // also avoids gc
+ private final FileObject file;
+
+ FileRandomAccessContent(final FileObject file, final RandomAccessContent content) {
+ super(content);
+ this.file = file;
+ }
+
+ /**
+ * Called after the stream has been closed.
+ */
+ @Override
+ protected void onClose() throws IOException {
+ try {
+ super.onClose();
+ } finally {
+ endRandomAccess(this);
+ }
+ }
+
+ @Override
+ public void close() throws FileSystemException {
+ try {
+ super.close();
+ } catch (final IOException e) {
+ throw new FileSystemException("vfs.provider/close-rac.error", file, e);
+ }
+ }
+ }
+
+ /**
+ * An output stream for writing content.
+ */
+ final class FileContentOutputStream extends MonitorOutputStream {
+ // avoid gc
+ private final FileObject file;
+
+ FileContentOutputStream(final FileObject file, final OutputStream outstr) {
+ super(outstr);
+ this.file = file;
+ }
+
+ FileContentOutputStream(final FileObject file, final OutputStream outstr, final int bufferSize) {
+ super(outstr, bufferSize);
+ this.file = file;
+ }
+
+ /**
+ * Closes this output stream.
+ */
+ @Override
+ public void close() throws FileSystemException {
+ try {
+ super.close();
+ } catch (final IOException e) {
+ throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
+ }
+ }
+
+ /**
+ * Called after this stream is closed.
+ */
+ @Override
+ protected void onClose() throws IOException {
+ try {
+ super.onClose();
+ } finally {
+ try {
+ endOutput();
+ } catch (final Exception e) {
+ throw new FileSystemException("vfs.provider/close-outstr.error", file, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the FileContentInfo which describes the content-type, content-encoding
+ *
+ * @return The FileContentInfo.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public FileContentInfo getContentInfo() throws FileSystemException {
+ if (fileContentInfo == null) {
+ fileContentInfo = fileContentInfoFactory.create(this);
+ }
+
+ return fileContentInfo;
+ }
+
+ /**
+ * Writes this content to another FileContent.
+ *
+ * @param fileContent The target FileContent.
+ * @return the total number of bytes written
+ * @throws IOException if an error occurs writing the content.
+ * @since 2.1
+ */
+ @Override
+ public long write(final FileContent fileContent) throws IOException {
+ final OutputStream output = fileContent.getOutputStream();
+ try {
+ return this.write(output);
+ } finally {
+ output.close();
+ }
+ }
+
+ /**
+ * Writes this content to another FileObject.
+ *
+ * @param file The target FileObject.
+ * @return the total number of bytes written
+ * @throws IOException if an error occurs writing the content.
+ * @since 2.1
+ */
+ @Override
+ public long write(final FileObject file) throws IOException {
+ return write(file.getContent());
+ }
+
+ /**
+ * Writes this content to an OutputStream.
+ *
+ * @param output The target OutputStream.
+ * @return the total number of bytes written
+ * @throws IOException if an error occurs writing the content.
+ * @since 2.1
+ */
+ @Override
+ public long write(final OutputStream output) throws IOException {
+ return write(output, WRITE_BUFFER_SIZE);
+ }
+
+ /**
+ * Writes this content to an OutputStream.
+ *
+ * @param output The target OutputStream.
+ * @param bufferSize The buffer size to write data chunks.
+ * @return the total number of bytes written
+ * @throws IOException if an error occurs writing the file.
+ * @since 2.1
+ */
+ @Override
+ public long write(final OutputStream output, final int bufferSize) throws IOException {
+ final InputStream input = this.getInputStream();
+ long count = 0;
+ try {
+ // This read/write code from Apache Commons IO
+ final byte[] buffer = new byte[bufferSize];
+ int n = 0;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ } finally {
+ input.close();
+ }
+ return count;
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DelegateFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DelegateFileObject.java
index 9d68532..4e414c9 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DelegateFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/DelegateFileObject.java
@@ -1,415 +1,415 @@
-/*
- * 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;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.cert.Certificate;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.vfs2.FileChangeEvent;
-import org.apache.commons.vfs2.FileContentInfo;
-import org.apache.commons.vfs2.FileListener;
-import org.apache.commons.vfs2.FileName;
-import org.apache.commons.vfs2.FileNotFolderException;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-import org.apache.commons.vfs2.util.WeakRefFileListener;
-
-/**
- * A file backed by another file.
- * <p>
- * TODO - Extract subclass that overlays the children.
- * </p>
- *
- * @param <AFS> A subclass of AbstractFileSystem.
- */
-public class DelegateFileObject<AFS extends AbstractFileSystem> extends AbstractFileObject<AFS>
- implements FileListener {
- private FileObject file;
- private final Set<String> children = new HashSet<>();
- private boolean ignoreEvent;
-
- public DelegateFileObject(final AbstractFileName name, final AFS fileSystem, final FileObject file)
- throws FileSystemException {
- super(name, fileSystem);
- this.file = file;
- if (file != null) {
- WeakRefFileListener.installListener(file, this);
- }
- }
-
- /**
- * Get access to the delegated file.
- *
- * @return The FileObject.
- * @since 2.0
- */
- public FileObject getDelegateFile() {
- return file;
- }
-
- /**
- * Adds a child to this file.
- *
- * @param baseName The base FileName.
- * @param type The FileType.
- * @throws Exception if an error occurs.
- */
- public void attachChild(final FileName baseName, final FileType type) throws Exception {
- final FileType oldType = doGetType();
- if (children.add(baseName.getBaseName())) {
- childrenChanged(baseName, type);
- }
- maybeTypeChanged(oldType);
- }
-
- /**
- * Attaches or detaches the target file.
- *
- * @param file The FileObject.
- * @throws Exception if an error occurs.
- */
- public void setFile(final FileObject file) throws Exception {
- final FileType oldType = doGetType();
-
- if (file != null) {
- WeakRefFileListener.installListener(file, this);
- }
- this.file = file;
- maybeTypeChanged(oldType);
- }
-
- /**
- * Checks whether the file's type has changed, and fires the appropriate events.
- *
- * @param oldType The old FileType.
- * @throws Exception if an error occurs.
- */
- private void maybeTypeChanged(final FileType oldType) throws Exception {
- final FileType newType = doGetType();
- if (oldType == FileType.IMAGINARY && newType != FileType.IMAGINARY) {
- handleCreate(newType);
- } else if (oldType != FileType.IMAGINARY && newType == FileType.IMAGINARY) {
- handleDelete();
- }
- }
-
- /**
- * Determines the type of the file, returns null if the file does not exist.
- */
- @Override
- protected FileType doGetType() throws FileSystemException {
- if (file != null) {
- return file.getType();
- } else if (children.size() > 0) {
- return FileType.FOLDER;
- } else {
- return FileType.IMAGINARY;
- }
- }
-
- /**
- * Determines if this file can be read.
- */
- @Override
- protected boolean doIsReadable() throws FileSystemException {
- if (file != null) {
- return file.isReadable();
- }
- return true;
- }
-
- /**
- * Determines if this file can be written to.
- */
- @Override
- protected boolean doIsWriteable() throws FileSystemException {
- if (file != null) {
- return file.isWriteable();
- }
- return false;
- }
-
- /**
- * Determines if this file is executable.
- */
- @Override
- protected boolean doIsExecutable() throws FileSystemException {
- if (file != null) {
- return file.isExecutable();
- }
- return false;
- }
-
- /**
- * Determines if this file is hidden.
- */
- @Override
- protected boolean doIsHidden() throws FileSystemException {
- if (file != null) {
- return file.isHidden();
- }
- return false;
- }
-
- /**
- * Lists the children of the file.
- */
- @Override
- protected String[] doListChildren() throws Exception {
- if (file != null) {
- final FileObject[] children;
-
- try {
- children = file.getChildren();
- }
- // VFS-210
- catch (final FileNotFolderException e) {
- throw new FileNotFolderException(getName(), e);
- }
-
- final String[] childNames = new String[children.length];
- for (int i = 0; i < children.length; i++) {
- childNames[i] = children[i].getName().getBaseName();
- }
- return childNames;
- }
- return children.toArray(new String[children.size()]);
- }
-
- /**
- * Creates this file as a folder.
- */
- @Override
- protected void doCreateFolder() throws Exception {
- ignoreEvent = true;
- try {
- file.createFolder();
- } finally {
- ignoreEvent = false;
- }
- }
-
- /**
- * Deletes the file.
- */
- @Override
- protected void doDelete() throws Exception {
- ignoreEvent = true;
- try {
- file.delete();
- } finally {
- ignoreEvent = false;
- }
- }
-
- /**
- * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
- * {@link FileType#FILE}.
- */
- @Override
- protected long doGetContentSize() throws Exception {
- return file.getContent().getSize();
- }
-
- /**
- * Returns the attributes of this file.
- */
- @Override
- protected Map<String, Object> doGetAttributes() throws Exception {
- return file.getContent().getAttributes();
- }
-
- /**
- * Sets an attribute of this file.
- */
- @Override
- protected void doSetAttribute(final String atttrName, final Object value) throws Exception {
- file.getContent().setAttribute(atttrName, value);
- }
-
- /**
- * Returns the certificates of this file.
- */
- @Override
- protected Certificate[] doGetCertificates() throws Exception {
- return file.getContent().getCertificates();
- }
-
- /**
- * Returns the last-modified time of this file.
- */
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- return file.getContent().getLastModifiedTime();
- }
-
- /**
- * Sets the last-modified time of this file.
- *
- * @since 2.0
- */
- @Override
- protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
- file.getContent().setLastModifiedTime(modtime);
- return true;
- }
-
- /**
- * Creates an input stream to read the file content from.
- */
- @Override
- protected InputStream doGetInputStream() throws Exception {
- return file.getContent().getInputStream();
- }
-
- /**
- * Creates an output stream to write the file content to.
- */
- @Override
- protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
- return file.getContent().getOutputStream(bAppend);
- }
-
- /**
- * Called when a file is created.
- *
- * @param event The FileChangeEvent.
- * @throws Exception if an error occurs.
- */
- @Override
- public void fileCreated(final FileChangeEvent event) throws Exception {
- if (event.getFile() != file) {
- return;
- }
- if (!ignoreEvent) {
- handleCreate(file.getType());
- }
- }
-
- /**
- * Called when a file is deleted.
- *
- * @param event The FileChangeEvent.
- * @throws Exception if an error occurs.
- */
- @Override
- public void fileDeleted(final FileChangeEvent event) throws Exception {
- if (event.getFile() != file) {
- return;
- }
- if (!ignoreEvent) {
- handleDelete();
- }
- }
-
- /**
- * Called when a file is changed.
- * <p>
- * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}.
- * </p>
- *
- * @param event The FileChangeEvent.
- * @throws Exception if an error occurs.
- */
- @Override
- public void fileChanged(final FileChangeEvent event) throws Exception {
- if (event.getFile() != file) {
- return;
- }
- if (!ignoreEvent) {
- handleChanged();
- }
- }
-
- /**
- * Close the delegated file.
- *
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void close() throws FileSystemException {
- super.close();
-
- if (file != null) {
- file.close();
- }
- }
-
- /**
- * Refresh file information.
- *
- * @throws FileSystemException if an error occurs.
- * @since 2.0
- */
- @Override
- public void refresh() throws FileSystemException {
- super.refresh();
- if (file != null) {
- file.refresh();
- }
- }
-
- /**
- * Return file content info.
- *
- * @return the file content info of the delegee.
- * @throws Exception Any thrown Exception is wrapped in FileSystemException.
- * @since 2.0
- */
- protected FileContentInfo doGetContentInfo() throws Exception {
- return file.getContent().getContentInfo();
- }
-
- /**
- * Renames the file.
- *
- * @param newFile the new location/name.
- * @throws Exception Any thrown Exception is wrapped in FileSystemException.
- * @since 2.0
- */
- @Override
- protected void doRename(final FileObject newFile) throws Exception {
- file.moveTo(((DelegateFileObject) newFile).file);
- }
-
- /**
- * Removes an attribute of this file.
- *
- * @since 2.0
- */
- @Override
- protected void doRemoveAttribute(final String atttrName) throws Exception {
- file.getContent().removeAttribute(atttrName);
- }
-
- /**
- * Creates access to the file for random i/o.
- *
- * @since 2.0
- */
- @Override
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- return file.getContent().getRandomAccessContent(mode);
- }
-}
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.cert.Certificate;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.vfs2.FileChangeEvent;
+import org.apache.commons.vfs2.FileContentInfo;
+import org.apache.commons.vfs2.FileListener;
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileNotFolderException;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+import org.apache.commons.vfs2.util.WeakRefFileListener;
+
+/**
+ * A file backed by another file.
+ * <p>
+ * TODO - Extract subclass that overlays the children.
+ * </p>
+ *
+ * @param <AFS> A subclass of AbstractFileSystem.
+ */
+public class DelegateFileObject<AFS extends AbstractFileSystem> extends AbstractFileObject<AFS>
+ implements FileListener {
+ private FileObject file;
+ private final Set<String> children = new HashSet<>();
+ private boolean ignoreEvent;
+
+ public DelegateFileObject(final AbstractFileName name, final AFS fileSystem, final FileObject file)
+ throws FileSystemException {
+ super(name, fileSystem);
+ this.file = file;
+ if (file != null) {
+ WeakRefFileListener.installListener(file, this);
+ }
+ }
+
+ /**
+ * Get access to the delegated file.
+ *
+ * @return The FileObject.
+ * @since 2.0
+ */
+ public FileObject getDelegateFile() {
+ return file;
+ }
+
+ /**
+ * Adds a child to this file.
+ *
+ * @param baseName The base FileName.
+ * @param type The FileType.
+ * @throws Exception if an error occurs.
+ */
+ public void attachChild(final FileName baseName, final FileType type) throws Exception {
+ final FileType oldType = doGetType();
+ if (children.add(baseName.getBaseName())) {
+ childrenChanged(baseName, type);
+ }
+ maybeTypeChanged(oldType);
+ }
+
+ /**
+ * Attaches or detaches the target file.
+ *
+ * @param file The FileObject.
+ * @throws Exception if an error occurs.
+ */
+ public void setFile(final FileObject file) throws Exception {
+ final FileType oldType = doGetType();
+
+ if (file != null) {
+ WeakRefFileListener.installListener(file, this);
+ }
+ this.file = file;
+ maybeTypeChanged(oldType);
+ }
+
+ /**
+ * Checks whether the file's type has changed, and fires the appropriate events.
+ *
+ * @param oldType The old FileType.
+ * @throws Exception if an error occurs.
+ */
+ private void maybeTypeChanged(final FileType oldType) throws Exception {
+ final FileType newType = doGetType();
+ if (oldType == FileType.IMAGINARY && newType != FileType.IMAGINARY) {
+ handleCreate(newType);
+ } else if (oldType != FileType.IMAGINARY && newType == FileType.IMAGINARY) {
+ handleDelete();
+ }
+ }
+
+ /**
+ * Determines the type of the file, returns null if the file does not exist.
+ */
+ @Override
+ protected FileType doGetType() throws FileSystemException {
+ if (file != null) {
+ return file.getType();
+ } else if (children.size() > 0) {
+ return FileType.FOLDER;
+ } else {
+ return FileType.IMAGINARY;
+ }
+ }
+
+ /**
+ * Determines if this file can be read.
+ */
+ @Override
+ protected boolean doIsReadable() throws FileSystemException {
+ if (file != null) {
+ return file.isReadable();
+ }
+ return true;
+ }
+
+ /**
+ * Determines if this file can be written to.
+ */
+ @Override
+ protected boolean doIsWriteable() throws FileSystemException {
+ if (file != null) {
+ return file.isWriteable();
+ }
+ return false;
+ }
+
+ /**
+ * Determines if this file is executable.
+ */
+ @Override
+ protected boolean doIsExecutable() throws FileSystemException {
+ if (file != null) {
+ return file.isExecutable();
+ }
+ return false;
+ }
+
+ /**
+ * Determines if this file is hidden.
+ */
+ @Override
+ protected boolean doIsHidden() throws FileSystemException {
+ if (file != null) {
+ return file.isHidden();
+ }
+ return false;
+ }
+
+ /**
+ * Lists the children of the file.
+ */
+ @Override
+ protected String[] doListChildren() throws Exception {
+ if (file != null) {
+ final FileObject[] children;
+
+ try {
+ children = file.getChildren();
+ }
+ // VFS-210
+ catch (final FileNotFolderException e) {
+ throw new FileNotFolderException(getName(), e);
+ }
+
+ final String[] childNames = new String[children.length];
+ for (int i = 0; i < children.length; i++) {
+ childNames[i] = children[i].getName().getBaseName();
+ }
+ return childNames;
+ }
+ return children.toArray(new String[children.size()]);
+ }
+
+ /**
+ * Creates this file as a folder.
+ */
+ @Override
+ protected void doCreateFolder() throws Exception {
+ ignoreEvent = true;
+ try {
+ file.createFolder();
+ } finally {
+ ignoreEvent = false;
+ }
+ }
+
+ /**
+ * Deletes the file.
+ */
+ @Override
+ protected void doDelete() throws Exception {
+ ignoreEvent = true;
+ try {
+ file.delete();
+ } finally {
+ ignoreEvent = false;
+ }
+ }
+
+ /**
+ * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
+ * {@link FileType#FILE}.
+ */
+ @Override
+ protected long doGetContentSize() throws Exception {
+ return file.getContent().getSize();
+ }
+
+ /**
+ * Returns the attributes of this file.
+ */
+ @Override
+ protected Map<String, Object> doGetAttributes() throws Exception {
+ return file.getContent().getAttributes();
+ }
+
+ /**
+ * Sets an attribute of this file.
+ */
+ @Override
+ protected void doSetAttribute(final String atttrName, final Object value) throws Exception {
+ file.getContent().setAttribute(atttrName, value);
+ }
+
+ /**
+ * Returns the certificates of this file.
+ */
+ @Override
+ protected Certificate[] doGetCertificates() throws Exception {
+ return file.getContent().getCertificates();
+ }
+
+ /**
+ * Returns the last-modified time of this file.
+ */
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ return file.getContent().getLastModifiedTime();
+ }
+
+ /**
+ * Sets the last-modified time of this file.
+ *
+ * @since 2.0
+ */
+ @Override
+ protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
+ file.getContent().setLastModifiedTime(modtime);
+ return true;
+ }
+
+ /**
+ * Creates an input stream to read the file content from.
+ */
+ @Override
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
+ return file.getContent().getInputStream(bufferSize);
+ }
+
+ /**
+ * Creates an output stream to write the file content to.
+ */
+ @Override
+ protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
+ return file.getContent().getOutputStream(bAppend);
+ }
+
+ /**
+ * Called when a file is created.
+ *
+ * @param event The FileChangeEvent.
+ * @throws Exception if an error occurs.
+ */
+ @Override
+ public void fileCreated(final FileChangeEvent event) throws Exception {
+ if (event.getFile() != file) {
+ return;
+ }
+ if (!ignoreEvent) {
+ handleCreate(file.getType());
+ }
+ }
+
+ /**
+ * Called when a file is deleted.
+ *
+ * @param event The FileChangeEvent.
+ * @throws Exception if an error occurs.
+ */
+ @Override
+ public void fileDeleted(final FileChangeEvent event) throws Exception {
+ if (event.getFile() != file) {
+ return;
+ }
+ if (!ignoreEvent) {
+ handleDelete();
+ }
+ }
+
+ /**
+ * Called when a file is changed.
+ * <p>
+ * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}.
+ * </p>
+ *
+ * @param event The FileChangeEvent.
+ * @throws Exception if an error occurs.
+ */
+ @Override
+ public void fileChanged(final FileChangeEvent event) throws Exception {
+ if (event.getFile() != file) {
+ return;
+ }
+ if (!ignoreEvent) {
+ handleChanged();
+ }
+ }
+
+ /**
+ * Close the delegated file.
+ *
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void close() throws FileSystemException {
+ super.close();
+
+ if (file != null) {
+ file.close();
+ }
+ }
+
+ /**
+ * Refresh file information.
+ *
+ * @throws FileSystemException if an error occurs.
+ * @since 2.0
+ */
+ @Override
+ public void refresh() throws FileSystemException {
+ super.refresh();
+ if (file != null) {
+ file.refresh();
+ }
+ }
+
+ /**
+ * Return file content info.
+ *
+ * @return the file content info of the delegee.
+ * @throws Exception Any thrown Exception is wrapped in FileSystemException.
+ * @since 2.0
+ */
+ protected FileContentInfo doGetContentInfo() throws Exception {
+ return file.getContent().getContentInfo();
+ }
+
+ /**
+ * Renames the file.
+ *
+ * @param newFile the new location/name.
+ * @throws Exception Any thrown Exception is wrapped in FileSystemException.
+ * @since 2.0
+ */
+ @Override
+ protected void doRename(final FileObject newFile) throws Exception {
+ file.moveTo(((DelegateFileObject) newFile).file);
+ }
+
+ /**
+ * Removes an attribute of this file.
+ *
+ * @since 2.0
+ */
+ @Override
+ protected void doRemoveAttribute(final String atttrName) throws Exception {
+ file.getContent().removeAttribute(atttrName);
+ }
+
+ /**
+ * Creates access to the file for random i/o.
+ *
+ * @since 2.0
+ */
+ @Override
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
+ return file.getContent().getRandomAccessContent(mode);
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/bzip2/Bzip2FileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/bzip2/Bzip2FileObject.java
index 44c2909..5592e47 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/bzip2/Bzip2FileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/bzip2/Bzip2FileObject.java
@@ -52,9 +52,9 @@ public class Bzip2FileObject extends CompressedFileFileObject<Bzip2FileSystem> {
}
@Override
- protected InputStream doGetInputStream() throws Exception {
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
// check file
- final InputStream is = getContainer().getContent().getInputStream();
+ final InputStream is = getContainer().getContent().getInputStream(bufferSize);
return wrapInputStream(getName().getURI(), is);
}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FTPClientWrapper.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FTPClientWrapper.java
index 3c31df9..e956a9f 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FTPClientWrapper.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FTPClientWrapper.java
@@ -251,6 +251,20 @@ public class FTPClientWrapper implements FtpClient {
}
@Override
+ public InputStream retrieveFileStream(final String relPath, final int bufferSize) throws IOException {
+ try {
+ final FTPClient client = getFtpClient();
+ client.setBufferSize(bufferSize);
+ return client.retrieveFileStream(relPath);
+ } catch (final IOException e) {
+ disconnect();
+ final FTPClient client = getFtpClient();
+ client.setBufferSize(bufferSize);
+ return client.retrieveFileStream(relPath);
+ }
+ }
+
+ @Override
public InputStream retrieveFileStream(final String relPath, final long restartOffset) throws IOException {
try {
final FTPClient client = getFtpClient();
@@ -265,6 +279,11 @@ public class FTPClientWrapper implements FtpClient {
}
@Override
+ public void setBufferSize(final int bufferSize) throws FileSystemException {
+ getFtpClient().setBufferSize(bufferSize);
+ }
+
+ @Override
public OutputStream storeFileStream(final String relPath) throws IOException {
try {
return getFtpClient().storeFileStream(relPath);
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpClient.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpClient.java
index 794e1ee..4afce34 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpClient.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpClient.java
@@ -57,8 +57,17 @@ public interface FtpClient {
InputStream retrieveFileStream(String relPath) throws IOException;
+ default InputStream retrieveFileStream(String relPath, int bufferSize) throws IOException {
+ // Backward compatibility: no buffer size.
+ return retrieveFileStream(relPath);
+ }
+
InputStream retrieveFileStream(String relPath, long restartOffset) throws IOException;
+ default void setBufferSize(int bufferSize) throws FileSystemException {
+ // Backward compatibility: do nothing.
+ }
+
OutputStream storeFileStream(String relPath) throws IOException;
}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpFileObject.java
index 09336f8..b75ad43 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/ftp/FtpFileObject.java
@@ -1,627 +1,632 @@
-/*
- * 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.ftp;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.commons.net.ftp.FTPFile;
-import org.apache.commons.vfs2.FileName;
-import org.apache.commons.vfs2.FileNotFolderException;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.provider.AbstractFileName;
-import org.apache.commons.vfs2.provider.AbstractFileObject;
-import org.apache.commons.vfs2.provider.UriParser;
-import org.apache.commons.vfs2.util.FileObjectUtils;
-import org.apache.commons.vfs2.util.Messages;
-import org.apache.commons.vfs2.util.MonitorInputStream;
-import org.apache.commons.vfs2.util.MonitorOutputStream;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-
-/**
- * An FTP file.
- */
-public class FtpFileObject extends AbstractFileObject<FtpFileSystem> {
-
- private static final long DEFAULT_TIMESTAMP = 0L;
- private static final Map<String, FTPFile> EMPTY_FTP_FILE_MAP = Collections
- .unmodifiableMap(new TreeMap<String, FTPFile>());
- private static final FTPFile UNKNOWN = new FTPFile();
- private static final Log log = LogFactory.getLog(FtpFileObject.class);
-
- private final String relPath;
-
- // Cached info
- private volatile FTPFile fileInfo;
- private volatile Map<String, FTPFile> children;
- private volatile FileObject linkDestination;
- private final AtomicBoolean inRefresh = new AtomicBoolean();
-
- protected FtpFileObject(final AbstractFileName name, final FtpFileSystem fileSystem, final FileName rootName)
- throws FileSystemException {
- super(name, fileSystem);
- final String relPath = UriParser.decode(rootName.getRelativeName(name));
- if (".".equals(relPath)) {
- // do not use the "." as path against the ftp-server
- // e.g. the uu.net ftp-server do a recursive listing then
- // this.relPath = UriParser.decode(rootName.getPath());
- // this.relPath = ".";
- this.relPath = null;
- } else {
- this.relPath = relPath;
- }
- }
-
- /**
- * Called by child file objects, to locate their ftp file info.
- *
- * @param name the file name in its native form ie. without uri stuff (%nn)
- * @param flush recreate children cache
- */
- private FTPFile getChildFile(final String name, final boolean flush) throws IOException {
- /*
- * If we should flush cached children, clear our children map unless we're in the middle of a refresh in which
- * case we've just recently refreshed our children. No need to do it again when our children are refresh()ed,
- * calling getChildFile() for themselves from within getInfo(). See getChildren().
- */
- if (flush && !inRefresh.get()) {
- children = null;
- }
-
- // List the children of this file
- doGetChildren();
-
- // Look for the requested child
- // VFS-210 adds the null check.
- return children != null ? children.get(name) : null;
- }
-
- /**
- * Fetches the children of this file, if not already cached.
- */
- private void doGetChildren() throws IOException {
- if (children != null) {
- return;
- }
-
- final FtpClient client = getAbstractFileSystem().getClient();
- try {
- final String path = fileInfo != null && fileInfo.isSymbolicLink()
- ? getFileSystem().getFileSystemManager().resolveName(getParent().getName(), fileInfo.getLink())
- .getPath()
- : relPath;
- final FTPFile[] tmpChildren = client.listFiles(path);
- if (tmpChildren == null || tmpChildren.length == 0) {
- children = EMPTY_FTP_FILE_MAP;
- } else {
- children = new TreeMap<>();
-
- // Remove '.' and '..' elements
- for (int i = 0; i < tmpChildren.length; i++) {
- final FTPFile child = tmpChildren[i];
- if (child == null) {
- if (log.isDebugEnabled()) {
- log.debug(Messages.getString("vfs.provider.ftp/invalid-directory-entry.debug",
- Integer.valueOf(i), relPath));
- }
- continue;
- }
- if (!".".equals(child.getName()) && !"..".equals(child.getName())) {
- children.put(child.getName(), child);
- }
- }
- }
- } finally {
- getAbstractFileSystem().putClient(client);
- }
- }
-
- /**
- * Attaches this file object to its file resource.
- */
- @Override
- protected void doAttach() throws IOException {
- // Get the parent folder to find the info for this file
- // VFS-210 getInfo(false);
- }
-
- /**
- * Fetches the info for this file.
- */
- private void getInfo(final boolean flush) throws IOException {
- synchronized (getFileSystem()) {
- final FtpFileObject parent = (FtpFileObject) FileObjectUtils.getAbstractFileObject(getParent());
- FTPFile newFileInfo;
- if (parent != null) {
- newFileInfo = parent.getChildFile(UriParser.decode(getName().getBaseName()), flush);
- } else {
- // Assume the root is a directory and exists
- newFileInfo = new FTPFile();
- newFileInfo.setType(FTPFile.DIRECTORY_TYPE);
- }
-
- if (newFileInfo == null) {
- this.fileInfo = UNKNOWN;
- } else {
- this.fileInfo = newFileInfo;
- }
- }}
-
- /**
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public void refresh() throws FileSystemException {
- if (inRefresh.compareAndSet(false, true)) {
- try {
- super.refresh();
- synchronized (getFileSystem()) {
- this.fileInfo = null;
- }
- /*
- * VFS-210 try { // this will tell the parent to recreate its children collection getInfo(true); } catch
- * (IOException e) { throw new FileSystemException(e); }
- */
- } finally {
- inRefresh.set(false);
- }
- }
- }
-
- /**
- * Detaches this file object from its file resource.
- */
- @Override
- protected void doDetach() {
- synchronized (getFileSystem()) {
- this.fileInfo = null;
- this.children = null;
- }
- }
-
- /**
- * Called when the children of this file change.
- */
- @Override
- protected void onChildrenChanged(final FileName child, final FileType newType) {
- if (children != null && newType.equals(FileType.IMAGINARY)) {
- try {
- children.remove(UriParser.decode(child.getBaseName()));
- } catch (final FileSystemException e) {
- throw new RuntimeException(e.getMessage());
- }
- } else {
- // if child was added we have to rescan the children
- // TODO - get rid of this
- children = null;
- }
- }
-
- /**
- * Called when the type or content of this file changes.
- */
- @Override
- protected void onChange() throws IOException {
- children = null;
-
- if (getType().equals(FileType.IMAGINARY)) {
- // file is deleted, avoid server lookup
- synchronized (getFileSystem()) {
- this.fileInfo = UNKNOWN;
- }
- return;
- }
-
- getInfo(true);
- }
-
- /**
- * Determines the type of the file, returns null if the file does not exist.
- */
- @Override
- protected FileType doGetType() throws Exception {
- // VFS-210
- synchronized (getFileSystem()) {
- if (this.fileInfo == null) {
- getInfo(false);
- }
-
- if (this.fileInfo == UNKNOWN) {
- return FileType.IMAGINARY;
- } else if (this.fileInfo.isDirectory()) {
- return FileType.FOLDER;
- } else if (this.fileInfo.isFile()) {
- return FileType.FILE;
- } else if (this.fileInfo.isSymbolicLink()) {
- final FileObject linkDest = getLinkDestination();
- // VFS-437: We need to check if the symbolic link links back to the symbolic link itself
- if (this.isCircular(linkDest)) {
- // If the symbolic link links back to itself, treat it as an imaginary file to prevent following
- // this link. If the user tries to access the link as a file or directory, the user will end up with
- // a FileSystemException warning that the file cannot be accessed. This is to prevent the infinite
- // call back to doGetType() to prevent the StackOverFlow
- return FileType.IMAGINARY;
- }
- return linkDest.getType();
-
- }
- }
- throw new FileSystemException("vfs.provider.ftp/get-type.error", getName());
- }
-
- private FileObject getLinkDestination() throws FileSystemException {
- if (linkDestination == null) {
- final String path;
- synchronized (getFileSystem()) {
- path = this.fileInfo == null ? null : this.fileInfo.getLink();
- }
- final FileName parent = getName().getParent();
- final FileName relativeTo = parent == null ? getName() : parent;
- final FileName linkDestinationName = getFileSystem().getFileSystemManager().resolveName(relativeTo, path);
- linkDestination = getFileSystem().resolveFile(linkDestinationName);
- }
- return linkDestination;
- }
-
- @Override
- protected FileObject[] doListChildrenResolved() throws Exception {
- synchronized (getFileSystem()) {
- if (this.fileInfo != null && this.fileInfo.isSymbolicLink()) {
- final FileObject linkDest = getLinkDestination();
- // VFS-437: Try to avoid a recursion loop.
- if (this.isCircular(linkDest)) {
- return null;
- }
- return linkDest.getChildren();
- }
- }
- return null;
- }
-
- /**
- * Returns the file's list of children.
- *
- * @return The list of children
- * @throws FileSystemException If there was a problem listing children
- * @see AbstractFileObject#getChildren()
- * @since 2.0
- */
- @Override
- public FileObject[] getChildren() throws FileSystemException {
- try {
- if (doGetType() != FileType.FOLDER) {
- throw new FileNotFolderException(getName());
- }
- } catch (final Exception ex) {
- throw new FileNotFolderException(getName(), ex);
- }
-
- try {
- /*
- * Wrap our parent implementation, noting that we're refreshing so that we don't refresh() ourselves and
- * each of our parents for each children. Note that refresh() will list children. Meaning, if if this file
- * has C children, P parents, there will be (C * P) listings made with (C * (P + 1)) refreshes, when there
- * should really only be 1 listing and C refreshes.
- */
- this.inRefresh.set(true);
- return super.getChildren();
- } finally {
- this.inRefresh.set(false);
- }
- }
-
- /**
- * Lists the children of the file.
- */
- @Override
- protected String[] doListChildren() throws Exception {
- // List the children of this file
- doGetChildren();
-
- // VFS-210
- if (children == null) {
- return null;
- }
-
- // TODO - get rid of this children stuff
- final String[] childNames = new String[children.size()];
- int childNum = -1;
- final Iterator<FTPFile> iterChildren = children.values().iterator();
- while (iterChildren.hasNext()) {
- childNum++;
- final FTPFile child = iterChildren.next();
- childNames[childNum] = child.getName();
- }
-
- return UriParser.encode(childNames);
- }
-
- /**
- * Deletes the file.
- */
- @Override
- protected void doDelete() throws Exception {
- synchronized (getFileSystem()) {
- if (this.fileInfo != null) {
- final boolean ok;
- final FtpClient ftpClient = getAbstractFileSystem().getClient();
- try {
- if (this.fileInfo.isDirectory()) {
- ok = ftpClient.removeDirectory(relPath);
- } else {
- ok = ftpClient.deleteFile(relPath);
- }
- } finally {
- getAbstractFileSystem().putClient(ftpClient);
- }
-
- if (!ok) {
- throw new FileSystemException("vfs.provider.ftp/delete-file.error", getName());
- }
- this.fileInfo = null;
- }
- this.children = EMPTY_FTP_FILE_MAP;
- }
- }
-
- /**
- * Renames the file
- */
- @Override
- protected void doRename(final FileObject newFile) throws Exception {
- synchronized (getFileSystem()) {
- final boolean ok;
- final FtpClient ftpClient = getAbstractFileSystem().getClient();
- try {
- final String oldName = relPath;
- final String newName = ((FtpFileObject) FileObjectUtils.getAbstractFileObject(newFile)).getRelPath();
- ok = ftpClient.rename(oldName, newName);
- } finally {
- getAbstractFileSystem().putClient(ftpClient);
- }
-
- if (!ok) {
- throw new FileSystemException("vfs.provider.ftp/rename-file.error", getName().toString(), newFile);
- }
- this.fileInfo = null;
- this.children = EMPTY_FTP_FILE_MAP;
- }
- }
-
- /**
- * Creates this file as a folder.
- */
- @Override
- protected void doCreateFolder() throws Exception {
- final boolean ok;
- final FtpClient client = getAbstractFileSystem().getClient();
- try {
- ok = client.makeDirectory(relPath);
- } finally {
- getAbstractFileSystem().putClient(client);
- }
-
- if (!ok) {
- throw new FileSystemException("vfs.provider.ftp/create-folder.error", getName());
- }
- }
-
- /**
- * Returns the size of the file content (in bytes).
- */
- @Override
- protected long doGetContentSize() throws Exception {
- synchronized (getFileSystem()) {
- if (this.fileInfo == null) {
- return 0;
- }
- if (this.fileInfo.isSymbolicLink()) {
- final FileObject linkDest = getLinkDestination();
- // VFS-437: Try to avoid a recursion loop.
- if (this.isCircular(linkDest)) {
- return this.fileInfo.getSize();
- }
- return linkDest.getContent().getSize();
- }
- return this.fileInfo.getSize();
- }
- }
-
- /**
- * get the last modified time on an ftp file
- *
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetLastModifiedTime()
- */
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- synchronized (getFileSystem()) {
- if (this.fileInfo == null) {
- return DEFAULT_TIMESTAMP;
- }
- if (this.fileInfo.isSymbolicLink()) {
- final FileObject linkDest = getLinkDestination();
- // VFS-437: Try to avoid a recursion loop.
- if (this.isCircular(linkDest)) {
- return getTimestamp();
- }
- return linkDest.getContent().getLastModifiedTime();
- }
- return getTimestamp();
- }
- }
-
- /**
- * Creates an input stream to read the file content from.
- */
- @Override
- protected InputStream doGetInputStream() throws Exception {
- final FtpClient client = getAbstractFileSystem().getClient();
- try {
- final InputStream instr = client.retrieveFileStream(relPath);
- // VFS-210
- if (instr == null) {
- throw new FileNotFoundException(getName().toString());
- }
- return new FtpInputStream(client, instr);
- } catch (final Exception e) {
- getAbstractFileSystem().putClient(client);
- throw e;
- }
- }
-
- @Override
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- return new FtpRandomAccessContent(this, mode);
- }
-
- /**
- * Creates an output stream to write the file content to.
- */
- @Override
- protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
- final FtpClient client = getAbstractFileSystem().getClient();
- try {
- OutputStream out = null;
- if (bAppend) {
- out = client.appendFileStream(relPath);
- } else {
- out = client.storeFileStream(relPath);
- }
-
- FileSystemException.requireNonNull(out, "vfs.provider.ftp/output-error.debug", this.getName(),
- client.getReplyString());
-
- return new FtpOutputStream(client, out);
- } catch (final Exception e) {
- getAbstractFileSystem().putClient(client);
- throw e;
- }
- }
-
- String getRelPath() {
- return relPath;
- }
-
- private long getTimestamp() {
- final Calendar timestamp = this.fileInfo != null ? this.fileInfo.getTimestamp() : null;
- return timestamp == null ? DEFAULT_TIMESTAMP : timestamp.getTime().getTime();
- }
-
- /**
- * This is an over simplistic implementation for VFS-437.
- */
- private boolean isCircular(final FileObject linkDest) throws FileSystemException {
- return linkDest.getName().getPathDecoded().equals(this.getName().getPathDecoded());
- }
-
- FtpInputStream getInputStream(final long filePointer) throws IOException {
- final FtpClient client = getAbstractFileSystem().getClient();
- try {
- final InputStream instr = client.retrieveFileStream(relPath, filePointer);
- FileSystemException.requireNonNull(instr, "vfs.provider.ftp/input-error.debug", this.getName(),
- client.getReplyString());
- return new FtpInputStream(client, instr);
- } catch (final IOException e) {
- getAbstractFileSystem().putClient(client);
- throw e;
- }
- }
-
- /**
- * An InputStream that monitors for end-of-file.
- */
- class FtpInputStream extends MonitorInputStream {
- private final FtpClient client;
-
- public FtpInputStream(final FtpClient client, final InputStream in) {
- super(in);
- this.client = client;
- }
-
- void abort() throws IOException {
- client.abort();
- close();
- }
-
- /**
- * Called after the stream has been closed.
- */
- @Override
- protected void onClose() throws IOException {
- final boolean ok;
- try {
- ok = client.completePendingCommand() || isTransferAbortedOkReplyCode();
- } finally {
- getAbstractFileSystem().putClient(client);
- }
-
- if (!ok) {
- throw new FileSystemException("vfs.provider.ftp/finish-get.error", getName());
- }
- }
-
- private boolean isTransferAbortedOkReplyCode() throws IOException {
- final List<Integer> transferAbortedOkReplyCodes = FtpFileSystemConfigBuilder
- .getInstance()
- .getTransferAbortedOkReplyCodes(getAbstractFileSystem().getFileSystemOptions());
- return transferAbortedOkReplyCodes != null && transferAbortedOkReplyCodes.contains(client.getReplyCode());
- }
- }
-
- /**
- * An OutputStream that monitors for end-of-file.
- */
- private class FtpOutputStream extends MonitorOutputStream {
- private final FtpClient client;
-
- public FtpOutputStream(final FtpClient client, final OutputStream outstr) {
- super(outstr);
- this.client = client;
- }
-
- /**
- * Called after this stream is closed.
- */
- @Override
- protected void onClose() throws IOException {
- final boolean ok;
- try {
- ok = client.completePendingCommand();
- } finally {
- getAbstractFileSystem().putClient(client);
- }
-
- if (!ok) {
- throw new FileSystemException("vfs.provider.ftp/finish-put.error", getName());
- }
- }
- }
-}
+/*
+ * 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.ftp;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileNotFolderException;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.provider.AbstractFileName;
+import org.apache.commons.vfs2.provider.AbstractFileObject;
+import org.apache.commons.vfs2.provider.UriParser;
+import org.apache.commons.vfs2.util.FileObjectUtils;
+import org.apache.commons.vfs2.util.Messages;
+import org.apache.commons.vfs2.util.MonitorInputStream;
+import org.apache.commons.vfs2.util.MonitorOutputStream;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+
+/**
+ * An FTP file.
+ */
+public class FtpFileObject extends AbstractFileObject<FtpFileSystem> {
+
+ private static final long DEFAULT_TIMESTAMP = 0L;
+ private static final Map<String, FTPFile> EMPTY_FTP_FILE_MAP = Collections
+ .unmodifiableMap(new TreeMap<String, FTPFile>());
+ private static final FTPFile UNKNOWN = new FTPFile();
+ private static final Log log = LogFactory.getLog(FtpFileObject.class);
+
+ private final String relPath;
+
+ // Cached info
+ private volatile FTPFile fileInfo;
+ private volatile Map<String, FTPFile> children;
+ private volatile FileObject linkDestination;
+ private final AtomicBoolean inRefresh = new AtomicBoolean();
+
+ protected FtpFileObject(final AbstractFileName name, final FtpFileSystem fileSystem, final FileName rootName)
+ throws FileSystemException {
+ super(name, fileSystem);
+ final String relPath = UriParser.decode(rootName.getRelativeName(name));
+ if (".".equals(relPath)) {
+ // do not use the "." as path against the ftp-server
+ // e.g. the uu.net ftp-server do a recursive listing then
+ // this.relPath = UriParser.decode(rootName.getPath());
+ // this.relPath = ".";
+ this.relPath = null;
+ } else {
+ this.relPath = relPath;
+ }
+ }
+
+ /**
+ * Called by child file objects, to locate their ftp file info.
+ *
+ * @param name the file name in its native form ie. without uri stuff (%nn)
+ * @param flush recreate children cache
+ */
+ private FTPFile getChildFile(final String name, final boolean flush) throws IOException {
+ /*
+ * If we should flush cached children, clear our children map unless we're in the middle of a refresh in which
+ * case we've just recently refreshed our children. No need to do it again when our children are refresh()ed,
+ * calling getChildFile() for themselves from within getInfo(). See getChildren().
+ */
+ if (flush && !inRefresh.get()) {
+ children = null;
+ }
+
+ // List the children of this file
+ doGetChildren();
+
+ // Look for the requested child
+ // VFS-210 adds the null check.
+ return children != null ? children.get(name) : null;
+ }
+
+ /**
+ * Fetches the children of this file, if not already cached.
+ */
+ private void doGetChildren() throws IOException {
+ if (children != null) {
+ return;
+ }
+
+ final FtpClient client = getAbstractFileSystem().getClient();
+ try {
+ final String path = fileInfo != null && fileInfo.isSymbolicLink()
+ ? getFileSystem().getFileSystemManager().resolveName(getParent().getName(), fileInfo.getLink())
+ .getPath()
+ : relPath;
+ final FTPFile[] tmpChildren = client.listFiles(path);
+ if (tmpChildren == null || tmpChildren.length == 0) {
+ children = EMPTY_FTP_FILE_MAP;
+ } else {
+ children = new TreeMap<>();
+
+ // Remove '.' and '..' elements
+ for (int i = 0; i < tmpChildren.length; i++) {
+ final FTPFile child = tmpChildren[i];
+ if (child == null) {
+ if (log.isDebugEnabled()) {
+ log.debug(Messages.getString("vfs.provider.ftp/invalid-directory-entry.debug",
+ Integer.valueOf(i), relPath));
+ }
+ continue;
+ }
+ if (!".".equals(child.getName()) && !"..".equals(child.getName())) {
+ children.put(child.getName(), child);
+ }
+ }
+ }
+ } finally {
+ getAbstractFileSystem().putClient(client);
+ }
+ }
+
+ /**
+ * Attaches this file object to its file resource.
+ */
+ @Override
+ protected void doAttach() throws IOException {
+ // Get the parent folder to find the info for this file
+ // VFS-210 getInfo(false);
+ }
+
+ /**
+ * Fetches the info for this file.
+ */
+ private void getInfo(final boolean flush) throws IOException {
+ synchronized (getFileSystem()) {
+ final FtpFileObject parent = (FtpFileObject) FileObjectUtils.getAbstractFileObject(getParent());
+ FTPFile newFileInfo;
+ if (parent != null) {
+ newFileInfo = parent.getChildFile(UriParser.decode(getName().getBaseName()), flush);
+ } else {
+ // Assume the root is a directory and exists
+ newFileInfo = new FTPFile();
+ newFileInfo.setType(FTPFile.DIRECTORY_TYPE);
+ }
+
+ if (newFileInfo == null) {
+ this.fileInfo = UNKNOWN;
+ } else {
+ this.fileInfo = newFileInfo;
+ }
+ }}
+
+ /**
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public void refresh() throws FileSystemException {
+ if (inRefresh.compareAndSet(false, true)) {
+ try {
+ super.refresh();
+ synchronized (getFileSystem()) {
+ this.fileInfo = null;
+ }
+ /*
+ * VFS-210 try { // this will tell the parent to recreate its children collection getInfo(true); } catch
+ * (IOException e) { throw new FileSystemException(e); }
+ */
+ } finally {
+ inRefresh.set(false);
+ }
+ }
+ }
+
+ /**
+ * Detaches this file object from its file resource.
+ */
+ @Override
+ protected void doDetach() {
+ synchronized (getFileSystem()) {
+ this.fileInfo = null;
+ this.children = null;
+ }
+ }
+
+ /**
+ * Called when the children of this file change.
+ */
+ @Override
+ protected void onChildrenChanged(final FileName child, final FileType newType) {
+ if (children != null && newType.equals(FileType.IMAGINARY)) {
+ try {
+ children.remove(UriParser.decode(child.getBaseName()));
+ } catch (final FileSystemException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ } else {
+ // if child was added we have to rescan the children
+ // TODO - get rid of this
+ children = null;
+ }
+ }
+
+ /**
+ * Called when the type or content of this file changes.
+ */
+ @Override
+ protected void onChange() throws IOException {
+ children = null;
+
+ if (getType().equals(FileType.IMAGINARY)) {
+ // file is deleted, avoid server lookup
+ synchronized (getFileSystem()) {
+ this.fileInfo = UNKNOWN;
+ }
+ return;
+ }
+
+ getInfo(true);
+ }
+
+ /**
+ * Determines the type of the file, returns null if the file does not exist.
+ */
+ @Override
+ protected FileType doGetType() throws Exception {
+ // VFS-210
+ synchronized (getFileSystem()) {
+ if (this.fileInfo == null) {
+ getInfo(false);
+ }
+
+ if (this.fileInfo == UNKNOWN) {
+ return FileType.IMAGINARY;
+ } else if (this.fileInfo.isDirectory()) {
+ return FileType.FOLDER;
+ } else if (this.fileInfo.isFile()) {
+ return FileType.FILE;
+ } else if (this.fileInfo.isSymbolicLink()) {
+ final FileObject linkDest = getLinkDestination();
+ // VFS-437: We need to check if the symbolic link links back to the symbolic link itself
+ if (this.isCircular(linkDest)) {
+ // If the symbolic link links back to itself, treat it as an imaginary file to prevent following
+ // this link. If the user tries to access the link as a file or directory, the user will end up with
+ // a FileSystemException warning that the file cannot be accessed. This is to prevent the infinite
+ // call back to doGetType() to prevent the StackOverFlow
+ return FileType.IMAGINARY;
+ }
+ return linkDest.getType();
+
+ }
+ }
+ throw new FileSystemException("vfs.provider.ftp/get-type.error", getName());
+ }
+
+ private FileObject getLinkDestination() throws FileSystemException {
+ if (linkDestination == null) {
+ final String path;
+ synchronized (getFileSystem()) {
+ path = this.fileInfo == null ? null : this.fileInfo.getLink();
+ }
+ final FileName parent = getName().getParent();
+ final FileName relativeTo = parent == null ? getName() : parent;
+ final FileName linkDestinationName = getFileSystem().getFileSystemManager().resolveName(relativeTo, path);
+ linkDestination = getFileSystem().resolveFile(linkDestinationName);
+ }
+ return linkDestination;
+ }
+
+ @Override
+ protected FileObject[] doListChildrenResolved() throws Exception {
+ synchronized (getFileSystem()) {
+ if (this.fileInfo != null && this.fileInfo.isSymbolicLink()) {
+ final FileObject linkDest = getLinkDestination();
+ // VFS-437: Try to avoid a recursion loop.
+ if (this.isCircular(linkDest)) {
+ return null;
+ }
+ return linkDest.getChildren();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the file's list of children.
+ *
+ * @return The list of children
+ * @throws FileSystemException If there was a problem listing children
+ * @see AbstractFileObject#getChildren()
+ * @since 2.0
+ */
+ @Override
+ public FileObject[] getChildren() throws FileSystemException {
+ try {
+ if (doGetType() != FileType.FOLDER) {
+ throw new FileNotFolderException(getName());
+ }
+ } catch (final Exception ex) {
+ throw new FileNotFolderException(getName(), ex);
+ }
+
+ try {
+ /*
+ * Wrap our parent implementation, noting that we're refreshing so that we don't refresh() ourselves and
+ * each of our parents for each children. Note that refresh() will list children. Meaning, if if this file
+ * has C children, P parents, there will be (C * P) listings made with (C * (P + 1)) refreshes, when there
+ * should really only be 1 listing and C refreshes.
+ */
+ this.inRefresh.set(true);
+ return super.getChildren();
+ } finally {
+ this.inRefresh.set(false);
+ }
+ }
+
+ /**
+ * Lists the children of the file.
+ */
+ @Override
+ protected String[] doListChildren() throws Exception {
+ // List the children of this file
+ doGetChildren();
+
+ // VFS-210
+ if (children == null) {
+ return null;
+ }
+
+ // TODO - get rid of this children stuff
+ final String[] childNames = new String[children.size()];
+ int childNum = -1;
+ final Iterator<FTPFile> iterChildren = children.values().iterator();
+ while (iterChildren.hasNext()) {
+ childNum++;
+ final FTPFile child = iterChildren.next();
+ childNames[childNum] = child.getName();
+ }
+
+ return UriParser.encode(childNames);
+ }
+
+ /**
+ * Deletes the file.
+ */
+ @Override
+ protected void doDelete() throws Exception {
+ synchronized (getFileSystem()) {
+ if (this.fileInfo != null) {
+ final boolean ok;
+ final FtpClient ftpClient = getAbstractFileSystem().getClient();
+ try {
+ if (this.fileInfo.isDirectory()) {
+ ok = ftpClient.removeDirectory(relPath);
+ } else {
+ ok = ftpClient.deleteFile(relPath);
+ }
+ } finally {
+ getAbstractFileSystem().putClient(ftpClient);
+ }
+
+ if (!ok) {
+ throw new FileSystemException("vfs.provider.ftp/delete-file.error", getName());
+ }
+ this.fileInfo = null;
+ }
+ this.children = EMPTY_FTP_FILE_MAP;
+ }
+ }
+
+ /**
+ * Renames the file
+ */
+ @Override
+ protected void doRename(final FileObject newFile) throws Exception {
+ synchronized (getFileSystem()) {
+ final boolean ok;
+ final FtpClient ftpClient = getAbstractFileSystem().getClient();
+ try {
+ final String oldName = relPath;
+ final String newName = ((FtpFileObject) FileObjectUtils.getAbstractFileObject(newFile)).getRelPath();
+ ok = ftpClient.rename(oldName, newName);
+ } finally {
+ getAbstractFileSystem().putClient(ftpClient);
+ }
+
+ if (!ok) {
+ throw new FileSystemException("vfs.provider.ftp/rename-file.error", getName().toString(), newFile);
+ }
+ this.fileInfo = null;
+ this.children = EMPTY_FTP_FILE_MAP;
+ }
+ }
+
+ /**
+ * Creates this file as a folder.
+ */
+ @Override
+ protected void doCreateFolder() throws Exception {
+ final boolean ok;
+ final FtpClient client = getAbstractFileSystem().getClient();
+ try {
+ ok = client.makeDirectory(relPath);
+ } finally {
+ getAbstractFileSystem().putClient(client);
+ }
+
+ if (!ok) {
+ throw new FileSystemException("vfs.provider.ftp/create-folder.error", getName());
+ }
+ }
+
+ /**
+ * Returns the size of the file content (in bytes).
+ */
+ @Override
+ protected long doGetContentSize() throws Exception {
+ synchronized (getFileSystem()) {
+ if (this.fileInfo == null) {
+ return 0;
+ }
+ if (this.fileInfo.isSymbolicLink()) {
+ final FileObject linkDest = getLinkDestination();
+ // VFS-437: Try to avoid a recursion loop.
+ if (this.isCircular(linkDest)) {
+ return this.fileInfo.getSize();
+ }
+ return linkDest.getContent().getSize();
+ }
+ return this.fileInfo.getSize();
+ }
+ }
+
+ /**
+ * get the last modified time on an ftp file
+ *
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetLastModifiedTime()
+ */
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ synchronized (getFileSystem()) {
+ if (this.fileInfo == null) {
+ return DEFAULT_TIMESTAMP;
+ }
+ if (this.fileInfo.isSymbolicLink()) {
+ final FileObject linkDest = getLinkDestination();
+ // VFS-437: Try to avoid a recursion loop.
+ if (this.isCircular(linkDest)) {
+ return getTimestamp();
+ }
+ return linkDest.getContent().getLastModifiedTime();
+ }
+ return getTimestamp();
+ }
+ }
+
+ /**
+ * Creates an input stream to read the file content from.
+ */
+ @Override
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
+ final FtpClient client = getAbstractFileSystem().getClient();
+ try {
+ final InputStream instr = client.retrieveFileStream(relPath, 0);
+ // VFS-210
+ if (instr == null) {
+ throw new FileNotFoundException(getName().toString());
+ }
+ return new FtpInputStream(client, instr, bufferSize);
+ } catch (final Exception e) {
+ getAbstractFileSystem().putClient(client);
+ throw e;
+ }
+ }
+
+ @Override
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
+ return new FtpRandomAccessContent(this, mode);
+ }
+
+ /**
+ * Creates an output stream to write the file content to.
+ */
+ @Override
+ protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
+ final FtpClient client = getAbstractFileSystem().getClient();
+ try {
+ OutputStream out = null;
+ if (bAppend) {
+ out = client.appendFileStream(relPath);
+ } else {
+ out = client.storeFileStream(relPath);
+ }
+
+ FileSystemException.requireNonNull(out, "vfs.provider.ftp/output-error.debug", this.getName(),
+ client.getReplyString());
+
+ return new FtpOutputStream(client, out);
+ } catch (final Exception e) {
+ getAbstractFileSystem().putClient(client);
+ throw e;
+ }
+ }
+
+ String getRelPath() {
+ return relPath;
+ }
+
+ private long getTimestamp() {
+ final Calendar timestamp = this.fileInfo != null ? this.fileInfo.getTimestamp() : null;
+ return timestamp == null ? DEFAULT_TIMESTAMP : timestamp.getTime().getTime();
+ }
+
+ /**
+ * This is an over simplistic implementation for VFS-437.
+ */
+ private boolean isCircular(final FileObject linkDest) throws FileSystemException {
+ return linkDest.getName().getPathDecoded().equals(this.getName().getPathDecoded());
+ }
+
+ FtpInputStream getInputStream(final long filePointer) throws IOException {
+ final FtpClient client = getAbstractFileSystem().getClient();
+ try {
+ final InputStream instr = client.retrieveFileStream(relPath, filePointer);
+ FileSystemException.requireNonNull(instr, "vfs.provider.ftp/input-error.debug", this.getName(),
+ client.getReplyString());
+ return new FtpInputStream(client, instr);
+ } catch (final IOException e) {
+ getAbstractFileSystem().putClient(client);
+ throw e;
+ }
+ }
+
+ /**
+ * An InputStream that monitors for end-of-file.
+ */
+ class FtpInputStream extends MonitorInputStream {
+ private final FtpClient client;
+
+ public FtpInputStream(final FtpClient client, final InputStream in) {
+ super(in);
+ this.client = client;
+ }
+
+ public FtpInputStream(final FtpClient client, final InputStream in, final int bufferSize) {
+ super(in, bufferSize);
+ this.client = client;
+ }
+
+ void abort() throws IOException {
+ client.abort();
+ close();
+ }
+
+ /**
+ * Called after the stream has been closed.
+ */
+ @Override
+ protected void onClose() throws IOException {
+ final boolean ok;
+ try {
+ ok = client.completePendingCommand() || isTransferAbortedOkReplyCode();
+ } finally {
+ getAbstractFileSystem().putClient(client);
+ }
+
+ if (!ok) {
+ throw new FileSystemException("vfs.provider.ftp/finish-get.error", getName());
+ }
+ }
+
+ private boolean isTransferAbortedOkReplyCode() throws IOException {
+ final List<Integer> transferAbortedOkReplyCodes = FtpFileSystemConfigBuilder
+ .getInstance()
+ .getTransferAbortedOkReplyCodes(getAbstractFileSystem().getFileSystemOptions());
+ return transferAbortedOkReplyCodes != null && transferAbortedOkReplyCodes.contains(client.getReplyCode());
+ }
+ }
+
+ /**
+ * An OutputStream that monitors for end-of-file.
+ */
+ private class FtpOutputStream extends MonitorOutputStream {
+ private final FtpClient client;
+
+ public FtpOutputStream(final FtpClient client, final OutputStream outstr) {
+ super(outstr);
+ this.client = client;
+ }
+
+ /**
+ * Called after this stream is closed.
+ */
+ @Override
+ protected void onClose() throws IOException {
+ final boolean ok;
+ try {
+ ok = client.completePendingCommand();
+ } finally {
+ getAbstractFileSystem().putClient(client);
+ }
+
+ if (!ok) {
+ throw new FileSystemException("vfs.provider.ftp/finish-put.error", getName());
+ }
+ }
+ }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/gzip/GzipFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/gzip/GzipFileObject.java
index 498ff0e..7706ba1 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/gzip/GzipFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/gzip/GzipFileObject.java
@@ -51,9 +51,9 @@ public class GzipFileObject extends CompressedFileFileObject<GzipFileSystem> {
}
@Override
- protected InputStream doGetInputStream() throws Exception {
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
final InputStream is = getContainer().getContent().getInputStream();
- return new GZIPInputStream(is);
+ return new GZIPInputStream(is, bufferSize);
}
@Override
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/hdfs/HdfsFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/hdfs/HdfsFileObject.java
index 3964d2d..8be836f 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/hdfs/HdfsFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/hdfs/HdfsFileObject.java
@@ -1,261 +1,261 @@
-/*
- * 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.hdfs;
-
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.vfs2.FileNotFolderException;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.provider.AbstractFileName;
-import org.apache.commons.vfs2.provider.AbstractFileObject;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-import org.apache.hadoop.fs.FileStatus;
-import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.Path;
-
-/**
- * A VFS representation of an HDFS file.
- *
- * @since 2.1
- */
-public class HdfsFileObject extends AbstractFileObject<HdfsFileSystem> {
-
- private final HdfsFileSystem fs;
- private final FileSystem hdfs;
- private final Path path;
- private FileStatus stat;
-
- /**
- * Constructs a new HDFS FileObject
- *
- * @param name FileName
- * @param fs HdfsFileSystem instance
- * @param hdfs Hadoop FileSystem instance
- * @param p Path to the file in HDFS
- */
- protected HdfsFileObject(final AbstractFileName name, final HdfsFileSystem fs, final FileSystem hdfs,
- final Path p) {
- super(name, fs);
- this.fs = fs;
- this.hdfs = hdfs;
- this.path = p;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#canRenameTo(org.apache.commons.vfs2.FileObject)
- */
- @Override
- public boolean canRenameTo(final FileObject newfile) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doAttach()
- */
- @Override
- protected void doAttach() throws Exception {
- try {
- this.stat = this.hdfs.getFileStatus(this.path);
- } catch (final FileNotFoundException e) {
- this.stat = null;
- return;
- }
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetAttributes()
- */
- @Override
- protected Map<String, Object> doGetAttributes() throws Exception {
- if (null == this.stat) {
- return super.doGetAttributes();
- }
- final Map<String, Object> attrs = new HashMap<>();
- attrs.put(HdfsFileAttributes.LAST_ACCESS_TIME.toString(), this.stat.getAccessTime());
- attrs.put(HdfsFileAttributes.BLOCK_SIZE.toString(), this.stat.getBlockSize());
- attrs.put(HdfsFileAttributes.GROUP.toString(), this.stat.getGroup());
- attrs.put(HdfsFileAttributes.OWNER.toString(), this.stat.getOwner());
- attrs.put(HdfsFileAttributes.PERMISSIONS.toString(), this.stat.getPermission().toString());
- attrs.put(HdfsFileAttributes.LENGTH.toString(), this.stat.getLen());
- attrs.put(HdfsFileAttributes.MODIFICATION_TIME.toString(), this.stat.getModificationTime());
- return attrs;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetContentSize()
- */
- @Override
- protected long doGetContentSize() throws Exception {
- return stat.getLen();
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetInputStream()
- */
- @Override
- protected InputStream doGetInputStream() throws Exception {
- return this.hdfs.open(this.path);
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetLastModifiedTime()
- */
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- if (null != this.stat) {
- return this.stat.getModificationTime();
- }
- return -1;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetRandomAccessContent
- * (org.apache.commons.vfs2.util.RandomAccessMode)
- */
- @Override
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- if (mode.equals(RandomAccessMode.READWRITE)) {
- throw new UnsupportedOperationException();
- }
- return new HdfsRandomAccessContent(this.path, this.hdfs);
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetType()
- */
- @Override
- protected FileType doGetType() throws Exception {
- try {
- doAttach();
- if (null == stat) {
- return FileType.IMAGINARY;
- }
- if (stat.isDirectory()) {
- return FileType.FOLDER;
- }
- return FileType.FILE;
- } catch (final FileNotFoundException fnfe) {
- return FileType.IMAGINARY;
- }
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsHidden()
- */
- @Override
- protected boolean doIsHidden() throws Exception {
- return false;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsReadable()
- */
- @Override
- protected boolean doIsReadable() throws Exception {
- return true;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsWriteable()
- */
- @Override
- protected boolean doIsWriteable() throws Exception {
- return false;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildren()
- */
- @Override
- protected String[] doListChildren() throws Exception {
- if (this.doGetType() != FileType.FOLDER) {
- throw new FileNotFolderException(this);
- }
-
- final FileStatus[] files = this.hdfs.listStatus(this.path);
- final String[] children = new String[files.length];
- int i = 0;
- for (final FileStatus status : files) {
- children[i++] = status.getPath().getName();
- }
- return children;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildrenResolved()
- */
- @Override
- protected FileObject[] doListChildrenResolved() throws Exception {
- if (this.doGetType() != FileType.FOLDER) {
- return null;
- }
- final String[] children = doListChildren();
- final FileObject[] fo = new FileObject[children.length];
- for (int i = 0; i < children.length; i++) {
- final Path p = new Path(this.path, children[i]);
- fo[i] = this.fs.resolveFile(p.toUri().toString());
- }
- return fo;
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doRemoveAttribute(java.lang.String)
- */
- @Override
- protected void doRemoveAttribute(final String attrName) throws Exception {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetAttribute(java.lang.String, java.lang.Object)
- */
- @Override
- protected void doSetAttribute(final String attrName, final Object value) throws Exception {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetLastModifiedTime(long)
- */
- @Override
- protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @see org.apache.commons.vfs2.provider.AbstractFileObject#exists()
- * @return boolean true if file exists, false if not
- */
- @Override
- public boolean exists() throws FileSystemException {
- try {
- doAttach();
- return this.stat != null;
- } catch (final FileNotFoundException fne) {
- return false;
- } catch (final Exception e) {
- throw new FileSystemException("Unable to check existance ", e);
- }
- }
-
-}
+/*
+ * 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.hdfs;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.vfs2.FileNotFolderException;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.provider.AbstractFileName;
+import org.apache.commons.vfs2.provider.AbstractFileObject;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+
+/**
+ * A VFS representation of an HDFS file.
+ *
+ * @since 2.1
+ */
+public class HdfsFileObject extends AbstractFileObject<HdfsFileSystem> {
+
+ private final HdfsFileSystem fs;
+ private final FileSystem hdfs;
+ private final Path path;
+ private FileStatus stat;
+
+ /**
+ * Constructs a new HDFS FileObject
+ *
+ * @param name FileName
+ * @param fs HdfsFileSystem instance
+ * @param hdfs Hadoop FileSystem instance
+ * @param p Path to the file in HDFS
+ */
+ protected HdfsFileObject(final AbstractFileName name, final HdfsFileSystem fs, final FileSystem hdfs,
+ final Path p) {
+ super(name, fs);
+ this.fs = fs;
+ this.hdfs = hdfs;
+ this.path = p;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#canRenameTo(org.apache.commons.vfs2.FileObject)
+ */
+ @Override
+ public boolean canRenameTo(final FileObject newfile) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doAttach()
+ */
+ @Override
+ protected void doAttach() throws Exception {
+ try {
+ this.stat = this.hdfs.getFileStatus(this.path);
+ } catch (final FileNotFoundException e) {
+ this.stat = null;
+ return;
+ }
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetAttributes()
+ */
+ @Override
+ protected Map<String, Object> doGetAttributes() throws Exception {
+ if (null == this.stat) {
+ return super.doGetAttributes();
+ }
+ final Map<String, Object> attrs = new HashMap<>();
+ attrs.put(HdfsFileAttributes.LAST_ACCESS_TIME.toString(), this.stat.getAccessTime());
+ attrs.put(HdfsFileAttributes.BLOCK_SIZE.toString(), this.stat.getBlockSize());
+ attrs.put(HdfsFileAttributes.GROUP.toString(), this.stat.getGroup());
+ attrs.put(HdfsFileAttributes.OWNER.toString(), this.stat.getOwner());
+ attrs.put(HdfsFileAttributes.PERMISSIONS.toString(), this.stat.getPermission().toString());
+ attrs.put(HdfsFileAttributes.LENGTH.toString(), this.stat.getLen());
+ attrs.put(HdfsFileAttributes.MODIFICATION_TIME.toString(), this.stat.getModificationTime());
+ return attrs;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetContentSize()
+ */
+ @Override
+ protected long doGetContentSize() throws Exception {
+ return stat.getLen();
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetInputStream(int)
+ */
+ @Override
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
+ return this.hdfs.open(this.path, bufferSize);
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetLastModifiedTime()
+ */
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ if (null != this.stat) {
+ return this.stat.getModificationTime();
+ }
+ return -1;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetRandomAccessContent
+ * (org.apache.commons.vfs2.util.RandomAccessMode)
+ */
+ @Override
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
+ if (mode.equals(RandomAccessMode.READWRITE)) {
+ throw new UnsupportedOperationException();
+ }
+ return new HdfsRandomAccessContent(this.path, this.hdfs);
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doGetType()
+ */
+ @Override
+ protected FileType doGetType() throws Exception {
+ try {
+ doAttach();
+ if (null == stat) {
+ return FileType.IMAGINARY;
+ }
+ if (stat.isDirectory()) {
+ return FileType.FOLDER;
+ }
+ return FileType.FILE;
+ } catch (final FileNotFoundException fnfe) {
+ return FileType.IMAGINARY;
+ }
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsHidden()
+ */
+ @Override
+ protected boolean doIsHidden() throws Exception {
+ return false;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsReadable()
+ */
+ @Override
+ protected boolean doIsReadable() throws Exception {
+ return true;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doIsWriteable()
+ */
+ @Override
+ protected boolean doIsWriteable() throws Exception {
+ return false;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildren()
+ */
+ @Override
+ protected String[] doListChildren() throws Exception {
+ if (this.doGetType() != FileType.FOLDER) {
+ throw new FileNotFolderException(this);
+ }
+
+ final FileStatus[] files = this.hdfs.listStatus(this.path);
+ final String[] children = new String[files.length];
+ int i = 0;
+ for (final FileStatus status : files) {
+ children[i++] = status.getPath().getName();
+ }
+ return children;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doListChildrenResolved()
+ */
+ @Override
+ protected FileObject[] doListChildrenResolved() throws Exception {
+ if (this.doGetType() != FileType.FOLDER) {
+ return null;
+ }
+ final String[] children = doListChildren();
+ final FileObject[] fo = new FileObject[children.length];
+ for (int i = 0; i < children.length; i++) {
+ final Path p = new Path(this.path, children[i]);
+ fo[i] = this.fs.resolveFile(p.toUri().toString());
+ }
+ return fo;
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doRemoveAttribute(java.lang.String)
+ */
+ @Override
+ protected void doRemoveAttribute(final String attrName) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetAttribute(java.lang.String, java.lang.Object)
+ */
+ @Override
+ protected void doSetAttribute(final String attrName, final Object value) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#doSetLastModifiedTime(long)
+ */
+ @Override
+ protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @see org.apache.commons.vfs2.provider.AbstractFileObject#exists()
+ * @return boolean true if file exists, false if not
+ */
+ @Override
+ public boolean exists() throws FileSystemException {
+ try {
+ doAttach();
+ return this.stat != null;
+ } catch (final FileNotFoundException fne) {
+ return false;
+ } catch (final Exception e) {
+ throw new FileSystemException("Unable to check existance ", e);
+ }
+ }
+
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http/HttpFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http/HttpFileObject.java
index 1e230cb..2066e54 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http/HttpFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http/HttpFileObject.java
@@ -1,247 +1,252 @@
-/*
- * 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.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.URIException;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.commons.httpclient.methods.HeadMethod;
-import org.apache.commons.httpclient.util.DateUtil;
-import org.apache.commons.httpclient.util.URIUtil;
-import org.apache.commons.vfs2.FileContentInfoFactory;
-import org.apache.commons.vfs2.FileNotFoundException;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileSystemOptions;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.provider.AbstractFileName;
-import org.apache.commons.vfs2.provider.AbstractFileObject;
-import org.apache.commons.vfs2.provider.URLFileName;
-import org.apache.commons.vfs2.util.MonitorInputStream;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-
-/**
- * A file object backed by Apache Commons HttpClient.
- * <p>
- * TODO - status codes.
- * </p>
- *
- * @param <FS> An {@link HttpFileSystem} subclass
- */
-public class HttpFileObject<FS extends HttpFileSystem> extends AbstractFileObject<FS> {
-
- /**
- * An InputStream that cleans up the HTTP connection on close.
- */
- static class HttpInputStream extends MonitorInputStream {
- private final GetMethod method;
-
- public HttpInputStream(final GetMethod method) throws IOException {
- super(method.getResponseBodyAsStream());
- this.method = method;
- }
-
- /**
- * Called after the stream has been closed.
- */
- @Override
- protected void onClose() throws IOException {
- method.releaseConnection();
- }
- }
-
- private final String urlCharset;
- private final String userAgent;
- private final boolean followRedirect;
-
- private HeadMethod method;
-
- protected HttpFileObject(final AbstractFileName name, final FS fileSystem) {
- this(name, fileSystem, HttpFileSystemConfigBuilder.getInstance());
- }
-
- protected HttpFileObject(final AbstractFileName name, final FS fileSystem,
- final HttpFileSystemConfigBuilder builder) {
- super(name, fileSystem);
- final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions();
- urlCharset = builder.getUrlCharset(fileSystemOptions);
- userAgent = builder.getUserAgent(fileSystemOptions);
- followRedirect = builder.getFollowRedirect(fileSystemOptions);
- }
-
- /**
- * Detaches this file object from its file resource.
- */
- @Override
- protected void doDetach() throws Exception {
- method = null;
- }
-
- /**
- * Returns the size of the file content (in bytes).
- */
- @Override
- protected long doGetContentSize() throws Exception {
- final Header header = method.getResponseHeader("content-length");
- if (header == null) {
- // Assume 0 content-length
- return 0;
- }
- return Long.parseLong(header.getValue());
- }
-
- /**
- * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
- * {@link FileType#FILE}.
- * <p>
- * It is guaranteed that there are no open output streams for this file when this method is called.
- * </p>
- * <p>
- * The returned stream does not have to be buffered.
- * </p>
- */
- @Override
- protected InputStream doGetInputStream() throws Exception {
- final GetMethod getMethod = new GetMethod();
- setupMethod(getMethod);
- final int status = getAbstractFileSystem().getClient().executeMethod(getMethod);
- if (status == HttpURLConnection.HTTP_NOT_FOUND) {
- throw new FileNotFoundException(getName());
- }
- if (status != HttpURLConnection.HTTP_OK) {
- throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status));
- }
-
- return new HttpInputStream(getMethod);
- }
-
- /**
- * Returns the last modified time of this file.
- * <p>
- * This implementation throws an exception.
- * </p>
- */
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- final Header header = method.getResponseHeader("last-modified");
- FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName());
- return DateUtil.parseDate(header.getValue()).getTime();
- }
-
- @Override
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- return new HttpRandomAccessContent<>(this, mode);
- }
-
- /**
- * 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 {
- // Use the HEAD method to probe the file.
- final int status = this.getHeadMethod().getStatusCode();
- if (status == HttpURLConnection.HTTP_OK
- || status == HttpURLConnection.HTTP_BAD_METHOD /* method is bad, but resource exist */) {
- return FileType.FILE;
- } else if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) {
- return FileType.IMAGINARY;
- } else {
- throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status));
- }
- }
-
- @Override
- protected boolean doIsWriteable() throws Exception {
- return false;
- }
-
- /**
- * Lists the children of this file.
- */
- @Override
- protected String[] doListChildren() throws Exception {
- throw new Exception("Not implemented.");
- }
-
- protected String encodePath(final String decodedPath) throws URIException {
- return URIUtil.encodePath(decodedPath);
- }
-
- @Override
- protected FileContentInfoFactory getFileContentInfoFactory() {
- return new HttpFileContentInfoFactory();
- }
-
- protected boolean getFollowRedirect() {
- return followRedirect;
- }
-
- protected String getUserAgent() {
- return userAgent;
- }
-
- HeadMethod getHeadMethod() throws IOException {
- if (method != null) {
- return method;
- }
- method = new HeadMethod();
- try {
- setupMethod(method);
- final HttpClient client = getAbstractFileSystem().getClient();
- client.executeMethod(method);
- } finally {
- method.releaseConnection();
- }
- return method;
- }
-
- protected String getUrlCharset() {
- return urlCharset;
- }
-
- /**
- * Prepares a HttpMethod object.
- *
- * @param method The object which gets prepared to access the file object.
- * @throws FileSystemException if an error occurs.
- * @throws URIException if path cannot be represented.
- * @since 2.0 (was package)
- */
- protected void setupMethod(final HttpMethod method) throws FileSystemException, URIException {
- final String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(this.getUrlCharset());
- method.setPath(pathEncoded);
- method.setFollowRedirects(this.getFollowRedirect());
- method.setRequestHeader("User-Agent", this.getUserAgent());
- }
-
- /*
- * protected Map doGetAttributes() throws Exception { TreeMap map = new TreeMap();
- *
- * Header contentType = method.getResponseHeader("content-type"); if (contentType != null) { HeaderElement[] element
- * = contentType.getValues(); if (element != null && element.length > 0) { map.put("content-type",
- * element[0].getName()); } }
- *
- * map.put("content-encoding", method.getResponseCharSet()); return map; }
- */
-}
+/*
+ * 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.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.util.DateUtil;
+import org.apache.commons.httpclient.util.URIUtil;
+import org.apache.commons.vfs2.FileContentInfoFactory;
+import org.apache.commons.vfs2.FileNotFoundException;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileSystemOptions;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.provider.AbstractFileName;
+import org.apache.commons.vfs2.provider.AbstractFileObject;
+import org.apache.commons.vfs2.provider.URLFileName;
+import org.apache.commons.vfs2.util.MonitorInputStream;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+
+/**
+ * A file object backed by Apache Commons HttpClient.
+ * <p>
+ * TODO - status codes.
+ * </p>
+ *
+ * @param <FS> An {@link HttpFileSystem} subclass
+ */
+public class HttpFileObject<FS extends HttpFileSystem> extends AbstractFileObject<FS> {
+
+ /**
+ * An InputStream that cleans up the HTTP connection on close.
+ */
+ static class HttpInputStream extends MonitorInputStream {
+ private final GetMethod method;
+
+ public HttpInputStream(final GetMethod method) throws IOException {
+ super(method.getResponseBodyAsStream());
+ this.method = method;
+ }
+
+ public HttpInputStream(final GetMethod method, final int bufferSize) throws IOException {
+ super(method.getResponseBodyAsStream(), bufferSize);
+ this.method = method;
+ }
+
+ /**
+ * Called after the stream has been closed.
+ */
+ @Override
+ protected void onClose() throws IOException {
+ method.releaseConnection();
+ }
+ }
+
+ private final String urlCharset;
+ private final String userAgent;
+ private final boolean followRedirect;
+
+ private HeadMethod method;
+
+ protected HttpFileObject(final AbstractFileName name, final FS fileSystem) {
+ this(name, fileSystem, HttpFileSystemConfigBuilder.getInstance());
+ }
+
+ protected HttpFileObject(final AbstractFileName name, final FS fileSystem,
+ final HttpFileSystemConfigBuilder builder) {
+ super(name, fileSystem);
+ final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions();
+ urlCharset = builder.getUrlCharset(fileSystemOptions);
+ userAgent = builder.getUserAgent(fileSystemOptions);
+ followRedirect = builder.getFollowRedirect(fileSystemOptions);
+ }
+
+ /**
+ * Detaches this file object from its file resource.
+ */
+ @Override
+ protected void doDetach() throws Exception {
+ method = null;
+ }
+
+ /**
+ * Returns the size of the file content (in bytes).
+ */
+ @Override
+ protected long doGetContentSize() throws Exception {
+ final Header header = method.getResponseHeader("content-length");
+ if (header == null) {
+ // Assume 0 content-length
+ return 0;
+ }
+ return Long.parseLong(header.getValue());
+ }
+
+ /**
+ * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
+ * {@link FileType#FILE}.
+ * <p>
+ * It is guaranteed that there are no open output streams for this file when this method is called.
+ * </p>
+ * <p>
+ * The returned stream does not have to be buffered.
+ * </p>
+ */
+ @Override
+ protected InputStream doGetInputStream(final int bufferSize) throws Exception {
+ final GetMethod getMethod = new GetMethod();
+ setupMethod(getMethod);
+ final int status = getAbstractFileSystem().getClient().executeMethod(getMethod);
+ if (status == HttpURLConnection.HTTP_NOT_FOUND) {
+ throw new FileNotFoundException(getName());
+ }
+ if (status != HttpURLConnection.HTTP_OK) {
+ throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status));
+ }
+
+ return new HttpInputStream(getMethod, bufferSize);
+ }
+
+ /**
+ * Returns the last modified time of this file.
+ * <p>
+ * This implementation throws an exception.
+ * </p>
+ */
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ final Header header = method.getResponseHeader("last-modified");
+ FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName());
+ return DateUtil.parseDate(header.getValue()).getTime();
+ }
+
+ @Override
+ protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
+ return new HttpRandomAccessContent<>(this, mode);
+ }
+
+ /**
+ * 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 {
+ // Use the HEAD method to probe the file.
+ final int status = this.getHeadMethod().getStatusCode();
+ if (status == HttpURLConnection.HTTP_OK
+ || status == HttpURLConnection.HTTP_BAD_METHOD /* method is bad, but resource exist */) {
+ return FileType.FILE;
+ } else if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) {
+ return FileType.IMAGINARY;
+ } else {
+ throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status));
+ }
+ }
+
+ @Override
+ protected boolean doIsWriteable() throws Exception {
+ return false;
+ }
+
+ /**
+ * Lists the children of this file.
+ */
+ @Override
+ protected String[] doListChildren() throws Exception {
+ throw new Exception("Not implemented.");
+ }
+
+ protected String encodePath(final String decodedPath) throws URIException {
+ return URIUtil.encodePath(decodedPath);
+ }
+
+ @Override
+ protected FileContentInfoFactory getFileContentInfoFactory() {
+ return new HttpFileContentInfoFactory();
+ }
+
+ protected boolean getFollowRedirect() {
+ return followRedirect;
+ }
+
+ protected String getUserAgent() {
+ return userAgent;
+ }
+
+ HeadMethod getHeadMethod() throws IOException {
+ if (method != null) {
+ return method;
+ }
+ method = new HeadMethod();
+ try {
+ setupMethod(method);
+ final HttpClient client = getAbstractFileSystem().getClient();
+ client.executeMethod(method);
+ } finally {
+ method.releaseConnection();
+ }
+ return method;
+ }
+
+ protected String getUrlCharset() {
+ return urlCharset;
+ }
+
+ /**
+ * Prepares a HttpMethod object.
+ *
+ * @param method The object which gets prepared to access the file object.
+ * @throws FileSystemException if an error occurs.
+ * @throws URIException if path cannot be represented.
+ * @since 2.0 (was package)
+ */
+ protected void setupMethod(final HttpMethod method) throws FileSystemException, URIException {
+ final String pathEncoded = ((URLFileName) getName()).getPathQueryEncoded(this.getUrlCharset());
+ method.setPath(pathEncoded);
+ method.setFollowRedirects(this.getFollowRedirect());
+ method.setRequestHeader("User-Agent", this.getUserAgent());
+ }
+
+ /*
+ * protected Map doGetAttributes() throws Exception { TreeMap map = new TreeMap();
+ *
+ * Header contentType = method.getResponseHeader("content-type"); if (contentType != null) { HeaderElement[] element
+ * = contentType.getValues(); if (element != null && element.length > 0) { map.put("content-type",
+ * element[0].getName()); } }
+ *
+ * map.put("content-encoding", method.getResponseCharSet()); return map; }
+ */
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java
index 697fc09..6f37998 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/http4/Http4FileObject.java
@@ -1,230 +1,230 @@
-/*
- * 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.http4;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import org.apache.commons.vfs2.FileContentInfoFactory;
-import org.apache.commons.vfs2.FileNotFoundException;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileSystemOptions;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.RandomAccessContent;
-import org.apache.commons.vfs2.provider.AbstractFileName;
-import org.apache.commons.vfs2.provider.AbstractFileObject;
-import org.apache.commons.vfs2.provider.GenericURLFileName;
-import org.apache.commons.vfs2.util.RandomAccessMode;
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpHead;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.protocol.HttpClientContext;
-import org.apache.http.client.utils.DateUtils;
-import org.apache.http.client.utils.URIUtils;
-import org.apache.http.protocol.HTTP;
-
-/**
- * A file object backed by Apache HttpComponents HttpClient.
- *
- * @param <FS> An {@link Http4FileSystem} subclass
- */
-public class Http4FileObject<FS extends Http4FileSystem> extends AbstractFileObject<FS> {
-
- /**
- * URL charset string.
- */
- private final String urlCharset;
-
- /**
- * Internal URI mapped to this <code>FileObject</code>.
- * For example, the internal URI of <code>http4://example.com/a.txt</code> is <code>http://example.com/a.txt</code>.
- */
- private final URI internalURI;
-
- /**
- * The last executed HEAD <code>HttpResponse</code> object.
- */
- private HttpResponse lastHeadResponse;
-
- /**
- * Construct <code>Http4FileObject</code>.
- *
- * @param name file name
- * @param fileSystem file system
- * @throws FileSystemException if any error occurs
- * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error
- */
- protected Http4FileObject(final AbstractFileName name, final FS fileSystem)
- throws FileSystemException, URISyntaxException {
- this(name, fileSystem, Http4FileSystemConfigBuilder.getInstance());
- }
-
- /**
- * Construct <code>Http4FileObject</code>.
- *
- * @param name file name
- * @param fileSystem file system
- * @param builder <code>Http4FileSystemConfigBuilder</code> object
- * @throws FileSystemException if any error occurs
- * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error
- */
- protected Http4FileObject(final AbstractFileName name, final FS fileSystem,
- final Http4FileSystemConfigBuilder builder) throws FileSystemException, URISyntaxException {
- super(name, fileSystem);
- final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions();
- urlCharset = builder.getUrlCharset(fileSystemOptions);
- final String pathEncoded = ((GenericURLFileName) name).getPathQueryEncoded(getUrlCharset());
- internalURI = URIUtils.resolve(fileSystem.getInternalBaseURI(), pathEncoded);
- }
-
- @Override
- protected FileType doGetType() throws Exception {
- lastHeadResponse = executeHttpUriRequest(new HttpHead(getInternalURI()));
- final int status = lastHeadResponse.getStatusLine().getStatusCode();
-
- if (status == HttpStatus.SC_OK
- || status == HttpStatus.SC_METHOD_NOT_ALLOWED /* method is not allowed, but resource exist */) {
- return FileType.FILE;
- } else if (status == HttpStatus.SC_NOT_FOUND || status == HttpStatus.SC_GONE) {
- return FileType.IMAGINARY;
- } else {
- throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status));
- }
- }
-
- @Override
- protected long doGetContentSize() throws Exception {
- if (lastHeadResponse == null) {
- return 0L;
- }
-
- final Header header = lastHeadResponse.getFirstHeader(HTTP.CONTENT_LEN);
-
- if (header == null) {
- // Assume 0 content-length
- return 0;
- }
-
- return Long.parseLong(header.getValue());
- }
-
- @Override
- protected long doGetLastModifiedTime() throws Exception {
- FileSystemException.requireNonNull(lastHeadResponse, "vfs.provider.http/last-modified.error", getName());
-
- final Header header = lastHeadResponse.getFirstHeader("Last-Modified");
-
- FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName());
-
- return DateUtils.parseDate(header.getValue()).getTime();
- }
-
-
- @Override
- protected InputStream doGetInputStream() throws Exception {
- final HttpGet getRequest = new HttpGet(getInternalURI());
- final HttpResponse httpResponse = executeHttpUriRequest(getRequest);
- final int status = httpResponse.getStatusLine().getStatusCode();
-
- if (status == HttpStatus.SC_NOT_FOUND) {
- throw new FileNotFoundException(getName());
- }
-
- if (status != HttpStatus.SC_OK) {
- throw new FileSystemException("vfs.provider.http/get.error", getName(), Integer.valueOf(status));
- }
-
- return new MonitoredHttpResponseContentInputStream(httpResponse);
- }
-
- @Override
- protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
- return new Http4RandomAccessContent<>(this, mode);
- }
-
- @Override
- protected String[] doListChildren() throws Exception {
- throw new UnsupportedOperationException("Not implemented.");
- }
-
- @Override
- protected boolean doIsWriteable() throws Exception {
- return false;
- }
-
- @Override
- protected FileContentInfoFactory getFileContentInfoFactory() {
- return new Http4FileContentInfoFactory();
- }
-
- @Override
- protected void doDetach() throws Exception {
- lastHeadResponse = null;
- }
-
- /**
- * Return URL charset string.
- * @return URL charset string
- */
- protected String getUrlCharset() {
- return urlCharset;
- }
-
- /**
- * Return the internal <code>URI</code> object mapped to this file object.
- *
- * @return the internal <code>URI</code> object mapped to this file object
- * @throws FileSystemException if any error occurs
- */
- protected URI getInternalURI() throws FileSystemException {
- return internalURI;
- }
-
- /**
- * Return the last executed HEAD <code>HttpResponse</code> object.
- *
- * @return the last executed HEAD <code>HttpResponse</code> object
- * @throws IOException if IO error occurs
- */
- HttpResponse getLastHeadResponse() throws IOException {
- if (lastHeadResponse != null) {
- return lastHeadResponse;
- }
-
- return executeHttpUriRequest(new HttpHead(getInternalURI()));
- }
-
- /**
- * Execute the request using the given {@code httpRequest} and return a <code>HttpResponse</code> from the execution.
- *
- * @param httpRequest <code>HttpUriRequest</code> object
- * @return <code>HttpResponse</code> from the execution
- * @throws IOException if IO error occurs
- */
- HttpResponse executeHttpUriRequest(final HttpUriRequest httpRequest) throws IOException {
- final HttpClient httpClient = getAbstractFileSystem().getHttpClient();
- final HttpClientContext httpClientContext = getAbstractFileSystem().getHttpClientContext();
- return httpClient.execute(httpRequest, httpClientContext);
- }
-
-}
+/*
+ * 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.http4;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.commons.vfs2.FileContentInfoFactory;
+import org.apache.commons.vfs2.FileNotFoundException;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileSystemOptions;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.RandomAccessContent;
+import org.apache.commons.vfs2.provider.AbstractFileName;
+import org.apache.commons.vfs2.provider.AbstractFileObject;
+import org.apache.commons.vfs2.provider.GenericURLFileName;
+import org.apache.commons.vfs2.util.RandomAccessMode;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.utils.DateUtils;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * A file object backed by Apache HttpComponents HttpClient.
+ *
+ * @param <FS> An {@link Http4FileSystem} subclass
+ */
+public class Http4FileObject<FS extends Http4FileSystem> extends AbstractFileObject<FS> {
+
+ /**
+ * URL charset string.
+ */
+ private final String urlCharset;
+
+ /**
+ * Internal URI mapped to this <code>FileObject</code>.
+ * For example, the internal URI of <code>http4://example.com/a.txt</code> is <code>http://example.com/a.txt</code>.
+ */
+ private final URI internalURI;
+
+ /**
+ * The last executed HEAD <code>HttpResponse</code> object.
+ */
+ private HttpResponse lastHeadResponse;
+
+ /**
+ * Construct <code>Http4FileObject</code>.
+ *
+ * @param name file name
+ * @param fileSystem file system
+ * @throws FileSystemException if any error occurs
+ * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error
+ */
+ protected Http4FileObject(final AbstractFileName name, final FS fileSystem)
+ throws FileSystemException, URISyntaxException {
+ this(name, fileSystem, Http4FileSystemConfigBuilder.getInstance());
+ }
+
+ /**
+ * Construct <code>Http4FileObject</code>.
+ *
+ * @param name file name
+ * @param fileSystem file system
+ * @param builder <code>Http4FileSystemConfigBuilder</code> object
+ * @throws FileSystemException if any error occurs
+ * @throws URISyntaxException if given file name cannot be converted to a URI due to URI syntax error
+ */
+ protected Http4FileObject(final AbstractFileName name, final FS fileSystem,
+ final Http4FileSystemConfigBuilder builder) throws FileSystemException, URISyntaxException {
+ super(name, fileSystem);
+ final FileSystemOptions fileSystemOptions = fileSystem.getFileSystemOptions();
+ urlCharset = builder.getUrlCharset(fileSystemOptions);
+ final String pathEncoded = ((GenericURLFileName) name).getPathQueryEncoded(getUrlCharset());
+ internalURI = URIUtils.resolve(fileSystem.getInternalBaseURI(), pathEncoded);
+ }
+
+ @Override
+ protected FileType doGetType() throws Exception {
+ lastHeadResponse = executeHttpUriRequest(new HttpHead(getInternalURI()));
+ final int status = lastHeadResponse.getStatusLine().getStatusCode();
+
+ if (status == HttpStatus.SC_OK
+ || status == HttpStatus.SC_METHOD_NOT_ALLOWED /* method is not allowed, but resource exist */) {
+ return FileType.FILE;
+ } else if (status == HttpStatus.SC_NOT_FOUND || status == HttpStatus.SC_GONE) {
+ return FileType.IMAGINARY;
+ } else {
+ throw new FileSystemException("vfs.provider.http/head.error", getName(), Integer.valueOf(status));
+ }
+ }
+
+ @Override
+ protected long doGetContentSize() throws Exception {
+ if (lastHeadResponse == null) {
+ return 0L;
+ }
+
+ final Header header = lastHeadResponse.getFirstHeader(HTTP.CONTENT_LEN);
+
+ if (header == null) {
+ // Assume 0 content-length
+ return 0;
+ }
+
+ return Long.parseLong(header.getValue());
+ }
+
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ FileSystemException.requireNonNull(lastHeadResponse, "vfs.provider.http/last-modified.error", getName());
+
+ final Header header = lastHeadResponse.getFirstHeader("Last-Modified");
+
+ FileSystemException.requireNonNull(header, "vfs.provider.http/last-modified.error", getName());
+
+ return DateUtils.parseDate(header.getValue()).getTime();
+ }
... 2406 lines suppressed ...