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