001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.gbean.runtime;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.apache.geronimo.gbean.AbstractName;
021: import org.apache.geronimo.kernel.DependencyManager;
022: import org.apache.geronimo.kernel.GBeanNotFoundException;
023: import org.apache.geronimo.kernel.Kernel;
024: import org.apache.geronimo.kernel.management.State;
025:
026: import java.util.Iterator;
027: import java.util.Set;
028:
029: /**
030: * @version $Rev: 542575 $ $Date: 2007-05-29 09:02:57 -0700 (Tue, 29 May 2007) $
031: */
032: public class GBeanInstanceState {
033: private static final Log log = LogFactory
034: .getLog(GBeanInstanceState.class);
035:
036: /**
037: * The GBeanInstance in which this server is registered.
038: */
039: private final GBeanInstance gbeanInstance;
040:
041: /**
042: * The kernel in which this server is registered.
043: */
044: private final Kernel kernel;
045:
046: /**
047: * The unique name of this service.
048: */
049: private final AbstractName abstractName;
050:
051: /**
052: * The dependency manager
053: */
054: private final DependencyManager dependencyManager;
055:
056: /**
057: * The broadcaster of lifecycle events
058: */
059: private final LifecycleBroadcaster lifecycleBroadcaster;
060:
061: // This must be volatile otherwise getState must be synchronized which will result in deadlock as dependent
062: // objects check if each other are in one state or another (i.e., classic A calls B while B calls A)
063: private volatile State state = State.STOPPED;
064:
065: GBeanInstanceState(AbstractName abstractName, Kernel kernel,
066: DependencyManager dependencyManager,
067: GBeanInstance gbeanInstance,
068: LifecycleBroadcaster lifecycleBroadcaster) {
069: this .abstractName = abstractName;
070: this .kernel = kernel;
071: this .dependencyManager = dependencyManager;
072: this .gbeanInstance = gbeanInstance;
073: this .lifecycleBroadcaster = lifecycleBroadcaster;
074: }
075:
076: /**
077: * Moves this MBean to the {@link org.apache.geronimo.kernel.management.State#STARTING} state and then attempts to move this MBean immediately
078: * to the {@link org.apache.geronimo.kernel.management.State#RUNNING} state.
079: * <p/>
080: * Note: This method cannot be called while the current thread holds a synchronized lock on this MBean,
081: * because this method sends JMX notifications. Sending a general notification from a synchronized block
082: * is a bad idea and therefore not allowed.
083: */
084: public final void start() {
085: assert !Thread.holdsLock(this ) : "This method cannot be called while holding a synchronized lock on this";
086:
087: // Move to the starting state
088: State originalState;
089: synchronized (this ) {
090: originalState = getStateInstance();
091: if (originalState == State.RUNNING) {
092: return;
093: }
094: // only try to change states if we are not already starting
095: if (originalState != State.STARTING) {
096: setStateInstance(State.STARTING);
097: }
098: }
099:
100: // only fire a notification if we are not already starting
101: if (originalState != State.STARTING) {
102: lifecycleBroadcaster.fireStartingEvent();
103: }
104:
105: attemptFullStart();
106: }
107:
108: /**
109: * Starts this MBean and then attempts to start all of its start dependent children.
110: * <p/>
111: * Note: This method cannot be call while the current thread holds a synchronized lock on this MBean,
112: * because this method sends JMX notifications. Sending a general notification from a synchronized block
113: * is a bad idea and therefore not allowed.
114: */
115: public final void startRecursive() {
116: assert !Thread.holdsLock(this ) : "This method cannot be called while holding a synchronized lock on this";
117:
118: State state = getStateInstance();
119: if (state != State.STOPPED && state != State.FAILED
120: && state != State.RUNNING) {
121: // Cannot startRecursive while in the stopping state
122: // Dain: I don't think we can throw an exception here because there is no way for the caller
123: // to lock the instance and check the state before calling
124: return;
125: }
126:
127: // get myself starting
128: start();
129:
130: // startRecursive all of objects that depend on me
131: Set dependents = dependencyManager.getChildren(abstractName);
132: for (Iterator iterator = dependents.iterator(); iterator
133: .hasNext();) {
134: AbstractName dependent = (AbstractName) iterator.next();
135: try {
136: kernel.startRecursiveGBean(dependent);
137: } catch (GBeanNotFoundException e) {
138: // this is ok the gbean died before we could start it
139: } catch (Exception e) {
140: // there is something wrong with this gbean... skip it
141: }
142: }
143: }
144:
145: /**
146: * Moves this MBean to the STOPPING state, calls stop on all start dependent children, and then attempt
147: * to move this MBean to the STOPPED state.
148: * <p/>
149: * Note: This method can not be call while the current thread holds a syncronized lock on this MBean,
150: * because this method sends JMX notifications. Sending a general notification from a synchronized block
151: * is a bad idea and therefore not allowed.
152: */
153: public final void stop() {
154: assert !Thread.holdsLock(this ) : "This method cannot be called while holding a synchronized lock on this";
155:
156: // move to the stopping state
157: State originalState;
158: synchronized (this ) {
159: originalState = getStateInstance();
160: if (originalState == State.STOPPED) {
161: return;
162: }
163:
164: // only try to change states if we are not already stopping
165: if (originalState != State.STOPPING) {
166: setStateInstance(State.STOPPING);
167: }
168: }
169:
170: // only fire a notification if we are not already stopping
171: if (originalState != State.STOPPING) {
172: lifecycleBroadcaster.fireStoppingEvent();
173: }
174:
175: // Don't try to stop dependents from within a synchronized block... this should reduce deadlocks
176:
177: // stop all of my dependent objects
178: Set dependents = dependencyManager.getChildren(abstractName);
179: for (Iterator iterator = dependents.iterator(); iterator
180: .hasNext();) {
181: AbstractName child = (AbstractName) iterator.next();
182: try {
183: log.trace("Checking if child is running: child="
184: + child);
185: if (kernel.getGBeanState(child) == State.RUNNING_INDEX) {
186: log.trace("Stopping child: child=" + child);
187: kernel.stopGBean(child);
188: log.trace("Stopped child: child=" + child);
189: }
190: } catch (Exception ignore) {
191: // not a big deal... did my best
192: }
193: }
194:
195: attemptFullStop();
196: }
197:
198: /**
199: * Moves this MBean to the FAILED state. There are no calls to dependent children, but they will be notified
200: * using standard J2EE management notification.
201: * <p/>
202: * Note: This method can not be call while the current thread holds a syncronized lock on this MBean,
203: * because this method sends JMX notifications. Sending a general notification from a synchronized block
204: * is a bad idea and therefore not allowed.
205: */
206: final void fail() {
207: assert !Thread.holdsLock(this ) : "This method cannot be called while holding a synchronized lock on this";
208:
209: synchronized (this ) {
210: State state = getStateInstance();
211: if (state == State.STOPPED || state == State.FAILED) {
212: return;
213: }
214: }
215:
216: try {
217: if (gbeanInstance.destroyInstance(false)) {
218: // instance is not ready to destroyed... this is because another thread has
219: // already killed the gbean.
220: return;
221: }
222: } catch (Throwable e) {
223: gbeanInstance.setStateReason(e.getMessage());
224: log.warn("Problem in doFail", e);
225: }
226: setStateInstance(State.FAILED);
227: lifecycleBroadcaster.fireFailedEvent();
228: }
229:
230: /**
231: * Attempts to bring the component into {@link org.apache.geronimo.kernel.management.State#RUNNING} state. If an Exception occurs while
232: * starting the component, the component will be failed.
233: * <p/>
234: * <p/>
235: * Note: Do not call this from within a synchronized block as it makes may send a JMX notification
236: */
237: void attemptFullStart() {
238: assert !Thread.holdsLock(this ) : "This method cannot be called while holding a synchronized lock on this";
239:
240: synchronized (this ) {
241: // if we are still trying to start and can start now... start
242: if (getStateInstance() != State.STARTING) {
243: return;
244: }
245:
246: // check if all of the gbeans we depend on are running
247: Set parents = dependencyManager.getParents(abstractName);
248: for (Iterator i = parents.iterator(); i.hasNext();) {
249: AbstractName parent = (AbstractName) i.next();
250: if (!kernel.isLoaded(parent)) {
251: log
252: .trace("Cannot run because parent is not registered: parent="
253: + parent);
254: return;
255: }
256: try {
257: log.trace("Checking if parent is running: parent="
258: + parent);
259: if (kernel.getGBeanState(parent) != State.RUNNING_INDEX) {
260: log
261: .trace("Cannot run because parent is not running: parent="
262: + parent);
263: return;
264: }
265: log.trace("Parent is running: parent=" + parent);
266: } catch (GBeanNotFoundException e) {
267: // depended on instance was removed bewteen the register check and the invoke
268: log
269: .trace("Cannot run because parent is not registered: parent="
270: + parent);
271: return;
272: } catch (Exception e) {
273: // problem getting the attribute, parent has most likely failed
274: log
275: .trace("Cannot run because an error occurred while checking if parent is running: parent="
276: + parent);
277: return;
278: }
279: }
280: }
281:
282: try {
283: // try to create the instance
284: if (!gbeanInstance.createInstance()) {
285: // instance is not ready to start... this is normally caused by references
286: // not being available, but could be because someone already started the gbean.
287: // in another thread. The reference will log a debug message about why
288: // it could not start
289: return;
290: }
291: } catch (Throwable t) {
292: // oops there was a problem and the gbean failed
293: log.error(
294: "Error while starting; GBean is now in the FAILED state: abstractName=\""
295: + abstractName + "\"", t);
296: setStateInstance(State.FAILED);
297: lifecycleBroadcaster.fireFailedEvent();
298:
299: if (t instanceof Exception) {
300: // ignore - we only rethrow errors
301: gbeanInstance.setStateReason(t.getMessage());
302: return;
303: } else if (t instanceof Error) {
304: throw (Error) t;
305: } else {
306: throw new Error(t);
307: }
308: }
309:
310: // started successfully... notify everyone else
311: setStateInstance(State.RUNNING);
312: lifecycleBroadcaster.fireRunningEvent();
313: }
314:
315: /**
316: * Attempt to bring the component into the fully stopped state.
317: * If an exception occurs while stopping the component, the component will be failed.
318: * <p/>
319: * <p/>
320: * Note: Do not call this from within a synchronized block as it may send a JMX notification
321: */
322: void attemptFullStop() {
323: assert !Thread.holdsLock(this ) : "This method cannot be called while holding a synchronized lock on this";
324:
325: // check if we are able to stop
326: synchronized (this ) {
327: // if we are still trying to stop...
328: if (getStateInstance() != State.STOPPING) {
329: return;
330: }
331:
332: // check if all of the mbeans depending on us are stopped
333: Set children = dependencyManager.getChildren(abstractName);
334: for (Iterator i = children.iterator(); i.hasNext();) {
335: AbstractName child = (AbstractName) i.next();
336: if (kernel.isLoaded(child)) {
337: try {
338: log
339: .trace("Checking if child is stopped: child="
340: + child);
341: int state = kernel.getGBeanState(child);
342: if (state == State.RUNNING_INDEX) {
343: log
344: .trace("Cannot stop because child is still running: child="
345: + child);
346: return;
347: }
348: } catch (GBeanNotFoundException e) {
349: // depended on instance was removed between the register check and the invoke
350: } catch (Exception e) {
351: // problem getting the attribute, depended on bean has most likely failed
352: log
353: .trace("Cannot run because an error occurred while checking if child is stopped: child="
354: + child);
355: return;
356: }
357: }
358: }
359: }
360:
361: // all is clear to stop... try to stop
362: try {
363: if (!gbeanInstance.destroyInstance(true)) {
364: // instance is not ready to stop... this is because another thread has
365: // already stopped the gbean.
366: return;
367: }
368: } catch (Throwable t) {
369: log.error(
370: "Error while stopping; GBean is now in the FAILED state: abstractName=\""
371: + abstractName + "\"", t);
372: setStateInstance(State.FAILED);
373: lifecycleBroadcaster.fireFailedEvent();
374:
375: if (t instanceof Exception) {
376: // ignore - we only rethrow errors
377: gbeanInstance.setStateReason(t.getMessage());
378: return;
379: } else if (t instanceof Error) {
380: throw (Error) t;
381: } else {
382: throw new Error(t);
383: }
384: }
385:
386: // we successfully stopped, notify everyone else
387: setStateInstance(State.STOPPED);
388: lifecycleBroadcaster.fireStoppedEvent();
389: }
390:
391: public int getState() {
392: return state.toInt();
393: }
394:
395: public final State getStateInstance() {
396: return state;
397: }
398:
399: /**
400: * Set the Component state.
401: *
402: * @param newState the target state to transition
403: * @throws IllegalStateException Thrown if the transition is not supported by the J2EE Management lifecycle.
404: */
405: private synchronized void setStateInstance(State newState)
406: throws IllegalStateException {
407: switch (state.toInt()) {
408: case State.STOPPED_INDEX:
409: switch (newState.toInt()) {
410: case State.STARTING_INDEX:
411: break;
412: case State.STOPPED_INDEX:
413: case State.RUNNING_INDEX:
414: case State.STOPPING_INDEX:
415: case State.FAILED_INDEX:
416: throw new IllegalStateException("Cannot transition to "
417: + newState + " state from " + state);
418: }
419: break;
420:
421: case State.STARTING_INDEX:
422: switch (newState.toInt()) {
423: case State.RUNNING_INDEX:
424: case State.FAILED_INDEX:
425: case State.STOPPING_INDEX:
426: break;
427: case State.STOPPED_INDEX:
428: case State.STARTING_INDEX:
429: throw new IllegalStateException("Cannot transition to "
430: + newState + " state from " + state);
431: }
432: break;
433:
434: case State.RUNNING_INDEX:
435: switch (newState.toInt()) {
436: case State.STOPPING_INDEX:
437: case State.FAILED_INDEX:
438: break;
439: case State.STOPPED_INDEX:
440: case State.STARTING_INDEX:
441: case State.RUNNING_INDEX:
442: throw new IllegalStateException("Cannot transition to "
443: + newState + " state from " + state);
444: }
445: break;
446:
447: case State.STOPPING_INDEX:
448: switch (newState.toInt()) {
449: case State.STOPPED_INDEX:
450: case State.FAILED_INDEX:
451: break;
452: case State.STARTING_INDEX:
453: case State.RUNNING_INDEX:
454: case State.STOPPING_INDEX:
455: throw new IllegalStateException("Cannot transition to "
456: + newState + " state from " + state);
457: }
458: break;
459:
460: case State.FAILED_INDEX:
461: switch (newState.toInt()) {
462: case State.STARTING_INDEX:
463: case State.STOPPING_INDEX:
464: break;
465: case State.RUNNING_INDEX:
466: case State.STOPPED_INDEX:
467: case State.FAILED_INDEX:
468: throw new IllegalStateException("Cannot transition to "
469: + newState + " state from " + state);
470: }
471: break;
472: }
473: log.debug(toString() + " State changed from " + state + " to "
474: + newState);
475: state = newState;
476: }
477:
478: public String toString() {
479: return "GBeanInstanceState for: " + abstractName;
480: }
481:
482: }
|