Main.java
001 /*
002  *  Main.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 1/Nov/00
013  *
014  *  $Id: Main.java 17652 2014-03-13 18:47:31Z markagreenwood $
015  */
016 
017 package gate;
018 
019 import gate.gui.MainFrame;
020 import gate.gui.OptionsDialog;
021 import gate.util.BomStrippingInputStreamReader;
022 import gate.util.Err;
023 import gate.util.Files;
024 import gate.util.GateException;
025 import gate.util.OptionsMap;
026 import gate.util.Out;
027 import gate.util.Strings;
028 import gate.util.ThreadWarningSystem;
029 import gate.util.persistence.PersistenceManager;
030 import gnu.getopt.Getopt;
031 
032 import java.awt.Dimension;
033 import java.awt.Font;
034 import java.awt.GraphicsConfiguration;
035 import java.awt.GraphicsEnvironment;
036 import java.awt.Rectangle;
037 import java.awt.Toolkit;
038 import java.io.BufferedReader;
039 import java.io.File;
040 import java.io.IOException;
041 import java.io.InputStream;
042 import java.io.PrintStream;
043 import java.lang.management.ThreadInfo;
044 import java.net.MalformedURLException;
045 import java.net.URL;
046 import java.util.ArrayList;
047 import java.util.Iterator;
048 import java.util.List;
049 
050 import javax.swing.JOptionPane;
051 import javax.swing.SwingUtilities;
052 import javax.swing.UIManager;
053 
054 import org.apache.commons.io.IOUtils;
055 import org.apache.log4j.Logger;
056 
057 /** Top-level entry point for the GATE command-line and GUI interfaces.
058   <P>
059   */
060 public class Main {
061 
062   /** Debug flag */
063   protected static final boolean DEBUG = false;
064 
065   /** Status flag for normal exit. */
066   protected static final int STATUS_NORMAL = 0;
067 
068   /** Status flag for error exit. */
069   protected static final int STATUS_ERROR = 1;
070 
071   /** Main routine for GATE.
072     * Command-line arguments:
073     <UL>
074     <LI>
075     <B>-h</B> display a short help message
076     <LI>
077     <B>-d URL</B> define URL to be a location for CREOLE resoures
078     <LI>
079     <B>-i file</B> additional initialisation file (probably called
080     *   <TT>gate.xml</TT>). Used for site-wide initialisation by the
081     *   start-up scripts
082     </UL>
083     */
084   public static void main(String[] argsthrows GateException {
085     
086     // check we have a useable JDK
087     if(
088       System.getProperty("java.version").compareTo(Gate.getMinJdkVersion())
089       0
090     ) {
091       throw new GateException(
092         "GATE requires JDK " + Gate.getMinJdkVersion() " or newer"
093       );
094     }
095     
096     ThreadWarningSystem tws = new ThreadWarningSystem();
097     tws.addListener(new ThreadWarningSystem.Listener() {
098       
099       final PrintStream out = System.out;
100       
101       @Override
102       public void deadlockDetected(ThreadInfo inf) {
103         out.println("Deadlocked Thread:");
104         out.println("------------------");
105         out.println(inf);
106         for (StackTraceElement ste : inf.getStackTrace()) {
107           out.println("\t" + ste);
108         }
109       }
110       @Override
111       public void thresholdExceeded(ThreadInfo[] threads) { }
112     });
113 
114     // process command-line options
115     processArgs(args);
116 
117     // GATE builtins should be loaded from the jar (or classes dir), not
118     // from a web server (we load them over the web during testing to
119     // make sure that users can load their own that way)
120     Gate.setNetConnected(false);
121     Gate.setLocalWebServer(false);
122 
123     runGui();
124   // main
125 
126   /** Register any CREOLE URLs that we got on the command line */
127   protected static void registerCreoleUrls() {
128     CreoleRegister reg = Gate.getCreoleRegister();
129     Iterator<URL> iter = pendingCreoleUrls.iterator();
130     while(iter.hasNext()) {
131       URL u = iter.next();
132       try {
133         reg.registerDirectories(u);
134       catch(GateException e) {
135         Err.prln("Couldn't register CREOLE directory: " + u);
136         Err.prln(e);
137         System.exit(STATUS_ERROR);
138       }
139     }
140   // registerCreoleUrls()
141 
142   /** Main Frame of the GUI; null when no GUI running */
143   protected static MainFrame frame;
144 
145   /**
146    * Get the main frame of the GUI. If the GUI isn't running, it
147    * is started.
148    */
149   public static MainFrame getMainFrame() throws GateException {
150     if(frame == null)
151       runGui();
152     return frame;
153   // getMainFrame()
154 
155   /** Run the user interface. */
156   protected static void runGui() throws GateException {
157 
158     Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
159 
160     // initialise the library and load user CREOLE directories
161     try{
162       Gate.init();
163     }catch(Throwable t){
164       log.error("Problem while initialising GATE", t);
165       int selection = JOptionPane.showOptionDialog(
166         null,
167         "Error during initialisation:\n" + t.toString() +
168         "\nDo you still want to start GATE?",
169         "GATE", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE,
170         null, new String[]{"Cancel""Start anyway"},
171         "Cancel");
172       if(selection != 1){
173         System.exit(1);
174       }
175     }
176 
177     //create the main frame, show it
178     SwingUtilities.invokeLater(new Runnable(){
179       @Override
180       public void run(){
181         GraphicsConfiguration gc = GraphicsEnvironment.
182         getLocalGraphicsEnvironment().getDefaultScreenDevice().
183         getDefaultConfiguration();
184 
185         //this needs to run before any GUI component is constructed.
186         applyUserPreferences();
187 
188         //all the defaults tables have been updated; build the GUI
189         frame = MainFrame.getInstance(gc);
190         if(DEBUGOut.prln("constructing GUI");
191         
192         // run the GUI
193         frame.setTitleChangable(true);
194         frame.setTitle(name + " " + version + " build " + build);
195         
196         // Set title from Java properties
197         String title =
198           System.getProperty(GateConstants.TITLE_JAVA_PROPERTY_NAME);
199         if(title != null) {
200           frame.setTitle(title);
201         // if
202         frame.setTitleChangable(false);
203 
204         // Set icon from Java properties
205         // iconName could be absolute or "gate:/img/..."
206         String iconName =
207           System.getProperty(GateConstants.APP_ICON_JAVA_PROPERTY_NAME);
208         if(iconName != null) {
209           try {
210             frame.setIconImage(Toolkit.getDefaultToolkit().getImage(
211                   new URL(iconName)));
212           catch(MalformedURLException mue){
213             log.warn("Could not load application icon.", mue);
214           }
215         // if
216 
217         // Validate frames that have preset sizes
218         frame.validate();
219 
220         // Center the window
221         Rectangle screenBounds = gc.getBounds();
222         Dimension screenSize = screenBounds.getSize();
223         Dimension frameSize = frame.getSize();
224         if (frameSize.height > screenSize.height) {
225           frameSize.height = screenSize.height;
226         }
227         if (frameSize.width > screenSize.width) {
228           frameSize.width = screenSize.width;
229         }
230         frame.setLocation((screenSize.width - frameSize.width2,
231                           (screenSize.height - frameSize.height2);
232 
233         frame.setVisible(true);
234 
235         //load session if required and available;
236         //do everything from a new thread.
237         Runnable runnable = new Runnable(){
238           @Override
239           public void run(){
240             try{
241               File sessionFile = Gate.getUserSessionFile();
242               if(sessionFile.exists()){
243                 MainFrame.lockGUI("Loading saved session...");
244                 PersistenceManager.loadObjectFromFile(sessionFile);
245               }
246             }catch(Exception e){
247               log.warn("Failed to load session data", e);
248             }finally{
249               MainFrame.unlockGUI();
250             }
251           }
252         };
253         Thread thread = new Thread(Thread.currentThread().getThreadGroup(),
254                                    runnable, "Session loader");
255         thread.setPriority(Thread.MIN_PRIORITY);
256         thread.start();
257       }
258     });
259     registerCreoleUrls();
260   // runGui()
261 
262   /**
263    * Reads the user config data and applies the required settings.
264    * This must be called <b>after</b> {@link Gate#init()} but <b>before</b>
265    * any GUI components are created.
266    */
267   public static void applyUserPreferences(){
268     //look and feel
269     String lnfClassName;
270     if(System.getProperty("swing.defaultlaf"!= null) {
271       lnfClassName = System.getProperty("swing.defaultlaf");
272     else {
273       lnfClassName = Gate.getUserConfig().
274                             getString(GateConstants.LOOK_AND_FEEL);
275     }
276     if(lnfClassName == null){
277       //if running on Linux, default to Metal rather than GTK because GTK LnF
278       //doesn't play nicely with most Gnome themes
279       if(System.getProperty("os.name").toLowerCase().indexOf("linux"!= -1){
280         //running on Linux
281         lnfClassName = UIManager.getCrossPlatformLookAndFeelClassName();
282       }else{
283         lnfClassName = UIManager.getSystemLookAndFeelClassName();
284       }      
285     }
286     try {
287       UIManager.setLookAndFeel(lnfClassName);
288     catch(Exception e) {
289       System.err.print("Could not set your preferred Look and Feel. The error was:\n" +
290               e.toString() "\nReverting to using Java Look and Feel");
291       try {
292         UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
293       }catch(Exception e1) {
294         //we just can't catch a break here. Let's forget about look and feel.
295         System.err.print(
296                 "Could not set the cross-platform Look and Feel either. The error was:\n" +
297                 e1.toString() "\nGiving up on Look and Feel.");
298       }
299     }
300     Gate.getUserConfig().put(GateConstants.LOOK_AND_FEEL, lnfClassName);
301     
302     //read the user config data
303     OptionsMap userConfig = Gate.getUserConfig();
304 
305     //text font
306     Font font = userConfig.getFont(GateConstants.TEXT_COMPONENTS_FONT);
307     if(font == null){
308       font = UIManager.getFont("TextPane.font");
309     }
310 
311     if(font != null){
312       OptionsDialog.setTextComponentsFont(font);
313     }
314 
315     //menus font
316     font = userConfig.getFont(GateConstants.MENUS_FONT);
317     if(font == null){
318       font = UIManager.getFont("Menu.font");
319     }
320 
321     if(font != null){
322       OptionsDialog.setMenuComponentsFont(font);
323     }
324 
325     //other gui font
326     font = userConfig.getFont(GateConstants.OTHER_COMPONENTS_FONT);
327     if(font == null){
328       font = UIManager.getFont("Button.font");
329     }
330 
331     if(font != null){
332       OptionsDialog.setComponentsFont(font);
333     }
334 
335   }
336 
337 
338 
339   // find out the version and build numbers
340   static {
341     
342     // we can't have the possibility of assigning to a final variable twice so
343     // we put the details into a temp variable and then once we are happy assign
344     // it to the static final variable
345     String temp;
346     
347     BufferedReader reader = null;
348     
349     // find out the version number    
350     try {
351       InputStream ver = Files.getGateResourceAsStream("version.txt");
352       if (ver==null) {
353         throw new IOException();
354       }
355       reader = new BomStrippingInputStreamReader(ver, "UTF-8");
356       temp = reader.readLine();
357     catch(IOException ioe) {
358       temp = "8.0";
359     finally {
360       IOUtils.closeQuietly(reader);
361     }
362     
363     version = temp;
364 
365     // find out the build number
366     try{
367       InputStream build = Files.getGateResourceAsStream("build.txt");
368       if (build==null) {
369         throw new IOException();
370       }
371       reader = new BomStrippingInputStreamReader(build, "UTF-8");
372       temp = reader.readLine();
373     catch(IOException ioe) {
374       temp = "0000";
375     finally {
376       IOUtils.closeQuietly(reader);
377     }
378     
379     build = temp;
380   // static initializer finding build and version
381 
382   public static final String name = "GATE Developer";
383   public static final String version;
384   public static final String build;
385   
386   private static final Logger log = Logger.getLogger(Main.class);
387 
388   /** Process arguments and set up member fields appropriately.
389     * Will shut down the process (via System.exit) if there are
390     * incorrect arguments, or if the arguments ask for something
391     * simple like printing the help message.
392     */
393   public static void processArgs(String[] args) {
394 
395     Getopt g = new Getopt("GATE main", args, "hd:ei:");
396     int c;
397     while( (c = g.getopt()) != -)
398       switch(c) {
399         // -h
400         case 'h':
401           help();
402           usage();
403           System.exit(STATUS_NORMAL);
404           break;
405         // -d creole-dir
406         case 'd':
407           String urlString = g.getOptarg();
408           URL u = null;
409           try {
410             u = new URL(urlString);
411           catch(MalformedURLException e) {
412             Err.prln("Bad URL: " + urlString);
413             Err.prln(e);
414             System.exit(STATUS_ERROR);
415           }
416           pendingCreoleUrls.add(u);
417           Out.prln(
418             "CREOLE Directory " + urlString + " queued for registration"
419           );
420           break;
421         // -i gate.xml site-wide init file
422         case 'i':
423           String optionString = g.getOptarg();
424           @SuppressWarnings("unused")
425           URL u2 = null;
426           File f = new File(optionString);
427           try {
428             u2 = f.toURI().toURL();
429           catch(MalformedURLException e) {
430             Err.prln("Bad initialisation file: " + optionString);
431             Err.prln(e);
432             System.exit(STATUS_ERROR);
433           }
434           Gate.setSiteConfigFile(f);
435           if(DEBUG)
436             Out.prln(
437               "Initialisation file " + optionString +
438               " recorded for initialisation"
439             );
440           break;
441         case '?':
442           // leave the warning to getopt
443           System.exit(STATUS_ERROR);
444           break;
445 
446         default:
447           // shouldn't happen!
448           Err.prln("getopt() returned " + c + "\n");
449           System.exit(STATUS_ERROR);
450           break;
451       // getopt switch
452 
453   // processArgs()
454 
455   /** Display a usage message */
456   public static void usage() {
457     Out.prln(
458       "Usage: java gate.Main\n" +
459       "-h -- show this help file\n" +
460       "-d CREOLE-URL -- a creole directory to load\n" +
461       "-i SITE_CONFIG_FILE -- the site config file to use"
462     );
463   // usage()
464 
465   /** Display a help message */
466   public static void help() {
467     String nl = Strings.getNl();
468     Out.prln(
469       "For help on command-line options and other information " + nl +
470       "see the user manual in your GATE distribution or at " + nl +
471       "http://gate.ac.uk/userguide/"
472     );
473   // help()
474 
475   /** The list of pending URLs to add to the CREOLE register */
476   protected static final List<URL> pendingCreoleUrls = new ArrayList<URL>();
477 
478 // class Main