1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.IllegalPluginStateException;
4 import com.atlassian.plugin.PluginException;
5 import com.atlassian.plugin.module.ContainerAccessor;
6 import com.atlassian.plugin.osgi.container.OsgiContainerException;
7 import com.atlassian.plugin.osgi.spring.DefaultSpringContainerAccessor;
8 import com.atlassian.plugin.osgi.util.BundleClassLoaderAccessor;
9 import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
10 import com.atlassian.plugin.util.resource.AlternativeDirectoryResourceLoader;
11 import com.google.common.base.Function;
12 import org.apache.felix.framework.resolver.Module;
13 import org.apache.felix.framework.resolver.Wire;
14 import org.osgi.framework.Bundle;
15 import org.osgi.service.packageadmin.PackageAdmin;
16 import org.osgi.util.tracker.ServiceTracker;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import java.io.InputStream;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.net.URL;
24 import java.util.Collections;
25 import java.util.Dictionary;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Set;
29
30 import static com.atlassian.fugue.Option.option;
31 import static com.atlassian.fugue.Suppliers.alwaysFalse;
32 import static com.google.common.base.Preconditions.checkNotNull;
33
34
35
36
37
38
39 final class OsgiPluginInstalledHelper implements OsgiPluginHelper
40 {
41 private static final Logger logger = LoggerFactory.getLogger(OsgiPluginInstalledHelper.class);
42 private final ClassLoader bundleClassLoader;
43 private final Bundle bundle;
44 private final PackageAdmin packageAdmin;
45 private volatile ContainerAccessor containerAccessor;
46 private volatile ServiceTracker[] serviceTrackers;
47
48
49
50
51
52 public OsgiPluginInstalledHelper(final Bundle bundle, final PackageAdmin packageAdmin)
53 {
54 this.bundle = checkNotNull(bundle);
55 this.packageAdmin = checkNotNull(packageAdmin);
56 bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
57 }
58
59 public Bundle getBundle()
60 {
61 return bundle;
62 }
63
64 public <T> Class<T> loadClass(final String clazz, final Class<?> callingClass) throws ClassNotFoundException
65 {
66 return BundleClassLoaderAccessor.loadClass(getBundle(), clazz);
67 }
68
69 public URL getResource(final String name)
70 {
71 return bundleClassLoader.getResource(name);
72 }
73
74 public InputStream getResourceAsStream(final String name)
75 {
76 return bundleClassLoader.getResourceAsStream(name);
77 }
78
79 public ClassLoader getClassLoader()
80 {
81 return bundleClassLoader;
82 }
83
84 public Bundle install()
85 {
86 logger.debug("Not installing OSGi plugin '{}' since it's already installed.", bundle.getSymbolicName());
87 throw new IllegalPluginStateException("Plugin '" + bundle.getSymbolicName() + "' has already been installed");
88 }
89
90 public void onEnable(final ServiceTracker... serviceTrackers) throws OsgiContainerException
91 {
92 for (final ServiceTracker svc : checkNotNull(serviceTrackers))
93 {
94 svc.open();
95 }
96
97 this.serviceTrackers = serviceTrackers;
98 }
99
100 public void onDisable() throws OsgiContainerException
101 {
102 final ServiceTracker[] serviceTrackers = this.serviceTrackers;
103 if (serviceTrackers != null)
104 {
105 for (final ServiceTracker svc : serviceTrackers)
106 {
107 svc.close();
108 }
109 this.serviceTrackers = null;
110 }
111 setPluginContainer(null);
112 }
113
114 public void onUninstall() throws OsgiContainerException
115 {
116 }
117
118 public Set<String> getRequiredPlugins()
119 {
120
121 if (bundle.getState() == Bundle.INSTALLED)
122 {
123 logger.debug("Bundle is in INSTALLED for {}", bundle.getSymbolicName());
124 if (!packageAdmin.resolveBundles(new Bundle[] { bundle }))
125 {
126
127 logger.error("Cannot determine required plugins, cannot resolve bundle '{}'", bundle.getSymbolicName());
128 return Collections.emptySet();
129 }
130 logger.debug("Bundle state is now {}", bundle.getState());
131 }
132
133 return getRequiredPluginKeys(bundle);
134 }
135
136
137
138
139
140
141
142
143 private Set<String> getRequiredPluginKeys(final Bundle bundle)
144 {
145 String errMsg = null;
146 final Set<String> list = new HashSet<String>();
147 try
148 {
149
150
151
152
153 final Method getModules = bundle.getClass().getDeclaredMethod("getModules");
154 getModules.setAccessible(true);
155
156 final List<Module> modules = (List<Module>) getModules.invoke(bundle);
157
158 for (final Module module : modules)
159 {
160 final List<Wire> wires = module.getWires();
161
162 for (final Wire wire : wires)
163 {
164 list.add(OsgiHeaderUtil.getPluginKey(wire.getExporter().getBundle()));
165 }
166 }
167 }
168 catch (final InvocationTargetException ignored)
169 {
170 errMsg = "Completely unexpected InvocationTargetException during Felix Bundle getModules(). Has the underlying Felix changed since this code was written?";
171 }
172 catch (final NoSuchMethodException ignored)
173 {
174 errMsg = "Completely unexpected NoSuchMethodException during Felix Bundle getModules(). Has the underlying Felix changed since this code was written?";
175 }
176 catch (final IllegalAccessException ignored)
177 {
178 errMsg = "Completely unexpected IllegalAccessException during Felix Bundle getModules(). Has the underlying Felix changed since this code was written?";
179 }
180
181
182
183 if (errMsg != null)
184 {
185 throw new IllegalStateException(errMsg);
186 }
187
188 return list;
189 }
190
191 public void setPluginContainer(final Object container)
192 {
193 if (container == null)
194 {
195 containerAccessor = null;
196 }
197 else if (container instanceof ContainerAccessor)
198 {
199 containerAccessor = (ContainerAccessor) container;
200 }
201 else
202 {
203 containerAccessor = new DefaultSpringContainerAccessor(container);
204 }
205 }
206
207 public ContainerAccessor getContainerAccessor()
208 {
209 return containerAccessor;
210 }
211
212
213
214
215 public ContainerAccessor getRequiredContainerAccessor() throws IllegalPluginStateException
216 {
217 if (containerAccessor == null)
218 {
219 throw new IllegalStateException("Cannot create object because the plugin container is unavailable for bundle '"
220 + bundle.getSymbolicName() + "'");
221 }
222 return containerAccessor;
223 }
224
225 public boolean isRemotePlugin()
226 {
227 return option(getBundle().getHeaders()).fold(alwaysFalse(), new Function<Dictionary, Boolean>()
228 {
229 @Override
230 public Boolean apply(final Dictionary headers)
231 {
232 return headers.get(OsgiPlugin.REMOTE_PLUGIN_KEY) != null;
233 }
234 });
235 }
236 }