/* * JBoss, Home of Professional Open Source * Copyright 2009, 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 java.io.Closeable; import java.io.File; import java.io.IOException; import java.security.SecureRandom; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import org.jboss.vfs.util.PathTokenizer; /** * Assembly of VirtualFiles that can be mounted into the VFS in a structure * that is not required to match a real filesystem structure. * * @author <a href="baileyje@gmail.com">John Bailey</a> */ public class VirtualFileAssembly implements Closeable { private static final Random RANDOM_NUM_GEN = new SecureRandom(); private final AssemblyNode rootNode = new AssemblyNode(""); private final List<Closeable> mountHandles = new CopyOnWriteArrayList<Closeable>(); private final VirtualFile mountRoot = VFS.getChild("assembly-mounts").getChild(getAssemblyId()); private TempFileProvider tempFileProvider; /** * Add a {@link VirtualFile} to the assembly. * * @param virtualFile */ public void add(VirtualFile virtualFile) { String path = virtualFile.getName(); AssemblyNode assemblyNode = rootNode.findOrBuild(path); assemblyNode.setTarget(virtualFile); } /** * Add a {@link VirtualFile} to the assembly in a given path. * * @param path * @param virtualFile */ public void add(String path, VirtualFile virtualFile) { AssemblyNode assemblyNode = rootNode.findOrBuild(path); assemblyNode.setTarget(virtualFile); } public void add(final String path, final File root) throws IOException { VirtualFile mountPoint = mountRoot.getChild(path); Closeable handle = VFS.mountReal(root, mountPoint); mountHandles.add(handle); add(path, mountPoint); } public void addZip(final String path, final File zipFile) throws IOException { VirtualFile mountPoint = mountRoot.getChild(path); Closeable handle = VFS.mountZip(zipFile, mountPoint, getTempFileProvider()); mountHandles.add(handle); add(path, mountPoint); } /** * Get the VirtualFile from the assembly. This will traverse VirtualFiles in assembly * to find children if needed. * * @param mountPoint * @param target * @return * @throws IOException */ public VirtualFile getFile(VirtualFile mountPoint, VirtualFile target) { final String path = target.getPathNameRelativeTo(mountPoint); return rootNode.getFile(new Path(path), mountPoint); } /** * Returns a list of all the names of the children in the assembly. * * @return */ public List<String> getChildNames(VirtualFile mountPoint, VirtualFile target) { List<String> names = new LinkedList<String>(); AssemblyNode targetNode = null; if (mountPoint.equals(target)) { targetNode = rootNode; } else { targetNode = rootNode.find(target.getPathNameRelativeTo(mountPoint)); } if (targetNode != null) { for (AssemblyNode childNode : targetNode.children.values()) { names.add(childNode.realName); } } return names; } public boolean contains(VirtualFile mountPoint, VirtualFile target) { final String path = target.getPathNameRelativeTo(mountPoint); return rootNode.find(path) != null; } /** * Close the assembly and nested resources. */ public void close() { VFSUtils.safeClose(mountHandles); } /** * @return * @throws IOException */ private TempFileProvider getTempFileProvider() throws IOException { if (tempFileProvider == null) { tempFileProvider = TempFileProvider.create("temp", Executors.newSingleThreadScheduledExecutor()); } return tempFileProvider; } private String getAssemblyId() { return Long.toHexString(RANDOM_NUM_GEN.nextLong()); } /** * Path representation to hold onto the elements of the path. */ private static class Path { private final Queue<String> parts; private Path(String path) { parts = new LinkedList<String>(); List<String> tokens = PathTokenizer.getTokens(path); parts.addAll(tokens); } private boolean isEndOfPath() { return parts.isEmpty(); } private String getCurrent() { return parts.poll(); } } /** * Node located within the assembly. */ private static class AssemblyNode { private final Map<String, AssemblyNode> children = new ConcurrentHashMap<String, AssemblyNode>(); private final String realName; private VirtualFile target; AssemblyNode(String realName) { this.realName = realName; } /** * Find an AssemblyNode staring with this node and return null if not found. * * @param path * @return */ public AssemblyNode find(String path) { return find(new Path(path), false); } /** * Find an AssemblyNode starting with this node and build the required nodes if not found. * * @param path * @return */ public AssemblyNode findOrBuild(String path) { return find(new Path(path), true); } /** * Find an AssemblyNode starting with this node. * * @param path * @param createIfMissing * @return */ public AssemblyNode find(Path path, boolean createIfMissing) { if (path.isEndOfPath()) { return this; } String current = path.getCurrent(); AssemblyNode childNode = getChild(current); if (childNode == null) { if (!createIfMissing) { return null; } childNode = new AssemblyNode(current); addChild(current, childNode); } return childNode.find(path, createIfMissing); } /** * Get the VirtualFile for a given path. Will traverse VirtualFile links if not * found in the assembly. * * @param path * @return * @throws IOException */ public VirtualFile getFile(Path path, VirtualFile assemblyMountPoint) { if (path.isEndOfPath()) { return target; } String current = path.getCurrent(); AssemblyNode childNode = getChild(current); if (childNode != null) { return childNode.getFile(path, assemblyMountPoint); } if (target != null) { VirtualFile currentFile = target != null ? target.getChild(current) : null; if (currentFile != null) { while (!path.isEndOfPath()) { current = path.getCurrent(); currentFile = currentFile.getChild(current); if (currentFile == null) { return null; } } return currentFile; } } return null; } private void addChild(String name, AssemblyNode child) { children.put(name.toLowerCase(), child); } private AssemblyNode getChild(String name) { return children.get(name.toLowerCase()); } private void setTarget(VirtualFile target) { this.target = target; } } }