Package: ApplicationContextDependencyMonitor

ApplicationContextDependencyMonitor

nameinstructionbranchcomplexitylinemethod
ApplicationContextDependencyMonitor(ScheduledExecutorService, EventLogger)
M: 12 C: 27
69%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 7
100%
M: 0 C: 1
100%
addServiceDependencyTicker(ApplicationContextDependencyMonitor.ServiceDependency, Bundle)
M: 12 C: 40
77%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 2 C: 8
80%
M: 0 C: 1
100%
changeInUnsatisfiedDependencies(List, Bundle)
M: 6 C: 37
86%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 0 C: 9
100%
M: 0 C: 1
100%
containerCreated(Bundle)
M: 6 C: 30
83%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
containerCreationFailed(Bundle)
M: 6 C: 26
81%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
createServiceDependencies(Event)
M: 7 C: 46
87%
M: 2 C: 6
75%
M: 2 C: 3
60%
M: 0 C: 8
100%
M: 0 C: 1
100%
dependencySatisfied(ApplicationContextDependencyMonitor.ServiceDependency, Ticker, Bundle)
M: 6 C: 12
67%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
dependencyTimedOut(ApplicationContextDependencyMonitor.ServiceDependency, Ticker, Bundle)
M: 6 C: 12
67%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
getServiceDependencyTickers(Bundle)
M: 6 C: 20
77%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 5
100%
M: 0 C: 1
100%
handleEvent(Event)
M: 6 C: 92
94%
M: 4 C: 14
78%
M: 4 C: 6
60%
M: 0 C: 22
100%
M: 0 C: 1
100%
handleRemovedTicker(Ticker, ApplicationContextDependencyMonitor.ServiceDependency, Bundle, boolean)
M: 6 C: 61
91%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
serviceDependenciesTimedOut(List, Bundle)
M: 6 C: 32
84%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 0 C: 7
100%
M: 0 C: 1
100%
stop()
M: 6 C: 4
40%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
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.blueprint;
13:
14: import java.util.ArrayList;
15: import java.util.HashMap;
16: import java.util.Iterator;
17: import java.util.List;
18: import java.util.Map;
19: import java.util.Map.Entry;
20: import java.util.concurrent.Callable;
21: import java.util.concurrent.ScheduledExecutorService;
22:
23: import org.osgi.framework.Bundle;
24: import org.osgi.service.event.Event;
25: import org.osgi.service.event.EventConstants;
26: import org.osgi.service.event.EventHandler;
27: import org.slf4j.Logger;
28: import org.slf4j.LoggerFactory;
29:
30: import org.eclipse.virgo.medic.eventlog.EventLogger;
31: import org.eclipse.virgo.nano.diagnostics.KernelLogEvents;
32:
33: /**
34: * {@link ApplicationContextDependencyMonitor} is a class that tracks the satisfaction of service dependencies needed
35: * during the creation of application contexts and issues log messages for delayed service dependencies.
36: * <p />
37: *
38: * <strong>Concurrent Semantics</strong><br />
39: *
40: * This class is thread safe.
41: *
42: */
43: public final class ApplicationContextDependencyMonitor implements EventHandler {
44:
45: private static final String TOPIC_BLUEPRINT_EVENTS = "org/osgi/service/blueprint/container/";
46:
47: private static final String EVENT_WAITING = TOPIC_BLUEPRINT_EVENTS + "WAITING";
48:
49: private static final String EVENT_GRACE_PERIOD = TOPIC_BLUEPRINT_EVENTS + "GRACE_PERIOD";
50:
51: private static final String EVENT_FAILURE = TOPIC_BLUEPRINT_EVENTS + "FAILURE";
52:
53: private static final String EVENT_CREATED = TOPIC_BLUEPRINT_EVENTS + "CREATED";
54:
55: private static final int MAXIMUM_WARNING_INTERVAL = 60 * 1000;
56:
57: private static final int WARNING_INTERVAL_INCREASE_RATE_PERCENT = 200;
58:
59: private static final int INITIAL_WARNING_INTERVAL = 5 * 1000;
60:
61: private static final int SLOW_WARNING_INTERVAL = 5 * 60 * 1000;
62:
63: private final Logger logger = LoggerFactory.getLogger(this.getClass());
64:
65: private final EventLogger eventLogger;
66:
67: private final ScheduledExecutorService scheduledExecutorService;
68:
69: private final Map<Bundle, Map<ServiceDependency, Ticker>> tickers = new HashMap<Bundle, Map<ServiceDependency, Ticker>>();
70:
71: private final Object monitor = new Object();
72:
73: /**
74: * Construct a {@link ApplicationContextDependencyMonitor} which uses the given {@link ScheduledExecutorService} to
75: * schedule its warning messages.
76: *
77: * @param scheduledExecutorService the {@link ScheduledExecutorService} for scheduling warning messages
78: * @param eventLogger
79: */
80: public ApplicationContextDependencyMonitor(ScheduledExecutorService scheduledExecutorService, EventLogger eventLogger) {
81: this.scheduledExecutorService = scheduledExecutorService;
82: this.eventLogger = eventLogger;
83: }
84:
85: /**
86: * {@inheritDoc}
87: */
88: public void handleEvent(Event event) {
89: synchronized (this.monitor) {
90: Bundle bundle = (Bundle) event.getProperty(EventConstants.BUNDLE);
91:
92:• if (EVENT_WAITING.equals(event.getTopic())) {
93: List<ServiceDependency> serviceDependencies = createServiceDependencies(event);
94:• if (serviceDependencies != null) {
95:• for (ServiceDependency serviceDependency : serviceDependencies) {
96: addServiceDependencyTicker(serviceDependency, bundle);
97: }
98: }
99:• } else if (EVENT_GRACE_PERIOD.equals(event.getTopic())) {
100: List<ServiceDependency> remainingUnsatisfiedDependencies = createServiceDependencies(event);
101:• if (remainingUnsatisfiedDependencies != null) {
102: changeInUnsatisfiedDependencies(remainingUnsatisfiedDependencies, bundle);
103: }
104:
105:• } else if (EVENT_FAILURE.equals(event.getTopic())) {
106: String[] dependenciesArray = (String[]) event.getProperty("dependencies");
107:• if (dependenciesArray != null) {
108: List<ServiceDependency> serviceDependencies = createServiceDependencies(event);
109:• if (serviceDependencies != null) {
110: serviceDependenciesTimedOut(serviceDependencies, bundle);
111: }
112: } else {
113: containerCreationFailed(bundle);
114: }
115:• } else if (EVENT_CREATED.equals(event.getTopic())) {
116: containerCreated(bundle);
117: }
118: }
119: }
120:
121: private void serviceDependenciesTimedOut(List<ServiceDependency> timedOutDependencies, Bundle bundle) {
122: Map<ServiceDependency, Ticker> bundlesTickers = this.tickers.get(bundle);
123:
124:• if (bundlesTickers != null) {
125:• for (ServiceDependency timedOutDependency : timedOutDependencies) {
126: Ticker ticker = bundlesTickers.remove(timedOutDependency);
127:• if (ticker != null) {
128: dependencyTimedOut(timedOutDependency, ticker, bundle);
129: }
130: }
131: }
132: }
133:
134: private void containerCreationFailed(Bundle bundle) {
135: Map<ServiceDependency, Ticker> tickers = this.tickers.remove(bundle);
136:• if (tickers != null) {
137:• for (Entry<ServiceDependency, Ticker> ticker : tickers.entrySet()) {
138: ticker.getValue().cancel();
139: }
140: }
141: }
142:
143: private void containerCreated(Bundle bundle) {
144: Map<ServiceDependency, Ticker> bundlesTickers = this.tickers.remove(bundle);
145:
146:• if (bundlesTickers != null) {
147:• for (Entry<ServiceDependency, Ticker> entry : bundlesTickers.entrySet()) {
148: dependencySatisfied(entry.getKey(), entry.getValue(), bundle);
149: }
150: }
151: }
152:
153: private void changeInUnsatisfiedDependencies(List<ServiceDependency> remainingUnsatisfiedDependencies, Bundle bundle) {
154: Map<ServiceDependency, Ticker> tickers = this.tickers.get(bundle);
155:
156:• if (tickers != null) {
157: Iterator<Entry<ServiceDependency, Ticker>> entries = tickers.entrySet().iterator();
158:
159:• while (entries.hasNext()) {
160: Entry<ServiceDependency, Ticker> entry = entries.next();
161:
162:• if (!remainingUnsatisfiedDependencies.contains(entry.getKey())) {
163: dependencySatisfied(entry.getKey(), entry.getValue(), bundle);
164: entries.remove();
165: }
166: }
167: }
168: }
169:
170: private void dependencySatisfied(ServiceDependency serviceDependency, Ticker ticker, Bundle bundle) {
171: logger.info("Service dependency '{}' has been satisfied", serviceDependency);
172: handleRemovedTicker(ticker, serviceDependency, bundle, true);
173: }
174:
175: private void dependencyTimedOut(ServiceDependency serviceDependency, Ticker ticker, Bundle bundle) {
176: logger.info("Service dependency '{}' has timed out", serviceDependency);
177: handleRemovedTicker(ticker, serviceDependency, bundle, false);
178: }
179:
180: private void handleRemovedTicker(Ticker ticker, ServiceDependency serviceDependency, Bundle bundle, boolean satisfied) {
181: boolean hasTicked = ticker.cancel();
182:• if (hasTicked) {
183:• if (satisfied) {
184: this.eventLogger.log(KernelLogEvents.APPLICATION_CONTEXT_DEPENDENCY_SATISFIED, serviceDependency.getBeanName(),
185: bundle.getSymbolicName(), bundle.getVersion(), serviceDependency.getFilter());
186: } else {
187: this.eventLogger.log(KernelLogEvents.APPLICATION_CONTEXT_DEPENDENCY_TIMED_OUT, serviceDependency.getBeanName(),
188: bundle.getSymbolicName(), bundle.getVersion(), serviceDependency.getFilter());
189: }
190: }
191: }
192:
193: /**
194: * Add a service dependency ticker for the given application context, given associated bundle, and given service
195: * dependency.
196: *
197: * @param applicationContext the partially constructed application context which needs the service dependency
198: * @param serviceDependency the service dependency
199: * @param bundle the {@link Bundle} associated with the given application context
200: */
201: private void addServiceDependencyTicker(final ServiceDependency serviceDependency, final Bundle bundle) {
202: Map<ServiceDependency, Ticker> serviceDependencyTickers = getServiceDependencyTickers(bundle);
203:• if (serviceDependencyTickers.containsKey(serviceDependency)) {
204: logger.warn("Service dependency '{}' already being waited upon", serviceDependency);
205: } else {
206: // Services which are flagged as likely to be slow to be published are given a longer initial warning
207: // interval.
208: boolean slowService = serviceDependency.getFilter().contains("(org.eclipse.virgo.server.slowservice=true)");
209:• serviceDependencyTickers.put(serviceDependency, StandardTicker.createExponentialTicker(slowService ? SLOW_WARNING_INTERVAL
210:• : INITIAL_WARNING_INTERVAL, WARNING_INTERVAL_INCREASE_RATE_PERCENT, slowService ? SLOW_WARNING_INTERVAL : MAXIMUM_WARNING_INTERVAL,
211: new Callable<Void>() {
212:
213: public Void call() throws Exception {
214: synchronized (ApplicationContextDependencyMonitor.this.monitor) {
215: if (bundle.getState() == Bundle.UNINSTALLED) {
216: ApplicationContextDependencyMonitor.this.containerCreationFailed(bundle);
217: } else {
218: eventLogger.log(KernelLogEvents.APPLICATION_CONTEXT_DEPENDENCY_DELAYED, serviceDependency.getBeanName(),
219: bundle.getSymbolicName(), bundle.getVersion(), serviceDependency.getFilter());
220: }
221: return null;
222: }
223: }
224: }, this.scheduledExecutorService));
225: }
226: }
227:
228: /**
229: * Get the possibly empty map of service dependency tickers for the given <code>Bundle</code>.
230: *
231: * @param bundle the <code>Bundle</code> whose application context's service dependencies are required
232: * @return a map of service dependency tickers
233: */
234: private Map<ServiceDependency, Ticker> getServiceDependencyTickers(Bundle bundle) {
235: Map<ServiceDependency, Ticker> tickers = this.tickers.get(bundle);
236:• if (tickers == null) {
237: tickers = new HashMap<ServiceDependency, Ticker>();
238: this.tickers.put(bundle, tickers);
239: }
240: return tickers;
241: }
242:
243: public void stop() {
244: this.scheduledExecutorService.shutdown();
245: }
246:
247: private List<ServiceDependency> createServiceDependencies(Event event) {
248: String[] filters = (String[]) event.getProperty("dependencies");
249: String[] beanNames = (String[]) event.getProperty("bean.name");
250:
251: List<ServiceDependency> serviceDependencies = new ArrayList<ServiceDependency>();
252:• if (filters != null && beanNames != null) {
253:• for (int i = 0; i < filters.length; i++) {
254: serviceDependencies.add(new ServiceDependency(filters[i], beanNames[i]));
255: }
256: return serviceDependencies;
257: }
258:
259: /*
260: * Return null when filters is non-null and beanNames is null. Blueprint events sometimes lack this information,
261: * but a corresponding event including bean names is posted by
262: * BlueprintEventPostingOsgiBundleApplicationContextListener on receipt of the underlying Spring DM event. A
263: * return value of null indicates that the caller should ignore this event.
264: */
265:• return filters == null ? serviceDependencies : null;
266: }
267:
268: private static final class ServiceDependency {
269:
270: private final String filter;
271:
272: private final String beanName;
273:
274: private ServiceDependency(String filter, String beanName) {
275: this.filter = filter;
276: this.beanName = beanName;
277: }
278:
279: public String getFilter() {
280: return filter;
281: }
282:
283: public String getBeanName() {
284: return beanName;
285: }
286:
287: @Override
288: public int hashCode() {
289: final int prime = 31;
290: int result = 1;
291: result = prime * result + beanName.hashCode();
292: result = prime * result + filter.hashCode();
293: return result;
294: }
295:
296: @Override
297: public boolean equals(Object obj) {
298: if (this == obj)
299: return true;
300: if (obj == null)
301: return false;
302: if (getClass() != obj.getClass())
303: return false;
304:
305: ServiceDependency other = (ServiceDependency) obj;
306:
307: if (!beanName.equals(other.beanName))
308: return false;
309:
310: if (!filter.equals(other.filter))
311: return false;
312:
313: return true;
314: }
315:
316: public String toString() {
317: return this.filter + " " + this.beanName;
318: }
319: }
320: }