1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.PluginState;
4 import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
5 import com.atlassian.plugin.descriptors.RequiresRestart;
6 import com.atlassian.plugin.event.PluginEventManager;
7 import com.atlassian.plugin.event.events.PluginContainerRefreshedEvent;
8 import com.atlassian.plugin.module.ModuleFactory;
9 import com.atlassian.plugin.osgi.container.OsgiContainerException;
10 import com.atlassian.plugin.osgi.util.OsgiSystemBundleUtil;
11 import junit.framework.TestCase;
12 import org.mockito.invocation.InvocationOnMock;
13 import org.mockito.stubbing.Answer;
14 import org.osgi.framework.Bundle;
15 import org.osgi.framework.BundleContext;
16 import org.osgi.framework.BundleException;
17 import org.osgi.framework.Constants;
18
19 import java.util.Dictionary;
20 import java.util.Hashtable;
21
22 import static org.mockito.Mockito.*;
23
24 public class TestOsgiPlugin extends TestCase
25 {
26 private Bundle bundle;
27 private OsgiPlugin plugin;
28 private BundleContext bundleContext;
29 private Bundle systemBundle;
30 private BundleContext systemBundleContext;
31 private Dictionary<String, String> dict;
32 private OsgiPluginHelper helper;
33
34 @Override
35 public void setUp()
36 {
37 bundle = mock(Bundle.class);
38 dict = new Hashtable<String, String>();
39 dict.put(Constants.BUNDLE_DESCRIPTION, "desc");
40 dict.put(Constants.BUNDLE_VERSION, "1.0");
41 when(bundle.getHeaders()).thenReturn(dict);
42 bundleContext = mock(BundleContext.class);
43 when(bundle.getBundleContext()).thenReturn(bundleContext);
44
45 systemBundle = mock(Bundle.class);
46 systemBundleContext = mock(BundleContext.class);
47 when(bundleContext.getBundle(OsgiSystemBundleUtil.SYSTEM_BUNDLE_ID)).thenReturn(systemBundle);
48 when(systemBundle.getBundleContext()).thenReturn(systemBundleContext);
49
50 helper = mock(OsgiPluginHelper.class);
51 when(helper.getBundle()).thenReturn(bundle);
52
53 plugin = new OsgiPlugin(mock(PluginEventManager.class), helper);
54 }
55
56 @Override
57 public void tearDown()
58 {
59 bundle = null;
60 plugin = null;
61 bundleContext = null;
62 }
63
64 public void testEnabled() throws BundleException
65 {
66 _enablePlugin();
67 verify(bundle).start();
68 }
69
70 private void _enablePlugin() {
71 when(bundle.getState()).thenReturn(Bundle.RESOLVED);
72 plugin.enable();
73 }
74
75 public void testDisabled() throws BundleException
76 {
77 when(bundle.getState()).thenReturn(Bundle.ACTIVE);
78 plugin.disable();
79 verify(bundle).stop();
80 }
81
82 public void testDisabledOnNonDynamicPlugin() throws BundleException
83 {
84 plugin.addModuleDescriptor(new StaticModuleDescriptor());
85 plugin.onPluginFrameworkStartedEvent(null);
86 when(bundle.getState()).thenReturn(Bundle.ACTIVE);
87 plugin.disable();
88 verify(bundle, never()).stop();
89 }
90
91 public void testUninstall() throws BundleException
92 {
93 _enablePlugin();
94 when(bundle.getState()).thenReturn(Bundle.ACTIVE);
95 _assertPluginUninstalled();
96 }
97
98 public void testUninstallingUninstalledBundle() throws BundleException
99 {
100 _enablePlugin();
101 when(bundle.getState()).thenReturn(Bundle.ACTIVE).thenReturn(Bundle.UNINSTALLED);
102 _assertPluginUninstalled();
103 _assertPluginUninstalled();
104 }
105
106 private void _assertPluginUninstalled() {
107 plugin.uninstallInternal();
108 assertEquals(plugin.getPluginState(), PluginState.UNINSTALLED);
109 }
110
111 public void testOnPluginContainerRefresh()
112 {
113 plugin.setKey("plugin-key");
114 _enablePlugin();
115 PluginContainerRefreshedEvent event = new PluginContainerRefreshedEvent(new Object(), "plugin-key");
116 plugin.onPluginContainerRefresh(event);
117 assertEquals(PluginState.ENABLED, plugin.getPluginState());
118 }
119
120 public void testQuickOnPluginContainerRefresh() throws BundleException, InterruptedException
121 {
122 plugin.setKey("plugin-key");
123 when(bundle.getState()).thenReturn(Bundle.RESOLVED);
124
125 final ConcurrentStateEngine states = new ConcurrentStateEngine("bundle-starting", "container-created", "bundle-started", "mid-start", "end");
126 when(bundle.getBundleContext()).thenAnswer(new Answer<Object>()
127 {
128 public Object answer(InvocationOnMock invocation) throws Throwable
129 {
130 states.tryNextState("bundle-started", "mid-start");
131 final BundleContext context = mock(BundleContext.class);
132 when(context.getBundle(OsgiSystemBundleUtil.SYSTEM_BUNDLE_ID)).thenReturn(systemBundle);
133
134 return context;
135 }
136 });
137
138 doAnswer(new Answer<Object>()
139 {
140 public Object answer(InvocationOnMock invocation) throws Throwable
141 {
142 states.state("bundle-starting");
143 Thread t = new Thread()
144 {
145 public void run()
146 {
147 PluginContainerRefreshedEvent event = new PluginContainerRefreshedEvent(new Object(), "plugin-key");
148 states.tryNextState("bundle-starting", "container-created");
149 plugin.onPluginContainerRefresh(event);
150 }
151 };
152 t.start();
153 states.tryNextState("container-created", "bundle-started");
154 return null;
155 }
156 }).when(bundle).start();
157
158 plugin.enable();
159
160
161 states.tryNextState("mid-start", "end");
162
163 assertEquals(PluginState.ENABLED, plugin.getPluginState());
164 }
165
166 public void testOnPluginContainerRefreshNotEnabling()
167 {
168 plugin.setKey("plugin-key");
169 PluginContainerRefreshedEvent event = new PluginContainerRefreshedEvent(new Object(), "plugin-key");
170 when(bundle.getState()).thenReturn(Bundle.ACTIVE);
171 plugin.disable();
172 plugin.onPluginContainerRefresh(event);
173 assertEquals(PluginState.DISABLED, plugin.getPluginState());
174 }
175
176 public void testUninstallRetryLogic() throws BundleException
177 {
178 try {
179 when(bundle.getState()).thenReturn(Bundle.ACTIVE);
180 plugin.enable();
181 when(bundle.getSymbolicName()).thenReturn("Mock Bundle");
182 doThrow(new BundleException("Mock Bundle Exception")).when(bundle).uninstall();
183 plugin.uninstallInternal();
184 } catch (Exception e) {
185 assertTrue("Should throw an OsgiContainerException", e instanceof OsgiContainerException);
186 } finally {
187 assertEquals(PluginState.ENABLED, plugin.getPluginState());
188 verify(bundle, times(3)).uninstall();
189 }
190 }
191
192 @RequiresRestart
193 public static class StaticModuleDescriptor extends AbstractModuleDescriptor<Object>
194 {
195 public StaticModuleDescriptor()
196 {
197 super(ModuleFactory.LEGACY_MODULE_FACTORY);
198 }
199
200 public Object getModule()
201 {
202 return null;
203 }
204 }
205 }