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}