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