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}