1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.deployment.scanner;
23
24 import javax.management.ObjectName;
25
26 import org.jboss.deployment.Deployer;
27 import org.jboss.deployment.MainDeployerMBean;
28 import org.jboss.logging.Logger;
29 import org.jboss.mx.util.MBeanProxyExt;
30 import org.jboss.mx.util.MBeanProxyInstance;
31 import org.jboss.system.MissingAttributeException;
32 import org.jboss.system.ServiceMBeanSupport;
33 import org.jboss.util.NullArgumentException;
34
35 import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
36
37 /**
38 * An abstract support class for implementing a deployment scanner.
39 *
40 * <p>Provides the implementation of period-based scanning, as well
41 * as Deployer integration.
42 *
43 * <p>Sub-classes only need to implement {@link DeploymentScanner#scan}.
44 *
45 * @version <tt>$Revision: 57108 $</tt>
46 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
47 * @author Scott.Stark@jboss.org
48 */
49 public abstract class AbstractDeploymentScanner extends ServiceMBeanSupport
50 implements DeploymentScanner, DeploymentScannerMBean
51 {
52 /** The scan period in milliseconds */
53 protected long scanPeriod = 5000;
54
55 /** True if period based scanning is enabled. */
56 protected boolean scanEnabled = true;
57
58 /** The stop timeout */
59 protected long stopTimeOut = 60000;
60
61 /** A proxy to the deployer we are using. */
62 protected Deployer deployer;
63
64 protected MainDeployerMBean mainDeployer;
65
66 /** The scanner thread. */
67 protected ScannerThread scannerThread;
68
69 /** HACK: Shutdown hook to get around problems with system service shutdown ordering. */
70 private Thread shutdownHook;
71
72
73 /////////////////////////////////////////////////////////////////////////
74 // DeploymentScanner //
75 /////////////////////////////////////////////////////////////////////////
76
77 public void setDeployer(final ObjectName deployerName)
78 {
79 if (deployerName == null)
80 throw new NullArgumentException("deployerName");
81
82 deployer = (Deployer)
83 MBeanProxyExt.create(Deployer.class, deployerName, server);
84 }
85
86 public ObjectName getDeployer()
87 {
88 return ((MBeanProxyInstance)deployer).getMBeanProxyObjectName();
89 }
90
91 /**
92 * Period must be >= 0.
93 */
94 public void setScanPeriod(final long period)
95 {
96 if (period < 0)
97 throw new IllegalArgumentException("ScanPeriod must be >= 0; have: " + period);
98
99 this.scanPeriod = period;
100 }
101
102 public long getScanPeriod()
103 {
104 return scanPeriod;
105 }
106
107 public void setScanEnabled(final boolean flag)
108 {
109 this.scanEnabled = flag;
110 }
111
112 public boolean isScanEnabled()
113 {
114 return scanEnabled;
115 }
116
117 public long getStopTimeOut()
118 {
119 return stopTimeOut;
120 }
121
122 public void setStopTimeOut(long stopTimeOut)
123 {
124 this.stopTimeOut = stopTimeOut;
125 }
126
127 /** This is here to work around a bug in the IBM vm that causes an
128 * AbstractMethodError to be thrown when the ScannerThread calls scan.
129 * @throws Exception
130 */
131 public abstract void scan() throws Exception;
132
133 /////////////////////////////////////////////////////////////////////////
134 // Scanner Thread //
135 /////////////////////////////////////////////////////////////////////////
136
137 /**
138 * Should use Timer/TimerTask instead? This has some issues with
139 * interaction with ScanEnabled attribute. ScanEnabled works only
140 * when starting/stopping.
141 */
142 public class ScannerThread
143 extends Thread
144 {
145 /** We get our own logger. */
146 protected Logger scannerLog = Logger.getLogger(ScannerThread.class);
147
148 /** True if the scan loop should run. */
149 protected SynchronizedBoolean enabled = new SynchronizedBoolean(false);
150
151 /** True if we are shutting down. */
152 protected SynchronizedBoolean shuttingDown = new SynchronizedBoolean(false);
153
154 /** Lock/notify object. */
155 protected Object lock = new Object();
156
157 /** Active synchronization. */
158 protected SynchronizedBoolean active = new SynchronizedBoolean(false);
159
160 public ScannerThread(boolean enabled)
161 {
162 super("ScannerThread");
163
164 this.enabled.set(enabled);
165 }
166
167 public void setEnabled(boolean enabled)
168 {
169 this.enabled.set(enabled);
170
171 synchronized (lock)
172 {
173 lock.notifyAll();
174 }
175
176 scannerLog.debug("Notified that enabled: " + enabled);
177 }
178
179 public void shutdown()
180 {
181 enabled.set(false);
182 shuttingDown.set(true);
183
184 synchronized (lock)
185 {
186 lock.notifyAll();
187 }
188
189 scannerLog.debug("Notified to shutdown");
190
191 // jason: shall we also interrupt this thread?
192 }
193
194 public void run()
195 {
196 scannerLog.debug("Running");
197
198 active.set(true);
199 try
200 {
201 while (shuttingDown.get() == false)
202 {
203 // If we are not enabled, then wait
204 if (enabled.get() == false)
205 {
206 synchronized (active)
207 {
208 active.set(false);
209 active.notifyAll();
210 }
211 try
212 {
213 scannerLog.debug("Disabled, waiting for notification");
214 synchronized (lock)
215 {
216 lock.wait();
217 }
218 }
219 catch (InterruptedException ignore)
220 {
221 }
222 active.set(true);
223 }
224
225 loop();
226 }
227 }
228 finally
229 {
230 synchronized (active)
231 {
232 active.set(false);
233 active.notifyAll();
234 }
235 }
236
237 scannerLog.debug("Shutdown");
238 }
239
240 protected void waitForInactive()
241 {
242 boolean interrupted = false;
243 synchronized (active)
244 {
245 try
246 {
247 if (active.get() && stopTimeOut > 0)
248 active.wait(stopTimeOut);
249 }
250 catch (InterruptedException ignored)
251 {
252 interrupted = true;
253 }
254 }
255 if (interrupted)
256 Thread.currentThread().interrupt();
257 }
258
259 public void doScan()
260 {
261 // Scan for new/removed/changed/whatever
262 try {
263 scan();
264 }
265 catch (Exception e) {
266 scannerLog.error("Scanning failed; continuing", e);
267 }
268 }
269
270 protected void loop()
271 {
272 while (enabled.get() && shuttingDown.get() == false)
273 {
274 doScan();
275
276 // Sleep for scan period
277 try
278 {
279 scannerLog.trace("Sleeping...");
280 Thread.sleep(scanPeriod);
281 }
282 catch (InterruptedException ignore) {}
283 }
284 }
285 }
286
287
288 /////////////////////////////////////////////////////////////////////////
289 // Service/ServiceMBeanSupport //
290 /////////////////////////////////////////////////////////////////////////
291
292 protected void createService() throws Exception
293 {
294 if (deployer == null)
295 throw new MissingAttributeException("Deployer");
296 mainDeployer = (MainDeployerMBean)MBeanProxyExt.create(MainDeployerMBean.class, MainDeployerMBean.OBJECT_NAME, server);
297 // setup + start scanner thread
298 scannerThread = new ScannerThread(false);
299 scannerThread.setDaemon(true);
300 scannerThread.start();
301 log.debug("Scanner thread started");
302
303 // HACK
304 //
305 // install a shutdown hook, as the current system service shutdown
306 // mechanism will not call this until all other services have stopped.
307 // we need to know soon, so we can stop scanning to try to avoid
308 // starting new services when shutting down
309
310 final ScannerThread _scannerThread = scannerThread;
311 shutdownHook = new Thread("DeploymentScanner Shutdown Hook")
312 {
313 ScannerThread thread = _scannerThread;
314
315 public void run()
316 {
317 thread.shutdown();
318 }
319 };
320
321 try
322 {
323 Runtime.getRuntime().addShutdownHook(shutdownHook);
324 }
325 catch (Exception e)
326 {
327 log.warn("Failed to add shutdown hook", e);
328 }
329 }
330
331 protected void startService() throws Exception
332 {
333 synchronized( scannerThread )
334 {
335 // scan before we enable the thread, so JBoss version shows up afterwards
336 scannerThread.doScan();
337
338 // enable scanner thread if we are enabled
339 scannerThread.setEnabled(scanEnabled);
340 }
341 }
342
343 protected void stopService() throws Exception
344 {
345 // disable scanner thread
346 if( scannerThread != null )
347 {
348 scannerThread.setEnabled(false);
349 scannerThread.waitForInactive();
350 }
351 }
352
353 protected void destroyService() throws Exception
354 {
355 // drop our ref to deployer, so scan will fail
356 deployer = null;
357
358 // shutdown scanner thread
359 if( scannerThread != null )
360 {
361 synchronized( scannerThread )
362 {
363 scannerThread.shutdown();
364 }
365 }
366
367 // HACK
368 //
369 // remove the shutdown hook, we don't need it anymore
370 try
371 {
372 Runtime.getRuntime().removeShutdownHook(shutdownHook);
373 }
374 catch (Exception ignore)
375 {
376 } // who cares really
377
378 // help gc
379 shutdownHook = null;
380 scannerThread = null;
381 }
382 }