001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans;
043:
044: // THIS CLASS OUGHT NOT USE NbBundle NOR org.openide CLASSES
045: // OUTSIDE OF openide-util.jar! UI AND FILESYSTEM/DATASYSTEM
046: // INTERACTIONS SHOULD GO ELSEWHERE.
047: // (NbBundle.getLocalizedValue is OK here.)
048:
049: import java.io.File;
050: import java.io.IOException;
051: import java.io.InputStream;
052: import java.security.AllPermission;
053: import java.security.CodeSource;
054: import java.security.PermissionCollection;
055: import java.security.Permissions;
056: import java.util.ArrayList;
057: import java.util.Collections;
058: import java.util.HashMap;
059: import java.util.HashSet;
060: import java.util.List;
061: import java.util.Locale;
062: import java.util.Map;
063: import java.util.MissingResourceException;
064: import java.util.Properties;
065: import java.util.ResourceBundle;
066: import java.util.Set;
067: import java.util.StringTokenizer;
068: import java.util.jar.Attributes;
069: import java.util.jar.JarFile;
070: import java.util.jar.Manifest;
071: import java.util.logging.Level;
072: import java.util.zip.ZipEntry;
073: import org.netbeans.Module.PackageExport;
074: import org.openide.modules.Dependency;
075: import org.openide.util.Exceptions;
076: import org.openide.util.NbBundle;
077:
078: /** Object representing one module, possibly installed.
079: * Responsible for opening of module JAR file; reading
080: * manifest; parsing basic information such as dependencies;
081: * and creating a classloader for use by the installer.
082: * Methods not defined in ModuleInfo must be called from within
083: * the module manager's read mutex as a rule.
084: * @author Jesse Glick
085: */
086: final class StandardModule extends Module {
087:
088: /** JAR file holding the module */
089: private final File jar;
090: /** if reloadable, temporary JAR file actually loaded from */
091: private File physicalJar = null;
092: private Manifest manifest;
093:
094: /** Map from extension JARs to sets of JAR that load them via Class-Path.
095: * Used only for debugging purposes, so that a warning is printed if two
096: * different modules try to load the same extension (which would cause them
097: * to both load their own private copy, which may not be intended).
098: */
099: private static final Map<File, Set<File>> extensionOwners = new HashMap<File, Set<File>>();
100: /** Simple registry of JAR files used as modules.
101: * Used only for debugging purposes, so that we can be sure
102: * that no one is using Class-Path to refer to other modules.
103: */
104: private static final Set<File> moduleJARs = new HashSet<File>();
105:
106: /** Set of locale-variants JARs for this module (or null).
107: * Added explicitly to classloader, and can be used by execution engine.
108: */
109: private Set<File> localeVariants = null;
110: /** Set of extension JARs that this module loads via Class-Path (or null).
111: * Can be used e.g. by execution engine. (#9617)
112: */
113: private Set<File> plainExtensions = null;
114: /** Set of localized extension JARs derived from plainExtensions (or null).
115: * Used to add these to the classloader. (#9348)
116: * Can be used e.g. by execution engine.
117: */
118: private Set<File> localeExtensions = null;
119: /** Patches added at the front of the classloader (or null).
120: * Files are assumed to be JARs; directories are themselves.
121: */
122: private Set<File> patches = null;
123:
124: /** localized properties, only non-null if requested from disabled module */
125: private Properties localizedProps;
126:
127: /** Use ModuleManager.create as a factory. */
128: public StandardModule(ModuleManager mgr, Events ev, File jar,
129: Object history, boolean reloadable, boolean autoload,
130: boolean eager) throws IOException {
131: super (mgr, ev, history, reloadable, autoload, eager);
132: this .jar = jar;
133: loadManifest();
134: parseManifest();
135: findExtensionsAndVariants(manifest);
136: // Check if some other module already listed this one in Class-Path.
137: // For the chronologically reverse case, see findExtensionsAndVariants().
138: Set<File> bogoOwners = extensionOwners.get(jar);
139: if (bogoOwners != null) {
140: Util.err
141: .warning("module "
142: + jar
143: + " was incorrectly placed in the Class-Path of other JARs "
144: + bogoOwners
145: + "; please use OpenIDE-Module-Module-Dependencies instead");
146: }
147: moduleJARs.add(jar);
148: }
149:
150: public @Override
151: Manifest getManifest() {
152: if (manifest == null) {
153: try {
154: loadManifest();
155: } catch (IOException x) {
156: Util.err.log(Level.WARNING,
157: "While loading manifest for " + this , x);
158: manifest = new Manifest();
159: }
160: }
161: return manifest;
162: }
163:
164: public @Override
165: void releaseManifest() {
166: manifest = null;
167: }
168:
169: /** Get a localized attribute.
170: * First, if OpenIDE-Module-Localizing-Bundle was given, the specified
171: * bundle file (in all locale JARs as well as base JAR) is searched for
172: * a key of the specified name.
173: * Otherwise, the manifest's main attributes are searched for an attribute
174: * with the specified name, possibly with a locale suffix.
175: * If the attribute name contains a slash, and there is a manifest section
176: * named according to the part before the last slash, then this section's attributes
177: * are searched instead of the main attributes, and for the attribute listed
178: * after the slash. Currently this would only be useful for localized filesystem
179: * names. E.g. you may request the attribute org/foo/MyFileSystem.class/Display-Name.
180: * In the future certain attributes known to be dangerous could be
181: * explicitly suppressed from this list; should only be used for
182: * documented localizable attributes such as OpenIDE-Module-Name etc.
183: */
184: public Object getLocalizedAttribute(String attr) {
185: String locb = getManifest().getMainAttributes().getValue(
186: "OpenIDE-Module-Localizing-Bundle"); // NOI18N
187: boolean usingLoader = false;
188: if (locb != null) {
189: if (classloader != null) {
190: if (locb.endsWith(".properties")) { // NOI18N
191: usingLoader = true;
192: String basename = locb.substring(0,
193: locb.length() - 11).replace('/', '.');
194: try {
195: ResourceBundle bundle = NbBundle.getBundle(
196: basename, Locale.getDefault(),
197: classloader);
198: try {
199: return bundle.getString(attr);
200: } catch (MissingResourceException mre) {
201: // Fine, ignore.
202: }
203: } catch (MissingResourceException mre) {
204: Util.err.log(Level.WARNING, null, mre);
205: }
206: } else {
207: Util.err
208: .warning("cannot efficiently load non-*.properties OpenIDE-Module-Localizing-Bundle: "
209: + locb);
210: }
211: }
212: if (!usingLoader) {
213: if (localizedProps == null) {
214: Util.err
215: .log(
216: Level.FINE,
217: "Trying to get localized attr {0} from disabled module {1}",
218: new Object[] { attr,
219: getCodeNameBase() });
220: try {
221: // check if the jar file still exists (see issue 82480)
222: if (jar != null && jar.isFile()) {
223: JarFile jarFile = new JarFile(jar, false);
224: try {
225: loadLocalizedProps(jarFile,
226: getManifest());
227: } finally {
228: jarFile.close();
229: }
230: } else {
231: Util.err
232: .log(
233: Level.FINE,
234: "Cannot get localized attr {0} from module {1} (missing or deleted JAR file: {2})",
235: new Object[] { attr,
236: getCodeNameBase(),
237: jar });
238: }
239: } catch (IOException ioe) {
240: Util.err.log(Level.WARNING, jar
241: .getAbsolutePath(), ioe);
242: }
243: }
244: if (localizedProps != null) {
245: String val = localizedProps.getProperty(attr);
246: if (val != null) {
247: return val;
248: }
249: }
250: }
251: }
252: // Try in the manifest now.
253: int idx = attr.lastIndexOf('/'); // NOI18N
254: if (idx == -1) {
255: // Simple main attribute.
256: return NbBundle.getLocalizedValue(getManifest()
257: .getMainAttributes(), new Attributes.Name(attr));
258: } else {
259: // Attribute of a manifest section.
260: String section = attr.substring(0, idx);
261: String realAttr = attr.substring(idx + 1);
262: Attributes attrs = getManifest().getAttributes(section);
263: if (attrs != null) {
264: return NbBundle.getLocalizedValue(attrs,
265: new Attributes.Name(realAttr));
266: } else {
267: return null;
268: }
269: }
270: }
271:
272: public boolean owns(Class clazz) {
273: ClassLoader cl = clazz.getClassLoader();
274: if (cl instanceof Util.ModuleProvider) {
275: return ((Util.ModuleProvider) cl).getModule() == this ;
276: }
277: return false;
278:
279: }
280:
281: public boolean isFixed() {
282: return false;
283: }
284:
285: /** Get the JAR this module is packaged in.
286: * May be null for modules installed specially, e.g.
287: * automatically from the classpath.
288: * @see #isFixed
289: */
290: public File getJarFile() {
291: return jar;
292: }
293:
294: /** Create a temporary test JAR if necessary.
295: * This is primarily necessary to work around a Java bug,
296: * #4405789, which is marked as fixed so might be obsolete.
297: */
298: private void ensurePhysicalJar() throws IOException {
299: if (reloadable && physicalJar == null) {
300: physicalJar = Util.makeTempJar(jar);
301: }
302: }
303:
304: private void destroyPhysicalJar() {
305: if (physicalJar != null) {
306: if (physicalJar.isFile()) {
307: if (!physicalJar.delete()) {
308: Util.err.warning("temporary JAR " + physicalJar
309: + " not currently deletable.");
310: } else {
311: Util.err.fine("deleted: " + physicalJar);
312: }
313: }
314: physicalJar = null;
315: } else {
316: Util.err.fine("no physicalJar to delete for " + this );
317: }
318: }
319:
320: /** Open the JAR, load its manifest, and do related things. */
321: private void loadManifest() throws IOException {
322: Util.err.fine("loading manifest of " + jar);
323: File jarBeingOpened = null; // for annotation purposes
324: try {
325: if (reloadable) {
326: // Never try to cache reloadable JARs.
327: jarBeingOpened = physicalJar; // might be null
328: ensurePhysicalJar();
329: jarBeingOpened = physicalJar; // might have changed
330: JarFile jarFile = new JarFile(physicalJar, false);
331: try {
332: Manifest m = jarFile.getManifest();
333: if (m == null)
334: throw new IOException("No manifest found in "
335: + physicalJar); // NOI18N
336: manifest = m;
337: } finally {
338: jarFile.close();
339: }
340: } else {
341: jarBeingOpened = jar;
342: manifest = getManager().loadManifest(jar);
343: }
344: } catch (IOException e) {
345: if (jarBeingOpened != null) {
346: Exceptions.attachMessage(e,
347: "While loading manifest from: "
348: + jarBeingOpened); // NOI18N
349: }
350: throw e;
351: }
352: }
353:
354: /** Find any extensions loaded by the module, as well as any localized
355: * variants of the module or its extensions.
356: */
357: private void findExtensionsAndVariants(Manifest m) {
358: assert jar != null : "Cannot load extensions from classpath module "
359: + getCodeNameBase();
360: localeVariants = null;
361: List<File> l = Util.findLocaleVariantsOf(jar);
362: if (!l.isEmpty()) {
363: localeVariants = new HashSet<File>(l);
364: }
365: plainExtensions = null;
366: localeExtensions = null;
367: String classPath = m.getMainAttributes().getValue(
368: Attributes.Name.CLASS_PATH);
369: if (classPath != null) {
370: StringTokenizer tok = new StringTokenizer(classPath);
371: while (tok.hasMoreTokens()) {
372: String ext = tok.nextToken();
373: if (new File(ext).isAbsolute()
374: || ext.indexOf("../") != -1) { // NOI18N
375: Util.err
376: .warning("Class-Path value "
377: + ext
378: + " from "
379: + jar
380: + " is illegal according to the Java Extension Mechanism: must be relative and not move up directories");
381: }
382: File extfile = new File(jar.getParentFile(), ext
383: .replace('/', File.separatorChar));
384: if (!extfile.exists()) {
385: // Ignore unloadable extensions.
386: Util.err.warning("Class-Path value " + ext
387: + " from " + jar + " cannot be found at "
388: + extfile);
389: continue;
390: }
391: //No need to sync on extensionOwners - we are in write mutex
392: Set<File> owners = extensionOwners.get(extfile);
393: if (owners == null) {
394: owners = new HashSet<File>(2);
395: owners.add(jar);
396: extensionOwners.put(extfile, owners);
397: } else if (!owners.contains(jar)) {
398: owners.add(jar);
399: events.log(Events.EXTENSION_MULTIPLY_LOADED,
400: extfile, owners);
401: } // else already know about it (OK or warned)
402: // Also check to make sure it is not a module JAR! See constructor for the reverse case.
403: if (moduleJARs.contains(extfile)) {
404: Util.err
405: .warning("Class-Path value "
406: + ext
407: + " from "
408: + jar
409: + " illegally refers to another module; use OpenIDE-Module-Module-Dependencies instead");
410: }
411: if (plainExtensions == null)
412: plainExtensions = new HashSet<File>();
413: plainExtensions.add(extfile);
414: l = Util.findLocaleVariantsOf(extfile);
415: if (!l.isEmpty()) {
416: if (localeExtensions == null) {
417: localeExtensions = new HashSet<File>();
418: }
419: localeExtensions.addAll(l);
420: }
421: }
422: }
423: // #9273: load any modules/patches/this-code-name/*.jar files first:
424: File patchdir = new File(new File(jar.getParentFile(),
425: "patches"), // NOI18N
426: getCodeNameBase().replace('.', '-')); // NOI18N
427: scanForPatches(patchdir);
428: // Use of the following system property is not supported, but is used
429: // by e.g. XTest to influence installed modules without changing the build.
430: // Format is -Dnetbeans.patches.org.nb.mods.foo=/path/to.file.jar:/path/to/dir
431: String patchesClassPath = System
432: .getProperty("netbeans.patches." + getCodeNameBase()); // NOI18N
433: if (patchesClassPath != null) {
434: StringTokenizer tokenizer = new StringTokenizer(
435: patchesClassPath, File.pathSeparator);
436: while (tokenizer.hasMoreTokens()) {
437: String element = tokenizer.nextToken();
438: File fileElement = new File(element);
439: if (fileElement.exists()) {
440: if (patches == null) {
441: patches = new HashSet<File>(15);
442: }
443: patches.add(fileElement);
444: }
445: }
446: }
447: Util.err.fine("localeVariants of " + jar + ": "
448: + localeVariants);
449: Util.err.fine("plainExtensions of " + jar + ": "
450: + plainExtensions);
451: Util.err.fine("localeExtensions of " + jar + ": "
452: + localeExtensions);
453: Util.err.fine("patches of " + jar + ": " + patches);
454: if (patches != null) {
455: for (File patch : patches) {
456: events.log(Events.PATCH, patch);
457: }
458: }
459: }
460:
461: /** Scans a directory for possible patch JARs. */
462: private void scanForPatches(File patchdir) {
463: if (!patchdir.isDirectory()) {
464: return;
465: }
466: File[] jars = patchdir.listFiles(Util.jarFilter());
467: if (jars != null) {
468: for (File patchJar : jars) {
469: if (patches == null) {
470: patches = new HashSet<File>(5);
471: }
472: patches.add(patchJar);
473: }
474: } else {
475: Util.err.warning("Could not search for patches in "
476: + patchdir);
477: }
478: }
479:
480: /** Check if there is any need to load localized properties.
481: * If so, try to load them. Throw an exception if they cannot
482: * be loaded for some reason. Uses an open JAR file for the
483: * base module at least, though may also open locale variants
484: * as needed.
485: * Note: due to #19698, this cache is not usually used; only if you
486: * specifically go to look at the display properties of a disabled module.
487: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=12549">#12549</a>
488: */
489: private void loadLocalizedProps(JarFile jarFile, Manifest m)
490: throws IOException {
491: String locbundle = m.getMainAttributes().getValue(
492: "OpenIDE-Module-Localizing-Bundle"); // NOI18N
493: if (locbundle != null) {
494: // Something requested, read it in.
495: // locbundle is a resource path.
496: {
497: ZipEntry bundleFile = jarFile.getEntry(locbundle);
498: // May not be present in base JAR: might only be in e.g. default locale variant.
499: if (bundleFile != null) {
500: localizedProps = new Properties();
501: InputStream is = jarFile.getInputStream(bundleFile);
502: try {
503: localizedProps.load(is);
504: } finally {
505: is.close();
506: }
507: }
508: }
509: {
510: // Check also for localized variant JARs and load in anything from them as needed.
511: // Note we need to go in the reverse of the usual search order, so as to
512: // overwrite less specific bundles with more specific.
513: int idx = locbundle.lastIndexOf('.'); // NOI18N
514: String name, ext;
515: if (idx == -1) {
516: name = locbundle;
517: ext = ""; // NOI18N
518: } else {
519: name = locbundle.substring(0, idx);
520: ext = locbundle.substring(idx);
521: }
522: List<Util.FileWithSuffix> pairs = Util
523: .findLocaleVariantsWithSuffixesOf(jar);
524: Collections.reverse(pairs);
525: for (Util.FileWithSuffix pair : pairs) {
526: File localeJar = pair.file;
527: String suffix = pair.suffix;
528: String rsrc = name + suffix + ext;
529: JarFile localeJarFile = new JarFile(localeJar,
530: false);
531: try {
532: ZipEntry bundleFile = localeJarFile
533: .getEntry(rsrc);
534: // Need not exist in all locale variants.
535: if (bundleFile != null) {
536: if (localizedProps == null) {
537: localizedProps = new Properties();
538: } // else append and overwrite base-locale values
539: InputStream is = localeJarFile
540: .getInputStream(bundleFile);
541: try {
542: localizedProps.load(is);
543: } finally {
544: is.close();
545: }
546: }
547: } finally {
548: localeJarFile.close();
549: }
550: }
551: }
552: if (localizedProps == null) {
553: // We should have loaded from at least some bundle in there...
554: throw new IOException(
555: "Could not find localizing bundle: "
556: + locbundle); // NOI18N
557: }
558: /* Don't log; too large and annoying:
559: if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) {
560: Util.err.fine("localizedProps=" + localizedProps);
561: }
562: */
563: }
564: }
565:
566: /** Get all JARs loaded by this module.
567: * Includes the module itself, any locale variants of the module,
568: * any extensions specified with Class-Path, any locale variants
569: * of those extensions.
570: * The list will be in classpath order (patches first).
571: * Currently the temp JAR is provided in the case of test modules, to prevent
572: * sporadic ZIP file exceptions when background threads (like Java parsing) tries
573: * to open libraries found in the library path.
574: * JARs already present in the classpath are <em>not</em> listed.
575: * @return a <code>List<File></code> of JARs
576: */
577: public List<File> getAllJars() {
578: List<File> l = new ArrayList<File>();
579: if (patches != null)
580: l.addAll(patches);
581: if (physicalJar != null) {
582: l.add(physicalJar);
583: } else if (jar != null) {
584: l.add(jar);
585: }
586: if (plainExtensions != null)
587: l.addAll(plainExtensions);
588: if (localeVariants != null)
589: l.addAll(localeVariants);
590: if (localeExtensions != null)
591: l.addAll(localeExtensions);
592: return l;
593: }
594:
595: /** Set whether this module is supposed to be reloadable.
596: * Has no immediate effect, only impacts what happens the
597: * next time it is enabled (after having been disabled if
598: * necessary).
599: * Must be called from within a write mutex.
600: * @param r whether the module should be considered reloadable
601: */
602: public void setReloadable(boolean r) {
603: getManager().assertWritable();
604: if (reloadable != r) {
605: reloadable = r;
606: getManager().fireReloadable(this );
607: }
608: }
609:
610: /** Used as a flag to tell if this module was really successfully released.
611: * Currently does not work, so if it cannot be made to work, delete it.
612: * (Someone seems to be holding a strong reference to the classloader--who?!)
613: */
614: private transient boolean released;
615: /** Count which release() call is really being checked. */
616: private transient int releaseCount = 0;
617:
618: /** Reload this module. Access from ModuleManager.
619: * If an exception is thrown, the module is considered
620: * to be in an invalid state.
621: */
622: public void reload() throws IOException {
623: // Probably unnecessary but just in case:
624: destroyPhysicalJar();
625: String codeNameBase1 = getCodeNameBase();
626: localizedProps = null;
627: loadManifest();
628: parseManifest();
629: findExtensionsAndVariants(manifest);
630: String codeNameBase2 = getCodeNameBase();
631: if (!codeNameBase1.equals(codeNameBase2)) {
632: throw new InvalidException(
633: "Code name base changed during reload: "
634: + codeNameBase1 + " -> " + codeNameBase2); // NOI18N
635: }
636: }
637:
638: // Access from ModuleManager:
639: /** Turn on the classloader. Passed a list of parent modules to use.
640: * The parents should already have had their classloaders initialized.
641: */
642: protected void classLoaderUp(Set<Module> parents)
643: throws IOException {
644: Util.err.fine("classLoaderUp on " + this + " with parents "
645: + parents);
646: // Find classloaders for dependent modules and parent to them.
647: List<ClassLoader> loaders = new ArrayList<ClassLoader>(parents
648: .size() + 1);
649: // This should really be the base loader created by org.nb.Main for loading openide etc.:
650: loaders.add(Module.class.getClassLoader());
651: for (Module parent : parents) {
652: PackageExport[] exports = parent.getPublicPackages();
653: if (exports != null && exports.length == 0) {
654: // Check if there is an impl dep here.
655: boolean implDep = false;
656: for (Dependency dep : getDependenciesArray()) {
657: if (dep.getType() == Dependency.TYPE_MODULE
658: && dep.getComparison() == Dependency.COMPARE_IMPL
659: && dep.getName().equals(
660: parent.getCodeName())) {
661: implDep = true;
662: break;
663: }
664: }
665: if (!implDep) {
666: // Nothing exported from here at all, no sense even adding it.
667: // Shortcut; would not harm anything to add it now, but we would
668: // never use it anyway.
669: // Cf. #27853.
670: continue;
671: }
672: }
673: ClassLoader l = parent.getClassLoader();
674: if (parent.isFixed() && loaders.contains(l)) {
675: Util.err
676: .fine("#24996: skipping duplicate classloader from "
677: + parent);
678: continue;
679: }
680: loaders.add(l);
681: }
682: List<File> classp = new ArrayList<File>(3);
683: if (patches != null)
684: classp.addAll(patches);
685:
686: if (reloadable) {
687: ensurePhysicalJar();
688: // Using OPEN_DELETE does not work well with test modules under 1.4.
689: // Random code (URL handler?) still expects the JAR to be there and
690: // it is not.
691: classp.add(physicalJar);
692: } else {
693: classp.add(jar);
694: }
695: // URLClassLoader would not otherwise find these, so:
696: if (localeVariants != null)
697: classp.addAll(localeVariants);
698:
699: if (localeExtensions != null)
700: classp.addAll(localeExtensions);
701:
702: if (plainExtensions != null)
703: classp.addAll(plainExtensions);
704:
705: // #27853:
706: getManager().refineClassLoader(this , loaders);
707:
708: try {
709: classloader = new OneModuleClassLoader(classp, loaders
710: .toArray(new ClassLoader[loaders.size()]));
711: } catch (IllegalArgumentException iae) {
712: // Should not happen, but just in case.
713: throw (IOException) new IOException(iae.toString())
714: .initCause(iae);
715: }
716: }
717:
718: /** Turn off the classloader and release all resources. */
719: protected void classLoaderDown() {
720: if (classloader instanceof ProxyClassLoader) {
721: ((ProxyClassLoader) classloader).destroy();
722: }
723: classloader = null;
724: Util.err.fine("classLoaderDown on " + this + ": releaseCount="
725: + releaseCount + " released=" + released);
726: released = false;
727: }
728:
729: /** Should be called after turning off the classloader of one or more modules & GC'ing. */
730: protected void cleanup() {
731: if (isEnabled())
732: throw new IllegalStateException(
733: "cleanup on enabled module: " + this ); // NOI18N
734: if (classloader != null)
735: throw new IllegalStateException(
736: "cleanup on module with classloader: " + this ); // NOI18N
737: if (!released) {
738: Util.err
739: .fine("Warning: not all resources associated with module "
740: + jar + " were successfully released.");
741: released = true;
742: } else {
743: Util.err.fine("All resources associated with module " + jar
744: + " were successfully released.");
745: }
746: // XXX should this rather be done when the classloader is collected?
747: destroyPhysicalJar();
748: }
749:
750: /** Notify the module that it is being deleted. */
751: public void destroy() {
752: moduleJARs.remove(jar);
753: }
754:
755: /** String representation for debugging. */
756: public String toString() {
757: String s = "StandardModule:" + getCodeNameBase() + " jarFile: "
758: + jar.getAbsolutePath(); // NOI18N
759: if (!isValid())
760: s += "[invalid]"; // NOI18N
761: return s;
762: }
763:
764: /** PermissionCollection with an instance of AllPermission. */
765: private static PermissionCollection modulePermissions;
766:
767: /** @return initialized @see #modulePermission */
768: private static synchronized PermissionCollection getAllPermission() {
769: if (modulePermissions == null) {
770: modulePermissions = new Permissions();
771: modulePermissions.add(new AllPermission());
772: modulePermissions.setReadOnly();
773: }
774: return modulePermissions;
775: }
776:
777: /** Class loader to load a single module.
778: * Auto-localizing, multi-parented, permission-granting, the works.
779: */
780: private class OneModuleClassLoader extends JarClassLoader implements
781: Util.ModuleProvider {
782: private int rc;
783:
784: /** Create a new loader for a module.
785: * @param classp the List of all module jars of code directories;
786: * includes the module itself, its locale variants,
787: * variants of extensions and Class-Path items from Manifest.
788: * The items are JarFiles for jars and Files for directories
789: * @param parents a set of parent classloaders (from other modules)
790: */
791: public OneModuleClassLoader(List<File> classp,
792: ClassLoader[] parents) throws IllegalArgumentException {
793: super (classp, parents, false, StandardModule.this );
794: rc = releaseCount++;
795: }
796:
797: public Module getModule() {
798: return StandardModule.this ;
799: }
800:
801: /** Inherited.
802: * @param cs is ignored
803: * @return PermissionCollection with an AllPermission instance
804: */
805: protected PermissionCollection getPermissions(CodeSource cs) {
806: return getAllPermission();
807: }
808:
809: /** look for JNI libraries also in modules/bin/ */
810: protected String findLibrary(String libname) {
811: String mapped = System.mapLibraryName(libname);
812: File lib = new File(new File(jar.getParentFile(), "lib"),
813: mapped); // NOI18N
814: if (lib.isFile()) {
815: return lib.getAbsolutePath();
816: } else {
817: return null;
818: }
819: }
820:
821: protected boolean shouldDelegateResource(String pkg,
822: ClassLoader parent) {
823: if (!super .shouldDelegateResource(pkg, parent)) {
824: return false;
825: }
826: Module other;
827: if (parent instanceof Util.ModuleProvider) {
828: other = ((Util.ModuleProvider) parent).getModule();
829: } else {
830: other = null;
831: }
832: return getManager().shouldDelegateResource(
833: StandardModule.this , other, pkg);
834: }
835:
836: public String toString() {
837: return super .toString() + "[" + getCodeNameBase() + "]"; // NOI18N
838: }
839:
840: protected void finalize() throws Throwable {
841: super .finalize();
842: Util.err.fine("Finalize for " + this + ": rc=" + rc
843: + " releaseCount=" + releaseCount + " released="
844: + released); // NOI18N
845: if (rc == releaseCount) {
846: // Hurrah! release() worked.
847: released = true;
848: } else {
849: Util.err.fine("Now resources for " + getCodeNameBase()
850: + " have been released."); // NOI18N
851: }
852: }
853: }
854:
855: }
|