001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.guitools.controlpanel; 028 029import static com.forgerock.opendj.cli.Utils.*; 030 031import static org.opends.messages.AdminToolMessages.*; 032import static org.opends.messages.ToolMessages.*; 033 034import java.io.File; 035import java.io.PrintStream; 036 037import javax.swing.SwingUtilities; 038 039import org.forgerock.i18n.LocalizableMessage; 040import org.forgerock.i18n.slf4j.LocalizedLogger; 041import org.opends.guitools.controlpanel.util.ControlPanelLog; 042import org.opends.messages.AdminToolMessages; 043import org.opends.quicksetup.ui.UIFactory; 044import org.opends.quicksetup.util.Utils; 045import org.opends.server.types.InitializationException; 046import org.opends.server.util.BuildVersion; 047import org.opends.server.util.DynamicConstants; 048 049import com.forgerock.opendj.cli.ArgumentException; 050 051/** 052 * The class that is invoked directly by the control-panel command-line. This 053 * class basically displays a splash screen and then calls the methods in 054 * ControlPanel. It also is in charge of detecting whether there are issues 055 * with the 056 * 057 */ 058public class ControlPanelLauncher 059{ 060 private static ControlPanelArgumentParser argParser; 061 062 /** Prefix for log files. */ 063 public static final String LOG_FILE_PREFIX = "opendj-control-panel-"; 064 065 /** Suffix for log files. */ 066 public static final String LOG_FILE_SUFFIX = ".log"; 067 068 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 069 070 /** 071 * Main method invoked by the control-panel script. 072 * @param args the arguments. 073 */ 074 public static void main(String[] args) 075 { 076 try { 077 ControlPanelLog.initLogFileHandler( 078 File.createTempFile(LOG_FILE_PREFIX, LOG_FILE_SUFFIX)); 079 } catch (Throwable t) { 080 System.err.println("Unable to initialize log"); 081 t.printStackTrace(); 082 } 083 084 argParser = new ControlPanelArgumentParser( 085 ControlPanelLauncher.class.getName(), 086 INFO_CONTROL_PANEL_LAUNCHER_USAGE_DESCRIPTION.get()); 087 // Validate user provided data 088 try 089 { 090 argParser.initializeArguments(); 091 argParser.parseArguments(args); 092 } 093 catch (ArgumentException ae) 094 { 095 argParser.displayMessageAndUsageReference(System.err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 096 System.exit(ErrorReturnCode.ERROR_PARSING_ARGS.getReturnCode()); 097 } 098 099 // If we should just display usage or version information, 100 // then print it and exit. 101 if (argParser.usageOrVersionDisplayed()) 102 { 103 ControlPanelLog.closeAndDeleteLogFile(); 104 System.exit(ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode()); 105 } 106 107 // Checks the version - if upgrade required, the tool is unusable 108 if (!argParser.isRemote()) 109 { 110 try 111 { 112 BuildVersion.checkVersionMismatch(); 113 } 114 catch (InitializationException e) 115 { 116 System.err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH)); 117 System.exit(ErrorReturnCode.ERROR_UNEXPECTED.getReturnCode()); 118 } 119 } 120 121 if (!argParser.usageOrVersionDisplayed()) 122 { 123 int exitCode = launchControlPanel(args); 124 if (exitCode != 0) 125 { 126 String logFileName = null; 127 if (ControlPanelLog.getLogFile() != null) 128 { 129 logFileName = ControlPanelLog.getLogFile().toString(); 130 } 131 if (logFileName != null) 132 { 133 System.err.println(wrapText( 134 ERR_CONTROL_PANEL_LAUNCHER_GUI_LAUNCH_FAILED_DETAILS.get( 135 logFileName), 136 Utils.getCommandLineMaxLineWidth())); 137 } 138 else 139 { 140 System.err.println(wrapText( 141 ERR_CONTROL_PANEL_LAUNCHER_GUI_LAUNCH_FAILED.get(), 142 Utils.getCommandLineMaxLineWidth())); 143 } 144 System.exit(exitCode); 145 } 146 } 147 ControlPanelLog.closeAndDeleteLogFile(); 148 } 149 150 /** 151 * Launches the graphical status panel. It is launched in a 152 * different thread that the main thread because if we have a problem with the 153 * graphical system (for instance the DISPLAY environment variable is not 154 * correctly set) the native libraries will call exit. However if we launch 155 * this from another thread, the thread will just be killed. 156 * 157 * This code also assumes that if the call to ControlPanelSplashScreen.main 158 * worked (and the splash screen was displayed) we will never get out of it 159 * (we will call a System.exit() when we close the graphical status dialog). 160 * 161 * @param args the arguments used to call the 162 * ControlPanelSplashScreen main method. 163 * @return 0 if everything worked fine, or 1 if we could not display properly 164 * the ControlPanelSplashScreen. 165 */ 166 private static int launchControlPanel(final String[] args) 167 { 168 final int[] returnValue = { -1 }; 169 Thread t = new Thread(new Runnable() 170 { 171 @Override 172 public void run() 173 { 174 try 175 { 176 try 177 { 178 initLookAndFeel(); 179 } 180 catch (Throwable t) 181 { 182 logger.warn(LocalizableMessage.raw("Error setting look and feel: "+t, t)); 183 } 184 185 ControlPanelSplashScreen.main(args); 186 returnValue[0] = 0; 187 } 188 catch (Throwable t) 189 { 190 if (ControlPanelLog.isInitialized()) 191 { 192 logger.warn(LocalizableMessage.raw("Error launching GUI: "+t)); 193 StringBuilder buf = new StringBuilder(); 194 while (t != null) 195 { 196 for (StackTraceElement aStack : t.getStackTrace()) { 197 buf.append(aStack).append("\n"); 198 } 199 200 t = t.getCause(); 201 if (t != null) 202 { 203 buf.append("Root cause:\n"); 204 } 205 } 206 logger.warn(LocalizableMessage.raw(buf)); 207 } 208 } 209 } 210 }); 211 /* 212 * This is done to avoid displaying the stack that might occur if there are 213 * problems with the display environment. 214 */ 215 PrintStream printStream = System.err; 216 System.setErr(Utils.getEmptyPrintStream()); 217 t.start(); 218 try 219 { 220 t.join(); 221 } 222 catch (InterruptedException ie) 223 { 224 /* An error occurred, so the return value will be -1. We got nothing to 225 do with this exception. */ 226 } 227 System.setErr(printStream); 228 return returnValue[0]; 229 } 230 231 private static void initLookAndFeel() throws Throwable 232 { 233// Setup MacOSX native menu bar before AWT is loaded. 234 LocalizableMessage title = Utils.getCustomizedObject("INFO_CONTROL_PANEL_TITLE", 235 AdminToolMessages.INFO_CONTROL_PANEL_TITLE.get( 236 DynamicConstants.PRODUCT_NAME), LocalizableMessage.class); 237 Utils.setMacOSXMenuBar(title); 238 UIFactory.initializeLookAndFeel(); 239 } 240} 241 242 243/** 244 * The enumeration containing the different return codes that the command-line 245 * can have. 246 * 247 */ 248enum ErrorReturnCode 249{ 250 /** 251 * Successful display of the status. 252 */ 253 SUCCESSFUL(0), 254 /** 255 * We did no have an error but the status was not displayed (displayed 256 * version or usage). 257 */ 258 SUCCESSFUL_NOP(0), 259 /** 260 * Unexpected error (potential bug). 261 */ 262 ERROR_UNEXPECTED(1), 263 /** 264 * Cannot parse arguments. 265 */ 266 ERROR_PARSING_ARGS(2), 267 /** 268 * User cancelled (for instance not accepting the certificate proposed) or 269 * could not use the provided connection parameters in interactive mode. 270 */ 271 USER_CANCELLED_OR_DATA_ERROR(3), 272 /** 273 * This occurs for instance when the authentication provided by the user is 274 * not valid. 275 */ 276 ERROR_READING_CONFIGURATION_WITH_LDAP(4); 277 278 private int returnCode; 279 private ErrorReturnCode(int returnCode) 280 { 281 this.returnCode = returnCode; 282 } 283 284 /** 285 * Returns the corresponding return code value. 286 * @return the corresponding return code value. 287 */ 288 public int getReturnCode() 289 { 290 return returnCode; 291 } 292} 293 294/** 295 * The splash screen for the control panel. 296 * 297 */ 298class ControlPanelSplashScreen extends org.opends.quicksetup.SplashScreen 299{ 300 private static final long serialVersionUID = 4472839063380302713L; 301 302 private static ControlPanel controlPanel; 303 304 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 305 306 /** 307 * The main method for this class. 308 * It can be called from the event thread and outside the event thread. 309 * @param args arguments to be passed to the method ControlPanel.initialize 310 */ 311 public static void main(String[] args) 312 { 313 ControlPanelSplashScreen screen = new ControlPanelSplashScreen(); 314 screen.display(args); 315 } 316 317 318 /** 319 * This methods constructs the ControlPanel object. 320 * This method assumes that is being called outside the event thread. 321 * @param args arguments to be passed to the method ControlPanel.initialize. 322 */ 323 @Override 324 protected void constructApplication(String[] args) 325 { 326 try 327 { 328 controlPanel = new ControlPanel(); 329 controlPanel.initialize(args); 330 } catch (Throwable t) 331 { 332 if (ControlPanelLog.isInitialized()) 333 { 334 logger.error(LocalizableMessage.raw("Error launching GUI: "+t, t)); 335 } 336 InternalError error = 337 new InternalError("Failed to invoke initialize method"); 338 error.initCause(t); 339 throw error; 340 } 341 } 342 343 /** 344 * This method displays the StatusPanel dialog. 345 * @see org.opends.guitools.controlpanel.ControlPanel#createAndDisplayGUI() 346 * This method assumes that is being called outside the event thread. 347 */ 348 @Override 349 protected void displayApplication() 350 { 351 Runnable runnable = new Runnable() 352 { 353 @Override 354 public void run() 355 { 356 try 357 { 358 logger.info(LocalizableMessage.raw("going to call createAndDisplayGUI.")); 359 controlPanel.createAndDisplayGUI(); 360 logger.info(LocalizableMessage.raw("called createAndDisplayGUI.")); 361 } catch (Throwable t) 362 { 363 logger.error(LocalizableMessage.raw("Error displaying GUI: "+t, t)); 364 InternalError error = 365 new InternalError("Failed to invoke display method"); 366 error.initCause(t); 367 throw error; 368 } 369 } 370 }; 371 if (SwingUtilities.isEventDispatchThread()) 372 { 373 runnable.run(); 374 } 375 else 376 { 377 try 378 { 379 SwingUtilities.invokeAndWait(runnable); 380 } 381 catch (Throwable t) 382 { 383 logger.error(LocalizableMessage.raw("Error calling SwingUtilities.invokeAndWait: "+t, 384 t)); 385 InternalError error = 386 new InternalError( 387 "Failed to invoke SwingUtilities.invokeAndWait method"); 388 error.initCause(t); 389 throw error; 390 } 391 } 392 } 393} 394 395