openrewrite / rewrite-maven-plugin

OpenRewrite's Maven plugin.
https://openrewrite.github.io/rewrite-maven-plugin/plugin-info.html
Apache License 2.0
140 stars 73 forks source link

Incompatibility with Maven Polyglot leads to `ParseError` #649

Open laeubi opened 11 months ago

laeubi commented 11 months ago

What version of OpenRewrite are you using?

I am using

How are you running OpenRewrite?

I am using the Maven plugin, and my project is a multi module project.

<plugins>
    <plugin>
        <groupId>org.openrewrite.maven</groupId>
        <artifactId>rewrite-maven-plugin</artifactId>
        <version>5.9.1</version>
        <configuration>
            <activeRecipes>
                <recipe>org.eclipse.ui.PlatformUISetHelp</recipe>
            </activeRecipes>
        </configuration>
    </plugin>
</plugins>

The project is hosted here: https://github.com/eclipse-platform/eclipse.platform

What is the smallest, simplest way to reproduce the problem?

I added the above snippet to the root pom.xml I created a rewrite.yml like this:

---
type: specs.openrewrite.org/v1beta/recipe
name: org.eclipse.ui.PlatformUISetHelp
displayName: Replace calls of PlatformUI.getWorkbench().getHelpSystem() with PlatformUI.getWorkbench().getHelpSystem().setHelp(...) with e4 safe PlatformUI.setHelp(...) variant
recipeList:
  - org.openrewrite.java.SimplifyMethodChain:
      methodPatternChain: ['org.eclipse.ui.PlatformUI getWorkbench()', 'org.eclipse.ui.IWorkbench getHelpSystem()', 'org.eclipse.ui.help.IWorkbenchHelpSystem setHelp(..)']
      newMethodName: setHelp
      matchOverrides: false

then I go to debug/org.eclipse.debug.ui in the project and call mvn rewrite:run

What is the full stack trace of any errors you encountered?

[INFO] --- rewrite:5.9.1:run (default-cli) @ org.eclipse.debug.ui ---
[INFO] Using active recipe(s) [org.eclipse.ui.PlatformUISetHelp]
[INFO] Using active styles(s) []
[INFO] Validating active recipes...
[INFO] Project [[bundle] Debug UI] Resolving Poms...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  21.543 s (Wall Clock)
[INFO] Finished at: 2023-11-01T17:27:01+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.openrewrite.maven:rewrite-maven-plugin:5.9.1:run (default-cli) on project org.eclipse.debug.ui: A type incompatibility occurred while executing org.openrewrite.maven:rewrite-maven-plugin:5.9.1:run: class org.openrewrite.tree.ParseError cannot be cast to class org.openrewrite.xml.tree.Xml$Document (org.openrewrite.tree.ParseError and org.openrewrite.xml.tree.Xml$Document are in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @75c258f9)

Are you interested in contributing a fix to OpenRewrite?

This might be because the project uses a polyglot build.

timtebeek commented 11 months ago

Hi @laeubi ; Sorry to hear you're having these issues! I'm looking at these now and can at least confirm your issue; to replicate I've created a rewrite.yml file in the root and ran this command in debug/org.eclipse.debug.ui

mvn -U org.openrewrite.maven:rewrite-maven-plugin:5.10.0:run \
  -DactiveRecipes=org.eclipse.ui.PlatformUISetHelp -X | grep -v DEBUG

It's a convenient shorthand instead of updating the pom.xml.

timtebeek commented 11 months ago

Using the above I get the following rough output:

org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.openrewrite.maven:rewrite-maven-plugin:5.10.0:run (default-cli) on project org.eclipse.debug.ui: A type incompatibility occurred while executing org.openrewrite.maven:rewrite-maven-plugin:5.10.0:run: class org.openrewrite.tree.ParseError cannot be cast to class org.openrewrite.xml.tree.Xml$Document (org.openrewrite.tree.ParseError and org.openrewrite.xml.tree.Xml$Document are in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @527bce68)
-----------------------------------------------------
realm =    plugin>org.openrewrite.maven:rewrite-maven-plugin:5.10.0
strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
Number of foreign imports: 1
import: Entry[import  from realm ClassRealm[project>org.eclipse.platform:org.eclipse.debug.ui:3.18.200-SNAPSHOT, parent: ClassRealm[maven.api, parent: null]]]

-----------------------------------------------------

    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:333)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:316)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:212)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:174)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:75)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:162)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:159)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at io.takari.maven.builder.smart.SmartBuilderImpl.buildProject (SmartBuilderImpl.java:209)
    at io.takari.maven.builder.smart.SmartBuilderImpl$ProjectBuildTask.run (SmartBuilderImpl.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:539)
    at java.util.concurrent.FutureTask.run (FutureTask.java:264)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1136)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:635)
    at java.lang.Thread.run (Thread.java:833)
Caused by: org.apache.maven.plugin.PluginExecutionException: A type incompatibility occurred while executing org.openrewrite.maven:rewrite-maven-plugin:5.10.0:run: class org.openrewrite.tree.ParseError cannot be cast to class org.openrewrite.xml.tree.Xml$Document (org.openrewrite.tree.ParseError and org.openrewrite.xml.tree.Xml$Document are in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @527bce68)
-----------------------------------------------------
realm =    plugin>org.openrewrite.maven:rewrite-maven-plugin:5.10.0
strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
Number of foreign imports: 1
import: Entry[import  from realm ClassRealm[project>org.eclipse.platform:org.eclipse.debug.ui:3.18.200-SNAPSHOT, parent: ClassRealm[maven.api, parent: null]]]

-----------------------------------------------------

    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:176)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:328)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:316)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:212)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:174)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:75)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:162)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:159)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at io.takari.maven.builder.smart.SmartBuilderImpl.buildProject (SmartBuilderImpl.java:209)
    at io.takari.maven.builder.smart.SmartBuilderImpl$ProjectBuildTask.run (SmartBuilderImpl.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:539)
    at java.util.concurrent.FutureTask.run (FutureTask.java:264)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1136)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:635)
    at java.lang.Thread.run (Thread.java:833)
Caused by: java.lang.ClassCastException: class org.openrewrite.tree.ParseError cannot be cast to class org.openrewrite.xml.tree.Xml$Document (org.openrewrite.tree.ParseError and org.openrewrite.xml.tree.Xml$Document are in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @527bce68)
    at org.openrewrite.maven.MavenMojoProjectParser.parseMaven (MavenMojoProjectParser.java:504)
    at org.openrewrite.maven.MavenMojoProjectParser.listSourceFiles (MavenMojoProjectParser.java:150)
    at org.openrewrite.maven.AbstractRewriteMojo.loadSourceSet (AbstractRewriteMojo.java:257)
    at org.openrewrite.maven.AbstractRewriteMojo.listResults (AbstractRewriteMojo.java:239)
    at org.openrewrite.maven.AbstractRewriteRunMojo.execute (AbstractRewriteRunMojo.java:53)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:126)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:328)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:316)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:212)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:174)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:75)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:162)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:159)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at io.takari.maven.builder.smart.SmartBuilderImpl.buildProject (SmartBuilderImpl.java:209)
    at io.takari.maven.builder.smart.SmartBuilderImpl$ProjectBuildTask.run (SmartBuilderImpl.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:539)
    at java.util.concurrent.FutureTask.run (FutureTask.java:264)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1136)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:635)
    at java.lang.Thread.run (Thread.java:833)
laeubi commented 11 months ago

It's a convenient shorthand instead of updating the pom.xml.

Thanks for the hint, I didn't find that in the documentation, maybe its worth to add it here: https://docs.openrewrite.org/running-recipes/getting-started#step-5-run-a-recipe-with-yaml-configuration

because it suggest adding it to the pom :-)

timtebeek commented 11 months ago

Ah yes; we don't add that shorthand to every guide; we do add it to the individual recipe pages, such that it should be easy to find when you explore recipes.

Just before the above exception I also see

[INFO] Project [[bundle] Debug UI] Resolving Poms...
java.util.NoSuchElementException
    at java.util.Spliterators$1Adapter.next (Spliterators.java:688)
    at org.openrewrite.maven.MavenParser.parseInputs (MavenParser.java:87)
    at org.openrewrite.Parser.parse (Parser.java:58)
    at org.openrewrite.maven.MavenMojoProjectParser.parseMaven (MavenMojoProjectParser.java:478)
    at org.openrewrite.maven.MavenMojoProjectParser.listSourceFiles (MavenMojoProjectParser.java:150)
    at org.openrewrite.maven.AbstractRewriteMojo.loadSourceSet (AbstractRewriteMojo.java:257)
    at org.openrewrite.maven.AbstractRewriteMojo.listResults (AbstractRewriteMojo.java:239)
    at org.openrewrite.maven.AbstractRewriteRunMojo.execute (AbstractRewriteRunMojo.java:53)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:126)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2 (MojoExecutor.java:328)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:316)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:212)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:174)
    at org.apache.maven.lifecycle.internal.MojoExecutor.access$000 (MojoExecutor.java:75)
    at org.apache.maven.lifecycle.internal.MojoExecutor$1.run (MojoExecutor.java:162)
    at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute (DefaultMojosExecutionStrategy.java:39)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:159)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:105)
    at io.takari.maven.builder.smart.SmartBuilderImpl.buildProject (SmartBuilderImpl.java:209)
    at io.takari.maven.builder.smart.SmartBuilderImpl$ProjectBuildTask.run (SmartBuilderImpl.java:81)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:539)
    at java.util.concurrent.FutureTask.run (FutureTask.java:264)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1136)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:635)
    at java.lang.Thread.run (Thread.java:833)
timtebeek commented 11 months ago

Looks like the Maven parser has an issue with there not being a Maven project in debug/org.eclipse.debug.ui. Perhaps you'd be interested in trying out either the Moderne platform or the Moderne CLI? Those also pick up Java classes outside Maven projects, which it looks like you'd need here.

For the Moderne platform I'd first need to ingest those projects before I can make them available; the Moderne CLI you can already download and try on your own machine.

laeubi commented 11 months ago

@timtebeek just in case you want to improve the plugin org.apache.maven.model.locator.ModelLocator / org.apache.maven.model.io.ModelReader should be the way to read the pom for a project.

Its still strange that there is no error but these classcast exceptions.

timtebeek commented 11 months ago

Never worked with those classes, and a bit unsure how to fit them in; would that help pick up a build file in debug/org.eclipse.debug.ui? Curious to know which file that refers to, just to learn more.

We currently have Maven tell us which projects there are, but I guess that breaks down when running on a sub module folder. https://github.com/openrewrite/rewrite-maven-plugin/blob/ef0eb3ac993c03298323aa477b000ddf0e670da6/src/main/java/org/openrewrite/maven/MavenMojoProjectParser.java#L141-L151

Any suggestions for improvement welcome, always good to learn more and expand our coverage.

For larger (and especially OSS) projects we do recommend the Moderne UI and CLI though, as that allows you to use pre-built models of your code, to speed things up and allow runs against large projects as a whole.

laeubi commented 11 months ago

I just guessing from the location of the exception mention org.openrewrite.maven.MavenParser.parseInputs, but not sure for what purpose one needs to really parse the poms (I suppose maybe for refactoring them), so probably there is something the parser don't like.

In general polyglot works by choosing another file than the default pom.xml as the materialized project-object-model, with ModelLocator you can locate such alternative names, you can output the generated pom by using:

-Dpolyglot.dump.pom=generated_pom.xml

You find other "dialects" here: https://github.com/takari/polyglot-maven

timtebeek commented 11 months ago

Thanks for background there on polyglot; I'd vaguely been aware of that up to now; hadn't encountered it yet, nor can I find how you're using it for that particular folder.

We parse the Maven build files to resolve dependencies, such that we have all types available to attribute LST elements. We need those types for our matchers before we can do replacements; missing dependencies, and thus types, would result in changes not being applied where you would otherwise expect them. That's also why we recommend to run against the full project, rather than a sub module only, as there's likely inter module dependencies there.

Running against the project as a whole might then also have Maven discover the projects that use polyglot, although that's merely my expectation, not something that I've tried. We'll see how the ingestion into app.moderne,io goes, and I'll keep you posted if you can do recipe runs there.

laeubi commented 11 months ago

Great, if you need any further information about polyglot let me know, Tycho makes heavy use of that an I currently try to get open-rewrite going in the Eclipse universe, in general you should get all dependencies from the model you don't need to parse the pom as it (especially for Tycho case) only has an incomplete set of dependencies.

laeubi commented 11 months ago

@timtebeek I just found how Tycho loads the pom for modifications:

https://github.com/eclipse-tycho/tycho/blob/811d3895a1714003d580a0c6e40553db1d839d4c/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/engine/ProjectMetadataReader.java#L106-L123

to see if this is a generated pom (aka not modifiable) we check for the file to start with .polyglot on some places.

praveensrao commented 10 months ago

I have the same error even with moderne CLI

[ERROR] Failed to execute goal io.moderne:moderne-lst-maven-plugin:2.5.0:compileLst (default-cli) on project postDeployTasks: A type incompatibility occurred while executing io.moderne:moderne-lst-maven-plugin:2.5.0:compileLst: class org.openrewrite.tree.ParseError cannot be cast to class org.openrewrite.xml.tree.Xml$Document (org.openrewrite.tree.ParseError and org.openrewrite.xml.tree.Xml$Document are in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @6987a133)
timtebeek commented 8 months ago

Thanks both! I've renamed the issue to better match what's going on as we've learned more; If I understood correctly we can use the PlexusContainer to get the ModelProcessor to locate the generated(?) pom files, which then help parse the project. I'm not sure I can fit that in myself on short term, but open to go over any PR that adds this to the plugin here.

Once we parse those generated pom files we might incidentally also end up modifying them with recipes; not sure if that's an expected issue, but figured point it out just in case.

timtebeek commented 8 months ago

Also came across this snippet in the MavenMojoProjectParser that might factor in here; although it was already in the version reported above: https://github.com/openrewrite/rewrite-maven-plugin/commit/12ff85a674761cd11851825bc6d0d0c098969f03

laeubi commented 8 months ago

@timtebeek yes in the end there is a pom file generated, but in general you should not need to read the file directly as the modelreader can do so. What makes me wonder why not use the MavenProject#getModel in the first place?