001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.server.deploy;
031:
032: import com.caucho.config.ConfigException;
033: import com.caucho.config.types.Period;
034: import com.caucho.lifecycle.Lifecycle;
035: import com.caucho.lifecycle.LifecycleListener;
036: import com.caucho.loader.DynamicClassLoader;
037: import com.caucho.util.Alarm;
038: import com.caucho.util.AlarmListener;
039: import com.caucho.util.L10N;
040: import com.caucho.util.WeakAlarm;
041: import com.caucho.vfs.Dependency;
042:
043: import java.io.IOException;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: /**
048: * DeployController controls the lifecycle of the DeployInstance.
049: */
050: abstract public class DeployController<I extends DeployInstance>
051: implements Dependency, AlarmListener {
052: private static final Logger log = Logger
053: .getLogger(DeployController.class.getName());
054: private static final L10N L = new L10N(DeployController.class);
055:
056: public static final String STARTUP_DEFAULT = "default";
057: public static final String STARTUP_AUTOMATIC = "automatic";
058: public static final String STARTUP_LAZY = "lazy";
059: public static final String STARTUP_MANUAL = "manual";
060:
061: public static final String REDEPLOY_DEFAULT = "default";
062: public static final String REDEPLOY_AUTOMATIC = "automatic";
063: public static final String REDEPLOY_LAZY = "lazy";
064: public static final String REDEPLOY_MANUAL = "manual";
065:
066: public static final long REDEPLOY_CHECK_INTERVAL = 60000;
067:
068: private ClassLoader _parentLoader;
069:
070: private String _id;
071:
072: private String _startupMode = STARTUP_DEFAULT;
073: private String _redeployMode = REDEPLOY_DEFAULT;
074:
075: private int _startupPriority = Integer.MAX_VALUE;
076:
077: private DeployControllerStrategy _strategy;
078:
079: protected final Lifecycle _lifecycle;
080:
081: private Alarm _alarm = new WeakAlarm(this );
082: private long _redeployCheckInterval = REDEPLOY_CHECK_INTERVAL;
083:
084: private long _startTime;
085: private I _deployInstance;
086:
087: protected DeployController(String id) {
088: this (id, null);
089: }
090:
091: protected DeployController(String id, ClassLoader parentLoader) {
092: _id = id;
093:
094: if (parentLoader == null)
095: parentLoader = Thread.currentThread()
096: .getContextClassLoader();
097:
098: _parentLoader = parentLoader;
099:
100: _lifecycle = new Lifecycle(getLog(), toString(), Level.FINEST);
101: }
102:
103: public void addLifecycleListener(LifecycleListener listener) {
104: _lifecycle.addListener(listener);
105: }
106:
107: /**
108: * Returns the controller's id.
109: */
110: public final String getId() {
111: return _id;
112: }
113:
114: /**
115: * Returns the parent class loader.
116: */
117: public ClassLoader getParentClassLoader() {
118: return _parentLoader;
119: }
120:
121: /**
122: * Sets the startup mode.
123: */
124: public void setStartupMode(String mode) {
125: try {
126: _startupMode = toStartupCode(mode);
127: } catch (Exception e) {
128: throw new RuntimeException(e);
129: }
130: }
131:
132: /**
133: * Sets the startup priority.
134: */
135: public void setStartupPriority(int priority) {
136: _startupPriority = priority;
137: }
138:
139: /**
140: * Gets the startup priority.
141: */
142: public int getStartupPriority() {
143: return _startupPriority;
144: }
145:
146: /**
147: * Merges with the old controller.
148: */
149: protected void mergeController(DeployController oldController) {
150: _parentLoader = oldController._parentLoader = _parentLoader;
151:
152: if (oldController._startupPriority < _startupPriority)
153: _startupPriority = oldController._startupPriority;
154: }
155:
156: /**
157: * Merge the startup mode.
158: */
159: public void mergeStartupMode(String mode) {
160: if (mode == null || STARTUP_DEFAULT.equals(mode))
161: return;
162:
163: _startupMode = mode;
164: }
165:
166: /**
167: * Returns the startup mode.
168: */
169: public String getStartupMode() {
170: return _startupMode;
171: }
172:
173: /**
174: * Converts startup mode to code.
175: */
176: public static String toStartupCode(String mode)
177: throws ConfigException {
178: if ("automatic".equals(mode))
179: return STARTUP_AUTOMATIC;
180: else if ("lazy".equals(mode))
181: return STARTUP_LAZY;
182: else if ("manual".equals(mode))
183: return STARTUP_MANUAL;
184: else {
185: throw new ConfigException(
186: L
187: .l(
188: "'{0}' is an unknown startup-mode. 'automatic', 'lazy', and 'manual' are the acceptable values.",
189: mode));
190: }
191: }
192:
193: /**
194: * Sets the redeploy mode.
195: */
196: public void setRedeployMode(String mode) {
197: try {
198: _redeployMode = toRedeployCode(mode);
199: } catch (Exception e) {
200: throw new RuntimeException(e);
201: }
202: }
203:
204: /**
205: * Merge the redeploy mode.
206: */
207: public void mergeRedeployMode(String mode) {
208: if (mode == null || REDEPLOY_DEFAULT.equals(mode))
209: return;
210:
211: _redeployMode = mode;
212: }
213:
214: /**
215: * Returns the redeploy mode.
216: */
217: public String getRedeployMode() {
218: return _redeployMode;
219: }
220:
221: /**
222: * Converts redeploy mode to code.
223: */
224: public static String toRedeployCode(String mode)
225: throws ConfigException {
226: if ("automatic".equals(mode))
227: return REDEPLOY_AUTOMATIC;
228: else if ("lazy".equals(mode))
229: return REDEPLOY_LAZY;
230: else if ("manual".equals(mode))
231: return REDEPLOY_MANUAL;
232: else
233: throw new ConfigException(
234: L
235: .l(
236: "'{0}' is an unknown redeploy-mode. 'automatic', 'lazy', and 'manual' are the acceptable values.",
237: mode));
238: }
239:
240: /**
241: * Sets the redeploy-check-interval
242: */
243: public void mergeRedeployCheckInterval(long interval) {
244: if (interval != REDEPLOY_CHECK_INTERVAL)
245: _redeployCheckInterval = interval;
246: }
247:
248: /**
249: * Sets the redeploy-check-interval
250: */
251: public void setRedeployCheckInterval(Period period) {
252: _redeployCheckInterval = period.getPeriod();
253:
254: if (_redeployCheckInterval < 0)
255: _redeployCheckInterval = Period.INFINITE;
256:
257: if (_redeployCheckInterval < 5000)
258: _redeployCheckInterval = 5000;
259: }
260:
261: /**
262: * Gets the redeploy-check-interval
263: */
264: public long getRedeployCheckInterval() {
265: return _redeployCheckInterval;
266: }
267:
268: /**
269: * Returns true if the entry matches.
270: */
271: public boolean isNameMatch(String name) {
272: return getId().equals(name);
273: }
274:
275: /**
276: * Returns the start time of the entry.
277: */
278: public long getStartTime() {
279: return _startTime;
280: }
281:
282: /**
283: * Returns the deploy admin.
284: */
285: protected DeployControllerAdmin getDeployAdmin() {
286: return null;
287: }
288:
289: /**
290: * Initialize the entry.
291: */
292: public final boolean init() {
293: if (!_lifecycle.toInitializing())
294: return false;
295:
296: Thread thread = Thread.currentThread();
297: ClassLoader oldLoader = thread.getContextClassLoader();
298:
299: try {
300: thread.setContextClassLoader(getParentClassLoader());
301:
302: initBegin();
303:
304: if (_startupMode == STARTUP_MANUAL) {
305: if (_redeployMode == REDEPLOY_AUTOMATIC) {
306: throw new IllegalStateException(
307: L
308: .l("startup='manual' and redeploy='automatic' is an unsupported combination."));
309: } else
310: _strategy = StartManualRedeployManualStrategy
311: .create();
312: } else if (_startupMode == STARTUP_LAZY) {
313: if (_redeployMode == REDEPLOY_MANUAL)
314: _strategy = StartLazyRedeployManualStrategy
315: .create();
316: else
317: _strategy = StartLazyRedeployAutomaticStrategy
318: .create();
319: } else {
320: if (_redeployMode == STARTUP_MANUAL)
321: _strategy = StartAutoRedeployManualStrategy
322: .create();
323: else
324: _strategy = StartAutoRedeployAutoStrategy.create();
325: }
326:
327: initEnd();
328:
329: return _lifecycle.toInit();
330: } finally {
331: thread.setContextClassLoader(oldLoader);
332: }
333: }
334:
335: /**
336: * Initial calls for init.
337: */
338: protected void initBegin() {
339: }
340:
341: /**
342: * Final calls for init.
343: */
344: protected void initEnd() {
345: }
346:
347: protected String getMBeanTypeName() {
348: String className = getDeployInstance().getClass().getName();
349: int p = className.lastIndexOf('.');
350: if (p > 0)
351: className = className.substring(p + 1);
352:
353: return className;
354: }
355:
356: protected String getMBeanId() {
357: String name = getId();
358: if (name == null || name.equals(""))
359: name = "default";
360:
361: return name;
362: }
363:
364: /**
365: * Returns the state name.
366: */
367: public String getState() {
368: if (isDestroyed())
369: return "destroyed";
370: else if (isStoppedLazy())
371: return "stopped-lazy";
372: else if (isStopped())
373: return "stopped";
374: else if (isError())
375: return "error";
376: else if (isModified())
377: return "active-modified";
378: else
379: return "active";
380: }
381:
382: /**
383: * Returns true if the instance is in the active state.
384: */
385: public boolean isActive() {
386: return _lifecycle.isActive();
387: }
388:
389: /**
390: * Returns true if the instance is in the stopped state.
391: *
392: * @return true on stopped state
393: */
394: public boolean isStopped() {
395: return _lifecycle.isStopped() || _lifecycle.isInit();
396: }
397:
398: /**
399: * Returns true for the stop-lazy state
400: */
401: public boolean isStoppedLazy() {
402: return _lifecycle.isInit();
403: }
404:
405: /**
406: * Returns true if the instance has been idle for longer than its timeout.
407: *
408: * @return true if idle
409: */
410: public boolean isActiveIdle() {
411: DeployInstance instance = getDeployInstance();
412:
413: if (!_lifecycle.isActive())
414: return false;
415: else if (instance == null)
416: return false;
417: else
418: return instance.isDeployIdle();
419: }
420:
421: /**
422: * Return true if the instance is in the error state.
423: *
424: * @return true for the error state.
425: */
426: public boolean isError() {
427: if (_lifecycle.isError())
428: return true;
429:
430: DeployInstance instance = getDeployInstance();
431:
432: return (instance != null && instance.getConfigException() != null);
433: }
434:
435: /**
436: * Returns true if there's currently an error.
437: */
438: public boolean isErrorNow() {
439: if (_lifecycle.isError())
440: return true;
441:
442: DeployInstance instance = getDeployInstance();
443:
444: return (instance != null && instance.getConfigException() != null);
445: }
446:
447: /**
448: * Returns true if the entry is modified.
449: */
450: public boolean isModified() {
451: DeployInstance instance = getDeployInstance();
452:
453: return instance == null || instance.isModified();
454: }
455:
456: /**
457: * Log the reason for modification
458: */
459: public boolean logModified(Logger log) {
460: DeployInstance instance = getDeployInstance();
461:
462: if (instance != null) {
463: Thread thread = Thread.currentThread();
464: ClassLoader loader = thread.getContextClassLoader();
465:
466: try {
467: thread.setContextClassLoader(instance.getClassLoader());
468:
469: return instance.logModified(log);
470: } finally {
471: thread.setContextClassLoader(loader);
472: }
473: } else
474: return false;
475: }
476:
477: /**
478: * Returns true if the entry is modified.
479: */
480: public boolean isModifiedNow() {
481: DeployInstance instance = getDeployInstance();
482:
483: return instance == null || instance.isModifiedNow();
484: }
485:
486: /**
487: * Returns the current instance.
488: */
489: public final I getDeployInstance() {
490: synchronized (this ) {
491: if (_deployInstance == null) {
492: Thread thread = Thread.currentThread();
493: ClassLoader oldLoader = thread.getContextClassLoader();
494:
495: try {
496: thread.setContextClassLoader(_parentLoader);
497:
498: _deployInstance = instantiateDeployInstance();
499: } finally {
500: thread.setContextClassLoader(oldLoader);
501: }
502: }
503:
504: return _deployInstance;
505: }
506: }
507:
508: /**
509: * Redeploys the entry if it's modified.
510: */
511: public void startOnInit() {
512: if (!_lifecycle.isAfterInit())
513: throw new IllegalStateException(L.l(
514: "startOnInit must be called after init (in '{0}')",
515: _lifecycle.getStateName()));
516:
517: _strategy.startOnInit(this );
518: }
519:
520: /**
521: * Force an instance start from an admin command.
522: */
523: public final void start() {
524: _strategy.start(this );
525: }
526:
527: public Throwable getConfigException() {
528: return null;
529: }
530:
531: /**
532: * Stops the controller from an admin command.
533: */
534: public final void stop() {
535: _strategy.stop(this );
536: }
537:
538: /**
539: * Force an instance restart from an admin command.
540: */
541: public final void restart() {
542: _strategy.stop(this );
543: _strategy.start(this );
544: }
545:
546: /**
547: * Update the controller from an admin command.
548: */
549: public final void update() {
550: _strategy.update(this );
551: }
552:
553: /**
554: * Returns the instance for a top-level request
555: * @return the request object or null for none.
556: */
557: public I request() {
558: if (_lifecycle.isDestroyed())
559: return null;
560: else if (_strategy != null)
561: return _strategy.request(this );
562: else
563: return null;
564: }
565:
566: /**
567: * Returns the instance for a subrequest.
568: *
569: * @return the request object or null for none.
570: */
571: public I subrequest() {
572: if (_lifecycle.isDestroyed())
573: return null;
574: else if (_strategy != null)
575: return _strategy.subrequest(this );
576: else
577: return null;
578: }
579:
580: /**
581: * Restarts the instance
582: *
583: * @return the new instance
584: */
585: I restartImpl() {
586: if (!_lifecycle.isStopped() && !_lifecycle.isInit())
587: stopImpl();
588:
589: return startImpl();
590: }
591:
592: /**
593: * Starts the entry.
594: */
595: protected I startImpl() {
596: assert (_lifecycle.isAfterInit());
597:
598: if (DynamicClassLoader.isModified(_parentLoader)) {
599: _deployInstance = null;
600: return null;
601: }
602:
603: I deployInstance = getDeployInstance();
604:
605: Thread thread = Thread.currentThread();
606: ClassLoader oldLoader = thread.getContextClassLoader();
607: ClassLoader loader = null;
608: boolean isStarting = false;
609:
610: try {
611: loader = deployInstance.getClassLoader();
612: thread.setContextClassLoader(loader);
613:
614: isStarting = _lifecycle.toStarting();
615:
616: if (!isStarting)
617: return deployInstance;
618:
619: expandArchive();
620:
621: addManifestClassPath();
622:
623: configureInstance(deployInstance);
624:
625: deployInstance.start();
626:
627: _startTime = Alarm.getCurrentTime();
628: } catch (ConfigException e) {
629: _lifecycle.toError();
630:
631: if (deployInstance != null)
632: deployInstance.setConfigException(e);
633: else {
634: log.severe(e.toString());
635: log.log(Level.FINEST, e.toString(), e);
636: }
637: } catch (Throwable e) {
638: _lifecycle.toError();
639:
640: if (deployInstance != null)
641: deployInstance.setConfigException(e);
642: else
643: log.log(Level.SEVERE, e.toString(), e);
644: } finally {
645: if (isStarting)
646: _lifecycle.toActive();
647:
648: // server/
649: if (loader instanceof DynamicClassLoader)
650: ((DynamicClassLoader) loader).clearModified();
651:
652: if (_alarm != null)
653: _alarm.queue(_redeployCheckInterval); // XXX: strategy-controlled
654:
655: thread.setContextClassLoader(oldLoader);
656: }
657:
658: return deployInstance;
659: }
660:
661: /**
662: * Deploys the entry, e.g. archive expansion.
663: */
664: protected void expandArchive() throws Exception {
665: }
666:
667: /**
668: * Stops the current instance, putting it in the lazy state.
669: */
670: void stopLazyImpl() {
671: if (_lifecycle.isInit())
672: return;
673:
674: stopImpl();
675:
676: _lifecycle.toPostInit();
677: }
678:
679: /**
680: * Stops the current instance.
681: */
682: void stopImpl() {
683: Thread thread = Thread.currentThread();
684: ClassLoader oldLoader = thread.getContextClassLoader();
685:
686: DeployInstance oldInstance = _deployInstance;
687: boolean isStopping = false;
688:
689: if (oldInstance != null)
690: thread.setContextClassLoader(oldInstance.getClassLoader());
691:
692: try {
693: isStopping = _lifecycle.toStopping();
694:
695: if (!isStopping)
696: return;
697:
698: synchronized (this ) {
699: oldInstance = _deployInstance;
700: _deployInstance = null;
701: }
702:
703: if (oldInstance != null) {
704: oldInstance.destroy();
705: }
706: } finally {
707: if (isStopping)
708: _lifecycle.toStop();
709:
710: thread.setContextClassLoader(oldLoader);
711: }
712:
713: return;
714: }
715:
716: /**
717: * Creates an instance.
718: */
719: abstract protected I instantiateDeployInstance();
720:
721: /**
722: * Adds any manifest Class-Path
723: */
724: protected void addManifestClassPath() throws IOException {
725: }
726:
727: /**
728: * Configuration of the instance
729: */
730: protected void configureInstance(I deployInstance) throws Throwable {
731: }
732:
733: /**
734: * Handles the redeploy check alarm.
735: */
736: public void handleAlarm(Alarm alarm) {
737: try {
738: _strategy.alarm(this );
739: } finally {
740: if (!_lifecycle.isDestroyed())
741: alarm.queue(_redeployCheckInterval);
742: }
743: }
744:
745: /**
746: * Returns true if the entry is destroyed.
747: */
748: public boolean isDestroyed() {
749: return _lifecycle.isDestroyed();
750: }
751:
752: /**
753: * Destroys the entry.
754: */
755: protected boolean destroy() {
756: if (_lifecycle.isAfterInit())
757: stop();
758:
759: if (!_lifecycle.toDestroy())
760: return false;
761:
762: Alarm alarm = _alarm;
763: _alarm = null;
764:
765: if (alarm != null) {
766: alarm.dequeue();
767: }
768:
769: return true;
770: }
771:
772: /**
773: * Returns the appropriate log for debugging.
774: */
775: protected Logger getLog() {
776: return log;
777: }
778:
779: /**
780: * Returns the entry's debug name.
781: */
782: public String toString() {
783: String className = getClass().getName();
784: int p = className.lastIndexOf('.');
785:
786: return className.substring(p + 1) + "[" + getId() + "]";
787: }
788: }
|