001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.core.main;
019:
020: import java.io.File;
021: import java.io.FilenameFilter;
022: import java.lang.reflect.Field;
023: import java.net.URL;
024: import java.net.URLClassLoader;
025: import java.text.MessageFormat;
026: import java.util.ArrayList;
027: import java.util.List;
028: import java.util.logging.Logger;
029:
030: import javax.swing.JPopupMenu;
031: import javax.swing.RepaintManager;
032: import javax.swing.SwingUtilities;
033:
034: import org.apache.commons.cli.CommandLine;
035: import org.apache.commons.cli.Option;
036: import org.apache.commons.cli.OptionBuilder;
037: import org.apache.commons.cli.ParseException;
038: import org.columba.core.association.AssociationStore;
039: import org.columba.core.backgroundtask.BackgroundTaskManager;
040: import org.columba.core.base.OSInfo;
041: import org.columba.core.component.ComponentManager;
042: import org.columba.core.config.Config;
043: import org.columba.core.config.DefaultConfigDirectory;
044: import org.columba.core.config.SaveConfig;
045: import org.columba.core.desktop.ColumbaDesktop;
046: import org.columba.core.gui.base.DebugRepaintManager;
047: import org.columba.core.gui.frame.FrameManager;
048: import org.columba.core.gui.profiles.Profile;
049: import org.columba.core.gui.profiles.ProfileManager;
050: import org.columba.core.gui.themes.ThemeSwitcher;
051: import org.columba.core.gui.trayicon.ColumbaTrayIcon;
052: import org.columba.core.gui.util.FontProperties;
053: import org.columba.core.gui.util.StartUpFrame;
054: import org.columba.core.logging.Logging;
055: import org.columba.core.plugin.PluginManager;
056: import org.columba.core.resourceloader.GlobalResourceLoader;
057: import org.columba.core.scripting.service.ServiceManager;
058: import org.columba.core.shutdown.ShutdownManager;
059: import org.columba.core.util.StackProfiler;
060: import org.columba.core.versioninfo.VersionInfo;
061: import org.frapuccino.swing.ActiveWindowTracker;
062:
063: import sun.misc.URLClassPath;
064:
065: public class Bootstrap {
066:
067: private static final Logger LOG = Logger
068: .getLogger("org.columba.core.main"); //$NON-NLS-1$
069:
070: private static final String RESOURCE_PATH = "org.columba.core.i18n.global"; //$NON-NLS-1$
071:
072: // TODO @author hubms have this flags, until the speed of the entitymanager
073: // is improved
074: public static boolean ENABLE_TAGS = false;
075:
076: private String path;
077:
078: private boolean showSplashScreen = true;
079:
080: public void run(String args[]) throws Exception {
081:
082: addNativeJarsToClasspath();
083: setLibraryPath();
084:
085: // For the Mac ScreenBarMenus to work, this must be declared before
086: // *ANY* AWT / Swing gets initialised. Do *NOT* move it to plugin init
087: // location because that is too late...
088: if (OSInfo.isMac()) {
089: System.setProperty("apple.laf.useScreenMenuBar", "true");
090: System.setProperty(
091: "com.apple.mrj.application.apple.menu.about.name",
092: "Columba");
093: }
094:
095: Logging.createDefaultHandler();
096: registerCommandLineArguments();
097:
098: StackProfiler profiler = new StackProfiler();
099: profiler.push("main");
100: profiler.push("config");
101: profiler.push("profile");
102: // prompt user for profile
103: Profile profile = ProfileManager.getInstance().getProfile(path);
104: profiler.pop("profile");
105:
106: // initialize configuration with selected profile
107: DefaultConfigDirectory.getInstance().setCurrentPath(
108: profile.getLocation());
109: new Config(profile.getLocation());
110: profiler.pop("config");
111:
112: // if user doesn't overwrite logger settings with commandline arguments
113: // just initialize default logging
114: // Logging.createDefaultHandler();
115: Logging.createDefaultFileHandler(DefaultConfigDirectory
116: .getDefaultPath());
117:
118: for (int i = 0; i < args.length; i++) {
119: LOG.info("arg[" + i + "]=" + args[i]);
120: }
121:
122: SessionController.passToRunningSessionAndExit(args);
123:
124: // enable debugging of repaint manager to track down swing gui
125: // access from outside the awt-event dispatcher thread
126:
127: if (Logging.DEBUG)
128: RepaintManager.setCurrentManager(new DebugRepaintManager());
129:
130: // use heavy-weight popups to ensure they are always on top
131: JPopupMenu.setDefaultLightWeightPopupEnabled(false);
132:
133: // keep track of active windows (used by dialogs which don't have a
134: // direct parent)
135: ActiveWindowTracker.class.getClass();
136:
137: // show splash screen
138: StartUpFrame frame = null;
139: if (showSplashScreen) {
140: frame = new StartUpFrame();
141: frame.setVisible(true);
142: }
143:
144: // register protocol handler
145: System.setProperty("java.protocol.handler.pkgs",
146: "org.columba.core.url|"
147: + System.getProperty(
148: "java.protocol.handler.pkgs", ""));
149:
150: profiler.push("i18n");
151: // load user-customised language pack
152: GlobalResourceLoader.loadLanguage();
153: profiler.pop("i18n");
154:
155: SaveConfig task = new SaveConfig();
156: BackgroundTaskManager.getInstance().register(task);
157: ShutdownManager.getInstance().register(task);
158:
159: profiler.push("plugins core");
160: initPlugins();
161: profiler.pop("plugins core");
162:
163: profiler.push("components");
164: // init all components
165: ComponentManager.getInstance().init();
166: ComponentManager.getInstance().registerCommandLineArguments();
167: profiler.pop("components");
168:
169: // set Look & Feel
170: ThemeSwitcher.setTheme();
171:
172: // initialize platform-dependant services
173: initPlatformServices();
174:
175: // init font configuration
176: new FontProperties();
177:
178: // set application wide font
179: FontProperties.setFont();
180:
181: // handle commandline parameters
182: if (handleCoreCommandLineParameters(args)) {
183: System.exit(0);
184: }
185:
186: // handle the commandline arguments of the modules
187: ComponentManager.getInstance().handleCommandLineParameters(
188: ColumbaCmdLineParser.getInstance()
189: .getParsedCommandLine());
190:
191: profiler.push("plugins external");
192: // now load all available plugins
193: // PluginManager.getInstance().initExternalPlugins();
194: profiler.pop("plugins external");
195:
196: profiler.push("frames");
197:
198: // restore frames of last session
199: if (ColumbaCmdLineParser.getInstance().getRestoreLastSession()) {
200: SwingUtilities.invokeLater(new Runnable() {
201: public void run() {
202: FrameManager.getInstance().openStoredViews();
203: }
204: });
205: }
206:
207: /* initialize services before dismissing the splash screen */
208: ServiceManager.getInstance().initServices();
209:
210: // register shutdown manager
211: ShutdownManager.getInstance().register(new Runnable() {
212: public void run() {
213:
214: ServiceManager.getInstance().stopServices();
215: ServiceManager.getInstance().disposeServices();
216: }
217: });
218:
219: profiler.pop("frames");
220:
221: // Add the tray icon to the System tray
222: // ColumbaTrayIcon.getInstance().addToSystemTray(
223: // FrameManager.getInstance().getActiveFrameMediator()
224: // .getFrameMediator());
225:
226: profiler.push("tagging");
227:
228: // initialize tagging
229: if (ENABLE_TAGS) {
230: AssociationStore.getInstance().init();
231: // register for cleanup
232: ShutdownManager.getInstance().register(
233: AssociationStore.getInstance());
234: }
235:
236: profiler.pop("tagging");
237:
238: // hide splash screen
239: if (frame != null) {
240: frame.setVisible(false);
241: }
242:
243: // call the postStartups of the modules
244: // e.g. check for default mailclient
245: ComponentManager.getInstance().postStartup();
246:
247: /* everything is up and running, start services */
248: ServiceManager.getInstance().startServices();
249:
250: profiler.pop("main");
251:
252: }
253:
254: /**
255: * initialize all extension handlers from core, mail and contacts.
256: * Additionally, load all internally shipped plugins and last but not least,
257: * load all external plugins residing in /plugin directory.
258: */
259: private void initPlugins() {
260:
261: //
262: // first load all extension handlers
263: //
264:
265: // load core extension handlers
266: PluginManager.getInstance().addExtensionHandlers(
267: "org/columba/core/plugin/extensionhandler.xml");
268:
269: // load addressbook extension handler
270: PluginManager.getInstance().addExtensionHandlers(
271: "org/columba/addressbook/plugin/extensionhandler.xml");
272:
273: // load mail extension handler
274: PluginManager.getInstance().addExtensionHandlers(
275: "org/columba/mail/plugin/extensionhandler.xml");
276:
277: // load all internal core plugins
278: String path = "org/columba/core/plugin/plugin.xml";
279: PluginManager.getInstance().addPlugin(path);
280:
281: //
282: // following internal components plugin registration
283: //
284:
285: // load all internal addressbook plugins
286: path = "org/columba/addressbook/plugin/plugin.xml";
287: PluginManager.getInstance().addPlugin(path);
288:
289: // load all internal mail plugins
290: path = "org/columba/mail/plugin/plugin.xml";
291: PluginManager.getInstance().addPlugin(path);
292:
293: // load all internal calendar plugins
294: path = "org/columba/calendar/plugin/plugin.xml";
295: PluginManager.getInstance().addPlugin(path);
296:
297: //
298: // now load all external plugins residing in /plugins directory
299: //
300: PluginManager.getInstance().initExternalPlugins();
301: }
302:
303: /**
304: * registerCommandLineArguments method
305: */
306: private void registerCommandLineArguments() {
307: ColumbaCmdLineParser parser = ColumbaCmdLineParser
308: .getInstance();
309:
310: parser
311: .addOption(new Option("version", GlobalResourceLoader
312: .getString(RESOURCE_PATH, "global",
313: "cmdline_version")));
314:
315: parser.addOption(new Option("help", GlobalResourceLoader
316: .getString(RESOURCE_PATH, "global", "cmdline_help")));
317:
318: parser.addOption(OptionBuilder.withArgName("path").hasArg()
319: .create("profile"));
320:
321: parser
322: .addOption(new Option("profile", GlobalResourceLoader
323: .getString(RESOURCE_PATH, "global",
324: "cmdline_profile")));
325:
326: parser.addOption(new Option("debug", GlobalResourceLoader
327: .getString(RESOURCE_PATH, "global", "cmdline_debug")));
328:
329: parser
330: .addOption(new Option("nosplash", GlobalResourceLoader
331: .getString(RESOURCE_PATH, "global",
332: "cmdline_nosplash")));
333:
334: // ComponentPluginHandler handler = null;
335: // try {
336: // handler = (ComponentPluginHandler) PluginManager.getInstance()
337: // .getHandler("org.columba.core.component");
338: // handler.registerCommandLineArguments();
339: // } catch (PluginHandlerNotFoundException e) {
340: // e.printStackTrace();
341: // }
342: }
343:
344: /**
345: * Uses the command line parser to validate the passed arguments and invokes
346: * handlers to process the detected options.
347: */
348: private boolean handleCoreCommandLineParameters(String[] args) {
349: ColumbaCmdLineParser parser = ColumbaCmdLineParser
350: .getInstance();
351: CommandLine commandLine;
352:
353: try {
354: commandLine = parser.parse(args);
355: } catch (ParseException e) {
356: // oops, something went wrong
357: System.err.println("Parsing failed. Reason: "
358: + e.getMessage());
359: parser.printUsage();
360:
361: return true;
362: }
363:
364: if (commandLine.hasOption("help")) {
365: parser.printUsage();
366:
367: return true;
368: }
369:
370: // TODO: Make this hack more i18n compatible
371: if (commandLine.hasOption("version")) {
372: LOG.info(MessageFormat.format(
373: GlobalResourceLoader.getString(RESOURCE_PATH,
374: "global", "info_version"), //$NON-NLS-2$
375: new Object[] { VersionInfo.getVersion(),
376: VersionInfo.getBuildDate() }));
377: System.out.println("Columba (" + VersionInfo.getVersion()
378: + ") built " + VersionInfo.getBuildDate() + "\n");
379: return true;
380: }
381:
382: if (commandLine.hasOption("profile")) {
383:
384: // TODO: There's probably a better way to do this hack...
385: path = commandLine.getArgList().toString();
386:
387: // This is necessary because getArgList returns the path in
388: // square brackets
389: path = path.substring(1, path.length() - 1);
390: }
391:
392: if (commandLine.hasOption("debug")) {
393: Logging.DEBUG = true;
394: Logging.setDebugging(true);
395: }
396:
397: if (commandLine.hasOption("nosplash")) {
398: showSplashScreen = false;
399: }
400:
401: // Do not exit
402: return false;
403: }
404:
405: /**
406: * This hacks the classloader to adjust the library path for convenient
407: * native support.
408: *
409: * @author tstich
410: *
411: * @throws Exception
412: */
413: private void setLibraryPath() throws Exception {
414: String libDir;
415: if (OSInfo.isAMD64Bit())
416: libDir = "amd64";
417: else
418: libDir = "lib";
419:
420: // Platform maintainers: add your platform here
421:
422: String propertyPath = System.getProperty("java.library.path");
423:
424: if (OSInfo.isLinux())
425: propertyPath += ":native/linux/";
426: else if (OSInfo.isMac())
427: propertyPath += ":native/mac/";
428: else if (OSInfo.isWin32Platform())
429: propertyPath += ";native\\win32\\";
430: // Platform maintainers: add your platform here
431:
432: propertyPath += libDir;
433:
434: System.setProperty("java.library.path", propertyPath);
435: LOG.info("The java.library.path = " + propertyPath);
436: Field fieldSysPath = ClassLoader.class
437: .getDeclaredField("sys_paths");
438: fieldSysPath.setAccessible(true);
439: if (fieldSysPath != null) {
440: fieldSysPath.set(System.class.getClassLoader(), null);
441: }
442: }
443:
444: /**
445: * This hacks the classloader to adjust the classpath for convenient native
446: * support.
447: * <p>
448: * I've cleaned this up using our new global class loader. This way we only
449: * add a few new <code>URLs</code> to our class loader instead of
450: * modifying the system class loader using reflection.
451: *
452: * @author tstich,fdietz
453: *
454: * @throws Exception
455: */
456: private void addNativeJarsToClasspath() throws Exception {
457: File nativeDir;
458:
459: String libDir;
460: if (OSInfo.isAMD64Bit())
461: libDir = "amd64";
462: else
463: libDir = "lib";
464:
465: // Setup the path
466: // Platform maintainers: add your platform here
467: // see also initPlatformServices() method
468: if (OSInfo.isLinux())
469: nativeDir = new File("native/linux/" + libDir);
470: else if (OSInfo.isMac())
471: nativeDir = new File("native/mac/" + libDir);
472: else if (OSInfo.isWin32Platform())
473: nativeDir = new File("native/win32/" + libDir);
474: else {
475: LOG.info("Native support for Platform not available.");
476: return;
477: }
478:
479: // Find all native jars
480: File[] nativeJars = nativeDir.listFiles(new FilenameFilter() {
481: public boolean accept(File dir, String name) {
482: return name.endsWith("jar") || name.endsWith("jnilib");
483: }
484: });
485: if (nativeJars == null)
486: return;
487:
488: // @author: fdietz
489: //
490: // The following line is not working - just don't know why
491: // Main.mainClassLoader.addURLs((URL[]) urlList.toArray(new URL[0]));
492: //
493: // WORKAROUND:
494: //
495: // Modify the system class loader instead - horrible! But it works!
496:
497: // Get the current classpath from the sysloader
498: // through reflection
499: URLClassLoader sysloader = (URLClassLoader) ClassLoader
500: .getSystemClassLoader();
501:
502: Field ucp = URLClassLoader.class.getDeclaredField("ucp");
503: ucp.setAccessible(true);
504: URLClassPath currentCP = (URLClassPath) ucp.get(sysloader);
505: URL[] currentURLs = currentCP.getURLs();
506:
507: // add all native jars
508: List<URL> urlList = new ArrayList<URL>();
509: for (int i = 0; i < nativeJars.length; i++) {
510: urlList.add(nativeJars[i].toURL());
511: }
512:
513: // add the old classpath
514: for (int i = 0; i < currentURLs.length; i++) {
515: urlList.add(currentURLs[i]);
516: }
517:
518: // replace with the modified classpath
519: ucp.set(sysloader, new URLClassPath((URL[]) urlList
520: .toArray(new URL[0])));
521: }
522:
523: /**
524: * Initialise system dependent stuff
525: */
526: private void initPlatformServices() {
527:
528: // Initialise system dependent stuff
529: ColumbaDesktop.getInstance().initActiveDesktop();
530: ColumbaTrayIcon.getInstance().initActiveIcon();
531: }
532: }
|