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