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: import java.io.File;
045: import java.io.IOException;
046: import java.io.InputStream;
047: import java.lang.reflect.Method;
048: import java.net.URL;
049: import java.net.URLConnection;
050: import java.security.AllPermission;
051: import java.security.CodeSource;
052: import java.security.PermissionCollection;
053: import java.security.Permissions;
054: import java.util.ArrayList;
055: import java.util.Collection;
056: import java.util.Enumeration;
057: import java.util.HashSet;
058: import java.util.List;
059: import java.util.Set;
060: import java.util.StringTokenizer;
061: import org.openide.util.Lookup;
062: import org.openide.util.lookup.Lookups;
063:
064: /** Bootstrap main class.
065: * @author Jaroslav Tulach, Jesse Glick
066: */
067: final class MainImpl extends Object {
068:
069: /** Starts the IDE.
070: * @param args the command line arguments
071: * @throws Exception for lots of reasons
072: */
073: public static void main(String args[]) throws Exception {
074: java.lang.reflect.Method[] m = new java.lang.reflect.Method[1];
075: int res = execute(args, System.in, System.out, System.err, m);
076: if (res == -1) {
077: // Connected to another running NB instance and succeeded in making a call.
078: return;
079: } else if (res != 0) {
080: // Some CLIHandler refused the invocation
081: System.exit(res);
082: }
083:
084: m[0].invoke(null, new Object[] { args });
085: }
086:
087: /** Returns string describing usage of the system. Does that by talking to
088: * all registered handlers and asking them to show their usage.
089: *
090: * @return the usage string for the system
091: */
092: public static String usage() throws Exception {
093: java.io.ByteArrayOutputStream os = new java.io.ByteArrayOutputStream();
094: java.io.ByteArrayOutputStream err = new java.io.ByteArrayOutputStream();
095:
096: String[] newArgs = { "--help" };
097:
098: execute(newArgs, System.in, os, err, null);
099: return new String(os.toByteArray());
100: }
101:
102: /** Constructs the correct ClassLoader, finds main method to execute
103: * and invokes all registered CLIHandlers.
104: *
105: * @param args the arguments to pass to the handlers
106: * @param reader the input stream reader for the handlers
107: * @param writer the output stream for the handlers
108: * @param methodToCall null or array with one item that will be set to
109: * a method that shall be executed as the main application
110: */
111: static int execute(String[] args, java.io.InputStream reader,
112: java.io.OutputStream writer, java.io.OutputStream error,
113: java.lang.reflect.Method[] methodToCall) throws Exception {
114: // #42431: turn off jar: caches, they are evil
115: // Note that setDefaultUseCaches changes a static field
116: // yet for some reason it is an instance method!
117: new URLConnection(MainImpl.class.getResource("Main.class")) { // NOI18N
118: public void connect() throws IOException {
119: }
120: }.setDefaultUseCaches(false);
121:
122: ArrayList<File> list = new ArrayList<File>();
123:
124: HashSet<File> processedDirs = new HashSet<File>();
125: String home = System.getProperty("netbeans.home"); // NOI18N
126: if (home != null) {
127: build_cp(new File(home), list, processedDirs);
128: }
129: // #34069: need to do the same for nbdirs.
130: String nbdirs = System.getProperty("netbeans.dirs"); // NOI18N
131: if (nbdirs != null) {
132: StringTokenizer tok = new StringTokenizer(nbdirs,
133: File.pathSeparator);
134: while (tok.hasMoreTokens()) {
135: // passing false as last argument as we need to initialize openfile-cli.jar
136: build_cp(new File(tok.nextToken()), list, processedDirs);
137: }
138: }
139:
140: //
141: // prepend classpath
142: //
143: String prepend = System.getProperty("netbeans.classpath"); // NOI18N
144: if (prepend != null) {
145: StringTokenizer tok = new StringTokenizer(prepend,
146: File.pathSeparator);
147: while (tok.hasMoreElements()) {
148: File f = new File(tok.nextToken());
149: list.add(0, f);
150: }
151: }
152:
153: // Compute effective dynamic classpath (mostly lib/*.jar) for TopLogging, NbInstaller:
154: StringBuffer buf = new StringBuffer(1000);
155: for (File o : list) {
156: String f = o.getAbsolutePath();
157: if (buf.length() > 0) {
158: buf.append(File.pathSeparatorChar);
159: }
160: buf.append(f);
161: }
162: System
163: .setProperty("netbeans.dynamic.classpath", buf
164: .toString());
165:
166: BootClassLoader loader = new BootClassLoader(list,
167: new ClassLoader[] { MainImpl.class.getClassLoader() });
168:
169: // Needed for Lookup.getDefault to find NbTopManager.Lkp.
170: // Note that ModuleManager.updateContextClassLoaders will later change
171: // the loader on this and other threads to be MM.SystemClassLoader anyway.
172: Thread.currentThread().setContextClassLoader(loader);
173:
174: //
175: // Evaluate command line interfaces and lock the user directory
176: //
177:
178: CLIHandler.Status result;
179: result = CLIHandler.initialize(args, reader, writer, error,
180: loader, true, false, loader);
181: if (result.getExitCode() == CLIHandler.Status.CANNOT_CONNECT) {
182: int value = javax.swing.JOptionPane.showConfirmDialog(null,
183: java.util.ResourceBundle.getBundle(
184: "org/netbeans/Bundle").getString(
185: "MSG_AlreadyRunning"),
186: java.util.ResourceBundle.getBundle(
187: "org/netbeans/Bundle").getString(
188: "MSG_AlreadyRunningTitle"),
189: javax.swing.JOptionPane.OK_CANCEL_OPTION,
190: javax.swing.JOptionPane.WARNING_MESSAGE);
191: if (value == javax.swing.JOptionPane.OK_OPTION) {
192: result = CLIHandler.initialize(args, reader, writer,
193: error, loader, true, true, loader);
194: }
195:
196: }
197:
198: String className = System.getProperty("netbeans.mainclass",
199: "org.netbeans.core.startup.Main" // NOI18N
200: );
201:
202: Class<?> c = loader.loadClass(className);
203: Method m = c.getMethod("main", String[].class); // NOI18N
204:
205: if (methodToCall != null) {
206: methodToCall[0] = m;
207: }
208:
209: return result.getExitCode();
210: }
211:
212: /**
213: * Call when the system is up and running, to complete handling of
214: * delayed command-line options like -open FILE.
215: */
216: public static void finishInitialization() {
217: int r = CLIHandler.finishInitialization(false);
218: if (r != 0) {
219: // Not much to do about it.
220: System.err
221: .println("Post-initialization command-line options could not be run."); // NOI18N
222: //System.err.println("r=" + r + " args=" + java.util.Arrays.asList(args.getArguments()));
223: }
224: }
225:
226: static final class BootClassLoader extends JarClassLoader implements
227: Runnable {
228: private Lookup metaInf;
229:
230: private List<CLIHandler> handlers;
231:
232: public BootClassLoader(List<File> cp, ClassLoader[] parents) {
233: super (cp, parents);
234:
235: metaInf = Lookups.metaInfServices(this );
236:
237: String value = null;
238: try {
239: if (cp.isEmpty()) {
240: value = searchBuildNumber(this
241: .getResources("META-INF/MANIFEST.MF"));
242: } else {
243: value = searchBuildNumber(this
244: .simpleFindResources("META-INF/MANIFEST.MF"));
245: }
246: } catch (IOException ex) {
247: ex.printStackTrace();
248: }
249:
250: if (value == null) {
251: System.err
252: .println("Cannot set netbeans.buildnumber property no OpenIDE-Module-Implementation-Version found"); // NOI18N
253: } else {
254: System.setProperty("netbeans.buildnumber", value); // NOI18N
255: }
256: }
257:
258: /** @param en enumeration of URLs */
259: private static String searchBuildNumber(Enumeration<URL> en) {
260: String value = null;
261: try {
262: java.util.jar.Manifest mf;
263: URL u = null;
264: while (en.hasMoreElements()) {
265: u = en.nextElement();
266: InputStream is = u.openStream();
267: mf = new java.util.jar.Manifest(is);
268: is.close();
269: value = mf.getMainAttributes().getValue(
270: "OpenIDE-Module-Implementation-Version"); // NOI18N
271: if (value != null) {
272: break;
273: }
274: }
275: } catch (IOException ex) {
276: ex.printStackTrace();
277: }
278: return value;
279: }
280:
281: private boolean onlyRunRunOnce;
282:
283: /** Checks for new JARs in netbeans.user */
284: public void run() {
285: // do not call this method twice
286: if (onlyRunRunOnce)
287: return;
288: onlyRunRunOnce = true;
289:
290: ArrayList<File> toAdd = new ArrayList<File>();
291: String user = System.getProperty("netbeans.user"); // NOI18N
292: try {
293: if (user != null) {
294: JarClassLoader.initializeCache();
295:
296: build_cp(new File(user), toAdd, new HashSet<File>());
297:
298: }
299:
300: if (!toAdd.isEmpty()) {
301: addSources(toAdd);
302: metaInf = Lookups.metaInfServices(this );
303: if (handlers != null) {
304: handlers.clear();
305: handlers.addAll(metaInf
306: .lookupAll(CLIHandler.class));
307: }
308: }
309: } catch (IOException ex) {
310: ex.printStackTrace();
311: }
312: }
313:
314: /** Startup optimalization. See issue 27226. */
315: protected PermissionCollection getPermissions(CodeSource cs) {
316: return getAllPermission();
317: }
318:
319: /** Startup optimalization. See issue 27226. */
320: private static PermissionCollection modulePermissions;
321:
322: /** Startup optimalization. See issue 27226. */
323: private static synchronized PermissionCollection getAllPermission() {
324: if (modulePermissions == null) {
325: modulePermissions = new Permissions();
326: modulePermissions.add(new AllPermission());
327: modulePermissions.setReadOnly();
328: }
329: return modulePermissions;
330: }
331:
332: /** For a given classloader finds all registered CLIHandlers.
333: */
334: public final Collection allCLIs() {
335: if (handlers == null) {
336: handlers = new ArrayList<CLIHandler>(metaInf
337: .lookupAll(CLIHandler.class));
338: }
339: return handlers;
340: }
341: } // end of BootClassLoader
342:
343: private static void append_jars_to_cp(File dir,
344: Collection<File> toAdd) throws IOException {
345: if (!dir.isDirectory())
346: return;
347:
348: File[] arr = dir.listFiles();
349: for (int i = 0; i < arr.length; i++) {
350: String n = arr[i].getName();
351: /*
352: if (n.equals("updater.jar") || // NOI18N
353: (dir.getName().equals("locale") && n.startsWith("updater_") && n.endsWith(".jar"))) { // NOI18N
354: // Used by launcher, not by us.
355: continue;
356: }
357: */
358: if (n.endsWith("jar") || n.endsWith("zip")) { // NOI18N
359: toAdd.add(arr[i]);
360: }
361: }
362: }
363:
364: private static void build_cp(File base, Collection<File> toAdd,
365: Set<File> processedDirs) throws java.io.IOException {
366: if (!processedDirs.add(base)) {
367: // already processed
368: return;
369: }
370:
371: append_jars_to_cp(new File(base, "core/patches"), toAdd); // NOI18N
372: append_jars_to_cp(new File(base, "core"), toAdd); // NOI18N
373: // XXX a minor optimization: exclude any unused locale JARs
374: // For example, lib/locale/ might contain:
375: // core_ja.jar
376: // core_f4j.jar
377: // core_f4j_ja.jar
378: // core_f4j_ce.jar
379: // core_f4j_ce_ja.jar
380: // core_ru.jar
381: // core_fr.jar
382: // [etc.]
383: // Only some of these will apply to the current session, based on the
384: // current values of Locale.default and NbBundle.branding.
385: append_jars_to_cp(new File(base, "core/locale"), toAdd); // NOI18N
386: }
387: }
|