eclipse / xtext

Eclipse Xtext™ is a language development framework
http://www.eclipse.org/Xtext
Eclipse Public License 2.0
766 stars 320 forks source link

Serializing model resource fails with VS Code client #1808

Open RobertWalterU3d opened 4 years ago

RobertWalterU3d commented 4 years ago

What I try to do: I want to use the Xtext generator to have a serialized version of my AST next to the actual file, so I have implemented it like this:

override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
    val rset = new ResourceSetImpl
    val xmlResource = rset.createResource(resource.URI.appendFileExtension("xml"))
    xmlResource.contents.add(resource.contents.head)
    xmlResource.save(Collections.EMPTY_MAP)
}

When I open a file in VS Code now, I get these exceptions:

SEVERE: Internal error: java.lang.NullPointerException
java.util.concurrent.CompletionException: java.lang.NullPointerException
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:331)
    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:346)
    at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:632)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
    at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
    at org.eclipse.xtext.ide.server.concurrent.WriteRequest.run(WriteRequest.java:44)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException
    at org.my.dsl.generator.SglGenerator.doGenerate(SglGenerator.java:28)
    at org.eclipse.xtext.generator.GeneratorDelegate.doGenerate(GeneratorDelegate.java:43)
    at org.eclipse.xtext.generator.GeneratorDelegate.generate(GeneratorDelegate.java:34)
    at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.generate(IncrementalBuilder.java:340)
    at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.lambda$launch$2(IncrementalBuilder.java:277)
    at com.google.common.collect.Iterators$6.transform(Iterators.java:783)
    at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47)
    at com.google.common.collect.FluentIterable.copyInto(FluentIterable.java:791)
    at org.eclipse.xtext.build.ClusteringStorageAwareResourceLoader.executeClustered(ClusteringStorageAwareResourceLoader.java:68)
    at org.eclipse.xtext.build.BuildContext.executeClustered(BuildContext.java:54)
    at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.launch(IncrementalBuilder.java:258)
    at org.eclipse.xtext.build.IncrementalBuilder.build(IncrementalBuilder.java:403)
    at org.eclipse.xtext.build.IncrementalBuilder.build(IncrementalBuilder.java:385)
    at org.eclipse.xtext.ide.server.ProjectManager.doBuild(ProjectManager.java:105)
    at org.eclipse.xtext.ide.server.ProjectManager.doInitialBuild(ProjectManager.java:96)
    at org.eclipse.xtext.ide.server.BuildManager.doInitialBuild(BuildManager.java:172)
    at org.eclipse.xtext.ide.server.WorkspaceManager.refreshWorkspaceConfig(WorkspaceManager.java:172)
    at org.eclipse.xtext.ide.server.WorkspaceManager.initialize(WorkspaceManager.java:145)
    at org.eclipse.xtext.ide.server.LanguageServerImpl.lambda$initialize$0(LanguageServerImpl.java:220)
    at org.eclipse.xtext.ide.server.concurrent.WriteRequest.run(WriteRequest.java:38)
    ... 5 more

[Error - 14:16:20] Server initialization failed.
  Message: Internal error.
  Code: -32603 
java.util.concurrent.CompletionException: java.lang.NullPointerException
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:331)
    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:346)
    at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:632)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
    at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
    at org.eclipse.xtext.ide.server.concurrent.WriteRequest.run(WriteRequest.java:44)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException
    at org.my.dsl.sgl.generator.SglGenerator.doGenerate(SglGenerator.java:28)
    at org.eclipse.xtext.generator.GeneratorDelegate.doGenerate(GeneratorDelegate.java:43)
    at org.eclipse.xtext.generator.GeneratorDelegate.generate(GeneratorDelegate.java:34)
    at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.generate(IncrementalBuilder.java:340)
    at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.lambda$launch$2(IncrementalBuilder.java:277)
    at com.google.common.collect.Iterators$6.transform(Iterators.java:783)
    at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47)
    at com.google.common.collect.FluentIterable.copyInto(FluentIterable.java:791)
    at org.eclipse.xtext.build.ClusteringStorageAwareResourceLoader.executeClustered(ClusteringStorageAwareResourceLoader.java:68)
    at org.eclipse.xtext.build.BuildContext.executeClustered(BuildContext.java:54)
    at org.eclipse.xtext.build.IncrementalBuilder$InternalStatefulIncrementalBuilder.launch(IncrementalBuilder.java:258)
    at org.eclipse.xtext.build.IncrementalBuilder.build(IncrementalBuilder.java:403)
    at org.eclipse.xtext.build.IncrementalBuilder.build(IncrementalBuilder.java:385)
    at org.eclipse.xtext.ide.server.ProjectManager.doBuild(ProjectManager.java:105)
    at org.eclipse.xtext.ide.server.ProjectManager.doInitialBuild(ProjectManager.java:96)
    at org.eclipse.xtext.ide.server.BuildManager.doInitialBuild(BuildManager.java:172)
    at org.eclipse.xtext.ide.server.WorkspaceManager.refreshWorkspaceConfig(WorkspaceManager.java:172)
    at org.eclipse.xtext.ide.server.WorkspaceManager.initialize(WorkspaceManager.java:145)
    at org.eclipse.xtext.ide.server.LanguageServerImpl.lambda$initialize$0(LanguageServerImpl.java:220)
    at org.eclipse.xtext.ide.server.concurrent.WriteRequest.run(WriteRequest.java:38)
    ... 5 more

Any idea what I do wrong?

szarnekow commented 4 years ago

xmlResource.contents.add(resource.contents.head) changes the original resource to start with.

RobertWalterU3d commented 4 years ago

does it? this used to work (and still does) in an older project of mine, which is just an eclipse plugin.

RobertWalterU3d commented 4 years ago

I mean, I am happy to serialize the AST differently, if you have a suggestion :)

cdietrich commented 4 years ago

maybe use a command? or a fresh resourceset?

szarnekow commented 4 years ago

Sure it does (by accident, though). The Eclipse builder uses a new resource set for each build run. If no subsequent code in your Eclipse scenario accesses to original resource, no code would notice. The LSP server has a long living resource set. If you corrupt that, it is ... corrupted.

RobertWalterU3d commented 4 years ago

soooo, the fact that I create a new resourceSet at the beginning does not help here?

cdietrich commented 4 years ago

xmlResource.contents.add(resource.contents.head)

moves from rs1 to rs2

RobertWalterU3d commented 4 years ago

and that's not what I want, or is it? Would you mind providing a bit more context, I have a hard time following your comments, sorry.

cdietrich commented 4 years ago

move = delete + add => you change the original rs

RobertWalterU3d commented 4 years ago

Or maybe, if it is not too much trouble, can you name or point me to the API I need to use in order to avoid the problem of corrupting the resourceSet used by VS Code? Is it that the add method call is not making a deep copy, is that the problem?

RobertWalterU3d commented 4 years ago

So the answer is to make a deep copy of my original resource?

cdietrich commented 4 years ago

i dont know as i dont have time to test

cdietrich commented 4 years ago

and as sebastian said: the lsp api is not yet "ausgereift" for many things and usecases

RobertWalterU3d commented 4 years ago

that's fair, but maybe you know or can point me to an API (if there is any) to make a deep copy of a resource?

szarnekow commented 4 years ago

I was just for a second inclined to go to lmgtfy ... EcoreUtil.copy is your friend.

RobertWalterU3d commented 4 years ago

Well, I really appreciate your help, honestly. But that exact passive aggressive demeanor is the reason why I left this community many years ago. I can understand that questions like the one I am posing here are trivial to contributors and members of the project. It just always boggles me that this community does not seem to be able to A) accept that people might struggle with easy questions (before I asked, I googled "ecore deep copy resource" and similar things, but EcoreUtil.copy I did not find) and that the accumulations of easy questions that you cannot answer can become overwhelming, so you start to ask them one by one, and B) why there is even the need to be that passive aggressive (at least it is perceived that way).

Again, I wholeheartedly appreciate your (immensely quick!) help, but all I can say is that I never felt so unwelcome in any other community with almost every interaction I have than the "eclipse modeling community".

szarnekow commented 4 years ago

You are right, thank you for calling me out on this. I'm sorry about this.

I don't want to justify this, but to me, it's sad to answer questions of the shape What am I doing wrong based on 5 lines of code and a stacktrace without any way to reproduce this on my end (in my spare time). So essentially the alternative is to not triage the incoming bugs or leave at least those aside that are not related to the project but more about an idea that was implemented in a specific custom context (which I don't know anything about, neither requirements nor the problem that is supposed to be solved but only a sidenote about a potential technical solution).

Nevertheless, I'm sorry for being rude. Thank's again for pointing that out.

RobertWalterU3d commented 4 years ago

I think it is a viable alternative to just point that out and let me know that there is information missing in order to provide meaningful answers, that's fair.

I was under the assumption that the code I posted in combination with the description of what I am trying to achieve is sufficient. If that assumption is wrong, I am happy to provide more information. As so often with work-related projects, it is not easy to share all the details, but please let me know what you are missing from my original description.

cdietrich commented 4 years ago

i am missing what is null in this case

RobertWalterU3d commented 4 years ago

So for what it's worth, I managed to get it working like so:

override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {

        val xmlResource = new XMLResourceFactoryImpl().createResource(resource.URI.appendFileExtension("xml")) 
        // this does not work, for whatever reason...
        //rset.createResource(resource.URI.appendFileExtension("xml")) 

        xmlResource.contents.add(EcoreUtil2.copy(resource.contents.head))

        var stream = new ByteArrayOutputStream();
        xmlResource.save(stream, Collections.EMPTY_MAP)
        val asString = new String(stream.toByteArray) 

        val fileName = URI.decode(resource.URI.trimFileExtension.lastSegment)
        fsa.generateFile(fileName+".xml", asString)
    }

My current assumption is that creating the resource always caused the NPE, so I will try to go back to the original solution using the XMLResourceFactoryImpl to see if this also works.

RobertWalterU3d commented 4 years ago

This works as well:

override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
    val xmlResource = new XMLResourceFactoryImpl().createResource(resource.URI.appendFileExtension("xml"))
    xmlResource.contents.add(EcoreUtil2.copy(resource.contents.head))
    xmlResource.save(Collections.EMPTY_MAP)
}