PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/netbeans-7.3/core.startup/src/org/netbeans/core/startup/NbInstaller.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1083 lines | 794 code | 56 blank | 233 comment | 216 complexity | c04d4f020a134207f1229d6e025b8652 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2012 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. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.core.startup;
  45. import java.io.DataOutputStream;
  46. import java.io.File;
  47. import java.io.IOException;
  48. import java.io.InputStream;
  49. import java.net.URL;
  50. import java.util.ArrayList;
  51. import java.util.Collection;
  52. import java.util.Collections;
  53. import java.util.Enumeration;
  54. import java.util.HashMap;
  55. import java.util.HashSet;
  56. import java.util.LinkedHashSet;
  57. import java.util.LinkedList;
  58. import java.util.List;
  59. import java.util.Locale;
  60. import java.util.Map;
  61. import java.util.Properties;
  62. import java.util.Set;
  63. import java.util.StringTokenizer;
  64. import java.util.TreeMap;
  65. import java.util.TreeSet;
  66. import java.util.jar.Attributes;
  67. import java.util.jar.JarEntry;
  68. import java.util.jar.JarFile;
  69. import java.util.jar.Manifest;
  70. import java.util.logging.Level;
  71. import java.util.logging.Logger;
  72. import org.netbeans.Events;
  73. import org.netbeans.InvalidException;
  74. import org.netbeans.Module;
  75. import org.netbeans.ModuleInstaller;
  76. import org.netbeans.ModuleManager;
  77. import org.netbeans.Stamps;
  78. import org.netbeans.Util;
  79. import org.netbeans.core.startup.layers.ModuleLayeredFileSystem;
  80. import org.openide.filesystems.FileObject;
  81. import org.openide.filesystems.FileUtil;
  82. import org.openide.modules.Dependency;
  83. import org.openide.modules.ModuleInfo;
  84. import org.openide.modules.ModuleInstall;
  85. import org.openide.modules.SpecificationVersion;
  86. import org.openide.util.NbCollections;
  87. import org.openide.util.SharedClassObject;
  88. import org.openide.util.NbBundle;
  89. import org.openide.util.Task;
  90. import org.openide.util.TaskListener;
  91. import org.openide.util.Utilities;
  92. import org.openide.util.lookup.InstanceContent;
  93. import org.xml.sax.SAXException;
  94. /** Concrete implementation of the module installation functionality.
  95. * This class can pay attention to the details of manifest format,
  96. * details of how to install particular sections and layers and so on.
  97. * It may have a limited UI, i.e. notifying problems with localized
  98. * messages and the like.
  99. * @author Jesse Glick, Jan Pokorsky, Jaroslav Tulach, et al.
  100. */
  101. final class NbInstaller extends ModuleInstaller {
  102. private static final Logger LOG = Logger.getLogger(NbInstaller.class.getName());
  103. /** set of manifest sections for each module */
  104. private final Map<Module,Set<ManifestSection>> sections = new HashMap<Module,Set<ManifestSection>>(100);
  105. /** ModuleInstall classes for each module that declares one */
  106. private final Map<Module,Class<? extends ModuleInstall>> installs = new HashMap<Module,Class<? extends ModuleInstall>>(100);
  107. /** layer resources for each module that declares one */
  108. private final Map<Module,String> layers = new HashMap<Module,String>(100);
  109. /** exact use of this is hard to explain */
  110. private boolean initializedFolderLookup = false;
  111. /** where to report events to */
  112. private final Events ev;
  113. /** associated controller of module list; needed for handling ModuleInstall ser */
  114. private ModuleList moduleList;
  115. /** associated manager */
  116. private ModuleManager mgr;
  117. /** set of permitted core or package dependencies from a module */
  118. private final Map<Module,Set<String>> kosherPackages = new HashMap<Module,Set<String>>(100);
  119. /** classpath ~ JRE packages to be hidden from a module */
  120. private final Map<Module,List<Module.PackageExport>> hiddenClasspathPackages = new HashMap<Module,List<Module.PackageExport>>();
  121. /** #164510: similar to {@link #hiddenClasspathPackages} but backwards for efficiency */
  122. private final Map<Module.PackageExport,List<Module>> hiddenClasspathPackagesReverse = new HashMap<Module.PackageExport,List<Module>>();
  123. /** caches important values from module manifests */
  124. private final Cache cache = new Cache();
  125. /** Processing @OnStart/@OnStop calls */
  126. private final NbStartStop onStartStop = new NbStartStop(null, null);
  127. /** Create an NbInstaller.
  128. * You should also call {@link #registerManager} and if applicable
  129. * {@link #registerList} to make the installer fully usable.
  130. * @param ev events to log
  131. */
  132. public NbInstaller(Events ev) {
  133. this.ev = ev;
  134. }
  135. /** Access from ModuleSystem. */
  136. void registerList(ModuleList list) {
  137. if (moduleList != null) throw new IllegalStateException();
  138. moduleList = list;
  139. }
  140. void registerManager(ModuleManager manager) {
  141. if (mgr != null) throw new IllegalStateException();
  142. mgr = manager;
  143. }
  144. // @SuppressWarnings("unchecked")
  145. @Override
  146. public void prepare(Module m) throws InvalidException {
  147. ev.log(Events.PREPARE, m);
  148. checkForHiddenPackages(m);
  149. Set<ManifestSection> mysections = null;
  150. Class<?> clazz = null;
  151. String processSections = cache.findGlobalProperty("processSections", null, "false"); // NOI18N
  152. if (!"false".equals(processSections)) { // NOI18N
  153. // Find and load manifest sections.
  154. Manifest mani = m.getManifest();
  155. if (mani == null) {
  156. throw new InvalidException(m, "no manifest");
  157. }
  158. for (Map.Entry<String,Attributes> entry : mani.getEntries().entrySet()) {
  159. ManifestSection section = ManifestSection.create(entry.getKey(), entry.getValue(), m);
  160. if (section != null) {
  161. if (mysections == null) {
  162. mysections = new HashSet<ManifestSection>(25);
  163. }
  164. mysections.add(section);
  165. }
  166. }
  167. if (mysections != null) {
  168. cache.findGlobalProperty("processSections", "false", "true"); // NOI18N
  169. }
  170. }
  171. String installClass = cache.findProperty(m, "OpenIDE-Module-Install", false); // NOI18N
  172. if (installClass != null) {
  173. String installClassName;
  174. try {
  175. installClassName = Util.createPackageName(installClass);
  176. } catch (IllegalArgumentException iae) {
  177. InvalidException ie = new InvalidException(m, iae.toString());
  178. ie.initCause(iae);
  179. throw ie;
  180. }
  181. if (installClass.endsWith(".ser")) throw new InvalidException(m, "Serialized module installs not supported: " + installClass); // NOI18N
  182. try {
  183. // We could simply load the ModuleInstall right away in all cases.
  184. // However consider a ModuleInstall that has a static or instance
  185. // initializer or initialize() calling NbBundle.getBundle(String). In the
  186. // old module installer that would work and it was never specifically discouraged,
  187. // so that needs to continue working. So do not resolve in most cases;
  188. // if the module specifically has a validate method, then this is clearly
  189. // documented to be called before systemClassLoader is ready, so the onus
  190. // is on the module author to avoid anything dangerous there.
  191. clazz = Class.forName(installClassName, false, m.getClassLoader());
  192. if (clazz.getClassLoader() != m.getClassLoader()) {
  193. ev.log(Events.WRONG_CLASS_LOADER, m, clazz, m.getClassLoader());
  194. }
  195. // Search for a validate() method; if there is none, do not resolve the class now!
  196. Class<?> c;
  197. for (c = clazz; c != ModuleInstall.class && c != Object.class; c = c.getSuperclass()) {
  198. try {
  199. // #13997: do not search in superclasses.
  200. c.getDeclaredMethod("validate"); // NOI18N
  201. // It has one. We are permitted to resolve the class, create
  202. // the installer instance, and validate it.
  203. ModuleInstall install = SharedClassObject.findObject(clazz.asSubclass(ModuleInstall.class), true);
  204. // The following can throw IllegalStateException, which we would
  205. // rethrow as InvalidException below:
  206. install.validate();
  207. } catch (NoSuchMethodException nsme) {
  208. // OK, did not find it here, continue.
  209. }
  210. }
  211. if (c == Object.class) throw new ClassCastException("Should extend ModuleInstall: " + clazz.getName()); // NOI18N
  212. // Did not find any validate() method, so remember the class and resolve later.
  213. } catch (Exception t) {
  214. InvalidException ie = new InvalidException(m, t.toString());
  215. ie.initCause(t);
  216. throw ie;
  217. } catch (LinkageError t) {
  218. InvalidException ie = new InvalidException(m, t.toString());
  219. ie.initCause(t);
  220. throw ie;
  221. }
  222. }
  223. // For layer & help set, validate only that the base-locale resource
  224. // exists, not its contents or anything.
  225. String layerResource = cache.findProperty(m, "OpenIDE-Module-Layer", false); // NOI18N
  226. if (layerResource != null && !m.isNetigso()) {
  227. URL layer = m.getClassLoader().getResource(layerResource);
  228. if (layer == null) throw new InvalidException(m, "Layer not found: " + layerResource); // NOI18N
  229. }
  230. String helpSetName = cache.findProperty(m, "OpenIDE-Module-Description", false); // NOI18N
  231. if (helpSetName != null) {
  232. Util.err.log(Level.WARNING, "Use of OpenIDE-Module-Description in {0} is deprecated.", m.getCodeNameBase());
  233. Util.err.warning("(Please install help using an XML layer instead.)");
  234. }
  235. // We are OK, commit everything to our cache.
  236. if (mysections != null) {
  237. sections.put(m, mysections);
  238. }
  239. if (clazz != null) {
  240. installs.put(m, clazz.asSubclass(ModuleInstall.class));
  241. }
  242. if (layerResource != null) {
  243. layers.put(m, layerResource);
  244. }
  245. }
  246. private void checkForHiddenPackages(Module m) throws InvalidException {
  247. List<Module.PackageExport> hiddenPackages = new ArrayList<Module.PackageExport>();
  248. List<Module> mWithDeps = new LinkedList<Module>();
  249. mWithDeps.add(m);
  250. for (Dependency d : m.getDependencies()) {
  251. if (d.getType() == Dependency.TYPE_MODULE) {
  252. Module _m = mgr.get((String) Util.parseCodeName(d.getName())[0]);
  253. assert _m != null : d;
  254. mWithDeps.add(_m);
  255. }
  256. }
  257. for (Module _m : mWithDeps) {
  258. String hidden = cache.findProperty(_m, "OpenIDE-Module-Hide-Classpath-Packages", false); // NOI18N
  259. if (hidden != null) {
  260. for (String piece : hidden.trim().split("[ ,]+")) { // NOI18N
  261. try {
  262. if (piece.endsWith(".*")) { // NOI18N
  263. String pkg = piece.substring(0, piece.length() - 2);
  264. Dependency.create(Dependency.TYPE_MODULE, pkg);
  265. if (pkg.lastIndexOf('/') != -1) {
  266. throw new IllegalArgumentException("Illegal OpenIDE-Module-Hide-Classpath-Packages: " + hidden); // NOI18N
  267. }
  268. hiddenPackages.add(new Module.PackageExport(pkg.replace('.', '/') + '/', false));
  269. } else if (piece.endsWith(".**")) { // NOI18N
  270. String pkg = piece.substring(0, piece.length() - 3);
  271. Dependency.create(Dependency.TYPE_MODULE, pkg);
  272. if (pkg.lastIndexOf('/') != -1) {
  273. throw new IllegalArgumentException("Illegal OpenIDE-Module-Hide-Classpath-Packages: " + hidden); // NOI18N
  274. }
  275. hiddenPackages.add(new Module.PackageExport(pkg.replace('.', '/') + '/', true));
  276. } else {
  277. throw new IllegalArgumentException("Illegal OpenIDE-Module-Hide-Classpath-Packages: " + hidden); // NOI18N
  278. }
  279. } catch (IllegalArgumentException x) {
  280. throw new InvalidException(_m, x.getMessage());
  281. }
  282. }
  283. }
  284. }
  285. if (!hiddenPackages.isEmpty()) {
  286. synchronized (hiddenClasspathPackages) {
  287. hiddenClasspathPackages.put(m, hiddenPackages);
  288. for (Module.PackageExport pkg : hiddenPackages) {
  289. List<Module> ms = hiddenClasspathPackagesReverse.get(pkg);
  290. if (ms == null) {
  291. hiddenClasspathPackagesReverse.put(pkg, ms = new LinkedList<Module>());
  292. }
  293. ms.add(m);
  294. }
  295. }
  296. }
  297. }
  298. public void dispose(Module m) {
  299. Util.err.fine("dispose: " + m);
  300. // Events probably not needed here.
  301. Set<ManifestSection> s = sections.remove(m);
  302. if (s != null) {
  303. for (ManifestSection sect : s) {
  304. sect.dispose();
  305. }
  306. }
  307. installs.remove(m);
  308. layers.remove(m);
  309. kosherPackages.remove(m);
  310. synchronized (hiddenClasspathPackages) {
  311. hiddenClasspathPackages.remove(m);
  312. for (List<Module> ms : hiddenClasspathPackagesReverse.values()) {
  313. ms.remove(m);
  314. // could also delete entry if ms.isEmpty()
  315. }
  316. }
  317. }
  318. @Override
  319. protected void classLoaderUp(ClassLoader cl) {
  320. MainLookup.systemClassLoaderChanged(cl);
  321. ev.log(Events.PERF_TICK, "META-INF/services/ additions registered"); // NOI18N
  322. }
  323. final void waitOnStart() {
  324. onStartStop.waitOnStart();
  325. }
  326. @Override
  327. public void load(final List<Module> modules) {
  328. FileUtil.runAtomicAction(new Runnable() {
  329. @Override
  330. public void run() {
  331. loadImpl(modules);
  332. }
  333. });
  334. }
  335. private void loadImpl(List<Module> modules) {
  336. ev.log(Events.START_LOAD, modules);
  337. checkForDeprecations(modules);
  338. loadLayers(modules, true);
  339. ev.log(Events.PERF_TICK, "layers loaded"); // NOI18N
  340. onStartStop.initialize();
  341. ev.log(Events.PERF_TICK, "@OnStart"); // NOI18N
  342. ev.log(Events.PERF_START, "NbInstaller.load - sections"); // NOI18N
  343. ev.log(Events.LOAD_SECTION);
  344. CoreBridge.getDefault().loaderPoolTransaction(true);
  345. try {
  346. for (Module m: modules) {
  347. try {
  348. loadSections(m, true);
  349. } catch (Exception t) {
  350. Util.err.log(Level.SEVERE, null, t);
  351. } catch (LinkageError le) {
  352. Util.err.log(Level.SEVERE, null, le);
  353. }
  354. ev.log(Events.PERF_TICK, "sections for " + m.getCodeName() + " loaded"); // NOI18N
  355. }
  356. } finally {
  357. CoreBridge.getDefault().loaderPoolTransaction(false);
  358. }
  359. ev.log(Events.PERF_END, "NbInstaller.load - sections"); // NOI18N
  360. // Yarda says to put this here.
  361. if (! initializedFolderLookup) {
  362. Util.err.fine("modulesClassPathInitialized");
  363. MainLookup.modulesClassPathInitialized();
  364. initializedFolderLookup = true;
  365. }
  366. // we need to initialize UI before we let modules run ModuleInstall.restore
  367. Main.initUICustomizations();
  368. ev.log(Events.PERF_START, "NbInstaller.load - ModuleInstalls"); // NOI18N
  369. for (Module m: modules) {
  370. try {
  371. loadCode(m, true);
  372. } catch (Exception t) {
  373. Util.err.log(Level.SEVERE, null, t);
  374. } catch (LinkageError le) {
  375. Util.err.log(Level.SEVERE, null, le);
  376. } catch (AssertionError e) {
  377. Util.err.log(Level.SEVERE, null, e);
  378. }
  379. ev.log(Events.PERF_TICK, "ModuleInstall for " + m.getCodeName() + " called"); // NOI18N
  380. }
  381. ev.log(Events.PERF_END, "NbInstaller.load - ModuleInstalls"); // NOI18N
  382. ev.log(Events.FINISH_LOAD, modules);
  383. if (Boolean.getBoolean("netbeans.preresolve.classes")) {
  384. preresolveClasses(modules);
  385. }
  386. }
  387. final void preloadCache(Collection<Module> modules) {
  388. for (Module m : modules) {
  389. // initialize the cache
  390. isShowInAutoUpdateClient(m);
  391. }
  392. }
  393. @Override
  394. public void unload(final List<Module> modules) {
  395. FileUtil.runAtomicAction(new Runnable() {
  396. @Override
  397. public void run() {
  398. unloadImpl(modules);
  399. }
  400. });
  401. }
  402. private void unloadImpl(List<Module> modules) {
  403. ev.log(Events.START_UNLOAD, modules);
  404. for (Module m: modules) {
  405. try {
  406. loadCode(m, false);
  407. } catch (Exception t) {
  408. Util.err.log(Level.SEVERE, null, t);
  409. } catch (LinkageError le) {
  410. Util.err.log(Level.SEVERE, null, le);
  411. }
  412. }
  413. CoreBridge.getDefault().loaderPoolTransaction(true);
  414. try {
  415. for (Module m: modules) {
  416. try {
  417. loadSections(m, false);
  418. } catch (Exception t) {
  419. Util.err.log(Level.SEVERE, null, t);
  420. } catch (LinkageError le) {
  421. Util.err.log(Level.SEVERE, null, le);
  422. }
  423. }
  424. } finally {
  425. try {
  426. CoreBridge.getDefault().loaderPoolTransaction(false);
  427. } catch (RuntimeException e) {
  428. Util.err.log(Level.SEVERE, null, e);
  429. }
  430. }
  431. loadLayers(modules, false);
  432. ev.log(Events.FINISH_UNLOAD, modules);
  433. }
  434. /** Load/unload installer code for a module. */
  435. @SuppressWarnings("deprecation") // old ModuleInstall methods we have to call
  436. private void loadCode(Module m, boolean load) throws Exception {
  437. Class<? extends ModuleInstall> instClazz = installs.get(m);
  438. if (instClazz != null) {
  439. ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
  440. if (load) {
  441. ev.log(Events.RESTORE, m);
  442. inst.restored();
  443. } else {
  444. ev.log(Events.UNINSTALL, m);
  445. inst.uninstalled();
  446. }
  447. }
  448. }
  449. /** Load/unload all manifest sections for a given module. */
  450. @SuppressWarnings("deprecation") // old ManifestSection.* we have to interpret
  451. private void loadSections(Module m, boolean load) throws Exception {
  452. Set<ManifestSection> s = sections.get(m);
  453. if (s == null) {
  454. return;
  455. }
  456. // Whether we yet had occasion to attach to the module actions list.
  457. boolean attachedToMA = false;
  458. try {
  459. ev.log(Events.LOAD_SECTION);
  460. for (ManifestSection sect : s) {
  461. if (sect instanceof ManifestSection.ActionSection) {
  462. if (! attachedToMA) {
  463. // First categorize the actions we will add.
  464. Object category = m.getLocalizedAttribute("OpenIDE-Module-Display-Category"); // NOI18N
  465. if (category == null) {
  466. // uncategorized modules group by themselves
  467. category = m.getCodeNameBase();
  468. }
  469. CoreBridge.getDefault().attachToCategory(category);
  470. attachedToMA = true;
  471. }
  472. CoreBridge.getDefault ().loadActionSection((ManifestSection.ActionSection)sect, load);
  473. } else if (sect instanceof ManifestSection.ClipboardConvertorSection) {
  474. loadGenericSection(sect, load);
  475. } else if (sect instanceof ManifestSection.DebuggerSection) {
  476. loadGenericSection(sect, load);
  477. } else if (sect instanceof ManifestSection.LoaderSection) {
  478. CoreBridge.getDefault().loadLoaderSection((ManifestSection.LoaderSection)sect, load);
  479. } else {
  480. assert false : sect;
  481. }
  482. }
  483. } finally {
  484. if (attachedToMA) {
  485. CoreBridge.getDefault ().attachToCategory (null);
  486. }
  487. }
  488. }
  489. // Load or unload various possible manifest sections.
  490. /** Simple section that can just be passed to lookup.
  491. * The lookup sees the real object, not the section.
  492. * You tell it whether to convert the result to the real
  493. * instance, or just register the section itself.
  494. */
  495. private void loadGenericSection(ManifestSection s, boolean load) {
  496. CoreBridge.getDefault().loadDefaultSection(s, convertor, load);
  497. }
  498. private final InstanceContent.Convertor<ManifestSection,Object> convertor = new Convertor();
  499. private final class Convertor implements InstanceContent.Convertor<ManifestSection,Object> { // or <ManifestSection,SharedClassObject>?
  500. Convertor() {}
  501. public Object convert(ManifestSection s) {
  502. try {
  503. return s.getInstance();
  504. } catch (Exception e) {
  505. Util.err.log(Level.WARNING, null, e);
  506. // Try to remove it from the pool so it does not continue
  507. // to throw errors over and over. Hopefully it is kosher to
  508. // do this while it is in the process of converting! I.e.
  509. // hopefully InstanceLookup is well-synchronized.
  510. loadGenericSection(s, false);
  511. return null;
  512. }
  513. }
  514. public Class<?> type(ManifestSection s) {
  515. return s.getSuperclass();
  516. }
  517. /** Computes the ID of the resulted object.
  518. * @param obj the registered object
  519. * @return the ID for the object
  520. */
  521. public String id(ManifestSection obj) {
  522. return obj.toString ();
  523. }
  524. /** The human presentable name for the object.
  525. * @param obj the registered object
  526. * @return the name representing the object for the user
  527. */
  528. public String displayName(ManifestSection obj) {
  529. return obj.toString ();
  530. }
  531. }
  532. /** Either load or unload the layer, if any, for a set of modules.
  533. * If the parameter load is true, load it, else unload it.
  534. * Locale/branding variants are likewise loaded or unloaded.
  535. * If a module has no declared layer, does nothing.
  536. */
  537. void loadLayers(List<Module> modules, boolean load) {
  538. ev.log(load ? Events.LOAD_LAYERS : Events.UNLOAD_LAYERS, modules);
  539. // #23609: dependent modules should be able to override:
  540. modules = new ArrayList<Module>(modules);
  541. Collections.reverse(modules);
  542. Map<ModuleLayeredFileSystem,Collection<URL>> urls = new HashMap<ModuleLayeredFileSystem,Collection<URL>>(5);
  543. ModuleLayeredFileSystem userModuleLayer = ModuleLayeredFileSystem.getUserModuleLayer();
  544. ModuleLayeredFileSystem installationModuleLayer = ModuleLayeredFileSystem.getInstallationModuleLayer();
  545. urls.put(userModuleLayer, new LinkedHashSet<URL>(1000));
  546. urls.put(installationModuleLayer, new LinkedHashSet<URL>(1000));
  547. for (Module m: modules) {
  548. // #19458: only put reloadables into the "session layer"
  549. // (where they will not have their layers cached). All others
  550. // should go into "installation layer" (so that they can mask
  551. // layers according to cross-dependencies).
  552. ModuleLayeredFileSystem host = m.isReloadable() ? userModuleLayer : installationModuleLayer;
  553. Collection<URL> theseurls = urls.get(host);
  554. if (theseurls == null) {
  555. theseurls = new LinkedHashSet<URL>(1000);
  556. urls.put(host, theseurls);
  557. }
  558. String s = layers.get(m);
  559. if (s != null) {
  560. Util.err.log(Level.FINE, "loadLayer: {0} load={1}", new Object[] { s, load });
  561. // Actually add a sequence of layers, in locale order.
  562. String base, ext;
  563. int idx = s.lastIndexOf('.'); // NOI18N
  564. if (idx == -1) {
  565. base = s;
  566. ext = ""; // NOI18N
  567. } else {
  568. base = s.substring(0, idx);
  569. ext = s.substring(idx);
  570. }
  571. boolean foundSomething = false;
  572. for (String suffix : NbCollections.iterable(NbBundle.getLocalizingSuffixes())) {
  573. String resource = base + suffix + ext;
  574. Enumeration<URL> en = m.findResources(resource);
  575. if (en.hasMoreElements()) {
  576. URL u = en.nextElement();
  577. assert !en.hasMoreElements() : "At most one resource per module: " + m;
  578. theseurls.add(u);
  579. foundSomething = true;
  580. }
  581. }
  582. if (! foundSomething) {
  583. // Should never happen (we already checked in prepare() for base layer)...
  584. Util.err.fine("Module layer not found: " + s);
  585. continue;
  586. }
  587. }
  588. Enumeration e = m.findResources("META-INF/generated-layer.xml"); // NOI18N
  589. while (e.hasMoreElements()) {
  590. URL u = (URL)e.nextElement();
  591. theseurls.add(u);
  592. }
  593. }
  594. // Now actually do it.
  595. for (Map.Entry<ModuleLayeredFileSystem,Collection<URL>> entry: urls.entrySet()) {
  596. ModuleLayeredFileSystem host = entry.getKey();
  597. Collection<URL> theseurls = entry.getValue();
  598. Util.err.log(Level.FINE, "Adding/removing layer URLs: host={0} urls={1}", new Object[] { host, theseurls });
  599. try {
  600. if (load) {
  601. host.addURLs(theseurls);
  602. } else {
  603. // #106737: we might have the wrong host, since it switches when reloadable flag is toggled.
  604. // To be safe, remove from both.
  605. userModuleLayer.removeURLs(theseurls);
  606. installationModuleLayer.removeURLs(theseurls);
  607. }
  608. } catch (Exception e) {
  609. Util.err.log(Level.WARNING, null, e);
  610. }
  611. }
  612. }
  613. /** Scan a (nondeprecated) module for direct dependencies on deprecated modules.
  614. * Deprecated modules can quietly depend on other deprecated modules.
  615. * And if the module is not actually enabled, it does not matter.
  616. * Indirect dependencies are someone else's problem.
  617. * Provide-require dependencies do not count either.
  618. * @param modules the modules which are now being turned on
  619. */
  620. private void checkForDeprecations(List<Module> modules) {
  621. Map<String,Set<String>> depToUsers = new TreeMap<String,Set<String>>();
  622. for (Module m : modules) {
  623. String depr = cache.findProperty(m, "OpenIDE-Module-Deprecated", false); // NOI18N
  624. if (!Boolean.parseBoolean(depr)) {
  625. for (Dependency dep : m.getDependencies()) {
  626. if (dep.getType() == Dependency.TYPE_MODULE) {
  627. String cnb = (String) Util.parseCodeName(dep.getName())[0];
  628. Set<String> users = depToUsers.get(cnb);
  629. if (users == null) {
  630. users = new TreeSet<String>();
  631. depToUsers.put(cnb, users);
  632. }
  633. users.add(m.getCodeNameBase());
  634. }
  635. }
  636. }
  637. }
  638. for (Map.Entry<String,Set<String>> entry : depToUsers.entrySet()) {
  639. String dep = entry.getKey();
  640. Module o = mgr.get(dep);
  641. assert o != null : "No such module: " + dep;
  642. String depr = cache.findProperty(o, "OpenIDE-Module-Deprecated", false); // NOI18N
  643. if (Boolean.parseBoolean(depr)) {
  644. String message = cache.findProperty(o, "OpenIDE-Module-Deprecation-Message", true); // NOI18N
  645. // XXX use NbEvents? I18N?
  646. // For now, assume this is a developer-oriented message that need not be localized or displayed in a pretty fashion.
  647. Set<String> users = entry.getValue();
  648. if (message != null) {
  649. Util.err.log(Level.WARNING, "the modules {0} use {1} which is deprecated: {2}", new Object[] {users, dep, message});
  650. } else {
  651. Util.err.log(Level.WARNING, "the modules {0} use {1} which is deprecated.", new Object[] {users, dep});
  652. }
  653. }
  654. }
  655. }
  656. public boolean closing(List<Module> modules) {
  657. Util.err.fine("closing: " + modules);
  658. for (Module m: modules) {
  659. Class<? extends ModuleInstall> instClazz = installs.get(m);
  660. if (instClazz != null) {
  661. try {
  662. ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
  663. if (! inst.closing()) {
  664. Util.err.fine("Module " + m + " refused to close");
  665. return false;
  666. }
  667. } catch (RuntimeException re) {
  668. Util.err.log(Level.SEVERE, null, re);
  669. // continue, assume it is trash
  670. } catch (LinkageError le) {
  671. Util.err.log(Level.SEVERE, null, le);
  672. }
  673. }
  674. }
  675. return onStartStop.closing(modules);
  676. }
  677. @Override
  678. public void close(List<Module> modules) {
  679. closeAsync(modules).waitFinished();
  680. }
  681. @Override
  682. public Task closeAsync(List<Module> modules) {
  683. Util.err.fine("close: " + modules);
  684. ev.log(Events.CLOSE);
  685. moduleList.shutDown();
  686. List<Task> waitFor = onStartStop.startClose(modules);
  687. // [PENDING] this may need to write out changed ModuleInstall externalized
  688. // forms...is that really necessary to do here, or isn't it enough to
  689. // do right after loading etc.? Currently these are only written when
  690. // a ModuleInstall has just been restored etc. which is probably fine.
  691. for (Module m : modules) {
  692. Class<? extends ModuleInstall> instClazz = installs.get(m);
  693. if (instClazz != null) {
  694. try {
  695. ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
  696. if (inst == null) throw new IllegalStateException("Inconsistent state: " + instClazz); // NOI18N
  697. inst.close();
  698. } catch (ThreadDeath td) {
  699. throw td;
  700. } catch (Throwable t) {
  701. // Catch even the heavy stuff here, we are going away.
  702. Util.err.log(Level.SEVERE, null, t);
  703. // oh well
  704. }
  705. }
  706. }
  707. waitFor.add(WarmUpSupport.waitTask());
  708. return new ProxyTask(waitFor);
  709. }
  710. private static String cacheCnb;
  711. private static Set<Dependency> cacheDeps;
  712. @Override
  713. protected Set<Dependency> loadDependencies(String cnb) {
  714. return cnb.equals(cacheCnb) ? cacheDeps : null;
  715. }
  716. @SuppressWarnings("unchecked")
  717. static void register(String name, Object obj) {
  718. cacheCnb = name;
  719. cacheDeps = (Set<Dependency>)obj;
  720. }
  721. /** Overridden to perform automatic API upgrades.
  722. * That is, should do nothing on new modules, but for older ones will
  723. * automatically make them depend on things they need.
  724. * This is now all handled from declarative configuration files:
  725. * in the system filesystem, ModuleAutoDeps/*.xml may be added
  726. * according to the DTD "-//NetBeans//DTD Module Automatic Dependencies 1.0//EN".
  727. */
  728. public @Override void refineDependencies(Module m, Set<Dependency> dependencies) {
  729. /* JST-PENDING just tring to comment this out
  730. // All modules implicitly depend on the APIs somehow.
  731. if (!m.getCodeNameBase().equals("org.openide") &&
  732. Util.getModuleDep(dependencies, "org.openide") == null) {
  733. dependencies.addAll(Dependency.create(Dependency.TYPE_MODULE, "org.openide/1 > 0")); // NOI18N
  734. }
  735. */
  736. if (Boolean.getBoolean("org.netbeans.core.modules.NbInstaller.noAutoDeps")) {
  737. // Skip them all - useful for unit tests.
  738. return;
  739. }
  740. AutomaticDependencies.Report rep = AutomaticDependencies.getDefault().refineDependenciesAndReport(m.getCodeNameBase(), dependencies);
  741. if (rep.isModified()) {
  742. Util.err.warning(rep.toString());
  743. }
  744. }
  745. public @Override String[] refineProvides (Module m) {
  746. if (m.getCodeNameBase ().equals ("org.openide.modules")) { // NOI18N
  747. List<String> arr = new ArrayList<String>(4);
  748. CoreBridge.defineOsTokens(arr);
  749. // module format is now 2
  750. arr.add("org.openide.modules.ModuleFormat1"); // NOI18N
  751. arr.add("org.openide.modules.ModuleFormat2"); // NOI18N
  752. return arr.toArray (new String[arr.size()]);
  753. }
  754. return null;
  755. }
  756. public @Override boolean shouldDelegateResource(Module m, Module parent, String pkg) {
  757. //Util.err.fine("sDR: m=" + m + " parent=" + parent + " pkg=" + pkg);
  758. // Cf. #19622:
  759. if (parent == null) {
  760. // Application classpath checks.
  761. for (String cppkg : CLASSPATH_PACKAGES) {
  762. if (pkg.startsWith(cppkg) && !findKosher(m).contains(cppkg)) {
  763. // Undeclared use of a classpath package. Refuse it.
  764. if (LOG.isLoggable(Level.FINE)) {
  765. LOG.fine("Refusing to load classpath package " + pkg + " for " + m.getCodeNameBase() + " without a proper dependency"); // NOI18N
  766. }
  767. return false;
  768. }
  769. }
  770. List<Module.PackageExport> hiddenPackages;
  771. synchronized (hiddenClasspathPackages) {
  772. hiddenPackages = hiddenClasspathPackages.get(m);
  773. }
  774. if (hiddenPackages != null) {
  775. for (Module.PackageExport hidden : hiddenPackages) {
  776. if (hidden.recursive ? pkg.startsWith(hidden.pkg) : pkg.equals(hidden.pkg)) {
  777. if (LOG.isLoggable(Level.FINE)) {
  778. LOG.fine("Refusing to load classpath package " + pkg + " for " + m.getCodeNameBase());
  779. }
  780. return false;
  781. }
  782. }
  783. }
  784. if (!checkBootDelegation(pkg)) {
  785. return false;
  786. }
  787. }
  788. if (LOG.isLoggable(Level.FINER) && /* otherwise ClassCircularityError on LogRecord*/!pkg.equals("java/util/logging/")) {
  789. LOG.finer("Delegating resource " + pkg + " from " + parent + " for " + m.getCodeNameBase());
  790. }
  791. return true;
  792. }
  793. public @Override boolean shouldDelegateClasspathResource(String pkg) {
  794. synchronized (hiddenClasspathPackages) {
  795. for (Map.Entry<Module.PackageExport,List<Module>> entry : hiddenClasspathPackagesReverse.entrySet()) {
  796. Module.PackageExport hidden = entry.getKey();
  797. if (hidden.recursive ? pkg.startsWith(hidden.pkg) : pkg.equals(hidden.pkg)) {
  798. for (Module m : entry.getValue()) {
  799. if (m.isEnabled()) {
  800. if (LOG.isLoggable(Level.FINE)) {
  801. LOG.fine("Refusing to load classpath package " + pkg + " because of " + m.getCodeNameBase());
  802. }
  803. return false;
  804. }
  805. }
  806. }
  807. }
  808. }
  809. return checkBootDelegation(pkg);
  810. }
  811. private boolean checkBootDelegation(String pkg) {
  812. String del = System.getProperty("netbeans.bootdelegation"); // NOI18N
  813. if (del != null) {
  814. if (!pkg.startsWith("java/")) {
  815. boolean allowed = false;
  816. for (String s : del.split(",")) {
  817. s = s.trim();
  818. if (s.endsWith(".*")) {
  819. s = s.substring(0, s.length() - 2).replace('.', '/') + '/';
  820. if (pkg.startsWith(s) && pkg.length() > s.length()) {
  821. allowed = true;
  822. break;
  823. }
  824. } else {
  825. s = s.replace('.', '/') + '/';
  826. if (pkg.equals(s)) {
  827. allowed = true;
  828. break;
  829. }
  830. }
  831. }
  832. if (!allowed) {
  833. return false;
  834. }
  835. }
  836. }
  837. return true;
  838. }
  839. private static final String[] CLASSPATH_PACKAGES = {
  840. // core.jar shall be inaccessible
  841. "org/netbeans/core/startup/",
  842. // Do not add JRE packages here! See issue #96711 for the alternative.
  843. };
  844. private Set<String> findKosher(Module m) {
  845. Set<String> s = kosherPackages.get(m);
  846. if (s == null) {
  847. s = new HashSet<String>();
  848. Set<Dependency> deps = m.getDependencies();
  849. SpecificationVersion openide = Util.getModuleDep(deps, "org.openide"); // NOI18N
  850. boolean pre27853 = (openide == null || openide.compareTo(new SpecificationVersion("1.3.12")) < 0); // NOI18N
  851. for (Dependency dep : deps) {
  852. // Extend this for other classpath modules:
  853. if (dep.getType() == Dependency.TYPE_MODULE &&
  854. dep.getName().equals("org.netbeans.core.startup/1")) { // NOI18N
  855. // Various modules (incl. o.n.core) dep on this as friends and need to access it.
  856. s.add("org/netbeans/core/startup/"); // NOI18N
  857. } else if (pre27853 && dep.getType() == Dependency.TYPE_MODULE) {
  858. // Module dependency. If a package was kosher for A and B depends
  859. // on A, we let B use it undeclared. Cf. javacvs -> vcscore & RE.
  860. // But #27853: only do this for old modules.
  861. String name = dep.getName();
  862. int idx = name.indexOf('/');
  863. if (idx != -1) {
  864. name = name.substring(0, idx);
  865. }
  866. Module other = mgr.get(name);
  867. if (other == null) throw new IllegalStateException("Should have found dep " + dep + " from " + m); // NOI18N
  868. s.addAll(findKosher(other));
  869. } else if (dep.getType() == Dependency.TYPE_PACKAGE) {
  870. String depname = dep.getName();
  871. String req;
  872. int idx = depname.indexOf('['); // NOI18N
  873. if (idx == -1) {
  874. // depname = org.apache.xerces.parsers
  875. // req = org/apache/xerces/parsers/
  876. req = depname.replace('.', '/').concat("/"); // NOI18N
  877. } else if (idx == 0) {
  878. // depname = [org.apache.xerces.parsers.DOMParser]
  879. // req = org/apache/xerces/parsers/
  880. int idx2 = depname.lastIndexOf('.');
  881. req = depname.substring(1, idx2).replace('.', '/').concat("/"); // NOI18N
  882. } else {
  883. // depname = org.apache.xerces.parsers[DOMParser]
  884. // req = org/apache/xerces/parsers/
  885. req = depname.substring(0, idx).replace('.', '/').concat("/"); // NOI18N
  886. }
  887. for (String cppkg : CLASSPATH_PACKAGES) {
  888. if (req.startsWith(cppkg)) {
  889. // Module requested this exact package or some subpackage or
  890. // a class in one of these packages; it is kosher.
  891. s.add(cppkg);
  892. }
  893. }
  894. }
  895. }
  896. if (s.isEmpty()) s = Collections.<String>emptySet();
  897. kosherPackages.put(m, s);
  898. }
  899. return s;
  900. }
  901. /** Information about contents of some JARs on the startup classpath (both lib/ and lib/ext/).
  902. * The first item in each is a prefix for JAR filenames.
  903. * For a JAR matching such a prefix, the remaining items are entries
  904. * from CLASSPATH_PACKAGES.
  905. * <p>In this case, the JAR is only on a module's effective classpath in case
  906. * {@link #findKosher} reports that at least one of the packages is requested -
  907. * in which case that package and any descendants, but not other packages in the JAR,
  908. * are included in the effective classpath.
  909. * <p>JARs which are not listed here but are in lib/ or lib/ext/ are assumed to be
  910. * in the effective classpath of every module, automatically.
  911. * <p>Note that updater.jar is *not* on the startup classpath and is explicitly
  912. * added to autoupdate.jar via Class-Path, so it need not concern us here -
  913. * it should appear on the effective classpath of autoupdate only.
  914. * @see "#22466"
  915. */
  916. private static final String[][] CLASSPATH_JARS = {
  917. {"core", "org/netbeans/core/", "org/netbeans/beaninfo/"}, // NOI18N
  918. // No one ought to be using boot.jar:
  919. {"boot"}, // NOI18N
  920. };
  921. /** Get the effective "classpath" used by a module.
  922. * Specific syntax: classpath entries as usual, but
  923. * they may be qualified with packages, e.g.:
  924. * <code>/path/to/api-module.jar[org.netbeans.api.foo.*,org.netbeans.spi.foo.**]:/path/to/wide-open.jar</code>
  925. * @see ModuleSystem#getEffectiveClasspath
  926. * @see "#22466"
  927. */
  928. String getEffectiveClasspath(Module m) {
  929. if (!m.isEnabled()) {
  930. // For disabled modules, we do not know for sure what it can load.
  931. return ""; // NOI18N
  932. }
  933. // The classpath entries - each is a filename possibly followed by package qualifications.
  934. List<String> l = new ArrayList<String>(100);
  935. // Start with boot classpath.
  936. createBootClassPath(l);
  937. // Move on to "startup classpath", qualified by applicable package deps etc.
  938. // Fixed classpath modules don't get restricted in this way.
  939. Set<String> kosher = m.isFixed() ? null : findKosher(m);
  940. StringTokenizer tok = new StringTokenizer(System.getProperty("java.class.path", ""), File.pathSeparator);
  941. while (tok.hasMoreTokens()) {
  942. addStartupClasspathEntry(new File(tok.nextToken()), l, kosher);
  943. }
  944. // See org.netbeans.Main for actual computation of the dynamic classpath.
  945. tok = new StringTokenizer(System.getProperty("netbeans.dynamic.classpath", ""), File.pathSeparator);
  946. while (tok.hasMoreTokens()) {
  947. addStartupClasspathEntry(new File(tok.nextToken()), l, kosher);
  948. }
  949. // Finally include this module and its dependencies recursively.
  950. // Modules whose direct classpath has already been added to the list:
  951. Set<Module> modulesConsidered = new HashSet<Module>(50);
  952. // Code names of modules on which this module has an impl dependency
  953. // (so can use any package):
  954. Set<String> implDeps = new HashSet<String>(10);
  955. for (Dependency dep : m.getDependencies()) {
  956. // Remember, provide-require deps do not affect classpath!
  957. if (dep.getType() == Dependency.TYPE_MODULE && dep.getComparison() == Dependency.COMPARE_IMPL) {
  958. // We can assume the impl dep has the correct version;
  959. // otherwise this module could not have been enabled to begin with.
  960. implDeps.add(dep.getName());
  961. }
  962. }
  963. SpecificationVersion openide = Util.getModuleDep(m.getDependencies(), "org.openide"); // NOI18N
  964. boolean pre27853 = (openide == null || openide.compareTo(new SpecificationVersion("1.3.12")) < 0); // NOI18N
  965. // #27853: only make recursive for old modules.
  966. addModuleClasspathEntries(m, m, modulesConsidered, implDeps, l, pre27853 ? Integer.MAX_VALUE : 1);
  967. // Done, package it all up as a string.
  968. StringBuilder buf = new StringBuilder(l.size() * 100 + 1);
  969. for (String s: l) {
  970. if (buf.length() > 0) {
  971. buf.append(File.pathSeparatorChar);
  972. }
  973. buf.append(s);
  974. }
  975. return buf.toString();
  976. }
  977. // Copied from NbClassPath:
  978. private static void createBootClassPath(List<String> l) {
  979. // boot
  980. String boot = System.getProperty("sun.boot.class.path"); // NOI18N
  981. if (boot != null) {
  982. StringTokenizer tok = new StringTokenizer(boot, File.pathSeparator);
  983. while (tok.hasMoreTokens()) {
  984. l.add(tok.nextToken());
  985. }
  986. }
  987. // std extensions
  988. String extensions = System.getProperty("java.ext.dirs"); // NOI18N
  989. if (extensions != null) {
  990. for (StringTokenizer st = new StringTokenizer(extensions, File.pathSeparator); st.hasMoreTokens();) {
  991. File dir = new File(st.nextToken());
  992. File[] entries = dir.listFiles();
  993. if (entries != null) {
  994. for (File f : entries) {
  995. String name = f.getName().toLowerCase(Locale.US);
  996. if (name.endsWith(".zip") || name.endsWith(".jar")) { // NOI18N
  997. l.add(f.getAbsolutePath());
  998. }
  999. }
  1000. }
  1001. }
  1002. }
  1003. }
  1004. /** Add a classpath entry from the lib/ or lib/ext/ dirs, if appropriate.
  1005. * @param entry a classpath entry; either a directory, or a JAR file which might be controlled
  1006. * @param cp the classpath (<code>List&lt;String&gt;</code>) to add to
  1007. * @param kosher known packages which may be accessed (<code>Set&lt;String&gt;</code>), or null for no restrictions
  1008. * @see "#22466"
  1009. */
  1010. private static void addStartupClasspathEntry(File cpEntry, List<String> cp, Set<String> kosher) {
  1011. if (cpEntry.isDirectory()) {
  1012. cp.add(cpEntry.getAbsolutePath());
  1013. return;
  1014. }
  1015. // JAR or ZIP. Check whether we can access it.
  1016. String name = cpEntry.getName();
  1017. for (String[] cpjar : CLASSPATH_JARS) {
  1018. if (kosher != null && name.startsWith(cpjar[0])) {
  1019. // Restricted JAR.
  1020. StringBuilder entry = null; // will be set if there are any packages
  1021. for (int k = 1; k < cpjar.length; k++) {
  1022. String pkg = cpjar[k];
  1023. if (kosher.contains(pkg)) {
  1024. // Module is permitted to use this package.
  1025. if (entry == null) {
  1026. entry = new StringBuilder(100);
  1027. entry.append(cpEntry.getAbsolute