-
Notifications
You must be signed in to change notification settings - Fork 196
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
feat(#159): Creates Arquillian SPI to resolve placeholders in configuration files #162
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.
I have just a few refactoring comments, otherwise, a really good job with this new feature 👍
I really like it!
Collections.sort(configurationPlaceholderResolvers, new Comparator<ConfigurationPlaceholderResolver>() { | ||
public int compare(ConfigurationPlaceholderResolver o1, ConfigurationPlaceholderResolver o2) { | ||
Integer a = o1.precedence(); | ||
Integer b = o2.precedence(); |
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 would use firstResolver
and secondResolver
instead of o1
and o2
Integer b = o2.precedence(); | ||
return b.compareTo(a); | ||
} | ||
}); |
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 lines 81-90 could be moved to a separated method - loadAndOrderPlaceholderResolvers
- to keep this method focused only on config loading.
String getValue(String key); | ||
} | ||
|
||
static class ClasspathPropertyResolver implements PropertyResolver { |
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.
from my point of view, having the classes and interface within this class looks really chaotic.
could you please move it outside of the class?
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 problem is that it is really a hack for this specific class, it implements like a Java 8 lambda, so something to not be used outside this scope, you can find this approach in several places in JDK code. Although I agree that generally speaking is something I try always to avoid, in this case since it is for simulating lambdas, I preferred to do it in this way.
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 problem is that it is really a hack for this specific class
But the logic is called from outside of this class - ConfigurationSysPropResolver
& ClasspathConfigurationPlaceholderResolver
(btw, these two classes could have similar naming format :-)). You could also call
StringPropertyReplacer.replaceProperties(string, new ClasspathPropertyResolver())
instead of
StringPropertyReplacer.replaceClasspath(descriptorAsString)
Although I agree that generally speaking is something I try always to avoid
then don't do it :-)
in this case since it is for simulating lambdas, I preferred to do it in this way.
what would you say before the lamdas time? :-)
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.
But the logic is called from outside of this class
logic yes but from outside they do not know the existence of these classes. It is something internal.
then don't do it :-)
Generally speaking, does not mean never
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.
But if this is something you see bad I'll extract them outside
ArquillianDescriptor resolve(ArquillianDescriptor arquillianDescriptor); | ||
|
||
/** | ||
* In case then more of one placeholder resolver, they are ordered as they appear on classpath. |
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.
In case then more of one placeholder resolver
please fix the wording
import static org.jboss.arquillian.config.descriptor.impl.AssertXPath.assertXPath; | ||
|
||
@RunWith(MockitoJUnitRunner.class) | ||
public class ClasspathReplacementInArqXmlTestCase extends AbstractManagerTestBase { |
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.
correct me if I'm wrong - this class is almost same as SyspropReplacementInArqXmlTestCase
. The only difference is the test method which are just a few lines of code - merge it into one test class or (maybe better) create an abstract-template class with the common logic and extend it.
public void should_replace_classpath_in_arquillian_xml() throws Exception { | ||
|
||
final String xml = desc.get().exportAsString(); | ||
System.out.println(xml); |
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.
is the system out necessary?
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 been there in all tests from the early ages :) so I think it is not necessary but who knows :D (check other tests so you can see it). I can remove it from our tests but how about the others?
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 saw it in SyspropReplacementInArqXmlTestCase
and my feeling is that it was forgotten after some debugging. From my point of view, there is no benefit of having it there - it just generates an unnecessary ballast in the output. If it is in some other test cases that you don't change in this PR, then keep it there, but please don't put it into the new tests...
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 is in a lot of tests :D
|
||
final String xml = desc.get().exportAsString(); | ||
System.out.println(xml); | ||
AssertXPath.assertXPath(xml, "/arquillian/container/@qualifier", VALUE_EL_OVERRIDE); |
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 asserts the logic of ConfigurationSysPropResolver
- that the system property is resolved, but the method is called should_replace_classpath_in_arquillian_xml
- either remove the assert or change the method name
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 would be also good to use given-when-then comments
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.
about given/when/then this is something not used in Arquillian Core, should we start doing it and have tests written in different "layout" or continue so they look the same?
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 would say "start doing it" - btw, you have already started in ArquillianDescriptorClasspathPropertiesTestCase
;-) https://github.com/arquillian/arquillian-core/pull/162/files#diff-3f0690a66704804b5e1a1a151cae218cR15
String classpathResource = key.substring(CLASSPATH.length(), key.length() - 1); | ||
final URL resource = contextClassLoader.getResource(classpathResource); | ||
|
||
//If resource is not found it is returned as null so no change is applicable. |
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 would be also good to log some information that the resource wasn't loaded from classpath
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.
Notice that in case of system properties if they are not resolved then no log is printed, they are just returned as is, so why we should do in case of classpath? What I mean is that if we do not log in case of sysprops nor envvars then it seems reasonable to not do it in case of classpath.
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.
sysprops is something expected and obvious - it works in bash, pom files and other programming languages in the same way. Resolving ${classpath()}
is something completely special and new just for our Arquillian case... That's why I would make it transparent what is going on and why it wasn't resolved...
|
||
public class ArquillianDescriptorClasspathPropertiesTestCase { | ||
|
||
|
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.
empty line - same for the line 67
@MatousJobanek I have updated the PR |
|
||
//If resource is not found it is returned as null so no change is applicable. | ||
if (resource == null) { | ||
logger.warning(String.format("Resource %s is not find in classspath and it is not replaced.", classpathResource)); |
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.
maybe better log message would be:
String.format("Resource %s is not found on the classspath so the property %s is not replaced.", classpathResource, key)
/** | ||
* Sets the name of the arquillian.xml under test | ||
*/ | ||
@BeforeClass |
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.
both @BeforeClass
and @AfterClass
methods are almost same (including the variables), shouldn't that be moved to the template as well? I know that the arquillian.xml file name is different - that can be retrieved by an abstract method getArquillianXmlFileName()
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 understand what you mean and I will do but I always have the feeling that using abstractions in tests makes look tests somehow less readable in the sense of just checking the class and know what they are doing (you need to navigate through parent classes and be present about that), but I know it is something personal.
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.
In fact, this approach you mention does not work because @BeforeAll
is (and must) be static so we cannot retrieve it from an abstract method. So we can put this logic in @Before
block but at the end this logic is something that should be executed only once.
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, right. You know, it's Friday and my brain is probably in the weekend mode :-D
@@ -29,5 +30,8 @@ | |||
@Override | |||
public void register(ExtensionBuilder builder) { | |||
builder.observer(ConfigurationRegistrar.class); | |||
|
|||
builder.service(ConfigurationPlaceholderResolver.class, ConfigurationSysPropResolver.class); | |||
builder.service(ConfigurationPlaceholderResolver.class, ClasspathConfigurationPlaceholderResolver.class); |
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.
Please, unify the naming format of the classes:
ConfigurationSysPropResolver
ClasspathConfigurationPlaceholderResolver
config/impl-base/pom.xml
Outdated
<artifactId>arquillian-test-spi</artifactId> | ||
<version>${project.version}</version> | ||
<scope>test</scope> | ||
</dependency> |
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 dependency is not necessary, you can remove it
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.
Awesome, thanks a lot! The code is OK from my side. Just the doc is missing, but you have it in the task list already :-)
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.
@AfterClass | ||
public static void clearSysprops() { | ||
System.clearProperty(ConfigurationRegistrar.ARQUILLIAN_XML_PROPERTY); | ||
System.clearProperty(SYSPROP_ARQ_CONTAINER); |
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.
Was wondering if we can't add https://github.com/stefanbirkner/system-rules for that?
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.
Requires JDK 6 or above, we are at 5 😢
Short description of what this resolves:
Creates SPI to resolve placeholders in configuration files
Changes proposed in this pull request:
ConfigurationPlaceholderResolver
SPIclasspath
resolutionAfter the discussion I will:
Fixes #159