Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » Error saving an EMF model with Crossreferences in a new XtextResource(A NewProject wizard creates two xtext models from dynamically created EMF models.)
Error saving an EMF model with Crossreferences in a new XtextResource [message #1803251] Mon, 25 February 2019 21:09 Go to next message
Alex Lotz is currently offline Alex LotzFriend
Messages: 9
Registered: October 2017
Junior Member
Context: I am working on a regular new-project wizards that internally creates two Xtext models based on dynamically created EMF models (using the regular EMF Factory methods). For the sake of simplicity I have boiled down my use-case to the bare minimum, now consisting of two very simple DSLs, MyDsl1 and MyDsl2 as shown in the attached figure below:

index.php/fa/34956/0/

From these two Ecore models I have instantiated related Xtext grammars (that are straight forward) and I can use both languages as expected. Please note that MyRoot2 element of MyDsl2 references a MyRoot1 element from MyDsl1 (this will be relevant in a moment).

Next, I have created a simple new-project creation wizard that besides of creating the project itself, also instantiates the two Xtext models as follows (I added the full example implementation, but there is only a small part really relevant as explained below):

package mynewprojectwizard;

// ... several imports ...

import myDsl1.MyDsl1Factory;
import myDsl1.MyRoot1;
import myDsl2.MyDsl2Factory;
import myDsl2.MyRoot2;

public class WizardMyNewProject extends Wizard implements INewWizard {

	private WizardNewProjectCreationPage pageOne;
	
	@Override
	public void addPages() {
		pageOne = new WizardNewProjectCreationPage("MyWizard");
		pageOne.setTitle("My Wizard");
		pageOne.setInitialProjectName("TestProject");
		addPage(pageOne);
	}
	
	@Override
	public void init(IWorkbench workbench, IStructuredSelection selection) { }
	
	@Override
	public boolean performFinish() {
		// create a proper project description
		final IProjectDescription description = ResourcesPlugin.getWorkspace().newProjectDescription(pageOne.getName());
		java.net.URI location = pageOne.getLocationURI();
		description.setLocationURI(location);
		
		boolean result = true; // as long as no exception will be thrown this remains true
		
		IRunnableWithProgress createProjectRunnable = new IRunnableWithProgress() {

			@Override
			public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
				SubMonitor subMonitor = SubMonitor.convert(monitor, "Project Creation Wizard", 150);
				try {
					IProject project = pageOne.getProjectHandle();
					project.create(description, subMonitor.split(10));
					// open project
					project.open(IResource.BACKGROUND_REFRESH, subMonitor.split(10));
					
					// add Xtext and Java project natures by default
					addProjectNatures(project, new String[] {"org.eclipse.xtext.ui.shared.xtextNature", JavaCore.NATURE_ID}, subMonitor.split(10));
					
					//Create a Java Project
					IJavaProject javaProject = JavaCore.create(project);
					
					//set the model folder as a source entry
					int defaultEntriesNumber = 2;
					IClasspathEntry[] buildPath = new  IClasspathEntry[defaultEntriesNumber];
					buildPath[0] = JavaRuntime.getDefaultJREContainerEntry();
					buildPath[1] = JavaCore.newSourceEntry(project.getFullPath().append("model"));
					
					javaProject.setRawClasspath(buildPath, project.getFullPath().append("bin"), subMonitor.split(10));
					
					if(project.isOpen()) {		
						// create model folder
						IFolder modelFolder = project.getFolder("model");
						modelFolder.create(true, true, subMonitor.split(10));
						
						// now create our two DSL-models
						createModels(project, modelFolder, subMonitor.split(10));
					}
					
				} catch (OperationCanceledException e) {
					e.printStackTrace();
				} catch (CoreException e) {
					e.printStackTrace();
				} finally {
					subMonitor.done();
				}
			}
		};
		
		try {
			boolean doFork = false;
			boolean cancelable = true;
			getContainer().run(doFork, cancelable, createProjectRunnable);
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			result = false;
		} catch (InterruptedException e) {
			e.printStackTrace();
			result = false;
		}
		return result;
	}

	private void createModels(IProject project, IFolder modelFolder, IProgressMonitor monitor) {
		// create model for MyDsl1
		MyRoot1 root1 = createDsl1();
		Injector i1 = getDsl1Injector();
		Resource r1 = createNewEMFResource(project, modelFolder, i1, null);
		saveEMFModelInResource(root1, r1, monitor);
		
		//########################################################
//		try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
		//########################################################
		
		
		// create model for MyDsl2, referencing the resource from MyDsl1
		List<Resource> relatedResources = Arrays.asList(r1);
		MyRoot2 root2 = createDsl2(root1);
		Injector i2 = getDsl2Injector();
		Resource r2 = createNewEMFResource(project, modelFolder, i2, relatedResources);
		saveEMFModelInResource(root2, r2, monitor);
	}
	
	private Injector getDsl1Injector() {
		return Mydsl1Activator.getInstance().getInjector(Mydsl1Activator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL1);
	}
	
	private Injector getDsl2Injector() {
		return Mydsl2Activator.getInstance().getInjector(Mydsl2Activator.ORG_XTEXT_EXAMPLE_MYDSL_MYDSL2);
	}
	
	private MyRoot1 createDsl1() {
		MyRoot1 r1 = MyDsl1Factory.eINSTANCE.createMyRoot1();
		r1.setName("Test1");
		return r1;
	}
	
	private MyRoot2 createDsl2(MyRoot1 r1) {
		MyRoot2 r2 = MyDsl2Factory.eINSTANCE.createMyRoot2();
		r2.setName("Test2");
		r2.setRoot1ref(r1);
		return r2;
	}
	
	private Resource createNewEMFResource(IProject project, IFolder modelFolder, Injector injector, List<Resource> relatedResources) {
		if(modelFolder.exists()) {
			String modelFileExtrnsion = getModelFileExtension(injector);
			IResourceSetProvider resourceSetProvider = injector.getInstance(IResourceSetProvider.class);
			ResourceSet resourceSet = resourceSetProvider.get(project);
			
			// add related resources (if there are any) to the resource-set
			if(relatedResources != null) {
				for(Resource relatedResource: relatedResources) {
					resourceSet.getResources().add(relatedResource);
				}
			}
			
			// get an existing resource for the current model
			URI uri = URI.createURI("platform:/resource/" 
					+ project.getName()+ "/" 
					+ modelFolder.getProjectRelativePath() + "/"
					+ project.getName()+ "." + modelFileExtrnsion);
			return resourceSet.createResource(uri);
		}
		return null;
	}
	
	private void saveEMFModelInResource(EObject emfModel, Resource newResource, IProgressMonitor monitor) {
		// add emfModel to the new resource
		newResource.getContents().add(emfModel);
		try {
			// the resource will internally parse and save the newly added EMF model
			SaveOptions saveOptions = SaveOptions.newBuilder().format().getOptions();
			newResource.save(saveOptions.toOptionsMap());
			// we need to wait until the saved resource gets recognized within the eclipse workspace
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	
	private String getModelFileExtension(Injector injector) {
		return injector.getInstance(FileExtensionProvider.class).getPrimaryFileExtension();
	}
	
	private void addProjectNatures(IProject project, String[] natures, IProgressMonitor monitor) throws CoreException {
		IProjectDescription description = project.getDescription();
        String[] currNatures = description.getNatureIds();
        String[] newNatures = new String[currNatures.length+natures.length];
        System.arraycopy(currNatures, 0, newNatures, 0, currNatures.length);
        System.arraycopy(natures, 0, newNatures, currNatures.length, natures.length);
        
		// validate the natures
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IStatus status = workspace.validateNatureSet(newNatures);
		
		// only apply new nature, if the status is ok
		if (status.getCode() == IStatus.OK) {
			description.setNatureIds(newNatures);
			project.setDescription(description, monitor);
		}
	}
}


Basically, the most interesting part here is the method createModels() which instantiates the two EMF models, and then saves both in a related XtextResource. Please note the commented out part where I call Thread.sleep(1000);. When I comment this sleep back in, the overall wizard works (most of the time). But in some rare cases and in all cases when the sleep is deactivated, I get this error:

java.lang.reflect.InvocationTargetException
	at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:448)
	at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:353)
	at org.eclipse.jface.wizard.WizardDialog.run(WizardDialog.java:980)
	at mynewprojectwizard.WizardMyNewProject.performFinish(WizardMyNewProject.java:120)
	at org.eclipse.jface.wizard.WizardDialog.finishPressed(WizardDialog.java:778)
	at org.eclipse.jface.wizard.WizardDialog.buttonPressed(WizardDialog.java:417)
	at org.eclipse.jface.dialogs.Dialog.lambda$0(Dialog.java:619)
	at org.eclipse.swt.events.SelectionListener$1.widgetSelected(SelectionListener.java:81)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:249)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:86)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5348)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1348)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4602)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4183)
	at org.eclipse.jface.window.Window.runEventLoop(Window.java:818)
	at org.eclipse.jface.window.Window.open(Window.java:794)
	at org.eclipse.ui.actions.NewProjectAction.run(NewProjectAction.java:115)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:473)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:565)
	at org.eclipse.jface.action.ActionContributionItem.lambda$4(ActionContributionItem.java:397)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:86)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5348)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1348)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4602)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4183)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1150)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1039)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:680)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:594)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:151)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:653)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:590)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1499)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1472)
Caused by: java.lang.RuntimeException: No EObjectDescription could be found in Scope MyRoot2.root1ref for MyRoot1'Test1'
Semantic Object: MyRoot2'Test2'
URI: platform:/resource/TestProject/model/TestProject.mydsl2
EStructuralFeature: myDsl2::MyRoot2.root1ref
	at org.eclipse.xtext.serializer.diagnostic.ISerializationDiagnostic$ExceptionThrowingAcceptor.accept(ISerializationDiagnostic.java:131)
	at org.eclipse.xtext.serializer.tokens.CrossReferenceSerializer.getCrossReferenceNameFromScope(CrossReferenceSerializer.java:138)
	at org.eclipse.xtext.serializer.tokens.CrossReferenceSerializer.serializeCrossRef(CrossReferenceSerializer.java:111)
	at org.eclipse.xtext.serializer.acceptor.SequenceFeeder.getToken(SequenceFeeder.java:481)
	at org.eclipse.xtext.serializer.acceptor.SequenceFeeder.accept(SequenceFeeder.java:238)
	at org.xtext.example.mydsl.serializer.MyDsl2SemanticSequencer.sequence_MyRoot2(MyDsl2SemanticSequencer.java:59)
	at org.xtext.example.mydsl.serializer.MyDsl2SemanticSequencer.sequence(MyDsl2SemanticSequencer.java:36)
	at org.eclipse.xtext.serializer.sequencer.AbstractSemanticSequencer.createSequence(AbstractSemanticSequencer.java:67)
	at org.eclipse.xtext.serializer.impl.Serializer.serialize(Serializer.java:115)
	at org.eclipse.xtext.serializer.impl.Serializer.serializeToRegions(Serializer.java:136)
	at org.eclipse.xtext.serializer.impl.Serializer.serialize(Serializer.java:142)
	at org.eclipse.xtext.serializer.impl.Serializer.serialize(Serializer.java:189)
	at org.eclipse.xtext.resource.XtextResource.doSave(XtextResource.java:386)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:1430)
	at org.eclipse.emf.ecore.resource.impl.ResourceImpl.save(ResourceImpl.java:999)
	at mynewprojectwizard.WizardMyNewProject.saveEMFModelInResource(WizardMyNewProject.java:205)
	at mynewprojectwizard.WizardMyNewProject.createModels(WizardMyNewProject.java:152)
	at mynewprojectwizard.WizardMyNewProject.access$2(WizardMyNewProject.java:131)
	at mynewprojectwizard.WizardMyNewProject$1.run(WizardMyNewProject.java:104)
	at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:437)
	... 46 more


I interpret the error such that the MyDsl2 model does not find the reference to MyDsl1 model. The strange thing is, that if I comment the sleep command back in, then this error does not occur. I spent a lot of time debugging and analyzing the problem and came to the following conclusion. The problem seems to be related to the Java-based resolution of the Xtext cross references. I have found out that a Java project creates the bin folder into which the new xtext models are somehow copied (automatically) at some point. I don't know which of the magic Eclipse background process does this, but obviously, the Xtext resource is not aware of this update and fails each time it tries to resolve the former resource that has not yet been created in the bin folder.

Well, obviously, the solution with sleep is a very bad solution. My question now is, how can I ensure that resource-saving runs in sync with the workspace, or what can I do else to fix my problem? My secondary question is: Do I need to use something different to IRunnableWithProgress (e.g. some transactional editing domain)? Did I maybe overlooked some obvious solution?

I appreciate any help!

Alex
Re: Error saving an EMF model with Crossreferences in a new XtextResource [message #1803253 is a reply to message #1803251] Mon, 25 February 2019 21:21 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
you should use the same resourceset and make sure it uses XtextLiveScopeResourceSetProvider to create it

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Error saving an EMF model with Crossreferences in a new XtextResource [message #1803255 is a reply to message #1803253] Mon, 25 February 2019 21:32 Go to previous messageGo to next message
Alex Lotz is currently offline Alex LotzFriend
Messages: 9
Registered: October 2017
Junior Member
Wow, I have injected the XtextLiveScopeResourceSetProvider and it seems to have solved my problem. Thank you very much, this is quite a trick that I have missed to find. But what is this resource-set provider doing differently, and why is it not injected by default?
Re: Error saving an EMF model with Crossreferences in a new XtextResource [message #1803258 is a reply to message #1803255] Mon, 25 February 2019 21:43 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
cause for read usescases it makes no sense

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Error saving an EMF model with Crossreferences in a new XtextResource [message #1803259 is a reply to message #1803258] Mon, 25 February 2019 21:54 Go to previous message
Alex Lotz is currently offline Alex LotzFriend
Messages: 9
Registered: October 2017
Junior Member
Ok, thanks again. You saved my day. ;-)
Previous Topic:How to get the list of classes for scope
Next Topic:Asynchronous validation and content assist
Goto Forum:
  


Current Time: Thu Mar 28 21:13:46 GMT 2024

Powered by FUDForum. Page generated in 0.03276 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top