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