PageRenderTime 52ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/nbbuild/antsrc/org/netbeans/nbbuild/MakeOSGi.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 956 lines | 813 code | 26 blank | 117 comment | 182 complexity | d341c7be4959de21ba8467f110b148cb MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * If you wish your version of this file to be governed by only the CDDL
  28. * or only the GPL Version 2, indicate your decision by adding
  29. * "[Contributor] elects to include this software in this distribution
  30. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  31. * single choice of license, a recipient has the option to distribute
  32. * your version of this file under either the CDDL, the GPL Version 2 or
  33. * to extend the choice of license to its licensees as provided above.
  34. * However, if you add GPL Version 2 code and therefore, elected the GPL
  35. * Version 2 license, then the option applies only if the new code is
  36. * made subject to such option by the copyright holder.
  37. *
  38. * Contributor(s):
  39. *
  40. * Portions Copyrighted 2010 Sun Microsystems, Inc.
  41. */
  42. package org.netbeans.nbbuild;
  43. import java.io.ByteArrayOutputStream;
  44. import java.io.File;
  45. import java.io.FileInputStream;
  46. import java.io.FileOutputStream;
  47. import java.io.IOException;
  48. import java.io.InputStream;
  49. import java.io.OutputStream;
  50. import java.net.URI;
  51. import java.util.ArrayList;
  52. import java.util.Arrays;
  53. import java.util.Collections;
  54. import java.util.Enumeration;
  55. import java.util.HashMap;
  56. import java.util.HashSet;
  57. import java.util.Iterator;
  58. import java.util.List;
  59. import java.util.Map;
  60. import java.util.Properties;
  61. import java.util.Set;
  62. import java.util.TreeMap;
  63. import java.util.TreeSet;
  64. import java.util.jar.Attributes;
  65. import java.util.jar.JarFile;
  66. import java.util.jar.JarOutputStream;
  67. import java.util.jar.Manifest;
  68. import java.util.regex.Matcher;
  69. import java.util.regex.Pattern;
  70. import java.util.zip.CRC32;
  71. import java.util.zip.ZipEntry;
  72. import java.util.zip.ZipOutputStream;
  73. import org.apache.tools.ant.BuildException;
  74. import org.apache.tools.ant.Project;
  75. import org.apache.tools.ant.Task;
  76. import org.apache.tools.ant.taskdefs.Copy;
  77. import org.apache.tools.ant.types.ResourceCollection;
  78. import org.apache.tools.ant.types.resources.FileResource;
  79. import org.w3c.dom.Document;
  80. import org.w3c.dom.Element;
  81. import org.w3c.dom.NodeList;
  82. import org.xml.sax.InputSource;
  83. /**
  84. * Converts a set of NetBeans modules into OSGi bundles.
  85. * Since there are some aspects of the translation that must be sensitive to
  86. * context in order to preserve as closely as possible the semantics of the
  87. * NetBeans module system, processing proceeds in two phases:
  88. * <ol>
  89. * <li>Each module in the input is opened and scanned for packages which it defines
  90. * (including in its {@code Class-Path} extensions), and for packages which it
  91. * (statically) refers to (not including packages it itself defines, or any
  92. * packages in the {@code java.*} namespace). {@code OpenIDE-Module-Hide-Classpath-Packages}
  93. * declarations are also tracked, and a list of packages which seem to be
  94. * exported from this module (if any) is collected.
  95. * <li>Each module in the input is reopened. Now it is actually converted to an
  96. * OSGi bundle. Package import and export information from the first phase is
  97. * used to decide how to represent this bundle's imports:
  98. * <ol>
  99. * <li>Packages hidden by this bundle, or one of its direct dependencies, are
  100. * never to be represented in the OSGi manifest.
  101. * <li>Packages exported by one of this bundle's dependencies can also be skipped,
  102. * since {@code Require-Bundle} will pick them all up.
  103. * <li>Packages in the {@code org.osgi.*} namespace can be imported.
  104. * <li>Packages from the known Java Platform API can be imported.
  105. * <li>All other packages can be dynamically imported. Perhaps these will be
  106. * available somehow at runtime, perhaps from the system bundle.
  107. * </ol>
  108. * </ol>
  109. */
  110. public class MakeOSGi extends Task {
  111. private File destdir;
  112. private List<ResourceCollection> modules = new ArrayList<ResourceCollection>();
  113. /**
  114. * Mandatory destination directory. Bundles will be created here.
  115. */
  116. public void setDestdir(File destdir) {
  117. this.destdir = destdir;
  118. }
  119. /**
  120. * Adds a set of module JARs.
  121. * It is permitted for them to be JARs anywhere on disk,
  122. * but it is best if they are in a cluster structure
  123. * with ../update_tracking/*.xml present
  124. * so that associated files can be included in the bundle.
  125. */
  126. public void add(ResourceCollection modules) {
  127. this.modules.add(modules);
  128. }
  129. static class Info {
  130. final Set<String> importedPackages = new TreeSet<String>();
  131. final Set<String> exportedPackages = new TreeSet<String>();
  132. final Set<String> hiddenPackages = new TreeSet<String>();
  133. final Set<String> hiddenSubpackages = new TreeSet<String>();
  134. }
  135. public @Override void execute() throws BuildException {
  136. if (destdir == null) {
  137. throw new BuildException("missing destdir");
  138. }
  139. List<File> jars = new ArrayList<File>();
  140. List<File> fragments = new ArrayList<File>();
  141. Map<String,Info> infos = new HashMap<String,Info>();
  142. log("Prescanning JARs...");
  143. for (ResourceCollection rc : modules) {
  144. Iterator<?> it = rc.iterator();
  145. while (it.hasNext()) {
  146. File jar = ((FileResource) it.next()).getFile();
  147. log("Prescanning " + jar, Project.MSG_VERBOSE);
  148. if (jar.getParentFile().getName().equals("locale")) {
  149. fragments.add(jar);
  150. continue;
  151. }
  152. try {
  153. JarFile jf = new JarFile(jar);
  154. try {
  155. Info info = new Info();
  156. String cnb = prescan(jf, info, this);
  157. if (cnb == null) {
  158. log(jar + " does not appear to be either a module or a bundle; skipping", Project.MSG_WARN);
  159. } else if (SKIPPED_PSEUDO_MODULES.contains(cnb)) {
  160. log("Skipping " + jar);
  161. } else if (infos.containsKey(cnb)) {
  162. log(jar + " appears to not be the only module named " + cnb, Project.MSG_WARN);
  163. } else {
  164. infos.put(cnb, info);
  165. jars.add(jar);
  166. }
  167. } finally {
  168. jf.close();
  169. }
  170. } catch (Exception x) {
  171. throw new BuildException("Could not prescan " + jar + ": " + x, x, getLocation());
  172. }
  173. }
  174. }
  175. for (File jar : jars) {
  176. try {
  177. process(jar, infos);
  178. } catch (Exception x) {
  179. throw new BuildException("Could not process " + jar + ": " + x, x, getLocation());
  180. }
  181. }
  182. for (File jar : fragments) {
  183. try {
  184. processFragment(jar);
  185. } catch (Exception x) {
  186. throw new BuildException("Could not process " + jar + ": " + x, x, getLocation());
  187. }
  188. }
  189. }
  190. static String prescan(JarFile module, Info info, Task task) throws Exception {
  191. Manifest manifest = module.getManifest();
  192. if (manifest == null) {
  193. return null;
  194. }
  195. Attributes attr = manifest.getMainAttributes();
  196. String cnb = attr.getValue("OpenIDE-Module");
  197. if ("org.netbeans.libs.osgi".equals(cnb)) {
  198. // Otherwise get e.g. CCE: org.netbeans.core.osgi.Activator cannot be cast to org.osgi.framework.BundleActivator
  199. return cnb;
  200. }
  201. Set<String> availablePackages = new TreeSet<String>();
  202. scanClasses(module, info.importedPackages, availablePackages, task);
  203. File antlib = new File(module.getName().replaceFirst("([/\\\\])modules([/\\\\][^/\\\\]+)", "$1ant$1nblib$2"));
  204. if (antlib.isFile()) {
  205. // ant/nblib/org-netbeans-modules-debugger-jpda-ant.jar references com.sun.jdi.* packages.
  206. // AntBridge.MainClassLoader.findClass will refuse to load these,
  207. // since it is expected that the module loader, thus also AuxClassLoader, can load them.
  208. // So we need to DynamicImport-Package these packages so that will be true.
  209. Set<String> antlibPackages = new HashSet<String>();
  210. JarFile antlibJF = new JarFile(antlib);
  211. try {
  212. scanClasses(antlibJF, antlibPackages, new HashSet<String>(), task);
  213. } finally {
  214. antlibJF.close();
  215. }
  216. for (String antlibImport : antlibPackages) {
  217. if (!antlibImport.startsWith("org.apache.tools.") && !availablePackages.contains(antlibImport)) {
  218. info.importedPackages.add(antlibImport);
  219. }
  220. }
  221. }
  222. if (cnb != null) {
  223. cnb = cnb.replaceFirst("/\\d+$", "");
  224. String hide = attr.getValue("OpenIDE-Module-Hide-Classpath-Packages");
  225. if (hide != null) {
  226. for (String piece : hide.split("[, ]+")) {
  227. if (piece.isEmpty()) {
  228. continue;
  229. }
  230. if (piece.endsWith(".*")) {
  231. info.hiddenPackages.add(piece.substring(0, piece.length() - ".*".length()));
  232. } else if (piece.endsWith(".**")) {
  233. info.hiddenSubpackages.add(piece.substring(0, piece.length() - ".**".length()));
  234. } else {
  235. throw new IOException("Bad OpenIDE-Module-Hide-Classpath-Packages piece: " + piece);
  236. }
  237. }
  238. }
  239. String pp = attr.getValue("OpenIDE-Module-Public-Packages");
  240. String implVersion = attr.getValue("OpenIDE-Module-Implementation-Version");
  241. if (implVersion != null && implVersion.matches("\\d+") &&
  242. /* not just e.g. 201005242201 */ attr.getValue("OpenIDE-Module-Build-Version") != null) {
  243. // Since we have no idea who might be using these packages, have to make everything public.
  244. info.exportedPackages.addAll(availablePackages);
  245. pp = null;
  246. }
  247. if (pp != null && !pp.equals("-")) {
  248. for (String p : pp.split("[, ]+")) {
  249. if (p.isEmpty()) {
  250. continue;
  251. }
  252. if (p.endsWith(".*")) {
  253. info.exportedPackages.add(p.substring(0, p.length() - ".*".length()));
  254. } else {
  255. if (!p.endsWith(".**")) {
  256. throw new IllegalArgumentException("Invalid package export: " + p);
  257. }
  258. for (String actual : availablePackages) {
  259. if (actual.equals(p.substring(0, p.length() - ".**".length()))
  260. || actual.startsWith(p.substring(0, p.length() - "**".length()))) {
  261. info.exportedPackages.add(actual);
  262. }
  263. }
  264. }
  265. }
  266. }
  267. } else { // #180201
  268. cnb = JarWithModuleAttributes.extractCodeName(attr);
  269. if (cnb == null) {
  270. return null;
  271. }
  272. String exportPackage = attr.getValue("Export-Package");
  273. if (exportPackage != null) {
  274. // http://stackoverflow.com/questions/1757065/java-splitting-a-comma-separated-string-but-ignoring-commas-in-quotes
  275. for (String piece : exportPackage.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)")) {
  276. info.exportedPackages.add(piece.trim().replaceFirst(";.+", ""));
  277. }
  278. }
  279. }
  280. return cnb;
  281. }
  282. private File findDestFile(String bundleName, String bundleVersion) throws IOException {
  283. File destFile = new File(destdir, bundleName + (bundleVersion != null ? "-" + bundleVersion : "") + ".jar");
  284. for (File stale : destdir.listFiles()) {
  285. if (stale.getName().matches("\\Q" + bundleName + "\\E(-.+)?[.]jar") && !stale.equals(destFile)) {
  286. log("Deleting copy under old name: " + stale);
  287. if (!stale.delete()) {
  288. throw new IOException("Could not delete: " + stale);
  289. }
  290. }
  291. }
  292. return destFile;
  293. }
  294. private void process(File module, Map<String,Info> infos) throws Exception {
  295. JarFile jar = new JarFile(module);
  296. try {
  297. Manifest netbeans = jar.getManifest();
  298. Attributes netbeansAttr = netbeans.getMainAttributes();
  299. if (netbeansAttr.getValue("Bundle-SymbolicName") != null) { // #180201
  300. File bundleFile = findDestFile(JarWithModuleAttributes.extractCodeName(netbeansAttr), netbeansAttr.getValue("Bundle-Version"));
  301. if (bundleFile.lastModified() > module.lastModified()) {
  302. log("Skipping " + module + " since " + bundleFile + " is newer", Project.MSG_VERBOSE);
  303. return;
  304. }
  305. Copy copy = new Copy();
  306. copy.setProject(getProject());
  307. copy.setOwningTarget(getOwningTarget());
  308. copy.setFile(module);
  309. copy.setTofile(bundleFile);
  310. copy.setVerbose(true);
  311. copy.execute();
  312. return;
  313. }
  314. Manifest osgi = new Manifest();
  315. Attributes osgiAttr = osgi.getMainAttributes();
  316. translate(netbeansAttr, osgiAttr, infos);
  317. String cnb = osgiAttr.getValue("Bundle-SymbolicName").replaceFirst(";.+", "");
  318. File bundleFile = findDestFile(cnb, osgiAttr.getValue("Bundle-Version"));
  319. if (bundleFile.lastModified() > module.lastModified()) {
  320. log("Skipping " + module + " since " + bundleFile + " is newer", Project.MSG_VERBOSE);
  321. return;
  322. }
  323. log("Processing " + module + " into " + bundleFile);
  324. String dynamicImports = osgiAttr.getValue("DynamicImport-Package");
  325. if (dynamicImports != null) {
  326. log(cnb + " has imports of no known origin: " + dynamicImports, Project.MSG_WARN);
  327. log("(you may need to define org.osgi.framework.system.packages.extra in your OSGi container)");
  328. }
  329. Properties localizedStrings = new Properties();
  330. String locbundle = netbeansAttr.getValue("OpenIDE-Module-Localizing-Bundle");
  331. if (locbundle != null) {
  332. InputStream is = jar.getInputStream(jar.getEntry(locbundle));
  333. try {
  334. localizedStrings.load(is);
  335. } finally {
  336. is.close();
  337. }
  338. osgiAttr.putValue("Bundle-Localization", locbundle.replaceFirst("[.]properties$", ""));
  339. }
  340. handleDisplayAttribute(localizedStrings, netbeansAttr, osgiAttr,
  341. "OpenIDE-Module-Name", "Bundle-Name");
  342. handleDisplayAttribute(localizedStrings, netbeansAttr, osgiAttr,
  343. "OpenIDE-Module-Display-Category", "Bundle-Category");
  344. handleDisplayAttribute(localizedStrings, netbeansAttr, osgiAttr,
  345. "OpenIDE-Module-Short-Description", "Bundle-Description");
  346. Map<String,File> bundledFiles = findBundledFiles(module, cnb);
  347. // XXX any use for OpenIDE-Module-Long-Description?
  348. String classPath = netbeansAttr.getValue("Class-Path");
  349. if (classPath != null) {
  350. StringBuilder bundleCP = new StringBuilder();
  351. for (String entry : classPath.split("[, ]+")) {
  352. String clusterPath = new URI(module.getParentFile().getName() + "/" + entry).normalize().toString();
  353. if (bundledFiles.containsKey(clusterPath)) {
  354. bundleCP.append("/OSGI-INF/files/").append(clusterPath).append(",");
  355. } else {
  356. log("Class-Path entry " + entry + " from " + module + " does not correspond to any apparent cluster file", Project.MSG_WARN);
  357. }
  358. }
  359. osgiAttr.putValue("Bundle-Classpath", bundleCP + ".");
  360. }
  361. StringBuilder execFiles = null;
  362. for (Map.Entry<String,File> bundledFile : bundledFiles.entrySet()) {
  363. // XXX would be better to use ${nbm.executable.files} as specified by project
  364. // (since building bundles on Windows will never set this even if running on Unix)
  365. // but this information is available only during the project's build or in its NBM
  366. if (bundledFile.getValue().canExecute()) {
  367. String name = bundledFile.getKey();
  368. if (execFiles == null) {
  369. execFiles = new StringBuilder(name);
  370. } else {
  371. execFiles.append(',').append(name);
  372. }
  373. }
  374. }
  375. if (execFiles != null) {
  376. osgiAttr.putValue("NetBeans-Executable-Files", execFiles.toString());
  377. }
  378. // XXX modules/lib/*.dll/so => Bundle-NativeCode (but syntax is rather complex)
  379. OutputStream bundle = new FileOutputStream(bundleFile);
  380. try {
  381. ZipOutputStream zos = new JarOutputStream(bundle, osgi);
  382. Set<String> parents = new HashSet<String>();
  383. Enumeration<? extends ZipEntry> entries = jar.entries();
  384. while (entries.hasMoreElements()) {
  385. ZipEntry entry = entries.nextElement();
  386. String path = entry.getName();
  387. if (path.endsWith("/") || path.equals("META-INF/MANIFEST.MF")) {
  388. continue;
  389. }
  390. InputStream is = jar.getInputStream(entry);
  391. try {
  392. writeEntry(zos, path, is, parents);
  393. } finally {
  394. is.close();
  395. }
  396. }
  397. for (Map.Entry<String,File> bundledFile : bundledFiles.entrySet()) {
  398. InputStream is = new FileInputStream(bundledFile.getValue());
  399. try {
  400. writeEntry(zos, "OSGI-INF/files/" + bundledFile.getKey(), is, parents);
  401. } finally {
  402. is.close();
  403. }
  404. }
  405. zos.finish();
  406. zos.close();
  407. } finally {
  408. bundle.close();
  409. }
  410. } finally {
  411. jar.close();
  412. }
  413. }
  414. /**
  415. * Translate NetBeans module metadata to OSGi equivalents.
  416. * @param netbeans manifest header to be read
  417. * @param osgi manifest header to be written
  418. * @param infos information about imported and exported packages among all processed JARs
  419. */
  420. void translate(Attributes netbeans, Attributes osgi, Map<String,Info> infos) throws Exception {
  421. osgi.putValue("Manifest-Version", "1.0"); // workaround for JDK bug
  422. osgi.putValue("Bundle-ManifestVersion", "2");
  423. String codename = netbeans.getValue("OpenIDE-Module");
  424. if (codename == null) {
  425. throw new IllegalArgumentException("Does not appear to be a NetBeans module");
  426. }
  427. String cnb = codename.replaceFirst("/\\d+$", "");
  428. osgi.putValue("Bundle-SymbolicName", cnb);
  429. if (cnb.equals("org.netbeans.core.osgi")) {
  430. osgi.putValue("Bundle-Activator", "org.netbeans.core.osgi.Activator");
  431. }
  432. Info myInfo = infos.get(cnb);
  433. String spec = netbeans.getValue("OpenIDE-Module-Specification-Version");
  434. String bundleVersion = null;
  435. if (spec != null) {
  436. bundleVersion = threeDotsWithMajor(spec, codename);
  437. String buildVersion = netbeans.getValue("OpenIDE-Module-Build-Version");
  438. if (buildVersion == null) {
  439. buildVersion = netbeans.getValue("OpenIDE-Module-Implementation-Version");
  440. }
  441. if (buildVersion != null) {
  442. bundleVersion += "." + buildVersion.replaceAll("[^a-zA-Z0-9_-]", "_");
  443. }
  444. osgi.putValue("Bundle-Version", bundleVersion);
  445. }
  446. // OpenIDE-Module-Friends is ignored since OSGi has no apparent equivalent
  447. // (could use mandatory export constraints but friends would then
  448. // need to use Import-Package to access, rather than Require-Bundle,
  449. // which would require knowing which packages are being imported by that dep)
  450. if (!myInfo.exportedPackages.isEmpty()) {
  451. StringBuilder b = new StringBuilder();
  452. for (String p : myInfo.exportedPackages) {
  453. if (b.length() > 0) {
  454. b.append(", ");
  455. }
  456. b.append(p);
  457. }
  458. osgi.putValue("Export-Package", b.toString());
  459. }
  460. for (String attrToCopy : new String[] {"OpenIDE-Module-Layer", "OpenIDE-Module-Install"}) {
  461. String val = netbeans.getValue(attrToCopy);
  462. if (val != null) {
  463. osgi.putValue(attrToCopy, val);
  464. }
  465. }
  466. StringBuilder requireBundles = new StringBuilder();
  467. if (!STARTUP_PSEUDO_MODULES.contains(cnb)) {
  468. // do not need to import any API, just need it to be started:
  469. requireBundles.append("org.netbeans.core.osgi");
  470. }
  471. Set<String> imports = new TreeSet<String>(myInfo.importedPackages);
  472. hideImports(imports, myInfo);
  473. String dependencies = netbeans.getValue("OpenIDE-Module-Module-Dependencies");
  474. if (dependencies != null) {
  475. for (String dependency : dependencies.split(" *, *")) {
  476. String depCnb = translateDependency(requireBundles, dependency);
  477. if (depCnb == null) {
  478. continue;
  479. }
  480. Info imported = infos.get(depCnb);
  481. if (imported != null) {
  482. imports.removeAll(imported.exportedPackages);
  483. hideImports(imports, imported);
  484. } else {
  485. log("dependency " + depCnb + " of " + cnb + " not found in batch; imports may not be correct", Project.MSG_WARN);
  486. }
  487. }
  488. }
  489. if (requireBundles.length() > 0) {
  490. osgi.putValue("Require-Bundle", requireBundles.toString());
  491. }
  492. StringBuilder staticImports = new StringBuilder();
  493. StringBuilder dynamicImports = new StringBuilder();
  494. for (String pkg : imports) {
  495. StringBuilder b = isOSGiOrJavaPlatform(pkg) ? staticImports : dynamicImports;
  496. // JRE-specific dependencies will not be exported by Felix by default.
  497. // But Felix can be configured to offer any desired packages from the framework.
  498. // Do not use DynamicImport-Package where not really needed, as it can lead to deadlocks in Felix:
  499. // ModuleImpl.findClassOrResourceByDelegation -> Felix.acquireGlobalLock
  500. if (b.length() > 0) {
  501. b.append(", ");
  502. }
  503. b.append(pkg);
  504. }
  505. if (staticImports.length() > 0) {
  506. osgi.putValue("Import-Package", staticImports.toString());
  507. }
  508. if (dynamicImports.length() > 0) {
  509. osgi.putValue("DynamicImport-Package", dynamicImports.toString());
  510. }
  511. // ignore OpenIDE-Module-Package-Dependencies; rarely used, and bytecode analysis is probably more accurate anyway
  512. String javaDeps = netbeans.getValue("OpenIDE-Module-Java-Dependencies");
  513. if (javaDeps != null) {
  514. Matcher m = Pattern.compile("Java > (1.[6-9])").matcher(javaDeps); // 1.5 is not supported anyway
  515. if (m.matches()) {
  516. osgi.putValue("Bundle-RequiredExecutionEnvironment", "JavaSE-" + m.group(1));
  517. }
  518. }
  519. for (String tokenAttr : new String[] {"OpenIDE-Module-Provides", "OpenIDE-Module-Needs"}) {
  520. String v = netbeans.getValue(tokenAttr);
  521. if (v != null) {
  522. osgi.putValue(tokenAttr, v);
  523. }
  524. }
  525. String v = netbeans.getValue("OpenIDE-Module-Requires");
  526. if (v != null) {
  527. StringBuilder b = null;
  528. for (String tok : v.split("[, ]+")) {
  529. if (!tok.matches("org.openide.modules.ModuleFormat\\d+")) {
  530. if (b == null) {
  531. b = new StringBuilder(tok);
  532. } else {
  533. b.append(", ").append(tok);
  534. }
  535. }
  536. }
  537. if (b != null) {
  538. osgi.putValue("OpenIDE-Module-Requires", b.toString());
  539. }
  540. }
  541. // autoload, eager status are ignored since OSGi has no apparent equivalent
  542. }
  543. private boolean isOSGiOrJavaPlatform(String pkg) {
  544. if (pkg.startsWith("org.osgi.")) {
  545. return true;
  546. }
  547. return JAVA_PLATFORM_PACKAGES.contains(pkg);
  548. }
  549. private static void hideImports(Set<String> imports, Info info) {
  550. imports.removeAll(info.hiddenPackages);
  551. Iterator<String> it = imports.iterator();
  552. while (it.hasNext()) {
  553. String p = it.next();
  554. for (String prefix : info.hiddenSubpackages) {
  555. if (p.equals(prefix) || p.startsWith(prefix + ".")) {
  556. it.remove();
  557. break;
  558. }
  559. }
  560. }
  561. }
  562. private static void writeEntry(ZipOutputStream zos, String path, InputStream data, Set<String> parents) throws IOException {
  563. int size = Math.max(data.available(), 100);
  564. ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
  565. byte[] buf = new byte[size];
  566. int read;
  567. while ((read = data.read(buf)) != -1) {
  568. baos.write(buf, 0, read);
  569. }
  570. writeEntry(zos, path, baos.toByteArray(), parents);
  571. }
  572. private static void writeEntry(ZipOutputStream zos, String path, byte[] data, Set<String> parents) throws IOException {
  573. assert path.length() > 0 && !path.endsWith("/") && !path.startsWith("/") && path.indexOf("//") == -1 : path;
  574. for (int i = 0; i < path.length(); i++) {
  575. if (path.charAt(i) == '/') {
  576. String parent = path.substring(0, i + 1);
  577. if (parents.add(parent)) {
  578. ZipEntry ze = new ZipEntry(parent);
  579. ze.setMethod(ZipEntry.STORED);
  580. ze.setSize(0);
  581. ze.setCrc(0);
  582. zos.putNextEntry(ze);
  583. zos.closeEntry();
  584. }
  585. }
  586. }
  587. ZipEntry ze = new ZipEntry(path);
  588. ze.setMethod(ZipEntry.STORED);
  589. ze.setSize(data.length);
  590. CRC32 crc = new CRC32();
  591. crc.update(data);
  592. ze.setCrc(crc.getValue());
  593. zos.putNextEntry(ze);
  594. zos.write(data, 0, data.length);
  595. zos.closeEntry();
  596. }
  597. // copied from NetigsoModuleFactory
  598. private static String threeDotsWithMajor(String version, String withMajor) {
  599. int indx = withMajor.indexOf('/');
  600. int major = 0;
  601. if (indx > 0) {
  602. major = Integer.parseInt(withMajor.substring(indx + 1));
  603. }
  604. String[] segments = (version + ".0.0.0").split("\\.");
  605. assert segments.length >= 3 && segments[0].length() > 0;
  606. return (Integer.parseInt(segments[0]) + major * 100) + "." + segments[1] + "." + segments[2];
  607. }
  608. static String translateDependency(StringBuilder b, String dependency) throws IllegalArgumentException {
  609. Matcher m = Pattern.compile("([^/ >=]+)(?:/(\\d+)(?:-(\\d+))?)? *(?:(=|>) *(.+))?").matcher(dependency);
  610. if (!m.matches()) {
  611. throw new IllegalArgumentException("bad dep: " + dependency);
  612. }
  613. String depCnb = m.group(1);
  614. if (SKIPPED_PSEUDO_MODULES.contains(depCnb)) {
  615. return null;
  616. }
  617. String depMajLo = m.group(2);
  618. String depMajHi = m.group(3);
  619. String comparison = m.group(4);
  620. String version = m.group(5);
  621. if (b.length() > 0) {
  622. b.append(", ");
  623. }
  624. b.append(depCnb);
  625. if (!"=".equals(comparison)) {
  626. if (version == null) {
  627. version = "0";
  628. }
  629. String targetVersion = threeDotsWithMajor(version, depMajLo == null ? "" : "x/" + depMajLo);
  630. b.append(";bundle-version=\"[").append(targetVersion).append(",");
  631. b.append(100 * ((depMajHi != null ? Integer.parseInt(depMajHi) : depMajLo != null ? Integer.parseInt(depMajLo) : 0) + 1));
  632. b.append(")\"");
  633. }
  634. return depCnb;
  635. }
  636. private void handleDisplayAttribute(Properties props, Attributes netbeans, Attributes osgi, String netbeansHeader, String osgiHeader) throws IOException {
  637. String val = netbeans.getValue(netbeansHeader);
  638. if (val != null) {
  639. osgi.putValue(osgiHeader, val);
  640. } else if (props.containsKey(netbeansHeader)) {
  641. osgi.putValue(osgiHeader, "%" + netbeansHeader);
  642. }
  643. }
  644. private Map<String,File> findBundledFiles(File module, String cnb) throws Exception {
  645. Map<String,File> result = new HashMap<String,File>();
  646. if (module.getParentFile().getName().matches("modules|core|lib")) {
  647. File cluster = module.getParentFile().getParentFile();
  648. File updateTracking = new File(new File(cluster, "update_tracking"), cnb.replace('.', '-') + ".xml");
  649. if (updateTracking.isFile()) {
  650. Document doc = XMLUtil.parse(new InputSource(updateTracking.toURI().toString()), false, false, null, null);
  651. NodeList nl = doc.getElementsByTagName("file");
  652. for (int i = 0; i < nl.getLength(); i++) {
  653. String path = ((Element) nl.item(i)).getAttribute("name");
  654. if (path.matches("config/(Modules|ModuleAutoDeps)/.+[.]xml|lib/nbexec.*")) {
  655. continue;
  656. }
  657. File f = new File(cluster, path);
  658. if (f.equals(module)) {
  659. continue;
  660. }
  661. if (f.isFile()) {
  662. result.put(path, f);
  663. } else {
  664. log("did not find " + f + " specified in " + updateTracking, Project.MSG_WARN);
  665. }
  666. }
  667. } else {
  668. log("did not find expected " + updateTracking, Project.MSG_WARN);
  669. }
  670. } else {
  671. log("JAR " + module + " not found in expected cluster layout", Project.MSG_WARN);
  672. }
  673. return result;
  674. }
  675. private static void scanClasses(JarFile module, Set<String> importedPackages, Set<String> availablePackages, Task task) throws Exception {
  676. Map<String, byte[]> classfiles = new TreeMap<String, byte[]>();
  677. VerifyClassLinkage.read(module, classfiles, new HashSet<File>(Collections.singleton(new File(module.getName()))), task, null);
  678. for (Map.Entry<String,byte[]> entry : classfiles.entrySet()) {
  679. String available = entry.getKey();
  680. int idx = available.lastIndexOf('.');
  681. if (idx != -1) {
  682. availablePackages.add(available.substring(0, idx));
  683. }
  684. for (String clazz : VerifyClassLinkage.dependencies(entry.getValue())) {
  685. if (classfiles.containsKey(clazz)) {
  686. // Part of the same module; probably not an external import.
  687. continue;
  688. }
  689. if (clazz.startsWith("java.")) {
  690. // No need to declare as an import.
  691. continue;
  692. }
  693. idx = clazz.lastIndexOf('.');
  694. if (idx != -1) {
  695. importedPackages.add(clazz.substring(0, idx));
  696. }
  697. }
  698. }
  699. }
  700. private void processFragment(File fragment) throws Exception {
  701. String cnb = findFragmentHost(fragment);
  702. File bundleFile = new File(destdir, fragment.getName());
  703. if (bundleFile.lastModified() > fragment.lastModified()) {
  704. log("Skipping " + fragment + " since " + bundleFile + " is newer", Project.MSG_VERBOSE);
  705. return;
  706. }
  707. log("Processing " + fragment + " into " + bundleFile);
  708. Manifest mf = new Manifest();
  709. Attributes attr = mf.getMainAttributes();
  710. attr.putValue("Manifest-Version", "1.0"); // workaround for JDK bug
  711. attr.putValue("Bundle-ManifestVersion", "2");
  712. attr.putValue("Bundle-SymbolicName", cnb + "-branding");
  713. attr.putValue("Fragment-Host", cnb);
  714. JarFile jar = new JarFile(fragment);
  715. try {
  716. OutputStream bundle = new FileOutputStream(bundleFile);
  717. try {
  718. ZipOutputStream zos = new JarOutputStream(bundle, mf);
  719. Set<String> parents = new HashSet<String>();
  720. Enumeration<? extends ZipEntry> entries = jar.entries();
  721. while (entries.hasMoreElements()) {
  722. ZipEntry entry = entries.nextElement();
  723. String path = entry.getName();
  724. if (path.endsWith("/") || path.equals("META-INF/MANIFEST.MF")) {
  725. continue;
  726. }
  727. InputStream is = jar.getInputStream(entry);
  728. try {
  729. writeEntry(zos, path, is, parents);
  730. } finally {
  731. is.close();
  732. }
  733. }
  734. zos.finish();
  735. zos.close();
  736. } finally {
  737. bundle.close();
  738. }
  739. } finally {
  740. jar.close();
  741. }
  742. }
  743. static String findFragmentHost(File jar) {
  744. String cnb = jar.getName().replaceFirst("(_[a-z][a-z0-9]*)*[.]jar$", "").replace('-', '.');
  745. // Historical naming patterns:
  746. if (cnb.equals("core") && jar.getParentFile().getParentFile().getName().equals("core")) {
  747. return "org.netbeans.core.startup";
  748. } else if (cnb.equals("boot") && jar.getParentFile().getParentFile().getName().equals("lib")) {
  749. return "org.netbeans.bootstrap";
  750. } else {
  751. return cnb;
  752. }
  753. }
  754. /**
  755. * List of packages guaranteed to be in the Java platform.
  756. * Taken from Felix's default.properties@1347814#jre-1.6 but same as JDK 6's package-list.
  757. * FELIX-2572 updated this to not include some packages actually in the JRE
  758. * (rt.jar and src.zip); can cause problems in some cases (cf. bug #210325 comment #11).
  759. */
  760. private static final Set<String> JAVA_PLATFORM_PACKAGES = new TreeSet<String>(Arrays.asList(
  761. "javax.accessibility",
  762. "javax.activation",
  763. "javax.activity",
  764. "javax.annotation",
  765. "javax.annotation.processing",
  766. "javax.crypto",
  767. "javax.crypto.interfaces",
  768. "javax.crypto.spec",
  769. "javax.imageio",
  770. "javax.imageio.event",
  771. "javax.imageio.metadata",
  772. "javax.imageio.plugins.bmp",
  773. "javax.imageio.plugins.jpeg",
  774. "javax.imageio.spi",
  775. "javax.imageio.stream",
  776. "javax.jws",
  777. "javax.jws.soap",
  778. "javax.lang.model",
  779. "javax.lang.model.element",
  780. "javax.lang.model.type",
  781. "javax.lang.model.util",
  782. "javax.management",
  783. "javax.management.loading",
  784. "javax.management.modelmbean",
  785. "javax.management.monitor",
  786. "javax.management.openmbean",
  787. "javax.management.relation",
  788. "javax.management.remote",
  789. "javax.management.remote.rmi",
  790. "javax.management.timer",
  791. "javax.naming",
  792. "javax.naming.directory",
  793. "javax.naming.event",
  794. "javax.naming.ldap",
  795. "javax.naming.spi",
  796. "javax.net",
  797. "javax.net.ssl",
  798. "javax.print",
  799. "javax.print.attribute",
  800. "javax.print.attribute.standard",
  801. "javax.print.event",
  802. "javax.rmi",
  803. "javax.rmi.CORBA",
  804. "javax.rmi.ssl",
  805. "javax.script",
  806. "javax.security.auth",
  807. "javax.security.auth.callback",
  808. "javax.security.auth.kerberos",
  809. "javax.security.auth.login",
  810. "javax.security.auth.spi",
  811. "javax.security.auth.x500",
  812. "javax.security.cert",
  813. "javax.security.sasl",
  814. "javax.sound.midi",
  815. "javax.sound.midi.spi",
  816. "javax.sound.sampled",
  817. "javax.sound.sampled.spi",
  818. "javax.sql",
  819. "javax.sql.rowset",
  820. "javax.sql.rowset.serial",
  821. "javax.sql.rowset.spi",
  822. "javax.swing",
  823. "javax.swing.border",
  824. "javax.swing.colorchooser",
  825. "javax.swing.event",
  826. "javax.swing.filechooser",
  827. "javax.swing.plaf",
  828. "javax.swing.plaf.basic",
  829. "javax.swing.plaf.metal",
  830. "javax.swing.plaf.multi",
  831. "javax.swing.plaf.synth",
  832. "javax.swing.table",
  833. "javax.swing.text",
  834. "javax.swing.text.html",
  835. "javax.swing.text.html.parser",
  836. "javax.swing.text.rtf",
  837. "javax.swing.tree",
  838. "javax.swing.undo",
  839. "javax.tools",
  840. "javax.transaction",
  841. "javax.transaction.xa",
  842. "javax.xml",
  843. "javax.xml.bind",
  844. "javax.xml.bind.annotation",
  845. "javax.xml.bind.annotation.adapters",
  846. "javax.xml.bind.attachment",
  847. "javax.xml.bind.helpers",
  848. "javax.xml.bind.util",
  849. "javax.xml.crypto",
  850. "javax.xml.crypto.dom",
  851. "javax.xml.crypto.dsig",
  852. "javax.xml.crypto.dsig.dom",
  853. "javax.xml.crypto.dsig.keyinfo",
  854. "javax.xml.crypto.dsig.spec",
  855. "javax.xml.datatype",
  856. "javax.xml.namespace",
  857. "javax.xml.parsers",
  858. "javax.xml.soap",
  859. "javax.xml.stream",
  860. "javax.xml.stream.events",
  861. "javax.xml.stream.util",
  862. "javax.xml.transform",
  863. "javax.xml.transform.dom",
  864. "javax.xml.transform.sax",
  865. "javax.xml.transform.stax",
  866. "javax.xml.transform.stream",
  867. "javax.xml.validation",
  868. "javax.xml.ws",
  869. "javax.xml.ws.handler",
  870. "javax.xml.ws.handler.soap",
  871. "javax.xml.ws.http",
  872. "javax.xml.ws.soap",
  873. "javax.xml.ws.spi",
  874. "javax.xml.ws.wsaddressing",
  875. "javax.xml.xpath",
  876. "org.ietf.jgss",
  877. "org.omg.CORBA",
  878. "org.omg.CORBA.DynAnyPackage",
  879. "org.omg.CORBA.ORBPackage",
  880. "org.omg.CORBA.TypeCodePackage",
  881. "org.omg.CORBA.portable",
  882. "org.omg.CORBA_2_3",
  883. "org.omg.CORBA_2_3.portable",
  884. "org.omg.CosNaming",
  885. "org.omg.CosNaming.NamingContextExtPackage",
  886. "org.omg.CosNaming.NamingContextPackage",
  887. "org.omg.Dynamic",
  888. "org.omg.DynamicAny",
  889. "org.omg.DynamicAny.DynAnyFactoryPackage",
  890. "org.omg.DynamicAny.DynAnyPackage",
  891. "org.omg.IOP",
  892. "org.omg.IOP.CodecFactoryPackage",
  893. "org.omg.IOP.CodecPackage",
  894. "org.omg.Messaging",
  895. "org.omg.PortableInterceptor",
  896. "org.omg.PortableInterceptor.ORBInitInfoPackage",
  897. "org.omg.PortableServer",
  898. "org.omg.PortableServer.CurrentPackage",
  899. "org.omg.PortableServer.POAManagerPackage",
  900. "org.omg.PortableServer.POAPackage",
  901. "org.omg.PortableServer.ServantLocatorPackage",
  902. "org.omg.PortableServer.portable",
  903. "org.omg.SendingContext",
  904. "org.omg.stub.java.rmi",
  905. "org.w3c.dom",
  906. "org.w3c.dom.bootstrap",
  907. "org.w3c.dom.events",
  908. "org.w3c.dom.ls",
  909. "org.xml.sax",
  910. "org.xml.sax.ext",
  911. "org.xml.sax.helpers"
  912. ));
  913. private static final Set<String> SKIPPED_PSEUDO_MODULES = new HashSet<String>(Arrays.asList(
  914. "org.eclipse.osgi",
  915. "org.netbeans.core.netigso",
  916. "org.netbeans.modules.netbinox",
  917. "org.netbeans.libs.osgi",
  918. "org.netbeans.libs.felix"
  919. ));
  920. private static final Set<String> STARTUP_PSEUDO_MODULES = new HashSet<String>(Arrays.asList(
  921. "org.openide.util.lookup",
  922. "org.openide.util",
  923. "org.openide.modules",
  924. "org.netbeans.bootstrap",
  925. "org.openide.filesystems",
  926. "org.netbeans.core.startup",
  927. "org.netbeans.core.osgi",
  928. "org.eclipse.osgi"
  929. ));
  930. }