All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.vfs.VFS Maven / Gradle / Ivy

There is a newer version: 3.3.2.Final
Show newest version
/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag.
*
 * Licensed 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.jboss.vfs;

import static org.jboss.vfs.VFSMessages.MESSAGES;

import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;

import org.jboss.vfs.spi.AssemblyFileSystem;
import org.jboss.vfs.spi.FileSystem;
import org.jboss.vfs.spi.JavaZipFileSystem;
import org.jboss.vfs.spi.MountHandle;
import org.jboss.vfs.spi.RealFileSystem;
import org.jboss.vfs.spi.RootFileSystem;

/**
 * Virtual File System
 *
 * @author Adrian Brock
 * @author [email protected]
 * @author Ales Justin
 * @author David M. Lloyd
 * @version $Revision: 1.1 $
 */
public class VFS {
    private static final ConcurrentMap> mounts = new ConcurrentHashMap>();
    private static final VirtualFile rootVirtualFile = createDefaultRoot();

    private static VirtualFile createDefaultRoot() {
        return isWindows() ? getChild("/") : new VirtualFile("/", null);
    }

    // Note that rootVirtualFile is ignored by RootFS
    private static final Mount rootMount = new Mount(RootFileSystem.ROOT_INSTANCE, rootVirtualFile);

    static {
        init();
    }

    /**
     * Do not allow construction
     */
    private VFS() {
    }

    /**
     * Initialize VFS protocol handlers package property.
     */
    private static void init() {
        String pkgs = System.getProperty("java.protocol.handler.pkgs");
        if (pkgs == null || pkgs.trim().length() == 0) {
            pkgs = "org.jboss.net.protocol|org.jboss.vfs.protocol";
            System.setProperty("java.protocol.handler.pkgs", pkgs);
        } else if (pkgs.contains("org.jboss.vfs.protocol") == false) {
            if (pkgs.contains("org.jboss.net.protocol") == false) { pkgs += "|org.jboss.net.protocol"; }
            pkgs += "|org.jboss.vfs.protocol";
            System.setProperty("java.protocol.handler.pkgs", pkgs);
        }
    }

    /**
     * Mount a filesystem on a mount point in the VFS.  The mount point is any valid file name, existent or non-existent.
     * If a relative path is given, it will be treated as relative to the VFS root.
     *
     * @param mountPoint the mount point
     * @param fileSystem the file system to mount
     * @return a handle which can be used to unmount the filesystem
     * @throws IOException if an I/O error occurs, such as a filesystem already being mounted at the given mount point
     */
    public static Closeable mount(VirtualFile mountPoint, FileSystem fileSystem) throws IOException {
        final VirtualFile parent = mountPoint.getParent();
        if (parent == null) {
            throw VFSMessages.MESSAGES.rootFileSystemAlreadyMounted();
        }
        final String name = mountPoint.getName();
        final Mount mount = new Mount(fileSystem, mountPoint);
        final ConcurrentMap> mounts = VFS.mounts;
        for (; ; ) {
            Map childMountMap = mounts.get(parent);
            Map newMap;
            if (childMountMap == null) {
                childMountMap = mounts.putIfAbsent(parent, Collections.singletonMap(name, mount));
                if (childMountMap == null) {
                    return mount;
                }
            }
            newMap = new HashMap(childMountMap);
            if (newMap.put(name, mount) != null) {
                throw VFSMessages.MESSAGES.fileSystemAlreadyMountedAtMountPoint(mountPoint);
            }
            if (mounts.replace(parent, childMountMap, newMap)) {
                VFSLogger.ROOT_LOGGER.tracef("Mounted filesystem %s on mount point %s", fileSystem, mountPoint);
                return mount;
            }
        }
    }

    /**
     * Find a virtual file.
     *
     * @param url the URL whose path component is the child path
     * @return the child
     * @throws IllegalArgumentException    if the path is null
     * @throws java.net.URISyntaxException for any uri error
     * @deprecated use getChild(URI) instead
     */
    @Deprecated
    public static VirtualFile getChild(URL url) throws URISyntaxException {
        return getChild(url.toURI());
    }

    private static boolean isWindows() {
        // Not completely accurate, but good enough
        return File.separatorChar == '\\';
    }

    /**
     * Find a virtual file.
     *
     * @param uri the URI whose path component is the child path
     * @return the child
     * @throws IllegalArgumentException if the path is null
     */
    public static VirtualFile getChild(URI uri) {
        String path = uri.getPath();
        if(path == null) {
            path = uri.getSchemeSpecificPart();
        }
        return getChild(path);
    }

    /**
     * Find a virtual file.
     *
     * @param path the child path
     * @return the child
     * @throws IllegalArgumentException if the path is null
     */
    public static VirtualFile getChild(String path) {
        if (path == null) {
            throw VFSMessages.MESSAGES.nullArgument("path");
        }

        VirtualFile root = null;

        if (isWindows()) {
            // Normalize the path using java.io.File
            //   TODO Consider creating our own normalization routine, which would
            //   allow for testing on non-Windows
            String absolute = new File(path).getAbsolutePath();
            if (absolute.length() > 2) {
                if (absolute.charAt(1) == ':') {
                    // Drive form
                    root = new VirtualFile("/" + absolute.charAt(0) + ":/", null);
                    path = absolute.substring(2).replace('\\', '/');
                } else if (absolute.charAt(0) == '\\' && absolute.charAt(1) == '\\') {
                    // UNC form
                    for (int i = 2; i < absolute.length(); i++) {
                        if (absolute.charAt(i) == '\\') {
                            // Switch \\ to // just like java file URLs.
                            // Note, it turns out that File.toURL puts this portion
                            // in the path portion of the URL, which is actually not
                            // correct, since // is supposed to signify the authority.
                            root = new VirtualFile("//" + absolute.substring(0, i) + "/", null);
                            path = absolute.substring(i).replace('\\', '/');
                            break;
                        }
                    }
                }
            }

            if (root == null) {
                throw MESSAGES.invalidWin32Path(path);
            }
        } else {
            root = rootVirtualFile;
        }

        return root.getChild(path);
    }

    /**
     * Get the root virtual file for this VFS instance.
     *
     * @return the root virtual file
     */
    public static VirtualFile getRootVirtualFile() {
        return rootVirtualFile;
    }

    /**
     * Get the children
     *
     * @return the children
     * @throws IOException for any problem accessing the virtual file system
     */
    public static List getChildren() throws IOException {
        return getRootVirtualFile().getChildren(null);
    }

    /**
     * Get the children
     *
     * @param filter to filter the children
     * @return the children
     * @throws IOException for any problem accessing the virtual file system
     */
    public static List getChildren(VirtualFileFilter filter) throws IOException {
        return getRootVirtualFile().getChildren(filter);
    }

    /**
     * Get all the children recursively

*

* This always uses {@link VisitorAttributes#RECURSE} * * @return the children * @throws IOException for any problem accessing the virtual file system */ public static List getChildrenRecursively() throws IOException { return getRootVirtualFile().getChildrenRecursively(null); } /** * Get all the children recursively

*

* This always uses {@link VisitorAttributes#RECURSE} * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system */ public static List getChildrenRecursively(VirtualFileFilter filter) throws IOException { return getRootVirtualFile().getChildrenRecursively(filter); } /** * Visit the virtual file system from the root * * @param visitor the visitor * @throws IOException for any problem accessing the VFS * @throws IllegalArgumentException if the visitor is null */ public static void visit(VirtualFileVisitor visitor) throws IOException { visitor.visit(getRootVirtualFile()); } /** * Visit the virtual file system * * @param file the file * @param visitor the visitor * @throws IOException for any problem accessing the VFS * @throws IllegalArgumentException if the file or visitor is null */ protected static void visit(VirtualFile file, VirtualFileVisitor visitor) throws IOException { if (file == null) { throw VFSMessages.MESSAGES.nullArgument("file"); } visitor.visit(file); } static Mount getMount(VirtualFile virtualFile) { final ConcurrentMap> mounts = VFS.mounts; for (; ; ) { final VirtualFile parent = virtualFile.getParent(); if (parent == null) { return rootMount; } final Map parentMounts = mounts.get(parent); if (parentMounts == null) { virtualFile = parent; } else { final Mount mount = parentMounts.get(virtualFile.getName()); if (mount == null) { virtualFile = parent; } else { return mount; } } } } /** * Get all immediate submounts for a path. * * @param virtualFile the path * @return the collection of present mount (simple) names */ static Set getSubmounts(VirtualFile virtualFile) { final ConcurrentMap> mounts = VFS.mounts; final Map mountMap = mounts.get(virtualFile); if (mountMap == null) { return emptyRemovableSet(); } return new HashSet(mountMap.keySet()); } private static MountHandle doMount(final FileSystem fileSystem, final VirtualFile mountPoint, Closeable... additionalCloseables) throws IOException { boolean ok = false; try { final Closeable mountHandle = mount(mountPoint, fileSystem); ok = true; return new BasicMountHandle(fileSystem, mountHandle, additionalCloseables); } finally { if (!ok) { VFSUtils.safeClose(fileSystem); } } } /** * Create and mount a zip file into the filesystem, returning a single handle which will unmount and close the file * system when closed. * * @param zipFile the zip file to mount * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountZip(File zipFile, VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { boolean ok = false; final TempDir tempDir = tempFileProvider.createTempDir(zipFile.getName()); try { final MountHandle handle = doMount(new JavaZipFileSystem(zipFile, tempDir), mountPoint); ok = true; return handle; } finally { if (!ok) { VFSUtils.safeClose(tempDir); } } } /** * Create and mount a zip file into the filesystem, returning a single handle which will unmount and close the file * system when closed. * * @param zipData an input stream containing the zip data * @param zipName the name of the archive * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountZip(InputStream zipData, String zipName, VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { boolean ok = false; try { final TempDir tempDir = tempFileProvider.createTempDir(zipName); try { final MountHandle handle = doMount(new JavaZipFileSystem(zipName, zipData, tempDir), mountPoint); ok = true; return handle; } finally { if (!ok) { VFSUtils.safeClose(tempDir); } } } finally { VFSUtils.safeClose(zipData); } } /** * Create and mount a zip file into the filesystem, returning a single handle which will unmount and close the file * system when closed. * * @param zipFile a zip file in the VFS * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountZip(VirtualFile zipFile, VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { return mountZip(zipFile.openStream(), zipFile.getName(), mountPoint, tempFileProvider); } /** * Create and mount a real file system, returning a single handle which will unmount and close the filesystem when * closed. * * @param realRoot the real filesystem root * @param mountPoint the point at which the filesystem should be mounted * @return a handle * @throws IOException if an error occurs */ public static Closeable mountReal(File realRoot, VirtualFile mountPoint) throws IOException { return doMount(new RealFileSystem(realRoot), mountPoint); } /** * Create and mount a temporary file system, returning a single handle which will unmount and close the filesystem * when closed. * * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountTemp(VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { boolean ok = false; final TempDir tempDir = tempFileProvider.createTempDir("tmpfs"); try { final MountHandle handle = doMount(new RealFileSystem(tempDir.getRoot()), mountPoint, tempDir); ok = true; return handle; } finally { if (!ok) { VFSUtils.safeClose(tempDir); } } } /** * Create and mount an expanded zip file in a temporary file system, returning a single handle which will unmount and * close the filesystem when closed. * * @param zipFile the zip file to mount * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountZipExpanded(File zipFile, VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { boolean ok = false; final TempDir tempDir = tempFileProvider.createTempDir(zipFile.getName()); try { final File rootFile = tempDir.getRoot(); VFSUtils.unzip(zipFile, rootFile); final MountHandle handle = doMount(new RealFileSystem(rootFile), mountPoint, tempDir); ok = true; return handle; } finally { if (!ok) { VFSUtils.safeClose(tempDir); } } } /** * Create and mount an expanded zip file in a temporary file system, returning a single handle which will unmount and * close the filesystem when closed. The given zip data stream is closed. * * @param zipData an input stream containing the zip data * @param zipName the name of the archive * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountZipExpanded(InputStream zipData, String zipName, VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { try { boolean ok = false; final TempDir tempDir = tempFileProvider.createTempDir(zipName); try { final File zipFile = File.createTempFile(zipName + "-", ".tmp", tempDir.getRoot()); try { final FileOutputStream os = new FileOutputStream(zipFile); try { // allow an error on close to terminate the unzip VFSUtils.copyStream(zipData, os); zipData.close(); os.close(); } finally { VFSUtils.safeClose(zipData); VFSUtils.safeClose(os); } final File rootFile = tempDir.getRoot(); VFSUtils.unzip(zipFile, rootFile); final MountHandle handle = doMount(new RealFileSystem(rootFile), mountPoint, tempDir); ok = true; return handle; } finally { //noinspection ResultOfMethodCallIgnored zipFile.delete(); } } finally { if (!ok) { VFSUtils.safeClose(tempDir); } } } finally { VFSUtils.safeClose(zipData); } } /** * Create and mount an expanded zip file in a temporary file system, returning a single handle which will unmount and * close the filesystem when closed. The given zip data stream is closed. * * @param zipFile a zip file in the VFS * @param mountPoint the point at which the filesystem should be mounted * @param tempFileProvider the temporary file provider * @return a handle * @throws IOException if an error occurs */ public static Closeable mountZipExpanded(VirtualFile zipFile, VirtualFile mountPoint, TempFileProvider tempFileProvider) throws IOException { return mountZipExpanded(zipFile.openStream(), zipFile.getName(), mountPoint, tempFileProvider); } /** * Create and mount an assembly file system, returning a single handle which will unmount and * close the filesystem when closed. * * @param assembly an {@link VirtualFileAssembly} to mount in the VFS * @param mountPoint the point at which the filesystem should be mounted * @return a handle * @throws IOException if an error occurs */ public static Closeable mountAssembly(VirtualFileAssembly assembly, VirtualFile mountPoint) throws IOException { return doMount(new AssemblyFileSystem(assembly), mountPoint); } @SuppressWarnings({"unchecked"}) private static Set emptyRemovableSet() { return EMPTY_REMOVABLE_SET; } @SuppressWarnings("unchecked") private static final Set EMPTY_REMOVABLE_SET = new EmptyRemovableSet(); private static final class EmptyRemovableSet extends AbstractSet { public boolean remove(Object o) { return false; } public boolean retainAll(Collection c) { return false; } public void clear() { } public Iterator iterator() { return Collections.emptySet().iterator(); } public int size() { return 0; } } /** * The mount representation. This instance represents a binding between a position in the virtual filesystem and the * backing filesystem implementation; the same {@code FileSystem} may be mounted in more than one place, however only * one {@code FileSystem} may be bound to a specific path at a time. */ static final class Mount implements Closeable { private final FileSystem fileSystem; private final VirtualFile mountPoint; private final StackTraceElement[] allocationPoint; private final AtomicBoolean closed = new AtomicBoolean(false); Mount(FileSystem fileSystem, VirtualFile mountPoint) { this.fileSystem = fileSystem; this.mountPoint = mountPoint; allocationPoint = Thread.currentThread().getStackTrace(); } public void close() throws IOException { if (closed.getAndSet(true)) { return; } final String name = mountPoint.getName(); final VirtualFile parent = mountPoint.getParent(); final ConcurrentMap> mounts = VFS.mounts; for (; ; ) { final Map parentMounts = mounts.get(parent); if (parentMounts == null) { return; } final VFS.Mount mount = parentMounts.get(name); if (mount != this) { return; } final Map newParentMounts; if (parentMounts.size() == 2) { final Iterator> ei = parentMounts.entrySet().iterator(); final Map.Entry e1 = ei.next(); if (e1.getKey().equals(name)) { final Map.Entry e2 = ei.next(); newParentMounts = Collections.singletonMap(e2.getKey(), e2.getValue()); } else { newParentMounts = Collections.singletonMap(e1.getKey(), e1.getValue()); } if (mounts.replace(parent, parentMounts, newParentMounts)) { VFSLogger.ROOT_LOGGER.tracef("Unmounted filesystem %s on mount point %s", fileSystem, mountPoint); return; } } else if (parentMounts.size() == 1) { if (mounts.remove(parent, parentMounts)) { VFSLogger.ROOT_LOGGER.tracef("Unmounted filesystem %s on mount point %s", fileSystem, mountPoint); return; } } else { newParentMounts = new HashMap(parentMounts); newParentMounts.remove(name); if (mounts.replace(parent, parentMounts, newParentMounts)) { VFSLogger.ROOT_LOGGER.tracef("Unmounted filesystem %s on mount point %s", fileSystem, mountPoint); return; } } } } FileSystem getFileSystem() { return fileSystem; } VirtualFile getMountPoint() { return mountPoint; } @SuppressWarnings({"FinalizeDoesntCallSuperFinalize"}) protected void finalize() throws IOException { if (!closed.get()) { final StackTraceElement[] allocationPoint = this.allocationPoint; if (allocationPoint != null) { final LeakDescriptor t = new LeakDescriptor(); t.setStackTrace(allocationPoint); VFSLogger.ROOT_LOGGER.vfsMountLeaked(mountPoint, t); } else { VFSLogger.ROOT_LOGGER.vfsMountLeaked(mountPoint, null); } close(); } } } private static final class LeakDescriptor extends Throwable { private static final long serialVersionUID = 6034058126740270584L; public String toString() { return "Allocation stack trace:"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy