org.objectweb.proactive.extensions.dataspaces.vfs.AbstractLimitingFileObject.java Source code

Java tutorial

Introduction

Here is the source code for org.objectweb.proactive.extensions.dataspaces.vfs.AbstractLimitingFileObject.java

Source

/*
 * ################################################################
 *
 * ProActive Parallel Suite(TM): The Java(TM) library for
 *    Parallel, Distributed, Multi-Core Computing for
 *    Enterprise Grids & Clouds
 *
 * Copyright (C) 1997-2012 INRIA/University of
 *                 Nice-Sophia Antipolis/ActiveEon
 * Contact: proactive@ow2.org or contact@activeeon.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; version 3 of
 * the License.
 *
 * 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
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * If needed, contact us to obtain a release under GPL Version 2 or 3
 * or a different license than the AGPL.
 *
 *  Initial developer(s):               The ProActive Team
 *                        http://proactive.inria.fr/team_members.htm
 *  Contributor(s):
 *
 * ################################################################
 * $$PROACTIVE_INITIAL_DEV$$
 */
package org.objectweb.proactive.extensions.dataspaces.vfs;

import java.io.InputStream;
import java.io.OutputStream;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.vfs.FileContent;
import org.apache.commons.vfs.FileContentInfo;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSelector;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.NameScope;
import org.apache.commons.vfs.RandomAccessContent;
import org.apache.commons.vfs.impl.DecoratedFileObject;
import org.apache.commons.vfs.util.RandomAccessMode;

/**
 * Abstract FileObject decorator, checking if to limit write or ancestor access to the file basing
 * on pluggable rules.
 * <p>
 * Decorator may limit write access basing on {@link #isReadOnly()} method: direct write access
 * (like deleting file, opening output stream from getContent()), write checks (like isWriteable()).
 * <p>
 * Decorator may limit ancestor access (for resolve and getParent queries) starting from some level
 * basing on pluggable rule {@link #canReturnAncestor(FileObject)}.
 * <p>
 * It also decorates every returned FileObject. Way of decorating returned files is also pluggable
 * through {@link #doDecorateFile(FileObject)}.
 * <p>
 * Limits for write actions and resolve queries are enforced by throwing {@link FileSystemException}
 * , query methods like {@link #isWriteable()} changes behavior, while {@link #getParent()} return
 * null for accessing disallowed ancestor.
 * <p>
 * <strong>Known limitations of decorator:</strong>
 * <ul>
 * <li>canRenameTo() invoked on non-decorated object with decorated object as target may return
 * false information</li>
 * <li>returned FileContent returns undecorated file for getFile(); depends on VFS bug: VFS-259
 * (fixed in VFS fork)</li>
 * <li>moveTo() invoked on non-decorated object with decorated object as target may not work for
 * some buggy providers; depends on VFS bug: VFS-258 (fixed in VFS fork)</li>
 * </ul>
 * <p>
 * Generic type should be an implementor type - class returned by
 * {@link #doDecorateFile(FileObject)}.
 */
// I do not know if it is technically possible to express generic type as an implementor class type.
// So, implementors have to set it to their own type.
public abstract class AbstractLimitingFileObject<T extends FileObject> extends DecoratedFileObject {

    public AbstractLimitingFileObject(final FileObject fileObject) {
        super(fileObject);
    }

    /**
     * @param file
     *            file to decorate for calls returning FileObjects; never <code>null</code>
     * @return decorated FileObject
     */
    protected abstract T doDecorateFile(FileObject file);

    /**
     * @return <code>true</code> if file is read-only, <code>false</code> otherwise
     */
    protected abstract boolean isReadOnly();

    /**
     * Checks whether provided ancestor can be returned. The rule should be consistent among
     * siblings and ancestors of given ancestor. I.e. if one ancestor is denied, then limitation
     * should concern also its siblings and ancestors in file system tree.
     *
     * @param decoratedAncestor
     *            ancestor to return, with decorator already applied by
     *            {@link #doDecorateFile(FileObject)}.
     * @return <code>true</code> if this decorated ancestor can be returned in any call,
     *         <code>false</code> otherwise
     */
    protected abstract boolean canReturnAncestor(T decoratedAncestor);

    @Override
    public boolean canRenameTo(FileObject newfile) {
        return !isReadOnly();
    }

    @Override
    public boolean isWriteable() throws FileSystemException {
        return !isReadOnly();
    }

    @Override
    public void copyFrom(FileObject srcFile, FileSelector selector) throws FileSystemException {
        checkIsNotReadOnly();
        super.copyFrom(srcFile, selector);
    }

    @Override
    public void createFile() throws FileSystemException {
        checkIsNotReadOnly();
        super.createFile();
    }

    @Override
    public void createFolder() throws FileSystemException {
        checkIsNotReadOnly();
        super.createFolder();
    }

    @Override
    public boolean delete() throws FileSystemException {
        checkIsNotReadOnly();
        return super.delete();
    }

    @Override
    public int delete(FileSelector selector) throws FileSystemException {
        checkIsNotReadOnly();
        return super.delete(selector);
    }

    @Override
    public void moveTo(FileObject destFile) throws FileSystemException {
        checkIsNotReadOnly();
        super.moveTo(destFile);
    }

    @Override
    public FileObject resolveFile(String name, NameScope scope) throws FileSystemException {
        final T resolvedDecorated = decorateFile(super.resolveFile(name, scope));
        checkIsNotDisallowedAncestor(resolvedDecorated);
        return resolvedDecorated;
    }

    @Override
    public FileObject resolveFile(String path) throws FileSystemException {
        final T resolvedDecorated = decorateFile(super.resolveFile(path));
        checkIsNotDisallowedAncestor(resolvedDecorated);
        return resolvedDecorated;
    }

    @Override
    public FileObject[] findFiles(FileSelector selector) throws FileSystemException {
        return decorateFiles(super.findFiles(selector));
    }

    @SuppressWarnings("unchecked")
    @Override
    public void findFiles(FileSelector selector, boolean depthwise, List selected) throws FileSystemException {
        final List<FileObject> selectedList = new ArrayList<FileObject>();
        super.findFiles(selector, depthwise, selectedList);
        selected.addAll(decorateFiles(selectedList));
    }

    @Override
    public FileObject getChild(String name) throws FileSystemException {
        final FileObject child = super.getChild(name);
        if (child == null)
            return null;
        return decorateFile(child);
    }

    @Override
    public FileObject[] getChildren() throws FileSystemException {
        return decorateFiles(super.getChildren());
    }

    @Override
    public FileObject getParent() throws FileSystemException {
        final T parentDecorated = decorateFile(super.getParent());
        if (isDisallowedAncestor(parentDecorated)) {
            return null;
        }
        return parentDecorated;
    }

    @Override
    public FileContent getContent() throws FileSystemException {
        return new LimitingFileContent(super.getContent());
    }

    private void checkIsNotReadOnly() throws FileSystemException {
        if (isReadOnly())
            throw new FileSystemException("File is read-only");
    }

    private T decorateFile(final FileObject file) {
        if (file == null)
            return null;
        return doDecorateFile(file);
    }

    private FileObject[] decorateFiles(final FileObject files[]) throws FileSystemException {
        if (files == null)
            return null;

        final FileObject result[] = new FileObject[files.length];
        for (int i = 0; i < files.length; i++)
            result[i] = decorateFile(files[i]);
        return result;
    }

    private List<FileObject> decorateFiles(final List<FileObject> files) throws FileSystemException {
        final List<FileObject> result = new ArrayList<FileObject>(files.size());
        for (final FileObject fo : files)
            result.add(decorateFile(fo));
        return result;
    }

    private boolean isDisallowedAncestor(final T decoratedFile) {
        if (decoratedFile == null)
            return false;
        if (getName().isDescendent(decoratedFile.getName(), NameScope.DESCENDENT_OR_SELF)) {
            return false;
        }
        return !canReturnAncestor(decoratedFile);
    }

    private void checkIsNotDisallowedAncestor(final T decoratedFile) throws FileSystemException {
        if (isDisallowedAncestor(decoratedFile)) {
            throw new FileSystemException("Access denied to one of resolved file ancestor(s)");
        }
    }

    private class LimitingFileContent implements FileContent {
        private FileContent content;

        public LimitingFileContent(FileContent content) {
            this.content = content;
        }

        public void close() throws FileSystemException {
            content.close();
        }

        public Object getAttribute(String attrName) throws FileSystemException {
            return content.getAttribute(attrName);
        }

        public String[] getAttributeNames() throws FileSystemException {
            return content.getAttributeNames();
        }

        @SuppressWarnings("unchecked")
        public Map getAttributes() throws FileSystemException {
            return content.getAttributes();
        }

        public Certificate[] getCertificates() throws FileSystemException {
            return content.getCertificates();
        }

        public FileContentInfo getContentInfo() throws FileSystemException {
            return content.getContentInfo();
        }

        public FileObject getFile() {
            // FIXME: depends on VFS-259, fixed in VFS fork
            // for vanilla VFS it would break some down-casting in providers implementations
            // (see HttpFileContentInfoFactory and WebdavFileContentInfoFactory).
            return decorateFile(content.getFile());
        }

        public InputStream getInputStream() throws FileSystemException {
            return content.getInputStream();
        }

        public long getLastModifiedTime() throws FileSystemException {
            return content.getLastModifiedTime();
        }

        public OutputStream getOutputStream() throws FileSystemException {
            checkIsNotReadOnly();
            return content.getOutputStream();
        }

        public OutputStream getOutputStream(boolean append) throws FileSystemException {
            checkIsNotReadOnly();
            return content.getOutputStream(append);
        }

        public RandomAccessContent getRandomAccessContent(RandomAccessMode mode) throws FileSystemException {
            if (mode.requestWrite())
                checkIsNotReadOnly();
            return content.getRandomAccessContent(mode);
        }

        public long getSize() throws FileSystemException {
            return content.getSize();
        }

        public boolean hasAttribute(String attrName) throws FileSystemException {
            return content.hasAttribute(attrName);
        }

        public boolean isOpen() {
            return content.isOpen();
        }

        public void removeAttribute(String attrName) throws FileSystemException {
            checkIsNotReadOnly();
            content.removeAttribute(attrName);
        }

        public void setAttribute(String attrName, Object value) throws FileSystemException {
            checkIsNotReadOnly();
            content.setAttribute(attrName, value);
        }

        public void setLastModifiedTime(long modTime) throws FileSystemException {
            checkIsNotReadOnly();
            content.setLastModifiedTime(modTime);
        }
    }
}