-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compatibility with m2e 1.x - 2.x #451
Conversation
* | ||
* Adaptation layer for m2e 1.x / 2.x API | ||
*/ | ||
public class M2eCompat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is duplicated, but a clean solution would require an entire new module, which was a bit... overkill
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly a dumb question, but wouldnt it be less overkill to do it the other way?
- 2 java files, identical, we now have to update one when the other changes
- 1 java file and one pom.xml, a dependency on maven core, m2e core (and the reflection util class can live there too)
Or am I missing something, and due to the "eclipse-project" nature that new module needs to have?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a OSGi environment, I'm simply not sure how long it will take... I'm not the right man to do the job the way you say it.
Even though I agree that it stinks.
The other solution I can think of, is just removing this M2eCompat stuff, and inlining it, so that it is exactly the same, but stinks less
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i have some experience with plugin development. maybe i could help here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make a PR against this branch in foosoftsrl/gwt-eclipse-plugin with the sorts of setup one would need to do this?
Alternatively, bake in to the ReflectionUtilities API a way to pass the params to build the reflected Methods (class+methodname pairs etc), and let it return a single Method/MethodHandle or throw, and then we don't need even one copy of this class at all.
Build failed - if I'm reading this right, your pre-built jar was built with --release 17, and while this plugin is built with java 17, it is built with --release 11 so that it can be used with java 11. |
Yes, I've seen the build failure. Technically speaking; We have a class WtpMavenProjectConfigurator, derived from WTPProjectConfigurator, which overrides this method:
but ProjectConfigurationRequest is a record in m2e As long as we use methods of this record, such as mavenProject(), everything is fine, but as soon as we try to use reflection, or even invoke getClass() the compiler complains because it says that we're referring java.lang.Record There is no real reason why the compilation must fail: WtpMavenProjectConfigurator does not need to know anything about records, and working bytecode could be generated. So, I'd say that this compilation failure is more of a (very reasonable) sanity check: the compiler tells that this thing won't work on JDK11, because java.lang.Record won't be there. Why this sanity check is not performed when we simply invoke a record method is beyond me. I can't think of a clean and simple way to handle this problem... I did not find any workaround. The only solution seems to be the one proposed by @keinhaar |
Not that I really understand why, but using generics fixed the compilation issue. Tested on 2022-06 and 2022-12 Does anyone want to do more testing? |
@@ -0,0 +1,16 @@ | |||
/** | |||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Populate header?
package com.google.gdt.eclipse.core; | ||
|
||
/** | ||
* @author luca |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a real note here?
* | ||
* Adaptation layer for m2e 1.x / 2.x API | ||
*/ | ||
public class M2eCompat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly a dumb question, but wouldnt it be less overkill to do it the other way?
- 2 java files, identical, we now have to update one when the other changes
- 1 java file and one pom.xml, a dependency on maven core, m2e core (and the reflection util class can live there too)
Or am I missing something, and due to the "eclipse-project" nature that new module needs to have?
*/ | ||
public class ReflectionUtilities { | ||
|
||
public static <T, R> R invoke(T obj, String method) throws ReflectiveOperationException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we always throw away the error (except when we wrap it with IllegalStateException, but we don't need to do that, since we already threw away the other error...), maybe just return Optional instead, so that these can be chained?
Also, T doesn't serve a purpose here, probably remove it.
Finally, I'm not clear how often these methods are called, but if it is more than "once when you create the project" it might be worth caching the Method that succeeded so we can just call it for free next time, or even using a MethodHandle and caching that instead? We could fail on the lookup that way (during static init of M2eCompat), and never have to try/catch when actually doing the work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'T' is what makes the whole thing work. If we cast a Record to an Object, the compiler complains, if it's a generic it does not. I wish I knew why.
The correct solution is having a JDK 17 build, and a JDK 11 build, which is probaby not what we want.
MethodHandle and Optionals... it's not a bad idea.
About caching the handle at construction time, would you throw immediately or, say... use an Optional in order to fail later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think you should not optimize code until you have a performance problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The optimization here isn't for performance, but for complexity and brittleness - simpler code is easier for future maintainers to come back in 5+ years and understand what on earth we were thinking (which is very much a problem in other areas, see #453 for example). Avoiding duplication or checked-in compiled code makes it easier to only fix a bug in one place rather than have to search for other common patterns and clean them all up.
Pre-computing the method or methodhandle lets us fail fast, and, indicating right away that there is a problem in the constructor (or possibly slightly earlier) if the code we're runtime-linked against doesn't match either expected API. Assuming construction doesn't happen so early or in such a weird way that it otherwise breaks eclipse (that seems very unlikely to me, but worth testing).
Making that change makes the Optional not really matter at all, since you already know the method exists (...as long as it doesn't throw, but we can unwrap such an exception and rethrow, at least according to the current contract of the two variants).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@niloc132,
Quoting myself:
As long as we use methods of this record, such as mavenProject(), everything is fine, but as soon as we try to use reflection, or even invoke getClass() the compiler complains because it says that we're referring java.lang.Record
I'm really afraid that your proposal won't work. Not because it is wrong or whatever, but because we're using JDK 17 objects in JDK 11.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. You're calling m.getClass() already though?
The org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest
class is definitely being loaded and is initialized by the time you get to the assorted configure()
methods, since you have an instance of it.
Can you share the initialization error, or how to reproduce?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the error, it happens at compile time, the class is definitely not loaded yet :)
Error: Failed to execute goal org.eclipse.tycho:tycho-compiler-plugin:3.0.1:compile (default-compile) on project com.gwtplugins.gdt.eclipse.maven: Compilation failure: Compilation failure:
Error: /home/runner/work/gwt-eclipse-plugin/gwt-eclipse-plugin/plugins/com.gwtplugins.gdt.eclipse.maven/src/com/google/gdt/eclipse/maven/configurators/M2eCompat.java:[1]
Error: package com.google.gdt.eclipse.maven.configurators;
Error: ^
Error: The type java.lang.Record cannot be resolved. It is indirectly referenced from required .class files
Error: -> [Help 1]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point is that you can invoke getClass() on a ProjectConfigurationRequest, but only if you do it in a generic method.
As least... this is what I saw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @niloc132,
This is the commit which does not compile:
This is the commit where I find the generic trick (sorry they've been split by mistake)
There's nothing smart in these commits, I'm just passing the ProjectConfigurationRequest to a generic function which does not trigger the compilation error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that's a compilation error, from referencing the class literal. As you're using the 2.x m2e jar on the classpath, but telling Java 17 that it should build to Java 11, it knows that this won't work - because it doesn't know that when using Java 11 there will be a 1.x jar for m2e instead.
Instead, try Class.forName("...ProjectConfigReq")
to get the Class instance up front. This probably can even be done in the core project, since it doesn't explicitly reference the class by name, and then you can test each method to see if it is there.
I get a ClassCastException probably related to https://bugs.openjdk.org/browse/JDK-8277620
Closing this PR as it is targets a very old Eclipse version and it's too hacky |
Added M2e 1.x support, required for Eclipse 2022-06
Fixes #450