1 package com.atlassian.plugin.osgi.util;
2
3 import aQute.lib.osgi.Clazz;
4 import aQute.libg.header.OSGiHeader;
5 import com.atlassian.plugin.osgi.factory.OsgiPlugin;
6 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
7 import com.atlassian.plugin.util.ClassLoaderUtils;
8 import com.atlassian.plugin.util.ClassUtils;
9 import com.atlassian.plugin.osgi.util.ClassBinaryScanner.InputStreamResource;
10 import com.atlassian.plugin.osgi.util.ClassBinaryScanner.ScanResult;
11 import com.google.common.base.Predicate;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Sets;
15 import org.osgi.framework.Bundle;
16 import org.osgi.framework.Constants;
17 import org.osgi.framework.Version;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.jar.Manifest;
32
33
34
35
36 public class OsgiHeaderUtil
37 {
38 static Logger log = LoggerFactory.getLogger(OsgiHeaderUtil.class);
39 private static final String EMPTY_OSGI_VERSION = Version.emptyVersion.toString();
40
41
42
43
44
45
46
47
48
49
50 public static Set<String> findReferredPackageNames(List<HostComponentRegistration> registrations) throws IOException
51 {
52 return findReferredPackagesInternal(registrations);
53 }
54
55
56
57
58
59
60
61
62
63 public static Map<String, String> findReferredPackageVersions(List<HostComponentRegistration> registrations, Map<String, String> packageVersions) throws IOException
64 {
65 Set<String> referredPackages = findReferredPackagesInternal(registrations);
66 return matchPackageVersions(referredPackages, packageVersions);
67 }
68
69
70
71
72
73
74
75
76
77
78 @Deprecated
79 public static String findReferredPackages(List<HostComponentRegistration> registrations) throws IOException
80 {
81 return findReferredPackages(registrations, Collections.<String, String>emptyMap());
82 }
83
84
85
86
87
88
89
90
91
92 @Deprecated
93 public static String findReferredPackages(List<HostComponentRegistration> registrations, Map<String, String> packageVersions) throws IOException
94 {
95 StringBuffer sb = new StringBuffer();
96 Set<String> referredPackages = new HashSet<String>();
97 Set<String> referredClasses = new HashSet<String>();
98 if (registrations == null)
99 {
100 sb.append(",");
101 }
102 else
103 {
104 for (HostComponentRegistration reg : registrations)
105 {
106 Set<Class> classesToScan = new HashSet<Class>();
107
108
109 for (Class inf : reg.getMainInterfaceClasses())
110 ClassUtils.findAllTypes(inf, classesToScan);
111
112 for (Class inf : classesToScan)
113 {
114 String clsName = inf.getName().replace('.','/')+".class";
115 crawlReferenceTree(clsName, referredClasses, referredPackages, 1);
116 }
117 }
118 for (String pkg : referredPackages)
119 {
120 String version = packageVersions.get(pkg);
121 sb.append(pkg);
122 if (version != null) {
123 try {
124 Version.parseVersion(version);
125 sb.append(";version=").append(version);
126 } catch (IllegalArgumentException ex) {
127 log.info("Unable to parse version: "+version);
128 }
129 }
130 sb.append(",");
131 }
132 }
133 return sb.toString();
134 }
135
136 static Map<String, String> matchPackageVersions(Set<String> packageNames, Map<String, String> packageVersions)
137 {
138 Map<String, String> output = new HashMap<String, String>();
139
140 for (String pkg : packageNames)
141 {
142 String version = packageVersions.get(pkg);
143
144 String effectiveKey = pkg;
145 String effectiveValue = EMPTY_OSGI_VERSION;
146
147 if (version != null)
148 {
149 try
150 {
151 Version.parseVersion(version);
152 effectiveValue = version;
153 }
154 catch (IllegalArgumentException ex)
155 {
156 log.info("Unable to parse version: "+version);
157 }
158 }
159 output.put(effectiveKey, effectiveValue);
160 }
161
162 return ImmutableMap.copyOf(output);
163 }
164
165 static Set<String> findReferredPackagesInternal(List<HostComponentRegistration> registrations) throws IOException
166 {
167 final Set<String> referredPackages = new HashSet<String>();
168 final Set<String> referredClasses = new HashSet<String>();
169
170 if (registrations != null)
171 {
172 for (HostComponentRegistration reg : registrations)
173 {
174 Set<Class> classesToScan = new HashSet<Class>();
175
176
177 for (Class inf : reg.getMainInterfaceClasses())
178 {
179 ClassUtils.findAllTypes(inf, classesToScan);
180 }
181
182 for (Class inf : classesToScan)
183 {
184 String clsName = inf.getName().replace('.','/')+".class";
185 crawlReferenceTree(clsName, referredClasses, referredPackages, 1);
186 }
187 }
188 }
189
190 return ImmutableSet.copyOf(referredPackages);
191 }
192
193
194
195
196 private static final Predicate<String> JAVA_PACKAGE_FILTER = new Predicate<String>()
197 {
198 public boolean apply(String pkg)
199 {
200 return !pkg.startsWith("java.");
201 }
202 };
203
204
205
206
207 private static final Predicate<String> JAVA_CLASS_FILTER = new Predicate<String>()
208 {
209 public boolean apply(String classEntry)
210 {
211 return !classEntry.startsWith("java/");
212 }
213 };
214
215
216
217
218
219
220
221
222
223
224 static void crawlReferenceTree(String className, Set<String> scannedClasses, Set<String> packageImports, int level) throws IOException
225 {
226 if (level <= 0)
227 {
228 return;
229 }
230
231 if (className.startsWith("java/"))
232 return;
233
234 if (scannedClasses.contains(className))
235 return;
236 else
237 scannedClasses.add(className);
238
239 if (log.isDebugEnabled())
240 log.debug("Crawling "+className);
241
242 InputStreamResource classBinaryResource = null;
243 try
244 {
245
246 InputStream in = ClassLoaderUtils.getResourceAsStream(className, OsgiHeaderUtil.class);
247 if (in == null)
248 {
249 log.error("Cannot find class: [" + className + "]");
250 return;
251 }
252
253
254 classBinaryResource = new InputStreamResource(in);
255 final ScanResult scanResult = ClassBinaryScanner.scanClassBinary(new Clazz(className, classBinaryResource));
256
257
258 packageImports.addAll(Sets.filter(scanResult.getReferredPackages(), JAVA_PACKAGE_FILTER));
259
260
261 Set<String> referredClasses = Sets.filter(scanResult.getReferredClasses(), JAVA_CLASS_FILTER);
262 for (String ref : referredClasses)
263 {
264 crawlReferenceTree(ref + ".class", scannedClasses, packageImports, level-1);
265 }
266 }
267 finally
268 {
269 if (classBinaryResource != null)
270 {
271 classBinaryResource.close();
272 }
273 }
274 }
275
276
277
278
279
280
281
282
283
284 public static Map<String,Map<String,String>> parseHeader(String header)
285 {
286 return OSGiHeader.parseHeader(header);
287 }
288
289
290
291
292
293
294
295 public static String buildHeader(Map<String,Map<String,String>> values)
296 {
297 StringBuilder header = new StringBuilder();
298 for (Iterator<Map.Entry<String,Map<String,String>>> i = values.entrySet().iterator(); i.hasNext(); )
299 {
300 Map.Entry<String,Map<String,String>> entry = i.next();
301 buildHeader(entry.getKey(), entry.getValue(), header);
302 if (i.hasNext())
303 {
304 header.append(",");
305 }
306 }
307 return header.toString();
308 }
309
310
311
312
313
314
315
316
317 public static String buildHeader(String key, Map<String,String> attrs)
318 {
319 StringBuilder fullPkg = new StringBuilder();
320 buildHeader(key, attrs, fullPkg);
321 return fullPkg.toString();
322 }
323
324
325
326
327
328 private static void buildHeader(String key, Map<String,String> attrs, StringBuilder builder)
329 {
330 builder.append(key);
331 if (attrs != null && !attrs.isEmpty())
332 {
333 for (Map.Entry<String,String> entry : attrs.entrySet())
334 {
335 builder.append(";");
336 builder.append(entry.getKey());
337 builder.append("=\"");
338 builder.append(entry.getValue());
339 builder.append("\"");
340 }
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356 public static String getPluginKey(Bundle bundle)
357 {
358 return getPluginKey(
359 bundle.getSymbolicName(),
360 bundle.getHeaders().get(OsgiPlugin.ATLASSIAN_PLUGIN_KEY),
361 bundle.getHeaders().get(Constants.BUNDLE_VERSION)
362 );
363 }
364
365
366
367
368
369
370
371
372 public static String getPluginKey(Manifest mf)
373 {
374 return getPluginKey(
375 mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME),
376 mf.getMainAttributes().getValue(OsgiPlugin.ATLASSIAN_PLUGIN_KEY),
377 mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION)
378 );
379 }
380
381 private static String getPluginKey(Object bundleName, Object atlKey, Object version)
382 {
383 Object key = atlKey;
384 if (key == null)
385 {
386 key = bundleName + "-" + version;
387 }
388
389 return key.toString();
390 }
391
392
393
394
395
396
397
398 public static String generatePackageVersionString(Map<String, String> packages)
399 {
400 if (packages == null || packages.size()==0)
401 {
402 return "";
403 }
404
405 final StringBuilder sb = new StringBuilder();
406
407
408 List<String> packageNames = new ArrayList<String>(packages.keySet());
409 Collections.sort(packageNames);
410
411 for(String packageName:packageNames)
412 {
413 sb.append(",");
414 sb.append(packageName);
415
416 String version = packages.get(packageName);
417
418
419 if (version != null && !version.equals(EMPTY_OSGI_VERSION))
420 {
421 sb.append(";version=").append(version);
422 }
423 }
424
425
426 sb.delete(0, 1);
427
428 return sb.toString();
429 }
430
431 }