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 2012-2015 ForgeRock AS. 026 */ 027 028package org.opends.quicksetup.ui; 029 030import org.opends.quicksetup.util.UIKeyStore; 031import org.opends.quicksetup.Application; 032import org.opends.quicksetup.ApplicationException; 033import org.opends.quicksetup.ButtonName; 034import org.opends.quicksetup.UserData; 035import org.opends.quicksetup.UserDataCertificateException; 036import org.opends.quicksetup.UserDataException; 037import org.opends.quicksetup.WizardStep; 038import org.opends.quicksetup.webstart.WebStartDownloader; 039import org.forgerock.i18n.LocalizableMessage; 040import static org.opends.messages.QuickSetupMessages.*; 041 042import javax.swing.*; 043import java.awt.event.WindowEvent; 044import java.security.cert.X509Certificate; 045import java.util.LinkedHashSet; 046import java.util.Set; 047 048import org.forgerock.i18n.slf4j.LocalizedLogger; 049 050/** 051 * This class represents an application with a wizard GUI that can be run in the 052 * context of QuickSetup. Examples of applications might be 'installer', 053 * and 'uninstaller'. 054 */ 055public abstract class GuiApplication extends Application { 056 057 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 058 059 /** The currently displayed wizard step. */ 060 private WizardStep displayedStep; 061 062 /** Downloads .jar files for webstart application. */ 063 protected WebStartDownloader loader; 064 065 /** The QuickSetupDialog in control. */ 066 private QuickSetupDialog qs; 067 068 private String[] args = {}; 069 070 /** 071 * Constructs an instance of an application. Subclasses 072 * of this application must have a default constructor. 073 */ 074 public GuiApplication() { 075 this.displayedStep = getFirstWizardStep(); 076 } 077 078 /** 079 * Gets the frame title of the GUI application that will be used 080 * in some operating systems. 081 * @return internationalized String representing the frame title 082 */ 083 public abstract LocalizableMessage getFrameTitle(); 084 085 /** 086 * Returns the initial wizard step. 087 * @return Step representing the first step to show in the wizard 088 */ 089 public abstract WizardStep getFirstWizardStep(); 090 091 /** 092 * Called by the quicksetup controller when the user advances to 093 * a new step in the wizard. Applications are expected to manipulate 094 * the QuickSetupDialog to reflect the current step. 095 * 096 * @param step Step indicating the new current step 097 * @param userData UserData representing the data specified by the user 098 * @param dlg QuickSetupDialog hosting the wizard 099 */ 100 public void setDisplayedWizardStep(WizardStep step, 101 UserData userData, 102 QuickSetupDialog dlg) { 103 this.displayedStep = step; 104 105 // First call the panels to do the required updates on their layout 106 dlg.setDisplayedStep(step, userData); 107 setWizardDialogState(dlg, userData, step); 108 } 109 110 /** 111 * Called when the user advances to new step in the wizard. Applications 112 * are expected to manipulate the QuickSetupDialog to reflect the current 113 * step. 114 * @param dlg QuickSetupDialog hosting the wizard 115 * @param userData UserData representing the data specified by the user 116 * @param step Step indicating the new current step 117 */ 118 public abstract void setWizardDialogState(QuickSetupDialog dlg, 119 UserData userData, 120 WizardStep step); 121 122 /** 123 * Returns the tab formatted. 124 * @return the tab formatted. 125 */ 126 protected LocalizableMessage getTab() 127 { 128 return formatter.getTab(); 129 } 130 131 /** 132 * Called by the controller when the window is closing. The application 133 * can take application specific actions here. 134 * @param dlg QuickSetupDialog that will be closing 135 * @param evt The event from the Window indicating closing 136 */ 137 public abstract void windowClosing(QuickSetupDialog dlg, WindowEvent evt); 138 139 /** 140 * This method is called when we detected that there is something installed 141 * we inform of this to the user and the user wants to proceed with the 142 * installation destroying the contents of the data and the configuration 143 * in the current installation. 144 */ 145 public void forceToDisplay() { 146 // This is really only appropriate for Installer. 147 // The default implementation is to do nothing. 148 // The Installer application overrides this with 149 // whatever it needs. 150 } 151 152 /** 153 * Called before the application cancels its operation, giving the 154 * user a chance to confirm the cancellation action. 155 * @param qs QuickSetup that can be used for confirming 156 * @return boolean where true indicates that the user answered 157 * affirmatively to the cancelation confirmation 158 */ 159 public boolean confirmCancel(QuickSetup qs) { 160 return qs.displayConfirmation( 161 INFO_CONFIRM_CANCEL_PROMPT.get(), 162 INFO_CONFIRM_CANCEL_TITLE.get()); 163 } 164 165 /** 166 * Get the name of the button that will receive initial focus. 167 * @return ButtonName of the button to receive initial focus 168 */ 169 public abstract ButtonName getInitialFocusButtonName(); 170 171 /** 172 * Creates the main panel for the wizard dialog. 173 * @param dlg QuickSetupDialog used 174 * @return JPanel frame panel 175 */ 176 public JPanel createFramePanel(QuickSetupDialog dlg) { 177 return new FramePanel(dlg.getStepsPanel(), 178 dlg.getCurrentStepPanel(), 179 dlg.getButtonsPanel()); 180 } 181 182 /** 183 * Returns the set of wizard steps used in this application's wizard. 184 * @return Set of Step objects representing wizard steps 185 */ 186 public abstract Set<? extends WizardStep> getWizardSteps(); 187 188 /** 189 * Creates a wizard panel given a specific step. 190 * @param step for which a panel representation should be created 191 * @return QuickSetupStepPanel for representing the <code>step</code> 192 */ 193 public abstract QuickSetupStepPanel createWizardStepPanel(WizardStep step); 194 195 /** 196 * Gets the next step in the wizard given a current step. 197 * @param step Step the current step 198 * @return Step the next step 199 */ 200 public abstract WizardStep getNextWizardStep(WizardStep step); 201 202 /** 203 * Gets the previous step in the wizard given a current step. 204 * @param step Step the current step 205 * @return Step the previous step 206 */ 207 public abstract WizardStep getPreviousWizardStep(WizardStep step); 208 209 /** 210 * Gets the finished step in the wizard. 211 * @return Step the finished step 212 */ 213 public abstract WizardStep getFinishedStep(); 214 215 /** 216 * Gets the currently displayed wizard step. 217 * @return WizardStep being displayed. 218 */ 219 public WizardStep getCurrentWizardStep() { 220 return displayedStep; 221 } 222 223 /** 224 * Indicates whether the provided <code>step</code> is a sub step or not. 225 * @param step WizardStep for which the return value indicates whether 226 * or not is a sub step. 227 * @return boolean where true indicates the provided <code>step</code> is a 228 * substep. 229 */ 230 public boolean isSubStep(WizardStep step) 231 { 232 return false; 233 } 234 235 /** 236 * Indicates whether the provided <code>step</code> is visible or not 237 * depending on the contents of the UserData object that is provided. 238 * @param step WizardStep for which the return value indicates whether 239 * or not is visible. 240 * @param userData the UserData to be used to determine if the step is 241 * visible or not. 242 * @return boolean where true indicates the provided <code>step</code> is 243 * visible. 244 */ 245 public boolean isVisible(WizardStep step, UserData userData) 246 { 247 return true; 248 } 249 250 /** 251 * Indicates whether the provided <code>step</code> is visible or not 252 * depending on the contents of the QuickSetup object that is provided. 253 * @param step WizardStep for which the return value indicates whether 254 * or not is visible. 255 * @param qs the QuickSetup to be used to determine if the step is 256 * visible or not. 257 * @return boolean where true indicates the provided <code>step</code> is 258 * visible. 259 */ 260 public boolean isVisible(WizardStep step, QuickSetup qs) 261 { 262 return true; 263 } 264 265 /** 266 * Returns the list of all the steps in an ordered manner. This is required 267 * because in the case of an application with substeps the user of the other 268 * interfaces is not enough. This is a default implementation that uses 269 * the getNextWizardStep method to calculate the list that work for 270 * applications with no substeps. 271 * @return a list containing ALL the steps (including substeps) in an ordered 272 * manner. 273 */ 274 public LinkedHashSet<WizardStep> getOrderedSteps() 275 { 276 LinkedHashSet<WizardStep> orderedSteps = new LinkedHashSet<>(); 277 WizardStep step = getFirstWizardStep(); 278 orderedSteps.add(step); 279 while (null != (step = getNextWizardStep(step))) { 280 orderedSteps.add(step); 281 } 282 return orderedSteps; 283 } 284 285 /** 286 * Indicates whether or not the user is allowed to return to a previous 287 * step from <code>step</code>. 288 * @param step WizardStep for which the the return value indicates whether 289 * or not the user can return to a previous step 290 * @return boolean where true indicates the user can return to a previous 291 * step from <code>step</code> 292 */ 293 public boolean canGoBack(WizardStep step) { 294 return !getFirstWizardStep().equals(step); 295 } 296 297 /** 298 * Indicates whether or not the user is allowed to move to a new 299 * step from <code>step</code>. 300 * @param step WizardStep for which the the return value indicates whether 301 * or not the user can move to a new step 302 * @return boolean where true indicates the user can move to a new 303 * step from <code>step</code> 304 */ 305 public boolean canGoForward(WizardStep step) { 306 return !step.isProgressStep() && getNextWizardStep(step) != null; 307 } 308 309 /** 310 * Indicates whether or not the user is allowed to finish the wizard from 311 * <code>step</code>. 312 * @param step WizardStep for which the the return value indicates whether 313 * or not the user can finish the wizard 314 * @return boolean where true indicates the user can finish the wizard 315 */ 316 public abstract boolean canFinish(WizardStep step); 317 318 /** 319 * Called when the user has clicked the 'previous' button. 320 * @param cStep WizardStep at which the user clicked the previous button 321 * @param qs QuickSetup controller 322 */ 323 public abstract void previousClicked(WizardStep cStep, QuickSetup qs); 324 325 /** 326 * Called when the user has clicked the 'finish' button. 327 * @param cStep WizardStep at which the user clicked the previous button 328 * @param qs QuickSetup controller 329 * @return boolean that the application uses to indicate the the 330 * application should be launched. If false, the application is 331 * responsible for updating the user data for the final screen and 332 * launching the application if this is the desired behavior. 333 */ 334 public abstract boolean finishClicked(final WizardStep cStep, 335 final QuickSetup qs); 336 337 /** 338 * Called when the user has clicked the 'next' button. 339 * @param cStep WizardStep at which the user clicked the next button 340 * @param qs QuickSetup controller 341 */ 342 public abstract void nextClicked(WizardStep cStep, QuickSetup qs); 343 344 /** 345 * Called when the user has clicked the 'close' button. 346 * @param cStep WizardStep at which the user clicked the close button 347 * @param qs QuickSetup controller 348 */ 349 public void closeClicked(WizardStep cStep, QuickSetup qs) { 350 qs.quit(); 351 } 352 353 /** 354 * Called when the user has clicked the 'quit' button. 355 * @param step WizardStep at which the user clicked the quit button 356 * @param qs QuickSetup controller 357 */ 358 public void quitClicked(WizardStep step, QuickSetup qs) { 359 qs.quit(); 360 } 361 362 /** 363 * Called whenever this application should update its user data from 364 * values found in QuickSetup. 365 * @param cStep current wizard step 366 * @param qs QuickSetup controller 367 * @throws org.opends.quicksetup.UserDataException if there is a problem with 368 * the data 369 */ 370 public abstract void updateUserData(WizardStep cStep, QuickSetup qs) 371 throws UserDataException; 372 373 /** 374 * Gets the key for the close button's tool tip text. 375 * @return String key of the text in the resource bundle 376 */ 377 public LocalizableMessage getCloseButtonToolTip() { 378 return INFO_CLOSE_BUTTON_TOOLTIP.get(); 379 } 380 381 /** 382 * Gets the key for the quit button's tool tip text. 383 * @return String key of the text in the resource bundle 384 */ 385 public LocalizableMessage getQuitButtonToolTip() { 386 return INFO_QUIT_BUTTON_INSTALL_TOOLTIP.get(); 387 } 388 389 /** 390 * Gets the key for the finish button's tool tip text. 391 * @return String key of the text in the resource bundle 392 */ 393 public LocalizableMessage getFinishButtonToolTip() { 394 return INFO_FINISH_BUTTON_TOOLTIP.get(); 395 } 396 397 /** 398 * Gets the key for the finish button's label. 399 * @return String key of the text in the resource bundle 400 */ 401 public LocalizableMessage getFinishButtonLabel() { 402 return INFO_FINISH_BUTTON_LABEL.get(); 403 } 404 405 /** 406 * Indicates whether the finish button must be placed on the left (close to 407 * "Next" button) or on the right (close to "Quit" button). 408 * @return <CODE>true</CODE> if the finish button must be placed on the left 409 * and <CODE>false</CODE> otherwise. 410 */ 411 public boolean finishOnLeft() 412 { 413 return true; 414 } 415 416 /** 417 * Updates the list of certificates accepted by the user in the trust manager 418 * based on the information stored in the UserDataCertificateException we got 419 * when trying to connect in secure mode. 420 * @param ce the UserDataCertificateException that contains the information to 421 * be used. 422 * @param acceptPermanently whether the certificate must be accepted 423 * permanently or not. 424 */ 425 protected void acceptCertificateForException(UserDataCertificateException ce, 426 boolean acceptPermanently) 427 { 428 X509Certificate[] chain = ce.getChain(); 429 String authType = ce.getAuthType(); 430 String host = ce.getHost(); 431 432 if (chain != null && authType != null && host != null) 433 { 434 logger.info(LocalizableMessage.raw("Accepting certificate presented by host "+host)); 435 getTrustManager().acceptCertificate(chain, authType, host); 436 } 437 else 438 { 439 if (chain == null) 440 { 441 logger.warn(LocalizableMessage.raw( 442 "The chain is null for the UserDataCertificateException")); 443 } 444 if (authType == null) 445 { 446 logger.warn(LocalizableMessage.raw( 447 "The auth type is null for the UserDataCertificateException")); 448 } 449 if (host == null) 450 { 451 logger.warn(LocalizableMessage.raw( 452 "The host is null for the UserDataCertificateException")); 453 } 454 } 455 if (acceptPermanently && chain != null) 456 { 457 try 458 { 459 UIKeyStore.acceptCertificate(chain); 460 } 461 catch (Throwable t) 462 { 463 logger.warn(LocalizableMessage.raw("Error accepting certificate: "+t, t)); 464 } 465 } 466 } 467 468 /** 469 * Begins downloading webstart jars in another thread 470 * for WebStart applications only. 471 */ 472 protected void initLoader() { 473 loader = new WebStartDownloader(); 474 loader.start(false); 475 } 476 477 /** 478 * Waits for the loader to be finished. Every time we have an update in the 479 * percentage that is downloaded we notify the listeners of this. 480 * 481 * @param maxRatio is the integer value that tells us which is the max ratio 482 * that corresponds to the download. It is used to calculate how the global 483 * installation ratio changes when the download ratio increases. For instance 484 * if we suppose that the download takes 25 % of the total installation 485 * process, then maxRatio will be 25. When the download is complete this 486 * method will send a notification to the ProgressUpdateListeners with a ratio 487 * of 25 %. 488 * @throws org.opends.quicksetup.ApplicationException if something goes wrong 489 * 490 */ 491 protected void waitForLoader(Integer maxRatio) throws ApplicationException { 492 int lastPercentage = -1; 493 WebStartDownloader.Status lastStatus = 494 WebStartDownloader.Status.DOWNLOADING; 495 while (!loader.isFinished() && loader.getException() == null) 496 { 497 checkAbort(); 498 // Pool until is over 499 int perc = loader.getDownloadPercentage(); 500 WebStartDownloader.Status downloadStatus = loader.getStatus(); 501 if (perc != lastPercentage || downloadStatus != lastStatus) 502 { 503 lastPercentage = perc; 504 int ratio = (perc * maxRatio) / 100; 505 LocalizableMessage summary; 506 switch (downloadStatus) 507 { 508 case VALIDATING: 509 summary = INFO_VALIDATING_RATIO.get(perc, loader.getCurrentValidatingPercentage()); 510 break; 511 case UPGRADING: 512 summary = INFO_UPGRADING_RATIO.get(perc, loader.getCurrentValidatingPercentage()); 513 break; 514 default: 515 summary = INFO_DOWNLOADING_RATIO.get(perc); 516 } 517 loader.setSummary(summary); 518 notifyListeners(ratio, summary, null); 519 } 520 checkAbort(); 521 try 522 { 523 Thread.sleep(300); 524 } catch (Exception ex) 525 { 526 // do nothing; 527 } 528 } 529 checkAbort(); 530 531 if (loader.getException() != null) 532 { 533 throw loader.getException(); 534 } 535 } 536 537 /** 538 * Gets the amount of addition pixels added to the height 539 * of the tallest panel in order to size the wizard for 540 * asthetic reasons. 541 * @return int height to add 542 */ 543 public int getExtraDialogHeight() { 544 return 0; 545 } 546 547 /** 548 * Sets the QuickSetupDialog driving this application. 549 * @param dialog QuickSetupDialog driving this application 550 */ 551 public void setQuickSetupDialog(QuickSetupDialog dialog) { 552 this.qs = dialog; 553 } 554 555 /** 556 * Sets the arguments passed in the command-line to launch the application. 557 * @param args the arguments passed in the command-line to launch the 558 * application. 559 */ 560 public void setUserArguments(String[] args) 561 { 562 this.args = args; 563 } 564 565 /** 566 * Returns the arguments passed in the command-line to launch the application. 567 * @return the arguments passed in the command-line to launch the application. 568 */ 569 public String[] getUserArguments() 570 { 571 return args; 572 } 573}