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