|
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;
}
}
}
|