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-2006 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: import java.io.File;
045: import java.io.IOException;
046: import java.io.InputStream;
047: import java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Locale;
052: import java.util.MissingResourceException;
053: import java.util.Properties;
054: import java.util.ResourceBundle;
055: import java.util.Set;
056: import java.util.jar.Attributes;
057: import java.util.jar.Manifest;
058: import java.util.logging.Level;
059: import org.openide.util.NbBundle;
060:
061: /** Object representing one module, possibly installed.
062: * Responsible for opening of module JAR file; reading
063: * manifest; parsing basic information such as dependencies;
064: * and creating a classloader for use by the installer.
065: * Methods not defined in ModuleInfo must be called from within
066: * the module manager's read mutex as a rule.
067: * @author Jesse Glick
068: */
069: final class FixedModule extends Module {
070:
071: /** localized properties, only non-null if requested from disabled module */
072: private Properties localizedProps;
073: private final Manifest manifest;
074:
075: /** Create a special-purpose "fixed" JAR. */
076: public FixedModule(ModuleManager mgr, Events ev, Manifest manifest,
077: Object history, ClassLoader classloader)
078: throws InvalidException {
079: this (mgr, ev, manifest, history, classloader, false, false);
080: }
081:
082: /**
083: * Create a special-purpose "fixed" JAR which may nonetheless be marked eager or autoload.
084: * @since 2.7
085: */
086: public FixedModule(ModuleManager mgr, Events ev, Manifest manifest,
087: Object history, ClassLoader classloader, boolean autoload,
088: boolean eager) throws InvalidException {
089: super (mgr, ev, history, classloader, autoload, eager);
090: this .manifest = manifest;
091: loadLocalizedPropsClasspath();
092: parseManifest();
093: }
094:
095: public @Override
096: Manifest getManifest() {
097: return manifest;
098: }
099:
100: /** Get a localized attribute.
101: * First, if OpenIDE-Module-Localizing-Bundle was given, the specified
102: * bundle file (in all locale JARs as well as base JAR) is searched for
103: * a key of the specified name.
104: * Otherwise, the manifest's main attributes are searched for an attribute
105: * with the specified name, possibly with a locale suffix.
106: * If the attribute name contains a slash, and there is a manifest section
107: * named according to the part before the last slash, then this section's attributes
108: * are searched instead of the main attributes, and for the attribute listed
109: * after the slash. Currently this would only be useful for localized filesystem
110: * names. E.g. you may request the attribute org/foo/MyFileSystem.class/Display-Name.
111: * In the future certain attributes known to be dangerous could be
112: * explicitly suppressed from this list; should only be used for
113: * documented localizable attributes such as OpenIDE-Module-Name etc.
114: */
115: public Object getLocalizedAttribute(String attr) {
116: String locb = getManifest().getMainAttributes().getValue(
117: "OpenIDE-Module-Localizing-Bundle"); // NOI18N
118: boolean usingLoader = false;
119: if (locb != null) {
120: if (classloader != null) {
121: if (locb.endsWith(".properties")) { // NOI18N
122: usingLoader = true;
123: String basename = locb.substring(0,
124: locb.length() - 11).replace('/', '.');
125: try {
126: ResourceBundle bundle = NbBundle.getBundle(
127: basename, Locale.getDefault(),
128: classloader);
129: try {
130: return bundle.getString(attr);
131: } catch (MissingResourceException mre) {
132: // Fine, ignore.
133: }
134: } catch (MissingResourceException mre) {
135: Util.err.log(Level.WARNING, null, mre);
136: }
137: } else {
138: Util.err
139: .warning("cannot efficiently load non-*.properties OpenIDE-Module-Localizing-Bundle: "
140: + locb);
141: }
142: }
143: if (!usingLoader) {
144: if (localizedProps != null) {
145: String val = localizedProps.getProperty(attr);
146: if (val != null) {
147: return val;
148: }
149: }
150: }
151: }
152: // Try in the manifest now.
153: int idx = attr.lastIndexOf('/'); // NOI18N
154: if (idx == -1) {
155: // Simple main attribute.
156: return NbBundle.getLocalizedValue(getManifest()
157: .getMainAttributes(), new Attributes.Name(attr));
158: } else {
159: // Attribute of a manifest section.
160: String section = attr.substring(0, idx);
161: String realAttr = attr.substring(idx + 1);
162: Attributes attrs = getManifest().getAttributes(section);
163: if (attrs != null) {
164: return NbBundle.getLocalizedValue(attrs,
165: new Attributes.Name(realAttr));
166: } else {
167: return null;
168: }
169: }
170: }
171:
172: public boolean isFixed() {
173: return true;
174: }
175:
176: /** Similar, but for fixed modules only.
177: * Should be very rarely used: only for classpath modules with a strangely
178: * named OpenIDE-Module-Localizing-Bundle (not *.properties).
179: */
180: private void loadLocalizedPropsClasspath() throws InvalidException {
181: Attributes attr = manifest.getMainAttributes();
182: String locbundle = attr
183: .getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N
184: if (locbundle != null) {
185: Util.err.fine("Localized props in " + locbundle + " for "
186: + attr.getValue("OpenIDE-Module"));
187: try {
188: int idx = locbundle.lastIndexOf('.'); // NOI18N
189: String name, ext;
190: if (idx == -1) {
191: name = locbundle;
192: ext = ""; // NOI18N
193: } else {
194: name = locbundle.substring(0, idx);
195: ext = locbundle.substring(idx);
196: }
197: List<String> suffixes = new ArrayList<String>(10);
198: Iterator<String> it = NbBundle.getLocalizingSuffixes();
199: while (it.hasNext()) {
200: suffixes.add(it.next());
201: }
202: Collections.reverse(suffixes);
203: for (String suffix : suffixes) {
204: String resource = name + suffix + ext;
205: InputStream is = classloader
206: .getResourceAsStream(resource);
207: if (is != null) {
208: Util.err.fine("Found " + resource);
209: if (localizedProps == null) {
210: localizedProps = new Properties();
211: }
212: localizedProps.load(is);
213: }
214: }
215: if (localizedProps == null) {
216: throw new IOException(
217: "Could not find localizing bundle: "
218: + locbundle); // NOI18N
219: }
220: } catch (IOException ioe) {
221: throw (InvalidException) new InvalidException(ioe
222: .toString()).initCause(ioe);
223: }
224: }
225: }
226:
227: /** Get all JARs loaded by this module.
228: * Includes the module itself, any locale variants of the module,
229: * any extensions specified with Class-Path, any locale variants
230: * of those extensions.
231: * The list will be in classpath order (patches first).
232: * Currently the temp JAR is provided in the case of test modules, to prevent
233: * sporadic ZIP file exceptions when background threads (like Java parsing) tries
234: * to open libraries found in the library path.
235: * JARs already present in the classpath are <em>not</em> listed.
236: * @return a <code>List<File></code> of JARs
237: */
238: public List<File> getAllJars() {
239: return Collections.emptyList();
240: }
241:
242: /**
243: * This method can be overriden
244: * in subclasses in case they want to change the reloadable semantix
245: * of the fixed modules.
246: *
247: * @throws IllegalStateException as FixedModule cannot be reloaded
248: */
249: public void setReloadable(boolean r) {
250: throw new IllegalStateException();
251: }
252:
253: /** Reload this module. Access from ModuleManager.
254: * If an exception is thrown, the module is considered
255: * to be in an invalid state.
256: *
257: * @throws IllegalStateException as FixedModule cannot be reloaded
258: */
259: public void reload() throws IOException {
260: throw new IOException("Fixed module cannot be reloaded!"); // NOI18N
261: }
262:
263: // Access from ModuleManager:
264: /** Turn on the classloader. Passed a list of parent modules to use.
265: * The parents should already have had their classloaders initialized.
266: */
267: protected void classLoaderUp(Set parents) throws IOException {
268: return; // no need
269: }
270:
271: /** Turn off the classloader and release all resources. */
272: protected void classLoaderDown() {
273: return; // don't touch it
274: }
275:
276: /** Should be called after turning off the classloader of one or more modules & GC'ing. */
277: protected void cleanup() {
278: return; // don't touch it
279: }
280:
281: /** Notify the module that it is being deleted. */
282: protected void destroy() {
283: }
284:
285: /** String representation for debugging. */
286: public @Override
287: String toString() {
288: String s = "FixedModule:" + getCodeNameBase(); // NOI18N
289: if (!isValid())
290: s += "[invalid]"; // NOI18N
291: return s;
292: }
293: }
|