Community
Participate
Working Groups
+++ This bug was initially created as a clone of Bug #319939 +++ The EMF.Core PlatformResourceURIHandlerImpl has one weakness that can lead to a race condition between refreshing Eclipse resources & saving EMF Resources. To demonstrate this, I wrote an Ant script that executes 2 tasks: - a cleanup task that deletes ecore resources - QVTo transformation that creates an ecore resource When the 2 tasks execute for the first time, there's nothing to cleanup, the QVT transformation executes cleanly: Buildfile: /Volumes/FMTools/Helios1/runtime.imce/EMF.PlatformResourceURIHandler.RaceConditionBug/build.xml Clean: Transform: [qvto:transformation] QVTo Hello World [qvto:transformation] Transformation 'platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/transforms/HelloWorld.qvto' has been executed CleanAndTransform: BUILD SUCCESSFUL Total time: 1 second When invoking the Ant script a second time, the cleanup task deletes the HelloWorld.ecore resource before launching the QVT transformation task. Because Ant is running in the Eclipse environment, the Eclipse workspace may not have been refreshed between the first and second task. This exacerbates the weakness in EMF.Core's PlatformResourceURIHandlerImpl enough to trip the following exception: Buildfile: /Volumes/FMTools/Helios1/runtime.imce/EMF.PlatformResourceURIHandler.RaceConditionBug/build.xml Clean: [delete] Deleting /Volumes/FMTools/Helios1/runtime.imce/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Transform: [qvto:transformation] QVTo Hello World BUILD FAILED /Volumes/FMTools/Helios1/runtime.imce/EMF.PlatformResourceURIHandler.RaceConditionBug/build.xml:13: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. Total time: 1 second The error message is incorrect; to appreciate this, we need to look at the stack trace information more closely: !SUBENTRY 1 org.eclipse.ant.core 4 1 2010-07-14 22:13:38.615 !MESSAGE /Volumes/FMTools/Helios1/runtime.imce/EMF.PlatformResourceURIHandler.RaceConditionBug/build.xml:13: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. !STACK 0 /Volumes/FMTools/Helios1/runtime.imce/EMF.PlatformResourceURIHandler.RaceConditionBug/build.xml:13: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. at org.eclipse.m2m.internal.qvt.oml.runtime.ant.QvtoAntTransformationTask.execute(QvtoAntTransformationTask.java:332) at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106) at org.apache.tools.ant.Task.perform(Task.java:348) at org.apache.tools.ant.Target.execute(Target.java:357) at org.apache.tools.ant.Target.performTasks(Target.java:385) at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1337) at org.apache.tools.ant.Project.executeTarget(Project.java:1306) at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41) at org.eclipse.ant.internal.core.ant.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32) at org.apache.tools.ant.Project.executeTargets(Project.java:1189) at org.eclipse.ant.internal.core.ant.InternalAntRunner.run(InternalAntRunner.java:662) at org.eclipse.ant.internal.core.ant.InternalAntRunner.run(InternalAntRunner.java:495) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.eclipse.ant.core.AntRunner.run(AntRunner.java:378) at org.eclipse.ant.internal.launching.launchConfigurations.AntLaunchDelegate$1.run(AntLaunchDelegate.java:298) at java.lang.Thread.run(Thread.java:637) Caused by: org.eclipse.m2m.internal.qvt.oml.common.MdaException: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore at org.eclipse.m2m.internal.qvt.oml.runtime.launch.QvtLaunchConfigurationDelegateBase.saveTransformationResult(QvtLaunchConfigurationDelegateBase.java:256) at org.eclipse.m2m.internal.qvt.oml.runtime.launch.QvtLaunchConfigurationDelegateBase.doLaunch(QvtLaunchConfigurationDelegateBase.java:202) at org.eclipse.m2m.internal.qvt.oml.runtime.ant.QvtoAntTransformationTask$1.run(QvtoAntTransformationTask.java:317) at org.eclipse.m2m.internal.qvt.oml.common.launch.SafeRunner$SameThreadRunner.run(SafeRunner.java:33) at org.eclipse.m2m.internal.qvt.oml.common.launch.SafeRunner$1.run(SafeRunner.java:26) at org.eclipse.m2m.internal.qvt.oml.runtime.ant.QvtoAntTransformationTask.execute(QvtoAntTransformationTask.java:325) ... 23 more Caused by: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore at org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil.saveModel(EmfUtil.java:198) at org.eclipse.m2m.internal.qvt.oml.runtime.launch.QvtLaunchConfigurationDelegateBase.saveTransformationResult(QvtLaunchConfigurationDelegateBase.java:253) ... 28 more Caused by: org.eclipse.emf.ecore.resource.Resource$IOWrappedException: Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. at org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl$PlatformResourceOutputStream.flush(PlatformResourceURIHandlerImpl.java:138) at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:278) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212) at org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.write(XMLSaveImpl.java:1010) at org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.save(XMLSaveImpl.java:266) at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doSave(XMLResourceImpl.java:206) at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:1406) at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:993) at org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil.saveModel(EmfUtil.java:196) ... 29 more Caused by: org.eclipse.core.internal.resources.ResourceException: Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. at org.eclipse.core.internal.resources.Resource.checkExists(Resource.java:326) at org.eclipse.core.internal.resources.Resource.checkAccessible(Resource.java:200) at org.eclipse.core.internal.resources.File.setContents(File.java:361) at org.eclipse.core.internal.resources.File.setContents(File.java:468) at org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl$PlatformResourceOutputStream.flush(PlatformResourceURIHandlerImpl.java:131) ... 38 more --- Nested Exception --- org.eclipse.m2m.internal.qvt.oml.common.MdaException: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore at org.eclipse.m2m.internal.qvt.oml.runtime.launch.QvtLaunchConfigurationDelegateBase.saveTransformationResult(QvtLaunchConfigurationDelegateBase.java:256) at org.eclipse.m2m.internal.qvt.oml.runtime.launch.QvtLaunchConfigurationDelegateBase.doLaunch(QvtLaunchConfigurationDelegateBase.java:202) at org.eclipse.m2m.internal.qvt.oml.runtime.ant.QvtoAntTransformationTask$1.run(QvtoAntTransformationTask.java:317) at org.eclipse.m2m.internal.qvt.oml.common.launch.SafeRunner$SameThreadRunner.run(SafeRunner.java:33) at org.eclipse.m2m.internal.qvt.oml.common.launch.SafeRunner$1.run(SafeRunner.java:26) at org.eclipse.m2m.internal.qvt.oml.runtime.ant.QvtoAntTransformationTask.execute(QvtoAntTransformationTask.java:325) at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106) at org.apache.tools.ant.Task.perform(Task.java:348) at org.apache.tools.ant.Target.execute(Target.java:357) at org.apache.tools.ant.Target.performTasks(Target.java:385) at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1337) at org.apache.tools.ant.Project.executeTarget(Project.java:1306) at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41) at org.eclipse.ant.internal.core.ant.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32) at org.apache.tools.ant.Project.executeTargets(Project.java:1189) at org.eclipse.ant.internal.core.ant.InternalAntRunner.run(InternalAntRunner.java:662) at org.eclipse.ant.internal.core.ant.InternalAntRunner.run(InternalAntRunner.java:495) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.eclipse.ant.core.AntRunner.run(AntRunner.java:378) at org.eclipse.ant.internal.launching.launchConfigurations.AntLaunchDelegate$1.run(AntLaunchDelegate.java:298) at java.lang.Thread.run(Thread.java:637) Caused by: org.eclipse.m2m.internal.qvt.oml.emf.util.EmfException: Failed to save model to platform:/resource/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore at org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil.saveModel(EmfUtil.java:198) at org.eclipse.m2m.internal.qvt.oml.runtime.launch.QvtLaunchConfigurationDelegateBase.saveTransformationResult(QvtLaunchConfigurationDelegateBase.java:253) ... 28 more Caused by: org.eclipse.emf.ecore.resource.Resource$IOWrappedException: Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. at org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl$PlatformResourceOutputStream.flush(PlatformResourceURIHandlerImpl.java:138) at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:278) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212) at org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.write(XMLSaveImpl.java:1010) at org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.save(XMLSaveImpl.java:266) at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doSave(XMLResourceImpl.java:206) at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:1406) at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:993) at org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil.saveModel(EmfUtil.java:196) ... 29 more Caused by: org.eclipse.core.internal.resources.ResourceException: Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. at org.eclipse.core.internal.resources.Resource.checkExists(Resource.java:326) at org.eclipse.core.internal.resources.Resource.checkAccessible(Resource.java:200) at org.eclipse.core.internal.resources.File.setContents(File.java:361) at org.eclipse.core.internal.resources.File.setContents(File.java:468) at org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl$PlatformResourceOutputStream.flush(PlatformResourceURIHandlerImpl.java:131) ... 38 more The problem comes from: org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl$PlatformResourceOutputStream.flush @Override public void flush() throws IOException { super.flush(); if (previouslyFlushed) { if (count == 0) { return; } } else { createContainer(file.getParent()); } byte[] contents = toByteArray(); InputStream inputStream = new ByteArrayInputStream(contents, 0, contents.length); try { if (previouslyFlushed) // *1* { file.appendContents(inputStream, force, false, progressMonitor); } else if (!file.exists()) // *2* { file.create(inputStream, false, null); previouslyFlushed = true; } else { // *3* if (!file.isSynchronized(IResource.DEPTH_ONE)) { file.refreshLocal(IResource.DEPTH_ONE, progressMonitor); } // *4* file.setContents(inputStream, force, keepHistory, progressMonitor); // line 131 previouslyFlushed = true; } reset(); } catch (CoreException exception) { throw new Resource.IOWrappedException(exception); } } The vulnerability comes from *1* where previouslyFlushed = false and the stale state of the information about files that results from the 1st Cleanup Ant task. This stale state results in file.exists() = true at step *2* until the code forces a refresh at step *3*. However, at step *4*, the information about the file is no longer stale, the file has truly been deleted! Consequently, the code will fail to execute file.setContents() at line 131 as confirmed by the exception trace: Caused by: org.eclipse.core.internal.resources.ResourceException: Resource '/EMF.PlatformResourceURIHandler.RaceConditionBug/models/HelloWorld.ecore' does not exist. at org.eclipse.core.internal.resources.Resource.checkExists(Resource.java:326) at org.eclipse.core.internal.resources.Resource.checkAccessible(Resource.java:200) at org.eclipse.core.internal.resources.File.setContents(File.java:361) at org.eclipse.core.internal.resources.File.setContents(File.java:468) at org.eclipse.emf.ecore.resource.impl.PlatformResourceURIHandlerImpl$PlatformResourceOutputStream.flush(PlatformResourceURIHandlerImpl.java:131) In this example, at *1*, previouslyFlushed = false. at *2*, file.exists() = true because of stale information at *3*, the refresh forces an update of the information; i.e., the file truly does not exist because the previous task deleted it!
The fix is committed to CVS for 2.7.
The fix is available in the latest build for the stream.