blob: 5411943e62638b0af171c04e7247eab129363da2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.core.startup;
import java.beans.Introspector;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.netbeans.ProxyURLStreamHandlerFactory;
import org.netbeans.Stamps;
import org.netbeans.Util;
import org.netbeans.core.startup.layers.SystemFileSystem;
import org.openide.LifecycleManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.Repository;
import org.openide.util.BaseUtilities;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.UserCancelException;
/**
* Main class for NetBeans when run in GUI mode.
*/
public final class Main extends Object {
private static final Logger LOG = Logger.getLogger(Main.class.getName());
/** module subsystem */
private static ModuleSystem moduleSystem;
/** module subsystem is fully ready */
private static boolean moduleSystemInitialized;
/** Prints the text to splash screen or to status line, if available.
*/
public static void setStatusText (String msg) {
Splash.getInstance().print (msg);
if (moduleSystemInitialized) {
CoreBridge.getDefault().setStatusText(msg);
}
}
/** Starts TopThreadGroup which properly overrides uncaughtException
* Further - new thread in the group execs main
*/
public static void main (String[] argv) throws Exception {
TopThreadGroup tg = new TopThreadGroup ("IDE Main", argv); // NOI18N - programatic name
StartLog.logStart ("Forwarding to topThreadGroup"); // NOI18N
tg.start ();
StartLog.logProgress ("Main.main finished"); // NOI18N
}
/** Initializes default stream factory */
public static void initializeURLFactory () {
ProxyURLStreamHandlerFactory.register();
}
/**
* Sets up the custom font size and theme url for the plaf library to
* process.
*/
public static void initUICustomizations() {
if (!CLIOptions.isGui () && CLIOptions.uiClassName == null) {
return;
}
Class uiClass = CLIOptions.uiClass;
// try again loading L&F class, this time with full module system.
if (CLIOptions.uiClassName != null && CLIOptions.uiClass == null) {
// try again
ClassLoader loader = Lookup.getDefault().lookup(ClassLoader.class);
try {
uiClass = loader.loadClass(CLIOptions.uiClassName);
CLIOptions.uiClass = uiClass;
} catch (ClassNotFoundException ex) {
// ignore
System.err.println(NbBundle.getMessage(Main.class, "ERR_UINotFound", CLIOptions.uiClassName));
if (!CLIOptions.isGui ()) {
return;
}
}
}
URL themeURL = null;
boolean wantTheme = Boolean.getBoolean ("netbeans.useTheme") ||
CLIOptions.uiClass != null && CLIOptions.uiClass.getName().indexOf("MetalLookAndFeel") >= 0;
try {
if (wantTheme) {
//Put a couple things into UIDefaults for the plaf library to process if it wants
FileObject fo = FileUtil.getConfigFile("themes.xml"); //NOI18N
if (fo == null) {
// File on SFS failed --> try to load from a jar from path
// /org/netbeans/core/startup/resources/themes.xml
try {
themeURL = new URL("nbresloc:/org/netbeans/core/startup/resources/themes.xml"); //NOI18N
// check whether the file is there:
themeURL.openStream().close();
} catch (IOException ex) {
themeURL = null;
}
} else {
themeURL = fo.toURL();
}
}
} finally {
CoreBridge.getDefault ().initializePlaf(CLIOptions.uiClass, CLIOptions.getFontSize(), themeURL);
}
if (CLIOptions.getFontSize() > 0 && "GTK".equals(UIManager.getLookAndFeel().getID())) { //NOI18N
Util.err.warning(NbBundle.getMessage(Main.class,
"GTK_FONTSIZE_UNSUPPORTED")); //NOI18N
}
StartLog.logProgress("Fonts updated"); // NOI18N
}
/** Get and initialize module subsystem. */
public static ModuleSystem getModuleSystem() {
return getModuleSystem(true);
}
/** Get and possibly initialize module subsystem.
* @param init <code>true</code> to initialize the system if it has not been initialized yet
* @return the module system or <code>null</code> (if the initialization is not requested)
* @since 1.47
*/
public static ModuleSystem getModuleSystem(boolean init) {
synchronized (Main.class) {
if (moduleSystem != null) {
return moduleSystem;
}
if (!init) {
return null;
}
StartLog.logStart ("Modules initialization"); // NOI18N
try {
moduleSystem = new ModuleSystem();
moduleSystem.init(FileUtil.getConfigRoot().getFileSystem());
SystemFileSystem.registerMutex(moduleSystem.getManager().mutex());
} catch (IOException ioe) {
// System will be screwed up.
throw (IllegalStateException) new IllegalStateException("Module system cannot be created").initCause(ioe); // NOI18N
}
StartLog.logProgress ("ModuleSystem created"); // NOI18N
}
moduleSystem.loadBootModules();
moduleSystem.readList();
moduleSystem.restore();
StartLog.logEnd ("Modules initialization"); // NOI18N
moduleSystemInitialized = true;
return moduleSystem;
}
/** Is used to find out whether the system has already been fully initialized
* or not yet. In case you need to access module system that is just being
* initialized consider using {@link #getModuleSystem(boolean)}.
*
* @return true if changes in the lookup shall mean real changes, false if it just
* the first initalization
*/
public static boolean isInitialized() {
return moduleSystemInitialized;
}
/**
* @exception SecurityException if it is called multiple times
*/
static void start (String[] args) throws SecurityException {
StartLog.logEnd ("Forwarding to topThreadGroup"); // NOI18N
StartLog.logStart ("Preparation"); // NOI18N
// just setup some reasonable values for this deprecated property
// 6.2 seems to be like the right version as that is the last one
// that ever saw openide
System.setProperty ("org.openide.specification.version", "6.2"); // NOI18N
System.setProperty ("org.openide.version", "deprecated"); // NOI18N
System.setProperty ("org.openide.major.version", "IDE/1"); // NOI18N
// In the past we derived ${jdk.home} from ${java.home} by appending
// "/.." to the end of ${java.home} assuming that JRE is under JDK
// directory. It does not always work. On MacOS X JDK and JRE files
// are mixed together, thus ${jdk.home} == ${java.home}. In several
// Linux distros JRE and JDK are installed at the same directory level
// with ${jdk.home}/jre a symlink to ${java.home}, which means
// ${java.home}/.. != ${jdk.home}.
//
// Now the launcher can set ${jdk.home} explicitly because it knows
// best where the JDK is.
String jdkHome = System.getProperty("jdk.home"); // NOI18N
if (jdkHome == null) {
jdkHome = System.getProperty("java.home"); // NOI18N
if (!BaseUtilities.isMac()) {
jdkHome += File.separator + ".."; // NOI18N
}
System.setProperty("jdk.home", jdkHome); // NOI18N
}
// initialize the URL factory
initializeURLFactory();
CLIOptions.initialize();
StartLog.logProgress ("Command line parsed"); // NOI18N
// 5. initialize GUI
//Bugfix #35919: Log message to console when initialization of local
//graphics environment fails eg. due to incorrect value of $DISPLAY
//on X Windows (Linux, Solaris). In such case IDE will not start
//so we must inform user about error.
if (CLIOptions.isGui ()) {
try {
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
} catch (java.lang.InternalError exc) {
String s = NbBundle.getMessage(Main.class, "EXC_GraphicsStartFails1", exc.getMessage());
System.out.println(s);
s = NbBundle.getMessage(Main.class, "EXC_GraphicsStartFails2", CLIOptions.getUserDir() + "/var/log/messages.log");
System.out.println(s);
throw exc;
}
}
InstalledFileLocatorImpl.prepareCache();
try {
if (!Boolean.getBoolean("netbeans.full.hack") && !Boolean.getBoolean("netbeans.close")) {
// -----------------------------------------------------------------------------------------------------
// License check
if (!handleLicenseCheck()) {
File userdir = new File(CLIOptions.getUserDir()); // #145936
// #189656: be conservative about what we delete.
deleteRec(new File(userdir, "var/cache")); // NOI18N
deleteRec(new File(userdir, "var/log")); // NOI18N
rm(new File(userdir, "var")); // NOI18N
rm(new File(userdir, "config")); // NOI18N
rm(userdir); // NOI18N
TopLogging.exit(0);
}
// -----------------------------------------------------------------------------------------------------
// Upgrade
if (!handleImportOfUserDir ()) {
TopLogging.exit(0);
}
}
} catch (Exception e) {
Exceptions.printStackTrace(e);
}
StartLog.logProgress ("License check performed and upgrade wizard consulted"); // NOI18N
//
// 8.5 - we can show the splash only after the upgrade wizard finished
//
Splash.getInstance().setRunning(true);
// -----------------------------------------------------------------------------------------------------
Splash.getInstance().print(NbBundle.getMessage(Main.class, "MSG_IDEInit"));
// -----------------------------------------------------------------------------------------------------
// 9. Modules
assert Repository.getDefault() instanceof NbRepository : "Has to be NbRepository: " + Repository.getDefault() + " Initialization stack: " + Repository.getDefault().when; // NOI18N
getModuleSystem ();
// property editors are registered in modules, so wait a while before loading them
CoreBridge.getDefault().registerPropertyEditors();
StartLog.logProgress ("PropertyEditors registered"); // NOI18N
// clean up the cache before --install, --update CLI options are processed
InstalledFileLocatorImpl.discardCache();
org.netbeans.Main.finishInitialization();
StartLog.logProgress("Ran any delayed command-line options"); // NOI18N
for (RunLevel level : Lookup.getDefault().lookupAll(RunLevel.class)) {
level.run();
}
Splash.getInstance().setRunning(false);
Splash.getInstance().dispose();
StartLog.logProgress ("Splash hidden"); // NOI18N
StartLog.logEnd ("Preparation"); // NOI18N
updateAllResources();
// start to store all caches after 15s
Stamps.getModulesJARs().flush(15000);
// initialize life-cycle manager
LifecycleManager.getDefault();
}
private static void deleteRec(File f) throws IOException {
if (f.isDirectory()) {
File[] kids = f.listFiles();
if (kids == null) {
Logger.getLogger(Main.class.getName()).log(Level.WARNING, "Could not list: {0}", f);
} else {
for (File kid : kids) {
deleteRec(kid);
}
}
}
rm(f);
}
private static void rm(File f) {
if (f.exists() && !f.delete()) {
Logger.getLogger(Main.class.getName()).log(Level.WARNING, "Failed to delete {0}", f);
}
}
/** Loads a class from available class loaders. */
private static final Class getKlass(String cls) {
try {
ClassLoader loader;
ModuleSystem ms = moduleSystem;
if (ms != null) {
loader = ms.getManager ().getClassLoader ();
} else {
loader = Main.class.getClassLoader ();
}
return Class.forName(cls, false, loader);
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getLocalizedMessage());
}
}
/** Does import of userdir. Made non-private just for testing purposes.
*
* @return true if the execution should continue or false if it should
* stop
*/
static boolean handleImportOfUserDir () {
class ImportHandler implements Runnable {
private File installed = new File (new File (CLIOptions.getUserDir (), "var"), "imported"); // NOI18N
private String classname;
private boolean executedOk;
public boolean shouldDoAnImport () {
classname = System.getProperty ("netbeans.importclass"); // NOI18N
return classname != null && !installed.exists ();
}
public void run() {
// This module is included in our distro somewhere... may or may not be turned on.
// Whatever - try running some classes from it anyway.
try {
Class<?> clazz = getKlass (classname);
// Method showMethod = wizardClass.getMethod( "handleUpgrade", new Class[] { Splash.SplashOutput.class } ); // NOI18N
Method showMethod = clazz.getMethod( "main", String[].class ); // NOI18N
showMethod.invoke (null, new Object[] {
new String[0]
});
executedOk = true;
} catch (InvocationTargetException ex) {
// canceled by user, all is fine
if (ex.getTargetException() instanceof UserCancelException) {
executedOk = true;
} else {
LOG.log(Level.WARNING, null, ex);
}
} catch (Exception e) {
// If exceptions are thrown, notify them - something is broken.
LOG.log(Level.WARNING, null, e);
} catch (LinkageError e) {
// These too...
LOG.log(Level.WARNING, null, e);
}
}
public boolean canContinue () {
if (shouldDoAnImport ()) {
try {
SwingUtilities.invokeAndWait (this);
if (executedOk) {
// if the import went fine, then we are fine
// just create the file
installed.getParentFile ().mkdirs ();
installed.createNewFile ();
return true;
} else {
return false;
}
} catch (IOException ex) {
// file was not created a bit of problem but go on
LOG.log(Level.WARNING, null, ex);
return true;
} catch (java.lang.reflect.InvocationTargetException ex) {
return false;
} catch (InterruptedException ex) {
LOG.log(Level.WARNING, null, ex);
return false;
}
} else {
// if there is no need to upgrade that every thing is good
return true;
}
}
}
ImportHandler handler = new ImportHandler ();
return handler.canContinue ();
}
/** Displays license to user to accept if necessary. Made non-private just for testing purposes.
*
* @return true if the execution should continue or false if it should
* stop
*/
static boolean handleLicenseCheck () {
class LicenseHandler implements Runnable {
private String classname;
private boolean executedOk;
/** Checks if licence was accepted already or not. */
public boolean shouldDisplayLicense () {
if (licenseFileExists()) return false;
classname = System.getProperty("netbeans.accept_license_class"); // NOI18N
return (classname != null);
}
@Override
public void run() {
// This module is included in our distro somewhere... may or may not be turned on.
// Whatever - try running some classes from it anyway.
try {
Class<?> clazz = getKlass (classname);
Method showMethod = clazz.getMethod("showLicensePanel"); // NOI18N
showMethod.invoke (null, new Object [] {});
executedOk = true;
//User accepted license => create file marker in userdir
File f = new File(new File(CLIOptions.getUserDir(), "var"), "license_accepted"); // NOI18N
if (!f.exists()) {
f.getParentFile().mkdirs();
try {
f.createNewFile();
if (!licenseFileExists()) {
throw new IOException("InstalledFileLocator can't find " + f); // NOI18N
}
} catch (IOException exc) {
LOG.log(Level.WARNING, null, exc);
}
}
} catch (InvocationTargetException ex) {
// canceled by user, all is fine
if (ex.getTargetException() instanceof UserCancelException) {
executedOk = false;
} else {
LOG.log(Level.WARNING, null, ex);
}
} catch (Exception ex) {
// If exceptions are thrown, notify them - something is broken.
LOG.log(Level.WARNING, null, ex);
} catch (LinkageError ex) {
// These too...
LOG.log(Level.WARNING, null, ex);
}
}
public boolean canContinue () {
if (shouldDisplayLicense()) {
try {
SwingUtilities.invokeAndWait(this);
if (executedOk) {
return true;
} else {
return false;
}
} catch (java.lang.reflect.InvocationTargetException ex) {
return false;
} catch (InterruptedException ex) {
LOG.log(Level.WARNING, null, ex);
return false;
}
} else {
// if there is no need to upgrade that every thing is good
return true;
}
}
private boolean licenseFileExists() {
for (File cluster : InstalledFileLocatorImpl.computeDirs()) {
File f = new File(new File(cluster, "var"), "license_accepted"); // NOI18N
if (f.exists()) {
return true;
}
}
return false;
}
}
LicenseHandler handler = new LicenseHandler ();
return handler.canContinue ();
}
static boolean updateAllResources() {
String value = System.getProperty("org.netbeans.core.update.all.resources", "always");
if (!"never".equals(value)) { // NOI18N
if ("missing".equals(value)) { // NOI18N
if (!org.netbeans.JarClassLoader.isArchivePopulated()) {
org.netbeans.JarClassLoader.saveArchive();
return true;
}
} else {
assert "always".equals(value); // NOI18N
org.netbeans.JarClassLoader.saveArchive();
return true;
}
}
return false;
}
}