/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.framework.internal;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URL;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.prefs.BackingStoreException;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.UIManager;
import org.eclipse.persistence.tools.workbench.framework.AbstractApplication;
import org.eclipse.persistence.tools.workbench.framework.Application;
import org.eclipse.persistence.tools.workbench.framework.NodeManager;
import org.eclipse.persistence.tools.workbench.framework.OpenException;
import org.eclipse.persistence.tools.workbench.framework.Plugin;
import org.eclipse.persistence.tools.workbench.framework.PluginFactory;
import org.eclipse.persistence.tools.workbench.framework.UnsupportedFileException;
import org.eclipse.persistence.tools.workbench.framework.app.AbstractPreferencesNode;
import org.eclipse.persistence.tools.workbench.framework.app.ApplicationNode;
import org.eclipse.persistence.tools.workbench.framework.app.PreferencesNode;
import org.eclipse.persistence.tools.workbench.framework.context.AbstractApplicationContext;
import org.eclipse.persistence.tools.workbench.framework.context.ApplicationContext;
import org.eclipse.persistence.tools.workbench.framework.context.PreferencesContext;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext;
import org.eclipse.persistence.tools.workbench.framework.help.HelpFacade;
import org.eclipse.persistence.tools.workbench.framework.help.HelpManager;
import org.eclipse.persistence.tools.workbench.framework.help.HelpManagerConfig;
import org.eclipse.persistence.tools.workbench.framework.resources.DefaultResourceRepository;
import org.eclipse.persistence.tools.workbench.framework.resources.ResourceRepository;
import org.eclipse.persistence.tools.workbench.framework.resources.ResourceRepositoryWrapper;
import org.eclipse.persistence.tools.workbench.framework.uitools.UIToolsIconResourceFileNameMap;
import org.eclipse.persistence.tools.workbench.framework.uitools.UIToolsResourceBundle;
import org.eclipse.persistence.tools.workbench.uitools.Console;
import org.eclipse.persistence.tools.workbench.uitools.RecentFilesManager;
import org.eclipse.persistence.tools.workbench.uitools.SplashScreen;
import org.eclipse.persistence.tools.workbench.uitools.app.BufferedPropertyValueModel;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.Command;
import org.eclipse.persistence.tools.workbench.utility.SynchronizedBoolean;
import org.eclipse.persistence.tools.workbench.utility.SynchronizedObject;
import org.eclipse.persistence.tools.workbench.utility.io.InvalidInputStream;
import org.eclipse.persistence.tools.workbench.utility.io.TeeOutputStream;
import org.eclipse.persistence.tools.workbench.utility.log.DeathHandler;
import org.eclipse.persistence.tools.workbench.utility.log.FileHandlerCleanup;
/**
* The UI framework starts here - this is the public entry into the
* TopLink Workbench application. We build plug-ins here and we
* build the objects for which there should only be a single instance
* per application (e.g. the node manager, the help manager).
*
* Hard code your PluginFactory class name and static method name in the static
* array PLUGIN_FACTORY_STATIC_METHOD_SPECS.
*/
public final class FrameworkApplication
extends AbstractApplication
{
/** This is false during launch and set to true once the launch is complete. */
private SynchronizedBoolean launchCompleteFlag;
/** This flag can be set to true via a command line argument, "-dev". */
private boolean developmentMode;
/** The logger used mostly for recording uncaught exceptions. */
private Logger logger;
/** The root node for all the preferences used by the application. */
private Preferences rootPreferences;
/** The node manager tracks all the "project-level" nodes. */
private FrameworkNodeManager nodeManager;
/** The default resource repository - includes both UI Tools and Framework resources. */
private ResourceRepository resourceRepository;
/** The OHJ context-sensitive help manager. */
private SynchronizedObject synchronizedHelpManager;
/** The "root" application context. */
private ApplicationContext rootApplicationContext;
/** The plug-ins built by the plug-in factories at start-up. */
private Plugin[] plugins;
/** The current set of open workbench windows. */
private Set workbenchWindows;
/** The preferences view passed to the preferences dialog(s). */
private PreferencesView preferencesView;
/** This is true only during the first execution. */
private boolean firstExecution;
/** There are two different consoles depending on development mode or runtime */
private Console frameworkConsole;
// ********** static fields **********
/** The name of the application's splash screen file - displayed on start-up. */
private static final String SPLASH_SCREEN_FILE_NAME = "/logo/splash.screen.gif";
/** The name of the application's splash screen icon - shown in the ALT-TAB dialog. */
private static final String SPLASH_SCREEN_ICON_FILE_NAME = "/logo/MappingWorkbench.large.gif";
/**
* The current preferences version number (major.minor). This string
* will be used to name a preferences under the root node under which
* all the other preferences nodes will be located.
* Change the minor number when the organization changes but is still
* backward-compatible. Change the major number when the organization
* changes and is no longer backward-compatible.
* If the major number changes, make the previous version available
* for migration.
*/
private static final String PREFERENCES_CURRENT_VERSION_NODE = "1.0";
/**
* The name of the "general" preferences node used to store
* preferences used by the framework, as opposed to the plug-ins.
*/
static final String GENERAL_PREFERENCES_NODE = "general";
/** Various start-up preferences. */
static final String DISPLAY_SPLASH_SCREEN_PREFERENCE = "display splash screen";
static final boolean DISPLAY_SPLASH_SCREEN_PREFERENCE_DEFAULT = true;
static final String LOOK_AND_FEEL_PREFERENCE = "look and feel";
static final String HTTP_PROXY_HOST_PREFERENCE = "http proxy host";
static final String HTTP_PROXY_HOST_PREFERENCE_DEFAULT = "";
static final String HTTP_PROXY_PORT_PREFERENCE = "http proxy port";
static final String HTTP_PROXY_PORT_PREFERENCE_DEFAULT = "80";
static final String NETWORK_CONNECT_TIMEOUT_PREFERENCE = "network connect timeout"; // specified in seconds
static final String NETWORK_CONNECT_TIMEOUT_PREFERENCE_DEFAULT = "10";
static final String NETWORK_READ_TIMEOUT_PREFERENCE = "network read timeout"; // specified in seconds
static final String NETWORK_READ_TIMEOUT_PREFERENCE_DEFAULT = "10";
static final String REOPEN_PROJECTS_PREFERENCE = "reopen projects";
static final boolean REOPEN_PROJECTS_PREFERENCE_DEFAULT = true;
static final String DYNAMIC_LAYOUT_PREFERENCE = "dynamic layout";
/**
* The list of plug-in factory static methods. Each static method must
* return an instance of PluginFactory. These factories will be used by
* the application to build the plug-ins. These are stored as strings so
* we don't have compile-time dependencies and they can be
* "externalized" at some later date.
*/
private static final PluginFactoryStaticMethodSpec[] PLUGIN_FACTORY_STATIC_METHOD_SPECS =
new PluginFactoryStaticMethodSpec[] {
// these class names MUST be strings ~bjv
new PluginFactoryStaticMethodSpec("org.eclipse.persistence.tools.workbench.mappingsplugin.MappingsPluginFactory", "instance"),
new PluginFactoryStaticMethodSpec("org.eclipse.persistence.tools.workbench.scplugin.SCPluginFactory", "instance"),
new PluginFactoryStaticMethodSpec("org.eclipse.persistence.tools.workbench.platformsplugin.ui.PlatformsPluginFactory", "instance"),
};
// ********** static methods **********
/**
* Launch the application.
*/
public static void launch(Logger logger, Preferences preferences, File projectFile, boolean developmentMode) {
boolean firstExecution = buildFirstExecution(preferences, logger);
boolean displaySplashScreen = buildDisplaySplashScreen(preferences);
Handler deathHandler = null;
SplashScreen ss = null;
if (displaySplashScreen) {
// if we start a splash screen, we need a "death handler" during launch
deathHandler = buildDeathHandler();
logger.addHandler(deathHandler);
ss = buildSplashScreen();
ss.start();
}
new FrameworkApplication(logger, preferences, firstExecution, developmentMode).launch(projectFile);
if (displaySplashScreen) {
ss.stop();
logger.removeHandler(deathHandler);
}
}
/**
* Creates the splash screen for this application.
*/
private static SplashScreen buildSplashScreen()
{
JFrame frame = new JFrame("EclipseLink Workbench");
Icon icon = getIcon(SPLASH_SCREEN_ICON_FILE_NAME);
if (icon instanceof ImageIcon) {
frame.setIconImage(((ImageIcon) icon).getImage());
}
// return new SplashScreen(frame, getString("COPYRIGHT"), getIcon(SPLASH_SCREEN_FILE_NAME));
return new SplashScreen(frame, getIcon(SPLASH_SCREEN_FILE_NAME));
}
/**
* Shut down the JVM if we have any exceptions during launch.
* We need this because if we launch the splash screen, there seems
* to be no way to get the JVM to die naturally. Even if we close the
* splash screen, the JVM keeps running.... ~bjv
*/
private static Handler buildDeathHandler() {
// wait 5 seconds and exit with a status code of 1
Handler handler = new DeathHandler(5000, 1);
handler.setLevel(Level.SEVERE);
return handler;
}
/**
* Check for the preferences node for the current preferences version;
* if it does NOT exist, this must be our first execution.
*/
private static boolean buildFirstExecution(Preferences preferences, Logger logger) {
try {
return ! CollectionTools.contains(preferences.childrenNames(), PREFERENCES_CURRENT_VERSION_NODE);
} catch (BackingStoreException ex) {
// if there are any problems, log the exception and act like it's a first execution
logger.log(Level.WARNING, "", ex);
return true;
}
}
/**
* No preferences yet - hard-code the appropriate preference.
*/
private static boolean buildDisplaySplashScreen(Preferences preferences) {
return preferences.node(PREFERENCES_CURRENT_VERSION_NODE).node(GENERAL_PREFERENCES_NODE).getBoolean(DISPLAY_SPLASH_SCREEN_PREFERENCE, DISPLAY_SPLASH_SCREEN_PREFERENCE_DEFAULT);
}
/**
* Return the string for the specified key.
* No string repository yet - hard-code the bundle class name.
*/
// this method was used by buildSplashScreen() until the copyright notice was removed
// private static String getString(String key) {
// return ResourceBundle.getBundle(getResourceBundleName()).getString(key);
// }
public static String getResourceBundleName() {
return FrameworkResourceBundle.class.getName();
}
/**
* No icon repository yet - hard-code the icon resource.
*/
private static Icon getIcon(String resourceName) {
return new ImageIcon(getResource(resourceName));
}
/**
* Return the URL of the specified resource on the classpath.
*/
private static URL getResource(String name) {
return FrameworkApplication.class.getResource(name);
}
// ********** constructor/initialization **********
/**
* Construct an application with the specified root preferences node.
*/
private FrameworkApplication(Logger logger, Preferences preferences, boolean firstExecution, boolean developmentMode) {
super();
this.firstExecution = firstExecution;
this.logger = logger;
this.developmentMode = developmentMode;
this.initialize(preferences);
}
private void initialize(Preferences preferences) {
this.launchCompleteFlag = new SynchronizedBoolean(false);
// shift the root to a version-specific subnode
this.rootPreferences = preferences.node(PREFERENCES_CURRENT_VERSION_NODE);
this.configureLoggerForLaunch();
this.initializeLookAndFeel();
this.initializeNetworkSettings();
this.nodeManager = this.buildNodeManager();
this.resourceRepository = this.buildResourceRepository();
this.startInitializeSynchronizedHelpManager();
this.rootApplicationContext = new RootApplicationContext();
this.plugins = this.buildPlugins(PLUGIN_FACTORY_STATIC_METHOD_SPECS);
this.initializeWorkbenchWindows();
// postpone building the preferences view until it is first needed
Toolkit.getDefaultToolkit().setDynamicLayout(this.generalPreferences().getBoolean(DYNAMIC_LAYOUT_PREFERENCE, true));
}
/**
* Initialize the logger:
* - add a handler that writes log entries to a file
* - register a hook to clean up the lock file on system exit
*/
private void configureLoggerForLaunch() {
FileHandler fileHandler;
try {
// TODO get log file name and size from preferences?
// true = append
fileHandler = new FileHandler(System.getProperty("user.home")
+ "/org.eclipse.persistence.tools.workbench.log", 50000, 1, true);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
// once the file handler is built, the lock file is present and open
FileHandlerCleanup.register(fileHandler);
fileHandler.setFormatter(new SimpleFormatter());
this.logger.addHandler(fileHandler);
}
/**
* Initialize the LAF to the user's preference.
*/
private void initializeLookAndFeel() {
try {
UIManager.setLookAndFeel(this.lookAndFeel());
} catch (Exception exception1) {
try {
// Revert back to the system look and feel
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception exception2) {
try {
// Revert back to the cross-platform look and feel
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception exception3) {
// This should never happen
throw new RuntimeException(exception3);
}
}
}
}
/**
* Default to the current platform's LAF;
* overriding the default, Metal LAF.
*/
private String lookAndFeel() {
String laf = this.generalPreferences().get(LOOK_AND_FEEL_PREFERENCE, defaultLookAndFeel());
// uncomment one of the following lines to try the other LAFs
// laf = UIManager.getCrossPlatformLookAndFeelClassName(); // Metal
// laf = com.sun.java.swing.plaf.windows.WindowsLookAndFeel.class.getName();
// laf = com.sun.java.swing.plaf.motif.MotifLookAndFeel.class.getName();
return laf;
}
/**
* Returns the default LAF. Under Linux, GTK+ is the default but because it
* has too many problems, the Metal LAF is used instead.
*/
private String defaultLookAndFeel() {
return (System.getProperty("os.name", "").toLowerCase().indexOf("linux") != -1) ?
UIManager.getCrossPlatformLookAndFeelClassName()
:
UIManager.getSystemLookAndFeelClassName();
}
/**
* Check for network settings. These are used by
* OHJ and for reading Schemas from the WWW.
*/
private void initializeNetworkSettings() {
String host = this.generalPreferences().get(HTTP_PROXY_HOST_PREFERENCE, null);
if (host != null) {
System.setProperty("http.proxyHost", host);
}
String port = this.generalPreferences().get(HTTP_PROXY_PORT_PREFERENCE, null);
if (port != null) {
System.setProperty("http.proxyPort", port);
}
String connectTimeout = this.generalPreferences().get(NETWORK_CONNECT_TIMEOUT_PREFERENCE, NETWORK_CONNECT_TIMEOUT_PREFERENCE_DEFAULT);
connectTimeout = String.valueOf(Integer.parseInt(connectTimeout) * 1000); // convert to milliseconds
System.setProperty("sun.net.client.defaultConnectTimeout", connectTimeout);
String readTimeout = this.generalPreferences().get(NETWORK_READ_TIMEOUT_PREFERENCE, NETWORK_READ_TIMEOUT_PREFERENCE_DEFAULT);
readTimeout = String.valueOf(Integer.parseInt(readTimeout) * 1000); // convert to milliseconds
System.setProperty("sun.net.client.defaultReadTimeout", readTimeout);
this.generalPreferences().addPreferenceChangeListener(this.buildNetworkSettingsListener());
}
/**
* Check for changes to the network settings and apply them
*/
private PreferenceChangeListener buildNetworkSettingsListener() {
return new PreferenceChangeListener() {
public void preferenceChange(PreferenceChangeEvent e) {
String key = e.getKey();
if (key.equals(HTTP_PROXY_HOST_PREFERENCE)) {
System.setProperty("http.proxyHost", e.getNewValue());
}
else if (key.equals(HTTP_PROXY_PORT_PREFERENCE)) {
System.setProperty("http.proxyPort", e.getNewValue());
}
else if (key.equals(NETWORK_CONNECT_TIMEOUT_PREFERENCE)) {
System.setProperty("sun.net.client.defaultConnectTimeout", e.getNewValue());
}
else if (key.equals(NETWORK_READ_TIMEOUT_PREFERENCE)) {
System.setProperty("sun.net.client.defaultReadTimeout", e.getNewValue());
}
}
};
}
/**
* Build a node manager for opening, holding, and closing nodes.
*/
private FrameworkNodeManager buildNodeManager() {
return new FrameworkNodeManager(this);
}
/**
* Build a resource repository that holds all the resources for UI Tools
* and the Framework.
*/
private ResourceRepository buildResourceRepository() {
ResourceRepository uiToolsRepository = new DefaultResourceRepository(UIToolsResourceBundle.class, new UIToolsIconResourceFileNameMap());
return new ResourceRepositoryWrapper(uiToolsRepository, FrameworkResourceBundle.class, new FrameworkIconResourceFileNameMap());
}
/**
* Build an empty "synchronized" help manager and
* start a thread to initialize it with a help manager.
*/
private void startInitializeSynchronizedHelpManager() {
// put the synchronized object in place -
// #getHelpManager() will wait until it is initialized and unlocked
this.synchronizedHelpManager = new SynchronizedObject();
new Thread(this.buildInitializeSynchronizedHelpManagerRunnable(), "Initialize Help").start();
}
/**
* Build a Runnable that will initialize the "synchronized" help manager.
*/
private Runnable buildInitializeSynchronizedHelpManagerRunnable() {
return new Runnable() {
public void run() {
FrameworkApplication.this.initializeSynchronizedHelpManager();
}
};
}
/**
* Hold the the lock while building the help manager
* and putting it in the synchronized object.
*/
void initializeSynchronizedHelpManager() {
try {
this.synchronizedHelpManager.execute(this.buildInitializeHelpManagerCommand());
} catch (InterruptedException ex) {
throw new RuntimeException(ex); // shouldn't happen
}
}
/**
* Build the command that will be executed while the "synchronized"
* help manager is locked.
*/
private Command buildInitializeHelpManagerCommand() {
return new Command() {
public void execute() {
FrameworkApplication.this.initializeHelpManager();
}
};
}
/**
* Build the appropriate help manager and put it in the
* synchronized object that wraps it.
*/
void initializeHelpManager() {
HelpManagerConfig config = new HelpManagerConfig();
config.setPreferences(this.generalPreferences());
config.setResourceRepository(this.resourceRepository);
config.setLogger(this.logger);
config.setDevelopmentMode(this.isDevelopmentMode());
config.setLaunchCompleteFlag(this.launchCompleteFlag);
config.setForceStupidWelcomeScreen(this.isFirstExecution());
this.synchronizedHelpManager.setValue(HelpFacade.buildHelpManager(config));
}
/**
* maintain the order of the specs
*/
private Plugin[] buildPlugins(PluginFactoryStaticMethodSpec[] pluginFactoryStaticMethodSpecs) {
int len = pluginFactoryStaticMethodSpecs.length;
Plugin[] result = new Plugin[len];
for (int i = 0; i < len; i++) {
result[i] = this.buildPlugin(pluginFactoryStaticMethodSpecs[i]);
}
return result;
}
private Plugin buildPlugin(PluginFactoryStaticMethodSpec pluginFactoryStaticMethodSpec) {
Class pluginFactoryClass;
try {
pluginFactoryClass = Class.forName(pluginFactoryStaticMethodSpec.getClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
PluginFactory pluginFactory = (PluginFactory) ClassTools.invokeStaticMethod(pluginFactoryClass, pluginFactoryStaticMethodSpec.getStaticMethodName());
return pluginFactory.createPlugin(this.rootApplicationContext);
}
private void initializeWorkbenchWindows() {
this.workbenchWindows = new HashSet();
int windowCount = this.generalPreferences().node("windows").getInt("count", 1);
for (int i = 0; i < windowCount; i++) {
WorkbenchWindow window = new WorkbenchWindow(this);
window.restoreState(this.generalPreferences().node("windows").node("window" + i));
this.workbenchWindows.add(window);
}
}
// ********** queries **********
Logger getLogger() {
return this.logger;
}
/**
* Return the root application preferences node.
* Each plug-in will have its own child node under this node.
*/
Preferences getRootPreferences() {
return this.rootPreferences;
}
FrameworkNodeManager getNodeManager() {
return this.nodeManager;
}
/**
* Postpone building the preferences view until it is first needed.
*/
PreferencesView getPreferencesView() {
if (this.preferencesView == null) {
// store the trigger in the context so the preferences pages have access to it;
// and put it in the view so the dialog OK button has access to it
BufferedPropertyValueModel.Trigger bufferTrigger = new BufferedPropertyValueModel.Trigger();
PreferencesContext context = new FrameworkPreferencesContext(this, bufferTrigger);
this.preferencesView = new PreferencesView(this.buildRootPreferencesNode(context), bufferTrigger);
}
return this.preferencesView;
}
/**
* Return the application's default resource repository, which
* includes the resources for UI Tools and the Framework.
*/
ResourceRepository getResourceRepository() {
return this.resourceRepository;
}
/**
* Return the "root" application context that all other application
* contexts and workbench contexts extend.
*/
ApplicationContext getRootApplicationContext() {
return this.rootApplicationContext;
}
/**
* Return all the application's plug-ins.
*/
Plugin[] getPlugins() {
return this.plugins;
}
/**
* Return the recent files manager that maintains a list
* of the recently-opened files.
*/
RecentFilesManager recentFilesManager() {
return this.nodeManager.getRecentFilesManager();
}
/**
* Return the preferences node that holds the "general" framework
* preferences, as opposed to the preferences nodes for the plug-ins.
*/
Preferences generalPreferences() {
return this.rootPreferences.node(GENERAL_PREFERENCES_NODE);
}
// ********** behavior **********
/**
* Open all the initial workbench windows.
*/
private void launch(File projectToOpen) {
int i = 0;
WorkbenchWindow window = null;
for (Iterator stream = this.workbenchWindows.iterator(); stream.hasNext(); i++) {
window = (WorkbenchWindow) stream.next();
window.setVisible(true);
}
if (projectToOpen != null) {
this.nodeManager.open(projectToOpen, window.getContext());
}
else if (generalPreferences().getBoolean(REOPEN_PROJECTS_PREFERENCE, REOPEN_PROJECTS_PREFERENCE_DEFAULT)) {
this.nodeManager.restoreProjectsState(window, generalPreferences().node("windows").node("window" + i));
}
this.redirectSystemStreams();
this.configureLoggerForExecution();
this.launchCompleteFlag.setTrue();
}
/**
* Once we are executing with a GUI, redirect the system streams.
* In "development" mode we "tee" the output streams to the
* "development console" and the "standard console";
* in "production" mode we re-direct the streams to a stream that
* throws an exception if anything is written to it (we will log this
* exception and notify the user).
* Changed this because of some bugs we were encountering where help
* was writing to System.err. The InvalidOuputStream was causing
* difficult debugging and causing the process to halt making progressing
* beyond it impossible. LDD
*/
private void redirectSystemStreams() {
System.setIn(InvalidInputStream.instance());
if (this.isDevelopmentMode()) {
this.frameworkConsole = new Console();
System.setOut(new PrintStream(new TeeOutputStream(System.out, this.frameworkConsole.getOutStream())));
System.setErr(new PrintStream(new TeeOutputStream(System.err, this.frameworkConsole.getErrStream())));
} else {
this.frameworkConsole = new FrameworkConsole(this.getRootApplicationContext());
System.setOut(new PrintStream(this.frameworkConsole.getOutStream()));
System.setErr(new PrintStream(this.frameworkConsole.getErrStream()));
}
}
/**
* Once we are executing with a GUI, we can stop forwarding
* log records to the root console handler and add a more
* "user-friendly" handler.
*/
private void configureLoggerForExecution() {
// at this point the root console handler is using the old,
// invalid, System.err and is pretty much useless
this.logger.setUseParentHandlers(false);
if (this.isDevelopmentMode()) {
// this will print all entries to the "development console"
this.logger.addHandler(new ConsoleHandler());
} else {
// any SEVERE log entries (typically unhandled exceptions)
// will be displayed to the user via a dialog
this.logger.addHandler(new FrameworkLogHandler(this, Level.SEVERE));
}
}
ApplicationNode open(File file, WorkbenchContext context) throws UnsupportedFileException, OpenException {
for (int i = 0; i < this.plugins.length; i++) {
try {
return this.plugins[i].open(file, context);
} catch (UnsupportedFileException ex) {
continue; // try the next plug-in
}
}
throw new UnsupportedFileException("could not find plug-in support for file: " + file);
}
void openNewWindow(WorkbenchWindow currentWindow) {
WorkbenchWindow window = new WorkbenchWindow(this, currentWindow);
this.workbenchWindows.add(window);
window.setVisible(true);
}
void openDevelopmentConsole() {
if (this.frameworkConsole == null) {
throw new IllegalStateException("This operation is only supported in \"development\" mode.");
}
this.frameworkConsole.open();
}
private AbstractPreferencesNode buildRootPreferencesNode(PreferencesContext context) {
AbstractPreferencesNode root = new RootPreferencesNode(context);
int childIndex = 0;
root.insert(new GeneralPreferencesNode(context), childIndex++);
for (int i = 0; i < this.plugins.length; i++) {
PreferencesNode[] nodes = this.plugins[i].buildPreferencesNodes(context);
for (int j = 0; j < nodes.length; j++) {
root.insert(nodes[j], childIndex++);
}
}
return root;
}
void close(WorkbenchWindow window) {
// if this is the last window, exit the application
if (this.workbenchWindows.size() == 1) {
// don't remove the last window, because the node manager will prompt
// the user to save any unsaved projects, at which point the user can
// cancel the exit instead of saving the projects
if (this.workbenchWindows.iterator().next() != window) {
throw new IllegalStateException("unknown window: " + window);
}
this.nodeManager.exit(window.getContext());
} else {
if ( ! this.workbenchWindows.remove(window)) {
throw new IllegalStateException("unknown window: " + window);
}
window.dispose();
}
}
/**
* This method is called from FrameworkNodeManager.exit(WorkbenchContext)
* once the user is prompted to save any unsaved projects.
*/
void exit() {
this.saveWindowsState();
for (Iterator stream = this.workbenchWindows.iterator(); stream.hasNext(); ) {
((WorkbenchWindow) stream.next()).dispose();
}
this.shutDown();
}
private void saveWindowsState() {
Preferences windowsPreferences = generalPreferences().node("windows");
try {
windowsPreferences.clear();
} catch (BackingStoreException e) {
//do nothing if this occurs
}
windowsPreferences.putInt("count", this.workbenchWindows.size());
int i = 0;
for (Iterator stream = this.workbenchWindows.iterator(); stream.hasNext(); i++) {
WorkbenchWindow window = (WorkbenchWindow) stream.next();
window.saveState(windowsPreferences.node("window" + i));
window.dispose();
}
}
protected void saveTreeExpansionStates() {
Preferences windowsPreferences = generalPreferences().node("windows");
int i = 0;
for (Iterator stream = this.workbenchWindows.iterator(); stream.hasNext(); i++) {
WorkbenchWindow window = (WorkbenchWindow) stream.next();
window.saveTreeExpansionState(windowsPreferences.node("window" + i));
}
}
private void shutDown() {
this.getHelpManager().shutDown();
// allow all the outstanding UI events to be handled before shutting down
EventQueue.invokeLater(
new Runnable() {
public void run() {
System.exit(0);
}
public String toString() {
return "runnable shutdown";
}
}
);
}
// ********** Help **********
/**
* Return the application's OHJ context-sensitive help manager.
* If necessary, wait for the initialization to complete.
*/
HelpManager getHelpManager() {
// once the help mgr is initialized, it should never change back to null
try {
this.synchronizedHelpManager.waitUntilNotNull();
} catch (InterruptedException ex) {
throw new RuntimeException(ex); // shouldn't happen
}
return (HelpManager) this.synchronizedHelpManager.getValue();
}
// ********** Application implementation **********
/**
* @see org.eclipse.persistence.tools.workbench.framework.Application#isFirstExecution()
*/
public boolean isFirstExecution() {
return this.firstExecution;
}
/**
* Allow the user to force "development mode" via a command line argument, "-dev".
* @see org.eclipse.persistence.tools.workbench.framework.AbstractApplication#isDevelopmentMode()
*/
public boolean isDevelopmentMode() {
return this.developmentMode || super.isDevelopmentMode();
}
// ********** AbstractApplication implementation **********
/**
* @see org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults#defaultSpecificationTitle()
*/
public String defaultSpecificationTitle() {
return "EclipseLink Workbench";
}
/**
* @see org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults#defaultSpecificationVendor()
*/
public String defaultSpecificationVendor() {
return "";
}
/**
* @see org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults#defaultReleaseDesignation()
*/
public String defaultReleaseDesignation() {
return "Version 1.0.0";
}
/**
* @see org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults#defaultLibraryDesignation()
*/
public String defaultLibraryDesignation() {
return "Workbench";
}
/**
* @see org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults#defaultSpecificationVersion()
*/
public String defaultSpecificationVersion() {
return "1.0.0";
}
/**
* @see org.eclipse.persistence.tools.workbench.utility.ManifestInterrogator.Defaults#defaultImplementationVersion()
*/
public String defaultImplementationVersion() {
return this.defaultSpecificationVersion();
}
// ********** nested classes **********
/**
* This is the root application context that all other application contexts
* and workbench contexts extend. All methods delegate to the application
*/
private class RootApplicationContext
extends AbstractApplicationContext
{
public Application getApplication() {
return FrameworkApplication.this;
}
public HelpManager getHelpManager() {
return FrameworkApplication.this.getHelpManager();
}
public NodeManager getNodeManager() {
return FrameworkApplication.this.getNodeManager();
}
public Preferences getPreferences() {
return FrameworkApplication.this.getRootPreferences();
}
public ResourceRepository getResourceRepository() {
return FrameworkApplication.this.getResourceRepository();
}
}
/**
* This class holds the information needed by the application to acquire
* a plug-in factory: the name of a class and the name of a static method
* implemented by that class that will return an instance of PluginFactory.
*/
private static class PluginFactoryStaticMethodSpec {
private String className;
private String staticMethodName;
PluginFactoryStaticMethodSpec(String className, String staticMethodName) {
super();
this.className = className;
this.staticMethodName = staticMethodName;
}
String getClassName() {
return this.className;
}
String getStaticMethodName() {
return this.staticMethodName;
}
}
/**
* The root preferences node should never be visible or selected.
*/
private static final class RootPreferencesNode extends AbstractPreferencesNode {
RootPreferencesNode(PreferencesContext context) {
super(context);
}
protected Component buildPropertiesPage() {
return null;
}
protected String buildDisplayString() {
return null;
}
}
}