]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchApplication.java
Adding configuration logging for Simantics platform with SLF4J and
[simantics/platform.git] / bundles / org.simantics.workbench / src / org / simantics / workbench / internal / SimanticsWorkbenchApplication.java
index cc0cc024e3314925b24b4b31e607d71dce28e4f9..8b0431f38e9cde7d6236b146ab5d239034eca37a 100644 (file)
-/*******************************************************************************\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();
+            }
+        });
+    }
+
+}