From: Scott S. <sco...@jb...> - 2006-07-10 17:05:22
|
User: starksm Date: 06/07/10 13:05:16 Added: src/main/org/jboss/system/server/profileservice VFSDeploymentScannerImpl.java Log: A deployment scanner that integrates with the VFS and ProfileService Revision Changes Path 1.1 date: 2006/07/10 17:05:16; author: starksm; state: Exp;system2/src/main/org/jboss/system/server/profileservice/VFSDeploymentScannerImpl.java Index: VFSDeploymentScannerImpl.java =================================================================== /* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt 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; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import org.jboss.deployers.spi.Deployment; import org.jboss.deployers.spi.MainDeployer; import org.jboss.deployment.IncompleteDeploymentException; import org.jboss.deployment.scanner.VFSDeploymentScanner; import org.jboss.net.protocol.URLLister; import org.jboss.net.protocol.URLLister.URLFilter; import org.jboss.profileservice.spi.Profile; import org.jboss.profileservice.spi.ProfileKey; import org.jboss.profileservice.spi.ProfileService; import org.jboss.util.JBossObject; import org.jboss.util.StringPropertyReplacer; import org.jboss.vfs.VFSFactory; import org.jboss.vfs.spi.ReadOnlyVFS; import org.jboss.vfs.spi.VirtualFile; /** * A DeploymentScanner build on top of the VFS and ProfileService. This is a * first pass to flesh out the APIs/concepts. * * @author <a href="mailto:dim...@jb...">Dimitris Andreadis</a> * @author Sco...@jb... * @version $Revision$ */ public class VFSDeploymentScannerImpl extends JBossObject implements VFSDeploymentScanner { // Private Data -------------------------------------------------- private MainDeployer mainDeployer; /** The VFSFactory used to obtain deployment VFS */ private VFSFactory factory; private ProfileService ps; private Profile serverProfile; private ProfileKey profileKey; /** The URIfied ServerHomeURL */ private URI serverHomeURI; /** The list of URIs to scan */ private List<URI> uriList = Collections.synchronizedList(new ArrayList()); /** The list of VirtualFiles to scan */ private List<VirtualFile> vdfList = Collections.synchronizedList(new ArrayList()); /** Allow a filter for scanned directories */ private URLLister.URLFilter filter; /** Whether to search for files inside directories whose names containing no dots */ private boolean doRecursiveSearch = true; /** A set of scanned VirtualFiles which have been deployed */ private Set deployedSet = Collections.synchronizedSet(new HashSet()); /** Remember the last exception for reporting */ private IncompleteDeploymentException lastIncompleteDeploymentException; // Constructor --------------------------------------------------- public VFSDeploymentScannerImpl() { // empty } // Attributes ---------------------------------------------------- public void setMainDeployer(MainDeployer deployer) { this.mainDeployer = deployer; this.factory = mainDeployer.getVFSFactory(); } /** * @return Returns the factory. */ public VFSFactory getVFSFactory() { return this.factory; } /** * @param factory The factory to set. */ public void setVFSFactory(VFSFactory factory) { this.factory = factory; } public void setProfileService(ProfileService ps) { this.ps = ps; } public ProfileService getProfileService() { return ps; } public ProfileKey getProfileKey() { return profileKey; } public void setProfileKey(ProfileKey key) { this.profileKey = key; } /* (non-Javadoc) * @see org.jboss.deployment.scanner.VFSDeploymentScanner#getScanPeriod() */ public long getScanPeriod() { // TODO Auto-generated method stub return 0; } /* (non-Javadoc) * @see org.jboss.deployment.scanner.VFSDeploymentScanner#isScanEnabled() */ public boolean isScanEnabled() { // TODO Auto-generated method stub return false; } /* (non-Javadoc) * @see org.jboss.deployment.scanner.VFSDeploymentScanner#setScanEnabled(boolean) */ public void setScanEnabled(boolean flag) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see org.jboss.deployment.scanner.VFSDeploymentScanner#setScanPeriod(long) */ public void setScanPeriod(long period) { // TODO Auto-generated method stub } /** * @throws URISyntaxException * @throws IOException */ public void setURIs(final String listspec) throws URISyntaxException, IOException { if (listspec == null) { throw new NullPointerException("listspec argument cannot be null"); } List<URI> list = new LinkedList<URI>(); StringTokenizer stok = new StringTokenizer(listspec, ","); while (stok.hasMoreTokens()) { String urispec = stok.nextToken().trim(); log.debug("Adding URI from spec: " + urispec); URI uri = makeURI(urispec); log.debug("URI: " + uri); list.add(uri); } setURIList(list); } /** * * @throws IOException */ public void setURIList(final List<URI> list) throws IOException { if (list == null) { throw new NullPointerException("list argument cannot be null"); } // start out with a fresh list uriList.clear(); for(int n = 0; n < list.size(); n ++) { URI uri = list.get(n); if (uri == null) { throw new IllegalArgumentException("list element["+n+"] is null"); } addURI(uri); } log.debug("URI list: " + uriList); } /** * @jmx:managed-attribute */ public List<URI> getURIList() { return new ArrayList<URI>(uriList); } /** * @jmx:managed-attribute */ public void setRecursiveSearch(boolean recurse) { doRecursiveSearch = recurse; } /** * @jmx:managed-attribute */ public boolean getRecursiveSearch() { return doRecursiveSearch; } /** * @jmx:managed-attribute */ public void setFilter(String classname) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class filterClass = Thread.currentThread().getContextClassLoader().loadClass(classname); filter = (URLLister.URLFilter)filterClass.newInstance(); } /** * @jmx:managed-attribute */ public String getFilter() { if (filter == null) { return null; } return filter.getClass().getName(); } /** * @jmx:managed-attribute */ public void setFilterInstance(URLFilter filter) { this.filter = filter; } /** * @jmx:managed-attribute */ public URLFilter getFilterInstance() { return filter; } // Operations ---------------------------------------------------- /** * @jmx:managed-operation */ public void addURI(final URI uri) throws IOException { if (uri == null) { throw new NullPointerException("uri argument cannot be null"); } if( uriList.add(uri) == true ) { log.debug("Added URI: " + uri); if (factory != null) { URL rootURL = uri.toURL(); ReadOnlyVFS vfs = factory.getVFS(rootURL); VirtualFile vf = vfs.resolveFile(""); vdfList.add(vf); } } } /** * @jmx:managed-operation */ public void removeURI(final URI uri) throws IOException { if (uri == null) { throw new NullPointerException("uri argument cannot be null"); } if (factory != null) { VirtualFile vf = getVFforURI(uri); vdfList.remove(vf); } boolean success = uriList.remove(uri); if (success) { log.debug("Removed URI: " + uri); } } /** * @jmx:managed-operation */ public boolean hasURI(final URI uri) { if (uri == null) { throw new NullPointerException("uri argument cannot be null"); } return uriList.contains(uri); } public void start() throws Exception { // synchronize uriList and vdfList because only at this point // setVirtualFileFactory() injection has been performed if (factory != null) { vdfList.clear(); for (Iterator<URI> i = uriList.iterator(); i.hasNext(); ) { URI uri = i.next(); VirtualFile vf = this.getVFforURI(uri); vdfList.add(vf); } } if( profileKey == null ) { profileKey = new ProfileKey("default"); } } // AbstractDeploymentScanner overrides --------------------------- public synchronized void scan() throws Exception { if (vdfList == null) { throw new IllegalStateException("not initialized"); } boolean trace = log.isTraceEnabled(); lastIncompleteDeploymentException = null; // Scan for deployments if (trace) { log.trace("Scanning for new deployments"); } // VirtualFiles to deploy List<VirtualFile> toDeployList = new LinkedList<VirtualFile>(); synchronized (vdfList) { for (Iterator i = vdfList.iterator(); i.hasNext();) { VirtualFile component = (VirtualFile)i.next(); if (component.isFile()) { // treat this as a deployable unit toDeployList.add(component); } else if (component.isDirectory()) { // process (possibly recursively) the dir addDeployments(toDeployList, component); } } } if (trace) { log.trace("toDeployList"); for (Iterator i = toDeployList.iterator(); i.hasNext();) { log.trace(i.next()); } } LinkedList<VirtualFile> toRemoveList = new LinkedList<VirtualFile>(); LinkedList<VirtualFile> toCheckForUpdateList = new LinkedList<VirtualFile>(); synchronized (deployedSet) { // remove previously deployed URLs no longer needed for (Iterator i = deployedSet.iterator(); i.hasNext();) { VirtualFile deployedComponent = (VirtualFile)i.next(); if (toDeployList.contains(deployedComponent)) { toCheckForUpdateList.add(deployedComponent); } else { toRemoveList.add(deployedComponent); } } } // ******** // Undeploy // ******** for (Iterator i = toRemoveList.iterator(); i.hasNext();) { VirtualFile deployedComponent = (VirtualFile)i.next(); undeploy(deployedComponent); } // ******** // Redeploy // ******** // compute the DeployedURL list to update ArrayList<VirtualFile> toUpdateList = new ArrayList<VirtualFile>(toCheckForUpdateList.size()); for (Iterator i = toCheckForUpdateList.iterator(); i.hasNext();) { VirtualFile deployedComponent = (VirtualFile)i.next(); if (isModified(deployedComponent)) { if (trace) { log.trace("Re-deploying " + deployedComponent); } toUpdateList.add(deployedComponent); } } // sort to update list //Collections.sort(toUpdateList, sorter); // Undeploy in order for (int i = toUpdateList.size() - 1; i >= 0; i--) { VirtualFile vf = toUpdateList.get(i); undeploy(vf); } // Deploy in order for (int i = 0; i < toUpdateList.size(); i++) { VirtualFile vf = toUpdateList.get(i); deploy(vf); } // ****** // Deploy // ****** //Collections.sort(toDeployList, sorter); for (Iterator i = toDeployList.iterator(); i.hasNext();) { VirtualFile component = (VirtualFile)i.next(); // if component is not deployed already, deploy it if (!deployedSet.contains(component)) { deploy(component); } // component must have been deployed by now, so remove it from list i.remove(); /* Check to see if mainDeployer suffix list has changed, if so, then resort if (i.hasNext() && updateSorter()) { Collections.sort(toDeployList, sorter); i = toDeployList.iterator(); } */ } /* TODO: Validate that there are still incomplete deployments if (lastIncompleteDeploymentException != null) { try { Object[] args = {}; String[] sig = {}; getServer().invoke(getDeployer(), "checkIncompleteDeployments", args, sig); } catch (Exception e) { Throwable t = JMXExceptionDecoder.decode(e); log.error(t); } } */ } // Private ------------------------------------------------------- /** * A helper to make a URI from a full/partial urispec */ private URI makeURI(String urispec) throws URISyntaxException { // First replace URI with appropriate properties urispec = StringPropertyReplacer.replaceProperties(urispec); return serverHomeURI.resolve(urispec); } /** * A helper to find all deployments under a directory component * and add them to the supplied list. * * We may recurse. */ private void addDeployments(List<VirtualFile> list, VirtualFile root) throws IOException { VirtualFile[] components = root.getChildren(); for (int i = 0; i < components.length; i++) { VirtualFile component = components[i]; if (component.isFile()) { // the first arg in filter.accept is not used! if (filter == null || filter.accept(null, component.getName())) { list.add(component); } } else if (component.isDirectory()) { if (component.getName().indexOf('.') == -1 && this.doRecursiveSearch) { // recurse if not '.' in name and recursive search is enabled addDeployments(list, component); } else { list.add(component); } } } } /** * A helper to deploy the given component using the deployer. */ private void deploy(final VirtualFile component) { // If the deployer is null simply ignore the request if (ps == null) { return; } if (log.isTraceEnabled()) { log.trace("Deploying: " + component); } Deployment deployment = null; try { URL componentUrl = component.toURL(); Profile profile = ps.getProfile(profileKey); // TODO: this is wasteful. Need a way to query the profile for a deployment URL? deployment = mainDeployer.parse(componentUrl); if( profile.getDeployment(deployment.getName()) == null ) profile.addDeployment(deployment); } catch (MalformedURLException e) { log.warn("Cannot convert to URL", e); return; } catch (Exception e) { log.debug("Failed to deploy: " + component, e); } String watchPath = getWatchURL(deployment); VirtualFile watchComponent = null; long deployedLastModified = -1; try { if (watchPath != null) { watchComponent = component.findChild(watchPath); deployedLastModified = watchComponent.getLastModified(); } else { deployedLastModified = component.getLastModified(); } } catch (IOException e) { log.warn(e); } //component.setContext(new ComponentContext(watchComponent, deployedLastModified)); if (!deployedSet.contains(component)) { deployedSet.add(component); } } /** * A helper to undeploy the given component using the deployer. */ private void undeploy(final VirtualFile component) { try { if (log.isTraceEnabled()) { log.trace("Undeploying: " + component); } deployedSet.remove(component); URL componentUrl = component.toURL(); Profile profile = ps.getProfile(profileKey); // TODO: this is wasteful. Need a way to query the profile for a deployment URL? Deployment deployment = mainDeployer.parse(componentUrl); profile.removeDeployment(deployment.getName()); } catch (Exception e) { log.error("Failed to undeploy: " + component, e); } } /** * Helper to get the watchURL for a deployment, if different. * Returns null otherwise */ private String getWatchURL(Deployment deployment) { String watchPath = (String) deployment.getRootContext().getContextData().get(""); return watchPath; } /** * Helper to find out if a deployed component has been modified * TODO: How should this be implemented */ public boolean isModified(VirtualFile component) { // get the context stored with every deployed component ComponentContext cc = null; //(ComponentContext)component.getContext(); // get modification time at the time of deployment long deployedLastModified = cc.deployedLastModified; // find out the current lastModified time either from // the component or the watched component, if exists long lastModified = (cc.watchComponent != null) ? cc.watchComponent.getLastModified() : component.getLastModified(); return deployedLastModified != lastModified; } private VirtualFile getVFforURI(URI uri) throws IOException { VirtualFile vf = null; if (factory != null) { URL rootURL = uri.toURL(); ReadOnlyVFS vfs = factory.getVFS(rootURL); vf = vfs.resolveFile(""); } return vf; } /** * Simple holder class to let us store extra info * on a VirtualFile object */ private static class ComponentContext { public VirtualFile watchComponent; public long deployedLastModified; public ComponentContext(VirtualFile watchComponent, long deployedLastModified) { this.watchComponent = watchComponent; this.deployedLastModified = deployedLastModified; } } } |