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