-
-
Notifications
You must be signed in to change notification settings - Fork 351
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
review fix(MavenLauncher): Improving MavenLauncher dependancy resolution #2112
Conversation
…arent pom, and other corner cases
is it ready for review ? |
This class needs a major refactoring because it is a real mess. |
OK it's a WIP then, I edited the title. |
I propose to change MavenLauncher's behavior: Instead of implementing every single rules of maven dependency resolution to build a classpath, I propose to build it by invoking maven.
|
LOGGER.log(Level.ERROR, "Unable to read the pom of the dependency:" + dependence.toString(), e); | ||
} | ||
return dependence; | ||
private File getDefaultMavenHome() throws SpoonException { |
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.
outch! This method looks really ugly!
Can't you rely on environment variable instead? The M2_HOME
was considered as a standard one, but it's not heavily used.
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.
Oh you saw that...Fine, I'll correct it.
Maven is written in Java. Shouldn't we get the implementation of Maven concerning their dependency resolution? |
I agree, but I could not find the documentation to do that. I'll look better. |
It's maven: the documentation is the source ;) https://github.com/apache/maven-dependency-plugin/tree/maven-dependency-plugin-3.1.1 |
I've tried to implement a version that uses directly MavenCli programatically (No calls to MavenInvoker). But to be honest, I am unsure whether it is a good idea. I am starting to think that there is a reason why most people seem to use MavenInvoker. I think it is more stable because it isolate the nested call to maven. What do you think? (Am I missing something?) |
So if I sum up: the problem is Maven dependencies are difficult to resolve, so the good way to do it is to use Maven directly. One solution is then to use a Maven Invoker, but clients would have to specify their Maven path. Then yeah maybe it makes sense to use your first solution...
WDYT? |
Yes, that is at least how I see it.
I think we could also try to exec directly the command mvn -version to determine the path to maven home, before throwing an error. |
Could someone who has access to travis CI add the following environment variable |
This code is ready for review! |
chore/travis/travis-jdk10.sh
Outdated
@@ -8,6 +8,6 @@ wget https://raw.githubusercontent.com/sormuras/bach/master/install-jdk.sh | |||
chmod +x install-jdk.sh | |||
|
|||
export JAVA_HOME=$HOME/openjdk8 | |||
source ./install-jdk.sh -f 10 | |||
source ./install-jdk.sh -f 10 -c |
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 explain why you needed this argument?
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.
Yes! So basically it tells install-jdk.sh
to use the default (the one present in the base conatiner) collection of trusted certificate authority (CA) certificates for java. Without this option, calling maven from java results in SSL errors.
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.
ok, can you add a comment on the script please?
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.
Done.
pom.xml
Outdated
<dependency> | ||
<groupId>org.apache.maven.shared</groupId> | ||
<artifactId>maven-invoker</artifactId> | ||
<version>2.2</version> |
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 you have one version 3.1.1 of maven-invoker and one version 2.2? Does it make sense?
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.
Probably not. This is a leftover from previous trials. I'll fix this ASAP.
properties.setProperty("mdep.outputFile", "spoon.classpath.tmp"); | ||
request.setProperties(properties); | ||
|
||
//FIXME Should the standard output made silent and error verbose? |
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 might be interesting to use a logger debug for this one? WDYT?
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.
Ok. I'll add it ASAP.
* | ||
* @param classPathFiles File[] containing the classpath elements separated with ':' | ||
*/ | ||
public static String[] readClassPath(File ... classPathFiles) { |
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.
Why does it take an array of files here? You should create only one classpath file, don't you?
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 I thought too. But this is the behavior of maven-dependency-plugin for multi module projects. It creates one classpath file per module.
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.
OK it worthes a comment then
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.
Moreover I'm not sure the method should be public 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.
For the comment, agreed.
About the public
, my idea of it was to expose a way for the user to manually add its classpath. But if you think it is not relevent, I can totally make it private.
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.
for the user to manually add its classpath
You can still use the environment.setSourceClasspath
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.
Yes, but his method gives you what content to feed in environment.setSourceClasspath
should you choose to generate it with maven.
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.
Yeah but the purpose of this method is to be called internally to feed automatically the environment. Then you can use it to improve the classpath by adding/removing some libs.
So I'm still not sure there's a strong reason to let this one open.
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.
Ok, I made it private.
} catch (NumberFormatException ignore) { | ||
br.close(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); //These Exceptions occur after the classpath has been read, so it should not prevent normal execution. |
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.
Throw instead a SpoonException maybe?
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 can. But do we really want to throw an excepetion for a temporary file that we failed to close? I mean the error happen after reading the information we need.
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.
Actually you can use a try with resources to be sure it's closed at the end.
Something like:
try (br = new BufferedReader(new FileReader(classPathFile));) {
...
}
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.
And maybe add the throws IOException to the method itself if it's not public anymore. You'll manage those elsewhere.
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.
Ok this should be fixed now.
} | ||
if (splitVersion.length > 2) { | ||
} catch (Exception e) { | ||
e.printStackTrace(); |
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.
Remove that one.
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.
ok
} | ||
} | ||
while ((line = output.readLine()) != null) { | ||
if (line.contains("Maven home: ")) { |
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 use a constant for "Maven home: " please?
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.
Yes, indeed.
return dependence; | ||
p.waitFor(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); |
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 again: declaring two exception for the same behaviour is useless. You can catch both in the same block and throw a SpoonException 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.
Ok.
try { | ||
classPathPrints = Files.find(Paths.get(pom.getParentFile().getAbsolutePath()), | ||
Integer.MAX_VALUE, | ||
(filePath, fileAttr) -> filePath.endsWith("spoon.classpath.tmp")) |
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 again: use a constant for the name of the file.
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.
Yes.
.map(p -> p.toFile()) | ||
.collect(Collectors.toList()); | ||
} catch (IOException e) { | ||
e.printStackTrace(); |
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.
No printstacktrace
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.
Done.
|
||
// with the tests | ||
launcher = new MavenLauncher("./", MavenLauncher.SOURCE_TYPE.ALL_SOURCE); | ||
|
||
assertEquals(30, launcher.getEnvironment().getSourceClasspath().length); | ||
assertEquals(194, launcher.getEnvironment().getSourceClasspath().length); |
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 add a new test to check this new behaviour: you did this PR because on some projects dependency resolution was not working. So could you put pom.xml files that wasn't working previously and that is working now, as a test?
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 not that it used to not work on certain poms but more than some types were not resolved because not existing in the classpath where they could have been if MavenLauncher generated a classpath more similar to Maven.
I don't think it is testable just with a pom. The problem appears when analyzing an AST.
I could add a test that check that for a maven project (may be spoon itself), all types are resolvable, even those defined in dependencies. WDYT?
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 could add a test that check that for a maven project (may be spoon itself), all types are resolvable, even those defined in dependencies. WDYT?
Sounds good yes.
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.
Done.
…type references point to resolvable types
thanks @nharrand :) |
One test has been added by @nharrand in #2112 to show the usage of the new dependency resolver. However this test consumes too much memory on our CI, on the job used to deploy maven artefacts: > [ERROR] Tests run: 7, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 68.03 s <<< FAILURE! - in spoon.MavenLauncherTest [ERROR] testTypeResolution(spoon.MavenLauncherTest) Time elapsed: 38.801 s <<< ERROR! java.lang.OutOfMemoryError: GC overhead limit exceeded at spoon.MavenLauncherTest.testTypeResolution(MavenLauncherTest.java:25) https://ci.inria.fr/sos/job/Spoon-Snapshot-Deployer/1689/console I propose to ignore the test for now.
Based on @tdurieux 's work. (WiP)
This could probably benefit some more testing and/or some refactoring.
Help welcome!