-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.workbench.internal;\r
-\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.OutputStream;\r
-import java.net.MalformedURLException;\r
-import java.net.URL;\r
-import java.util.Map;\r
-import java.util.Properties;\r
-\r
-import org.eclipse.core.runtime.IConfigurationElement;\r
-import org.eclipse.core.runtime.IExecutableExtension;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Platform;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.equinox.app.IApplication;\r
-import org.eclipse.equinox.app.IApplicationContext;\r
-import org.eclipse.jface.dialogs.Dialog;\r
-import org.eclipse.jface.dialogs.MessageDialog;\r
-import org.eclipse.osgi.service.datalocation.Location;\r
-import org.eclipse.osgi.util.NLS;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.MessageBox;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.ui.IWorkbench;\r
-import org.eclipse.ui.PlatformUI;\r
-import org.eclipse.ui.application.WorkbenchAdvisor;\r
-import org.eclipse.ui.internal.WorkbenchPlugin;\r
-import org.eclipse.ui.internal.ide.ChooseWorkspaceData;\r
-import org.eclipse.ui.internal.ide.ChooseWorkspaceDialog;\r
-import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;\r
-import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;\r
-import org.eclipse.ui.internal.ide.StatusUtil;\r
-import org.simantics.application.arguments.ApplicationUtils;\r
-import org.simantics.application.arguments.Arguments;\r
-import org.simantics.application.arguments.IArgumentFactory;\r
-import org.simantics.application.arguments.IArguments;\r
-import org.simantics.application.arguments.SimanticsArguments;\r
-import org.simantics.db.management.ISessionContextProvider;\r
-import org.simantics.db.management.ISessionContextProviderSource;\r
-import org.simantics.db.management.SessionContextProvider;\r
-import org.simantics.db.management.SingleSessionContextProviderSource;\r
-import org.simantics.ui.SimanticsUI;\r
-import org.simantics.ui.WorkbenchWindowSessionContextProviderSource;\r
-import org.simantics.utils.ui.BundleUtils;\r
-\r
-\r
-/**\r
- * The "main program" for the Eclipse IDE.\r
- * \r
- * @since 3.0\r
- */\r
-public class SimanticsWorkbenchApplication implements IApplication, IExecutableExtension {\r
-\r
- /**\r
- * The name of the folder containing metadata information for the workspace.\r
- */\r
- public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$\r
-\r
- private static final String VERSION_FILENAME = "version.ini"; //$NON-NLS-1$\r
-\r
- private static final String WORKSPACE_VERSION_KEY = "org.eclipse.core.runtime"; //$NON-NLS-1$\r
-\r
- private static final String WORKSPACE_VERSION_VALUE = "1"; //$NON-NLS-1$\r
-\r
- private static final String PROP_EXIT_CODE = "eclipse.exitcode"; //$NON-NLS-1$\r
-\r
- /**\r
- * A special return code that will be recognized by the launcher and used to\r
- * restart the workbench.\r
- */\r
- private static final Integer EXIT_RELAUNCH = new Integer(24);\r
-\r
- /**\r
- * A special return code that will be recognized by the PDE launcher and used to\r
- * show an error dialog if the workspace is locked.\r
- */\r
- private static final Integer EXIT_WORKSPACE_LOCKED = new Integer(15);\r
-\r
- /**\r
- * Creates a new IDE application.\r
- */\r
- public SimanticsWorkbenchApplication() {\r
- // There is nothing to do for WorkbenchApplication\r
- }\r
-\r
- public WorkbenchAdvisor createWorkbenchAdvisor(IArguments args, DelayedEventsProcessor processor) {\r
- return new SimanticsWorkbenchAdvisor(args, processor);\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext context)\r
- */\r
- @Override\r
- public Object start(IApplicationContext appContext) throws Exception {\r
- ApplicationUtils.loadSystemProperties(BundleUtils.find(Activator.PLUGIN_ID, "system.properties"));\r
- IArguments args = parseArguments((String[]) appContext.getArguments().get(IApplicationContext.APPLICATION_ARGS));\r
-\r
- Display display = createDisplay();\r
- // processor must be created before we start event loop\r
- DelayedEventsProcessor processor = new DelayedEventsProcessor(display);\r
-\r
- try {\r
- Object argCheck = verifyArguments(args);\r
- if (argCheck != null)\r
- return argCheck;\r
-\r
- // look and see if there's a splash shell we can parent off of\r
- Shell shell = WorkbenchPlugin.getSplashShell(display);\r
- if (shell != null) {\r
- // should should set the icon and message for this shell to be the \r
- // same as the chooser dialog - this will be the guy that lives in\r
- // the task bar and without these calls you'd have the default icon \r
- // with no message.\r
- shell.setText(ChooseWorkspaceDialog.getWindowTitle());\r
- shell.setImages(Dialog.getDefaultImages());\r
- }\r
-\r
- Object instanceLocationCheck = checkInstanceLocation(shell, appContext.getArguments(), args);\r
- if (instanceLocationCheck != null) {\r
- WorkbenchPlugin.unsetSplashShell(display);\r
- Platform.endSplash();\r
- return instanceLocationCheck;\r
- }\r
-\r
- final ISessionContextProvider provider = new SessionContextProvider(null);\r
- final ISessionContextProviderSource contextProviderSource = new SingleSessionContextProviderSource(provider);\r
- //final ISessionContextProviderSource contextProviderSource = new WorkbenchWindowSessionContextProviderSource(PlatformUI.getWorkbench());\r
- SimanticsUI.setSessionContextProviderSource(contextProviderSource);\r
- org.simantics.db.layer0.internal.SimanticsInternal.setSessionContextProviderSource(contextProviderSource);\r
- org.simantics.Simantics.setSessionContextProviderSource(contextProviderSource);\r
- \r
- // create the workbench with this advisor and run it until it exits\r
- // N.B. createWorkbench remembers the advisor, and also registers\r
- // the workbench globally so that all UI plug-ins can find it using\r
- // PlatformUI.getWorkbench() or AbstractUIPlugin.getWorkbench()\r
- int returnCode = PlatformUI.createAndRunWorkbench(display,\r
- createWorkbenchAdvisor(args, processor));\r
-\r
- // the workbench doesn't support relaunch yet (bug 61809) so\r
- // for now restart is used, and exit data properties are checked\r
- // here to substitute in the relaunch return code if needed\r
- if (returnCode != PlatformUI.RETURN_RESTART) {\r
- return EXIT_OK;\r
- }\r
-\r
- // if the exit code property has been set to the relaunch code, then\r
- // return that code now, otherwise this is a normal restart\r
- return EXIT_RELAUNCH.equals(Integer.getInteger(PROP_EXIT_CODE)) ? EXIT_RELAUNCH\r
- : EXIT_RESTART;\r
- } finally {\r
- if (display != null) {\r
- display.dispose();\r
- }\r
- Location instanceLoc = Platform.getInstanceLocation();\r
- if (instanceLoc != null)\r
- instanceLoc.release();\r
- }\r
- }\r
-\r
- /*************************************************************************/\r
-\r
- private IArguments parseArguments(String[] args) {\r
- IArgumentFactory<?>[] accepted = {\r
- SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS,\r
- SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL,\r
- SimanticsArguments.DEFAULT_WORKSPACE_LOCATION,\r
- SimanticsArguments.WORKSPACE_CHOOSER,\r
- SimanticsArguments.WORKSPACE_NO_REMEMBER,\r
- SimanticsArguments.PERSPECTIVE,\r
- SimanticsArguments.SERVER,\r
- SimanticsArguments.NEW_MODEL,\r
- SimanticsArguments.EXPERIMENT,\r
- SimanticsArguments.DISABLE_INDEX,\r
- SimanticsArguments.DATABASE_ID,\r
- };\r
- IArguments result = Arguments.parse(args, accepted);\r
- return result;\r
- }\r
-\r
- private Object verifyArguments(IArguments args) {\r
- StringBuilder report = new StringBuilder();\r
-\r
-// if (args.contains(SimanticsArguments.NEW_PROJECT)) {\r
-// if (args.contains(SimanticsArguments.PROJECT)) {\r
-// exclusiveArguments(report, SimanticsArguments.PROJECT, SimanticsArguments.NEW_PROJECT);\r
-// }\r
-// // Must have a server to checkout from when creating a new\r
-// // project right from the beginning.\r
-// if (!args.contains(SimanticsArguments.SERVER)) {\r
-// missingArgument(report, SimanticsArguments.SERVER);\r
-// }\r
-// } else if (args.contains(SimanticsArguments.PROJECT)) {\r
-// // To load a project, a server must be defined to checkout from\r
-// if (!args.contains(SimanticsArguments.SERVER)) {\r
-// missingArgument(report, SimanticsArguments.SERVER);\r
-// }\r
-// }\r
-\r
- // NEW_MODEL and MODEL arguments are optional\r
- // EXPERIMENT argument is optional\r
-\r
- String result = report.toString();\r
- boolean valid = result.length() == 0;\r
-\r
- if (!valid) {\r
- String msg = NLS.bind(Messages.Application_1, result);\r
- MessageDialog.openInformation(null, Messages.Application_2, msg);\r
- }\r
- return valid ? null : EXIT_OK;\r
- }\r
-\r
-// private void exclusiveArguments(StringBuilder sb, IArgumentFactory<?> arg1, IArgumentFactory<?> arg2) {\r
-// sb.append(NLS.bind(Messages.Application_3, arg1.getArgument(), arg2.getArgument()));\r
-// sb.append('\n');\r
-// }\r
-//\r
-// private void missingArgument(StringBuilder sb, IArgumentFactory<?> arg) {\r
-// sb.append(NLS.bind(Messages.Application_0, arg.getArgument()));\r
-// sb.append('\n');\r
-// }\r
-\r
- /*************************************************************************/\r
-\r
- /**\r
- * Creates the display used by the application.\r
- * \r
- * @return the display used by the application\r
- */\r
- protected Display createDisplay() {\r
- return PlatformUI.createDisplay();\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)\r
- */\r
- @Override\r
- public void setInitializationData(IConfigurationElement config,\r
- String propertyName, Object data) {\r
- // There is nothing to do for ProConfApplication\r
- }\r
-\r
- /**\r
- * Return true if a valid workspace path has been set and false otherwise.\r
- * Prompt for and set the path if possible and required.\r
- * @param applicationArguments \r
- * \r
- * @return true if a valid instance location has been set and false\r
- * otherwise\r
- */\r
- private Object checkInstanceLocation(Shell shell, Map<?,?> applicationArguments, IArguments args) {\r
- // -data @none was specified but an ide requires workspace\r
- Location instanceLoc = Platform.getInstanceLocation();\r
- if (instanceLoc == null) {\r
- MessageDialog\r
- .openError(\r
- shell,\r
- IDEWorkbenchMessages.IDEApplication_workspaceMandatoryTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceMandatoryMessage);\r
- return EXIT_OK;\r
- }\r
-\r
- // -data "/valid/path", workspace already set\r
- // This information is stored in configuration/.settings/org.eclipse.ui.ide.prefs\r
- if (instanceLoc.isSet()) {\r
- // make sure the meta data version is compatible (or the user has\r
- // chosen to overwrite it).\r
- if (!checkValidWorkspace(shell, instanceLoc.getURL())) {\r
- return EXIT_OK;\r
- }\r
-\r
- // at this point its valid, so try to lock it and update the\r
- // metadata version information if successful\r
- try {\r
- if (instanceLoc.lock()) {\r
- writeWorkspaceVersion();\r
- return null;\r
- }\r
-\r
- // we failed to create the directory.\r
- // Two possibilities:\r
- // 1. directory is already in use\r
- // 2. directory could not be created\r
- File workspaceDirectory = new File(instanceLoc.getURL().getFile());\r
- if (workspaceDirectory.exists()) {\r
- if (isDevLaunchMode(applicationArguments)) {\r
- return EXIT_WORKSPACE_LOCKED;\r
- }\r
- MessageDialog.openError(\r
- shell,\r
- IDEWorkbenchMessages.IDEApplication_workspaceCannotLockTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage);\r
- } else {\r
- MessageDialog.openError(\r
- shell,\r
- IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);\r
- }\r
- } catch (IOException e) {\r
- IDEWorkbenchPlugin.log("Could not obtain lock for workspace location", //$NON-NLS-1$\r
- e);\r
- MessageDialog\r
- .openError(\r
- shell,\r
- IDEWorkbenchMessages.InternalError,\r
- e.getMessage());\r
- }\r
- return EXIT_OK;\r
- }\r
-\r
- // -data @noDefault or -data not specified, prompt and set\r
- ChooseWorkspaceData launchData = null;\r
- if (args.contains(SimanticsArguments.DEFAULT_WORKSPACE_LOCATION)) {\r
- launchData = new ChooseWorkspaceData(args.get(SimanticsArguments.DEFAULT_WORKSPACE_LOCATION));\r
- } else {\r
- launchData = new ChooseWorkspaceData(instanceLoc.getDefault());\r
- }\r
-\r
- boolean force = args.contains(SimanticsArguments.WORKSPACE_CHOOSER);\r
- boolean suppressAskAgain = args.contains(SimanticsArguments.WORKSPACE_NO_REMEMBER);\r
-\r
- while (true) {\r
- URL workspaceUrl = promptForWorkspace(shell, launchData, force, suppressAskAgain);\r
- if (workspaceUrl == null) {\r
- return EXIT_OK;\r
- }\r
-\r
- // if there is an error with the first selection, then force the\r
- // dialog to open to give the user a chance to correct\r
- force = true;\r
-\r
- try {\r
- // the operation will fail if the url is not a valid\r
- // instance data area, so other checking is unneeded\r
- if (instanceLoc.setURL(workspaceUrl, true)) {\r
- launchData.writePersistedData();\r
- writeWorkspaceVersion();\r
- return null;\r
- }\r
- } catch (IllegalStateException e) {\r
- MessageDialog\r
- .openError(\r
- shell,\r
- IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);\r
- return EXIT_OK;\r
- }\r
-\r
- // by this point it has been determined that the workspace is\r
- // already in use -- force the user to choose again\r
- MessageDialog.openError(shell, IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage);\r
- }\r
- }\r
-\r
- private static boolean isDevLaunchMode(Map<?,?> args) {\r
- // see org.eclipse.pde.internal.core.PluginPathFinder.isDevLaunchMode()\r
- if (Boolean.getBoolean("eclipse.pde.launch")) //$NON-NLS-1$\r
- return true;\r
- return args.containsKey("-pdelaunch"); //$NON-NLS-1$\r
- }\r
-\r
- /**\r
- * Open a workspace selection dialog on the argument shell, populating the\r
- * argument data with the user's selection. Perform first level validation\r
- * on the selection by comparing the version information. This method does\r
- * not examine the runtime state (e.g., is the workspace already locked?).\r
- * \r
- * @param shell\r
- * @param launchData\r
- * @param force\r
- * setting to true makes the dialog open regardless of the\r
- * showDialog value\r
- * @return An URL storing the selected workspace or null if the user has\r
- * canceled the launch operation.\r
- */\r
- private URL promptForWorkspace(Shell shell, ChooseWorkspaceData launchData,\r
- boolean force, boolean suppressAskAgain) {\r
- URL url = null;\r
- do {\r
- // okay to use the shell now - this is the splash shell\r
- new ChooseWorkspaceDialog(shell, launchData, suppressAskAgain, true).prompt(force);\r
- String instancePath = launchData.getSelection();\r
- if (instancePath == null) {\r
- return null;\r
- }\r
-\r
- // the dialog is not forced on the first iteration, but is on every\r
- // subsequent one -- if there was an error then the user needs to be\r
- // allowed to fix it\r
- force = true;\r
-\r
- // 70576: don't accept empty input\r
- if (instancePath.length() <= 0) {\r
- MessageDialog\r
- .openError(\r
- shell,\r
- IDEWorkbenchMessages.IDEApplication_workspaceEmptyTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceEmptyMessage);\r
- continue;\r
- }\r
-\r
- // create the workspace if it does not already exist\r
- File workspace = new File(instancePath);\r
- if (!workspace.exists()) {\r
- workspace.mkdir();\r
- }\r
-\r
- try {\r
- // Don't use File.toURL() since it adds a leading slash that Platform does not\r
- // handle properly. See bug 54081 for more details.\r
- String path = workspace.getAbsolutePath().replace(\r
- File.separatorChar, '/');\r
- url = new URL("file", null, path); //$NON-NLS-1$\r
- } catch (MalformedURLException e) {\r
- MessageDialog\r
- .openError(\r
- shell,\r
- IDEWorkbenchMessages.IDEApplication_workspaceInvalidTitle,\r
- IDEWorkbenchMessages.IDEApplication_workspaceInvalidMessage);\r
- continue;\r
- }\r
- } while (!checkValidWorkspace(shell, url));\r
-\r
- return url;\r
- }\r
-\r
- /**\r
- * Return true if the argument directory is ok to use as a workspace and\r
- * false otherwise. A version check will be performed, and a confirmation\r
- * box may be displayed on the argument shell if an older version is\r
- * detected.\r
- * \r
- * @return true if the argument URL is ok to use as a workspace and false\r
- * otherwise.\r
- */\r
- private boolean checkValidWorkspace(Shell shell, URL url) {\r
- // a null url is not a valid workspace\r
- if (url == null) {\r
- return false;\r
- }\r
-\r
- String version = readWorkspaceVersion(url);\r
-\r
- // if the version could not be read, then there is not any existing\r
- // workspace data to trample, e.g., perhaps its a new directory that\r
- // is just starting to be used as a workspace\r
- if (version == null) {\r
- return true;\r
- }\r
-\r
- final int ide_version = Integer.parseInt(WORKSPACE_VERSION_VALUE);\r
- int workspace_version = Integer.parseInt(version);\r
-\r
- // equality test is required since any version difference (newer\r
- // or older) may result in data being trampled\r
- if (workspace_version == ide_version) {\r
- return true;\r
- }\r
-\r
- // At this point workspace has been detected to be from a version\r
- // other than the current ide version -- find out if the user wants\r
- // to use it anyhow.\r
- int severity;\r
- String title;\r
- String message;\r
- if (workspace_version < ide_version) {\r
- // Workspace < IDE. Update must be possible without issues,\r
- // so only inform user about it.\r
- severity = MessageDialog.INFORMATION;\r
- title = IDEWorkbenchMessages.IDEApplication_versionTitle_olderWorkspace;\r
- message = NLS.bind(IDEWorkbenchMessages.IDEApplication_versionMessage_olderWorkspace, url.getFile());\r
- } else {\r
- // Workspace > IDE. It must have been opened with a newer IDE version.\r
- // Downgrade might be problematic, so warn user about it.\r
- severity = MessageDialog.WARNING;\r
- title = IDEWorkbenchMessages.IDEApplication_versionTitle_newerWorkspace;\r
- message = NLS.bind(IDEWorkbenchMessages.IDEApplication_versionMessage_newerWorkspace, url.getFile());\r
- }\r
-\r
- MessageBox mbox = new MessageBox(shell, SWT.OK | SWT.CANCEL\r
- | SWT.ICON_WARNING | SWT.APPLICATION_MODAL);\r
- mbox.setText(title);\r
- mbox.setMessage(message);\r
- return mbox.open() == SWT.OK;\r
- }\r
-\r
- /**\r
- * Look at the argument URL for the workspace's version information. Return\r
- * that version if found and null otherwise.\r
- */\r
- private static String readWorkspaceVersion(URL workspace) {\r
- File versionFile = getVersionFile(workspace, false);\r
- if (versionFile == null || !versionFile.exists()) {\r
- return null;\r
- }\r
-\r
- try {\r
- // Although the version file is not spec'ed to be a Java properties\r
- // file, it happens to follow the same format currently, so using\r
- // Properties to read it is convenient.\r
- Properties props = new Properties();\r
- FileInputStream is = new FileInputStream(versionFile);\r
- try {\r
- props.load(is);\r
- } finally {\r
- is.close();\r
- }\r
-\r
- return props.getProperty(WORKSPACE_VERSION_KEY);\r
- } catch (IOException e) {\r
- IDEWorkbenchPlugin.log("Could not read version file", new Status( //$NON-NLS-1$\r
- IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH,\r
- IStatus.ERROR,\r
- e.getMessage() == null ? "" : e.getMessage(), //$NON-NLS-1$,\r
- e));\r
- return null;\r
- }\r
- }\r
-\r
- /**\r
- * Write the version of the metadata into a known file overwriting any\r
- * existing file contents. Writing the version file isn't really crucial,\r
- * so the function is silent about failure\r
- */\r
- private static void writeWorkspaceVersion() {\r
- Location instanceLoc = Platform.getInstanceLocation();\r
- if (instanceLoc == null || instanceLoc.isReadOnly()) {\r
- return;\r
- }\r
-\r
- File versionFile = getVersionFile(instanceLoc.getURL(), true);\r
- if (versionFile == null) {\r
- return;\r
- }\r
-\r
- OutputStream output = null;\r
- try {\r
- String versionLine = WORKSPACE_VERSION_KEY + '='\r
- + WORKSPACE_VERSION_VALUE;\r
-\r
- output = new FileOutputStream(versionFile);\r
- output.write(versionLine.getBytes("UTF-8")); //$NON-NLS-1$\r
- } catch (IOException e) {\r
- IDEWorkbenchPlugin.log("Could not write version file", //$NON-NLS-1$\r
- StatusUtil.newStatus(IStatus.ERROR, e.getMessage(), e));\r
- } finally {\r
- try {\r
- if (output != null) {\r
- output.close();\r
- }\r
- } catch (IOException e) {\r
- // do nothing\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * The version file is stored in the metadata area of the workspace. This\r
- * method returns an URL to the file or null if the directory or file does\r
- * not exist (and the create parameter is false).\r
- * \r
- * @param create\r
- * If the directory and file does not exist this parameter\r
- * controls whether it will be created.\r
- * @return An url to the file or null if the version file does not exist or\r
- * could not be created.\r
- */\r
- private static File getVersionFile(URL workspaceUrl, boolean create) {\r
- if (workspaceUrl == null) {\r
- return null;\r
- }\r
-\r
- try {\r
- // make sure the directory exists\r
- File metaDir = new File(workspaceUrl.getPath(), METADATA_FOLDER);\r
- if (!metaDir.exists() && (!create || !metaDir.mkdir())) {\r
- return null;\r
- }\r
-\r
- // make sure the file exists\r
- File versionFile = new File(metaDir, VERSION_FILENAME);\r
- if (!versionFile.exists()\r
- && (!create || !versionFile.createNewFile())) {\r
- return null;\r
- }\r
-\r
- return versionFile;\r
- } catch (IOException e) {\r
- // cannot log because instance area has not been set\r
- return null;\r
- }\r
- }\r
-\r
- /* (non-Javadoc)\r
- * @see org.eclipse.equinox.app.IApplication#stop()\r
- */\r
- @Override\r
- public void stop() {\r
- final IWorkbench workbench = PlatformUI.getWorkbench();\r
- if (workbench == null)\r
- return;\r
- final Display display = workbench.getDisplay();\r
- display.syncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!display.isDisposed())\r
- workbench.close();\r
- }\r
- });\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.workbench.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.application.WorkbenchAdvisor;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.ide.ChooseWorkspaceData;
+import org.eclipse.ui.internal.ide.ChooseWorkspaceDialog;
+import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
+import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
+import org.eclipse.ui.internal.ide.StatusUtil;
+import org.simantics.application.arguments.ApplicationUtils;
+import org.simantics.application.arguments.Arguments;
+import org.simantics.application.arguments.IArgumentFactory;
+import org.simantics.application.arguments.IArguments;
+import org.simantics.application.arguments.SimanticsArguments;
+import org.simantics.db.management.ISessionContextProvider;
+import org.simantics.db.management.ISessionContextProviderSource;
+import org.simantics.db.management.SessionContextProvider;
+import org.simantics.db.management.SingleSessionContextProviderSource;
+import org.simantics.ui.SimanticsUI;
+import org.simantics.utils.ui.BundleUtils;
+
+
+/**
+ * The "main program" for the Eclipse IDE.
+ *
+ * @since 3.0
+ */
+public class SimanticsWorkbenchApplication implements IApplication, IExecutableExtension {
+
+ /**
+ * The name of the folder containing metadata information for the workspace.
+ */
+ public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$
+
+ private static final String VERSION_FILENAME = "version.ini"; //$NON-NLS-1$
+
+ private static final String WORKSPACE_VERSION_KEY = "org.eclipse.core.runtime"; //$NON-NLS-1$
+
+ private static final String WORKSPACE_VERSION_VALUE = "1"; //$NON-NLS-1$
+
+ private static final String PROP_EXIT_CODE = "eclipse.exitcode"; //$NON-NLS-1$
+
+ /**
+ * A special return code that will be recognized by the launcher and used to
+ * restart the workbench.
+ */
+ private static final Integer EXIT_RELAUNCH = new Integer(24);
+
+ /**
+ * A special return code that will be recognized by the PDE launcher and used to
+ * show an error dialog if the workspace is locked.
+ */
+ private static final Integer EXIT_WORKSPACE_LOCKED = new Integer(15);
+
+ /**
+ * Creates a new IDE application.
+ */
+ public SimanticsWorkbenchApplication() {
+ // There is nothing to do for WorkbenchApplication
+ }
+
+ public WorkbenchAdvisor createWorkbenchAdvisor(IArguments args, DelayedEventsProcessor processor) {
+ return new SimanticsWorkbenchAdvisor(args, processor);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext context)
+ */
+ @Override
+ public Object start(IApplicationContext appContext) throws Exception {
+ ApplicationUtils.loadSystemProperties(BundleUtils.find(Activator.PLUGIN_ID, "system.properties"));
+ IArguments args = parseArguments((String[]) appContext.getArguments().get(IApplicationContext.APPLICATION_ARGS));
+
+ Display display = createDisplay();
+ // processor must be created before we start event loop
+ DelayedEventsProcessor processor = new DelayedEventsProcessor(display);
+
+ try {
+ Object argCheck = verifyArguments(args);
+ if (argCheck != null)
+ return argCheck;
+
+ // look and see if there's a splash shell we can parent off of
+ Shell shell = WorkbenchPlugin.getSplashShell(display);
+ if (shell != null) {
+ // should should set the icon and message for this shell to be the
+ // same as the chooser dialog - this will be the guy that lives in
+ // the task bar and without these calls you'd have the default icon
+ // with no message.
+ shell.setText(ChooseWorkspaceDialog.getWindowTitle());
+ shell.setImages(Dialog.getDefaultImages());
+ }
+
+ Object instanceLocationCheck = checkInstanceLocation(shell, appContext.getArguments(), args);
+ if (instanceLocationCheck != null) {
+ WorkbenchPlugin.unsetSplashShell(display);
+ Platform.endSplash();
+ return instanceLocationCheck;
+ }
+
+ final ISessionContextProvider provider = new SessionContextProvider(null);
+ final ISessionContextProviderSource contextProviderSource = new SingleSessionContextProviderSource(provider);
+ //final ISessionContextProviderSource contextProviderSource = new WorkbenchWindowSessionContextProviderSource(PlatformUI.getWorkbench());
+ SimanticsUI.setSessionContextProviderSource(contextProviderSource);
+ org.simantics.db.layer0.internal.SimanticsInternal.setSessionContextProviderSource(contextProviderSource);
+ org.simantics.Simantics.setSessionContextProviderSource(contextProviderSource);
+
+ // create the workbench with this advisor and run it until it exits
+ // N.B. createWorkbench remembers the advisor, and also registers
+ // the workbench globally so that all UI plug-ins can find it using
+ // PlatformUI.getWorkbench() or AbstractUIPlugin.getWorkbench()
+ int returnCode = PlatformUI.createAndRunWorkbench(display,
+ createWorkbenchAdvisor(args, processor));
+
+ // the workbench doesn't support relaunch yet (bug 61809) so
+ // for now restart is used, and exit data properties are checked
+ // here to substitute in the relaunch return code if needed
+ if (returnCode != PlatformUI.RETURN_RESTART) {
+ return EXIT_OK;
+ }
+
+ // if the exit code property has been set to the relaunch code, then
+ // return that code now, otherwise this is a normal restart
+ return EXIT_RELAUNCH.equals(Integer.getInteger(PROP_EXIT_CODE)) ? EXIT_RELAUNCH
+ : EXIT_RESTART;
+ } finally {
+ if (display != null) {
+ display.dispose();
+ }
+ Location instanceLoc = Platform.getInstanceLocation();
+ if (instanceLoc != null)
+ instanceLoc.release();
+ }
+ }
+
+ /*************************************************************************/
+
+ private IArguments parseArguments(String[] args) {
+ IArgumentFactory<?>[] accepted = {
+ SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS,
+ SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL,
+ SimanticsArguments.DEFAULT_WORKSPACE_LOCATION,
+ SimanticsArguments.WORKSPACE_CHOOSER,
+ SimanticsArguments.WORKSPACE_NO_REMEMBER,
+ SimanticsArguments.PERSPECTIVE,
+ SimanticsArguments.SERVER,
+ SimanticsArguments.NEW_MODEL,
+ SimanticsArguments.EXPERIMENT,
+ SimanticsArguments.DISABLE_INDEX,
+ SimanticsArguments.DATABASE_ID,
+ };
+ IArguments result = Arguments.parse(args, accepted);
+ return result;
+ }
+
+ private Object verifyArguments(IArguments args) {
+ StringBuilder report = new StringBuilder();
+
+// if (args.contains(SimanticsArguments.NEW_PROJECT)) {
+// if (args.contains(SimanticsArguments.PROJECT)) {
+// exclusiveArguments(report, SimanticsArguments.PROJECT, SimanticsArguments.NEW_PROJECT);
+// }
+// // Must have a server to checkout from when creating a new
+// // project right from the beginning.
+// if (!args.contains(SimanticsArguments.SERVER)) {
+// missingArgument(report, SimanticsArguments.SERVER);
+// }
+// } else if (args.contains(SimanticsArguments.PROJECT)) {
+// // To load a project, a server must be defined to checkout from
+// if (!args.contains(SimanticsArguments.SERVER)) {
+// missingArgument(report, SimanticsArguments.SERVER);
+// }
+// }
+
+ // NEW_MODEL and MODEL arguments are optional
+ // EXPERIMENT argument is optional
+
+ String result = report.toString();
+ boolean valid = result.length() == 0;
+
+ if (!valid) {
+ String msg = NLS.bind(Messages.Application_1, result);
+ MessageDialog.openInformation(null, Messages.Application_2, msg);
+ }
+ return valid ? null : EXIT_OK;
+ }
+
+// private void exclusiveArguments(StringBuilder sb, IArgumentFactory<?> arg1, IArgumentFactory<?> arg2) {
+// sb.append(NLS.bind(Messages.Application_3, arg1.getArgument(), arg2.getArgument()));
+// sb.append('\n');
+// }
+//
+// private void missingArgument(StringBuilder sb, IArgumentFactory<?> arg) {
+// sb.append(NLS.bind(Messages.Application_0, arg.getArgument()));
+// sb.append('\n');
+// }
+
+ /*************************************************************************/
+
+ /**
+ * Creates the display used by the application.
+ *
+ * @return the display used by the application
+ */
+ protected Display createDisplay() {
+ return PlatformUI.createDisplay();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void setInitializationData(IConfigurationElement config,
+ String propertyName, Object data) {
+ // There is nothing to do for ProConfApplication
+ }
+
+ /**
+ * Return true if a valid workspace path has been set and false otherwise.
+ * Prompt for and set the path if possible and required.
+ * @param applicationArguments
+ *
+ * @return true if a valid instance location has been set and false
+ * otherwise
+ */
+ private Object checkInstanceLocation(Shell shell, Map<?,?> applicationArguments, IArguments args) {
+ // -data @none was specified but an ide requires workspace
+ Location instanceLoc = Platform.getInstanceLocation();
+ if (instanceLoc == null) {
+ MessageDialog
+ .openError(
+ shell,
+ IDEWorkbenchMessages.IDEApplication_workspaceMandatoryTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceMandatoryMessage);
+ return EXIT_OK;
+ }
+
+ // -data "/valid/path", workspace already set
+ // This information is stored in configuration/.settings/org.eclipse.ui.ide.prefs
+ if (instanceLoc.isSet()) {
+ // make sure the meta data version is compatible (or the user has
+ // chosen to overwrite it).
+ if (!checkValidWorkspace(shell, instanceLoc.getURL())) {
+ return EXIT_OK;
+ }
+
+ // at this point its valid, so try to lock it and update the
+ // metadata version information if successful
+ try {
+ if (instanceLoc.lock()) {
+ writeWorkspaceVersion();
+ return null;
+ }
+
+ // we failed to create the directory.
+ // Two possibilities:
+ // 1. directory is already in use
+ // 2. directory could not be created
+ File workspaceDirectory = new File(instanceLoc.getURL().getFile());
+ if (workspaceDirectory.exists()) {
+ if (isDevLaunchMode(applicationArguments)) {
+ return EXIT_WORKSPACE_LOCKED;
+ }
+ MessageDialog.openError(
+ shell,
+ IDEWorkbenchMessages.IDEApplication_workspaceCannotLockTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage);
+ } else {
+ MessageDialog.openError(
+ shell,
+ IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);
+ }
+ } catch (IOException e) {
+ IDEWorkbenchPlugin.log("Could not obtain lock for workspace location", //$NON-NLS-1$
+ e);
+ MessageDialog
+ .openError(
+ shell,
+ IDEWorkbenchMessages.InternalError,
+ e.getMessage());
+ }
+ return EXIT_OK;
+ }
+
+ // -data @noDefault or -data not specified, prompt and set
+ ChooseWorkspaceData launchData = null;
+ if (args.contains(SimanticsArguments.DEFAULT_WORKSPACE_LOCATION)) {
+ launchData = new ChooseWorkspaceData(args.get(SimanticsArguments.DEFAULT_WORKSPACE_LOCATION));
+ } else {
+ launchData = new ChooseWorkspaceData(instanceLoc.getDefault());
+ }
+
+ boolean force = args.contains(SimanticsArguments.WORKSPACE_CHOOSER);
+ boolean suppressAskAgain = args.contains(SimanticsArguments.WORKSPACE_NO_REMEMBER);
+
+ while (true) {
+ URL workspaceUrl = promptForWorkspace(shell, launchData, force, suppressAskAgain);
+ if (workspaceUrl == null) {
+ return EXIT_OK;
+ }
+
+ // if there is an error with the first selection, then force the
+ // dialog to open to give the user a chance to correct
+ force = true;
+
+ try {
+ // the operation will fail if the url is not a valid
+ // instance data area, so other checking is unneeded
+ if (instanceLoc.setURL(workspaceUrl, true)) {
+ launchData.writePersistedData();
+ writeWorkspaceVersion();
+ return null;
+ }
+ } catch (IllegalStateException e) {
+ MessageDialog
+ .openError(
+ shell,
+ IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);
+ return EXIT_OK;
+ }
+
+ // by this point it has been determined that the workspace is
+ // already in use -- force the user to choose again
+ MessageDialog.openError(shell, IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage);
+ }
+ }
+
+ private static boolean isDevLaunchMode(Map<?,?> args) {
+ // see org.eclipse.pde.internal.core.PluginPathFinder.isDevLaunchMode()
+ if (Boolean.getBoolean("eclipse.pde.launch")) //$NON-NLS-1$
+ return true;
+ return args.containsKey("-pdelaunch"); //$NON-NLS-1$
+ }
+
+ /**
+ * Open a workspace selection dialog on the argument shell, populating the
+ * argument data with the user's selection. Perform first level validation
+ * on the selection by comparing the version information. This method does
+ * not examine the runtime state (e.g., is the workspace already locked?).
+ *
+ * @param shell
+ * @param launchData
+ * @param force
+ * setting to true makes the dialog open regardless of the
+ * showDialog value
+ * @return An URL storing the selected workspace or null if the user has
+ * canceled the launch operation.
+ */
+ private URL promptForWorkspace(Shell shell, ChooseWorkspaceData launchData,
+ boolean force, boolean suppressAskAgain) {
+ URL url = null;
+ do {
+ // okay to use the shell now - this is the splash shell
+ new ChooseWorkspaceDialog(shell, launchData, suppressAskAgain, true).prompt(force);
+ String instancePath = launchData.getSelection();
+ if (instancePath == null) {
+ return null;
+ }
+
+ // the dialog is not forced on the first iteration, but is on every
+ // subsequent one -- if there was an error then the user needs to be
+ // allowed to fix it
+ force = true;
+
+ // 70576: don't accept empty input
+ if (instancePath.length() <= 0) {
+ MessageDialog
+ .openError(
+ shell,
+ IDEWorkbenchMessages.IDEApplication_workspaceEmptyTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceEmptyMessage);
+ continue;
+ }
+
+ // create the workspace if it does not already exist
+ File workspace = new File(instancePath);
+ if (!workspace.exists()) {
+ workspace.mkdir();
+ }
+
+ try {
+ // Don't use File.toURL() since it adds a leading slash that Platform does not
+ // handle properly. See bug 54081 for more details.
+ String path = workspace.getAbsolutePath().replace(
+ File.separatorChar, '/');
+ url = new URL("file", null, path); //$NON-NLS-1$
+ } catch (MalformedURLException e) {
+ MessageDialog
+ .openError(
+ shell,
+ IDEWorkbenchMessages.IDEApplication_workspaceInvalidTitle,
+ IDEWorkbenchMessages.IDEApplication_workspaceInvalidMessage);
+ continue;
+ }
+ } while (!checkValidWorkspace(shell, url));
+
+ return url;
+ }
+
+ /**
+ * Return true if the argument directory is ok to use as a workspace and
+ * false otherwise. A version check will be performed, and a confirmation
+ * box may be displayed on the argument shell if an older version is
+ * detected.
+ *
+ * @return true if the argument URL is ok to use as a workspace and false
+ * otherwise.
+ */
+ private boolean checkValidWorkspace(Shell shell, URL url) {
+ // a null url is not a valid workspace
+ if (url == null) {
+ return false;
+ }
+
+ String version = readWorkspaceVersion(url);
+
+ // if the version could not be read, then there is not any existing
+ // workspace data to trample, e.g., perhaps its a new directory that
+ // is just starting to be used as a workspace
+ if (version == null) {
+ return true;
+ }
+
+ final int ide_version = Integer.parseInt(WORKSPACE_VERSION_VALUE);
+ int workspace_version = Integer.parseInt(version);
+
+ // equality test is required since any version difference (newer
+ // or older) may result in data being trampled
+ if (workspace_version == ide_version) {
+ return true;
+ }
+
+ // At this point workspace has been detected to be from a version
+ // other than the current ide version -- find out if the user wants
+ // to use it anyhow.
+ int severity;
+ String title;
+ String message;
+ if (workspace_version < ide_version) {
+ // Workspace < IDE. Update must be possible without issues,
+ // so only inform user about it.
+ severity = MessageDialog.INFORMATION;
+ title = IDEWorkbenchMessages.IDEApplication_versionTitle_olderWorkspace;
+ message = NLS.bind(IDEWorkbenchMessages.IDEApplication_versionMessage_olderWorkspace, url.getFile());
+ } else {
+ // Workspace > IDE. It must have been opened with a newer IDE version.
+ // Downgrade might be problematic, so warn user about it.
+ severity = MessageDialog.WARNING;
+ title = IDEWorkbenchMessages.IDEApplication_versionTitle_newerWorkspace;
+ message = NLS.bind(IDEWorkbenchMessages.IDEApplication_versionMessage_newerWorkspace, url.getFile());
+ }
+
+ MessageBox mbox = new MessageBox(shell, SWT.OK | SWT.CANCEL
+ | SWT.ICON_WARNING | SWT.APPLICATION_MODAL);
+ mbox.setText(title);
+ mbox.setMessage(message);
+ return mbox.open() == SWT.OK;
+ }
+
+ /**
+ * Look at the argument URL for the workspace's version information. Return
+ * that version if found and null otherwise.
+ */
+ private static String readWorkspaceVersion(URL workspace) {
+ File versionFile = getVersionFile(workspace, false);
+ if (versionFile == null || !versionFile.exists()) {
+ return null;
+ }
+
+ try {
+ // Although the version file is not spec'ed to be a Java properties
+ // file, it happens to follow the same format currently, so using
+ // Properties to read it is convenient.
+ Properties props = new Properties();
+ FileInputStream is = new FileInputStream(versionFile);
+ try {
+ props.load(is);
+ } finally {
+ is.close();
+ }
+
+ return props.getProperty(WORKSPACE_VERSION_KEY);
+ } catch (IOException e) {
+ IDEWorkbenchPlugin.log("Could not read version file", new Status( //$NON-NLS-1$
+ IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH,
+ IStatus.ERROR,
+ e.getMessage() == null ? "" : e.getMessage(), //$NON-NLS-1$,
+ e));
+ return null;
+ }
+ }
+
+ /**
+ * Write the version of the metadata into a known file overwriting any
+ * existing file contents. Writing the version file isn't really crucial,
+ * so the function is silent about failure
+ */
+ private static void writeWorkspaceVersion() {
+ Location instanceLoc = Platform.getInstanceLocation();
+ if (instanceLoc == null || instanceLoc.isReadOnly()) {
+ return;
+ }
+
+ File versionFile = getVersionFile(instanceLoc.getURL(), true);
+ if (versionFile == null) {
+ return;
+ }
+
+ OutputStream output = null;
+ try {
+ String versionLine = WORKSPACE_VERSION_KEY + '='
+ + WORKSPACE_VERSION_VALUE;
+
+ output = new FileOutputStream(versionFile);
+ output.write(versionLine.getBytes("UTF-8")); //$NON-NLS-1$
+ } catch (IOException e) {
+ IDEWorkbenchPlugin.log("Could not write version file", //$NON-NLS-1$
+ StatusUtil.newStatus(IStatus.ERROR, e.getMessage(), e));
+ } finally {
+ try {
+ if (output != null) {
+ output.close();
+ }
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+
+ /**
+ * The version file is stored in the metadata area of the workspace. This
+ * method returns an URL to the file or null if the directory or file does
+ * not exist (and the create parameter is false).
+ *
+ * @param create
+ * If the directory and file does not exist this parameter
+ * controls whether it will be created.
+ * @return An url to the file or null if the version file does not exist or
+ * could not be created.
+ */
+ private static File getVersionFile(URL workspaceUrl, boolean create) {
+ if (workspaceUrl == null) {
+ return null;
+ }
+
+ try {
+ // make sure the directory exists
+ File metaDir = new File(workspaceUrl.getPath(), METADATA_FOLDER);
+ if (!metaDir.exists() && (!create || !metaDir.mkdir())) {
+ return null;
+ }
+
+ // make sure the file exists
+ File versionFile = new File(metaDir, VERSION_FILENAME);
+ if (!versionFile.exists()
+ && (!create || !versionFile.createNewFile())) {
+ return null;
+ }
+
+ return versionFile;
+ } catch (IOException e) {
+ // cannot log because instance area has not been set
+ return null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#stop()
+ */
+ @Override
+ public void stop() {
+ final IWorkbench workbench = PlatformUI.getWorkbench();
+ if (workbench == null)
+ return;
+ final Display display = workbench.getDisplay();
+ display.syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!display.isDisposed())
+ workbench.close();
+ }
+ });
+ }
+
+}