1 package com.atlassian.plugin.osgi.container.felix;
2
3 import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
4 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
5 import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
6 import com.atlassian.plugin.util.PluginFrameworkUtils;
7 import com.google.common.base.Predicate;
8 import com.google.common.collect.Sets;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.twdata.pkgscanner.DefaultOsgiVersionConverter;
12 import org.twdata.pkgscanner.ExportPackage;
13 import org.twdata.pkgscanner.PackageScanner;
14
15 import static com.atlassian.plugin.osgi.container.felix.ExportBuilderUtils.parseExportFile;
16 import static com.atlassian.plugin.osgi.container.felix.ExportBuilderUtils.copyUnlessExist;
17 import static org.twdata.pkgscanner.PackageScanner.exclude;
18 import static org.twdata.pkgscanner.PackageScanner.include;
19 import static org.twdata.pkgscanner.PackageScanner.jars;
20 import static org.twdata.pkgscanner.PackageScanner.packages;
21
22 import javax.servlet.ServletContext;
23 import java.io.File;
24 import java.io.IOException;
25 import java.net.MalformedURLException;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31
32
33
34 class ExportsBuilder
35 {
36 static final String JDK_6 = "1.6";
37 static final String JDK_7 = "1.7";
38
39 static final String OSGI_PACKAGES_PATH = "osgi-packages.txt";
40 static final String JDK_PACKAGES_PATH = "jdk-packages.txt";
41
42 private static Logger log = LoggerFactory.getLogger(ExportsBuilder.class);
43 private static String exportStringCache;
44
45 private static final Predicate<String> UNDER_PLUGIN_FRAMEWORK = new Predicate<String>()
46 {
47 public boolean apply(String pkg)
48 {
49 return pkg.startsWith("com.atlassian.plugin.");
50 }
51 };
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public String getExports(List<HostComponentRegistration> regs, PackageScannerConfiguration packageScannerConfig)
67 {
68 if (exportStringCache == null)
69 {
70 exportStringCache = determineExports(regs, packageScannerConfig);
71 }
72 return exportStringCache;
73 }
74
75
76
77
78
79
80 public void clearExportCache()
81 {
82 exportStringCache = null;
83 }
84
85
86
87
88
89
90
91
92
93
94 @SuppressWarnings ({ "UnusedDeclaration" })
95 public String determineExports(List<HostComponentRegistration> regs, PackageScannerConfiguration packageScannerConfig, File cacheDir)
96 {
97 return determineExports(regs, packageScannerConfig);
98 }
99
100
101
102
103
104
105
106
107 String determineExports(List<HostComponentRegistration> regs, PackageScannerConfiguration packageScannerConfig)
108 {
109 Map<String, String> exportPackages = new HashMap<String, String>();
110
111
112 copyUnlessExist(exportPackages, parseExportFile(OSGI_PACKAGES_PATH));
113
114
115 copyUnlessExist(exportPackages, parseExportFile(JDK_PACKAGES_PATH));
116
117
118 Collection<ExportPackage> scannedPackages = generateExports(packageScannerConfig);
119 copyUnlessExist(exportPackages, ExportBuilderUtils.toMap(scannedPackages));
120
121
122 try
123 {
124 Map<String,String> referredPackages = OsgiHeaderUtil.findReferredPackageVersions(regs, packageScannerConfig.getPackageVersions());
125 copyUnlessExist(exportPackages, referredPackages);
126 }
127 catch (IOException ex)
128 {
129 log.error("Unable to calculate necessary exports based on host components", ex);
130 }
131
132
133 enforceFrameworkVersion(exportPackages);
134
135
136 final String exports = OsgiHeaderUtil.generatePackageVersionString(exportPackages);
137
138 if (log.isDebugEnabled())
139 {
140 log.debug("Exports:\n"+exports.replaceAll(",", "\r\n"));
141 }
142
143 return exports;
144 }
145
146 private void enforceFrameworkVersion(Map<String, String> exportPackages)
147 {
148 final String frameworkVersion = PluginFrameworkUtils.getPluginFrameworkVersion();
149
150
151 DefaultOsgiVersionConverter converter = new DefaultOsgiVersionConverter();
152 final String frameworkVersionOsgi = converter.getVersion(frameworkVersion);
153
154 for(String pkg: Sets.filter(exportPackages.keySet(), UNDER_PLUGIN_FRAMEWORK))
155 {
156 exportPackages.put(pkg, frameworkVersionOsgi);
157 }
158 }
159
160 Collection<ExportPackage> generateExports(PackageScannerConfiguration packageScannerConfig)
161 {
162 String[] arrType = new String[0];
163
164 Map<String,String> pkgVersions = new HashMap<String,String>(packageScannerConfig.getPackageVersions());
165 if (packageScannerConfig.getServletContext() != null)
166 {
167 String ver = packageScannerConfig.getServletContext().getMajorVersion() + "." + packageScannerConfig.getServletContext().getMinorVersion();
168 pkgVersions.put("javax.servlet*", ver);
169 }
170
171 PackageScanner scanner = new PackageScanner()
172 .select(
173 jars(
174 include(packageScannerConfig.getJarIncludes().toArray(arrType)),
175 exclude(packageScannerConfig.getJarExcludes().toArray(arrType))),
176 packages(
177 include(packageScannerConfig.getPackageIncludes().toArray(arrType)),
178 exclude(packageScannerConfig.getPackageExcludes().toArray(arrType)))
179 )
180 .withMappings(pkgVersions);
181
182 if (log.isDebugEnabled())
183 {
184 scanner.enableDebug();
185 }
186
187 Collection<ExportPackage> exports = scanner.scan();
188 log.info("Package scan completed. Found " + exports.size() + " packages to export.");
189
190 if (!isPackageScanSuccessful(exports) && packageScannerConfig.getServletContext() != null)
191 {
192 log.warn("Unable to find expected packages via classloader scanning. Trying ServletContext scanning...");
193 ServletContext ctx = packageScannerConfig.getServletContext();
194 try
195 {
196 exports = scanner.scan(ctx.getResource("/WEB-INF/lib"), ctx.getResource("/WEB-INF/classes"));
197 }
198 catch (MalformedURLException e)
199 {
200 log.warn("Unable to scan webapp for packages", e);
201 }
202 }
203
204 if (!isPackageScanSuccessful(exports))
205 {
206 throw new IllegalStateException("Unable to find required packages via classloader or servlet context"
207 + " scanning, most likely due to an application server bug.");
208 }
209 return exports;
210 }
211
212
213
214
215
216
217
218 private static boolean isPackageScanSuccessful(Collection<ExportPackage> exports)
219 {
220 boolean slf4jFound = false;
221 for (ExportPackage export : exports)
222 {
223 if (export.getPackageName().equals("org.slf4j"))
224 {
225 slf4jFound = true;
226 break;
227 }
228 }
229 return slf4jFound;
230 }
231 }