[jboss-cvs] JBossAS SVN: r58655 - trunk/system/src/main/org/jboss/deployers/plugins/scanner

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue Nov 21 03:02:23 EST 2006


Author: scott.stark at jboss.org
Date: 2006-11-21 03:02:21 -0500 (Tue, 21 Nov 2006)
New Revision: 58655

Added:
   trunk/system/src/main/org/jboss/deployers/plugins/scanner/VFSDeploymentScannerImpl.java
Log:
Restore the hotdeployment vfs scanner

Added: trunk/system/src/main/org/jboss/deployers/plugins/scanner/VFSDeploymentScannerImpl.java
===================================================================
--- trunk/system/src/main/org/jboss/deployers/plugins/scanner/VFSDeploymentScannerImpl.java	2006-11-21 08:02:19 UTC (rev 58654)
+++ trunk/system/src/main/org/jboss/deployers/plugins/scanner/VFSDeploymentScannerImpl.java	2006-11-21 08:02:21 UTC (rev 58655)
@@ -0,0 +1,615 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * 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.deployers.plugins.scanner;
+
+import static org.jboss.deployers.spi.structure.StructureDetermined.PREDETERMINED;
+
+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.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import org.jboss.deployers.plugins.structure.AbstractDeploymentContext;
+import org.jboss.deployers.plugins.structure.BasicStructuredDeployers;
+import org.jboss.deployers.plugins.structure.StructureMetaDataImpl;
+import org.jboss.deployers.spi.DeploymentException;
+import org.jboss.deployers.spi.deployment.MainDeployer;
+import org.jboss.deployers.spi.structure.DeploymentContext;
+import org.jboss.deployers.spi.structure.vfs.StructureBuilder;
+import org.jboss.deployers.spi.structure.vfs.StructureDeployer;
+import org.jboss.deployers.spi.structure.vfs.StructureMetaData;
+import org.jboss.deployers.spi.structure.vfs.StructuredDeployers;
+import org.jboss.logging.Logger;
+import org.jboss.util.StringPropertyReplacer;
+import org.jboss.virtual.VFS;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.virtual.VirtualFileFilter;
+
+
+/**
+ * A DeploymentScanner build on top of the VFS and MainDeployer. This is a
+ * first pass to flesh out the APIs/concepts.
+ * 
+ * @author <a href="mailto:dimitris at jboss.org">Dimitris Andreadis</a>
+ * @author Scott.Stark at jboss.org
+ * @version $Revision$
+ */
+public class VFSDeploymentScannerImpl
+   implements Runnable
+{
+   private static final Logger log = Logger.getLogger(VFSDeploymentScannerImpl.class);
+   // Private Data --------------------------------------------------
+   private MainDeployer mainDeployer;
+   /** The structure builder that translates structure metadata to deployments */
+   private StructureBuilder structureBuilder;
+   private StructuredDeployers structureDeployers  = new BasicStructuredDeployers();
+
+   /** */
+   private VirtualFileFilter filter;
+   /** The ExecutorService/ThreadPool for performing scans */
+   private ScheduledExecutorService scanExecutor;
+   private ScheduledFuture activeScan;
+
+   /** The URIfied ServerHomeURL */
+   private URI serverHomeURI;
+   
+   /** The list of URIs to scan */
+   private List<URI> uriList = Collections.synchronizedList(new ArrayList<URI>());
+   
+   /** The list of VirtualFiles to scan */
+   private List<VirtualFile> vdfList = Collections.synchronizedList(new ArrayList<VirtualFile>());
+   
+   /** Whether to search for files inside directories whose names containing no dots */
+   private boolean doRecursiveSearch = true;
+   /** Period in ms between deployment scans */
+   private long scanPeriod = 5000;
+   private int scanCount;
+
+   /** A set of scanned VirtualFiles which have been deployed */
+   private Map<VirtualFile, DeploymentContext> deployedMap = new ConcurrentHashMap<VirtualFile, DeploymentContext>();
+
+   // Constructor ---------------------------------------------------
+   
+   public VFSDeploymentScannerImpl()
+   {
+      // empty
+   }
+   
+   // Attributes ----------------------------------------------------
+
+   public void setMainDeployer(MainDeployer deployer)
+   {
+      this.mainDeployer = deployer;
+   }
+
+   /**
+    * Get the structure deployers
+    * 
+    * @return the structure deployers
+    */
+   public synchronized Set<StructureDeployer> getStructureDeployers()
+   {
+      SortedSet<StructureDeployer> sdeployers = structureDeployers.getDeployers();
+      return sdeployers;
+   }
+
+   public VirtualFileFilter getFilter()
+   {
+      return filter;
+   }
+   public void setFilter(VirtualFileFilter filter)
+   {
+      this.filter = filter;
+   }
+
+   /**
+    * @return Returns the scanExecutor.
+    */
+   public ScheduledExecutorService getScanExecutor()
+   {
+      return this.scanExecutor;
+   }
+
+   /**
+    * @param scanExecutor The scanExecutor to set.
+    */
+   public void setScanExecutor(ScheduledExecutorService scanExecutor)
+   {
+      this.scanExecutor = scanExecutor;
+   }
+
+   /* (non-Javadoc)
+    * @see org.jboss.deployment.scanner.VFSDeploymentScanner#getScanPeriod()
+    */
+   public long getScanPeriod()
+   {
+      return scanPeriod;
+   }
+   /* (non-Javadoc)
+    * @see org.jboss.deployment.scanner.VFSDeploymentScanner#setScanPeriod(long)
+    */
+   public void setScanPeriod(long period)
+   {
+      this.scanPeriod = period;
+   }
+
+   /** Are deployment scans enabled.
+    */
+   public boolean isScanEnabled()
+   {
+      return activeScan != null;
+   }
+
+   public synchronized int getScanCount()
+   {
+      return scanCount;
+   }
+   public synchronized void resetScanCount()
+   {
+      this.scanCount = 0;
+   }
+
+   /**
+    * Enable/disable deployment scans.
+    * @param scanEnabled true to enable scans, false to disable.
+    */
+   public synchronized void setScanEnabled(boolean scanEnabled)
+   {
+      if( scanEnabled == true && activeScan == null )
+      {
+         activeScan = this.scanExecutor.scheduleWithFixedDelay(this, 0,
+               scanPeriod, TimeUnit.MILLISECONDS);
+      }
+      else if( scanEnabled == false && activeScan != null )
+      {
+         activeScan.cancel(true);
+         activeScan = null;
+      }
+   }
+
+    /**
+    * @throws URISyntaxException
+    * @throws IOException
+    */
+   public void setURIs(final String listspec) throws URISyntaxException, IOException
+   {
+      if (listspec == null)
+      {
+         this.uriList.clear();
+         return;
+      }
+      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)
+      {
+         return;
+      }
+
+      // 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);
+   }
+   
+   public List<URI> getURIList()
+   {
+      return new ArrayList<URI>(uriList);
+   }
+
+   public void setRecursiveSearch(boolean recurse)
+   {
+      doRecursiveSearch = recurse;
+   }
+   
+   public boolean getRecursiveSearch()
+   {
+      return doRecursiveSearch;
+   }
+
+   // Operations ----------------------------------------------------
+   
+   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);
+         VirtualFile vf = VFS.getRoot(uri);
+         vdfList.add(vf);
+      }      
+   }
+
+   public void removeURI(final URI uri)
+      throws IOException
+   {
+      if (uri == null)
+      {
+         throw new NullPointerException("uri argument cannot be null");
+      }
+      VirtualFile vf = VFS.getRoot(uri);
+      vdfList.remove(vf);
+      boolean success = uriList.remove(uri);
+      
+      if (success)
+      {
+         log.debug("Removed URI: " + uri);
+      }
+   }
+   
+   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
+      vdfList.clear();
+      for (URI uri : uriList)
+      {
+         VirtualFile vf = VFS.getRoot(uri);
+         vdfList.add(vf);
+      }
+
+      // Default to a single thread executor
+      if( scanExecutor == null )
+      {
+         scanExecutor = Executors.newSingleThreadScheduledExecutor(
+            new ThreadFactory()
+            {
+               public Thread newThread(Runnable r)
+               {
+                  return new Thread(r, "VFSDeploymentScanner");
+               }
+            }
+        );
+      }
+      activeScan = scanExecutor.scheduleWithFixedDelay(this, 0,
+            scanPeriod, TimeUnit.MILLISECONDS);
+   }
+
+   /**
+    * Executes scan 
+    *
+    */
+   public void run()
+   {
+      try
+      {
+         scan();
+      }
+      catch(Throwable e)
+      {
+         log.warn("Scan failed", e);
+      }
+      finally
+      {
+         incScanCount();
+      }
+   }
+
+   public void stop()
+   {
+      if( activeScan != null )
+      {
+         activeScan.cancel(true);
+         activeScan = null;
+      }
+   }
+
+   public synchronized void scan() throws Exception
+   {
+      if (vdfList == null)
+      {
+         throw new IllegalStateException("not initialized");
+      }
+      boolean trace = log.isTraceEnabled();
+
+      // Scan for deployments
+      log.debug("Begin deployment scan");
+
+      // VirtualFiles to deploy
+      List<VirtualFile> toDeployList = new LinkedList<VirtualFile>();
+      synchronized (vdfList)
+      {
+         for (VirtualFile vf : vdfList)
+         {
+            if( trace )
+               log.trace("Checking file: "+vf);
+            if (vf.isLeaf())
+            {
+               // treat this as a deployable unit
+               toDeployList.add(vf);
+            }
+            else
+            {
+               // process (possibly recursively) the dir
+               addDeployments(toDeployList, vf);
+            }
+         }
+      }
+
+      if (trace)
+      {
+         log.trace("toDeployList: "+toDeployList);
+      }
+      LinkedList<VirtualFile> toRemoveList = new LinkedList<VirtualFile>();
+      LinkedList<VirtualFile> toCheckForUpdateList = new LinkedList<VirtualFile>();
+
+      synchronized (deployedMap)
+      {
+         // remove previously deployed URLs no longer needed
+         Iterator<VirtualFile> iter = deployedMap.keySet().iterator();
+         while( iter.hasNext() )
+         {
+            VirtualFile vf = iter.next();
+            if (toDeployList.contains(vf))
+            {
+               toCheckForUpdateList.add(vf);
+            }
+            else
+            {
+               toRemoveList.add(vf);
+            }
+         }
+      }
+
+      // ********
+      // Undeploy
+      // ********
+   
+      for (VirtualFile vf : toRemoveList)
+      {
+         undeploy(vf);
+      }
+
+      // ********
+      // Redeploy
+      // ********
+
+      // compute the DeployedURL list to update
+      ArrayList<VirtualFile> toUpdateList = new ArrayList<VirtualFile>(toCheckForUpdateList.size());
+      for (VirtualFile vf : toUpdateList)
+      {
+         DeploymentContext context = deployedMap.get(vf);
+         long modified = vf.getLastModified();
+         Long prevLastDeployed = context.getTransientAttachments().getAttachment("deployedLastModified", Long.class);
+         if (prevLastDeployed != null && prevLastDeployed.compareTo(modified) < 0)
+         {
+            if (trace)
+            {
+               log.trace("Re-deploying " + vf);
+            }
+            toUpdateList.add(vf);
+         }
+      }
+
+      // 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 vf = (VirtualFile)i.next();
+         
+         // if vf is not deployed already, deploy it
+         if (!deployedMap.containsKey(vf))
+         {
+            deploy(vf);
+         }
+
+         // vf must have been deployed by now, so remove it from list 
+         i.remove();
+         
+      }   
+      log.debug("End deployment scan");
+   }
+
+   /**
+    * Inc the scanCount and to a notifyAll.
+    *
+    */
+   protected synchronized void incScanCount()
+   {
+      scanCount ++;
+      notifyAll();
+   }
+
+   // 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 vf
+    * and add them to the supplied list.
+    * 
+    * We may recurse.
+    */
+   private void addDeployments(List<VirtualFile> list, VirtualFile root)
+      throws IOException
+   {
+      List<VirtualFile> components = root.getChildren();
+      
+      for (VirtualFile vf : components)
+      {
+         if (vf.isLeaf())
+         {
+            // the first arg in filter.accept is not used!
+            if (filter == null || filter.accepts(vf))
+            {
+               list.add(vf);
+            }            
+         }
+         else
+         {
+            if (vf.getName().indexOf('.') == -1 && this.doRecursiveSearch)
+            {
+               // recurse if not '.' in name and recursive search is enabled
+               addDeployments(list, vf);
+            }
+            else
+            {
+               list.add(vf);
+            }
+         }
+      }
+   }
+
+   /**
+    * A helper to deploy the given vf using the deployer.
+    */
+   private void deploy(final VirtualFile vf)
+   {
+      // If the deployer is null simply ignore the request
+      log.debug("Deploying: " + vf);
+      DeploymentContext context = new AbstractDeploymentContext(vf);
+      try
+      {
+         mainDeployer.addDeploymentContext(context);
+         mainDeployer.process();
+      }
+      catch (Exception e)
+      {
+         log.warn("Failed to deploy: " + vf, e);
+         // TODO: somehow need to ignore bad deployments to avoid repeated errors
+         return;
+      }
+
+      /* TODO: this differs from the previous behavior. We would need a type to metainf location
+       if we want to watch the same file as jboss4. But since not all files have a deployment
+       descriptor, we need to be able to watch the deployment root anyway.
+      */
+      try
+      {
+         long deployedLastModified = vf.getLastModified();
+         context.getTransientAttachments().addAttachment("deployedLastModified", deployedLastModified);
+         if (!deployedMap.containsKey(vf))
+         {
+            deployedMap.put(vf, context);
+         }
+      }
+      catch(IOException e)
+      {
+         log.warn("Failed to obtain lastModified for: "+vf, e);
+      }
+   }
+
+   /**
+    * A helper to undeploy the given vf using the deployer.
+    */
+   private void undeploy(final VirtualFile vf)
+   {
+      try
+      {
+         log.debug("Undeploying: " + vf);
+         DeploymentContext deployment = deployedMap.remove(vf);
+         mainDeployer.removeDeploymentContext(deployment.getName());
+      }
+      catch (Exception e)
+      {
+         log.error("Failed to undeploy: " + vf, e);
+      }
+   }
+
+}




More information about the jboss-cvs-commits mailing list