1 package com.atlassian.sal.core.lifecycle;
2
3 import java.util.Collection;
4 import java.util.Hashtable;
5
6 import com.atlassian.plugin.Plugin;
7 import com.atlassian.plugin.PluginAccessor;
8 import com.atlassian.plugin.PluginController;
9 import com.atlassian.plugin.event.PluginEventManager;
10 import com.atlassian.plugin.event.events.PluginEnabledEvent;
11 import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
12 import com.atlassian.plugin.osgi.factory.OsgiPlugin;
13 import com.atlassian.sal.api.lifecycle.LifecycleAware;
14
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableMap;
17
18 import org.junit.Before;
19 import org.junit.Test;
20 import org.junit.runner.RunWith;
21 import org.mockito.Mock;
22 import org.mockito.invocation.InvocationOnMock;
23 import org.mockito.runners.MockitoJUnitRunner;
24 import org.mockito.stubbing.Answer;
25 import org.osgi.framework.Bundle;
26 import org.osgi.framework.BundleContext;
27 import org.osgi.framework.ServiceEvent;
28 import org.osgi.framework.ServiceListener;
29 import org.osgi.framework.ServiceReference;
30
31 import static org.hamcrest.MatcherAssert.assertThat;
32 import static org.hamcrest.Matchers.is;
33 import static org.hamcrest.Matchers.notNullValue;
34 import static org.mockito.Matchers.any;
35 import static org.mockito.Matchers.anyString;
36 import static org.mockito.Matchers.same;
37 import static org.mockito.Mockito.doAnswer;
38 import static org.mockito.Mockito.doThrow;
39 import static org.mockito.Mockito.mock;
40 import static org.mockito.Mockito.never;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.when;
43
44 @RunWith(MockitoJUnitRunner.class)
45 public class TestDefaultLifecycleManager
46 {
47 private final String pluginKey = "pluginKey";
48
49 @Mock private PluginEventManager pluginEventManager;
50 @Mock private PluginAccessor pluginAccessor;
51 @Mock private BundleContext bundleContext;
52
53 @Mock private Bundle pluginBundle;
54 @Mock private PluginController pluginController;
55 private ServiceListener serviceListener;
56
57 private boolean isApplicationSetup;
58 private int notifyOnStartCalled;
59 private DefaultLifecycleManager defaultLifecycleManager;
60
61 @Before
62 public void setUp() throws Exception
63 {
64
65
66 isApplicationSetup = false;
67 defaultLifecycleManager = new DefaultLifecycleManager(pluginEventManager, pluginAccessor, bundleContext)
68 {
69 @Override
70 public boolean isApplicationSetUp()
71 {
72 return isApplicationSetup;
73 }
74
75 @Override
76 protected void notifyOnStart()
77 {
78 super.notifyOnStart();
79 ++notifyOnStartCalled;
80 }
81 };
82
83
84
85 pluginBundle = mockBundle(pluginKey);
86
87
88 final Answer answer = new Answer()
89 {
90 @Override
91 public Object answer(final InvocationOnMock invocation) throws Throwable
92 {
93 serviceListener = (ServiceListener) invocation.getArguments()[0];
94 return null;
95 }
96 };
97 doAnswer(answer).when(bundleContext).addServiceListener(any(ServiceListener.class), anyString());
98 }
99
100 @Test
101 public void onStartCalledForServicesRegisteredBeforeDefaultLifecycleManagerIsInitialized() throws Exception
102 {
103 final LifecycleAware lifecycleAware = mock(LifecycleAware.class);
104 final ServiceReference<LifecycleAware> service = registerService(pluginBundle, lifecycleAware);
105 when(bundleContext.getServiceReferences(LifecycleAware.class, null)).thenReturn(ImmutableList.of(service));
106
107 defaultLifecycleManager.postConstruct();
108
109 isApplicationSetup = true;
110 enablePlugin(pluginKey);
111
112
113 verify(lifecycleAware, never()).onStart();
114
115 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
116
117
118 verify(lifecycleAware).onStart();
119
120
121
122 verify(bundleContext).ungetService(service);
123 }
124
125 @Test
126 public void onStartCalledWhenPluginFrameworkStartedIfApplicationIsSetup() throws Exception
127 {
128 defaultLifecycleManager.postConstruct();
129
130 final LifecycleAware lifecycleAware = mockLifecycleAwareAndRegisterService(pluginBundle);
131 enablePlugin(pluginKey);
132
133
134 verify(lifecycleAware, never()).onStart();
135
136 isApplicationSetup = true;
137 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
138
139
140 verify(lifecycleAware).onStart();
141 }
142
143 @Test
144 public void onStartCalledWhenStartCalledIfApplicationIsSetupAfterPluginFrameworkStarted() throws Exception
145 {
146 defaultLifecycleManager.postConstruct();
147
148 final LifecycleAware lifecycleAware = mockLifecycleAwareAndRegisterService(pluginBundle);
149 enablePlugin(pluginKey);
150
151 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
152
153
154 verify(lifecycleAware, never()).onStart();
155
156 isApplicationSetup = true;
157 defaultLifecycleManager.start();
158
159
160 verify(lifecycleAware).onStart();
161 }
162
163 @Test
164 public void onStartCalledWhenPluginEnabledAfterStarted() throws Exception
165 {
166 defaultLifecycleManager.postConstruct();
167
168 final LifecycleAware lifecycleAware = mockLifecycleAwareAndRegisterService(pluginBundle);
169 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
170 isApplicationSetup = true;
171 defaultLifecycleManager.start();
172
173
174 verify(lifecycleAware, never()).onStart();
175
176 enablePlugin(pluginKey);
177
178
179 verify(lifecycleAware).onStart();
180 }
181
182 @Test
183 public void onStartCalledWhenLifecycleAwareRegisteredAfterPluginEnabled() throws Exception
184 {
185 defaultLifecycleManager.postConstruct();
186
187 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
188 isApplicationSetup = true;
189 defaultLifecycleManager.start();
190
191 enablePlugin(pluginKey);
192
193 final LifecycleAware lifecycleAware = mockLifecycleAwareAndRegisterService(pluginBundle);
194
195 verify(lifecycleAware).onStart();
196 }
197
198 @Test
199 public void notifyOnStartCalledWhenPluginFrameworkStartedWhenSetup() throws Exception
200 {
201 defaultLifecycleManager.postConstruct();
202
203 isApplicationSetup = true;
204 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
205
206 assertThat(notifyOnStartCalled, is(1));
207 }
208
209 @Test
210 public void notifyOnStartCalledWhenStartCalledIfApplicationIsSetupAfterPluginFrameworkStarted() throws Exception
211 {
212 defaultLifecycleManager.postConstruct();
213
214 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
215
216
217 assertThat(notifyOnStartCalled, is(0));
218
219 isApplicationSetup = true;
220 defaultLifecycleManager.start();
221
222 assertThat(notifyOnStartCalled, is(1));
223 }
224
225 @Test
226 public void notifyOnStartOnlyCalledOnceEvenWhenBothPluginFrameworkStartedAndStartCalledWhenSetup() throws Exception
227 {
228 defaultLifecycleManager.postConstruct();
229
230 isApplicationSetup = true;
231 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
232 defaultLifecycleManager.start();
233
234 assertThat(notifyOnStartCalled, is(1));
235 }
236
237 @Test
238 public void onStartNotCalledWhenLifecycleAwareUnregisteredBeforeStart() throws Exception
239 {
240 defaultLifecycleManager.postConstruct();
241
242 final LifecycleAware lifecycleAware = mock(LifecycleAware.class);
243 final ServiceReference serviceReference = registerService(pluginBundle, lifecycleAware);
244 unregisterService(serviceReference);
245
246 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
247 isApplicationSetup = true;
248 defaultLifecycleManager.start();
249 }
250
251 @Test
252 public void verifyListenerRegistrationManagement() throws Exception
253 {
254 defaultLifecycleManager.postConstruct();
255
256 assertThat(serviceListener, notNullValue());
257
258 verify(pluginEventManager).register(defaultLifecycleManager);
259
260
261 verify(bundleContext).addServiceListener(same(serviceListener), anyString());
262
263
264 verify(pluginEventManager, never()).unregister(defaultLifecycleManager);
265 verify(bundleContext, never()).removeServiceListener(serviceListener);
266
267 defaultLifecycleManager.preDestroy();
268
269 verify(pluginEventManager).unregister(defaultLifecycleManager);
270 verify(bundleContext).removeServiceListener(serviceListener);
271 }
272
273 @Test
274 public void serviceDeregisteredJustAfterPostConstructSnapshotIsNotLeaked() throws Exception
275 {
276
277 final LifecycleAware lifecycleAware = mock(LifecycleAware.class);
278 final ServiceReference<LifecycleAware> serviceReference = registerService(pluginBundle, lifecycleAware);
279
280 when(bundleContext.getServiceReferences(LifecycleAware.class, null)).thenAnswer(
281 new Answer<Collection<ServiceReference<LifecycleAware>>>()
282 {
283 @Override
284 public Collection<ServiceReference<LifecycleAware>> answer(final InvocationOnMock invocation)
285 throws Throwable
286 {
287
288 final ImmutableList<ServiceReference<LifecycleAware>> services = ImmutableList.of(serviceReference);
289 unregisterService(serviceReference);
290 return services;
291 }
292 });
293
294 defaultLifecycleManager.postConstruct();
295
296
297
298 enablePlugin(pluginKey);
299 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
300 isApplicationSetup = true;
301 defaultLifecycleManager.start();
302
303
304 verify(lifecycleAware, never()).onStart();
305 }
306
307 @Test
308 public void onStartExceptionsAreCaught() throws Exception
309 {
310
311
312 defaultLifecycleManager.postConstruct();
313
314 final LifecycleAware lifecycleAware = mock(LifecycleAware.class);
315 final ServiceReference<LifecycleAware> service = registerService(pluginBundle, lifecycleAware);
316 doThrow(new RuntimeException("Test onStart failure")).when(lifecycleAware).onStart();
317 enablePlugin(pluginKey);
318
319
320 verify(lifecycleAware, never()).onStart();
321
322 isApplicationSetup = true;
323 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
324
325
326 verify(lifecycleAware).onStart();
327
328 verify(bundleContext).ungetService(service);
329 }
330
331 @Test
332 public void staleServiceReferencesReturningNullBundleAndLifecycleAreHandled() throws Exception
333 {
334 verifyStaleServiceReferencesAreHandled(true);
335 }
336
337 @Test
338 public void staleServiceReferencesReturningNullLifecycleAreHandled() throws Exception
339 {
340 verifyStaleServiceReferencesAreHandled(false);
341 }
342
343 private void verifyStaleServiceReferencesAreHandled(final boolean makeBundleNull) throws Exception
344 {
345
346
347 defaultLifecycleManager.postConstruct();
348
349 final LifecycleAware lifecycleAware = mock(LifecycleAware.class);
350 final ServiceReference<LifecycleAware> service = registerService(pluginBundle, lifecycleAware);
351 enablePlugin(pluginKey);
352
353 if (makeBundleNull)
354 {
355 when(service.getBundle()).thenReturn(null);
356 }
357 when(bundleContext.getService(service)).thenReturn(null);
358
359 isApplicationSetup = true;
360 defaultLifecycleManager.onPluginFrameworkStarted(new PluginFrameworkStartedEvent(pluginController, pluginAccessor));
361
362
363
364
365 verify(lifecycleAware, never()).onStart();
366
367 verify(bundleContext, never()).ungetService(service);
368 }
369
370 private void enablePlugin(final String pluginKey)
371 {
372
373 when(pluginAccessor.isPluginEnabled(pluginKey)).thenReturn(true);
374
375 defaultLifecycleManager.onPluginEnabled(new PluginEnabledEvent(mock(Plugin.class)));
376 }
377
378 private Bundle mockBundle(final String pluginKey)
379 {
380 final Bundle bundle = mock(Bundle.class);
381 final ImmutableMap<String, String> headers = ImmutableMap.<String, String>builder()
382 .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, pluginKey)
383 .build();
384 when(bundle.getHeaders()).thenReturn(new Hashtable<String, String>(headers));
385 return bundle;
386 }
387
388 private LifecycleAware mockLifecycleAwareAndRegisterService(final Bundle bundle)
389 {
390 final LifecycleAware lifecycleAware = mock(LifecycleAware.class);
391 registerService(bundle, lifecycleAware);
392 return lifecycleAware;
393 }
394
395 private ServiceReference<LifecycleAware> registerService(final Bundle bundle, final LifecycleAware lifecycleAware)
396 {
397 @SuppressWarnings("unchecked")
398
399 final ServiceReference<LifecycleAware> serviceReference = mock(ServiceReference.class);
400 when(serviceReference.getBundle()).thenReturn(bundle);
401 when(bundleContext.getService(serviceReference)).thenReturn(lifecycleAware);
402
403 if (null != serviceListener)
404 {
405 serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, serviceReference));
406 }
407 return serviceReference;
408 }
409
410 private void unregisterService(final ServiceReference serviceReference)
411 {
412
413 if (null != serviceListener)
414 {
415 serviceListener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, serviceReference));
416 }
417 when(serviceReference.getBundle()).thenReturn(null);
418 }
419 }