001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.deployment.scanner;
023:
024: import javax.management.ObjectName;
025:
026: import org.jboss.deployment.Deployer;
027: import org.jboss.deployment.MainDeployerMBean;
028: import org.jboss.logging.Logger;
029: import org.jboss.mx.util.MBeanProxyExt;
030: import org.jboss.mx.util.MBeanProxyInstance;
031: import org.jboss.system.MissingAttributeException;
032: import org.jboss.system.ServiceMBeanSupport;
033: import org.jboss.util.NullArgumentException;
034:
035: import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
036:
037: /**
038: * An abstract support class for implementing a deployment scanner.
039: *
040: * <p>Provides the implementation of period-based scanning, as well
041: * as Deployer integration.
042: *
043: * <p>Sub-classes only need to implement {@link DeploymentScanner#scan}.
044: *
045: * @version <tt>$Revision: 57205 $</tt>
046: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
047: * @author Scott.Stark@jboss.org
048: */
049: public abstract class AbstractDeploymentScanner extends
050: ServiceMBeanSupport implements DeploymentScanner,
051: DeploymentScannerMBean {
052: /** The scan period in milliseconds */
053: protected long scanPeriod = 5000;
054:
055: /** True if period based scanning is enabled. */
056: protected boolean scanEnabled = true;
057:
058: /** The stop timeout */
059: protected long stopTimeOut = 60000;
060:
061: /** A proxy to the deployer we are using. */
062: protected Deployer deployer;
063:
064: protected MainDeployerMBean mainDeployer;
065:
066: /** The scanner thread. */
067: protected ScannerThread scannerThread;
068:
069: /** HACK: Shutdown hook to get around problems with system service shutdown ordering. */
070: private Thread shutdownHook;
071:
072: /////////////////////////////////////////////////////////////////////////
073: // DeploymentScanner //
074: /////////////////////////////////////////////////////////////////////////
075:
076: public void setDeployer(final ObjectName deployerName) {
077: if (deployerName == null)
078: throw new NullArgumentException("deployerName");
079:
080: deployer = (Deployer) MBeanProxyExt.create(Deployer.class,
081: deployerName, server);
082: }
083:
084: public ObjectName getDeployer() {
085: return ((MBeanProxyInstance) deployer)
086: .getMBeanProxyObjectName();
087: }
088:
089: /**
090: * Period must be >= 0.
091: */
092: public void setScanPeriod(final long period) {
093: if (period < 0)
094: throw new IllegalArgumentException(
095: "ScanPeriod must be >= 0; have: " + period);
096:
097: this .scanPeriod = period;
098: }
099:
100: public long getScanPeriod() {
101: return scanPeriod;
102: }
103:
104: public void setScanEnabled(final boolean flag) {
105: this .scanEnabled = flag;
106: }
107:
108: public boolean isScanEnabled() {
109: return scanEnabled;
110: }
111:
112: public long getStopTimeOut() {
113: return stopTimeOut;
114: }
115:
116: public void setStopTimeOut(long stopTimeOut) {
117: this .stopTimeOut = stopTimeOut;
118: }
119:
120: /** This is here to work around a bug in the IBM vm that causes an
121: * AbstractMethodError to be thrown when the ScannerThread calls scan.
122: * @throws Exception
123: */
124: public abstract void scan() throws Exception;
125:
126: /////////////////////////////////////////////////////////////////////////
127: // Scanner Thread //
128: /////////////////////////////////////////////////////////////////////////
129:
130: /**
131: * Should use Timer/TimerTask instead? This has some issues with
132: * interaction with ScanEnabled attribute. ScanEnabled works only
133: * when starting/stopping.
134: */
135: public class ScannerThread extends Thread {
136: /** We get our own logger. */
137: protected Logger scannerLog = Logger
138: .getLogger(ScannerThread.class);
139:
140: /** True if the scan loop should run. */
141: protected SynchronizedBoolean enabled = new SynchronizedBoolean(
142: false);
143:
144: /** True if we are shutting down. */
145: protected SynchronizedBoolean shuttingDown = new SynchronizedBoolean(
146: false);
147:
148: /** Lock/notify object. */
149: protected Object lock = new Object();
150:
151: /** Active synchronization. */
152: protected SynchronizedBoolean active = new SynchronizedBoolean(
153: false);
154:
155: public ScannerThread(boolean enabled) {
156: super ("ScannerThread");
157:
158: this .enabled.set(enabled);
159: }
160:
161: public void setEnabled(boolean enabled) {
162: this .enabled.set(enabled);
163:
164: synchronized (lock) {
165: lock.notifyAll();
166: }
167:
168: scannerLog.debug("Notified that enabled: " + enabled);
169: }
170:
171: public void shutdown() {
172: enabled.set(false);
173: shuttingDown.set(true);
174:
175: synchronized (lock) {
176: lock.notifyAll();
177: }
178:
179: scannerLog.debug("Notified to shutdown");
180:
181: // jason: shall we also interrupt this thread?
182: }
183:
184: public void run() {
185: scannerLog.debug("Running");
186:
187: active.set(true);
188: try {
189: while (shuttingDown.get() == false) {
190: // If we are not enabled, then wait
191: if (enabled.get() == false) {
192: synchronized (active) {
193: active.set(false);
194: active.notifyAll();
195: }
196: try {
197: scannerLog
198: .debug("Disabled, waiting for notification");
199: synchronized (lock) {
200: lock.wait();
201: }
202: } catch (InterruptedException ignore) {
203: }
204: active.set(true);
205: }
206:
207: loop();
208: }
209: } finally {
210: synchronized (active) {
211: active.set(false);
212: active.notifyAll();
213: }
214: }
215:
216: scannerLog.debug("Shutdown");
217: }
218:
219: protected void waitForInactive() {
220: boolean interrupted = false;
221: synchronized (active) {
222: try {
223: if (active.get() && stopTimeOut > 0)
224: active.wait(stopTimeOut);
225: } catch (InterruptedException ignored) {
226: interrupted = true;
227: }
228: }
229: if (interrupted)
230: Thread.currentThread().interrupt();
231: }
232:
233: public void doScan() {
234: // Scan for new/removed/changed/whatever
235: try {
236: scan();
237: } catch (Exception e) {
238: scannerLog.error("Scanning failed; continuing", e);
239: }
240: }
241:
242: protected void loop() {
243: while (enabled.get() && shuttingDown.get() == false) {
244: doScan();
245:
246: // Sleep for scan period
247: try {
248: scannerLog.trace("Sleeping...");
249: Thread.sleep(scanPeriod);
250: } catch (InterruptedException ignore) {
251: }
252: }
253: }
254: }
255:
256: /////////////////////////////////////////////////////////////////////////
257: // Service/ServiceMBeanSupport //
258: /////////////////////////////////////////////////////////////////////////
259:
260: protected void createService() throws Exception {
261: if (deployer == null)
262: throw new MissingAttributeException("Deployer");
263: mainDeployer = (MainDeployerMBean) MBeanProxyExt.create(
264: MainDeployerMBean.class, MainDeployerMBean.OBJECT_NAME,
265: server);
266: // setup + start scanner thread
267: scannerThread = new ScannerThread(false);
268: scannerThread.setDaemon(true);
269: scannerThread.start();
270: log.debug("Scanner thread started");
271:
272: // HACK
273: //
274: // install a shutdown hook, as the current system service shutdown
275: // mechanism will not call this until all other services have stopped.
276: // we need to know soon, so we can stop scanning to try to avoid
277: // starting new services when shutting down
278:
279: final ScannerThread _scannerThread = scannerThread;
280: shutdownHook = new Thread("DeploymentScanner Shutdown Hook") {
281: ScannerThread thread = _scannerThread;
282:
283: public void run() {
284: thread.shutdown();
285: }
286: };
287:
288: try {
289: Runtime.getRuntime().addShutdownHook(shutdownHook);
290: } catch (Exception e) {
291: log.warn("Failed to add shutdown hook", e);
292: }
293: }
294:
295: protected void startService() throws Exception {
296: synchronized (scannerThread) {
297: // scan before we enable the thread, so JBoss version shows up afterwards
298: scannerThread.doScan();
299:
300: // enable scanner thread if we are enabled
301: scannerThread.setEnabled(scanEnabled);
302: }
303: }
304:
305: protected void stopService() throws Exception {
306: // disable scanner thread
307: if (scannerThread != null) {
308: scannerThread.setEnabled(false);
309: scannerThread.waitForInactive();
310: }
311: }
312:
313: protected void destroyService() throws Exception {
314: // drop our ref to deployer, so scan will fail
315: deployer = null;
316:
317: // shutdown scanner thread
318: if (scannerThread != null) {
319: synchronized (scannerThread) {
320: scannerThread.shutdown();
321: }
322: }
323:
324: // HACK
325: //
326: // remove the shutdown hook, we don't need it anymore
327: try {
328: Runtime.getRuntime().removeShutdownHook(shutdownHook);
329: } catch (Exception ignore) {
330: } // who cares really
331:
332: // help gc
333: shutdownHook = null;
334: scannerThread = null;
335: }
336: }
|