/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2009 Sun Microsystems, Inc. * Portions Copyright 2013-2015 ForgeRock AS. */ package org.opends.quicksetup; import org.forgerock.i18n.LocalizableMessage; import com.forgerock.opendj.cli.ArgumentParser; import static org.opends.messages.QuickSetupMessages.*; import static org.opends.server.util.DynamicConstants.PRINTABLE_VERSION_STRING; import static com.forgerock.opendj.cli.ArgumentConstants.*; import org.opends.quicksetup.util.Utils; import java.io.PrintStream; import java.io.File; import org.forgerock.i18n.slf4j.LocalizedLogger; /** * Responsible for providing initial evaluation of command line arguments * and determining whether to launch a CLI, GUI, or print a usage statement. */ public abstract class Launcher { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** Arguments with which this launcher was invoked. */ protected String[] args; /** * Creates a Launcher. * @param args String[] of argument passes from the command line */ public Launcher(String[] args) { if (args == null) { throw new IllegalArgumentException("args cannot be null"); } this.args = args; } /** * Gets the arguments with which this launcher was invoked. * @return String[] args from the CLI invocation */ public String[] getArguments() { return this.args; } /** * Gets an argument parser appropriate for this CLI launcher. * * @return ArgumentParser for parsing args */ public abstract ArgumentParser getArgumentParser(); /** * Indicates whether or not the launcher should print a usage * statement based on the content of the arguments passed into * the constructor. * @return boolean where true indicates usage should be printed */ protected boolean shouldPrintUsage() { if (args != null && args.length > 0) { for (String arg : args) { if (arg.equals("-?") || arg.equalsIgnoreCase("-H") || arg.equalsIgnoreCase("--help")) { return true; } } } return false; } /** * Indicates whether or not the launcher should print a usage * statement based on the content of the arguments passed into * the constructor. * @return boolean where true indicates usage should be printed */ protected boolean isQuiet() { if (args != null && args.length > 0) { for (String arg : args) { if (arg.equals("-?") || arg.equalsIgnoreCase("-Q") || arg.equalsIgnoreCase("--quiet")) { return true; } } } return false; } /** * Indicates whether or not the launcher should print a version * statement based on the content of the arguments passed into * the constructor. * @return boolean where true indicates version should be printed */ protected boolean shouldPrintVersion() { if (args != null && args.length > 0) { for (String arg : args) { if (arg.equalsIgnoreCase("--version")) { return true; } } } return false; } /** * Indicates whether the launcher will launch a command line versus * a graphical application based on the contents of the arguments * passed into the constructor. * * @return boolean where true indicates that a CLI application * should be launched */ protected boolean isCli() { for (String arg : args) { if (arg.equalsIgnoreCase("--"+OPTION_LONG_CLI) || arg.equalsIgnoreCase("-"+OPTION_SHORT_CLI)) { return true; } } return false; } /** * Prints a usage message to the terminal. * @param i18nMsg localized user message that will be printed to the terminal. * @param toStdErr whether the message must be printed to the standard error * or the standard output. */ protected void printUsage(String i18nMsg, boolean toStdErr) { if (toStdErr) { System.err.println(i18nMsg); } else { System.out.println(i18nMsg); } } /** * Launches the graphical uninstall. The graphical uninstall is launched in a * different thread that the main thread because if we have a problem with the * graphical system (for instance the DISPLAY environment variable is not * correctly set) the native libraries will call exit. However if we launch * this from another thread, the thread will just be killed. * * This code also assumes that if the call to SplashWindow.main worked (and * the splash screen was displayed) we will never get out of it (we will call * a System.exit() when we close the graphical uninstall dialog). * * @param args String[] the arguments used to call the SplashWindow main * method * @return 0 if everything worked fine, or 1 if we could not display properly * the SplashWindow. */ protected int launchGui(final String[] args) { // Setup MacOSX native menu bar before AWT is loaded. Utils.setMacOSXMenuBar(getFrameTitle()); final int[] returnValue = { -1 }; Thread t = new Thread(new Runnable() { public void run() { try { SplashScreen.main(args); returnValue[0] = 0; } catch (Throwable t) { if (QuickSetupLog.isInitialized()) { logger.warn(LocalizableMessage.raw("Error launching GUI: "+t)); StringBuilder buf = new StringBuilder(); while (t != null) { for (StackTraceElement aStack : t.getStackTrace()) { buf.append(aStack).append("\n"); } t = t.getCause(); if (t != null) { buf.append("Root cause:\n"); } } logger.warn(LocalizableMessage.raw(buf)); } } } }); /* * This is done to avoid displaying the stack that might occur if there are * problems with the display environment. */ PrintStream printStream = System.err; System.setErr(Utils.getEmptyPrintStream()); t.start(); try { t.join(); } catch (InterruptedException ie) { /* An error occurred, so the return value will be -1. We got nothing to do with this exception. */ } System.setErr(printStream); return returnValue[0]; } /** * Gets the frame title of the GUI application that will be used * in some operating systems. * @return internationalized String representing the frame title */ protected abstract LocalizableMessage getFrameTitle(); /** * Launches the command line based uninstall. * * @param cliApp the CLI application to launch * @return 0 if everything worked fine, and an error code if something wrong * occurred. */ protected int launchCli(CliApplication cliApp) { System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); QuickSetupCli cli = new QuickSetupCli(cliApp, this); ReturnCode returnValue = cli.run(); if (returnValue.equals(ReturnCode.USER_DATA_ERROR)) { printUsage(true); System.exit(ReturnCode.USER_DATA_ERROR.getReturnCode()); } else if (returnValue.equals(ReturnCode.CANCELED)) { System.exit(ReturnCode.CANCELED.getReturnCode()); } else if (returnValue.equals(ReturnCode.USER_INPUT_ERROR)) { System.exit(ReturnCode.USER_INPUT_ERROR.getReturnCode()); } return returnValue.getReturnCode(); } /** * Prints the version statement to standard output terminal. */ protected void printVersion() { System.out.print(PRINTABLE_VERSION_STRING); } /** * Prints a usage statement to terminal and exits with an error * code. * @param toStdErr whether the message must be printed to the standard error * or the standard output. */ protected void printUsage(boolean toStdErr) { try { ArgumentParser argParser = getArgumentParser(); if (argParser != null) { String msg = argParser.getUsage(); printUsage(msg, toStdErr); } } catch (Throwable t) { System.out.println("ERROR: "+t); t.printStackTrace(); } } /** * Creates a CLI application that will be run if the * launcher needs to launch a CLI application. * @return CliApplication that will be run */ protected abstract CliApplication createCliApplication(); /** * Called before the launcher launches the GUI. Here * subclasses can do any application specific things * like set system properties of print status messages * that need to be done before the GUI launches. */ protected abstract void willLaunchGui(); /** * Called if launching of the GUI failed. Here * subclasses can so application specific things * like print a message. * @param logFileName the log file containing more information about why * the launch failed. */ protected abstract void guiLaunchFailed(String logFileName); /** * The main method which is called by the command lines. */ public void launch() { if (shouldPrintVersion()) { ArgumentParser parser = getArgumentParser(); if (parser == null || !parser.usageOrVersionDisplayed()) { printVersion(); } System.exit(ReturnCode.PRINT_VERSION.getReturnCode()); } else if (shouldPrintUsage()) { ArgumentParser parser = getArgumentParser(); if (parser == null || !parser.usageOrVersionDisplayed()) { printUsage(false); } System.exit(ReturnCode.SUCCESSFUL.getReturnCode()); } else if (isCli()) { CliApplication cliApp = createCliApplication(); int exitCode = launchCli(cliApp); preExit(cliApp); System.exit(exitCode); } else { willLaunchGui(); int exitCode = launchGui(args); if (exitCode != 0) { File logFile = QuickSetupLog.getLogFile(); if (logFile != null) { guiLaunchFailed(logFile.toString()); } else { guiLaunchFailed(null); } CliApplication cliApp = createCliApplication(); exitCode = launchCli(cliApp); preExit(cliApp); System.exit(exitCode); } } } private void preExit(CliApplication cliApp) { if (cliApp != null) { UserData ud = cliApp.getUserData(); if (ud != null && !ud.isQuiet()) { // Add an extra space systematically System.out.println(); File logFile = QuickSetupLog.getLogFile(); if (logFile != null) { System.out.println(INFO_GENERAL_SEE_FOR_DETAILS.get( QuickSetupLog.getLogFile().getPath())); } } } } }