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