1 /**
2 *
3 * Copyright 2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.geronimo.gbean.runtime;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.geronimo.gbean.AbstractName;
22 import org.apache.geronimo.kernel.DependencyManager;
23 import org.apache.geronimo.kernel.GBeanNotFoundException;
24 import org.apache.geronimo.kernel.Kernel;
25 import org.apache.geronimo.kernel.management.State;
26
27 import java.util.Iterator;
28 import java.util.Set;
29
30 /**
31 * @version $Rev: 430508 $ $Date: 2006-08-10 12:56:47 -0700 (Thu, 10 Aug 2006) $
32 */
33 public class GBeanInstanceState {
34 private static final Log log = LogFactory.getLog(GBeanInstanceState.class);
35
36 /**
37 * The GBeanInstance in which this server is registered.
38 */
39 private final GBeanInstance gbeanInstance;
40
41 /**
42 * The kernel in which this server is registered.
43 */
44 private final Kernel kernel;
45
46 /**
47 * The unique name of this service.
48 */
49 private final AbstractName abstractName;
50
51 /**
52 * The dependency manager
53 */
54 private final DependencyManager dependencyManager;
55
56 /**
57 * The broadcaster of lifecycle events
58 */
59 private final LifecycleBroadcaster lifecycleBroadcaster;
60
61
62
63 private volatile State state = State.STOPPED;
64
65 GBeanInstanceState(AbstractName abstractName, Kernel kernel, DependencyManager dependencyManager, GBeanInstance gbeanInstance, LifecycleBroadcaster lifecycleBroadcaster) {
66 this.abstractName = abstractName;
67 this.kernel = kernel;
68 this.dependencyManager = dependencyManager;
69 this.gbeanInstance = gbeanInstance;
70 this.lifecycleBroadcaster = lifecycleBroadcaster;
71 }
72
73 /**
74 * Moves this MBean to the {@link org.apache.geronimo.kernel.management.State#STARTING} state and then attempts to move this MBean immediately
75 * to the {@link org.apache.geronimo.kernel.management.State#RUNNING} state.
76 * <p/>
77 * Note: This method cannot be called while the current thread holds a synchronized lock on this MBean,
78 * because this method sends JMX notifications. Sending a general notification from a synchronized block
79 * is a bad idea and therefore not allowed.
80 */
81 public final void start() {
82 assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
83
84
85 State originalState;
86 synchronized (this) {
87 originalState = getStateInstance();
88 if (originalState == State.RUNNING) {
89 return;
90 }
91
92 if (originalState != State.STARTING) {
93 setStateInstance(State.STARTING);
94 }
95 }
96
97
98 if (originalState != State.STARTING) {
99 lifecycleBroadcaster.fireStartingEvent();
100 }
101
102 attemptFullStart();
103 }
104
105 /**
106 * Starts this MBean and then attempts to start all of its start dependent children.
107 * <p/>
108 * Note: This method cannot be call while the current thread holds a synchronized lock on this MBean,
109 * because this method sends JMX notifications. Sending a general notification from a synchronized block
110 * is a bad idea and therefore not allowed.
111 */
112 public final void startRecursive() {
113 assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
114
115 State state = getStateInstance();
116 if (state != State.STOPPED && state != State.FAILED && state != State.RUNNING) {
117
118
119
120 return;
121 }
122
123
124 start();
125
126
127 Set dependents = dependencyManager.getChildren(abstractName);
128 for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
129 AbstractName dependent = (AbstractName) iterator.next();
130 try {
131 kernel.startRecursiveGBean(dependent);
132 } catch (GBeanNotFoundException e) {
133
134 } catch (Exception e) {
135
136 }
137 }
138 }
139
140 /**
141 * Moves this MBean to the STOPPING state, calls stop on all start dependent children, and then attempt
142 * to move this MBean to the STOPPED state.
143 * <p/>
144 * Note: This method can not be call while the current thread holds a syncronized lock on this MBean,
145 * because this method sends JMX notifications. Sending a general notification from a synchronized block
146 * is a bad idea and therefore not allowed.
147 */
148 public final void stop() {
149 assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
150
151
152 State originalState;
153 synchronized (this) {
154 originalState = getStateInstance();
155 if (originalState == State.STOPPED) {
156 return;
157 }
158
159
160 if (originalState != State.STOPPING) {
161 setStateInstance(State.STOPPING);
162 }
163 }
164
165
166 if (originalState != State.STOPPING) {
167 lifecycleBroadcaster.fireStoppingEvent();
168 }
169
170
171
172
173 Set dependents = dependencyManager.getChildren(abstractName);
174 for (Iterator iterator = dependents.iterator(); iterator.hasNext();) {
175 AbstractName child = (AbstractName) iterator.next();
176 try {
177 log.trace("Checking if child is running: child=" + child);
178 if (kernel.getGBeanState(child) == State.RUNNING_INDEX) {
179 log.trace("Stopping child: child=" + child);
180 kernel.stopGBean(child);
181 log.trace("Stopped child: child=" + child);
182 }
183 } catch (Exception ignore) {
184
185 }
186 }
187
188 attemptFullStop();
189 }
190
191 /**
192 * Moves this MBean to the FAILED state. There are no calls to dependent children, but they will be notified
193 * using standard J2EE management notification.
194 * <p/>
195 * Note: This method can not be call while the current thread holds a syncronized lock on this MBean,
196 * because this method sends JMX notifications. Sending a general notification from a synchronized block
197 * is a bad idea and therefore not allowed.
198 */
199 final void fail() {
200 assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
201
202 synchronized (this) {
203 State state = getStateInstance();
204 if (state == State.STOPPED || state == State.FAILED) {
205 return;
206 }
207 }
208
209 try {
210 if (gbeanInstance.destroyInstance(false)) {
211
212
213 return;
214 }
215 } catch (Throwable e) {
216 log.warn("Problem in doFail", e);
217 }
218 setStateInstance(State.FAILED);
219 lifecycleBroadcaster.fireFailedEvent();
220 }
221
222 /**
223 * Attempts to bring the component into {@link org.apache.geronimo.kernel.management.State#RUNNING} state. If an Exception occurs while
224 * starting the component, the component will be failed.
225 * <p/>
226 * <p/>
227 * Note: Do not call this from within a synchronized block as it makes may send a JMX notification
228 */
229 void attemptFullStart() {
230 assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
231
232 synchronized (this) {
233
234 if (getStateInstance() != State.STARTING) {
235 return;
236 }
237
238
239 Set parents = dependencyManager.getParents(abstractName);
240 for (Iterator i = parents.iterator(); i.hasNext();) {
241 AbstractName parent = (AbstractName) i.next();
242 if (!kernel.isLoaded(parent)) {
243 log.trace("Cannot run because parent is not registered: parent=" + parent);
244 return;
245 }
246 try {
247 log.trace("Checking if parent is running: parent=" + parent);
248 if (kernel.getGBeanState(parent) != State.RUNNING_INDEX) {
249 log.trace("Cannot run because parent is not running: parent=" + parent);
250 return;
251 }
252 log.trace("Parent is running: parent=" + parent);
253 } catch (GBeanNotFoundException e) {
254
255 log.trace("Cannot run because parent is not registered: parent=" + parent);
256 return;
257 } catch (Exception e) {
258
259 log.trace("Cannot run because an error occurred while checking if parent is running: parent=" + parent);
260 return;
261 }
262 }
263 }
264
265 try {
266
267 if (!gbeanInstance.createInstance()) {
268
269
270
271
272 return;
273 }
274 } catch (Throwable t) {
275
276 log.error("Error while starting; GBean is now in the FAILED state: abstractName=\"" + abstractName + "\"", t);
277 setStateInstance(State.FAILED);
278 lifecycleBroadcaster.fireFailedEvent();
279
280 if (t instanceof Exception) {
281
282 return;
283 } else if (t instanceof Error) {
284 throw (Error) t;
285 } else {
286 throw new Error(t);
287 }
288 }
289
290
291 setStateInstance(State.RUNNING);
292 lifecycleBroadcaster.fireRunningEvent();
293 }
294
295 /**
296 * Attempt to bring the component into the fully stopped state.
297 * If an exception occurs while stopping the component, the component will be failed.
298 * <p/>
299 * <p/>
300 * Note: Do not call this from within a synchronized block as it may send a JMX notification
301 */
302 void attemptFullStop() {
303 assert !Thread.holdsLock(this): "This method cannot be called while holding a synchronized lock on this";
304
305
306 synchronized (this) {
307
308 if (getStateInstance() != State.STOPPING) {
309 return;
310 }
311
312
313 Set children = dependencyManager.getChildren(abstractName);
314 for (Iterator i = children.iterator(); i.hasNext();) {
315 AbstractName child = (AbstractName) i.next();
316 if (kernel.isLoaded(child)) {
317 try {
318 log.trace("Checking if child is stopped: child=" + child);
319 int state = kernel.getGBeanState(child);
320 if (state == State.RUNNING_INDEX) {
321 log.trace("Cannot stop because child is still running: child=" + child);
322 return;
323 }
324 } catch (GBeanNotFoundException e) {
325
326 } catch (Exception e) {
327
328 log.trace("Cannot run because an error occurred while checking if child is stopped: child=" + child);
329 return;
330 }
331 }
332 }
333 }
334
335
336 try {
337 if (!gbeanInstance.destroyInstance(true)) {
338
339
340 return;
341 }
342 } catch (Throwable t) {
343 log.error("Error while stopping; GBean is now in the FAILED state: abstractName=\"" + abstractName + "\"", t);
344 setStateInstance(State.FAILED);
345 lifecycleBroadcaster.fireFailedEvent();
346
347 if (t instanceof Exception) {
348
349 return;
350 } else if (t instanceof Error) {
351 throw (Error) t;
352 } else {
353 throw new Error(t);
354 }
355 }
356
357
358 setStateInstance(State.STOPPED);
359 lifecycleBroadcaster.fireStoppedEvent();
360 }
361
362 public int getState() {
363 return state.toInt();
364 }
365
366 public final State getStateInstance() {
367 return state;
368 }
369
370 /**
371 * Set the Component state.
372 *
373 * @param newState the target state to transition
374 * @throws IllegalStateException Thrown if the transition is not supported by the J2EE Management lifecycle.
375 */
376 private synchronized void setStateInstance(State newState) throws IllegalStateException {
377 switch (state.toInt()) {
378 case State.STOPPED_INDEX:
379 switch (newState.toInt()) {
380 case State.STARTING_INDEX:
381 break;
382 case State.STOPPED_INDEX:
383 case State.RUNNING_INDEX:
384 case State.STOPPING_INDEX:
385 case State.FAILED_INDEX:
386 throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
387 }
388 break;
389
390 case State.STARTING_INDEX:
391 switch (newState.toInt()) {
392 case State.RUNNING_INDEX:
393 case State.FAILED_INDEX:
394 case State.STOPPING_INDEX:
395 break;
396 case State.STOPPED_INDEX:
397 case State.STARTING_INDEX:
398 throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
399 }
400 break;
401
402 case State.RUNNING_INDEX:
403 switch (newState.toInt()) {
404 case State.STOPPING_INDEX:
405 case State.FAILED_INDEX:
406 break;
407 case State.STOPPED_INDEX:
408 case State.STARTING_INDEX:
409 case State.RUNNING_INDEX:
410 throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
411 }
412 break;
413
414 case State.STOPPING_INDEX:
415 switch (newState.toInt()) {
416 case State.STOPPED_INDEX:
417 case State.FAILED_INDEX:
418 break;
419 case State.STARTING_INDEX:
420 case State.RUNNING_INDEX:
421 case State.STOPPING_INDEX:
422 throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
423 }
424 break;
425
426 case State.FAILED_INDEX:
427 switch (newState.toInt()) {
428 case State.STARTING_INDEX:
429 case State.STOPPING_INDEX:
430 break;
431 case State.RUNNING_INDEX:
432 case State.STOPPED_INDEX:
433 case State.FAILED_INDEX:
434 throw new IllegalStateException("Cannot transition to " + newState + " state from " + state);
435 }
436 break;
437 }
438 log.debug(toString() + " State changed from " + state + " to " + newState);
439 state = newState;
440 }
441
442 public String toString() {
443 return "GBeanInstanceState for: " + abstractName;
444 }
445
446 }