/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, 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;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jboss.deployers.vfs.spi.client.VFSDeployment;
import org.jboss.deployers.vfs.spi.client.VFSDeploymentFactory;
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.virtual.VFS;
import org.jboss.virtual.VirtualFile;
import org.jboss.virtual.VirtualFileFilter;
/**
* 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:dimitris@jboss.org">Dimitris Andreadis</a>
* @author Scott.Stark@jboss.org
* @author adrian@jboss.org
* @version $Revision: 85526 $
*/
public abstract class VFSScanner extends JBossObject
{
/** The profile service */
private ProfileService profileService;
/** The profile service key */
private ProfileKey profileKey;
/** The URIfied ServerHomeURL */
private URI serverHomeURI;
/** The list of URIs to scan */
private List<URI> uriList = new CopyOnWriteArrayList<URI>();
/** The list of VirtualFiles to scan */
private List<VirtualFile> vdfList = new CopyOnWriteArrayList<VirtualFile>();
/** Allow a filter for scanned directories */
private VirtualFileFilter filter;
/** Whether to search for files inside directories whose names containing no dots */
private boolean doRecursiveSearch = true;
/** A map of deployed virtual files to their names */
private Map<VirtualFile, String> deployedSet = new ConcurrentHashMap<VirtualFile, String>();
/** The deployment factory */
private VFSDeploymentFactory deploymentFactory = VFSDeploymentFactory.getInstance();
/**
* Get the profileKey.
*
* @return the profileKey.
*/
public ProfileKey getProfileKey()
{
return profileKey;
}
/**
* Set the profileKey.
*
* @param profileKey the profileKey.
*/
public void setProfileKey(ProfileKey profileKey)
{
this.profileKey = profileKey;
}
/**
* Get the profileService.
*
* @return the profileService.
*/
public ProfileService getProfileService()
{
return profileService;
}
/**
* Set the profileService.
*
* @param profileService the profileService.
*/
public void setProfileService(ProfileService profileService)
{
this.profileService = profileService;
}
/**
* Set the uris
*
* @param listspec the uris
* @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);
}
/**
* Set the uri list
*
* @param list the 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);
}
/**
* Get the uri list
*
* @return the list
*/
public List<URI> getURIList()
{
return new ArrayList<URI>(uriList);
}
/**
* Set whether to do recursive search
*
* @param recurse true when recurisve
*/
public void setRecursiveSearch(boolean recurse)
{
doRecursiveSearch = recurse;
}
/**
* Get the recursive search
*
* @return true when recursive
*/
public boolean getRecursiveSearch()
{
return doRecursiveSearch;
}
/**
* Set the filter
*
* @param classname the filter class name
* @throws ClassNotFoundException when the class is not found
* @throws IllegalAccessException when the class's default constructor is not public
* @throws InstantiationException when there is an error constructing the class
*/
@SuppressWarnings("unchecked")
public void setFilter(String classname)
throws ClassNotFoundException, IllegalAccessException, InstantiationException
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<VirtualFileFilter> filterClass = (Class<VirtualFileFilter>) loader.loadClass(classname);
filter = filterClass.newInstance();
}
/**
* Get the filer
*
* @return the filter
*/
public String getFilter()
{
if (filter == null)
{
return null;
}
return filter.getClass().getName();
}
/**
* Set the filter instance
*
* @param filter ther filter
*/
public void setFilterInstance(VirtualFileFilter filter)
{
this.filter = filter;
}
/**
* Get the filter instance
*
* @return the filter
*/
public VirtualFileFilter getFilterInstance()
{
return filter;
}
/**
* Add a uri
*
* @param uri the uri
* @throws IOException for an error accessing the uri
*/
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 = getVFforURI(uri);
vdfList.add(vf);
}
}
/**
* Remove a uri
*
* @param uri the uri
* @throws IOException for an error accessing the uri
*/
public void removeURI(final URI uri)
throws IOException
{
if (uri == null)
{
throw new NullPointerException("uri argument cannot be null");
}
VirtualFile vf = getVFforURI(uri);
vdfList.remove(vf);
boolean success = uriList.remove(uri);
if (success)
{
log.debug("Removed URI: " + uri);
}
}
/**
* Whether it has the uri
*
* @param uri the uri
* @return when the uri is configured
*/
public boolean hasURI(final URI uri)
{
if (uri == null)
{
throw new NullPointerException("uri argument cannot be null");
}
return uriList.contains(uri);
}
/**
* Start the scan
*
* @throws Exception for any error
*/
public void start() throws Exception
{
// synchronize uriList and vdfList because only at this point
// setVirtualFileFactory() injection has been performed
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");
}
scan();
}
/**
* Scan
*
* @throws Exception for any error
*/
public synchronized void scan() throws Exception
{
if (vdfList == null)
{
throw new IllegalStateException("not initialized");
}
boolean trace = log.isTraceEnabled();
// 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.isLeaf())
{
// treat this as a deployable unit
toDeployList.add(component);
}
else
{
// 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>();
synchronized (deployedSet)
{
// remove previously deployed URLs no longer needed
for (VirtualFile deployedComponent : deployedSet.keySet())
{
if (toDeployList.contains(deployedComponent) == false)
{
toRemoveList.add(deployedComponent);
}
}
}
// ********
// Undeploy
// ********
for (Iterator i = toRemoveList.iterator(); i.hasNext();)
{
VirtualFile deployedComponent = (VirtualFile)i.next();
undeploy(deployedComponent);
}
// ******
// Deploy
// ******
for (Iterator i = toDeployList.iterator(); i.hasNext();)
{
VirtualFile component = (VirtualFile)i.next();
// if component is not deployed already, deploy it
if (!deployedSet.containsKey(component))
{
deploy(component);
}
// component must have been deployed by now, so remove it from list
i.remove();
}
}
/**
* Make a uri
*
* @param urispec the uri spec
* @return the uri
* @throws URISyntaxException for an error parsing he uri
*/
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.
*
* @param list the list of virtual files
* @param root the root file
* @throws IOException for any error
*/
private void addDeployments(List<VirtualFile> list, VirtualFile root)
throws IOException
{
List<VirtualFile> components = root.getChildren();
for (VirtualFile component : components)
{
// Filter the component regardless of its type
if( filter != null && filter.accepts(component) == false)
continue;
if (component.isLeaf())
{
list.add(component);
}
// TODO replace . in the name with isArchive() == false?
else 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.
*
* @param component the virtual file
*/
private void deploy(final VirtualFile component)
{
// If the deployer is null simply ignore the request
if (profileService == null)
{
return;
}
if (log.isTraceEnabled())
{
log.trace("Deploying: " + component);
}
VFSDeployment deployment = null;
try
{
Profile profile = profileService.getProfile(profileKey);
deployment = add(profile, component);
}
catch (Exception e)
{
log.debug("Failed to deploy: " + component, e);
}
if (deployment != null && !deployedSet.containsKey(component))
{
deployedSet.put(component, deployment.getName());
}
}
/**
* A helper to undeploy the given component using the deployer.
*
* @param component the component
*/
private void undeploy(final VirtualFile component)
{
try
{
if (log.isTraceEnabled())
{
log.trace("Undeploying: " + component);
}
String name = deployedSet.remove(component);
Profile profile = profileService.getProfile(profileKey);
remove(profile, name);
}
catch (Exception e)
{
log.error("Failed to undeploy: " + component, e);
}
}
/**
* Remove the component
*
* @param profile the profile
* @param file the virtual file
* @return the deployment context or null if not added, e.g. it already exists
* @throws Exception for any error
*/
protected abstract VFSDeployment add(Profile profile, VirtualFile file) throws Exception;
/**
* Remove the component
*
* @param profile the profile
* @param name the name
* @throws Exception for any error
*/
protected abstract void remove(Profile profile, String name) throws Exception;
/**
* Create a deployment
*
* @param file the root file
* @return the deployment
*/
protected VFSDeployment createDeployment(VirtualFile file)
{
if (file == null)
throw new IllegalArgumentException("Null file");
return deploymentFactory.createVFSDeployment(file);
}
/**
* Get virtual file for uri.
*
* @param uri the uri
* @return vritual file representing uri
* @throws IOException for any error
*/
private VirtualFile getVFforURI(URI uri) throws IOException
{
return VFS.getRoot(uri);
}
}