/* * JBoss, Home of Professional Open Source. * Copyright 2009, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.system.server.profileservice.repository; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.jboss.logging.Logger; import org.jboss.profileservice.spi.NoSuchDeploymentException; import org.jboss.profileservice.spi.NoSuchProfileException; import org.jboss.profileservice.spi.ProfileDeployment; import org.jboss.virtual.VFS; import org.jboss.virtual.VirtualFile; import org.jboss.virtual.VirtualFileFilter; /** * A abstract VFS based source for profile deployments. * * @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a> * @version $Revision: 89033 $ */ public abstract class AbstractVFSProfileSource { /** The repository uris. */ protected final Collection<URI> uris; /** The VFSDeployments. */ private Map<String, ProfileDeployment> applicationCtxs = new ConcurrentHashMap<String, ProfileDeployment>(); /** Allowed deployments filter. */ private VirtualFileFilter deploymentFilter; /** The application files keyed by VirtualFile URI string. */ private final Map<String, VirtualFile> applicationVFCache = new ConcurrentHashMap<String, VirtualFile>(); /** Do a recursive search - default == true. */ private boolean recursiveScan = true; /** The last time the profile was modified. */ private volatile long lastModified; /** The logger. */ protected final Logger log = Logger.getLogger(getClass()); public AbstractVFSProfileSource(URI[] uris) { if(uris == null) throw new IllegalArgumentException("Null uris"); this.uris = new ArrayList<URI>(); for(URI uri : uris) { this.uris.add(uri); } } public VirtualFileFilter getDeploymentFilter() { return deploymentFilter; } public void setDeploymentFilter(VirtualFileFilter deploymentFilter) { this.deploymentFilter = deploymentFilter; } public boolean isRecursiveScan() { return recursiveScan; } public void setRecursiveScan(boolean recursiveScan) { this.recursiveScan = recursiveScan; } public URI[] getRepositoryURIs() { return uris.toArray(new URI[uris.size()]); } public Set<String> getDeploymentNames() { return this.applicationCtxs.keySet(); } public Collection<ProfileDeployment> getDeployments() { return this.applicationCtxs.values(); } public long getLastModified() { return this.lastModified; } public void destroy() { // Unload this.applicationCtxs.clear(); this.applicationVFCache.clear(); updateLastModfied(); } /** * Add a deployment to this profile source. * * @param vfsPath the deployment name * @param d the deployment * @throws Exception */ public void addDeployment(String vfsPath, ProfileDeployment d) throws Exception { if(d == null) throw new IllegalArgumentException("Null deployment"); // Add deployment and vfs this.applicationCtxs.put(d.getName(), d); if(d.getRoot() != null) this.applicationVFCache.put(d.getName(), d.getRoot()); updateLastModfied(); } /** * Get the deployment * * @param vfsPath the deployment * @return the deployment or null if it does not exist */ public ProfileDeployment getDeployment(String vfsPath) throws NoSuchDeploymentException { if(vfsPath == null) throw new IllegalArgumentException("Null vfsPath"); return this.applicationCtxs.get(vfsPath); } /** * Remove a deployment from this source. * * @param vfsPath the deployment name * @return the deployment * @throws NoSuchProfileException if the deployment does not exist */ public ProfileDeployment removeDeployment(String vfsPath) throws Exception { if(vfsPath == null) throw new IllegalArgumentException("Null vfsPath"); // Get the deployment ProfileDeployment deployment = getDeployment(vfsPath); // Remove the entries this.applicationCtxs.remove(deployment.getName()); this.applicationVFCache.remove(deployment.getName()); // Update last modified updateLastModfied(); // Return return deployment; } /** * Load all the applications under the applicationDir. * * @param applicationDir the application directory * @throws IOException */ protected void loadApplications(VirtualFile applicationDir) throws Exception { ArrayList<VirtualFile> added = new ArrayList<VirtualFile>(); addedDeployment(added, applicationDir); for (VirtualFile vf : added) { ProfileDeployment vfCtx = createDeployment(vf); addDeployment(vfCtx.getName(), vfCtx); } } /** * Scan the children of the root for deployments. * * @param list a list of virtual files, where new deployments are added to * @param root the root to scan * @throws IOException * @throws URISyntaxException */ protected void addedDeployments(List<VirtualFile> list, VirtualFile root) throws IOException, URISyntaxException { if(root.isLeaf() == true || root.isArchive() == true) { addedDeployment(list, root); } else { List<VirtualFile> components = root.getChildren(); for (VirtualFile component : components) { addedDeployment(list, component); } } } /** * Scan a given virtualFile for deployments. * * @param list a list of virtual files, where new deployments are added to * @param component the root file * @throws IOException * @throws URISyntaxException */ protected void addedDeployment(List<VirtualFile> list, VirtualFile component) throws IOException, URISyntaxException { // Excluding files from scanning if(deploymentFilter != null && this.deploymentFilter.accepts(component) == false) { if(log.isTraceEnabled()) log.trace("ignoring "+ component); return; } // Check if we accept this deployment String key = component.toURI().toString(); if(acceptsDeployment(key) == false) return; // If it's a directory or exploded deployment if(component.isLeaf() == false && component.isArchive() == false) { // Check the name if(isRecursiveScan() && component.getName().indexOf('.') == -1) { // recurse if not '.' in name and recursive search is enabled addedDeployments(list, component); } else { list.add(component); } } else { list.add(component); } } /** * Check if the deployment should be added. * * @param name the deployment name * @return */ protected boolean acceptsDeployment(String name) { return applicationCtxs.containsKey(name) == false; } /** * Try to find a deployment, based on the cached * virtual roots of deployments we have. * * @param name the deployment name * @return a collection of matching names */ protected List<String> findDeploymentContent(String name) { if(this.applicationVFCache.containsKey(name)) { return Collections.singletonList(name); } List<String> contents = new ArrayList<String>(); for(String cacheName : this.applicationVFCache.keySet()) { String fixedName = cacheName; if(cacheName.endsWith("/")) fixedName = cacheName.substring(0, cacheName.length() -1); if(fixedName.endsWith(name)) { contents.add(cacheName); } } return contents; } /** * Add a virtualFile to the local virtualFileCache * * @param vf the virtual file * @return the name * @throws MalformedURLException * @throws URISyntaxException */ protected String addVirtualFileCache(VirtualFile vf) throws MalformedURLException, URISyntaxException { String uri = vf.toURI().toString(); this.applicationVFCache.put(uri, vf); return uri; } /** * Get a cached virtual file. * * @param name the name * @return the virtual file or null, if it does not exist */ protected VirtualFile getCachedVirtualFile(String name) { return this.applicationVFCache.get(name); } /** * Get a cached virtual file. This will add the uri to the * virtualFile if it does not exist. * * @param uri the uri * @return the virtual file * @throws IOException */ protected VirtualFile getCachedVirtualFile(URI uri) throws IOException { VirtualFile vf = getCachedVirtualFile(uri.toString()); if(vf == null) { vf = VFS.getRoot(uri); this.applicationVFCache.put(uri.toString(), vf); } return vf; } /** * Create a ProfileDeployment, based on the virtual file. * * @param vf the deployment root * @return the profile deployment * @throws Exception */ protected static ProfileDeployment createDeployment(VirtualFile vf) throws Exception { return new AbstractProfileDeployment(vf); } /** * Internally update the lastModified timestamp. */ protected void updateLastModfied() { this.lastModified = System.currentTimeMillis(); } }