View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap;
21  
22  import gnu.getopt.Getopt;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStreamReader;
27  import java.text.MessageFormat;
28  import java.util.Locale;
29  import java.util.MissingResourceException;
30  import java.util.ResourceBundle;
31  
32  import org.apache.log4j.Level;
33  import org.xnap.cmdl.CommandLine;
34  import org.xnap.gui.ErrorHandler;
35  import org.xnap.gui.SplashWindow;
36  import org.xnap.gui.XNapFrame;
37  import org.xnap.gui.theme.ThemeManager;
38  import org.xnap.loader.XNapClassLoader;
39  import org.xnap.loader.XNapLoader;
40  import org.xnap.util.Debug;
41  import org.xnap.util.FileHelper;
42  import org.xnap.util.Preferences;
43  import org.xnap.util.SystemHelper;
44  import org.xnap.util.UncaughtExceptionListener;
45  import org.xnap.util.UncaughtExceptionManager;
46  import org.xnap.util.Updater;
47  import org.xnap.util.VersionParser;
48  import org.xnap.util.XNapThreadGroup;
49  
50  /***
51   * Starts the main application.
52   */
53  public class XNap {
54  
55      //--- Constant(s) ---
56  
57      public static final String FILE_REVISION = "$Revision: 1.20 $";
58  
59      /***
60       * The minimal JDK version required to run the commandline version
61       */
62      public static final String REQUIRED_CMDL_JDK = "1.2.0";
63  
64      /***
65       * The minimal JDK version required to run the gui version
66       */
67      public static final String REQUIRED_GUI_JDK = "1.3.0";
68  
69      /***
70       * This list contains all languages the XNap interface has been
71       * translated into. Add new languages here (see TRANSLATION file).
72       */
73      public static final Locale[] LANGUAGES = {
74  		Locale.CHINESE, Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN, 
75  		Locale.ITALIAN, Locale.JAPANESE, new Locale("nl", "", ""), 
76  		new Locale("es", "", ""), new Locale("ru", "", ""),
77      };
78  
79      /***
80       * This list contains all public keys automatically accepted by xnap.
81       */
82      public static final String[] TRUSTED_KEYS = {
83  		// first xnap key (xnap-3.0-beta4)
84  		"MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4"
85  		+"C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7u"
86  		+"p1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUA"
87  		+"l2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMC"
88  		+"z0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpn"
89  		+"WRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYU"
90  		+"AAoGBAO2rM0tzQs52zUd0yXq7JbPd9lclsE00b9H5s4dDRtkGzzqViywV8qxCbkjJc4Kq"
91  		+"O249NBY5Je0nlLnMRxqTepHgeXF2WEDQmIsz5r7ZoVfUhZ90XTIHnE4IwdktX02Pbj/H6"
92  		+"a1aOugUBaNnkBIClv239djzy1FekT+N0gGsN5w4"
93      };
94  
95  	public static final long START_TIME = System.currentTimeMillis();
96  	
97      //--- Data field(s) ---
98  
99  	public static boolean clearPackageInfos = false;
100 
101     /*** Reference to the current localization bundle. */
102     private static ResourceBundle resources;
103 
104 	/*** The global thread group */
105 	private static XNapThreadGroup tg;
106 	private static UncaughtExceptionListener exceptionHandler;
107 
108 	/*** The version that was last launched.  */
109     public static String lastLaunchVersion;
110 
111 	/***
112 	 * True if xnap is run from cvs. Enables a few development quirks.
113 	 */
114 	private static boolean isRunFromCvs = false;
115 
116 	private static boolean firstTime = true;
117 
118     //--- Method(s) ---
119 
120 	/***
121 	 * Cleanly exits the applicaton.
122 	 */
123 	public static void exit()
124 	{
125 		if (XNapFrame.getInstance() != null) {
126 			XNapFrame.getInstance().stop();
127 		}
128 
129 		if (CommandLine.getInstance() != null) {
130 			CommandLine.getInstance().stop();
131 		}
132 
133 		Updater.stop();
134 		System.exit(0);
135 	}
136 
137     /***
138      * Brings up the Command Line (Cmdl) or Graphic User Interface (GUI).
139      *
140      * @param args parameters
141      */
142     public static void main(final String[] argv)
143     {
144 		tg = new XNapThreadGroup("XNapThreadGroup");
145 		System.setProperty("sun.awt.exception.handler", 
146 						   "xnap.util.XNapAWTExceptionHandler");
147 		Thread mainRunner = new Thread(tg, "XNapMain") {
148 			public void run() {
149 				setContextClassLoader(XNapClassLoader.getInstance());
150 				runMain(argv);
151 			}
152 		};
153 		
154 		mainRunner.start();		
155     }
156 
157 	private static void runMain(String[] argv) 
158 	{
159 		Level level = Level.OFF;
160 		boolean debug = false;
161 		boolean startCmdl = false;
162 		boolean showHelp  = false;
163 		boolean jdkCheck = true;
164 		
165 		Getopt g = new Getopt("xnap", argv, "cd::hnpvx");
166 		
167 		int c;
168 		while ((c = g.getopt()) != -1) {
169 			//System.out.println((char)c + " [" + g.getOptarg() + "]");
170 			switch (c) {
171 			case 'c':
172 				startCmdl = true;
173 				break;
174 			case 'd':
175 				debug = true;
176 				String arg = g.getOptarg();
177 				level = (arg != null) ? Level.toLevel(arg) : Level.ALL;
178 				break;
179 			case 'h':
180 				showHelp = true;
181 				break;
182 			case 'n':
183 				jdkCheck = false;
184 				break;
185 			case 'p':
186 				clearPackageInfos = true;
187 				break;
188 			case 'v':
189 				System.out.println(XNapLoader.VERSION);
190 				System.exit(0);
191 				break;
192 			case 'x':
193 				isRunFromCvs = true;
194 				break;
195 			case '?':
196 				System.exit(1);
197 				break;
198 			}
199 		}
200 		
201 		File debugPrefs = new File(FileHelper.getHomeDir() + "debug.prefs");
202 		Debug.init(debugPrefs, level);
203 		
204  		if (!debug) {
205  			File f = new File(FileHelper.getHomeDir() + "error.log");
206  			Debug.setErrorFile(f);
207  		}
208 		
209 		// here we go...
210 		SystemHelper.init();
211 		Preferences prefs = Preferences.getInstance();
212 		prefs.read();
213 		
214 		lastLaunchVersion = prefs.getLastLaunchVersion();
215 		if (!XNapLoader.VERSION.equals(lastLaunchVersion)) {
216 			// clear package information when launching a different version
217 			clearPackageInfos = true;
218 		}
219 		prefs.setLastLaunchVersion(XNapLoader.VERSION);
220 
221 		initLocale(prefs.getLanguage());
222 		
223 		if (debug) {
224 			try {
225 				InputStreamReader in = new InputStreamReader(System.in);
226 				System.err.println("Default encoding: " + in.getEncoding());
227 				in.close();
228 			}
229 			catch (IOException e) {
230 			}
231 		}
232 		
233 		if (showHelp) {
234 			showHelp();
235 			System.exit(0);
236 		}
237 		else {
238 			if (jdkCheck) {
239 				checkJDK(REQUIRED_CMDL_JDK);
240 			}
241 
242  			if (!startCmdl) {
243  				exceptionHandler = new ErrorHandler();
244  				UncaughtExceptionManager.getInstance().addExceptionListener
245  					(exceptionHandler);
246 
247 				if (prefs.getShowSplash()) {
248 					SplashWindow.showSplashWindow(0);
249 					SplashWindow.setText(XNap.tr("Starting up"));
250 				}
251 			}
252 
253 			Updater.start();
254 
255 			if (startCmdl) {
256 				CommandLine cmdl = new CommandLine();
257 				cmdl.start();
258 			}
259 			else {
260 				startGUI();
261 			}
262 		}
263 	}
264 
265     /***
266      * Sets default locale and loads resources.
267      */
268     public static void initLocale(String language)
269     {
270 		for (int i = 0; i < LANGUAGES.length; i++) {
271 			if (language.equals(LANGUAGES[i].getLanguage())) {
272 				Locale.setDefault(LANGUAGES[i]);
273 				break;
274 			}
275 		}
276 
277         try {
278 			resources = ResourceBundle.getBundle("org.xnap.resources.XNap",
279 												 Locale.getDefault());
280         }
281 		catch (MissingResourceException e) {
282             System.err.println("org/xnap/resources/XNap.properties not found");
283             System.exit(1);
284 		}
285     }
286     
287 	public static final XNapThreadGroup getThreadGroup()
288 	{
289 		return tg;
290 	}
291 
292 	public static boolean isRunFromCvs()
293 	{
294 		return isRunFromCvs;
295 	}
296 
297     /***
298      * Prints a warning message if <code>reqVer</code> is smaller than the
299      * current jdk version.
300      */
301     public static final void checkJDK(String reqVer)
302     {
303 		String jdkVer = System.getProperty("java.version");
304 
305 		if (VersionParser.compare(jdkVer, reqVer) < 0) {
306 			System.out.println(XNap.tr("Your java version") + "\t: " + jdkVer);
307 			System.out.println(XNap.tr("Required version") + "\t: " + reqVer);
308 			System.out.println(XNap.tr("If XNap runs fine, you can safely ignore this message.\n") 
309 							   + XNap.tr("Otherwise you probably need to update your jdk from\nhttp://java.sun.com/products/jdk/1.3/jre/index.html."));
310 			System.out.println(tr("See http://xnap.sourceforge.net for more information.\n") 
311 							   + "(" + XNap.tr("use -n to suppress this message") + ")\n");
312 		}
313     }
314     
315     /***
316      * Print a help message to stdout.
317      */
318     public static final void showHelp()
319     {
320 		System.out.println(XNap.tr("Usage: xnap [options]"));
321 		System.out.println(" -c\t " + XNap.tr("start in commandline mode"));
322 		System.out.println(" -d[level]\t " + XNap.tr("show debug messages"));
323 		System.out.println(" -h\t " + XNap.tr("show this help"));
324 		System.out.println(" -n\t" + XNap.tr("do not check jdk version"));
325     }
326 
327 	public static final void startGUI()
328 	{
329 		if (exceptionHandler == null) {
330 			exceptionHandler = new ErrorHandler();
331 			UncaughtExceptionManager.getInstance().addExceptionListener
332 				(exceptionHandler);
333 		}
334 
335 		if (firstTime) {
336 			ThemeManager.initialize();
337 			XNapFrame.updateLookAndFeel();
338 			firstTime = false;
339 		}
340 
341 		XNapFrame f = new XNapFrame();
342 		f.setVisible(true);
343 
344 		f.guiVisible();
345 				
346 		SplashWindow.closeSplashWindow();
347 	}
348 
349 	public static final void stopGUI()
350 	{
351 		XNapFrame.getInstance().stop();
352 
353 		UncaughtExceptionManager.getInstance().removeExceptionListener
354 			(exceptionHandler);
355 		exceptionHandler = null;
356 	}
357 
358     /***
359      * Returns <code>text</code> translated into the currently selected
360      * language. Every user-visible string in the program must be wrapped
361      * into this function.  
362      */
363     public static final String tr(String text)
364     {
365 		try {
366 			return resources.getString(text);
367 		}
368 		catch (MissingResourceException e) {
369 			//System.err.println("missing translation key: \"" + text + "\"");
370 			//e.printStackTrace(System.err);
371 			return text;
372 		}
373 		catch (NullPointerException e) {
374 			// the message bundle has not been loaded yet
375 			return text;
376 		}
377     }
378 
379     /***
380      * Returns <code>text</code> translated into the currently selected
381      * language. 
382      *
383      * <p>The first occurence of {0} is replaced by <code>o1.toString()</code>.
384      */
385     public static final String tr(String text, Object o1)
386     {
387 		return MessageFormat.format(XNap.tr(text), new Object[] { o1 });
388     }
389 
390     /***
391      * Returns <code>text</code> translated into the currently selected
392      * language. 
393      *
394      * <p>The first occurence of {0} is replaced by <code>o1.toString()</code>.
395      * The first occurence of {1} is replaced by <code>o2.toString()</code>.
396      */
397     public static final String tr(String text, Object o1, Object o2)
398     {
399 		return MessageFormat.format(XNap.tr(text), new Object[] { o1, o2 });
400     }
401 
402     /***
403      * Returns <code>text</code> translated into the currently selected
404      * language. 
405      *
406      * <p>The first occurence of {0} is replaced by <code>o1.toString()</code>.
407      * The first occurence of {1} is replaced by <code>o2.toString()</code>.
408      * The first occurence of {2} is replaced by <code>o3.toString()</code>.
409      */
410     public static final String tr(String text, Object o1, Object o2,
411 								  Object o3)
412     {
413 		return MessageFormat.format(XNap.tr(text), 
414 									new Object[] { o1, o2, o3 });
415     }
416 
417 	/***
418 	 * Returns <code>text</code> translated into the currently selected
419 	 * language. 
420 	 *
421 	 * <p>The first occurence of {0} is replaced by <code>o1.toString()</code>.
422 	 * The first occurence of {1} is replaced by <code>o2.toString()</code>.
423 	 * The first occurence of {2} is replaced by <code>o3.toString()</code>.
424 	 * The first occurence of {3} is replaced by <code>o4.toString()</code>.
425 	 */
426 	public static final String tr(String text, Object o1, Object o2,
427 								  Object o3, Object o4)
428 	{
429 		return MessageFormat.format(XNap.tr(text), 
430 									new Object[] { o1, o2, o3, o4 });
431 	}
432 
433     /***
434      * Returns <code>text</code> translated into the currently selected
435      * language. Prepends and appends <code>padding</code> whitespaces.
436      */
437     public static final String tr(String text, int padding)
438     {
439 		String s = tr(text);
440 		if (padding <= 0) {
441 			return s;
442 		}
443 		StringBuffer sb = new StringBuffer(s.length() + padding * 2);
444 		append(sb, " ", padding);
445 		sb.append(s);
446 		append(sb, " ", padding);
447 		return sb.toString();
448     }
449 
450     /***
451      * Returns <code>text</code> translated into the currently selected
452      * language. Prepends <code>lpadding</code> whitespaces. Appends
453      * <code>rpadding</code> whitespaces.
454      */
455     public static final String tr(String text, int lpadding, int rpadding)
456     {
457 		String s = tr(text);
458 		StringBuffer sb = new StringBuffer(s.length() + lpadding + rpadding);
459 		append(sb, " ", lpadding);
460 		sb.append(s);
461 		append(sb, " ", rpadding);
462 		return sb.toString();
463     }
464 
465     private static final void append(StringBuffer sb, String s, int count)
466     {
467 		for (int i = 0; i < count; i++) {
468 			sb.append(s);
469 		}
470     }
471 }