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 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2015 ForgeRock AS.
026 */
027package org.opends.quicksetup;
028
029import static org.opends.messages.QuickSetupMessages.*;
030
031import java.awt.Dimension;
032import java.awt.Frame;
033import java.awt.Graphics;
034import java.awt.Image;
035import java.awt.MediaTracker;
036import java.awt.Toolkit;
037import java.awt.Window;
038
039import javax.swing.SwingUtilities;
040
041import org.opends.quicksetup.ui.Utilities;
042
043/**
044 * This is the class that displays a splash screen and in the background it will
045 * create QuickSetup object.
046 *
047 * The main method of this class is directly called by the Java Web Start
048 * mechanism to launch the JWS setup.
049 *
050 * This class tries to minimize the time to be displayed. So it does the loading
051 * of the setup class in runtime once we already have displayed the splash
052 * screen. This is why the quickSetup variable is of type Object.
053 *
054 * This class can be reused by simply overwriting the methods
055 * constructApplication() and displayApplication().
056 */
057public class SplashScreen extends Window
058{
059  private static final long serialVersionUID = 8918803902867388766L;
060
061  private Image image;
062
063  private Object quickSetup;
064
065  private Class<?> quickSetupClass;
066
067  /** Constant for the display of the splash screen. */
068  private static final int MIN_SPLASH_DISPLAY = 3000;
069
070  /**
071   * The main method for this class.
072   * It can be called from the event thread and outside the event thread.
073   * @param args arguments to be passed to the method QuickSetup.initialize
074   */
075  public static void main(String[] args)
076  {
077    SplashScreen screen = new SplashScreen();
078    screen.display(args);
079  }
080
081  /** {@inheritDoc} */
082  public void update(Graphics g)
083  {
084    paint(g);
085  }
086
087  /** {@inheritDoc} */
088  public void paint(Graphics g)
089  {
090    g.drawImage(image, 0, 0, this);
091  }
092
093  /** Protected constructor to force to use the main method. */
094  protected SplashScreen()
095  {
096    super(new Frame());
097    try
098    {
099      image = getSplashImage();
100      MediaTracker mt = new MediaTracker(this);
101      mt.addImage(image, 0);
102      mt.waitForID(0);
103
104      int width = image.getWidth(this);
105      int height = image.getHeight(this);
106      setPreferredSize(new Dimension(width, height));
107      setSize(width, height);
108      Utilities.centerOnScreen(this);
109    } catch (Exception ex)
110    {
111      ex.printStackTrace(); // Bug
112    }
113  }
114
115  /**
116   * The method used to display the splash screen.  It will also call create
117   * the application associated with this SplashScreen and display it.
118   * It can be called from the event thread and outside the event thread.
119   * @param args arguments to be passed to the method QuickSetup.initialize
120   */
121  protected void display(String[] args)
122  {
123    if (SwingUtilities.isEventDispatchThread())
124    {
125      final String[] fArgs = args;
126      Thread t = new Thread(new Runnable()
127      {
128        public void run()
129        {
130          mainOutsideEventThread(fArgs);
131        }
132      });
133      t.start();
134    } else
135    {
136      mainOutsideEventThread(args);
137    }
138  }
139
140  /**
141   * This method creates the image directly instead of using UIFactory to reduce
142   * class loading.
143   * @return the splash image.
144   */
145  private Image getSplashImage()
146  {
147    String resource = INFO_SPLASH_ICON.get().toString();
148    resource = "org/opends/quicksetup/" + resource;
149    return Toolkit.getDefaultToolkit().createImage(
150        getClass().getClassLoader().getResource(resource));
151  }
152
153  /**
154   * This is basically the method that is execute in SplashScreen.main but it
155   * it assumes that is being called outside the event thread.
156   *
157   * @param args arguments to be passed to the method QuickSetup.initialize.
158   */
159  private void mainOutsideEventThread(String[] args)
160  {
161    displaySplashScreen();
162    long splashDisplayStartTime = System.currentTimeMillis();
163    constructApplication(args);
164    sleepIfNecessary(splashDisplayStartTime);
165    disposeSplashScreen();
166    displayApplication();
167  }
168
169  /**
170   * This methods displays the splash screen.
171   * This method assumes that is being called outside the event thread.
172   */
173  private void displaySplashScreen()
174  {
175    try
176    {
177      SwingUtilities.invokeAndWait(new Runnable()
178      {
179        public void run()
180        {
181          setVisible(true);
182        }
183      });
184    } catch (Exception ex)
185    {
186      ex.printStackTrace();
187    }
188  }
189
190  /**
191   * This methods constructs the objects before displaying them.
192   * This method assumes that is being called outside the event thread.
193   * This method can be overwritten by subclasses to construct other objects
194   * different than the Quick Setup.
195   * @param args arguments passed in the main of this class.
196   */
197  protected void constructApplication(String[] args)
198  {
199    try
200    {
201      quickSetupClass = Class.forName("org.opends.quicksetup.ui.QuickSetup");
202      quickSetup = quickSetupClass.newInstance();
203      quickSetupClass.getMethod("initialize", new Class[]
204        { String[].class }).invoke(quickSetup, new Object[]
205        { args });
206    } catch (Exception e)
207    {
208      InternalError error =
209          new InternalError("Failed to invoke initialize method");
210      error.initCause(e);
211      throw error;
212    }
213  }
214
215  /**
216   * This method displays the QuickSetup dialog.
217   * @see org.opends.quicksetup.ui.QuickSetup#display
218   * This method assumes that is being called outside the event thread.
219   * This method can be overwritten by subclasses to construct other objects
220   * different than the Quick Setup.
221   */
222  protected void displayApplication()
223  {
224    try
225    {
226      SwingUtilities.invokeAndWait(new Runnable()
227      {
228        public void run()
229        {
230          try
231          {
232            quickSetupClass.getMethod("display").invoke(quickSetup);
233          } catch (Exception e)
234          {
235            InternalError error =
236                new InternalError("Failed to invoke display method");
237            error.initCause(e);
238            throw error;
239          }
240        }
241      });
242    } catch (Exception ex)
243    {
244      // do nothing;
245    }
246  }
247
248  /**
249   * Disposes the splash screen.
250   * This method assumes that is being called outside the event thread.
251   */
252  private void disposeSplashScreen()
253  {
254    try
255    {
256      SwingUtilities.invokeAndWait(new Runnable()
257      {
258        public void run()
259        {
260          setVisible(false);
261          dispose();
262        }
263      });
264    } catch (Exception ex)
265    {
266      // do nothing;
267    }
268  }
269
270  /**
271   * This method just executes an sleep depending on how long the splash
272   * screen has been displayed.  The idea of calling this method is to have the
273   * splash screen displayed a minimum time (specified by
274   * MIN_SPLASH_DISPLAY).
275   * @param splashDisplayStartTime the time in milliseconds when the splash
276   * screen started displaying.
277   */
278  private void sleepIfNecessary(long splashDisplayStartTime)
279  {
280    long t2 = System.currentTimeMillis();
281
282    long sleepTime = MIN_SPLASH_DISPLAY - (t2 - splashDisplayStartTime);
283
284    if (sleepTime > 0)
285    {
286      try
287      {
288        Thread.sleep(sleepTime);
289      } catch (Exception ex)
290      {
291        // do nothing;
292      }
293    }
294  }
295}