/* * 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.deployment.scanner; import javax.management.ObjectName; import org.jboss.deployment.Deployer; import org.jboss.deployment.MainDeployerMBean; import org.jboss.logging.Logger; import org.jboss.mx.util.MBeanProxyExt; import org.jboss.mx.util.MBeanProxyInstance; import org.jboss.system.MissingAttributeException; import org.jboss.system.ServiceMBeanSupport; import org.jboss.util.NullArgumentException; import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean; /** * An abstract support class for implementing a deployment scanner. * * <p>Provides the implementation of period-based scanning, as well * as Deployer integration. * * <p>Sub-classes only need to implement {@link DeploymentScanner#scan}. * * @version <tt>$Revision: 81033 $</tt> * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> * @author Scott.Stark@jboss.org */ public abstract class AbstractDeploymentScanner extends ServiceMBeanSupport implements DeploymentScanner, DeploymentScannerMBean { /** The scan period in milliseconds */ protected long scanPeriod = 5000; /** True if period based scanning is enabled. */ protected boolean scanEnabled = true; /** The stop timeout */ protected long stopTimeOut = 60000; /** A proxy to the deployer we are using. */ protected Deployer deployer; protected MainDeployerMBean mainDeployer; /** The scanner thread. */ protected ScannerThread scannerThread; /** HACK: Shutdown hook to get around problems with system service shutdown ordering. */ private Thread shutdownHook; ///////////////////////////////////////////////////////////////////////// // DeploymentScanner // ///////////////////////////////////////////////////////////////////////// public void setDeployer(final ObjectName deployerName) { if (deployerName == null) throw new NullArgumentException("deployerName"); deployer = (Deployer) MBeanProxyExt.create(Deployer.class, deployerName, server); } public ObjectName getDeployer() { return ((MBeanProxyInstance)deployer).getMBeanProxyObjectName(); } /** * Period must be >= 0. */ public void setScanPeriod(final long period) { if (period < 0) throw new IllegalArgumentException("ScanPeriod must be >= 0; have: " + period); this.scanPeriod = period; } public long getScanPeriod() { return scanPeriod; } public void setScanEnabled(final boolean flag) { this.scanEnabled = flag; } public boolean isScanEnabled() { return scanEnabled; } public long getStopTimeOut() { return stopTimeOut; } public void setStopTimeOut(long stopTimeOut) { this.stopTimeOut = stopTimeOut; } /** This is here to work around a bug in the IBM vm that causes an * AbstractMethodError to be thrown when the ScannerThread calls scan. * @throws Exception */ public abstract void scan() throws Exception; ///////////////////////////////////////////////////////////////////////// // Scanner Thread // ///////////////////////////////////////////////////////////////////////// /** * Should use Timer/TimerTask instead? This has some issues with * interaction with ScanEnabled attribute. ScanEnabled works only * when starting/stopping. */ public class ScannerThread extends Thread { /** We get our own logger. */ protected Logger scannerLog = Logger.getLogger(ScannerThread.class); /** True if the scan loop should run. */ protected SynchronizedBoolean enabled = new SynchronizedBoolean(false); /** True if we are shutting down. */ protected SynchronizedBoolean shuttingDown = new SynchronizedBoolean(false); /** Lock/notify object. */ protected Object lock = new Object(); /** Active synchronization. */ protected SynchronizedBoolean active = new SynchronizedBoolean(false); public ScannerThread(boolean enabled) { super("ScannerThread"); this.enabled.set(enabled); } public void setEnabled(boolean enabled) { this.enabled.set(enabled); synchronized (lock) { lock.notifyAll(); } scannerLog.debug("Notified that enabled: " + enabled); } public void shutdown() { enabled.set(false); shuttingDown.set(true); synchronized (lock) { lock.notifyAll(); } scannerLog.debug("Notified to shutdown"); // jason: shall we also interrupt this thread? } public void run() { scannerLog.debug("Running"); active.set(true); try { while (shuttingDown.get() == false) { // If we are not enabled, then wait if (enabled.get() == false) { synchronized (active) { active.set(false); active.notifyAll(); } try { scannerLog.debug("Disabled, waiting for notification"); synchronized (lock) { lock.wait(); } } catch (InterruptedException ignore) { } active.set(true); } loop(); } } finally { synchronized (active) { active.set(false); active.notifyAll(); } } scannerLog.debug("Shutdown"); } protected void waitForInactive() { boolean interrupted = false; synchronized (active) { try { if (active.get() && stopTimeOut > 0) active.wait(stopTimeOut); } catch (InterruptedException ignored) { interrupted = true; } } if (interrupted) Thread.currentThread().interrupt(); } public void doScan() { // Scan for new/removed/changed/whatever try { scan(); } catch (Exception e) { scannerLog.error("Scanning failed; continuing", e); } } protected void loop() { while (enabled.get() && shuttingDown.get() == false) { doScan(); // Sleep for scan period try { scannerLog.trace("Sleeping..."); Thread.sleep(scanPeriod); } catch (InterruptedException ignore) {} } } } ///////////////////////////////////////////////////////////////////////// // Service/ServiceMBeanSupport // ///////////////////////////////////////////////////////////////////////// protected void createService() throws Exception { if (deployer == null) throw new MissingAttributeException("Deployer"); mainDeployer = (MainDeployerMBean)MBeanProxyExt.create(MainDeployerMBean.class, MainDeployerMBean.OBJECT_NAME, server); // setup + start scanner thread scannerThread = new ScannerThread(false); scannerThread.setDaemon(true); scannerThread.start(); log.debug("Scanner thread started"); // HACK // // install a shutdown hook, as the current system service shutdown // mechanism will not call this until all other services have stopped. // we need to know soon, so we can stop scanning to try to avoid // starting new services when shutting down final ScannerThread _scannerThread = scannerThread; shutdownHook = new Thread("DeploymentScanner Shutdown Hook") { ScannerThread thread = _scannerThread; public void run() { thread.shutdown(); } }; try { Runtime.getRuntime().addShutdownHook(shutdownHook); } catch (Exception e) { log.warn("Failed to add shutdown hook", e); } } protected void startService() throws Exception { synchronized( scannerThread ) { // scan before we enable the thread, so JBoss version shows up afterwards scannerThread.doScan(); // enable scanner thread if we are enabled scannerThread.setEnabled(scanEnabled); } } protected void stopService() throws Exception { // disable scanner thread if( scannerThread != null ) { scannerThread.setEnabled(false); scannerThread.waitForInactive(); } } protected void destroyService() throws Exception { // drop our ref to deployer, so scan will fail deployer = null; // shutdown scanner thread if( scannerThread != null ) { synchronized( scannerThread ) { scannerThread.shutdown(); } } // HACK // // remove the shutdown hook, we don't need it anymore try { Runtime.getRuntime().removeShutdownHook(shutdownHook); } catch (Exception ignore) { } // who cares really // help gc shutdownHook = null; scannerThread = null; } }