This question is about the proper way to read configuration in a REST service in a portable way, e.g. should run on Thorntail 2.4.0 and Wildfly 15.
This was the original implementation suggested by Thorntail
#Inject
#org.wildfly.swarm.spi.runtime.annotations.ConfigurationValue("swarm.port.offset")
private Optional<String> portOffset;
This was not working in WildFly 15 so we changed this code in the following way:
#Inject
#ConfigProperty(name="swarm.port.offset")
private Optional<String> portOffset;
And provided the system property is set, it works nicely.
However, back in Thorntail, it generates the following exception:
org.jboss.weld.exceptions.DeploymentException:
WELD-001408:
Unsatisfied dependencies for type Optional with qualifiers
#ConfigProperty
at injection point [BackedAnnotatedField] #Inject
#ConfigProperty
private com.my-company.core.internal.util.ZookeeperRegistry.portOffset
at com.my-company.core.internal.util.ZookeeperRegistry.portOffset(ZookeeperRegistry.java:0)
WELD-001475: The following beans match by type, but none have matching
qualifiers:
- Producer Method [Optional] with qualifiers [#Any #ConfigurationValue] declared as [[UnbackedAnnotatedMethod]
#ConfigurationValue #Dependent #Produces
org.wildfly.swarm.container.runtime.cdi.ConfigurationValueProducer.produceOptionalConfigValue(InjectionPoint)]
Many thanks in advance.
The code runs finally on both environments, with a single pom file.
I detail below the solution adopted.
Used #ConfigProperty, not #org.wildfly.swarm.spi.runtime.annotations.ConfigurationValue
Use #Any #ConfigProperty, resolved WELD-001475
In terms of maven dependencies, I included this dependency regardless of whether we are building for Thorntail or for WildFLy
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
</dependency>
The actual version is resolved using dependencyManagement for the Eclipse microprofile:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>2.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencyManagement>
Maven profiles are used to import Thorntail implementations that are not "core", e.g. microprofile-health, but in the case of microprofile-config, it is not necessary. For WildFly, the implementation org.wildfly.extension.microprofile.config.smallrye is provided so the library shall not be included in the war/ear.
Related
I have been upgrading one of the app to spring framwork 5.X from 3.X, one of the problem I am failing to resolve it error is following - this makes a use of spring-jms
cannot access org.springframework.context.SmartLifecycle [ERROR]
class file for org.springframework.context.SmartLifecycle not found
I can see last version the exact same code works with Spring 4.3.9 (last of 4.X release) and breaks with Spring 5.0.0 (First of 5.X releases)
I don't see my code exposing the class SmartLifecycle but I have suspicion that DefaultMessageListenerContainer instance is at a fault.
public class Factory {
private DefaultMessageListenerContainer testListnerService;
public void start() {
final TestMessageListener testMessageListener = new TestMessageListener();
testListnerService = new DefaultMessageListenerContainer(); <-- This is where compilation breaks
testListnerService.setMessageListener(testMessageListener);
testListnerService.setSessionTransacted(true);
testListnerService.afterPropertiesSet();
}
I have created minimal example here https://github.com/milandesai47/com.jms.test/blob/master/pom.xml#L33
Am I missing anything obvious in terms of spring dependencies?
You need to add the following dependency to your pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
So I have this springboot application which I'm migrating from a WAS to a springboot setup. And I have a couple of JSPs which has to be configured. To accomodate these I added the following dependencies:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
</dependency>
The application already came with the following dependency which is being used throughout the application:
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm-jaxrpc-client</artifactId>
<version>6.0</version>
</dependency>
The issue I'm facing is that both these dependencies (jaxrpc-client and tomcat-embed-jasper) have javax.servlet.ServletContext classes in them which is causing the following error:
The method's class, javax.servlet.ServletContext, is available from the following locations:
jar:file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar!/javax/servlet/ServletContext.class
jar:file:/C:/Users/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.30/tomcat-embed-core-9.0.30.jar!/javax/servlet/ServletContext.class
It was loaded from the following location:
file:/C:/Users/.m2/repository/com/ibm/com.ibm-jaxrpc-client/6.0/com.ibm-jaxrpc-client-6.0.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of javax.servlet.ServletContext
I can't afford to remove any of these dependencies. jaxrpc-client is being referenced in the code already in too many places and I need tomcat-embed-jasper to render my jsp pages. I can't exclude the ServletContext class since its not a dependency(If I'm not wrong about the concept of exclusion). Please help with with a way forward with this issue.
I'm not familiar with IBM's jaxrpc client, but I assume, you have this exception in runtime, when trying to load the application.
In this case consider the following approaches:
Use another jax-rpc client library
Consider Loading the code that uses this library with the different class-loader (you'll have to create one classloader for this) to avoid the clash
Kind of paraphrasing the second option. You can "play" (override the order of loading of specific classes) with spring boot classloader as described in this article
I know, this is too general answer, but hopefully its still helpful.
The first solution is by far the easiest way I can think of.
The second solution is doable, however it pretty much depends on how exactly the code that uses the jax rpc client is loaded and used.
I'm working on a test automation framework which use TestNG. I decided to use Dependency Injection pattern in order to implement more readable, reusable page objects and tests.
I've chosen Google Guice due to TestNG provides built-in support to inject test objects with Guice Modules. I only had to specify my Guice Modules as you can see at next code snippet:
#Guice(modules = CommModule.class)
public class CommunicationTest {
#Inject
private Communication comms;
#Test
public void testSendMessage() {
Assertions.assertThat(comms.sendMessage("Hello World!")).isTrue();
}
}
So far so good, although I'm going to need more advance DI features such as:
Lifecycle management
Configuration to field mapping
Generic binding annotations
Therefore, I'd like to use Netflix/Governator since it enhance Google Guice with these features. In order to trigger Governator features I must create the Injector through it instead of TestNG. e.g:
Injector injector = LifecycleInjector.builder()
.withModules(CommModules.class).build().createInjector();
And I'd like to do it mostly transparent as possible like TestNG does it.
I would like to know if:
Is it possible to provide my own Injector instance to TestNG in order to reuse #Guice annotation approach ?
Do you know any library for integrating Governator with TestNG ?
You can find in here what I've done so far.
This was not possible until now. I have fixed this in the latest snapshot version of TestNG. It should be available in the upcoming version of TestNG (Any version greater than 7.0.0)
The issue that I created to track this : https://github.com/cbeust/testng/issues/2199
In a nutshell, you can do the following :
Implement the interface org.testng.IInjectorFactory
Plugin the fully qualified class name of the newly created implementation via the command line argument -dependencyinjectorfactory
Since Allow user to provide DI Injector TestNG feature is going to be present in versions greater than 7.0.0. I implemented a solution using TestNG version 7.0.0 listeners.
Firstly, I created a module called autopilot-testng-runner with the following dependencies:
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.governator</groupId>
<artifactId>governator</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
</dependencies>
This module contains the artifacts described at next:
#AutopilotTest: Custom annotation for declaring which Guice modules must be used for creating the Injector with LifecycleInjector.builder(). I couldn't reuse #Guice annotation due to TestNG also will create its Injector and declared dependencies will be created twice.
AutopilotSuiteListener: Implementation of ISuiteListener for creating the parent Injector, Governator's LifecycleManager instance and bind configuration properties before Suite starts. Therefore each Suite is going to have a parent Injector built with Governator and a life-cycle manager.
AutopilotTestListener: ITestListener implementation in charge of injecting dependencies in the running test case.
META-INF/services/org.testng.ITestNGListener: Service provider configuration file containing the fully qualified names of both ITestNGListener implementations.
Then, I added autopilot-testng-runner as a maven dependency in my project
<dependency>
<groupId>com.github.eljaiek.playground</groupId>
<artifactId>autopilot-testng-runner</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
And finally, I replaced #Guice annotation with #AutopilotTest
#AutopilotTest(modules = CommModule.class)
public class CommunicationTest {
#Inject
private Communication comms;
#Test
public void testSendMessage() {
Assertions.assertThat(comms.sendMessage("Hello World!")).isTrue();
}
}
Summary
When trying XMLConfiguration configuration = new XMLConfiguration("config/config.xml"); with only commons-configuration 1.10 I need to add more depencies (namely commons-collections not newer than 3.2.1) to my maven setup. Why is that so and why doesn't maven simply resolve all needed dependencies?
Details
I am trying to get commons-configuration to work. First I wanted to use the latest version, 2.0-alpha2, which didn't work well at all since I was unable to configure Maven to download the correct ressources - but that is another story.
After I found out that version 1.10 is in fact "one point ten" (not "one point one zero") and thus the latest version of commons-configuration 1 (and covered by the tutorials), I decided to give it a try instead.
For my maven dependencies (integrated in eclipse) I used:
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
However, when trying out this example:
package main;
import java.util.Iterator;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
public class ConfigurationTest {
public static void main(String... args) {
try {
XMLConfiguration configuration =
new XMLConfiguration("config/config.xml");
Iterator<String> iterator = configuration.getKeys();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
}
with the following config.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<configuration>
<property>value</property>
<nestedproperty>
<arrayvalue>0,1,2,3,4</arrayvalue>
<property>anothervalue</property>
</nestedproperty>
</configuration>
I got the error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/CollectionUtils
at org.apache.commons.configuration.XMLConfiguration.constructHierarchy(XMLConfiguration.java:640)
at org.apache.commons.configuration.XMLConfiguration.initProperties(XMLConfiguration.java:596)
at org.apache.commons.configuration.XMLConfiguration.load(XMLConfiguration.java:1009)
at org.apache.commons.configuration.XMLConfiguration.load(XMLConfiguration.java:972)
at org.apache.commons.configuration.XMLConfiguration$XMLFileConfigurationDelegate.load(XMLConfiguration.java:1647)
at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:324)
at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:261)
at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:238)
at org.apache.commons.configuration.AbstractHierarchicalFileConfiguration.load(AbstractHierarchicalFileConfiguration.java:184)
at org.apache.commons.configuration.AbstractHierarchicalFileConfiguration.<init>(AbstractHierarchicalFileConfiguration.java:95)
at org.apache.commons.configuration.XMLConfiguration.<init>(XMLConfiguration.java:261)
at main.ConfigurationTest.main(ConfigurationTest.java:12)
I first hoped they (not me, of course) just screwed up some maven dependencies and since I wouldn't bother which version to use anyway anymore (I didn't get 2.0 to work, remember?) I decided to go down to version 1.9 by replacing the maven dependency with:
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.9</version>
</dependency>
That solved the problem pretty well, the test case is running:
property
nestedproperty.arrayvalue
nestedproperty.property
But when I tried to implement a similar example to the one referenced in Very simple Apache-commons configuration example throws NoClassDefFoundError and its follow-up question I got the exact same error which is referenced there - but the solution, importing org.apache.commons.beanutils.PropertyUtils is not working as I am missing the beanutils. So basically by downgrading I just switched from the error of missing the collections to missing beanutils.
There is a dependency overview where you can see which dependencies are used when you do what. I was a bit suprised to learn that version 1.10 now used other dependencies (namely the CollectionUtils) than 1.9 did in the constructor call. Since there were dependency problems in 1.10 as well as in 1.9 I just sticked to the newer version.
I found the CollectionUtils located in the following artifact (as I was pointed there by its maven repository):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
Sadly that one (not obvious to me at first) doesn't define the class CollectionUtils in the package collections, but in the package collections4. It was hinted at this problem on the dependency overview, but they only mentioned possible problems with earlier versions... I appeared to be at a point of not thinking much about it anymore but simply changed the dependency to:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
I got everything to work (more or less, but the Exceptions I get now are not anymore depending on missing class definitions) after using these dependencies:
<dependencies>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
Why do I have to add the dependencies myself? I thought the whole point in using maven is to avoid having to do such things and in terms of javadocs and source files it does a pretty good job.
By now I am convinced that the dependencies are not included in the hierarchy by design (is that so?), probably to avoid overhead. However is there a way to either simply get all dependencies at once or even better to get all dependencies I need? And why is it designed this way?
If we analyse commons-configuration's POM we see that the commons-collections dependency is optional:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
<optional>true</optional>
</dependency>
...
Furthermore, from the Maven docs:
If a user wants to use functionality related to an optional
dependency, they will have to redeclare that optional dependency in
their own project.
This issue is explained on the Runtime dependencies page of the Commons Configuration website.
Quoting from that page:
A lot of dependencies are declared in the Maven POM. These are all needed during compile time. On runtime however you only need to add the dependencies to your classpath that are required by the parts of the Commons Configuration package you are using. The following table helps you to determine which dependencies you have to include based on the components you intend to use.
The other answers explain why this works from a Maven perspective. This answer is intended to provide a defence, of sorts, to the Commons Configuration folks. They did at least warn you!
In cases where the dependencies are on other Apache Commons components, they've taken the time to test with a variety of versions and have posted information on compatibility at the bottom of that page.
Maven tries to resolve all necessary dependencies for a library you're using in your pom. Well sometimes you have some dependencies which are only necessary for some specific features and you don't want to force the user of your dependency to download it if he doesn't use it. Then you're declaring your dependency as optional. This happened with commons-collections within commons-configuration. See commons-configuration-pom here
I've got a Heroku Java app that makes use of the Spymemcached library, which in my case is included by my use of the hibernate-memcached library (1.3).
I now need to make sure that all requests to my app go over HTTPS. This led me to this post, where the solution pivots on making use of the webapp-runner plugin and some config to get the right headers to my app (you provide the runner a context.xml).
My problem is that the webapp-runner plugin has a dependency (further down the dependency graph) on the Spymemcached library as well, which causes a conflict on start up. Furthermore, I can't downgrade webapp-runner to 7.0.22.1 as suggested by this post, as the support for specifying the context.xml came after the fact.
So I thought it would be a simple matter of excluding Spymemcached from my hibernate-memcached dependency so that only the webapp-runner's Spymemcached source would be included:
<dependency>
<groupId>com.googlecode</groupId>
<artifactId>hibernate-memcached</artifactId>
<version>1.3</version>
<exclusions>
<exclusion>
<artifactId>hibernate</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
<exclusion>
<groupId>spy</groupId>
<artifactId>spymemcached</artifactId>
</exclusion>
</exclusions>
</dependency>
But for some reason I still get the conflict on start up - on the factory bean that creates my memcachedClient which I specify in my application context:
<bean id="memcachedClient" class="net.spy.memcached.spring.MemcachedClientFactoryBean">...</bean>
Resulting in the infamous java.lang.NoClassDefFoundError:
Error loading class [net.spy.memcached.spring.MemcachedClientFactoryBean] for bean with name 'memcachedClient' defined in file [/home/markus/coding/reader/target/tomcat.8080/work/Tomcat/localhost/_/WEB-INF/classes/META-INF/spring/applicationContext.xml]: problem with class file or dependent class; nested exception is java.lang.NoClassDefFoundError: org/springframework/beans/factory/FactoryBean
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:353)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:153)...
When I search for the MemcachedClientFactoryBean in my IDE I can see that it's made available by the webapp-runner and not hibernate-memcached, so the exclusion seemed to have done something right.
Am I missing something obvious here? How do I get rid of this NoClassDefFoundError?
FYI I found out that version 7.0.22 of webapp-runner does indeed have support for providing it a context.xml by running java -jar target/webapp-runner.jar --help
It differs slightly to the later versions where you specify ... --context_xml ... instead of ... --context-xml ...
Version 7.0.22 of webapp-runner doesn't have Spymemcached as a dependency, which solves the problem.