1 package com.atlassian.plugin.osgi.util;
2
3 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
4 import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
5 import com.atlassian.plugin.util.ClassLoaderUtils;
6 import com.atlassian.plugin.util.ClassUtils;
7
8 import java.util.*;
9 import java.util.jar.Manifest;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.BufferedReader;
13 import java.io.InputStreamReader;
14 import java.net.MalformedURLException;
15
16 import org.twdata.pkgscanner.ExportPackage;
17 import org.twdata.pkgscanner.PackageScanner;
18 import static org.twdata.pkgscanner.PackageScanner.jars;
19 import static org.twdata.pkgscanner.PackageScanner.include;
20 import static org.twdata.pkgscanner.PackageScanner.exclude;
21 import static org.twdata.pkgscanner.PackageScanner.packages;
22 import org.osgi.framework.Version;
23 import org.osgi.framework.Constants;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.commons.io.IOUtils;
27 import aQute.lib.osgi.Analyzer;
28 import aQute.lib.osgi.Jar;
29
30 import javax.servlet.ServletContext;
31
32
33
34
35 public class OsgiHeaderUtil
36 {
37 static final String JDK_PACKAGES_PATH = "jdk-packages.txt";
38 static final String JDK6_PACKAGES_PATH = "jdk6-packages.txt";
39 static Log log = LogFactory.getLog(OsgiHeaderUtil.class);
40
41
42
43
44
45
46
47
48 public static String findReferredPackages(List<HostComponentRegistration> registrations) throws IOException
49 {
50 StringBuffer sb = new StringBuffer();
51 Set<String> referredPackages = new HashSet<String>();
52 Set<String> referredClasses = new HashSet<String>();
53 if (registrations == null)
54 {
55 sb.append(",");
56 }
57 else
58 {
59 for (HostComponentRegistration reg : registrations)
60 {
61 Set<Class> classesToScan = new HashSet<Class>();
62
63
64 for (Class inf : reg.getMainInterfaceClasses())
65 ClassUtils.findAllTypes(inf, classesToScan);
66
67 for (Class inf : classesToScan)
68 {
69 String clsName = inf.getName().replace('.','/')+".class";
70 crawlReferenceTree(clsName, referredClasses, referredPackages, 1);
71 }
72 }
73 for (String pkg : referredPackages)
74 {
75 sb.append(pkg).append(",");
76 }
77 }
78 return sb.toString();
79 }
80
81
82
83
84
85
86
87
88
89
90 static void crawlReferenceTree(String className, Set<String> scannedClasses, Set<String> packageImports, int level) throws IOException
91 {
92 if (level <= 0)
93 {
94 return;
95 }
96
97 if (className.startsWith("java/"))
98 return;
99
100 if (scannedClasses.contains(className))
101 return;
102 else
103 scannedClasses.add(className);
104
105 if (log.isDebugEnabled())
106 log.debug("Crawling "+className);
107
108 InputStream in = ClassLoaderUtils.getResourceAsStream(className, OsgiHeaderUtil.class);
109 if (in == null)
110 {
111 log.error("Cannot find interface "+className);
112 return;
113 }
114 Clazz clz = new Clazz(className, in);
115 packageImports.addAll(clz.getReferred().keySet());
116
117 Set<String> referredClasses = clz.getReferredClasses();
118 for (String ref : referredClasses)
119 crawlReferenceTree(ref, scannedClasses, packageImports, level-1);
120
121 }
122
123
124
125
126
127
128
129
130 public static String determineExports(List<HostComponentRegistration> regs, PackageScannerConfiguration packageScannerConfig){
131 String exports = null;
132
133 StringBuilder origExports = new StringBuilder();
134 origExports.append("org.osgi.framework; version=1.4.1,");
135 origExports.append("org.osgi.service.packageadmin; version=1.2.0," );
136 origExports.append("org.osgi.service.startlevel; version=1.0.0,");
137 origExports.append("org.osgi.service.url; version=1.0.0,");
138 origExports.append("org.osgi.util; version=1.4.1,");
139 origExports.append("org.osgi.util.tracker; version=1.4.1,");
140 origExports.append("host.service.command; version=1.0.0,");
141
142 constructJdkExports(origExports, JDK_PACKAGES_PATH);
143 origExports.append(",");
144
145 if (System.getProperty("java.specification.version").equals("1.6")) {
146 constructJdkExports(origExports, JDK6_PACKAGES_PATH);
147 origExports.append(",");
148 }
149
150 Collection<ExportPackage> exportList = generateExports(packageScannerConfig);
151 constructAutoExports(origExports, exportList);
152
153
154 try
155 {
156 origExports.append(findReferredPackages(regs));
157
158 Analyzer analyzer = new Analyzer();
159 analyzer.setJar(new Jar("somename.jar"));
160
161
162
163 analyzer.setProperty(Constants.IMPORT_PACKAGE, origExports.toString());
164 Manifest mf = analyzer.calcManifest();
165
166 exports = mf.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
167 } catch (IOException ex)
168 {
169 log.error("Unable to calculate necessary exports based on host components", ex);
170 exports = origExports.toString();
171 }
172
173 if (log.isDebugEnabled()) {
174 log.debug("Exports:\n"+exports.replaceAll(",", "\r\n"));
175 }
176 return exports;
177 }
178
179 static void constructAutoExports(StringBuilder sb, Collection<ExportPackage> packageExports) {
180 for (Iterator<ExportPackage> i = packageExports.iterator(); i.hasNext(); ) {
181 ExportPackage pkg = i.next();
182 sb.append(pkg.getPackageName());
183 if (pkg.getVersion() != null) {
184 try {
185 Version.parseVersion(pkg.getVersion());
186 sb.append(";version=").append(pkg.getVersion());
187 } catch (IllegalArgumentException ex) {
188 log.info("Unable to parse version: "+pkg.getVersion());
189 }
190 }
191 sb.append(",");
192 }
193 }
194
195 static Collection<ExportPackage> generateExports(PackageScannerConfiguration packageScannerConfig)
196 {
197 String[] arrType = new String[0];
198
199 PackageScanner scanner = new PackageScanner()
200 .select(
201 jars(
202 include(packageScannerConfig.getJarIncludes().toArray(arrType)),
203 exclude(packageScannerConfig.getJarExcludes().toArray(arrType))),
204 packages(
205 include(packageScannerConfig.getPackageIncludes().toArray(arrType)),
206 exclude(packageScannerConfig.getPackageExcludes().toArray(arrType)))
207 )
208 .withMappings(packageScannerConfig.getPackageVersions());
209
210 Collection<ExportPackage> exports = scanner.scan();
211
212 if (!isPackageScanSuccessful(exports) && packageScannerConfig.getServletContext() != null)
213 {
214 log.warn("Unable to find expected packages via classloader scanning. Trying ServletContext scanning...");
215 ServletContext ctx = packageScannerConfig.getServletContext();
216 try
217 {
218 exports = scanner.scan(ctx.getResource("/WEB-INF/lib"), ctx.getResource("/WEB-INF/classes"));
219 }
220 catch (MalformedURLException e)
221 {
222 log.warn(e);
223 }
224 }
225
226 if (!isPackageScanSuccessful(exports))
227 {
228 throw new IllegalStateException("Unable to find required packages via classloader or servlet context"
229 + " scanning, most likely due to an application server bug.");
230 }
231 return exports;
232 }
233
234
235
236
237
238
239
240 private static boolean isPackageScanSuccessful(Collection<ExportPackage> exports)
241 {
242 boolean log4jFound = false;
243 for (ExportPackage export : exports)
244 {
245 if (export.getPackageName().equals("org.apache.log4j"))
246 {
247 log4jFound = true;
248 break;
249 }
250 }
251 return log4jFound;
252 }
253
254 static void constructJdkExports(StringBuilder sb, String packageListPath)
255 {
256 InputStream in = null;
257 try
258 {
259 in = ClassLoaderUtils.getResourceAsStream(packageListPath, OsgiHeaderUtil.class);
260 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
261 String line;
262 while ((line = reader.readLine()) != null)
263 {
264 line = line.trim();
265 if (line.length() > 0)
266 {
267 if (line.charAt(0) != '#')
268 {
269 if (sb.length() > 0)
270 sb.append(',');
271 sb.append(line);
272 }
273 }
274 }
275 } catch (IOException e)
276 {
277 IOUtils.closeQuietly(in);
278 }
279 }
280
281 }