001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2015 ForgeRock AS. 016 */ 017package org.opends.quicksetup; 018 019import org.forgerock.i18n.LocalizableMessage; 020import com.forgerock.opendj.cli.ArgumentParser; 021 022import static org.opends.messages.QuickSetupMessages.*; 023import static org.opends.server.util.DynamicConstants.PRINTABLE_VERSION_STRING; 024import static com.forgerock.opendj.cli.ArgumentConstants.*; 025 026import org.opends.quicksetup.util.Utils; 027 028import java.io.PrintStream; 029import java.io.File; 030 031import org.forgerock.i18n.slf4j.LocalizedLogger; 032 033/** 034 * Responsible for providing initial evaluation of command line arguments 035 * and determining whether to launch a CLI, GUI, or print a usage statement. 036 */ 037public abstract class Launcher { 038 039 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 040 041 /** Arguments with which this launcher was invoked. */ 042 protected String[] args; 043 044 /** 045 * Creates a Launcher. 046 * @param args String[] of argument passes from the command line 047 */ 048 public Launcher(String[] args) { 049 if (args == null) { 050 throw new IllegalArgumentException("args cannot be null"); 051 } 052 this.args = args; 053 054 } 055 056 /** 057 * Gets the arguments with which this launcher was invoked. 058 * @return String[] args from the CLI invocation 059 */ 060 public String[] getArguments() { 061 return this.args; 062 } 063 064 /** 065 * Gets an argument parser appropriate for this CLI launcher. 066 * 067 * @return ArgumentParser for parsing args 068 */ 069 public abstract ArgumentParser getArgumentParser(); 070 071 /** 072 * Indicates whether or not the launcher should print a usage 073 * statement based on the content of the arguments passed into 074 * the constructor. 075 * @return boolean where true indicates usage should be printed 076 */ 077 protected boolean shouldPrintUsage() { 078 if (args != null && args.length > 0) { 079 for (String arg : args) { 080 if (arg.equals("-?") || 081 arg.equalsIgnoreCase("-H") || 082 arg.equalsIgnoreCase("--help")) { 083 return true; 084 } 085 } 086 } 087 return false; 088 } 089 090 /** 091 * Indicates whether or not the launcher should print a usage 092 * statement based on the content of the arguments passed into 093 * the constructor. 094 * @return boolean where true indicates usage should be printed 095 */ 096 protected boolean isQuiet() { 097 if (args != null && args.length > 0) { 098 for (String arg : args) { 099 if (arg.equals("-?") || 100 arg.equalsIgnoreCase("-Q") || 101 arg.equalsIgnoreCase("--quiet")) { 102 return true; 103 } 104 } 105 } 106 return false; 107 } 108 109 /** 110 * Indicates whether or not the launcher should print a version 111 * statement based on the content of the arguments passed into 112 * the constructor. 113 * @return boolean where true indicates version should be printed 114 */ 115 protected boolean shouldPrintVersion() { 116 if (args != null && args.length > 0) 117 { 118 for (String arg : args) 119 { 120 if (arg.equalsIgnoreCase("--version")) 121 { 122 return true; 123 } 124 } 125 } 126 return false; 127 } 128 129 /** 130 * Indicates whether the launcher will launch a command line versus 131 * a graphical application based on the contents of the arguments 132 * passed into the constructor. 133 * 134 * @return boolean where true indicates that a CLI application 135 * should be launched 136 */ 137 protected boolean isCli() { 138 for (String arg : args) { 139 if (arg.equalsIgnoreCase("--"+OPTION_LONG_CLI) || 140 arg.equalsIgnoreCase("-"+OPTION_SHORT_CLI)) { 141 return true; 142 } 143 } 144 return false; 145 } 146 147 /** 148 * Prints a usage message to the terminal. 149 * @param i18nMsg localized user message that will be printed to the terminal. 150 * @param toStdErr whether the message must be printed to the standard error 151 * or the standard output. 152 */ 153 protected void printUsage(String i18nMsg, boolean toStdErr) { 154 if (toStdErr) 155 { 156 System.err.println(i18nMsg); 157 } 158 else 159 { 160 System.out.println(i18nMsg); 161 } 162 } 163 164 /** 165 * Launches the graphical uninstall. The graphical uninstall is launched in a 166 * different thread that the main thread because if we have a problem with the 167 * graphical system (for instance the DISPLAY environment variable is not 168 * correctly set) the native libraries will call exit. However if we launch 169 * this from another thread, the thread will just be killed. 170 * 171 * This code also assumes that if the call to SplashWindow.main worked (and 172 * the splash screen was displayed) we will never get out of it (we will call 173 * a System.exit() when we close the graphical uninstall dialog). 174 * 175 * @param args String[] the arguments used to call the SplashWindow main 176 * method 177 * @return 0 if everything worked fine, or 1 if we could not display properly 178 * the SplashWindow. 179 */ 180 protected int launchGui(final String[] args) 181 { 182// Setup MacOSX native menu bar before AWT is loaded. 183 Utils.setMacOSXMenuBar(getFrameTitle()); 184 final int[] returnValue = 185 { -1 }; 186 Thread t = new Thread(new Runnable() 187 { 188 public void run() 189 { 190 try 191 { 192 SplashScreen.main(args); 193 returnValue[0] = 0; 194 } 195 catch (Throwable t) 196 { 197 if (QuickSetupLog.isInitialized()) 198 { 199 logger.warn(LocalizableMessage.raw("Error launching GUI: "+t)); 200 StringBuilder buf = new StringBuilder(); 201 while (t != null) 202 { 203 for (StackTraceElement aStack : t.getStackTrace()) { 204 buf.append(aStack).append("\n"); 205 } 206 207 t = t.getCause(); 208 if (t != null) 209 { 210 buf.append("Root cause:\n"); 211 } 212 } 213 logger.warn(LocalizableMessage.raw(buf)); 214 } 215 } 216 } 217 }); 218 /* 219 * This is done to avoid displaying the stack that might occur if there are 220 * problems with the display environment. 221 */ 222 PrintStream printStream = System.err; 223 System.setErr(Utils.getEmptyPrintStream()); 224 t.start(); 225 try 226 { 227 t.join(); 228 } 229 catch (InterruptedException ie) 230 { 231 /* An error occurred, so the return value will be -1. We got nothing to 232 do with this exception. */ 233 } 234 System.setErr(printStream); 235 return returnValue[0]; 236 } 237 238 /** 239 * Gets the frame title of the GUI application that will be used 240 * in some operating systems. 241 * @return internationalized String representing the frame title 242 */ 243 protected abstract LocalizableMessage getFrameTitle(); 244 245 /** 246 * Launches the command line based uninstall. 247 * 248 * @param cliApp the CLI application to launch 249 * @return 0 if everything worked fine, and an error code if something wrong 250 * occurred. 251 */ 252 protected int launchCli(CliApplication cliApp) 253 { 254 System.setProperty(Constants.CLI_JAVA_PROPERTY, "true"); 255 QuickSetupCli cli = new QuickSetupCli(cliApp, this); 256 ReturnCode returnValue = cli.run(); 257 if (returnValue.equals(ReturnCode.USER_DATA_ERROR)) 258 { 259 printUsage(true); 260 System.exit(ReturnCode.USER_DATA_ERROR.getReturnCode()); 261 } 262 else if (returnValue.equals(ReturnCode.CANCELED)) 263 { 264 System.exit(ReturnCode.CANCELED.getReturnCode()); 265 } 266 else if (returnValue.equals(ReturnCode.USER_INPUT_ERROR)) 267 { 268 System.exit(ReturnCode.USER_INPUT_ERROR.getReturnCode()); 269 } 270 return returnValue.getReturnCode(); 271 } 272 273 /** 274 * Prints the version statement to standard output terminal. 275 */ 276 protected void printVersion() 277 { 278 System.out.print(PRINTABLE_VERSION_STRING); 279 } 280 281 /** 282 * Prints a usage statement to terminal and exits with an error 283 * code. 284 * @param toStdErr whether the message must be printed to the standard error 285 * or the standard output. 286 */ 287 protected void printUsage(boolean toStdErr) { 288 try 289 { 290 ArgumentParser argParser = getArgumentParser(); 291 if (argParser != null) { 292 String msg = argParser.getUsage(); 293 printUsage(msg, toStdErr); 294 } 295 } 296 catch (Throwable t) 297 { 298 System.out.println("ERROR: "+t); 299 t.printStackTrace(); 300 } 301 } 302 303 /** 304 * Creates a CLI application that will be run if the 305 * launcher needs to launch a CLI application. 306 * @return CliApplication that will be run 307 */ 308 protected abstract CliApplication createCliApplication(); 309 310 /** 311 * Called before the launcher launches the GUI. Here 312 * subclasses can do any application specific things 313 * like set system properties of print status messages 314 * that need to be done before the GUI launches. 315 */ 316 protected abstract void willLaunchGui(); 317 318 /** 319 * Called if launching of the GUI failed. Here 320 * subclasses can so application specific things 321 * like print a message. 322 * @param logFileName the log file containing more information about why 323 * the launch failed. 324 */ 325 protected abstract void guiLaunchFailed(String logFileName); 326 327 /** 328 * The main method which is called by the command lines. 329 */ 330 public void launch() { 331 if (shouldPrintVersion()) { 332 ArgumentParser parser = getArgumentParser(); 333 if (parser == null || !parser.usageOrVersionDisplayed()) { 334 printVersion(); 335 } 336 System.exit(ReturnCode.PRINT_VERSION.getReturnCode()); 337 } 338 else if (shouldPrintUsage()) { 339 ArgumentParser parser = getArgumentParser(); 340 if (parser == null || !parser.usageOrVersionDisplayed()) { 341 printUsage(false); 342 } 343 System.exit(ReturnCode.SUCCESSFUL.getReturnCode()); 344 } else if (isCli()) { 345 CliApplication cliApp = createCliApplication(); 346 int exitCode = launchCli(cliApp); 347 preExit(cliApp); 348 System.exit(exitCode); 349 } else { 350 willLaunchGui(); 351 int exitCode = launchGui(args); 352 if (exitCode != 0) { 353 File logFile = QuickSetupLog.getLogFile(); 354 if (logFile != null) 355 { 356 guiLaunchFailed(logFile.toString()); 357 } 358 else 359 { 360 guiLaunchFailed(null); 361 } 362 CliApplication cliApp = createCliApplication(); 363 exitCode = launchCli(cliApp); 364 preExit(cliApp); 365 System.exit(exitCode); 366 } 367 } 368 } 369 370 private void preExit(CliApplication cliApp) { 371 if (cliApp != null) { 372 UserData ud = cliApp.getUserData(); 373 if (ud != null && !ud.isQuiet()) { 374 375 // Add an extra space systematically 376 System.out.println(); 377 378 File logFile = QuickSetupLog.getLogFile(); 379 if (logFile != null) { 380 System.out.println(INFO_GENERAL_SEE_FOR_DETAILS.get( 381 QuickSetupLog.getLogFile().getPath())); 382 } 383 } 384 } 385 } 386}