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