]> gerrit.simantics Code Review - simantics/platform.git/blob
cc0cc024e3314925b24b4b31e607d71dce28e4f9
[simantics/platform.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.workbench.internal;\r
13 \r
14 import java.io.File;\r
15 import java.io.FileInputStream;\r
16 import java.io.FileOutputStream;\r
17 import java.io.IOException;\r
18 import java.io.OutputStream;\r
19 import java.net.MalformedURLException;\r
20 import java.net.URL;\r
21 import java.util.Map;\r
22 import java.util.Properties;\r
23 \r
24 import org.eclipse.core.runtime.IConfigurationElement;\r
25 import org.eclipse.core.runtime.IExecutableExtension;\r
26 import org.eclipse.core.runtime.IStatus;\r
27 import org.eclipse.core.runtime.Platform;\r
28 import org.eclipse.core.runtime.Status;\r
29 import org.eclipse.equinox.app.IApplication;\r
30 import org.eclipse.equinox.app.IApplicationContext;\r
31 import org.eclipse.jface.dialogs.Dialog;\r
32 import org.eclipse.jface.dialogs.MessageDialog;\r
33 import org.eclipse.osgi.service.datalocation.Location;\r
34 import org.eclipse.osgi.util.NLS;\r
35 import org.eclipse.swt.SWT;\r
36 import org.eclipse.swt.widgets.Display;\r
37 import org.eclipse.swt.widgets.MessageBox;\r
38 import org.eclipse.swt.widgets.Shell;\r
39 import org.eclipse.ui.IWorkbench;\r
40 import org.eclipse.ui.PlatformUI;\r
41 import org.eclipse.ui.application.WorkbenchAdvisor;\r
42 import org.eclipse.ui.internal.WorkbenchPlugin;\r
43 import org.eclipse.ui.internal.ide.ChooseWorkspaceData;\r
44 import org.eclipse.ui.internal.ide.ChooseWorkspaceDialog;\r
45 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;\r
46 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;\r
47 import org.eclipse.ui.internal.ide.StatusUtil;\r
48 import org.simantics.application.arguments.ApplicationUtils;\r
49 import org.simantics.application.arguments.Arguments;\r
50 import org.simantics.application.arguments.IArgumentFactory;\r
51 import org.simantics.application.arguments.IArguments;\r
52 import org.simantics.application.arguments.SimanticsArguments;\r
53 import org.simantics.db.management.ISessionContextProvider;\r
54 import org.simantics.db.management.ISessionContextProviderSource;\r
55 import org.simantics.db.management.SessionContextProvider;\r
56 import org.simantics.db.management.SingleSessionContextProviderSource;\r
57 import org.simantics.ui.SimanticsUI;\r
58 import org.simantics.ui.WorkbenchWindowSessionContextProviderSource;\r
59 import org.simantics.utils.ui.BundleUtils;\r
60 \r
61 \r
62 /**\r
63  * The "main program" for the Eclipse IDE.\r
64  * \r
65  * @since 3.0\r
66  */\r
67 public class SimanticsWorkbenchApplication implements IApplication, IExecutableExtension {\r
68 \r
69     /**\r
70      * The name of the folder containing metadata information for the workspace.\r
71      */\r
72     public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$\r
73 \r
74     private static final String VERSION_FILENAME = "version.ini"; //$NON-NLS-1$\r
75 \r
76     private static final String WORKSPACE_VERSION_KEY = "org.eclipse.core.runtime"; //$NON-NLS-1$\r
77 \r
78     private static final String WORKSPACE_VERSION_VALUE = "1"; //$NON-NLS-1$\r
79 \r
80     private static final String PROP_EXIT_CODE = "eclipse.exitcode"; //$NON-NLS-1$\r
81 \r
82     /**\r
83      * A special return code that will be recognized by the launcher and used to\r
84      * restart the workbench.\r
85      */\r
86     private static final Integer EXIT_RELAUNCH = new Integer(24);\r
87 \r
88     /**\r
89      * A special return code that will be recognized by the PDE launcher and used to\r
90      * show an error dialog if the workspace is locked.\r
91      */\r
92     private static final Integer EXIT_WORKSPACE_LOCKED = new Integer(15);\r
93 \r
94     /**\r
95      * Creates a new IDE application.\r
96      */\r
97     public SimanticsWorkbenchApplication() {\r
98         // There is nothing to do for WorkbenchApplication\r
99     }\r
100 \r
101     public WorkbenchAdvisor createWorkbenchAdvisor(IArguments args, DelayedEventsProcessor processor) {\r
102         return new SimanticsWorkbenchAdvisor(args, processor);\r
103     }\r
104 \r
105     /* (non-Javadoc)\r
106      * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext context)\r
107      */\r
108     @Override\r
109     public Object start(IApplicationContext appContext) throws Exception {\r
110         ApplicationUtils.loadSystemProperties(BundleUtils.find(Activator.PLUGIN_ID, "system.properties"));\r
111         IArguments args = parseArguments((String[]) appContext.getArguments().get(IApplicationContext.APPLICATION_ARGS));\r
112 \r
113         Display display = createDisplay();\r
114         // processor must be created before we start event loop\r
115         DelayedEventsProcessor processor = new DelayedEventsProcessor(display);\r
116 \r
117         try {\r
118             Object argCheck = verifyArguments(args);\r
119             if (argCheck != null)\r
120                 return argCheck;\r
121 \r
122             // look and see if there's a splash shell we can parent off of\r
123             Shell shell = WorkbenchPlugin.getSplashShell(display);\r
124             if (shell != null) {\r
125                 // should should set the icon and message for this shell to be the \r
126                 // same as the chooser dialog - this will be the guy that lives in\r
127                 // the task bar and without these calls you'd have the default icon \r
128                 // with no message.\r
129                 shell.setText(ChooseWorkspaceDialog.getWindowTitle());\r
130                 shell.setImages(Dialog.getDefaultImages());\r
131             }\r
132 \r
133             Object instanceLocationCheck = checkInstanceLocation(shell, appContext.getArguments(), args);\r
134             if (instanceLocationCheck != null) {\r
135                 WorkbenchPlugin.unsetSplashShell(display);\r
136                 Platform.endSplash();\r
137                 return instanceLocationCheck;\r
138             }\r
139 \r
140             final ISessionContextProvider provider = new SessionContextProvider(null);\r
141             final ISessionContextProviderSource contextProviderSource = new SingleSessionContextProviderSource(provider);\r
142             //final ISessionContextProviderSource contextProviderSource = new WorkbenchWindowSessionContextProviderSource(PlatformUI.getWorkbench());\r
143             SimanticsUI.setSessionContextProviderSource(contextProviderSource);\r
144             org.simantics.db.layer0.internal.SimanticsInternal.setSessionContextProviderSource(contextProviderSource);\r
145             org.simantics.Simantics.setSessionContextProviderSource(contextProviderSource);\r
146             \r
147             // create the workbench with this advisor and run it until it exits\r
148             // N.B. createWorkbench remembers the advisor, and also registers\r
149             // the workbench globally so that all UI plug-ins can find it using\r
150             // PlatformUI.getWorkbench() or AbstractUIPlugin.getWorkbench()\r
151             int returnCode = PlatformUI.createAndRunWorkbench(display,\r
152                     createWorkbenchAdvisor(args, processor));\r
153 \r
154             // the workbench doesn't support relaunch yet (bug 61809) so\r
155             // for now restart is used, and exit data properties are checked\r
156             // here to substitute in the relaunch return code if needed\r
157             if (returnCode != PlatformUI.RETURN_RESTART) {\r
158                 return EXIT_OK;\r
159             }\r
160 \r
161             // if the exit code property has been set to the relaunch code, then\r
162             // return that code now, otherwise this is a normal restart\r
163             return EXIT_RELAUNCH.equals(Integer.getInteger(PROP_EXIT_CODE)) ? EXIT_RELAUNCH\r
164                     : EXIT_RESTART;\r
165         } finally {\r
166             if (display != null) {\r
167                 display.dispose();\r
168             }\r
169             Location instanceLoc = Platform.getInstanceLocation();\r
170             if (instanceLoc != null)\r
171                 instanceLoc.release();\r
172         }\r
173     }\r
174 \r
175     /*************************************************************************/\r
176 \r
177     private IArguments parseArguments(String[] args) {\r
178         IArgumentFactory<?>[] accepted = {\r
179                 SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS,\r
180                 SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL,\r
181                 SimanticsArguments.DEFAULT_WORKSPACE_LOCATION,\r
182                 SimanticsArguments.WORKSPACE_CHOOSER,\r
183                 SimanticsArguments.WORKSPACE_NO_REMEMBER,\r
184                 SimanticsArguments.PERSPECTIVE,\r
185                 SimanticsArguments.SERVER,\r
186                 SimanticsArguments.NEW_MODEL,\r
187                 SimanticsArguments.EXPERIMENT,\r
188                 SimanticsArguments.DISABLE_INDEX,\r
189                 SimanticsArguments.DATABASE_ID,\r
190         };\r
191         IArguments result = Arguments.parse(args, accepted);\r
192         return result;\r
193     }\r
194 \r
195     private Object verifyArguments(IArguments args) {\r
196         StringBuilder report = new StringBuilder();\r
197 \r
198 //        if (args.contains(SimanticsArguments.NEW_PROJECT)) {\r
199 //            if (args.contains(SimanticsArguments.PROJECT)) {\r
200 //                exclusiveArguments(report, SimanticsArguments.PROJECT, SimanticsArguments.NEW_PROJECT);\r
201 //            }\r
202 //            // Must have a server to checkout from when creating a new\r
203 //            // project right from the beginning.\r
204 //            if (!args.contains(SimanticsArguments.SERVER)) {\r
205 //                missingArgument(report, SimanticsArguments.SERVER);\r
206 //            }\r
207 //        } else if (args.contains(SimanticsArguments.PROJECT)) {\r
208 //            // To load a project, a server must be defined to checkout from\r
209 //            if (!args.contains(SimanticsArguments.SERVER)) {\r
210 //                missingArgument(report, SimanticsArguments.SERVER);\r
211 //            }\r
212 //        }\r
213 \r
214         // NEW_MODEL and MODEL arguments are optional\r
215         // EXPERIMENT argument is optional\r
216 \r
217         String result = report.toString();\r
218         boolean valid = result.length() == 0;\r
219 \r
220         if (!valid) {\r
221             String msg = NLS.bind(Messages.Application_1, result);\r
222             MessageDialog.openInformation(null, Messages.Application_2, msg);\r
223         }\r
224         return valid ? null : EXIT_OK;\r
225     }\r
226 \r
227 //    private void exclusiveArguments(StringBuilder sb, IArgumentFactory<?> arg1, IArgumentFactory<?> arg2) {\r
228 //        sb.append(NLS.bind(Messages.Application_3, arg1.getArgument(), arg2.getArgument()));\r
229 //        sb.append('\n');\r
230 //    }\r
231 //\r
232 //    private void missingArgument(StringBuilder sb, IArgumentFactory<?> arg) {\r
233 //        sb.append(NLS.bind(Messages.Application_0, arg.getArgument()));\r
234 //        sb.append('\n');\r
235 //    }\r
236 \r
237     /*************************************************************************/\r
238 \r
239     /**\r
240      * Creates the display used by the application.\r
241      * \r
242      * @return the display used by the application\r
243      */\r
244     protected Display createDisplay() {\r
245         return PlatformUI.createDisplay();\r
246     }\r
247 \r
248     /* (non-Javadoc)\r
249      * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object)\r
250      */\r
251     @Override\r
252     public void setInitializationData(IConfigurationElement config,\r
253             String propertyName, Object data) {\r
254         // There is nothing to do for ProConfApplication\r
255     }\r
256 \r
257     /**\r
258      * Return true if a valid workspace path has been set and false otherwise.\r
259      * Prompt for and set the path if possible and required.\r
260      * @param applicationArguments \r
261      * \r
262      * @return true if a valid instance location has been set and false\r
263      *         otherwise\r
264      */\r
265     private Object checkInstanceLocation(Shell shell, Map<?,?> applicationArguments, IArguments args) {\r
266         // -data @none was specified but an ide requires workspace\r
267         Location instanceLoc = Platform.getInstanceLocation();\r
268         if (instanceLoc == null) {\r
269             MessageDialog\r
270             .openError(\r
271                     shell,\r
272                     IDEWorkbenchMessages.IDEApplication_workspaceMandatoryTitle,\r
273                     IDEWorkbenchMessages.IDEApplication_workspaceMandatoryMessage);\r
274             return EXIT_OK;\r
275         }\r
276 \r
277         // -data "/valid/path", workspace already set\r
278         // This information is stored in configuration/.settings/org.eclipse.ui.ide.prefs\r
279         if (instanceLoc.isSet()) {\r
280             // make sure the meta data version is compatible (or the user has\r
281             // chosen to overwrite it).\r
282             if (!checkValidWorkspace(shell, instanceLoc.getURL())) {\r
283                 return EXIT_OK;\r
284             }\r
285 \r
286             // at this point its valid, so try to lock it and update the\r
287             // metadata version information if successful\r
288             try {\r
289                 if (instanceLoc.lock()) {\r
290                     writeWorkspaceVersion();\r
291                     return null;\r
292                 }\r
293 \r
294                 // we failed to create the directory.\r
295                 // Two possibilities:\r
296                 // 1. directory is already in use\r
297                 // 2. directory could not be created\r
298                 File workspaceDirectory = new File(instanceLoc.getURL().getFile());\r
299                 if (workspaceDirectory.exists()) {\r
300                     if (isDevLaunchMode(applicationArguments)) {\r
301                         return EXIT_WORKSPACE_LOCKED;\r
302                     }\r
303                     MessageDialog.openError(\r
304                             shell,\r
305                             IDEWorkbenchMessages.IDEApplication_workspaceCannotLockTitle,\r
306                             IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage);\r
307                 } else {\r
308                     MessageDialog.openError(\r
309                             shell,\r
310                             IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,\r
311                             IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);\r
312                 }\r
313             } catch (IOException e) {\r
314                 IDEWorkbenchPlugin.log("Could not obtain lock for workspace location", //$NON-NLS-1$\r
315                         e);\r
316                 MessageDialog\r
317                 .openError(\r
318                         shell,\r
319                         IDEWorkbenchMessages.InternalError,\r
320                         e.getMessage());\r
321             }\r
322             return EXIT_OK;\r
323         }\r
324 \r
325         // -data @noDefault or -data not specified, prompt and set\r
326         ChooseWorkspaceData launchData = null;\r
327         if (args.contains(SimanticsArguments.DEFAULT_WORKSPACE_LOCATION)) {\r
328             launchData = new ChooseWorkspaceData(args.get(SimanticsArguments.DEFAULT_WORKSPACE_LOCATION));\r
329         } else {\r
330             launchData = new ChooseWorkspaceData(instanceLoc.getDefault());\r
331         }\r
332 \r
333         boolean force = args.contains(SimanticsArguments.WORKSPACE_CHOOSER);\r
334         boolean suppressAskAgain = args.contains(SimanticsArguments.WORKSPACE_NO_REMEMBER);\r
335 \r
336         while (true) {\r
337             URL workspaceUrl = promptForWorkspace(shell, launchData, force, suppressAskAgain);\r
338             if (workspaceUrl == null) {\r
339                 return EXIT_OK;\r
340             }\r
341 \r
342             // if there is an error with the first selection, then force the\r
343             // dialog to open to give the user a chance to correct\r
344             force = true;\r
345 \r
346             try {\r
347                 // the operation will fail if the url is not a valid\r
348                 // instance data area, so other checking is unneeded\r
349                 if (instanceLoc.setURL(workspaceUrl, true)) {\r
350                     launchData.writePersistedData();\r
351                     writeWorkspaceVersion();\r
352                     return null;\r
353                 }\r
354             } catch (IllegalStateException e) {\r
355                 MessageDialog\r
356                 .openError(\r
357                         shell,\r
358                         IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,\r
359                         IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);\r
360                 return EXIT_OK;\r
361             }\r
362 \r
363             // by this point it has been determined that the workspace is\r
364             // already in use -- force the user to choose again\r
365             MessageDialog.openError(shell, IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle,\r
366                     IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage);\r
367         }\r
368     }\r
369 \r
370     private static boolean isDevLaunchMode(Map<?,?> args) {\r
371         // see org.eclipse.pde.internal.core.PluginPathFinder.isDevLaunchMode()\r
372         if (Boolean.getBoolean("eclipse.pde.launch")) //$NON-NLS-1$\r
373             return true;\r
374         return args.containsKey("-pdelaunch"); //$NON-NLS-1$\r
375     }\r
376 \r
377     /**\r
378      * Open a workspace selection dialog on the argument shell, populating the\r
379      * argument data with the user's selection. Perform first level validation\r
380      * on the selection by comparing the version information. This method does\r
381      * not examine the runtime state (e.g., is the workspace already locked?).\r
382      * \r
383      * @param shell\r
384      * @param launchData\r
385      * @param force\r
386      *            setting to true makes the dialog open regardless of the\r
387      *            showDialog value\r
388      * @return An URL storing the selected workspace or null if the user has\r
389      *         canceled the launch operation.\r
390      */\r
391     private URL promptForWorkspace(Shell shell, ChooseWorkspaceData launchData,\r
392             boolean force, boolean suppressAskAgain) {\r
393         URL url = null;\r
394         do {\r
395             // okay to use the shell now - this is the splash shell\r
396             new ChooseWorkspaceDialog(shell, launchData, suppressAskAgain, true).prompt(force);\r
397             String instancePath = launchData.getSelection();\r
398             if (instancePath == null) {\r
399                 return null;\r
400             }\r
401 \r
402             // the dialog is not forced on the first iteration, but is on every\r
403             // subsequent one -- if there was an error then the user needs to be\r
404             // allowed to fix it\r
405             force = true;\r
406 \r
407             // 70576: don't accept empty input\r
408             if (instancePath.length() <= 0) {\r
409                 MessageDialog\r
410                 .openError(\r
411                         shell,\r
412                         IDEWorkbenchMessages.IDEApplication_workspaceEmptyTitle,\r
413                         IDEWorkbenchMessages.IDEApplication_workspaceEmptyMessage);\r
414                 continue;\r
415             }\r
416 \r
417             // create the workspace if it does not already exist\r
418             File workspace = new File(instancePath);\r
419             if (!workspace.exists()) {\r
420                 workspace.mkdir();\r
421             }\r
422 \r
423             try {\r
424                 // Don't use File.toURL() since it adds a leading slash that Platform does not\r
425                 // handle properly.  See bug 54081 for more details.\r
426                 String path = workspace.getAbsolutePath().replace(\r
427                         File.separatorChar, '/');\r
428                 url = new URL("file", null, path); //$NON-NLS-1$\r
429             } catch (MalformedURLException e) {\r
430                 MessageDialog\r
431                 .openError(\r
432                         shell,\r
433                         IDEWorkbenchMessages.IDEApplication_workspaceInvalidTitle,\r
434                         IDEWorkbenchMessages.IDEApplication_workspaceInvalidMessage);\r
435                 continue;\r
436             }\r
437         } while (!checkValidWorkspace(shell, url));\r
438 \r
439         return url;\r
440     }\r
441 \r
442     /**\r
443      * Return true if the argument directory is ok to use as a workspace and\r
444      * false otherwise. A version check will be performed, and a confirmation\r
445      * box may be displayed on the argument shell if an older version is\r
446      * detected.\r
447      * \r
448      * @return true if the argument URL is ok to use as a workspace and false\r
449      *         otherwise.\r
450      */\r
451     private boolean checkValidWorkspace(Shell shell, URL url) {\r
452         // a null url is not a valid workspace\r
453         if (url == null) {\r
454             return false;\r
455         }\r
456 \r
457         String version = readWorkspaceVersion(url);\r
458 \r
459         // if the version could not be read, then there is not any existing\r
460         // workspace data to trample, e.g., perhaps its a new directory that\r
461         // is just starting to be used as a workspace\r
462         if (version == null) {\r
463             return true;\r
464         }\r
465 \r
466         final int ide_version = Integer.parseInt(WORKSPACE_VERSION_VALUE);\r
467         int workspace_version = Integer.parseInt(version);\r
468 \r
469         // equality test is required since any version difference (newer\r
470         // or older) may result in data being trampled\r
471         if (workspace_version == ide_version) {\r
472             return true;\r
473         }\r
474 \r
475         // At this point workspace has been detected to be from a version\r
476         // other than the current ide version -- find out if the user wants\r
477         // to use it anyhow.\r
478                 int severity;\r
479                 String title;\r
480                 String message;\r
481                 if (workspace_version < ide_version) {\r
482                         // Workspace < IDE. Update must be possible without issues,\r
483                         // so only inform user about it.\r
484                         severity = MessageDialog.INFORMATION;\r
485                         title = IDEWorkbenchMessages.IDEApplication_versionTitle_olderWorkspace;\r
486                         message = NLS.bind(IDEWorkbenchMessages.IDEApplication_versionMessage_olderWorkspace, url.getFile());\r
487                 } else {\r
488                         // Workspace > IDE. It must have been opened with a newer IDE version.\r
489                         // Downgrade might be problematic, so warn user about it.\r
490                         severity = MessageDialog.WARNING;\r
491                         title = IDEWorkbenchMessages.IDEApplication_versionTitle_newerWorkspace;\r
492                         message = NLS.bind(IDEWorkbenchMessages.IDEApplication_versionMessage_newerWorkspace, url.getFile());\r
493                 }\r
494 \r
495         MessageBox mbox = new MessageBox(shell, SWT.OK | SWT.CANCEL\r
496                 | SWT.ICON_WARNING | SWT.APPLICATION_MODAL);\r
497         mbox.setText(title);\r
498         mbox.setMessage(message);\r
499         return mbox.open() == SWT.OK;\r
500     }\r
501 \r
502     /**\r
503      * Look at the argument URL for the workspace's version information. Return\r
504      * that version if found and null otherwise.\r
505      */\r
506     private static String readWorkspaceVersion(URL workspace) {\r
507         File versionFile = getVersionFile(workspace, false);\r
508         if (versionFile == null || !versionFile.exists()) {\r
509             return null;\r
510         }\r
511 \r
512         try {\r
513             // Although the version file is not spec'ed to be a Java properties\r
514             // file, it happens to follow the same format currently, so using\r
515             // Properties to read it is convenient.\r
516             Properties props = new Properties();\r
517             FileInputStream is = new FileInputStream(versionFile);\r
518             try {\r
519                 props.load(is);\r
520             } finally {\r
521                 is.close();\r
522             }\r
523 \r
524             return props.getProperty(WORKSPACE_VERSION_KEY);\r
525         } catch (IOException e) {\r
526             IDEWorkbenchPlugin.log("Could not read version file", new Status( //$NON-NLS-1$\r
527                     IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH,\r
528                     IStatus.ERROR,\r
529                     e.getMessage() == null ? "" : e.getMessage(), //$NON-NLS-1$,\r
530                             e));\r
531             return null;\r
532         }\r
533     }\r
534 \r
535     /**\r
536      * Write the version of the metadata into a known file overwriting any\r
537      * existing file contents. Writing the version file isn't really crucial,\r
538      * so the function is silent about failure\r
539      */\r
540     private static void writeWorkspaceVersion() {\r
541         Location instanceLoc = Platform.getInstanceLocation();\r
542         if (instanceLoc == null || instanceLoc.isReadOnly()) {\r
543             return;\r
544         }\r
545 \r
546         File versionFile = getVersionFile(instanceLoc.getURL(), true);\r
547         if (versionFile == null) {\r
548             return;\r
549         }\r
550 \r
551         OutputStream output = null;\r
552         try {\r
553             String versionLine = WORKSPACE_VERSION_KEY + '='\r
554             + WORKSPACE_VERSION_VALUE;\r
555 \r
556             output = new FileOutputStream(versionFile);\r
557             output.write(versionLine.getBytes("UTF-8")); //$NON-NLS-1$\r
558         } catch (IOException e) {\r
559             IDEWorkbenchPlugin.log("Could not write version file", //$NON-NLS-1$\r
560                     StatusUtil.newStatus(IStatus.ERROR, e.getMessage(), e));\r
561         } finally {\r
562             try {\r
563                 if (output != null) {\r
564                     output.close();\r
565                 }\r
566             } catch (IOException e) {\r
567                 // do nothing\r
568             }\r
569         }\r
570     }\r
571 \r
572     /**\r
573      * The version file is stored in the metadata area of the workspace. This\r
574      * method returns an URL to the file or null if the directory or file does\r
575      * not exist (and the create parameter is false).\r
576      * \r
577      * @param create\r
578      *            If the directory and file does not exist this parameter\r
579      *            controls whether it will be created.\r
580      * @return An url to the file or null if the version file does not exist or\r
581      *         could not be created.\r
582      */\r
583     private static File getVersionFile(URL workspaceUrl, boolean create) {\r
584         if (workspaceUrl == null) {\r
585             return null;\r
586         }\r
587 \r
588         try {\r
589             // make sure the directory exists\r
590             File metaDir = new File(workspaceUrl.getPath(), METADATA_FOLDER);\r
591             if (!metaDir.exists() && (!create || !metaDir.mkdir())) {\r
592                 return null;\r
593             }\r
594 \r
595             // make sure the file exists\r
596             File versionFile = new File(metaDir, VERSION_FILENAME);\r
597             if (!versionFile.exists()\r
598                     && (!create || !versionFile.createNewFile())) {\r
599                 return null;\r
600             }\r
601 \r
602             return versionFile;\r
603         } catch (IOException e) {\r
604             // cannot log because instance area has not been set\r
605             return null;\r
606         }\r
607     }\r
608 \r
609     /* (non-Javadoc)\r
610      * @see org.eclipse.equinox.app.IApplication#stop()\r
611      */\r
612     @Override\r
613     public void stop() {\r
614         final IWorkbench workbench = PlatformUI.getWorkbench();\r
615         if (workbench == null)\r
616             return;\r
617         final Display display = workbench.getDisplay();\r
618         display.syncExec(new Runnable() {\r
619             @Override\r
620             public void run() {\r
621                 if (!display.isDisposed())\r
622                     workbench.close();\r
623             }\r
624         });\r
625     }\r
626 \r
627 }\r