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 System.exit(ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode()); 104 } 105 106 // Checks the version - if upgrade required, the tool is unusable 107 if (!argParser.isRemote()) 108 { 109 try 110 { 111 BuildVersion.checkVersionMismatch(); 112 } 113 catch (InitializationException e) 114 { 115 System.err.println(wrapText(e.getMessage(), MAX_LINE_WIDTH)); 116 System.exit(ErrorReturnCode.ERROR_UNEXPECTED.getReturnCode()); 117 } 118 } 119 120 if (!argParser.usageOrVersionDisplayed()) 121 { 122 int exitCode = launchControlPanel(args); 123 if (exitCode != 0) 124 { 125 String logFileName = null; 126 if (ControlPanelLog.getLogFile() != null) 127 { 128 logFileName = ControlPanelLog.getLogFile().toString(); 129 } 130 if (logFileName != null) 131 { 132 System.err.println(wrapText( 133 ERR_CONTROL_PANEL_LAUNCHER_GUI_LAUNCH_FAILED_DETAILS.get( 134 logFileName), 135 Utils.getCommandLineMaxLineWidth())); 136 } 137 else 138 { 139 System.err.println(wrapText( 140 ERR_CONTROL_PANEL_LAUNCHER_GUI_LAUNCH_FAILED.get(), 141 Utils.getCommandLineMaxLineWidth())); 142 } 143 System.exit(exitCode); 144 } 145 } 146 } 147 148 /** 149 * Launches the graphical status panel. It is launched in a 150 * different thread that the main thread because if we have a problem with the 151 * graphical system (for instance the DISPLAY environment variable is not 152 * correctly set) the native libraries will call exit. However if we launch 153 * this from another thread, the thread will just be killed. 154 * 155 * This code also assumes that if the call to ControlPanelSplashScreen.main 156 * worked (and the splash screen was displayed) we will never get out of it 157 * (we will call a System.exit() when we close the graphical status dialog). 158 * 159 * @param args the arguments used to call the 160 * ControlPanelSplashScreen main method. 161 * @return 0 if everything worked fine, or 1 if we could not display properly 162 * the ControlPanelSplashScreen. 163 */ 164 private static int launchControlPanel(final String[] args) 165 { 166 final int[] returnValue = { -1 }; 167 Thread t = new Thread(new Runnable() 168 { 169 @Override 170 public void run() 171 { 172 try 173 { 174 try 175 { 176 initLookAndFeel(); 177 } 178 catch (Throwable t) 179 { 180 logger.warn(LocalizableMessage.raw("Error setting look and feel: "+t, t)); 181 } 182 183 ControlPanelSplashScreen.main(args); 184 returnValue[0] = 0; 185 } 186 catch (Throwable t) 187 { 188 if (ControlPanelLog.isInitialized()) 189 { 190 logger.warn(LocalizableMessage.raw("Error launching GUI: "+t)); 191 StringBuilder buf = new StringBuilder(); 192 while (t != null) 193 { 194 for (StackTraceElement aStack : t.getStackTrace()) { 195 buf.append(aStack).append("\n"); 196 } 197 198 t = t.getCause(); 199 if (t != null) 200 { 201 buf.append("Root cause:\n"); 202 } 203 } 204 logger.warn(LocalizableMessage.raw(buf)); 205 } 206 } 207 } 208 }); 209 /* 210 * This is done to avoid displaying the stack that might occur if there are 211 * problems with the display environment. 212 */ 213 PrintStream printStream = System.err; 214 System.setErr(Utils.getEmptyPrintStream()); 215 t.start(); 216 try 217 { 218 t.join(); 219 } 220 catch (InterruptedException ie) 221 { 222 /* An error occurred, so the return value will be -1. We got nothing to 223 do with this exception. */ 224 } 225 System.setErr(printStream); 226 return returnValue[0]; 227 } 228 229 private static void initLookAndFeel() throws Throwable 230 { 231// Setup MacOSX native menu bar before AWT is loaded. 232 LocalizableMessage title = Utils.getCustomizedObject("INFO_CONTROL_PANEL_TITLE", 233 AdminToolMessages.INFO_CONTROL_PANEL_TITLE.get( 234 DynamicConstants.PRODUCT_NAME), LocalizableMessage.class); 235 Utils.setMacOSXMenuBar(title); 236 UIFactory.initializeLookAndFeel(); 237 } 238} 239 240 241/** 242 * The enumeration containing the different return codes that the command-line 243 * can have. 244 * 245 */ 246enum ErrorReturnCode 247{ 248 /** 249 * Successful display of the status. 250 */ 251 SUCCESSFUL(0), 252 /** 253 * We did no have an error but the status was not displayed (displayed 254 * version or usage). 255 */ 256 SUCCESSFUL_NOP(0), 257 /** 258 * Unexpected error (potential bug). 259 */ 260 ERROR_UNEXPECTED(1), 261 /** 262 * Cannot parse arguments. 263 */ 264 ERROR_PARSING_ARGS(2), 265 /** 266 * User cancelled (for instance not accepting the certificate proposed) or 267 * could not use the provided connection parameters in interactive mode. 268 */ 269 USER_CANCELLED_OR_DATA_ERROR(3), 270 /** 271 * This occurs for instance when the authentication provided by the user is 272 * not valid. 273 */ 274 ERROR_READING_CONFIGURATION_WITH_LDAP(4); 275 276 private int returnCode; 277 private ErrorReturnCode(int returnCode) 278 { 279 this.returnCode = returnCode; 280 } 281 282 /** 283 * Returns the corresponding return code value. 284 * @return the corresponding return code value. 285 */ 286 public int getReturnCode() 287 { 288 return returnCode; 289 } 290} 291 292/** 293 * The splash screen for the control panel. 294 * 295 */ 296class ControlPanelSplashScreen extends org.opends.quicksetup.SplashScreen 297{ 298 private static final long serialVersionUID = 4472839063380302713L; 299 300 private static ControlPanel controlPanel; 301 302 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 303 304 /** 305 * The main method for this class. 306 * It can be called from the event thread and outside the event thread. 307 * @param args arguments to be passed to the method ControlPanel.initialize 308 */ 309 public static void main(String[] args) 310 { 311 ControlPanelSplashScreen screen = new ControlPanelSplashScreen(); 312 screen.display(args); 313 } 314 315 316 /** 317 * This methods constructs the ControlPanel object. 318 * This method assumes that is being called outside the event thread. 319 * @param args arguments to be passed to the method ControlPanel.initialize. 320 */ 321 @Override 322 protected void constructApplication(String[] args) 323 { 324 try 325 { 326 controlPanel = new ControlPanel(); 327 controlPanel.initialize(args); 328 } catch (Throwable t) 329 { 330 if (ControlPanelLog.isInitialized()) 331 { 332 logger.error(LocalizableMessage.raw("Error launching GUI: "+t, t)); 333 } 334 InternalError error = 335 new InternalError("Failed to invoke initialize method"); 336 error.initCause(t); 337 throw error; 338 } 339 } 340 341 /** 342 * This method displays the StatusPanel dialog. 343 * @see org.opends.guitools.controlpanel.ControlPanel#createAndDisplayGUI() 344 * This method assumes that is being called outside the event thread. 345 */ 346 @Override 347 protected void displayApplication() 348 { 349 Runnable runnable = new Runnable() 350 { 351 @Override 352 public void run() 353 { 354 try 355 { 356 logger.info(LocalizableMessage.raw("going to call createAndDisplayGUI.")); 357 controlPanel.createAndDisplayGUI(); 358 logger.info(LocalizableMessage.raw("called createAndDisplayGUI.")); 359 } catch (Throwable t) 360 { 361 logger.error(LocalizableMessage.raw("Error displaying GUI: "+t, t)); 362 InternalError error = 363 new InternalError("Failed to invoke display method"); 364 error.initCause(t); 365 throw error; 366 } 367 } 368 }; 369 if (SwingUtilities.isEventDispatchThread()) 370 { 371 runnable.run(); 372 } 373 else 374 { 375 try 376 { 377 SwingUtilities.invokeAndWait(runnable); 378 } 379 catch (Throwable t) 380 { 381 logger.error(LocalizableMessage.raw("Error calling SwingUtilities.invokeAndWait: "+t, 382 t)); 383 InternalError error = 384 new InternalError( 385 "Failed to invoke SwingUtilities.invokeAndWait method"); 386 error.initCause(t); 387 throw error; 388 } 389 } 390 } 391} 392 393