View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  
18  /*
19   * Project: KernelConfigurator
20   * Created on 23-feb-2004
21   *
22   * Copyright (c)2003 Grid Systems
23   */
24  package com.gridsystems.config.app;
25  
26  import java.io.BufferedReader;
27  import java.io.IOException;
28  import java.io.InputStreamReader;
29  import java.lang.reflect.Method;
30  import java.net.URL;
31  import java.text.MessageFormat;
32  import java.util.Collection;
33  import java.util.Enumeration;
34  import java.util.HashMap;
35  import java.util.Locale;
36  import java.util.Map;
37  import java.util.ResourceBundle;
38  import java.util.StringTokenizer;
39  
40  import javax.swing.UIManager;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  
45  import com.gridsystems.config.Configurator;
46  import com.gridsystems.config.modules.jvm.JVMConfigurator;
47  import com.gridsystems.config.tools.console.ConsoleTools;
48  import com.gridsystems.utils.AWTUtils;
49  
50  /**
51   * Root class and provider for user interface delegation. Each subclass is
52   * specialized in a specific type of user interface environment.
53   *
54   * @author Rodrigo Ruiz Aguayo
55   * @version 1.0
56   */
57  public abstract class UI {
58  
59    /**
60     * Help text to show in the console.
61     */
62    private static final String[] HELP_TEXT = {
63      "Valid options:",
64      "-swing      Executes in GUI mode",
65      "-console    Executes in text mode",
66      "-silent     Executes with no user interface",
67      "-locale:xx  Sets the locale to xx (en, es_ES, ...). ",
68      "            Defaults to the user environment settings",
69      "-username:xx The name of the user that will execute the configured server",
70      "",
71      "Text mode flags:",
72      "-ansi       If the terminal supports ANSI escape sequences",
73      "-noacs      If line characters are not correctly displayed",
74      "",
75      "Swing mode flags:",
76      "-laf:x      Sets the look and feel to use",
77      "            Valid values are: system, cross, metal, gtk, motif and windows",
78      "",
79      "Other flags:",
80      "-expert     Turns on the advanced configuration mode. Ignored in silent mode"
81    };
82  
83    /**
84     * For this class logs.
85     */
86    private static Log log = LogFactory.getLog(UI.class);
87  
88    /**
89     * The application resource bundle.
90     */
91    private static ResourceBundle bundle = ResourceBundle.getBundle("config");
92  
93    /**
94     * Specifies the Null UI.
95     */
96    public static final int NONE = 0;
97  
98    /**
99     * Specifies the Swing UI.
100    */
101   public static final int SWING = 1;
102 
103   /**
104    * Specified the Console UI.
105    */
106   public static final int CONSOLE = 2;
107 
108   /**
109    * Class names for each declared constant.
110    */
111   private static final String[] CLASS_NAMES = {
112     "com.gridsystems.config.app.NullUI",
113     "com.gridsystems.config.app.SwingUI",
114     "com.gridsystems.config.app.ConsoleUI"
115   };
116 
117   /**
118    * Flag that controls the "Expert" mode.
119    */
120   private static boolean expertMode = false;
121 
122   /**
123    * The list of discovered configurators.
124    */
125   protected static Configurator[] configurators = null;
126 
127   /**
128    * Default constructor.
129    */
130   public UI() { }
131 
132   /**
133    * UI specific execution.
134    * @return boolean
135    */
136   public abstract boolean execute();
137 
138   /**
139    * Gets the message corresponding to the specified key in the "base" bundle.
140    * <p>
141    * This method is here to give easy access to the base bundle without knowledge
142    * of the bundle name or location.
143    *
144    * @param key The key of the message to retrieve
145    * @return    The message
146    */
147   public static String getString(String key) {
148     return bundle.getString(key);
149   }
150 
151   /**
152    * Gets if the configurator is running in "expert mode".
153    *
154    * @return  <code>true</code> if the configurator is in expert mode
155    */
156   public static boolean isExpertMode() {
157     return expertMode;
158   }
159 
160   /**
161    * Builds the list of Configutator implementations in the classpath
162    * through inspection of "configurator.txt" resources found via ClassLoader.
163    * <p>
164    * Each "configurator.txt" resource is expected to contain a list of
165    * Configurator classes.
166    *
167    * @return An array of Configurator instances
168    */
169   public static Configurator[] getConfigurators() {
170     if (configurators == null) {
171       JVMConfigurator jvmc = JVMConfigurator.getInstance();
172 
173       HashMap<String, Configurator> map = new HashMap<String, Configurator>();
174       ClassLoader cl = Thread.currentThread().getContextClassLoader();
175       try {
176         // Special configurator. No need to register in configurators.txt
177         register(map, jvmc);
178 
179         // Preloads for third party configurators access
180         jvmc.getModel().load();
181 
182         Enumeration enumeration = cl.getResources("configurators.txt");
183         while (enumeration.hasMoreElements()) {
184           URL url = (URL)enumeration.nextElement();
185 
186           BufferedReader reader = null;
187           try {
188             reader = new BufferedReader(new InputStreamReader(url.openStream()));
189             String line = reader.readLine();
190             while (line != null) {
191               line = line.trim();
192               if (line.charAt(0) != '#') {
193                 Configurator config = newConfigurator(line);
194                 if (config != null) {
195                   config.getModel().load();
196                   register(map, config);
197                 }
198               }
199               line = reader.readLine();
200             }
201           } finally {
202             ReflectionTools.close(reader);
203           }
204         }
205       } catch (IOException e) { }
206 
207       Collection<Configurator> col = map.values();
208       Configurator[] configs = new Configurator[col.size()];
209       col.toArray(configs);
210 
211       configurators = configs;
212     }
213     return configurators;
214   }
215 
216   /**
217    * Shows the help message in the console.
218    */
219   private static void showHelp() {
220     for (int i = 0; i < HELP_TEXT.length; i++) {
221       System.out.println(HELP_TEXT[i]);
222     }
223   }
224   /**
225    * Checks the specified mode is "usable", and initializes it.
226    *
227    * @param mode The desired UI mode
228    * @param ansi Flag for ANSI mode
229    * @param acs  Flaf for Alternative Character Set mode
230    * @return The "adjusted" UI mode
231    */
232   private static int prepareMode(int mode, boolean ansi, boolean acs) {
233     // Switch to CONSOLE if display does not exist
234     if (mode == Configurator.MODE_SWING && !AWTUtils.existDisplay()) {
235       mode = Configurator.MODE_CONSOLE;
236     }
237 
238     if (mode == Configurator.MODE_CONSOLE) {
239       ConsoleTools.setup(ansi, acs);
240     }
241 
242     return mode;
243   }
244 
245   /**
246    * Sets the locale to the one specified in the parameter string.
247    *
248    * @param s The string containing the command-line argument with the locale
249    */
250   private static void parseLocale(String s) {
251     String locale = s.substring("-locale:".length());
252     setDefaultLocale(locale);
253   }
254 
255   /**
256    * Performs the execution of the appropriate UI implementation, according to
257    * the passed command-line arguments.
258    *
259    * @param args The command-line arguments
260    * @throws Exception In case of an unhandled error
261    */
262   public static void main(String[] args) throws Exception {
263     int mode = Configurator.MODE_SILENT;
264     boolean ansi = false;
265     boolean acs = true;
266 
267     for (int i = 0; i < args.length; i++) {
268       if ("-help".equals(args[i])) {
269         showHelp();
270         System.exit(0);
271       } else if ("-expert".equals(args[i])) {
272         expertMode = true;
273       } else if ("-swing".equals(args[i])) {
274         mode = Configurator.MODE_SWING;
275       } else if ("-console".equals(args[i])) {
276         mode = Configurator.MODE_CONSOLE;
277       } else if ("-silent".equals(args[i])) {
278         mode = Configurator.MODE_SILENT;
279       } else if ("-ansi".equals(args[i])) {
280         ansi = true;
281       } else if ("-noacs".equals(args[i])) {
282         acs = false;
283       } else if (args[i].startsWith("-locale:")) {
284         // Explicit language selection
285         parseLocale(args[i]);
286       } else if (args[i].startsWith("-laf:")) {
287         parseLaf(args[i].substring("-laf:".length()));
288       } else if (args[i].startsWith("-username:")) {
289         Configurator.setUsername(args[i].substring("-username:".length()));
290       } else {
291         log.warn("Invalid " + args[i] + " option. Ignored");
292       }
293     }
294 
295     mode = prepareMode(mode, ansi, acs);
296 
297     boolean quitConfigurator = false;
298     do {
299       boolean correctExecution = execute(mode);
300       quitConfigurator = quitConfigurator(correctExecution);
301     } while (!quitConfigurator);
302   }
303 
304   /**
305    * @param correctExecution execution has finished correctly
306    * @return <code>true</code> if configurator has to be reopened
307    */
308   private static boolean quitConfigurator(boolean correctExecution) {
309     if (correctExecution) {
310       return true;
311     }
312     //ask the user to reopen the configurator
313     String line;
314 
315     do {
316       try {
317         line =
318           ConsoleTools.readLine(UI.getString("console.readLine.applyErrors"));
319       } catch (IOException e) {
320         log.error("Error while reading information");
321         e.printStackTrace();
322         return false;
323       }
324     } while (wrongValue(line));
325 
326     if (line.equals("y") || line.equals("Y")) {
327       return true;
328     }
329 
330     return false;
331 
332   }
333 
334   /**
335    * @param line characters to validate
336    * @return validation
337    */
338   private static boolean wrongValue(String line) {
339     return line.length() != 1
340             || (!line.equals("y") && !line.equals("n")
341                 && !line.equals("Y") && !line.equals("N"));
342   }
343 
344   /**
345    * Executes the appropriate UI implementation.
346    *
347    * @param mode The selected execution mode
348    * @return returns boolean
349    * @throws Exception If an error occurs
350    */
351   private static boolean execute(int mode) throws Exception {
352     if (mode >= 0 && mode <= 2) {
353       try {
354         Configurator.setViewMode(mode);
355         Class c = Class.forName(CLASS_NAMES[mode]);
356         if (c != null) {
357           UI ui = (UI)c.newInstance();
358           UI.getConfigurators();
359           return ui.execute();
360         }
361       } catch (Exception e) {
362         e.printStackTrace();
363         System.exit(1);
364         return false;
365       }
366     } else {
367       throw new Exception("No UI implemented for this options");
368     }
369     return false;
370   }
371 
372   /**
373    * Parses the specified LookAndFeel name and returns the appropriate class name.
374    *
375    * @param laf The LAF name
376    * @return The LAF class name
377    */
378   private static String parseLaf(String laf) {
379     if (laf.equalsIgnoreCase("system")) {
380       laf = UIManager.getSystemLookAndFeelClassName();
381     } else if (laf.equalsIgnoreCase("cross")) {
382       laf = UIManager.getCrossPlatformLookAndFeelClassName();
383     } else if (laf.equalsIgnoreCase("windows")) {
384       laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
385     } else if (laf.equalsIgnoreCase("gtk")) {
386       laf = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
387     } else if (laf.equalsIgnoreCase("metal")) {
388       laf = "javax.swing.plaf.metal.MetalLookAndFeel";
389     } else if (laf.equalsIgnoreCase("motif")) {
390       laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
391     }
392 
393     try {
394       // Checks the specified class exists
395       Class.forName(laf);
396       System.setProperty("swing.defaultlaf", laf);
397     } catch (Exception e) {
398       System.err.println("WARNING: Invalid LAF specified");
399     }
400 
401     return laf;
402   }
403 
404   /**
405    * Adds the configurator to the specified map, using its path as the map key.
406    *
407    * @param map The map where c is to be put
408    * @param c   The configurator instance
409    */
410   private static void register(Map<String, Configurator> map, Configurator c) {
411     if (map.containsKey(c.getPath())) {
412       (new InternalError("Configurator overriden!!!")).printStackTrace();
413       System.exit(1);
414     }
415     map.put(c.getPath(), c);
416   }
417 
418   /**
419    * Creates a new configurator instance through reflection.
420    * <p>
421    * This method first attempts to obtain an instance by calling to a
422    * static method called getInstance(). If no such a method is found, it
423    * will try to create a new instance through the default constructor.
424    *
425    * @param className The name of the class to invoke
426    * @return The class instance, or null if instantiation was not successful
427    */
428   private static Configurator newConfigurator(String className) {
429     try {
430       Class c = Class.forName(className);
431       if (Configurator.class.isAssignableFrom(c)) {
432         try {
433           Method m = c.getMethod("getInstance");
434           return (Configurator)m.invoke(null);
435         } catch (Exception e) {
436           return (Configurator)c.newInstance();
437         }
438       }
439     } catch (Exception e) {
440       String pattern = UI.getString("messages.classload");
441       String msg = MessageFormat.format(pattern, new Object[] { className });
442       log.warn(msg, e);
443     }
444     return null;
445   }
446 
447   /**
448    * Parses the specified string and sets the JVM default Locale from it.
449    * <p>
450    * The specified locale should be in the form:
451    * <p>
452    * <code>language[_country[_variant]]</code>
453    *
454    * @param slocale A locale string representation
455    */
456   private static void setDefaultLocale(String slocale) {
457     StringTokenizer st = new StringTokenizer(slocale, "_");
458     Locale locale = null;
459     if (st.hasMoreTokens()) {
460       String lang = st.nextToken().trim().toLowerCase();
461       if (st.hasMoreTokens()) {
462         String country = st.nextToken().trim().toUpperCase();
463         if (st.hasMoreTokens()) {
464           String variant = st.nextToken().trim();
465           locale = new Locale(lang, country, variant);
466         } else {
467           locale = new Locale(lang, country);
468         }
469       } else {
470         locale = new Locale(lang);
471       }
472     }
473 
474     if (locale != null) {
475       Locale.setDefault(locale);
476       bundle = ResourceBundle.getBundle("config", locale);
477     }
478   }
479 }