-
Notifications
You must be signed in to change notification settings - Fork 40
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
Config #892
Conversation
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 me, using a system variable for choosing the PlatformConfigProvider is a bad solution. You should rely only on service loader and classpath configuration. PlatformConfigProvider implementation should be discovered at runtime using service loader and without any system variable:
- if none is found use default platform config provider (normal behaviour)
- if one is found, use it (for instance to run unit tests)
- if more than one is found, throws an exception
So, in all powsybl modules that needs to run unit tests accessing platform config (almost all...), we only have to add a dependency on a Maven artifact that contains the in EmptyPlatformConfigProvider.
<dependency>
<groupid>com.powsybl</groupid>
<artifactid>powsybl-platform-config-empty<artifactid>
<version>...</version>
<scope>test</scope>
</dependency>
Also, instead of an EmptyPlatformConfig we could have a ClassLoaderPlatformConfig, that would allow developpers to add an src/test/resources/config.json to setup platform config using a json file for unit tests. That would be really convenient because in actual code we need to define platform config data programmatically in unit tests.
We had a discussion about these ideas and concluded against using them. We can still discuss. The argument against it were:
However, if we choose to implement your solution, maybe we should use slf4j behavior's of not throwing an exception for multiple implementations ? "Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random. As of version 1.6.6, SLF4J will name the framework/implementation class it is actually bound to." Or at least explicitly find a good reason not to do like slf4j.
I'm not sure about this one anymore. For the classpath, because of transitive dependencies, you either have to fix projects far away from the failing tests (hard or impossible), or use exclusions (ugly). However, the system property is maybe even worse because IDEs do not pick the maven system property configuration.. This leads many projects to warn their developpers "the tests run in maven CLI are not the same as the tests run in your IDE". If we can avoid that, we should.
What do you think ?
This means that all the tests in one maven project have the same config. I'm not sure if it's enough to solve the problem of having to define data programmatically in the tests. If it's enough, an alternative would be to configure the target/ folder as a powsybl.config.dirs of the StandardPlatformConfigLoader but I agree that the classpath approach is simpler and superior. |
I think enforcing to have only one config provider in the classpath will not be very convenient :
I think we need to have some priority system, to at least authorize having the default implementation and another one in the classpath. In that case the other one will be used. But it's probably not enough, because of the first reason mentioned above. |
But what could be the use case having 2 differents providers in your unit test? |
Do you agree that having a different behaviour in Maven, and IDE is a showstopper? It would be so a mess. |
I have an additional issue to bring to the discussion :) The Do we want to support such a feature ? |
For sure database platform config is a valid use case, but why do you need multiple PlatformConfigProvider for that? What I really like in this PR is that with a simple developement it could solve several current issues.
Platform configuration is a complex topic and I think that we must be pragmatic and not try to solve all issues / use cases in this PR, otherwise we will just never make any progress on it. |
I agree that we should try really hard to avoid this, that's why I have mentioned the problem.
the classpath is the result of a pretty complex computation (maven dependency mediation) on a pretty big graph (hundreds of deps). The system variable is a manually written string. From my experience, it happens more frequently that people get something bad in their classpath (causing things like slf4j's "Multiple bindings were found on the class path." than that they write the wrong value in their system property. Forcing a system variable is often possible in all kinds of setups (maven cli, IDEs, ...). Forcing the classpath is harder. However, what will be extremely common though is that they will forget to set the system property (like when running the test in the IDE) and we had not taken this into account when we said that it's easier to get wrong, so because of this case I take back what I said: it's easy to forget to set a system variable.
So should we do that when there are multiple implementations, we just log a warning and use a random one like SLF4J instead of throwing like you proposed initially ?
Doesn't seem crazy to me... setting the xmx for tests, configuring some loggers for tests.. For example: https://github.com/spring-projects/spring-framework/blob/master/import-into-eclipse.md "If attempting to run all JUnit tests from within the IDE, you will likely need to set the following VM options to avoid out of memory errors: -XX:MaxPermSize=2048m -Xmx2048m -XX:MaxHeapSize=2048m" Log4j2 uses this mechanism: https://logging.apache.org/log4j/2.x/manual/extending.html -DLoggerContextFactory=... and -Dlog4j.configurationFactory
Yes I try as much as I can to do this too. Using a system property to choose one of the implementations in the class path instead of expecting the classpath to contain only one implementation has the additional benefit to allow to rerun a program with a different PlatformConfigProvider. It could be useful if users report bugs and you want to disable their config, you can tell them to start the program with -Dpowsybl.config.provider=empty ? In the end, I think there are 2 different aspects to this problem:
What about looking for a system property first, and only if missing look for a hardcoded file in the classpath containing the same thing as the system property ? (Not sure, I can't tell if this is crazy or not) |
|
This proposition was that we don't set the system property at all for the tests (the system property is only used by users running programs, if they want. Or the system property could be removed entirely if we don't need the feature). Without a system property, all the info comes from the classpath so tests run the same in maven CLI and the IDE. But instead of coming from the META-INF/services/PlatformConfigProvider file, it comes from another one (we could use META-INF/powsybl-platformconfigprovider or something) This is basically saying that a module running tests can't easily control what META-INF/services/PlatformConfigProvider files they have in the classpath, but they can more easily control this other file. I've never seen this idea implemented so it fails the "More generally widely used design are often best..." rule though :) Additionally, This is a good example of how the classpath can be hard to control: Another example: (from https://www.slf4j.org/codes.html ):
Side note: It's really sad that IDEs correctly import the classpath from maven but incorrectly import system properties.. What a nuisance.. |
What about keeping the service-based approach, but with an ordering system as proposed by @mathbagu on monday ? I think it would solve most problems : the problem of enforcing the use of one implementation for tests, and the problem of not being able to inherit from one implementation for another one. It's simple to implement with a It's similar to the solution adopted in spring to order collections of beans with However, it would not solve the issue I mentioned above with external application components interoperability. I guess for this we will still need some static variable mechanism. |
PS: |
What I don't like about this approach is that it can get abused and people start using order=1000 and then order=10000 etc.. |
It sounds to me like a very theoretical downside to be compared with a very tangible added value :) What exactly is wrong in your example ? What will be the actual cost of having arbitrary values such as 10000 ? Note: it would also be possible to provide abstract implementations to let the user decide of the actual order. |
Let's go forward with the classpath solution ?
Is this OK? or can we avoid to duplicate this dependency everywhere ? If we put the dependency in the top pom (with scope=test), it's included everywhere but the project hosting the class can't use the top pom as its parent or else it would be self referencing in the dependencies. |
Side Note: in a somewhat shocking plot twist, it turns out that intellij by default correctly imports maven's systempropertyvariables and passes it to it's junit runner ( cf https://www.jetbrains.com/help/idea/running-tests.html and I tested manually using idea-IC-192.6262.58 ), so importing a project with a systempropertyvariable defined for tests works out of the box even when clicking on the little green arrow inside intellij.. In eclipse, the testng runner has this feature too ( https://testng.org/doc/eclipse.html ), but not the junit runner apparently :( Does this change anything for you ? I assume it doesn't. |
@geofjamg Did you have a solution in mind for the classpath based config ? Currently the config API uses Path objects and hence need a proper filesystem in 2 places:
I think the long term plan should be to remove filesystems entirely from the config API (breaking change). Did you want to do this now or in another PR ? An alternative would be to make the classpath based config only usable during tests and in this case we can use jimfs to implement everything. An even simpler alternative is just to use EmptyPlatformConfigProvider and make the tests work with an empty config: most tests should run with the empty config and can call defaultConfig() but tests requiring specific config must pass it explicitly and can't use defaultConfig() |
Note: Can paths be replaced with (using Files.InputStream newInputStream(Path path, OpenOption... o):
Or maybe all of them so that the API is more flexible ? |
So I pushed a new implementation called TestPlatformConfigProvider. It uses google reflections https://github.com/ronmamo/reflections and jimfs to provide a platformconfig initiliazed with the values of a folder in src/test/resources/ For now I have not implemented a new system to use this in all the modules so it's with the system properties. (Also there's a hack to use it everywhere automatically that requires jimfs and reflections to be in scope=provided in powsybl-commons but this will be be removed eventually) |
Just pushed a version implementings @geofjamg initial proposal:
PlatformConfig.defaultConfig() throws if the classpath doesn't contain exactly one serviceloader implementation of PlatformConfigProvider. This has 2 consequences:
To discuss.. |
dc023cf
to
222ec99
Compare
Google reflections doesn't work on java9+ ronmamo/reflections#202 |
5ad461b
to
930b80f
Compare
config-standard/pom.xml
Outdated
<version>3.0.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>powsybl-config-standard</artifactId> |
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'm not sure the name of this module is ok, mainly the standard
word. Maybe we should use simple
, classic
, impl
...?
config-standard/pom.xml
Outdated
<artifactId>slf4j-api</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.powsybl</groupId> |
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.
Same remark
config-standard/pom.xml
Outdated
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>junit</groupId> |
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.
Dependencies should be sorted alphabetically (groupId+artefactId)
config-standard/src/main/java/com/powsybl/configstandard/StandardPlatformConfigProvider.java
Outdated
Show resolved
Hide resolved
config-standard/src/test/java/com/powsybl/configstandard/StandardConfigTest.java
Outdated
Show resolved
Hide resolved
config-test/pom.xml
Outdated
<artifactId>slf4j-api</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.powsybl</groupId> |
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.
Same remark here
sensitivity-api/pom.xml
Outdated
@@ -52,6 +52,12 @@ | |||
<artifactId>jimfs</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.powsybl</groupId> |
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.
Fix the groupId (see previous remarks)
346b2ce
to
e3999fc
Compare
.gitignore
Outdated
@@ -31,6 +31,7 @@ | |||
/commons/target/ | |||
/computation-local/target/ | |||
/computation/target/ | |||
/config-standard/target/ |
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 has been renamed as config-classic
… default to ClassicPlatformConfigProvider, Use TestPlatformConfigProvider in tests Signed-off-by: Jon Harper <jon.harper87@gmail.com>
…odule and use a classpath only resolution mechanism Signed-off-by: Jon Harper <jon.harper87@gmail.com>
Other information:
Needs discussion: maven cli tests now behave differently from IDE tests...
Please check if the PR fulfills these requirements (please use
'[x]'
to check the checkboxes, or submit the PR and then click the checkboxes)Does this PR already have an issue describing the problem ? If so, link to this issue using
'#XXX'
and skip the restNO
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
feature
What is the current behavior? (You can also link to an open issue here)
The PlatformConfig from defaultConfig() has the hardcoded behavior of looking into powsybl.config.dirs (or ~/.itools if not defined) and looking into the environnement
What is the new behavior (if this is a feature change)?
Allow to choose a PlatformConfigProvider using system variables (-Dpowsybl.config.provider). It should implement a getConfig method that is called (once, it is cached) when using defaultConfig(). By default it keeps the old behavior.
Does this PR introduce a breaking change or deprecate an API? If yes, check the following:
NO