package org.jboss.deployment.scanner;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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 javax.management.MBeanServer;
import javax.management.ObjectName;
import org.jboss.deployment.IncompleteDeploymentException;
import org.jboss.deployment.DefaultDeploymentSorter;
import org.jboss.mx.util.JMXExceptionDecoder;
import org.jboss.net.protocol.URLLister;
import org.jboss.net.protocol.URLLister.URLFilter;
import org.jboss.net.protocol.URLListerFactory;
import org.jboss.system.server.ServerConfigLocator;
import org.jboss.system.server.ServerConfig;
import org.jboss.util.NullArgumentException;
import org.jboss.util.StringPropertyReplacer;
public class URLDeploymentScanner
extends AbstractDeploymentScanner
implements DeploymentScanner, URLDeploymentScannerMBean
{
protected List urlList = Collections.synchronizedList(new ArrayList());
protected Set deployedSet = Collections.synchronizedSet(new HashSet());
protected File serverHome;
protected URL serverHomeURL;
protected Comparator sorter;
protected URLFilter filter;
protected IncompleteDeploymentException lastIncompleteDeploymentException;
protected boolean doRecursiveSearch = true;
public void setRecursiveSearch (boolean recurse)
{
doRecursiveSearch = recurse;
}
public boolean getRecursiveSearch ()
{
return doRecursiveSearch;
}
public void setURLList(final List list)
{
if (list == null)
throw new NullArgumentException("list");
boolean debug = log.isDebugEnabled();
urlList.clear();
Iterator iter = list.iterator();
while (iter.hasNext())
{
URL url = (URL)iter.next();
if (url == null)
throw new NullArgumentException("list element");
addURL(url);
}
if (debug)
{
log.debug("URL list: " + urlList);
}
}
public void setURLComparator(String classname)
throws ClassNotFoundException, IllegalAccessException,
InstantiationException
{
sorter = (Comparator)Thread.currentThread().getContextClassLoader().loadClass(classname).newInstance();
}
public String getURLComparator()
{
if (sorter == null)
return null;
return sorter.getClass().getName();
}
public void setFilter(String classname)
throws ClassNotFoundException, IllegalAccessException, InstantiationException
{
Class filterClass = Thread.currentThread().getContextClassLoader().loadClass(classname);
filter = (URLFilter) filterClass.newInstance();
}
public String getFilter()
{
if (filter == null)
return null;
return filter.getClass().getName();
}
public void setFilterInstance(URLFilter filter)
{
this.filter = filter;
}
public URLFilter getFilterInstance()
{
return filter;
}
public List getURLList()
{
return new ArrayList(urlList);
}
public void addURL(final URL url)
{
if (url == null)
throw new NullArgumentException("url");
urlList.add(url);
if (log.isDebugEnabled())
{
log.debug("Added url: " + url);
}
}
public void removeURL(final URL url)
{
if (url == null)
throw new NullArgumentException("url");
boolean success = urlList.remove(url);
if (success && log.isDebugEnabled())
{
log.debug("Removed url: " + url);
}
}
public boolean hasURL(final URL url)
{
if (url == null)
throw new NullArgumentException("url");
return urlList.contains(url);
}
public void setURLs(final String listspec) throws MalformedURLException
{
if (listspec == null)
throw new NullArgumentException("listspec");
boolean debug = log.isDebugEnabled();
List list = new LinkedList();
StringTokenizer stok = new StringTokenizer(listspec, ",");
while (stok.hasMoreTokens())
{
String urlspec = stok.nextToken().trim();
if (debug)
{
log.debug("Adding URL from spec: " + urlspec);
}
URL url = makeURL(urlspec);
if (debug)
{
log.debug("URL: " + url);
}
list.add(url);
}
setURLList(list);
}
protected URL makeURL(String urlspec) throws MalformedURLException
{
urlspec = StringPropertyReplacer.replaceProperties (urlspec);
return new URL(serverHomeURL, urlspec);
}
public void addURL(final String urlspec) throws MalformedURLException
{
addURL(makeURL(urlspec));
}
public void removeURL(final String urlspec) throws MalformedURLException
{
removeURL(makeURL(urlspec));
}
public boolean hasURL(final String urlspec) throws MalformedURLException
{
return hasURL(makeURL(urlspec));
}
protected void deploy(final DeployedURL du)
{
if( deployer == null )
return;
if (log.isTraceEnabled())
{
log.trace("Deploying: " + du);
}
try
{
deployer.deploy(du.url);
}
catch (IncompleteDeploymentException e)
{
lastIncompleteDeploymentException = e;
}
catch (Exception e)
{
log.debug("Failed to deploy: " + du, e);
}
du.deployed();
if (!deployedSet.contains(du))
{
deployedSet.add(du);
}
}
protected void undeploy(final DeployedURL du)
{
try
{
if (log.isTraceEnabled())
{
log.trace("Undeploying: " + du);
}
deployer.undeploy(du.url);
deployedSet.remove(du);
}
catch (Exception e)
{
log.error("Failed to undeploy: " + du, e);
}
}
protected boolean isDeployed(final URL url)
{
DeployedURL du = new DeployedURL(url);
return deployedSet.contains(du);
}
public synchronized void scan() throws Exception
{
lastIncompleteDeploymentException = null;
if (urlList == null)
throw new IllegalStateException("not initialized");
updateSorter();
boolean trace = log.isTraceEnabled();
URLListerFactory factory = new URLListerFactory();
List urlsToDeploy = new LinkedList();
if (trace)
{
log.trace("Scanning for new deployments");
}
synchronized (urlList)
{
for (Iterator i = urlList.iterator(); i.hasNext();)
{
URL url = (URL) i.next();
if (url.toString().endsWith("/"))
{
URLLister lister = factory.createURLLister(url);
urlsToDeploy.addAll(lister.listMembers(url, filter, doRecursiveSearch));
}
else
{
urlsToDeploy.add(url);
}
}
}
if (trace)
{
log.trace("Updating existing deployments");
}
LinkedList urlsToRemove = new LinkedList();
LinkedList urlsToCheckForUpdate = new LinkedList();
synchronized (deployedSet)
{
for (Iterator i = deployedSet.iterator(); i.hasNext();)
{
DeployedURL deployedURL = (DeployedURL) i.next();
if (urlsToDeploy.contains(deployedURL.url))
{
urlsToCheckForUpdate.add(deployedURL);
}
else
{
urlsToRemove.add(deployedURL);
}
}
}
for (Iterator i = urlsToRemove.iterator(); i.hasNext();)
{
DeployedURL deployedURL = (DeployedURL) i.next();
if (trace)
{
log.trace("Removing " + deployedURL.url);
}
undeploy(deployedURL);
}
ArrayList urlsToUpdate = new ArrayList(urlsToCheckForUpdate.size());
for (Iterator i = urlsToCheckForUpdate.iterator(); i.hasNext();)
{
DeployedURL deployedURL = (DeployedURL) i.next();
if (deployedURL.isModified())
{
if (trace)
{
log.trace("Re-deploying " + deployedURL.url);
}
urlsToUpdate.add(deployedURL);
}
}
Collections.sort(urlsToUpdate, new Comparator()
{
public int compare(Object o1, Object o2)
{
return sorter.compare(((DeployedURL) o1).url, ((DeployedURL) o2).url);
}
});
for (int i = urlsToUpdate.size() - 1; i >= 0;i--)
{
undeploy((DeployedURL) urlsToUpdate.get(i));
}
for (int i = 0; i < urlsToUpdate.size();i++)
{
deploy((DeployedURL) urlsToUpdate.get(i));
}
Collections.sort(urlsToDeploy, sorter);
for (Iterator i = urlsToDeploy.iterator(); i.hasNext();)
{
URL url = (URL) i.next();
DeployedURL deployedURL = new DeployedURL(url);
if (deployedSet.contains(deployedURL) == false)
{
if (trace)
{
log.trace("Deploying " + deployedURL.url);
}
deploy(deployedURL);
}
i.remove();
if (i.hasNext() && updateSorter())
{
Collections.sort(urlsToDeploy, sorter);
i = urlsToDeploy.iterator();
}
}
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);
}
}
}
protected boolean updateSorter(){ if (sorter instanceof DefaultDeploymentSorter)
{
DefaultDeploymentSorter defaultSorter = (DefaultDeploymentSorter)sorter;
if (defaultSorter.getSuffixOrder() != mainDeployer.getSuffixOrder())
{
defaultSorter.setSuffixOrder(mainDeployer.getSuffixOrder());
return true;
}
}
return false;
}
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception
{
ServerConfig serverConfig = ServerConfigLocator.locate();
serverHome = serverConfig.getServerHomeDir();
serverHomeURL = serverConfig.getServerHomeURL();
return super.preRegister(server, name);
}
protected class DeployedURL
{
public URL url;
public URL watchUrl;
public long deployedLastModified;
public DeployedURL(final URL url)
{
this.url = url;
}
public void deployed()
{
deployedLastModified = getLastModified();
}
public boolean isFile()
{
return url.getProtocol().equals("file");
}
public File getFile()
{
return new File(url.getFile());
}
public boolean isRemoved()
{
if (isFile())
{
File file = getFile();
return !file.exists();
}
return false;
}
public long getLastModified()
{
if (watchUrl == null)
{
try
{
Object o = getServer().invoke(getDeployer(), "getWatchUrl",
new Object[] { url },
new String[] { URL.class.getName() });
watchUrl = o == null ? url : (URL)o;
getLog().debug("Watch URL for: " + url + " -> " + watchUrl);
}
catch (Exception e)
{
watchUrl = url;
getLog().debug("Unable to obtain watchUrl from deployer. Use url: " + url, e);
}
}
try
{
URLConnection connection;
if (watchUrl != null)
{
connection = watchUrl.openConnection();
} else
{
connection = url.openConnection();
}
long lastModified = connection.getLastModified();
return lastModified;
}
catch (java.io.IOException e)
{
log.warn("Failed to check modfication of deployed url: " + url, e);
}
return -1;
}
public boolean isModified()
{
long lastModified = getLastModified();
if (lastModified == -1) {
return false;
}
return deployedLastModified != lastModified;
}
public int hashCode()
{
return url.hashCode();
}
public boolean equals(final Object other)
{
if (other instanceof DeployedURL)
{
return ((DeployedURL)other).url.equals(this.url);
}
return false;
}
public String toString()
{
return super.toString() +
"{ url=" + url +
", deployedLastModified=" + deployedLastModified +
" }";
}
}
}