package org.limewire.ui.swing; import java.awt.Frame; import java.awt.Image; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicHTML; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdesktop.application.Application; import org.limewire.core.impl.mozilla.LimeMozillaOverrides; import org.limewire.core.settings.ConnectionSettings; import org.limewire.core.settings.SharingSettings; import org.limewire.inject.GuiceUtils; import org.limewire.io.IOUtils; import org.limewire.net.FirewallService; import org.limewire.nio.NIODispatcher; import org.limewire.service.ErrorService; import org.limewire.service.MessageService; import org.limewire.ui.support.BugManager; import org.limewire.ui.support.DeadlockSupport; import org.limewire.ui.support.ErrorHandler; import org.limewire.ui.support.FatalBugManager; import org.limewire.ui.swing.browser.LimeMozillaInitializer; import org.limewire.ui.swing.components.MultiLineLabel; import org.limewire.ui.swing.components.SplashWindow; import org.limewire.ui.swing.event.ExceptionPublishingSwingEventService; import org.limewire.ui.swing.mainframe.AppFrame; import org.limewire.ui.swing.settings.InstallSettings; import org.limewire.ui.swing.settings.StartupSettings; import org.limewire.ui.swing.util.GuiUtils; import org.limewire.ui.swing.util.I18n; import org.limewire.ui.swing.util.LocaleUtils; import org.limewire.ui.swing.util.MacOSXUtils; import org.limewire.ui.swing.util.SwingUtils; import org.limewire.ui.swing.wizard.IntentDialog; import org.limewire.util.CommonUtils; import org.limewire.util.I18NConvert; import org.limewire.util.OSUtils; import org.limewire.util.Stopwatch; import org.limewire.util.SystemUtils; import org.limewire.util.Version; import org.limewire.util.VersionFormatException; import org.mozilla.browser.MozillaPanel; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; import com.google.inject.Stage; import com.limegroup.gnutella.ActiveLimeWireCheck; import com.limegroup.gnutella.LifecycleManager; import com.limegroup.gnutella.LimeCoreGlue; import com.limegroup.gnutella.UPnPManager; import com.limegroup.gnutella.ActiveLimeWireCheck.ActiveLimeWireException; import com.limegroup.gnutella.LimeCoreGlue.InstallFailedException; import com.limegroup.gnutella.browser.ExternalControl; import com.limegroup.gnutella.util.LimeWireUtils; import com.limegroup.gnutella.util.LogUtils; /** Initializes (creates, starts, & displays) the LimeWire Core & UI. */ public final class Initializer { /** The log -- set only after Log4J can be determined. */ private final Log LOG; /** Refuse to start after this date */ private final long EXPIRATION_DATE = Long.MAX_VALUE; /** True if is running from a system startup. */ private volatile boolean isStartup = false; /** The start memory -- only set if debugging. */ private long startMemory; /** A stopwatch for debug logging. */ private final Stopwatch stopwatch; /** The SplashWindow reference. */ private final AtomicReference<SplashWindow> splashRef = new AtomicReference<SplashWindow>(); // Providers so we don't guarantee early creation, let it be as lazy as possible. @Inject private Provider<ExternalControl> externalControl; @Inject private Provider<FirewallService> firewallServices; @Inject private Provider<LifecycleManager> lifecycleManager; @Inject private Provider<LimeCoreGlue> limeCoreGlue; @Inject private Provider<NIODispatcher> nioDispatcher; @Inject private Provider<UPnPManager> upnpManager; @Inject private Provider<LimeMozillaOverrides> mozillaOverrides; Initializer() { // If Log4J is available then remove the NoOpLog if (LogUtils.isLog4JAvailable()) { System.getProperties().remove("org.apache.commons.logging.Log"); } LOG = LogFactory.getLog(Initializer.class); if(LOG.isTraceEnabled()) { startMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); LOG.trace("START Initializer, using: " + startMemory + " memory"); } stopwatch = new Stopwatch(LOG); } /** * Initializes all of the necessary application classes. * * If this throws any exceptions, then LimeWire was not able to construct * properly and must be shut down. */ void initialize(String args[], Frame awtSplash, Image splashImage) throws Throwable { // ** THE VERY BEGINNING -- DO NOT ADD THINGS BEFORE THIS ** preinit(); // Various startup tasks... setupCallbacksAndListeners(); validateStartup(args); // Creates LimeWire itself. Injector injector = createLimeWire(); // Various tasks that can be done after core is glued & started. glueCore(); validateEarlyCore(); // Validate any arguments or properties outside of the LW environment. runExternalChecks(args, injector); // Starts some system monitoring for deadlocks. DeadlockSupport.startDeadlockMonitoring(); stopwatch.resetAndLog("Start deadlock monitor"); // Installs properties. installProperties(); // show nifty alpha info // showAlphaInfo(); //must agree not to use LW for copyright infringement on first running confirmIntent(awtSplash); // Move from the AWT splash to the Swing splash & start early core. //assuming not showing splash screen if there are program arguments switchSplashes(awtSplash, splashImage, LimeWireUtils.isPro()); startEarlyCore(); // Initialize early UI components, display the setup manager (if necessary), // and ensure the save directory is valid. initializeEarlyUI(); // Load the UI, system tray & notification handlers, // and hide the splash screen & display the UI. loadUI(); enablePreferences(); SettingsWarningManager.checkTemporaryDirectoryUsage(); SettingsWarningManager.checkSettingsLoadSaveFailure(); // Start the core & run any queued control requests, and load DAAP. startCore(); runQueuedRequests(); // Run any after-init tasks. postinit(); } // private void showAlphaInfo() { // final String msg = "Welcome to the LimeWire 5 Alpha!\n\n" // + "Here's some important information about the alpha...\n" // + " - There are bugs. Lots. It's because this is an alpha.\n" // + " We're confident the program is stable (it's not going\n" // + " to delete your computer or anything), but you will run\n" // + " into some internal errors.\n" // + " - It isn't finished. We're very actively working on\n" // + " changes, tweaks, rewrites, and all sorts of things.\n" // + " - Everything can (and probably will) change. Don't\n" // + " take inclusion or exclusion of a certain feature to\n" // + " mean that we're adding or dropping it. It's probably\n" // + " just an oversight.\n\n" // + " Thanks!\n" // + " The LimeWire Team"; // SwingUtils.invokeAndWait(new Runnable() { // public void run() { // JOptionPane.showMessageDialog(null, // msg, // "LimeWire 5 Alpha Info", // JOptionPane.INFORMATION_MESSAGE); // } // }); // } /** * Shows legal conditions and exits if the user does not agree to them. * * Takes a parameter for the splash screen so it can hide it if * the intent dialogue needs to be shown to avoid a troublesome situation * where the splash screen actually covers the shown intent dialogue. */ private void confirmIntent(final Frame awtSplash) { File versionFile = new File(CommonUtils.getUserSettingsDir(), "versions.props"); Properties properties = new Properties(); FileInputStream inputStream = null; try { inputStream = new FileInputStream(versionFile); properties.load(inputStream); } catch (IOException iox) { } finally { IOUtils.close(inputStream); } updateVersionsUsed(properties); String exists = properties.getProperty(LimeWireUtils.getLimeWireVersion()); if (exists == null || !exists.equals("true")) { SwingUtils.invokeAndWait(new Runnable() { @Override public void run() { if (awtSplash != null) { awtSplash.setVisible(false); } //must warn users about sharing documents again after an upgrade SharingSettings.WARN_SHARING_DOCUMENTS_WITH_WORLD.setValue(true); boolean confirmed = new IntentDialog(LimeWireUtils.getLimeWireVersion()).confirmLegal(); if (!confirmed) { System.exit(0); } if (awtSplash != null) { awtSplash.setVisible(true); } } }); properties.put(LimeWireUtils.getLimeWireVersion(), "true"); FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(versionFile); properties.store(outputStream, "Started & Ran Versions"); } catch (IOException ignored) { } finally { IOUtils.close(outputStream); } } } /** * Fills in any previously run versions from the versions.props file. */ private void updateVersionsUsed(Properties properties) { for(Object key : properties.keySet()) { String versionString = key.toString(); try { Version version = new Version(versionString); InstallSettings.PREVIOUS_RAN_VERSIONS.add(version.getVersion()); } catch (VersionFormatException e) { //do nothing } } } /** Initializes the very early things. */ /* * DO NOT CHANGE THIS WITHOUT KNOWING WHAT YOU'RE DOING. * PREINSTALL MUST BE DONE BEFORE ANYTHING ELSE IS REFERENCED. * (Because it sets the preference directory in CommonUtils.) */ private void preinit() { // Make sure the settings directory is set. try { LimeCoreGlue.preinstall(); stopwatch.resetAndLog("Preinstall"); } catch(InstallFailedException ife) { GuiUtils.hideAndDisposeAllWindows(); failPreferencesPermissions(); } // Before anything, set a default L&F, so that // if an error occurs, we can display the error // message with the right L&F. SwingUtils.invokeLater(new Runnable() { public void run() { String name = UIManager.getSystemLookAndFeelClassName(); if(OSUtils.isLinux()) { //mozswing on linux is not compatible with the gtklook and feel in jvms less than 1.7 //forcing cross platform look and feel for linux. name = UIManager.getCrossPlatformLookAndFeelClassName(); } try { UIManager.setLookAndFeel(name); } catch(Throwable ignored) {} } }); } /** Installs all callbacks & listeners. */ private void setupCallbacksAndListeners() { SwingUtils.invokeAndWait(new Runnable() { @Override public void run() { BugManager.instance(); } }); // Set the error handler so we can receive core errors. ErrorService.setErrorCallback(new ErrorHandler()); // set error handler for uncaught exceptions originating from non-LW Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandlerImpl()); stopwatch.resetAndLog("ErrorHandler install"); // Set the messaging handler so we can receive core messages MessageService.setCallback(new MessageHandler()); stopwatch.resetAndLog("MessageHandler install"); // Set the default event error handler so we can receive uncaught // AWT errors. DefaultErrorCatcher.install(); stopwatch.resetAndLog("DefaultErrorCatcher install"); //Enable the EDT event service (used by the EventBus library) that publishes to LW error handling ExceptionPublishingSwingEventService.install(); stopwatch.resetAndLog("DefaultErrorCatcher install"); if (OSUtils.isMacOSX()) { // Raise the number of allowed concurrent open files to 1024. SystemUtils.setOpenFileLimit(1024); stopwatch.resetAndLog("Open file limit raise"); MacEventHandler.instance(); stopwatch.resetAndLog("MacEventHandler instance"); } } /** * Ensures this should continue running, by checking * for expiration failures or startup settings. */ private void validateStartup(String[] args) { // check if this version has expired. if (System.currentTimeMillis() > EXPIRATION_DATE) { GuiUtils.hideAndDisposeAllWindows(); failExpired(); } // Yield so any other events can be run to determine // startup status, but only if we're going to possibly // be starting... if(StartupSettings.RUN_ON_STARTUP.getValue()) { stopwatch.reset(); Thread.yield(); stopwatch.resetAndLog("Thread yield"); } if (args.length >= 1 && "-startup".equals(args[0])) isStartup = true; if (isStartup) { args = null; // reset for later Active check // if the user doesn't want to start on system startup, exit the // JVM immediately if(!StartupSettings.RUN_ON_STARTUP.getValue()) System.exit(0); } // Exit if another LimeWire is already running... if(!StartupSettings.ALLOW_MULTIPLE_INSTANCES.getValue()) { ActiveLimeWireCheck activeCheck = ActiveLimeWireCheck.instance(); stopwatch.resetAndLog("Create ActiveLimeWireCheck"); try { if(activeCheck.checkForActiveLimeWire(args)) System.exit(0); } catch(ActiveLimeWireException e) { LOG.debug(e); stopwatch.resetAndLog("Warn user about running instance"); GuiUtils.hideAndDisposeAllWindows(); if(!warnAlreadyRunning()) System.exit(0); } stopwatch.resetAndLog("Run ActiveLimeWireCheck"); } } /** Wires together LimeWire. */ private Injector createLimeWire() { stopwatch.reset(); Injector injector = Guice.createInjector(Stage.DEVELOPMENT, new LimeWireModule(), new AbstractModule() { @Override protected void configure() { requestStaticInjection(AppFrame.class); requestInjection(Initializer.this); } }); GuiceUtils.loadEagerSingletons(injector); stopwatch.resetAndLog("Create injector"); return injector; } /** Wires together remaining non-Guiced pieces. */ private void glueCore() { limeCoreGlue.get().install(); stopwatch.resetAndLog("Install core glue"); } /** Tasks that can be done after core is created, before it's started. */ private void validateEarlyCore() { // See if our NIODispatcher clunked out. if(!nioDispatcher.get().isRunning()) { GuiUtils.hideAndDisposeAllWindows(); failInternetBlocked(); } stopwatch.resetAndLog("Check for NIO dispatcher"); } /** * Initializes any code that is dependent on external controls. * Specifically, GURLHandler & MacEventHandler on OS X, * ensuring that multiple LimeWire's can't run at once, * and processing any arguments that were passed to LimeWire. */ private void runExternalChecks(String[] args, Injector injector) { stopwatch.resetAndLog("Get externalControl"); if(OSUtils.isMacOSX()) { GURLHandler.getInstance().enable(externalControl.get()); stopwatch.resetAndLog("Enable GURL"); injector.injectMembers(MacEventHandler.instance()); stopwatch.resetAndLog("Enable macEventHandler"); } // Test for preexisting LimeWire and pass it a magnet URL if one // has been passed in. if (args.length > 0 && !args[0].equals("-startup")) { String arg = ExternalControl.preprocessArgs(args); stopwatch.resetAndLog("Preprocess args"); externalControl.get().enqueueControlRequest(arg); stopwatch.resetAndLog("Enqueue control req"); } } /** Installs any system properties. */ private void installProperties() { System.setProperty("http.agent", LimeWireUtils.getHttpServer()); stopwatch.resetAndLog("set system properties"); if (OSUtils.isMacOSX()) { System.setProperty("user.fullname", MacOSXUtils.getUserName()); // for DAAP System.setProperty("apple.laf.useScreenMenuBar", "true"); stopwatch.resetAndLog("set OSX properties"); } SwingUtils.invokeAndWait(new Runnable() { public void run() { LocaleUtils.setLocaleFromPreferences(); LocaleUtils.validateLocaleAndFonts(); } }); stopwatch.resetAndLog("set locale"); } /** Starts any early core-related functionality. */ private void startEarlyCore() { // Add this running program to the Windows Firewall Exceptions list boolean inFirewallException = firewallServices.get().addToFirewall(); stopwatch.resetAndLog("add firewall exception"); if(!inFirewallException) { lifecycleManager.get().loadBackgroundTasks(); stopwatch.resetAndLog("load background tasks"); } } /** Switches from the AWT splash to the Swing splash. */ private void switchSplashes(final Frame awtSplash, final Image splashImage, final boolean isPro) { SwingUtils.invokeAndWait(new Runnable() { @Override public void run() { splashRef.set(new SplashWindow(splashImage, isPro, LocaleUtils.getCurrentLocale(), 4)); if(!isStartup) { splashRef.get().begin(); stopwatch.resetAndLog("begin splash window"); } } }); if(awtSplash != null) { awtSplash.dispose(); stopwatch.resetAndLog("dispose AWT splash"); } } /** * Initializes any early UI tasks, such as HTML loading & the Bug Manager. */ private void initializeEarlyUI() { // Load up the HTML engine. splashRef.get().setStatusText(I18n.tr("Muddling Mint...")); //html engine stopwatch.resetAndLog("update splash for HTML engine"); SwingUtils.invokeAndWait(new Runnable() { public void run() { stopwatch.resetAndLog("enter evt queue"); JLabel label = new JLabel(); // setting font and color to null to minimize generated css // script, which causes a parser exception under circumstances label.setFont(null); label.setForeground(null); BasicHTML.createHTMLView(label, "<html>.</html>"); stopwatch.resetAndLog("create HTML view"); } }); stopwatch.resetAndLog("return from evt queue"); splashRef.get().setStatusText(I18n.tr("Scouring NYC for Limes...")); //loading browser // Not pretty but Mozilla initialization errors should not crash the // program if (LimeMozillaInitializer.shouldInitialize()) { // See LWC-2860 for why we change Turkish -> English. // If MozSwing ever fixes this for us, we can remove this workaround. Locale locale = Locale.getDefault(); if(locale.getLanguage().equals("tr")) { Locale.setDefault(Locale.ENGLISH); } try { LimeMozillaInitializer.initialize(); mozillaOverrides.get().overrideMozillaDefaults(); } catch (Exception e) { // If it failed, don't keep the wrong locale active. Locale.setDefault(locale); LOG.error("Mozilla initialization failed"); } stopwatch.resetAndLog("Load XUL Library Path"); SwingUtils.invokeAndWait(new Runnable() { public void run() { stopwatch.resetAndLog("enter evt queue"); new MozillaPanel(); stopwatch.resetAndLog("Load MozillaPanel"); } }); } stopwatch.resetAndLog("return from evt queue"); } /** Loads the UI. */ private void loadUI() { splashRef.get().setStatusText(I18n.tr("Squeezing Limes...")); //loading user interface stopwatch.resetAndLog("update splash for UI"); DefaultErrorCatcher.storeCaughtBugs(); String[] launchParams = isStartup ? new String[] { AppFrame.STARTUP } : new String[0]; Application.launch(AppFrame.class, launchParams); // Initialize late tasks, like Icon initialization & install listeners. loadLateTasksForUI(); SwingUtils.invokeAndWait(new Runnable() { public void run() { splashRef.get().dispose(); splashRef.set(null); List<Throwable> caughtBugs = DefaultErrorCatcher.getAndResetStoredBugs(); if(!AppFrame.isStarted()) { // Report the last bug that caused us to fail. assert caughtBugs.size() > 0; FatalBugManager.handleFatalBug(caughtBugs.get(caughtBugs.size()-1)); } else { for(Throwable throwable : caughtBugs) { ErrorService.error(throwable, "Startup Error"); } } } }); } private void enablePreferences() { if (OSUtils.isMacOSX()) { MacEventHandler.instance().enablePreferences(); } } /** Runs any late UI tasks, such as initializing Icons, I18n support. */ private void loadLateTasksForUI() { // Touch the I18N stuff to ensure it loads properly. splashRef.get().setStatusText(I18n.tr("Prepping Mojitos...")); //other languages? I18NConvert.instance(); stopwatch.resetAndLog("I18nConvert instance"); } /** Starts the core. */ private void startCore() { // Start the backend threads. Note that the GUI is not yet visible, // but it needs to be constructed at this point lifecycleManager.get().start(); stopwatch.resetAndLog("lifecycle manager start"); if (!ConnectionSettings.DISABLE_UPNP.getValue()) { upnpManager.get().start(); stopwatch.resetAndLog("start UPnPManager"); } } /** Runs control requests that we queued early in initializing. */ private void runQueuedRequests() { // Activate a download for magnet URL locally if one exists externalControl.get().runQueuedControlRequest(); stopwatch.resetAndLog("run queued control req"); } /** Runs post initialization tasks. */ private void postinit() { if(LOG.isTraceEnabled()) { long stopMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); LOG.trace("STOP Initializer, using: " + stopMemory + " memory, consumed: " + (stopMemory - startMemory)); } } /** * Sets the startup property to be true. */ void setStartup() { isStartup = true; } /** Fails because alpha expired. */ private void failExpired() { fail(I18n.tr("This Alpha version has expired. Press Ok to exit. ")); } /** Fails because internet is blocked. */ private void failInternetBlocked() { fail(I18n .tr("LimeWire was unable to initialize and start. This is usually due to a firewall program blocking LimeWire\'s access to the internet or loopback connections on the local machine. Please allow LimeWire access to the internet and restart LimeWire.")); } /** Fails because preferences can't be set. */ private void failPreferencesPermissions() { fail(I18n.tr("LimeWire could not create a temporary preferences folder.\n\nThis is generally caused by a lack of permissions. Please make sure that LimeWire (and you) have access to create files/folders on your computer. If the problem persists, please visit www.limewire.com and click the \'Support\' link.\n\nLimeWire will now exit. Thank You.")); } /** Shows a msg & fails. */ private void fail(final String msgKey) { SwingUtils.invokeAndWait(new Runnable() { @Override public void run() { JOptionPane.showMessageDialog(null, new MultiLineLabel(msgKey, 300), I18n.tr("Error"), JOptionPane.ERROR_MESSAGE); } }); System.exit(1); } /** * Warns the user that another instance of LimeWire appears to be * running and that it should be shut down before proceeding. * * @return true if the user chooses to proceed. */ private boolean warnAlreadyRunning() { final AtomicInteger response = new AtomicInteger(JOptionPane.CANCEL_OPTION); final String message = I18n.tr("Another instance of LimeWire " + "appears to be running. Please completely shut down all " + "other instances of LimeWire before continuing. If this " + "problem persists, please restart your computer."); SwingUtils.invokeAndWait(new Runnable() { @Override public void run() { response.set(JOptionPane.showConfirmDialog(null, new MultiLineLabel(message, 300), I18n.tr("LimeWire is already running"), JOptionPane.OK_CANCEL_OPTION)); } }); return response.get() == JOptionPane.OK_OPTION; } }