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