From 3a7cdab822a11320eb969c1e96e3745ebf2b579e Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 24 Mar 2017 11:00:35 +0000 Subject: [PATCH] CCM NG: NIOFileSystemAdapter (Fallback for CcmFiles) git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4642 8810af33-2d31-482b-a856-94f89814c4df --- .../java/org/libreccm/files/CcmFiles.java | 6 + .../libreccm/files/CcmFilesConfiguration.java | 45 +++ .../files/DirectoryNotEmptyException.java | 53 +-- .../libreccm/files/FileAccessException.java | 37 +- .../files/FileAlreadyExistsException.java | 39 +-- .../files/FileDoesNotExistException.java | 36 +- .../org/libreccm/files/FileSystemAdapter.java | 4 + .../InsufficientPermissionsException.java | 39 +-- .../libreccm/files/NIOFileSystemAdapter.java | 317 ++++++++++++++++++ .../libreccm/files/NoDirectoryException.java | 37 +- 10 files changed, 487 insertions(+), 126 deletions(-) create mode 100644 ccm-core/src/main/java/org/libreccm/files/CcmFilesConfiguration.java create mode 100644 ccm-core/src/main/java/org/libreccm/files/NIOFileSystemAdapter.java diff --git a/ccm-core/src/main/java/org/libreccm/files/CcmFiles.java b/ccm-core/src/main/java/org/libreccm/files/CcmFiles.java index 46d419337..ee6b6001f 100644 --- a/ccm-core/src/main/java/org/libreccm/files/CcmFiles.java +++ b/ccm-core/src/main/java/org/libreccm/files/CcmFiles.java @@ -23,7 +23,10 @@ import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.util.List; + +import javax.enterprise.inject.Instance; import javax.faces.bean.RequestScoped; +import javax.inject.Inject; /** * This class provides access to the file (local) system. If available an @@ -46,6 +49,9 @@ import javax.faces.bean.RequestScoped; @RequestScoped public class CcmFiles { + @Inject + private Instance fileSystemAdapters; + /** * Creates a {@link Reader} for the provided {@code path}. * diff --git a/ccm-core/src/main/java/org/libreccm/files/CcmFilesConfiguration.java b/ccm-core/src/main/java/org/libreccm/files/CcmFilesConfiguration.java new file mode 100644 index 000000000..a5cb52742 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/files/CcmFilesConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.files; + +import org.libreccm.configuration.Configuration; +import org.libreccm.configuration.Setting; + +/** + * + * @author Jens Pelzetter + */ +@Configuration(descBundle + = "org.libreccm.files.CcmFilesConfiguration.properties", + titleKey = "title", + descKey = "description") +public class CcmFilesConfiguration { + + @Setting() + private String dataPath; + + public String getDataPath() { + return dataPath; + } + + public void setDataPath(final String dataPath) { + this.dataPath = dataPath; + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/files/DirectoryNotEmptyException.java b/ccm-core/src/main/java/org/libreccm/files/DirectoryNotEmptyException.java index bc50ff7dd..15ed17a60 100644 --- a/ccm-core/src/main/java/org/libreccm/files/DirectoryNotEmptyException.java +++ b/ccm-core/src/main/java/org/libreccm/files/DirectoryNotEmptyException.java @@ -20,36 +20,43 @@ package org.libreccm.files; /** * Thrown if a non empty directory is not deleted recursively. - * + * * @author Jens Pelzetter */ public class DirectoryNotEmptyException extends Exception { private static final long serialVersionUID = -8515711805034123260L; - /** - * Creates a new instance of DirectoryNotEmptyException without - * detail message. - */ - DirectoryNotEmptyException() { - super(); + private static final String MESSAGE_TEMPLATE + = "The directory '%s' is not empty."; + + DirectoryNotEmptyException(final String path) { + super(String.format(MESSAGE_TEMPLATE, path)); } - /** - * Constructs an instance of DirectoryNotEmptyException with - * the specified detail message. - * - * @param msg the detail message. - */ - DirectoryNotEmptyException(final String msg) { - super(msg); - } - - DirectoryNotEmptyException(final Exception ex) { - super(ex); - } - - DirectoryNotEmptyException(final String msg, final Exception ex) { - super(msg, ex); +// /** +// * Creates a new instance of DirectoryNotEmptyException without +// * detail message. +// */ +// DirectoryNotEmptyException() { +// super(); +// } +// +// /** +// * Constructs an instance of DirectoryNotEmptyException with +// * the specified detail message. +// * +// * @param msg the detail message. +// */ +// DirectoryNotEmptyException(final String msg) { +// super(msg); +// } +// +// DirectoryNotEmptyException(final Exception ex) { +// super(ex); +// } +// + DirectoryNotEmptyException(final String path, final Exception ex) { + super(String.format(MESSAGE_TEMPLATE, path), ex); } } diff --git a/ccm-core/src/main/java/org/libreccm/files/FileAccessException.java b/ccm-core/src/main/java/org/libreccm/files/FileAccessException.java index b255b7f08..88dde614c 100644 --- a/ccm-core/src/main/java/org/libreccm/files/FileAccessException.java +++ b/ccm-core/src/main/java/org/libreccm/files/FileAccessException.java @@ -28,29 +28,24 @@ public class FileAccessException extends Exception { private static final long serialVersionUID = 1L; - /** - * Creates a new instance of FileAccessException without detail - * message. - */ - FileAccessException() { - super(); + private static final String MESSAGE_TEMPLATE = "Error accessing file '%s'."; + +// /** +// * Creates a new instance of FileAccessException without detail +// * message. +// */ +// FileAccessException() { +// super(); +// } + FileAccessException(final String path) { + super(String.format(MESSAGE_TEMPLATE, path)); } - /** - * Constructs an instance of FileAccessException with the - * specified detail message. - * - * @param msg the detail message. - */ - FileAccessException(final String msg) { - super(msg); +// FileAccessException(final Exception ex) { +// super(ex); +// } + FileAccessException(final String path, final Exception ex) { + super(String.format(MESSAGE_TEMPLATE, path), ex); } - FileAccessException(final Exception ex) { - super(ex); - } - - FileAccessException(final String msg, final Exception ex) { - super(msg, ex); - } } diff --git a/ccm-core/src/main/java/org/libreccm/files/FileAlreadyExistsException.java b/ccm-core/src/main/java/org/libreccm/files/FileAlreadyExistsException.java index 6bc5fc2ab..0a0deb955 100644 --- a/ccm-core/src/main/java/org/libreccm/files/FileAlreadyExistsException.java +++ b/ccm-core/src/main/java/org/libreccm/files/FileAlreadyExistsException.java @@ -27,30 +27,27 @@ package org.libreccm.files; public class FileAlreadyExistsException extends Exception { private static final long serialVersionUID = 2237027823060973043L; + + private static final String MESSAGE_TEMPLATE = "The file '%s' already exists."; - /** - * Creates a new instance of FileAlreadyExistsException without - * detail message. - */ - FileAlreadyExistsException() { - super(); +// /** +// * Creates a new instance of FileAlreadyExistsException without +// * detail message. +// */ +// FileAlreadyExistsException() { +// super(); +// } + + + FileAlreadyExistsException(final String path) { + super(String.format(MESSAGE_TEMPLATE, path)); } - /** - * Constructs an instance of FileAlreadyExistsException with - * the specified detail message. - * - * @param msg the detail message. - */ - FileAlreadyExistsException(final String msg) { - super(msg); - } +// FileAlreadyExistsException(final Exception ex) { +// super(ex); +// } - FileAlreadyExistsException(final Exception ex) { - super(ex); - } - - FileAlreadyExistsException(final String msg, final Exception ex) { - super(msg, ex); + FileAlreadyExistsException(final String path, final Exception ex) { + super(String.format(MESSAGE_TEMPLATE, path), ex); } } diff --git a/ccm-core/src/main/java/org/libreccm/files/FileDoesNotExistException.java b/ccm-core/src/main/java/org/libreccm/files/FileDoesNotExistException.java index 3be582ec0..da71056af 100644 --- a/ccm-core/src/main/java/org/libreccm/files/FileDoesNotExistException.java +++ b/ccm-core/src/main/java/org/libreccm/files/FileDoesNotExistException.java @@ -27,29 +27,25 @@ package org.libreccm.files; public class FileDoesNotExistException extends Exception { private static final long serialVersionUID = 1L; + + private static final String MESSAGE_TEMPLATE = "The file '%s' does not exist."; - /** - * Creates a new instance of FileDoesNotExistException without - * detail message. - */ - FileDoesNotExistException() { +// /** +// * Creates a new instance of FileDoesNotExistException without +// * detail message. +// */ +// FileDoesNotExistException() { +// } + + FileDoesNotExistException(final String path) { + super(String.format(MESSAGE_TEMPLATE, path)); } - /** - * Constructs an instance of FileDoesNotExistException with the - * specified detail message. - * - * @param msg the detail message. - */ - FileDoesNotExistException(String msg) { - super(msg); - } +// FileDoesNotExistException(final Exception ex) { +// super(ex); +// } - FileDoesNotExistException(final Exception ex) { - super(ex); - } - - FileDoesNotExistException(final String msg, final Exception ex) { - super(msg, ex); + FileDoesNotExistException(final String path, final Exception ex) { + super(String.format(MESSAGE_TEMPLATE, path), ex); } } diff --git a/ccm-core/src/main/java/org/libreccm/files/FileSystemAdapter.java b/ccm-core/src/main/java/org/libreccm/files/FileSystemAdapter.java index e12114b85..439c8ea31 100644 --- a/ccm-core/src/main/java/org/libreccm/files/FileSystemAdapter.java +++ b/ccm-core/src/main/java/org/libreccm/files/FileSystemAdapter.java @@ -232,6 +232,9 @@ public interface FileSystemAdapter { * exceptions occurs. * @throws FileDoesNotExistException If the requested file does not * exist. + * @throws DirectoryNotEmptyException If the directory is not empty + * and {@code recursively} + * is set to {@code false}. * @throws InsufficientPermissionsException If the user which runs the * application server does not have * the permission to access the @@ -240,6 +243,7 @@ public interface FileSystemAdapter { void deleteFile(String path, boolean recursively) throws FileAccessException, FileDoesNotExistException, + DirectoryNotEmptyException, InsufficientPermissionsException; } diff --git a/ccm-core/src/main/java/org/libreccm/files/InsufficientPermissionsException.java b/ccm-core/src/main/java/org/libreccm/files/InsufficientPermissionsException.java index c259a0bf6..218c8d4b2 100644 --- a/ccm-core/src/main/java/org/libreccm/files/InsufficientPermissionsException.java +++ b/ccm-core/src/main/java/org/libreccm/files/InsufficientPermissionsException.java @@ -27,30 +27,27 @@ package org.libreccm.files; public class InsufficientPermissionsException extends Exception { private static final long serialVersionUID = -7496839503615573013L; + + private static final String MESSAGE_TEMPLATE = "Insufficient permissions for accessing file '%s'."; - /** - * Creates a new instance of InsufficientPermissionsException - * without detail message. - */ - InsufficientPermissionsException() { - super(); +// /** +// * Creates a new instance of InsufficientPermissionsException +// * without detail message. +// */ +// InsufficientPermissionsException() { +// super(); +// } + + + InsufficientPermissionsException(final String path) { + super(String.format(MESSAGE_TEMPLATE, path)); } - /** - * Constructs an instance of InsufficientPermissionsException - * with the specified detail message. - * - * @param msg the detail message. - */ - InsufficientPermissionsException(final String msg) { - super(msg); - } +// InsufficientPermissionsException(final Exception ex) { +// super(ex); +// } - InsufficientPermissionsException(final Exception ex) { - super(ex); - } - - InsufficientPermissionsException(final String msg, final Exception ex) { - super(msg, ex); + InsufficientPermissionsException(final String path, final Exception ex) { + super(String.format(MESSAGE_TEMPLATE, path), ex); } } diff --git a/ccm-core/src/main/java/org/libreccm/files/NIOFileSystemAdapter.java b/ccm-core/src/main/java/org/libreccm/files/NIOFileSystemAdapter.java new file mode 100644 index 000000000..6fad272fe --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/files/NIOFileSystemAdapter.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.files; + +import org.libreccm.configuration.ConfigurationManager; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; + +/** + * This implementation of the {@link FileSystemAdapter} interface is used by + * {@link CcmFiles} as a fallback if no other implementations are available. + * + * This adapter uses the classes from the {@code java.nio} for accessing the + * file system directly. Using this adapter is not recommended. Operations may + * fail due to security constraints of the application server if this adapter is + * used. + * + * @author Jens Pelzetter + */ +@RequestScoped +public class NIOFileSystemAdapter implements FileSystemAdapter { + + @Inject + private ConfigurationManager confManager; + + private String dataPath; + + @PostConstruct + private void init() { + final CcmFilesConfiguration filesConf = confManager.findConfiguration( + CcmFilesConfiguration.class); + dataPath = filesConf.getDataPath(); + } + + @Override + public boolean isConfigured() { + return dataPath != null && !dataPath.isEmpty(); + } + + @Override + public Reader createReader(final String path) + throws FileDoesNotExistException, + FileAccessException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.exists(nioPath)) { + throw new FileDoesNotExistException(path); + } + + if (!Files.isReadable(nioPath)) { + throw new InsufficientPermissionsException(path); + } + + final FileReader fileReader; + try { + fileReader = new FileReader(nioPath.toFile()); + } catch (FileNotFoundException ex) { + throw new FileDoesNotExistException(path, ex); + } + + return fileReader; + } + + @Override + public Writer createWriter(final String path) + throws FileAccessException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.exists(nioPath)) { + try { + Files.createFile(nioPath); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + } + + if (!Files.isWritable(nioPath)) { + throw new InsufficientPermissionsException(path); + } + + final FileWriter fileWriter; + try { + fileWriter = new FileWriter(nioPath.toFile()); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + + return fileWriter; + } + + @Override + public InputStream createInputStream(final String path) throws + FileDoesNotExistException, + FileAccessException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.exists(nioPath)) { + throw new FileDoesNotExistException(path); + } + + if (!Files.isReadable(nioPath)) { + throw new InsufficientPermissionsException(path); + } + + final FileInputStream fileInputStream; + try { + fileInputStream = new FileInputStream(nioPath.toFile()); + } catch (FileNotFoundException ex) { + throw new FileDoesNotExistException(path, ex); + } + + return fileInputStream; + } + + @Override + public OutputStream createOutputStream(final String path) throws + FileAccessException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.exists(nioPath)) { + try { + Files.createFile(nioPath); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + } + + if (!Files.isWritable(nioPath)) { + throw new InsufficientPermissionsException(path); + } + + final FileOutputStream fileOutputStream; + try { + fileOutputStream = new FileOutputStream(nioPath.toFile()); + } catch (FileNotFoundException ex) { + throw new FileAccessException(path, ex); + } + + return fileOutputStream; + } + + @Override + public boolean existsFile(final String path) + throws FileAccessException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + return Files.exists(nioPath); + } + + @Override + public boolean isDirectory(final String path) throws FileAccessException, + FileDoesNotExistException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + return Files.isDirectory(nioPath); + } + + @Override + public void createDirectory(final String path) + throws FileAccessException, + FileAlreadyExistsException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (Files.exists(nioPath)) { + throw new FileAlreadyExistsException(path); + } + + try { + Files.createDirectories(nioPath); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + } + + @Override + public List listFiles(final String path) + throws FileAccessException, + FileDoesNotExistException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.isDirectory(nioPath)) { + throw new FileAccessException(path); + } + + final Stream paths; + try { + paths = Files.list(nioPath); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + + return paths + .map(filePath -> filePath.getFileName().toString()) + .collect(Collectors.toList()); + } + + @Override + public void deleteFile(final String path) + throws FileAccessException, + FileDoesNotExistException, + DirectoryNotEmptyException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.isWritable(nioPath)) { + throw new InsufficientPermissionsException(path); + } + + try { + if (Files.isDirectory(nioPath) && Files.list(nioPath).count() > 0) { + throw new DirectoryNotEmptyException(path); + } + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + + try { + Files.deleteIfExists(nioPath); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + } + + @Override + public void deleteFile(final String path, final boolean recursively) + throws FileAccessException, + FileDoesNotExistException, + DirectoryNotEmptyException, + InsufficientPermissionsException { + + final Path nioPath = Paths.get(path); + + if (!Files.isWritable(nioPath)) { + throw new InsufficientPermissionsException(path); + } + + try { + if (Files.isDirectory(nioPath) && Files.list(nioPath).count() > 0) { + throw new DirectoryNotEmptyException(path); + } + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + + if (recursively && Files.isDirectory(nioPath)) { + final List files; + try { + files = Files + .list(nioPath) + .map(file -> file.toString()) + .collect(Collectors.toList()); + } catch (IOException ex) { + throw new FileAccessException(path, ex); + } + for (final String file : files) { + deleteFile(file, recursively); + } + } else { + deleteFile(path); + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/files/NoDirectoryException.java b/ccm-core/src/main/java/org/libreccm/files/NoDirectoryException.java index 8d3f8087d..79c26e3b7 100644 --- a/ccm-core/src/main/java/org/libreccm/files/NoDirectoryException.java +++ b/ccm-core/src/main/java/org/libreccm/files/NoDirectoryException.java @@ -28,29 +28,26 @@ public class NoDirectoryException extends Exception { private static final long serialVersionUID = -5811387600385322767L; - /** - * Creates a new instance of NoDirectoryException without - * detail message. - */ - NoDirectoryException() { - super(); - } + private static final String MESSAGE_TEMPLATE = "The file '%s' is not a directory."; + +// /** +// * Creates a new instance of NoDirectoryException without +// * detail message. +// */ +// NoDirectoryException() { +// super(); +// } - /** - * Constructs an instance of NoDirectoryException with the - * specified detail message. - * - * @param msg the detail message. - */ - NoDirectoryException(final String msg) { - super(msg); + + NoDirectoryException(final String path) { + super(String.format(MESSAGE_TEMPLATE, path)); } - NoDirectoryException(final Exception ex) { - super(ex); - } +// NoDirectoryException(final Exception ex) { +// super(ex); +// } - NoDirectoryException(final String msg, final Exception ex) { - super(msg, ex); + NoDirectoryException(final String path, final Exception ex) { + super(String.format(MESSAGE_TEMPLATE, path), ex); } }