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