Skip to content
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

Merged
merged 4 commits into from
Feb 22, 2018

Conversation

lordofthejars
Copy link
Member

@lordofthejars lordofthejars commented Feb 8, 2018

Short description of what this resolves:

Creates SPI to resolve placeholders in configuration files

Changes proposed in this pull request:

  • Creates ConfigurationPlaceholderResolver SPI
  • Move current logic of sysprop/env into SPI
  • Implemented classpath resolution

After the discussion I will:

  • Update javadoc
  • Update docs

Fixes #159

Copy link
Contributor

@MatousJobanek MatousJobanek left a 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();
Copy link
Contributor

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);
}
});
Copy link
Contributor

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 {
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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? :-)

Copy link
Member Author

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

Copy link
Member Author

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.
Copy link
Contributor

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 {
Copy link
Contributor

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);
Copy link
Contributor

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?

Copy link
Member Author

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?

Copy link
Contributor

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...

Copy link
Member Author

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);
Copy link
Contributor

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

Copy link
Contributor

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

Copy link
Member Author

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?

Copy link
Contributor

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.
Copy link
Contributor

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

Copy link
Member Author

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.

Copy link
Contributor

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 {


Copy link
Contributor

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

@lordofthejars
Copy link
Member Author

@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));
Copy link
Contributor

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
Copy link
Contributor

@MatousJobanek MatousJobanek Feb 9, 2018

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()

Copy link
Member Author

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.

Copy link
Member Author

@lordofthejars lordofthejars Feb 9, 2018

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.

Copy link
Contributor

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);
Copy link
Contributor

@MatousJobanek MatousJobanek Feb 9, 2018

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

<artifactId>arquillian-test-spi</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
Copy link
Contributor

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

Copy link
Contributor

@MatousJobanek MatousJobanek left a 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 :-)

Copy link
Member

@bartoszmajsak bartoszmajsak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

triumph-colbert-report

Great stuff @lordofthejars! Thank you.

Can you make JIRA for it and link the PR?

@AfterClass
public static void clearSysprops() {
System.clearProperty(ConfigurationRegistrar.ARQUILLIAN_XML_PROPERTY);
System.clearProperty(SYSPROP_ARQ_CONTAINER);
Copy link
Member

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?

Copy link
Member Author

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 😢

@lordofthejars
Copy link
Member Author

@lordofthejars lordofthejars merged commit 57e2417 into arquillian:master Feb 22, 2018
@lordofthejars lordofthejars deleted the issue-159 branch February 22, 2018 09:30
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extend expression language in arquillian configuration
3 participants