Package: BundleStartTracker

BundleStartTracker

nameinstructionbranchcomplexitylinemethod
BundleStartTracker(ExecutorService)
M: 12 C: 36
75%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
cleanup(Bundle, boolean, Throwable)
M: 6 C: 36
86%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 0 C: 9
100%
M: 0 C: 1
100%
driveSignals(List, boolean, Throwable)
M: 6 C: 11
65%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
driveSignalsIfStartCompleted(Bundle, boolean)
M: 30 C: 77
72%
M: 4 C: 14
78%
M: 3 C: 7
70%
M: 4 C: 21
84%
M: 0 C: 1
100%
handleEvent(Event)
M: 17 C: 80
82%
M: 4 C: 6
60%
M: 3 C: 3
50%
M: 2 C: 18
90%
M: 0 C: 1
100%
initialize(BundleContext)
M: 6 C: 5
45%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
isBundleActive(Bundle)
M: 8 C: 10
56%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 1 C: 2
67%
M: 0 C: 1
100%
isSpringDmPoweredBundle(Bundle)
M: 6 C: 3
33%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
recordApplicationContextCreation(Bundle)
M: 6 C: 18
75%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
static {...}
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
stop()
M: 6 C: 5
45%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
trackStart(Bundle, AbortableSignal)
M: 11 C: 66
86%
M: 3 C: 7
70%
M: 3 C: 3
50%
M: 1 C: 15
94%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2008, 2010 VMware Inc.
3: * All rights reserved. This program and the accompanying materials
4: * are made available under the terms of the Eclipse Public License v1.0
5: * which accompanies this distribution, and is available at
6: * http://www.eclipse.org/legal/epl-v10.html
7: *
8: * Contributors:
9: * VMware Inc. - initial contribution
10: *******************************************************************************/
11:
12: package org.eclipse.virgo.nano.core.internal;
13:
14: import java.util.ArrayList;
15: import java.util.Arrays;
16: import java.util.HashMap;
17: import java.util.List;
18: import java.util.Map;
19: import java.util.concurrent.ExecutorService;
20:
21: import org.eclipse.virgo.nano.core.AbortableSignal;
22: import org.eclipse.virgo.nano.core.BundleUtils;
23: import org.eclipse.virgo.nano.core.Signal;
24: import org.osgi.framework.Bundle;
25: import org.osgi.framework.BundleContext;
26: import org.osgi.framework.BundleEvent;
27: import org.osgi.framework.BundleListener;
28: import org.osgi.framework.SynchronousBundleListener;
29: import org.osgi.service.event.Event;
30: import org.osgi.service.event.EventHandler;
31: import org.slf4j.Logger;
32: import org.slf4j.LoggerFactory;
33:
34: /**
35: * <code>BundleStartTracker</code> tracks the startup of bundles, including any asynchronous portion of the startup,
36: * notifying a {@link Signal} upon completion (successful or otherwise).
37: *
38: * <p/>
39: *
40: * <strong>Note</strong> if the synchronous portion of startup fails, i.e. {@link Bundle#start()} does not return
41: * successfully the <code>Signal</code> is <strong>not</strong> driven and it is the responsibility of the caller of
42: * <code>start</code> to handle the failure.
43: *
44: * <p/>
45: *
46: * <strong>Concurrent Semantics</strong><br />
47: *
48: * Thread-safe.
49: *
50: */
51: final class BundleStartTracker implements EventHandler {
52:
53: private static final String TOPIC_BLUEPRINT_EVENTS = "org/osgi/service/blueprint/container/";
54:
55: private static final String EVENT_REGION_STARTING = "org/eclipse/virgo/kernel/region/STARTING";
56:
57: private static final String EVENT_CREATED = TOPIC_BLUEPRINT_EVENTS + "CREATED";
58:
59: private static final String EVENT_FAILURE = TOPIC_BLUEPRINT_EVENTS + "FAILURE";
60:
61: private static final Logger LOGGER = LoggerFactory.getLogger(BundleStartTracker.class);
62:
63: private final Object monitor = new Object();
64:
65: private final List<Bundle> bundlesWithCreatedApplicationContexts = new ArrayList<Bundle>();
66:
67: private final Map<Bundle, Throwable> failureMap = new HashMap<Bundle, Throwable>();
68:
69: private final Map<Bundle, List<AbortableSignal>> signalMap = new HashMap<Bundle, List<AbortableSignal>>();
70:
71: private final BundleListener bundleListener = new StartupTrackerBundleListener();
72:
73: private final ExecutorService signalExecutor;
74:
75: BundleStartTracker(ExecutorService signalExecutor) {
76: this.signalExecutor = signalExecutor;
77: }
78:
79: void initialize(BundleContext bundleContext) {
80: bundleContext.addBundleListener(this.bundleListener);
81: }
82:
83: private void recordApplicationContextCreation(Bundle bundle) {
84: LOGGER.info("Recording created application context for bundle '{}'", bundle);
85:
86: synchronized (this.monitor) {
87: this.bundlesWithCreatedApplicationContexts.add(bundle);
88: }
89: }
90:
91: private void driveSignalsIfStartCompleted(Bundle bundle, boolean springDmPowered) {
92:
93: List<AbortableSignal> signals = null;
94: Throwable failure = null;
95: boolean isActive = isBundleActive(bundle);
96:
97: synchronized (this.monitor) {
98:• if (springDmPowered) {
99: boolean created = this.bundlesWithCreatedApplicationContexts.contains(bundle);
100: failure = this.failureMap.get(bundle);
101:
102:• if (created && failure != null) {
103: throw new IllegalStateException("Spring DM has notified an application context both successfully constructed and failed: " + failure);
104: }
105:
106:• if (created) {
107: LOGGER.info("Bundle '{}' has started and its application context is available", bundle);
108: signals = this.signalMap.remove(bundle);
109:• } else if (failure != null) {
110: LOGGER.info("Bundle '{}' failed to start, the failure was '{}'", bundle, failure);
111: signals = this.signalMap.remove(bundle);
112: }
113: }
114: else {
115:• if (isActive) {
116: signals = this.signalMap.remove(bundle);
117: }
118: }
119: }
120: // signals to drive
121:• if (signals != null) {
122:• if (!springDmPowered && isActive) {
123: LOGGER.info("Non-Spring DM powered bundle '{}' has started. Driving signals '{}'.", bundle, signals);
124: driveSignals(signals, false, null);
125: }
126: else {
127: driveSignals(signals, false, failure);
128: }
129: }
130: }
131:
132: private void driveSignals(final List<AbortableSignal> signals, final boolean aborted, final Throwable cause) {
133: this.signalExecutor.execute(new Runnable() {
134: public void run() {
135: for (AbortableSignal signal : signals) {
136: LOGGER.info("Driving signal '{}'", signal);
137: if (aborted){
138:         signal.signalAborted();
139: } else if (cause == null) {
140: signal.signalSuccessfulCompletion();
141: } else {
142: signal.signalFailure(cause);
143: }
144: }
145: }
146: });
147: }
148:
149: /**
150: * {@inheritDoc}
151: */
152: public void handleEvent(Event event) {
153:
154: LOGGER.info("Handling event '{}'", event);
155:
156: Throwable cause = null;
157: List<AbortableSignal> signals = null;
158:
159: Bundle bundle = (Bundle) event.getProperty("bundle");
160:• if (EVENT_FAILURE.equals(event.getTopic())) {
161: cause = (Throwable) event.getProperty("exception");
162:• if (cause != null) {
163: synchronized (this.monitor) {
164: LOGGER.error("Recording application context construction failure '{}' for bundle '{}'", cause, bundle);
165: this.failureMap.put(bundle, cause);
166: signals = this.signalMap.remove(bundle);
167: }
168: }
169:• } else if (EVENT_CREATED.equals(event.getTopic())) {
170: synchronized (this.monitor) {
171: recordApplicationContextCreation(bundle);
172: signals = this.signalMap.remove(bundle);
173: }
174:• } else if (EVENT_REGION_STARTING.equals(event.getTopic())) {
175: initialize((BundleContext) event.getProperty("region.bundleContext"));
176: }
177:
178:• if (signals != null) {
179: driveSignals(signals, false, cause);
180: }
181: }
182:
183: public void trackStart(Bundle bundle, AbortableSignal signal) {
184:• if (BundleUtils.isFragmentBundle(bundle)) {
185: throw new IllegalArgumentException("Cannot track the start of a fragment bundle.");
186: }
187:
188: boolean springDmPowered = isSpringDmPoweredBundle(bundle);
189:
190: boolean bundleActive = isBundleActive(bundle);
191:
192:• if (signal != null) {
193:• if (springDmPowered || !bundleActive) {
194: List<AbortableSignal> queue;
195: synchronized (this.monitor) {
196: queue = this.signalMap.get(bundle);
197:• if (queue == null) {
198: queue = new ArrayList<AbortableSignal>();
199: this.signalMap.put(bundle, queue);
200: }
201: LOGGER.info("Adding signal '{}' for bundle '{}'", signal, bundle);
202: queue.add(signal);
203: }
204: } else {
205: // !springDmPowered && bundleActive
206: driveSignals(Arrays.asList(signal), false, null);
207: }
208: }
209: driveSignalsIfStartCompleted(bundle, springDmPowered);
210: }
211:
212: private static boolean isBundleActive(Bundle bundle) {
213:• if (bundle!=null) {
214:• return ( bundle.getState() == Bundle.ACTIVE );
215: }
216: return false;
217: }
218:
219: private static boolean isSpringDmPoweredBundle(Bundle bundle) {
220: return SpringUtils.isSpringDMPoweredBundle(bundle);
221: }
222:
223: private final class StartupTrackerBundleListener implements SynchronousBundleListener {
224:
225: private Boolean isLazyBundle = false;
226:
227: /**
228: * {@inheritDoc}
229: */
230: public void bundleChanged(BundleEvent event) {
231: Bundle bundle = event.getBundle();
232: if (event.getType() == BundleEvent.STARTED) {
233: List<AbortableSignal> signals = null;
234: if (!isSpringDmPoweredBundle(bundle)) {
235: synchronized (BundleStartTracker.this.monitor) {
236: signals = BundleStartTracker.this.signalMap.remove(bundle);
237: }
238: if (signals != null) {
239: LOGGER.info("Non-Spring DM powered bundle '{}' has started. Driving signals '{}'.", bundle, signals);
240: driveSignals(signals, false, null);
241: }
242: }
243: }
244: if (event.getType() == BundleEvent.LAZY_ACTIVATION) {
245: this.isLazyBundle = true;
246: LOGGER.info("Bundle '{}' has lazy activation and is in the starting state.", bundle);
247: }
248: if (event.getType() == BundleEvent.STOPPED) {
249: LOGGER.info("Bundle '{}' has stopped. Removing its related tracking state.", bundle);
250: BundleStartTracker.this.cleanup(bundle, this.isLazyBundle, this.isLazyBundle ? null : new RuntimeException("Bundle '" + bundle + "' stopped"));
251: }
252: }
253: }
254:
255: /**
256: * Remove tracking state associated with this bundle
257: * @param bundle whose tracking state is removed
258: * @param cause reason for cleaning up
259: */
260: public void cleanup(Bundle bundle, boolean aborted, Throwable cause) {
261: List<AbortableSignal> danglingSignals = null;
262: synchronized (BundleStartTracker.this.monitor) {
263:• if (bundle != null) {
264: BundleStartTracker.this.bundlesWithCreatedApplicationContexts.remove(bundle);
265: BundleStartTracker.this.failureMap.remove(bundle);
266: danglingSignals = BundleStartTracker.this.signalMap.remove(bundle);
267: }
268: }
269:• if (danglingSignals != null) {
270: driveSignals(danglingSignals, aborted, cause);
271: }
272: }
273:
274: /**
275: *
276: */
277: public void stop() {
278:         this.signalExecutor.shutdownNow();
279: }
280: }