View Javadoc

1   package com.melloware.jukes.gui;
2   
3   import java.awt.AWTException;
4   import java.awt.Color;
5   import java.awt.Dimension;
6   import java.awt.Image;
7   import java.awt.SystemTray;
8   import java.io.File;
9   import java.text.MessageFormat;
10  import java.util.Iterator;
11  import java.util.List;
12  import java.util.Locale;
13  import java.util.Properties;
14  import java.util.ResourceBundle;
15  import java.util.logging.LogManager;
16  
17  import javax.sound.sampled.spi.FormatConversionProvider;
18  import javax.swing.UIManager;
19  import javax.swing.plaf.DimensionUIResource;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.commons.lang.SystemUtils;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.log4j.Level;
26  import org.apache.log4j.Logger;
27  import org.hibernate.HibernateException;
28  import org.hibernate.exception.JDBCConnectionException;
29  
30  import com.jgoodies.looks.Options;
31  import com.jgoodies.uif.AbstractFrame;
32  import com.jgoodies.uif.action.ActionManager;
33  import com.jgoodies.uif.application.Application;
34  import com.jgoodies.uif.application.ApplicationConfiguration;
35  import com.jgoodies.uif.application.ApplicationDescription;
36  import com.jgoodies.uif.application.ResourceIDs;
37  import com.jgoodies.uif.osx.OSXApplicationMenu;
38  import com.jgoodies.uif.splash.ImageSplash;
39  import com.jgoodies.uif.splash.Splash;
40  import com.jgoodies.uif.splash.SplashProvider;
41  import com.jgoodies.uif.util.ResourceUtils;
42  import com.jgoodies.uifextras.convenience.DefaultApplicationStarter;
43  import com.melloware.jintellitype.JIntellitype;
44  import com.melloware.jintellitype.JIntellitypeException;
45  import com.melloware.jukes.db.Database;
46  import com.melloware.jukes.db.HibernateUtil;
47  import com.melloware.jukes.exception.InfrastructureException;
48  import com.melloware.jukes.gui.tool.Actions;
49  import com.melloware.jukes.gui.tool.MainController;
50  import com.melloware.jukes.gui.tool.MainModule;
51  import com.melloware.jukes.gui.tool.Resources;
52  import com.melloware.jukes.gui.tool.Settings;
53  import com.melloware.jukes.gui.tool.logging.AwtLogHandler;
54  import com.melloware.jukes.gui.view.MainFrame;
55  import com.melloware.jukes.tray.ITrayIcon;
56  import com.melloware.jukes.tray.JukesTrayIcon;
57  import com.melloware.jukes.util.MessageUtil;
58  import com.melloware.jukes.util.NoFlickerSplashWrapper;
59  import com.sun.media.sound.JDK13Services;
60  
61  /**
62   * This is the main class of the Jukes application. It utilizes the default
63   * application startup process from the JGoodies UI framework.
64   * <p>
65   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
66   * @author Emil A. Lefkof III <info@melloware.com>
67   * @version 4.0
68   * 2009, 2010 AZ Development
69   * <p>
70   * @see Actions
71   * @see MainController
72   * @see MainModule
73   * @see MainFrame
74   */
75  public final class Jukes extends DefaultApplicationStarter {
76  
77     private static final Log LOG = LogFactory.getLog(Jukes.class);
78     private static ApplicationDescription description;
79     private static ApplicationConfiguration configuration;
80     private static MainModule mainModule;
81  
82     /**
83      * Defines a bunch of application wide constants, and launches the boot
84      * process for the Jukes application.
85      */
86     public static void main(String[] arguments) {
87        LOG.info("Application Started...");
88        try {
89           // assigns a custom handler for catching uncaught exceptions
90           System.setProperty("sun.awt.exception.handler", AwtLogHandler.class.getName());
91  
92           // set the resource bundle path now, so that we can internationalize
93           // some parameters
94           ResourceUtils.setBundlePath("Resource");
95           
96           //AZ: seek for JIntellitype.dll file in the Application directory
97           final File aFile = new File("JIntellitype.dll");
98           if (!aFile.exists()) {
99          	 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
100         	 final String errorMessage = ResourceUtils.getString("messages.JIntellitypeNotExists");
101              LOG.error(errorMessage);
102              MessageUtil.showError(mainFrame, errorMessage);
103              System.exit(1);
104          }
105 
106          // verify that the user has the correct version of the JRE
107          verifyJREVersion();
108 
109          // load the setup information for the application
110          getConfiguration();
111          getDescription();
112 
113          if (SystemUtils.IS_OS_WINDOWS) {
114             fixWindowsTimingBug();
115 
116             if (JIntellitype.checkInstanceAlreadyRunning(Resources.APPLICATION_NAME)) {
117                System.exit(0);
118             }
119          }
120 
121          final Jukes launcher = new Jukes();
122          launcher.boot(description, configuration);
123       } catch (Throwable ex) {
124     	 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
125     	 final String errorMessage = ResourceUtils.getString("messages.ExceptionStarting" + "\n\n" + ex);
126          LOG.error(errorMessage, ex);
127          MessageUtil.showError(mainFrame, errorMessage); //AZ 
128          System.exit(1);
129       }
130 
131    }
132 
133    /*
134     * (non-Javadoc)
135     * @see com.jgoodies.swing.convenience.DefaultApplicationStarter#getDefaultLogFilePattern()
136     */
137    protected String getDefaultLogFilePattern() {
138       //return "%h/.jukes/gui.log";
139 	  return "./log/gui.log";//AZ redirect LOG-file to /log/ sub-directory
140    }
141 
142    /**
143     * Configures the splash to set a brown progress bar.
144     */
145    protected void configureSplash() {
146       super.configureSplash();
147       // Create image splash
148       final Image image = ResourceUtils.getIcon(ResourceIDs.SPLASH_IMAGE).getImage();
149       final ImageSplash splash = new ImageSplash(image, true);
150 
151       // Wrap with de-flicker "filter"
152       final SplashProvider splashWrapper = new NoFlickerSplashWrapper(splash);
153       Splash.setProvider(splashWrapper);
154       splash.setNoteEnabled(true);
155       splash.setAlwaysOnTop(true);
156       splash.setProgressBarBounds(60);
157       splash.setTextColor(Color.WHITE);
158       splash.setForeground(Color.YELLOW);
159       splash.setBackground(Color.WHITE);
160    }
161 
162    /**
163     * Configures the user interface.
164     */
165    protected void configureUI() {
166       Options.setUseSystemFonts(true);
167       Options.setDefaultIconSize(new Dimension(16, 16));
168       Options.setPopupDropShadowEnabled(true);
169       UIManager.put("ToolBar.separatorSize", new DimensionUIResource(6, 18));
170       UIManager.put("FileChooser.useSystemIcons", Boolean.TRUE);
171       LOG.info("Adding Look and Feel [net.beeger.squareness.SquarenessLookAndFeel]");
172       UIManager.installLookAndFeel("Squareness", "net.beeger.squareness.SquarenessLookAndFeel");
173       LOG.info("Adding Look and Feel [net.sourceforge.napkinlaf.NapkinLookAndFeel]");
174       UIManager.installLookAndFeel("Napkin", "net.sourceforge.napkinlaf.NapkinLookAndFeel");
175       LOG.info("Adding Look and Feel [com.nilo.plaf.nimrod.NimRODLookAndFeel]");
176       UIManager.installLookAndFeel("NimROD", "com.nilo.plaf.nimrod.NimRODLookAndFeel");
177       LOG.info("Adding Look and Feel [org.jvnet.substance.SubstanceLookAndFeel]");
178       UIManager.installLookAndFeel("Substance", "org.jvnet.substance.SubstanceLookAndFeel");
179       LOG.info("Adding Look and Feel [com.lipstikLF.LipstikLookAndFeel]");
180       UIManager.installLookAndFeel("Lipstik", "com.lipstikLF.LipstikLookAndFeel");
181       super.configureUI();
182    }
183 
184    /*
185     * (non-Javadoc)
186     * @see com.jgoodies.uifextras.convenience.DefaultApplicationStarter#createMainFrame()
187     */
188    @Override
189    protected AbstractFrame createMainFrame() {
190       final MainFrame mainFrame = new MainFrame(mainModule);
191       Application.setDefaultParentFrame(mainFrame);
192       return mainFrame;
193    }
194 
195    /**
196     * Initializes the actions used in this application.
197     */
198    @Override
199    protected void initializeActions() {
200       LOG.debug("Initialize Actions");
201       OSXApplicationMenu.register(ActionManager.get(Actions.HELP_ABOUT_DIALOG_ID), ActionManager
202                .get(Actions.PREFERENCES_ID), ActionManager.get(Actions.EXIT_ID));
203       OSXApplicationMenu.setAboutName(Resources.APPLICATION_NAME);
204 
205    }
206 
207    /**
208     * Load all the SPI sound codecs and log them.
209     */
210    protected void initializeCodecs() {
211       LOG.info("Loading Codecs");
212       final List codecs = JDK13Services.getProviders(FormatConversionProvider.class);
213       for (Iterator iter = codecs.iterator(); iter.hasNext();) {
214          FormatConversionProvider codec = (FormatConversionProvider) iter.next();
215          LOG.info("Sound Codec: " + codec.toString());
216       }
217    }
218 
219    /**
220     * Brings up the application, it therefore initializes the main frame, checks
221     * the setup process, initializes all actions, then builds the main frame,
222     * and finally opens it.
223     */
224    protected void launchApplication() {
225       // Create the module that provides all high-level models.
226       Splash.setNote("Creating Models...", 20);
227       LOG.info("Creating Models...");
228       mainModule = new MainModule();
229 
230       // initialize logging
231       Splash.setNote("Init Logging...", 30);
232       LOG.info("Init Logging...");
233       initializeLogging();
234 
235       // Now add a more sophisticated handler.
236       addMessageHandler();
237 
238       // initialize language from prefs
239       ResourceUtils.setBundle(ResourceBundle.getBundle("Resource", new Locale(MainModule.SETTINGS.getLocale()),
240                Jukes.class.getClassLoader()));
241 
242       // initialize database and hibernate
243       Splash.setNote("Init DB...", 35);
244       LOG.info("Init DB...");
245       initializeDatabase();
246 
247       // Create the controller that provides the major operations.
248       Splash.setNote("Controller...", 40);
249       LOG.info("Creating Controller...");
250       final MainController mainController = new MainController(mainModule);
251 
252       // initialize sound codecs
253       Splash.setNote("Sound Codecs...", 45);
254       initializeCodecs();
255 
256       // Initialize all Actions
257       Splash.setNote("Init Actions...", 50);
258       LOG.info("Init Actions...");
259       Actions.initializeFor(mainModule, mainController);
260       initializeActions();
261 
262       // Create and build the main frame.
263       Splash.setNote("Create Mainframe...", 55);
264       LOG.info("Create Mainframe...");
265       final AbstractFrame mainFrame = createMainFrame();
266 
267       checkSetup();
268 
269       // initialize tray icon if on Windows
270       Splash.setNote("Init Tray...", 60);
271       LOG.info("Init Tray...");
272       intializeTrayIcon((MainFrame) mainFrame);
273 
274       // initialize any browsers on the system
275       Splash.setNote("Init Jintellitype...", 70);
276       LOG.info("Init Jintellitype...");
277       intializeJIntellitype((MainFrame) mainFrame);
278 
279       // add the shutdown listener
280       shutdownHook();
281 
282       Splash.setNote("Building UI...", 80);
283       LOG.info("Building UI...");
284       mainFrame.build();
285 
286       Splash.setNote("Finishing...", 90);
287       LOG.info("Finishing...");
288       mainFrame.open();
289       mainController.checkForOpenTipOfTheDayDialog();
290       LOG.info("Application Finished Loading.");
291    }
292 
293    /**
294     * Return JGoodies UIF ApplicationConfiguration for specified prefix. The
295     * prefix is used as a subdirectory under the .bb directory to have separate
296     * persistent areas, and is also used as a prefix node in the Preferences
297     * (register) to keep those separate as well.
298     * @return application configuration object.
299     */
300    private static synchronized ApplicationConfiguration getConfiguration() {
301       if (configuration == null) {
302          configuration = new ApplicationConfiguration(Resources.APPLICATION_LOCATION, 
303                   "", // resource properties URL
304                   Resources.getString(Resources.HELP_GLOBAL_HELPSET), //"docs/help/global/Help.hs",  helpset URL
305                   Resources.getString(Resources.HELP_TIPS_INDEX)); //"docs/help/tips/index.txt" Tips index path
306       }
307 
308       return configuration;
309    }
310 
311    /**
312     * Creates application description object.
313     * @return application description.
314     */
315    private static synchronized ApplicationDescription getDescription() {
316       // get the version from the manifest file
317       final String buildFullLabel = getProjectVersion();
318 
319       // trim off the build number
320       final String buildLabel = StringUtils.substringBeforeLast(buildFullLabel, ".");
321       if (description == null) {
322          LOG.info("Jukes Version: " + buildFullLabel);
323          description = new ApplicationDescription(Resources.APPLICATION_NAME, 
324                   Resources.APPLICATION_NAME, // Application long name
325                   buildLabel, // Version
326                   buildFullLabel, // Full version
327                   Resources.APPLICATION_DESCRIPTION, // Description
328                   Resources.APPLICATION_COPYRIGHT, // Copyright
329                   Resources.APPLICATION_VENDOR, // Vendor
330                   Resources.APPLICATION_URL, // Vendor URL
331                   Resources.APPLICATION_EMAIL); // Vendor email
332       }
333 
334       return description;
335    }
336 
337    /**
338     * Attempts to read the version number out of the pom.properties. If not
339     * found then RUNNING.IN.IDE.FULL is returned as the version.
340     * <p>
341     * @return the full version number of this application
342     */
343    private static String getProjectVersion() {
344       String version;
345 
346       try {
347          final Properties pomProperties = new Properties();
348          pomProperties.load(Jukes.class.getResourceAsStream("/META-INF/maven/com.melloware/jukes/pom.properties"));
349          version = pomProperties.getProperty("version");
350       } catch (Exception e) {
351          version = "RUNNING.IN.IDE.FULL";
352       }
353       return version;
354    }
355 
356    /**
357     * Listens for application shutdown and cleans up any resources and properly
358     * compacts the HSQLDB to prevent restart problems with a .lck file being
359     * leftover. Also is a good citizen by properly cleaning up Windows DLL's
360     * used.
361     */
362    private static void shutdownHook() {
363       // Set shutdown hook for HSQLDB and DLLS
364       Runtime.getRuntime().addShutdownHook(new Thread() {
365          public void run() {
366             LOG.info("Shutdown hook.");
367             if (JIntellitype.isJIntellitypeSupported()) {
368                LOG.info("Cleaning up JIntellitype");
369                JIntellitype.getInstance().cleanUp();
370             }
371 
372             // shut down database and hibernate
373             try {
374                HibernateUtil.shutdown();
375             } catch (RuntimeException ex) {
376           	   final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
377         	   final String errorMessage = ResourceUtils.getString("messages.ErrorShuttingDown");
378                LOG.error(errorMessage, ex);
379                MessageUtil.showError(mainFrame, errorMessage); //AZ 
380             }
381          }
382       });
383    }
384 
385    /**
386     * If the Java version installed is not AT LEAST the correct version then
387     * throw error and terminate. Using common-lang SystemUtils.
388     */
389    private static void verifyJREVersion() {
390       final float requiredVersion = 1.60f;
391 
392       LOG.info("Checking Java version...");
393       LOG.info("Current Java version: " + SystemUtils.JAVA_VERSION);
394 
395       if (!SystemUtils.isJavaVersionAtLeast(requiredVersion)) {
396          LOG.info("Java version is not sufficient to run this application!");
397          LOG.info("Current Java version: " + SystemUtils.JAVA_VERSION);
398          LOG.info("REQUIRED Java version: " + requiredVersion);
399 
400          final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
401          final String errorMsg = ResourceUtils.getString("application.java.version.error.text");
402          final String displayMsg = MessageFormat.format(errorMsg, new Object[] { SystemUtils.JAVA_VERSION,
403                   Float.toString(requiredVersion) });
404          LOG.error(displayMsg);
405          MessageUtil.showError(mainFrame, displayMsg); //AZ 
406          System.exit(1);
407       }
408    }
409 
410    /**
411     * According to Sun Bug 6435126 found here:
412     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6435126 On some windows
413     * machines the clocks speeds up like crazy while using the Jukes and
414     * multiple timers.
415     */
416    private static void fixWindowsTimingBug() {
417       LOG.info("Fixing Windows Timing Bug...");
418       new Thread() {
419          {
420             this.setDaemon(true);
421             this.start();
422          }
423 
424          public void run() {
425             while (true) {
426                try {
427                   Thread.sleep(Integer.MAX_VALUE);
428                } catch (InterruptedException ex) {
429                }
430             }
431          }
432       };
433    }
434 
435    /**
436     * Initalize HSQLDB and Hibernate. If any problems exit the application.
437     */
438    @SuppressWarnings("deprecation")
439    private void initializeDatabase() {
440       LOG.info("Initializing Database.");
441       final String remoteURL = MainModule.SETTINGS.getRemoteDatabaseURL();
442 
443       try {
444          String dbLocation = MainModule.SETTINGS.getDatabaseLocation().getAbsolutePath();
445          dbLocation = dbLocation + SystemUtils.FILE_SEPARATOR + Resources.APPLICATION_LOCATION;
446          // start database
447          Database.startup(dbLocation, Resources.APPLICATION_LOCATION);
448 
449          // if there is a remote URL then we need to set it in the Hibernate
450          // config
451          if ((StringUtils.isNotBlank(remoteURL)) && (!Settings.DEFAULT_REMOTE_DATABASE_URL.equals(remoteURL))) {
452             HibernateUtil.setRemoteUrl(remoteURL);
453          }
454          // initialize Hibernate
455          HibernateUtil.initialize();
456 
457          // set the write delay on the HSQL database so writes are immediate
458          if (HibernateUtil.isHSQLDialect()) {
459             // initializes Hibernate
460             HibernateUtil.getSession();
461             Database.setWriteDelay(HibernateUtil.getSession().connection(), "FALSE");
462          }
463       } catch (JDBCConnectionException ex) {
464     	  final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
465     	  final String errorMessage = remoteURL + " " + ResourceUtils.getString("messages.ErrorConnection"); 
466           MessageUtil.showError(mainFrame, errorMessage); //AZ 
467           LOG.error(errorMessage);
468          System.exit(1);
469       } catch (InfrastructureException ex) {
470     	 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
471          LOG.error("InfrastructureException", ex);
472          MessageUtil.showError(mainFrame, "InfrastructureException"); //AZ 
473          System.exit(1);
474       } catch (HibernateException ex) {
475     	 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
476          LOG.error("HibernateException", ex);
477          MessageUtil.showError(mainFrame, "HibernateException"); //AZ 
478          System.exit(1);
479       } catch (Exception ex) {
480     	 final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
481          LOG.error("Exception", ex);
482          MessageUtil.showError(mainFrame, "Exception"); //AZ 
483          System.exit(1);
484       }
485    }
486 
487    /**
488     * Initalize java.util.logging for any libraries used in the application that
489     * do not use commons-logging.
490     */
491    private void initializeLogging() {
492       try {
493          super.configureLogging();
494 
495          // configure java.util.logging
496          LogManager.getLogManager().readConfiguration(ClassLoader.getSystemResourceAsStream("java.logging.properties"));
497 
498          // set log4j level from prefs
499          Logger.getRootLogger().setLevel(Level.toLevel(MainModule.SETTINGS.getLogLevel()));
500          Logger.getLogger("com.melloware").setLevel(Level.toLevel(MainModule.SETTINGS.getLogLevel()));
501          Logger.getLogger("com.melloware.jukes.gui").setLevel(Level.toLevel(MainModule.SETTINGS.getLogLevel()));
502       } catch (Exception ex) {
503          LOG.warn("Error configuring logging.", ex);
504       }
505    }
506 
507    /**
508     * Initializes the JIntellitype library if on Windows. Else do nothing.
509     * <p>
510     * @param mainFrame the MainFrame of the application to store the
511     *           JIntellitype ref
512     */
513    private void intializeJIntellitype(MainFrame mainFrame) {
514       if (JIntellitype.isJIntellitypeSupported()) {
515          try {
516             mainFrame.setJintellitype(JIntellitype.getInstance());
517          } catch (JIntellitypeException ex) {
518             LOG.error("JIntellitypeException", ex);
519             MessageUtil.showError(mainFrame, "JIntellitypeException"); //AZ 
520          }
521       }
522    }
523 
524    /**
525     * Initializes the tray icon if on Windows. Else do nothing.
526     * <p>
527     * @param mainFrame the MainFrame of the application to store the TrayIcon
528     *           ref
529     */
530    private void intializeTrayIcon(MainFrame mainFrame) {
531       if (SystemTray.isSupported()) {
532          try {
533             final ITrayIcon trayIcon = new JukesTrayIcon(mainFrame);
534             mainFrame.setTrayIcon(trayIcon);
535          } catch (AWTException ex) {
536             LOG.error("AWTException", ex);
537             MessageUtil.showError(mainFrame, "AWTException"); //AZ 
538          }
539       }
540    }
541 
542 }