Having an enhancement for logging convinience in WebClient we've added jetty reactive client:
public Service () {
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
HttpClient httpClient = new HttpClient(sslContextFactory) {
#Override
public Request newRequest(URI uri) {
Request request = super.newRequest(uri);
return enhance(request);
}
};
this.webClient = WebClient.create().mutate()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
}
+--- org.eclipse.jetty:jetty-reactive-httpclient:3.0.4
| +--- org.reactivestreams:reactive-streams:1.0.3
| +--- org.eclipse.jetty:jetty-client:11.0.6 -> 9.4.44.v20210927
| | +--- org.eclipse.jetty:jetty-http:9.4.44.v20210927
| | | +--- org.eclipse.jetty:jetty-util:9.4.44.v20210927
| | | \--- org.eclipse.jetty:jetty-io:9.4.44.v20210927
| | | \--- org.eclipse.jetty:jetty-util:9.4.44.v20210927
| | \--- org.eclipse.jetty:jetty-io:9.4.44.v20210927 (*)
| \--- org.slf4j:slf4j-api:2.0.0-alpha1 -> 1.7.32
..and jetty dependency tree above showes all related jars. Build contains both 'implementation' and 'test-Implementation' of group: 'org.eclipse.jetty', name: 'jetty-reactive-client', version: "3.0.4" . It works fine but test with ok-http falls with :
org/eclipse/jetty/client/api/Request$Content
java.lang.NoClassDefFoundError: org/eclipse/jetty/client/ap/Request$Content
test :
#SpringBootTest(classes = Service.class)
#ActiveProfiles(profiles = {"test", "local"})
class ServiceTest {
#Autowired
private Service service;
#MockBean
private LogHelper logHelper;
private static MockWebServer mockBackEnd;
private static ObjectMapper objectMapper;
}
( ommiting tests for brievety.)
Any suggestions?
+--- org.eclipse.jetty:jetty-reactive-httpclient:3.0.4
| +--- org.reactivestreams:reactive-streams:1.0.3
| +--- org.eclipse.jetty:jetty-client:11.0.6 -> 9.4.44.v20210927
You have specified jetty-reactive-httpclient version 3.0.4
That requires Jetty 11 specific behaviors.
You cannot downgrade Jetty arbitrarily like that.
As noted in the README for jetty-reactive-httpclient ...
https://github.com/jetty-project/jetty-reactive-httpclient/blob/1.1.x/README.md
Reactive Client Versions
Java Min Version
Jetty Min Version
1.1.x
Java 8
Jetty 9.4.x
2.0.x
Java 11
Jetty 10.0.x
3.0.x
Java 11
Jetty 11.0.x
If you have to stick with Jetty 9 for whatever reason, then you should stick with the jetty-reactive-httpclient in the version range 1.1.x
Related
EDIT: To clarify the question, I added many explanations.
In my project dependencies, I have both slf4j and log4j. This can't be changed for some technical reason.
>gradlew dependencies
Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could
not be reused, use --status for details
> Task :dependencies
.
.
| | | +--- org.springframework.boot:spring-boot-starter-logging:2.5.7
| | | | +--- ch.qos.logback:logback-classic:1.2.7
| | | | | +--- ch.qos.logback:logback-core:1.2.7
| | | | | \--- org.slf4j:slf4j-api:1.7.32
| | | | +--- org.apache.logging.log4j:log4j-to-slf4j:2.14.1
| | | | | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.32
| | | | | \--- org.apache.logging.log4j:log4j-api:2.14.1 -> 2.17.0
| | | | \--- org.slf4j:jul-to-slf4j:1.7.32
| | | | \--- org.slf4j:slf4j-api:1.7.32
.
.
+--- org.apache.logging.log4j:log4j-core:2.17.0
| \--- org.apache.logging.log4j:log4j-api:2.17.0
+--- org.apache.logging.log4j:log4j-api:2.17.0
BUILD SUCCESSFUL in 16s
1 actionable task: 1 executed
I want to set the log level in the code, and AFAIK that can only be done with log4j. As a result, I simply look for a way to make this code line (LogManager is part of log4j) returns log4j implementation and not slf4j implementation:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
.
.
private final Logger logger = LogManager.getLogger(getClass());
Sadly, the logger's class returns is org.apache.logging.slf4j.SLF4JLogger:
System.out.println("logger class is: [" + logger.getClass() + "]");
output:
logger class is: [class org.apache.logging.slf4j.SLF4JLogger]
Even though it sounds easy, I haven't been able to do it or find an example online. What can be done?
Log4j 2.x Core and the Log4j to SLF4J Adapter are two implementations of the Log4j 2.x API. If they are both present on the classpath log4j-to-slf4j is used. That is what happens in your case: messages logged using the Log4j 2.x API are sent to SLF4J, which sends them to Logback.
That is the standard logging configuration brought by the spring-boot-starter-logging. If you want to use Log4j 2 as backend instead you need to exclude that artifact and add spring-boot-starter-log4j2.
Anyway, since you are using Spring Boot, you have two ways to set the level of a logger:
Using Spring Boot's LoggingSystem abstraction (works for all available logging systems):
final LoggingSystem loggingSystem = LoggingSystem.get(getClass().getClassLoader());
loggingSystem.setLogLevel("loggerName", LogLevel.DEBUG);
Using the underlying logging system. In your case it is Logback, so you should use:
final Logger logger = LoggerFactory.getLogger("loggerName");
if (logger instanceof ch.qos.logback.classic.Logger) {
((ch.qos.logback.classic.Logger) logger).setLevel(Level.DEBUG);
}
Remark: due to recent vulnerabilities in both Log4j and Logback, you should bump your version of Spring Boot to 2.5.8 (or 2.6.2).
Whats the full class path of LogManager? It has to be imported, there you should see the full class.
My guess is that you need to change the Import to import some log4j LogManager, not the slf4j one.
I've got a Spring Boot muli-module project with a config file in its root:
project-root
|
\-- config
| \-- file.yaml
|
\-- module1
| \-- pom.xml
|
\-- module2
| \-- src
| | \-- main
| | | \-- ...
| | \-- test
| | \-- java
| | \-- test.java
| |
| \-- pom.xml
|
\-- module3
| \-- pom.xml
...
|
\-- moduleN
| \-- pom.xml
|
\-- pom.xml
Some classes in the modules (for example project-root/module2/src/main/**.java) load the file.yaml using
new FileSystemResource("config/file.yaml");
in their constructors successfully, but when I run test.java, which contains:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Test.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class Test {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void test() {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body)...
...
}
...
}
and the resource-loading code gets covered, I get a FileNotFoundException saying the file.yaml is expected in project-root/module2/config (instead of project-root/config). Why?
You haven't said how you're running your tests, but when you run them with Maven it changes the working directory to the directory of the module which is being built and tested. In Maven terminology, this is referred to as the basedir This is documented here. If you're running the tests in your IDE, it, presumably, mimics Maven's behaviour for consistency.
I'm working under project conversion from java 8 to 11 + gradle 4.5 -> 4.10.3 and found very strange thing for me. gradle take wrong jar versions . for example part of
gradle dependencies
\--- org.springframework.security:spring-security-web:5.1.3.RELEASE -> 4.2.2.RELEASE
Line 407: | +--- org.springframework:spring-beans:4.3.5.RELEASE -> 5.1.4.RELEASE (*)
Line 408: | +--- org.springframework:spring-context:4.3.5.RELEASE -> 5.1.4.RELEASE (*)
Line 409: | +--- org.springframework:spring-core:4.3.5.RELEASE -> 5.1.4.RELEASE (*)
Line 410: | +--- org.springframework:spring-expression:4.3.5.RELEASE -> 5.1.4.RELEASE (*)
Line 411: | \--- org.springframework:spring-web:4.3.5.RELEASE -> 5.1.4.RELEASE (*)
Line 419: | | | +--- com.fasterxml.jackson.core:jackson-databind:2.9.8 -> 2.8.7 (*)
Line 420: | | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.8 -> 2.8.7
Line 423: | | | +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.8 -> 2.8.7
Line 427: | | | \--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.8 -> 2.8.7
Line 432: | | | +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.14 -> 8.5.11
Line 433: | | | +--- org.apache.tomcat.embed:tomcat-embed-el:9.0.14 -> 8.5.11
Line 434: | | | \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.14 -> 8.5.11
Line 437: | | | +--- javax.validation:validation-api:2.0.1.Final -> 1.1.0.Final
Line 438: | | | +--- org.jboss.logging:jboss-logging:3.3.2.Final -> 3.3.0.Final
spring-security-web:5.1.3.RELEASE -> 4.2.2.RELEASE !!!
tomcat-embed-core:9.0.14 -> 8.5.11!!!
tomcat-embed-el:9.0.14 -> 8.5.11!!!
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.14 -> 8.5.11!!!
and application failed in runtime:
An attempt was made to call the method org.apache.coyote.AbstractProtocol.setAcceptCount(I)V but it does not exist. Its class, org.apache.coyote.AbstractProtocol, is available from the following locations:
jar:file:/C:/yyyyyyyyyyyyyy/bin/java/TxVersionServer/lib/tomcat-embed-core-8.5.11.jar!/org/apache/coyote/AbstractProtocol.class
It was loaded from the following location:
file:/C:/Program%20Files/Dalet/DaletPlus/bin/java/TxVersionServer/lib/tomcat-embed-core-8.5.11.jar
it mean gradle use version X for compilation and version Y for runtime, why compilation was not failed ? method exists only in version 9. compilation must fail on it, no? how I can avoid it? looks like I need specify version of 3d party dependencies in my gradle too, for example: A depends on B , B depends on C, and I need to write in A: A depends on B and A depends on C. I tested it and this work, but it's look very and very wrong way. any ideas how to resolve dependencies without such version hops.
This Gradle version does not seem to support Java 11. See Issue 7835
You will have to upgrade to Gradle 5.0 to use Java 11, see release notes.
There are some mentions that Gradle itself will run with Java 11 but not for builds themselves. It looks a bit confusing but the release notes a quite clear that Java 11 support comes only with Gradle 5.0+
From my understanding Gradle will carry over all compile dependencies as runtime dependencies.
What is an instance when you should only use runtime? All child dependencies are grabbed from compile and pulled into the compilation when gradle build is called.
For example, when I do a diff on what is printed when I call
> gradle -q dependencies
The list printed out for compile and runtime are identical. Example output may show following for both:
+--- org.springframework.boot:spring-boot-starter-web: -> 1.5.4.RELEASE
| +--- org.springframework.boot:spring-boot-starter:1.5.4.RELEASE
| | +--- org.springframework.boot:spring-boot:1.5.4.RELEASE
| | | +--- org.springframework:spring-core:4.3.9.RELEASE
| | | \--- org.springframework:spring-context:4.3.9.RELEASE
| | | +--- org.springframework:spring-aop:4.3.9.RELEASE
| | | | +--- org.springframework:spring-beans:4.3.9.RELEASE
| | | | | \--- org.springframework:spring-core:4.3.9.RELEASE
| | | | \--- org.springframework:spring-core:4.3.9.RELEASE
| | | +--- org.springframework:spring-beans:4.3.9.RELEASE (*)
| | | +--- org.springframework:spring-core:4.3.9.RELEASE
| | | \--- org.springframework:spring-expression:4.3.9.RELEASE
| | | \--- org.springframework:spring-core:4.3.9.RELEASE
| | +--- org.springframework.boot:spring-boot-autoconfigure:1.5.4.RELEASE
| | | \--- org.springframework.boot:spring-boot:1.5.4.RELEASE (*)
| | +--- org.springframework.boot:spring-boot-starter-logging:1.5.4.RELEASE
| | | +--- ch.qos.logback:logback-classic:1.1.11
| | | | +--- ch.qos.logback:logback-core:1.1.11
| | | | \--- org.slf4j:slf4j-api:1.7.22 -> 1.7.25
| | | +--- org.slf4j:jcl-over-slf4j:1.7.25
| | | | \--- org.slf4j:slf4j-api:1.7.25
I have looked at this answer which helped explain a little about the difference between compile and runtime, but it only showed that runtime is when your code actually executes a dependency. When would you have a runtime dependency, but not a compile time?
A typical case involves dynamically creating classes via reflection. As a contrived example, consider this app:
package net.codetojoy;
public class App {
public static void main(String[] args) throws Exception {
Class c = Class.forName("org.apache.commons.lang3.StringUtils");
Object object = c.getConstructor().newInstance();
System.out.println("object is : " + object);
}
}
It will create an object of StringUtils from Apache Commons Lang. (This example is silly; consider a case where libA will effectively do this for classes in libB).
There is no compile-time dependency so there is no reason to burden the compile-time classpath with the jar. However at run-time, the jar is certainly required. The build.gradle file is below. It uses the application plugin which nicely bundles the dependencies into a runnable deliverable.
apply plugin: 'java'
apply plugin: 'application'
repositories {
jcenter()
}
dependencies {
runtime group: 'org.apache.commons', name: 'commons-lang3', version: '3.6'
}
mainClassName = 'net.codetojoy.App'
Example output:
$ gradle run -q
object is : org.apache.commons.lang3.StringUtils#4aa298b7
Simple project, below is my gradle dependency. This is the only CXF dependency that I declare. Also are my gradle dependencies that materialize as the result of that declared dependency. When I run my code I get the exception below. I cannot figure this out.
Help!
compile(group: 'org.apache.cxf', name: 'cxf-rt-frontend-jaxrs', version:'2.7.17')
+--- org.apache.cxf:cxf-rt-frontend-jaxrs:2.7.17
| +--- org.apache.cxf:cxf-api:2.7.17
| | +--- org.codehaus.woodstox:woodstox-core-asl:4.4.1 (*)
| | +--- org.apache.ws.xmlschema:xmlschema-core:2.1.0 -> 2.2.1
| | +--- org.apache.geronimo.specs:geronimo-javamail_1.4_spec:1.7.1
| | \--- wsdl4j:wsdl4j:1.6.3
| +--- org.apache.cxf:cxf-rt-core:2.7.17
| | +--- org.apache.cxf:cxf-api:2.7.17 (*)
| | +--- com.sun.xml.bind:jaxb-impl:2.1.13 -> 2.2.11
| | \--- org.apache.ws.xmlschema:xmlschema-core:2.1.0 -> 2.2.1
| +--- javax.ws.rs:javax.ws.rs-api:2.0-m10
| +--- org.apache.cxf:cxf-rt-bindings-xml:2.7.17
| | \--- org.apache.cxf:cxf-api:2.7.17 (*)
| \--- org.apache.cxf:cxf-rt-transports-http:2.7.17
| +--- org.apache.cxf:cxf-api:2.7.17 (*)
| \--- org.apache.cxf:cxf-rt-core:2.7.17 (*)
java.lang.NoSuchMethodError: org.apache.cxf.common.util.ClassHelper.getRealClass(Lorg/apache/cxf/Bus;Ljava/lang/Object;)Ljava/lang/Class;
at org.apache.cxf.jaxrs.provider.ProviderFactory.setCommonProviders(ProviderFactory.java:499)
at org.apache.cxf.jaxrs.client.ClientProviderFactory.setProviders(ClientProviderFactory.java:85)
at org.apache.cxf.jaxrs.provider.ProviderFactory.initBaseFactory(ProviderFactory.java:137)
at org.apache.cxf.jaxrs.client.ClientProviderFactory.initBaseFactory(ClientProviderFactory.java:77)
at org.apache.cxf.jaxrs.client.ClientProviderFactory.createInstance(ClientProviderFactory.java:56)
at org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean.initClient(JAXRSClientFactoryBean.java:377)
at org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean.createWebClient(JAXRSClientFactoryBean.java:225)
at org.apache.cxf.jaxrs.client.WebClient.create(WebClient.java:113)
java.lang.NoSuchMethodError is generally when you use the new API jar but the implementation is still the old one which doesn't have the new methods implemented. I would suggest you to check the version of the jars and see if that is accurate. If you fix the version then you would not see this error.