1 package com.atlassian.activeobjects.osgi;
2
3 import com.atlassian.activeobjects.config.ActiveObjectsConfiguration;
4 import com.atlassian.activeobjects.internal.ActiveObjectsFactory;
5 import com.atlassian.activeobjects.osgi.ActiveObjectsServiceFactory.BundleRef;
6 import com.atlassian.activeobjects.plugin.ActiveObjectModuleDescriptor;
7 import com.atlassian.activeobjects.spi.ContextClassLoaderThreadFactory;
8 import com.atlassian.activeobjects.spi.InitExecutorServiceProvider;
9 import com.atlassian.event.api.EventPublisher;
10 import com.atlassian.plugin.ModuleDescriptor;
11 import com.atlassian.plugin.Plugin;
12 import com.atlassian.plugin.event.events.PluginDisabledEvent;
13 import com.atlassian.plugin.event.events.PluginEnabledEvent;
14 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
15 import com.atlassian.plugin.osgi.factory.OsgiPlugin;
16 import com.atlassian.sal.api.executor.ThreadLocalDelegateExecutorFactory;
17 import com.atlassian.tenancy.api.Tenant;
18 import com.atlassian.tenancy.api.TenantContext;
19 import org.junit.Before;
20 import org.junit.Rule;
21 import org.junit.Test;
22 import org.junit.rules.ExpectedException;
23 import org.junit.runner.RunWith;
24 import org.mockito.Mock;
25 import org.mockito.junit.MockitoJUnitRunner;
26 import org.osgi.framework.Bundle;
27
28 import java.util.Collections;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.TimeUnit;
32
33 import static org.hamcrest.CoreMatchers.is;
34 import static org.hamcrest.CoreMatchers.notNullValue;
35 import static org.hamcrest.CoreMatchers.sameInstance;
36 import static org.hamcrest.MatcherAssert.assertThat;
37 import static org.hamcrest.collection.IsMapContaining.hasEntry;
38 import static org.hamcrest.core.IsInstanceOf.instanceOf;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.when;
41
42 @RunWith(MockitoJUnitRunner.class)
43 public final class ActiveObjectsServiceFactoryTest {
44 private ActiveObjectsServiceFactory serviceFactory;
45
46 @Rule
47 public ExpectedException expectedException = ExpectedException.none();
48 @Mock
49 private ActiveObjectsFactory factory;
50 @Mock
51 private EventPublisher eventPublisher;
52 @Mock
53 private TenantContext tenantContext;
54 @Mock
55 private ThreadLocalDelegateExecutorFactory threadLocalDelegateExecutorFactory;
56 @Mock
57 private InitExecutorServiceProvider initExecutorServiceProvider;
58 @Mock
59 private Tenant tenant1;
60 @Mock
61 private Tenant tenant2;
62 @Mock
63 private Tenant tenant3;
64 @Mock
65 private ExecutorService executorService1;
66 @Mock
67 private ExecutorService executorService2;
68 @Mock
69 private ExecutorService executorService3;
70 @Mock
71 private Bundle bundle1;
72 @Mock
73 private Bundle bundle2;
74 @Mock
75 private TenantAwareActiveObjects babyBear1;
76 @Mock
77 private TenantAwareActiveObjects babyBear2;
78 @Mock
79 private PluginModuleEnabledEvent moduleEnabledEvent;
80 @Mock
81 private PluginEnabledEvent enabledEvent;
82 @Mock
83 private PluginDisabledEvent disabledEvent;
84 @Mock
85 private ActiveObjectModuleDescriptor moduleDescriptor;
86 @Mock
87 private OsgiPlugin plugin1;
88 @Mock
89 private Plugin nonOsgiPlugin;
90 @Mock
91 private ActiveObjectsConfiguration aoConfig1;
92 @Mock
93 private ActiveObjectsConfiguration aoConfig2;
94
95 @Before
96 public void setUp() {
97 serviceFactory = new ActiveObjectsServiceFactory(factory, eventPublisher, tenantContext,
98 threadLocalDelegateExecutorFactory, initExecutorServiceProvider);
99
100 assertThat(serviceFactory.aoContextThreadFactory, instanceOf(ContextClassLoaderThreadFactory.class));
101 assertThat(((ContextClassLoaderThreadFactory) serviceFactory.aoContextThreadFactory).getContextClassLoader(), sameInstance(Thread.currentThread().getContextClassLoader()));
102 assertThat(serviceFactory.destroying, is(false));
103 assertThat(serviceFactory.cleaning, is(false));
104
105 when(babyBear1.getBundle()).thenReturn(bundle1);
106 when(babyBear2.getBundle()).thenReturn(bundle2);
107
108 when(moduleEnabledEvent.getModule()).thenReturn((ModuleDescriptor) moduleDescriptor);
109 when(moduleDescriptor.getPlugin()).thenReturn(plugin1);
110 when(enabledEvent.getPlugin()).thenReturn(plugin1);
111 when(disabledEvent.getPlugin()).thenReturn(plugin1);
112 when(plugin1.getBundle()).thenReturn(bundle1);
113 when(moduleDescriptor.getConfiguration()).thenReturn(aoConfig1);
114 }
115
116 @Test
117 public void afterPropertiesSet() throws Exception {
118 serviceFactory.afterPropertiesSet();
119
120 verify(eventPublisher).register(serviceFactory);
121 }
122
123 @Test
124 public void destroy() throws Exception {
125 serviceFactory.initExecutorsByTenant.put(tenant1, executorService1);
126 serviceFactory.initExecutorsByTenant.put(tenant2, executorService2);
127 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
128 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle2), babyBear2);
129
130 serviceFactory.destroy();
131
132 assertThat(serviceFactory.destroying, is(true));
133
134 verify(eventPublisher).unregister(serviceFactory);
135 verify(babyBear1).destroy();
136 verify(babyBear2).destroy();
137 verify(executorService1).shutdownNow();
138 verify(executorService2).shutdownNow();
139 }
140
141 @Test
142 public void startCleaning() throws InterruptedException {
143 serviceFactory.initExecutorsByTenant.put(tenant1, executorService1);
144 serviceFactory.initExecutorsByTenant.put(tenant2, executorService2);
145 serviceFactory.initExecutorsByTenant.put(tenant3, executorService3);
146
147 when(executorService1.awaitTermination(ActiveObjectsServiceFactory.INIT_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS)).thenThrow(new InterruptedException());
148 when(executorService2.awaitTermination(ActiveObjectsServiceFactory.INIT_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS)).thenReturn(false);
149 when(executorService3.awaitTermination(ActiveObjectsServiceFactory.INIT_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS)).thenReturn(true);
150
151 serviceFactory.startCleaning();
152
153 assertThat(serviceFactory.cleaning, is(true));
154
155 verify(executorService1).shutdownNow();
156 verify(executorService2).shutdownNow();
157 verify(executorService3).shutdownNow();
158 verify(executorService1).awaitTermination(ActiveObjectsServiceFactory.INIT_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
159 verify(executorService2).awaitTermination(ActiveObjectsServiceFactory.INIT_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
160 verify(executorService3).awaitTermination(ActiveObjectsServiceFactory.INIT_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
161 }
162
163 @Test
164 public void initExecutorFn() {
165 serviceFactory.initExecutorsByTenant.put(tenant1, executorService1);
166 serviceFactory.initExecutorsByTenant.put(tenant2, executorService2);
167
168 assertThat(serviceFactory.initExecutorFn.apply(tenant1), sameInstance(executorService1));
169 }
170
171 @Test
172 public void initExecutorFnAfterDestroy() throws Exception {
173 serviceFactory.destroy();
174
175 expectedException.expect(IllegalStateException.class);
176
177 serviceFactory.initExecutorFn.apply(tenant1);
178 }
179
180 @Test
181 public void getService() {
182 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
183 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle2), babyBear2);
184
185 TenantAwareActiveObjects babyBear = (TenantAwareActiveObjects) serviceFactory.getService(bundle1, null);
186 assertThat(babyBear, sameInstance(babyBear1));
187 }
188
189 @Test
190 public void unGetService() {
191 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
192 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle2), babyBear2);
193
194 assertThat(serviceFactory.aoDelegatesByBundle.asMap(), hasEntry(new BundleRef(bundle1), babyBear1));
195 assertThat(serviceFactory.aoDelegatesByBundle.asMap(), hasEntry(new BundleRef(bundle2), babyBear2));
196 assertThat(serviceFactory.aoDelegatesByBundle.asMap().size(), is(2));
197
198 serviceFactory.ungetService(bundle1, null, babyBear1);
199
200 assertThat(serviceFactory.aoDelegatesByBundle.asMap(), hasEntry(new BundleRef(bundle2), babyBear2));
201 assertThat(serviceFactory.aoDelegatesByBundle.asMap().size(), is(1));
202
203 verify(babyBear1).destroy();
204 }
205
206 @Test
207 public void onTenantArrived() {
208 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
209 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle2), babyBear2);
210
211 when(tenantContext.getCurrentTenant()).thenReturn(tenant1);
212
213 serviceFactory.onTenantArrived(null);
214
215 verify(babyBear1).startActiveObjects(tenant1);
216 verify(babyBear2).startActiveObjects(tenant1);
217 }
218
219 @Test
220 public void onHotRestart() {
221 serviceFactory.initExecutorsByTenant.put(tenant1, executorService1);
222
223 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
224 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle2), babyBear2);
225
226 when(tenantContext.getCurrentTenant()).thenReturn(tenant1);
227
228 serviceFactory.onHotRestart(null);
229
230 verify(babyBear1).restartActiveObjects(tenant1);
231 verify(babyBear2).restartActiveObjects(tenant1);
232 verify(executorService1).shutdownNow();
233
234 assertThat(serviceFactory.initExecutorsByTenant.asMap(), is(Collections.<Tenant, ExecutorService>emptyMap()));
235 }
236
237 @Test
238 public void onPluginModuleEnabledEventNoDelegate() {
239 serviceFactory.onPluginModuleEnabledEvent(moduleEnabledEvent);
240
241 assertThat(serviceFactory.unattachedConfigByBundle, hasEntry(bundle1, aoConfig1));
242 }
243
244 @Test
245 public void onPluginModuleEnabledEventNonOsgi() {
246 when(moduleDescriptor.getPlugin()).thenReturn(nonOsgiPlugin);
247
248 serviceFactory.onPluginModuleEnabledEvent(moduleEnabledEvent);
249
250 assertThat(serviceFactory.unattachedConfigByBundle, is(Collections.<Bundle, ActiveObjectsConfiguration>emptyMap()));
251 }
252
253 @Test
254 public void onPluginModuleEnabledEventHasDelegate() {
255 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
256
257 serviceFactory.onPluginModuleEnabledEvent(moduleEnabledEvent);
258
259 assertThat(serviceFactory.unattachedConfigByBundle, is(Collections.<Bundle, ActiveObjectsConfiguration>emptyMap()));
260
261 verify(babyBear1).setAoConfiguration(aoConfig1);
262 }
263
264 @Test
265 public void onPluginEnabledAttachMatching() {
266 serviceFactory.unattachedConfigByBundle.put(bundle1, aoConfig1);
267
268 serviceFactory.onPluginEnabledEvent(enabledEvent);
269
270 assertThat(serviceFactory.unattachedConfigByBundle, is(Collections.<Bundle, ActiveObjectsConfiguration>emptyMap()));
271 }
272
273 @Test
274 public void onPluginEnabledIgnoreNonMatching() {
275 serviceFactory.unattachedConfigByBundle.put(bundle2, aoConfig1);
276
277 serviceFactory.onPluginEnabledEvent(enabledEvent);
278
279 assertThat(serviceFactory.unattachedConfigByBundle, hasEntry(bundle2, aoConfig1));
280 }
281
282 @Test
283 public void onPluginEnabledNonOsgi() {
284 when(enabledEvent.getPlugin()).thenReturn(nonOsgiPlugin);
285 serviceFactory.unattachedConfigByBundle.put(bundle1, aoConfig1);
286
287 serviceFactory.onPluginEnabledEvent(enabledEvent);
288
289 assertThat(serviceFactory.unattachedConfigByBundle, hasEntry(bundle1, aoConfig1));
290 }
291
292 @Test
293 public void onPluginDisabledEvent() {
294 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle1), babyBear1);
295 serviceFactory.aoDelegatesByBundle.put(new BundleRef(bundle2), babyBear2);
296 serviceFactory.unattachedConfigByBundle.put(bundle1, aoConfig1);
297 serviceFactory.unattachedConfigByBundle.put(bundle2, aoConfig2);
298
299 serviceFactory.onPluginDisabledEvent(disabledEvent);
300
301 assertThat(serviceFactory.aoDelegatesByBundle.asMap(), hasEntry(new BundleRef(bundle2), babyBear2));
302 assertThat(serviceFactory.aoDelegatesByBundle.asMap().size(), is(1));
303 assertThat(serviceFactory.unattachedConfigByBundle, hasEntry(bundle2, aoConfig2));
304 assertThat(serviceFactory.unattachedConfigByBundle.size(), is(1));
305 }
306
307 @Test
308 public void aoDelegatesByBundleLoader() throws ExecutionException, InterruptedException {
309 serviceFactory.unattachedConfigByBundle.put(bundle1, aoConfig1);
310
311 final TenantAwareActiveObjects aoDelegate = serviceFactory.aoDelegatesByBundle.get(new BundleRef(bundle1));
312
313 assertThat(aoDelegate, notNullValue());
314 assertThat(aoDelegate.aoConfigFuture.isDone(), is(true));
315 assertThat(aoDelegate.aoConfigFuture.get(), is(aoConfig1));
316 }
317 }