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

build(shade): shade and relocate dependencies to avoid conflicts #224

Merged
merged 17 commits into from
Oct 19, 2023

Conversation

andrewazores
Copy link
Member

@andrewazores andrewazores commented Oct 6, 2023

Related to #223
Fixes #213
Closes #214

This shades and relocates dependencies to minimize the risk that the Agent JAR conflicts with the application it is being loaded into. SmallRye Config is not (yet) relocated because I've been having a hard time getting that to work, it seems to do a lot of runtime dynamic lookups and my shade plugin configuration knowledge is failing here.

I was also hoping to come up with a solution to #213 here where an slf4j implementation could be shaded and relocated in so that the compile target is slf4j-api but both the API and implementation would be hidden away from the target application's classpath, but I haven't solved that yet either. The other solution I have been trying to chase is to get slf4j working in the usual way, where the Agent only depends on and compiles against slf4j-api, and then picks up a specific provider at runtime, but it seems that the classloading order breaks this - the Agent JAR is loaded first, containing only slf4j-api, and cannot locate any logging providers, so it auto-configures the no-op logging provider. Then the rest of the classpath is loaded, and if there is any other slf4j provider there, it is too late for the Agent to pick up and use.

As an interim "solution" for the slf4j problem above, this PR includes slf4j-api as the API compilation target and slf4j-simple as the provider, both included in the shaded JAR. The SimpleLogger provider just writes to System.err and I included a simplelogger.properties file to configure it to show a date/time stamp. It's possible for the end user to further configure this by overriding system properties on the logger even after initialization so that subsequent messages are written to System.out or to a local file, but this is not a proper slf4j facade that allows full configuration to write out to a different logging framework like log4j/logback/etc. But at the least, it should not cause classloading conflicts at runtime or other strangeness. It does mean that slf4j JARs may be found at runtime which may conflict with other logging libraries - see my Spring Petclinic fork, for example, which has to explicitly exclude a Spring logging component to avoid a classloading error at boot.

To test, this should use the very latest cryostat-core due to cryostatio/cryostat-core#268 :

diff --git a/pom.xml b/pom.xml
index abdecb0..409458f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,7 +50,7 @@
   <com.google.dagger.version>2.47</com.google.dagger.version>
   <com.google.dagger.compiler.version>${com.google.dagger.version}</com.google.dagger.compiler.version>
 
-  <io.cryostat.core.version>2.23.0</io.cryostat.core.version>
+  <io.cryostat.core.version>2.25.0-SNAPSHOT</io.cryostat.core.version>
 
   <org.apache.commons.io.version>2.11.0</org.apache.commons.io.version>
   <org.apache.httpcomponents.httpclient.version>4.5.14</org.apache.httpcomponents.httpclient.version>

Check out cryostat-core and mvn install it, then build the Agent as of this PR:

$ ./mvnw clean install

Then build and run sample applications with the PR agent:


quarkus-test

$ gh repo clone https://github.com/andrewazores/quarkus-test
$ ./build.bash && ./run.bash

Ensure that the quarkus-test server comes up with output like:

Starting the Java application using /opt/jboss/container/java/run/run-java.sh ...
INFO exec  java -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+ExitOnOutOfMemoryError -Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dcom.sun.management.jmxremote.port=9097 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -javaagent:/deployments/app/cryostat-agent-shaded.jar -cp "." -jar /deployments/quarkus-run.jar 
2023-10-10 20:19:15:209 +0000 [cryostat-agent-main] INFO io.cryostat.agent.Agent - Cryostat Agent starting...
2023-10-10 20:19:15:400 +0000 [cryostat-agent-main] INFO io.cryostat.agent.shaded.io.cryostat.core.net.JFRConnectionToolkit - Computed self JVM ID: Uvh9AvpleF94VJkLRZRlRQ5X5kfQoos89gibXIYQU5U=
2023-10-10 20:19:15:401 +0000 [cryostat-agent-main] ERROR io.cryostat.agent.Agent - Agent startup failure
java.util.NoSuchElementException: SRCFG00040: The config property cryostat.agent.baseuri is defined as the empty String ("") which the following Converter considered to be null: io.cryostat.agent.shaded.io.smallrye.config.ImplicitConverters$ConstructorConverter
	at io.cryostat.agent.shaded.io.smallrye.config.SmallRyeConfig.convertValue(SmallRyeConfig.java:300)
	at io.cryostat.agent.shaded.io.smallrye.config.SmallRyeConfig.getValue(SmallRyeConfig.java:242)
	at io.cryostat.agent.shaded.io.smallrye.config.SmallRyeConfig.getValue(SmallRyeConfig.java:167)
	at io.cryostat.agent.ConfigModule.provideCryostatAgentBaseUri(ConfigModule.java:104)
	at io.cryostat.agent.ConfigModule_ProvideCryostatAgentBaseUriFactory.provideCryostatAgentBaseUri(ConfigModule_ProvideCryostatAgentBaseUriFactory.java:44)
	at io.cryostat.agent.ConfigModule_ProvideCryostatAgentBaseUriFactory.get(ConfigModule_ProvideCryostatAgentBaseUriFactory.java:35)
	at io.cryostat.agent.ConfigModule_ProvideCryostatAgentBaseUriFactory.get(ConfigModule_ProvideCryostatAgentBaseUriFactory.java:13)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
	at io.cryostat.agent.MainModule_ProvideCryostatClientFactory.get(MainModule_ProvideCryostatClientFactory.java:66)
	at io.cryostat.agent.MainModule_ProvideCryostatClientFactory.get(MainModule_ProvideCryostatClientFactory.java:15)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
	at io.cryostat.agent.MainModule_ProvideRegistrationFactory.get(MainModule_ProvideRegistrationFactory.java:74)
	at io.cryostat.agent.MainModule_ProvideRegistrationFactory.get(MainModule_ProvideRegistrationFactory.java:13)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
	at io.cryostat.agent.DaggerAgent_Client$ClientImpl.registration(DaggerAgent_Client.java:247)
	at io.cryostat.agent.Agent.main(Agent.java:49)
	at io.cryostat.agent.Agent.lambda$agentmain$2(Agent.java:130)
	at java.base/java.lang.Thread.run(Thread.java:833)
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2023-10-10 20:19:15,656 INFO  [io.quarkus] (main) quarkus-test 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.7.2.Final) started in 0.360s. Listening on: http://0.0.0.0:10010
2023-10-10 20:19:15,657 INFO  [io.quarkus] (main) Profile prod activated. 
2023-10-10 20:19:15,657 INFO  [io.quarkus] (main) Installed features: [cdi, rest-client, rest-client-jackson, resteasy, smallrye-context-propagation, vertx]
^C2023-10-10 20:19:20,372 INFO  [io.quarkus] (Shutdown thread) quarkus-test stopped in 0.015s

spring-petclinic

$ gh repo clone https://github.com/andrewazores/spring-petclinic
$ ./build.bash && ./run.bash

Ensure that the Spring server comes up with output like:

Setting Active Processor Count to 32
Calculating JVM memory based on 44299196K available memory
For more information on this calculation, see https://paketo.io/docs/reference/java-reference/#memory-calculator
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx43633803K -XX:MaxMetaspaceSize=153392K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 44299196K, Thread Count: 250, Loaded Class Count: 24668, Headroom: 0%)
Enabling Java Native Memory Tracking
Adding 137 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -javaagent:/workspace/BOOT-INF/lib/cryostat-agent-0.4.0-SNAPSHOT-shaded.jar -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:ActiveProcessorCount=32 -XX:MaxDirectMemorySize=10M -Xmx43633803K -XX:MaxMetaspaceSize=153392K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
2023-10-10 20:20:21:360 +0000 [cryostat-agent-main] INFO io.cryostat.agent.Agent - Cryostat Agent starting...
Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
2023-10-10 20:20:21:606 +0000 [cryostat-agent-main] INFO io.cryostat.agent.shaded.io.cryostat.core.net.JFRConnectionToolkit - Computed self JVM ID: ayqeIZLhJHmJBi3JfIaesNzJk4bSzMLDVunEnETEwjU=
2023-10-10 20:20:21:608 +0000 [cryostat-agent-main] ERROR io.cryostat.agent.Agent - Agent startup failure
java.util.NoSuchElementException: SRCFG00040: The config property cryostat.agent.baseuri is defined as the empty String ("") which the following Converter considered to be null: io.cryostat.agent.shaded.io.smallrye.config.ImplicitConverters$ConstructorConverter
	at io.cryostat.agent.shaded.io.smallrye.config.SmallRyeConfig.convertValue(SmallRyeConfig.java:300)
	at io.cryostat.agent.shaded.io.smallrye.config.SmallRyeConfig.getValue(SmallRyeConfig.java:242)
	at io.cryostat.agent.shaded.io.smallrye.config.SmallRyeConfig.getValue(SmallRyeConfig.java:167)
	at io.cryostat.agent.ConfigModule.provideCryostatAgentBaseUri(ConfigModule.java:104)
	at io.cryostat.agent.ConfigModule_ProvideCryostatAgentBaseUriFactory.provideCryostatAgentBaseUri(ConfigModule_ProvideCryostatAgentBaseUriFactory.java:44)
	at io.cryostat.agent.ConfigModule_ProvideCryostatAgentBaseUriFactory.get(ConfigModule_ProvideCryostatAgentBaseUriFactory.java:35)
	at io.cryostat.agent.ConfigModule_ProvideCryostatAgentBaseUriFactory.get(ConfigModule_ProvideCryostatAgentBaseUriFactory.java:13)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
	at io.cryostat.agent.MainModule_ProvideCryostatClientFactory.get(MainModule_ProvideCryostatClientFactory.java:66)
	at io.cryostat.agent.MainModule_ProvideCryostatClientFactory.get(MainModule_ProvideCryostatClientFactory.java:15)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
	at io.cryostat.agent.MainModule_ProvideRegistrationFactory.get(MainModule_ProvideRegistrationFactory.java:74)
	at io.cryostat.agent.MainModule_ProvideRegistrationFactory.get(MainModule_ProvideRegistrationFactory.java:13)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
	at io.cryostat.agent.DaggerAgent_Client$ClientImpl.registration(DaggerAgent_Client.java:247)
	at io.cryostat.agent.Agent.main(Agent.java:49)
	at io.cryostat.agent.Agent.lambda$agentmain$2(Agent.java:130)
	at java.base/java.lang.Thread.run(Unknown Source)


              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/

:: Built with Spring Boot :: 3.1.3


[main] INFO org.springframework.samples.petclinic.PetClinicApplication - Starting PetClinicApplication v3.1.0-SNAPSHOT using Java 17.0.7 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
[main] INFO org.springframework.samples.petclinic.PetClinicApplication - No active profile set, falling back to 1 default profile: "default"
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 23 ms. Found 2 JPA repository interfaces.
[main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
Oct 10, 2023 8:20:22 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Oct 10, 2023 8:20:22 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/10.1.12]
Oct 10, 2023 8:20:22 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring embedded WebApplicationContext
[main] INFO org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 731 ms
Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:019caa0a-ff9a-46ca-bd75-3937367e0fa2 user=SA
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
Oct 10, 2023 8:20:22 PM org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [name: default]
Oct 10, 2023 8:20:22 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.2.7.Final
Oct 10, 2023 8:20:22 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000406: Using bytecode reflection optimizer
Oct 10, 2023 8:20:22 PM org.hibernate.bytecode.internal.BytecodeProviderInitiator buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : bytebuddy
[main] INFO org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer
Oct 10, 2023 8:20:22 PM org.hibernate.bytecode.internal.BytecodeProviderInitiator buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : bytebuddy
Oct 10, 2023 8:20:23 PM org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
[main] INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
[main] INFO org.springframework.data.jpa.repository.query.QueryEnhancerFactory - Hibernate is in classpath; If applicable, HQL parser will be used.
[main] INFO org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver - Exposing 13 endpoint(s) beneath base path '/actuator'
[main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
[main] INFO org.springframework.samples.petclinic.PetClinicApplication - Started PetClinicApplication in 2.412 seconds (process running for 2.589)
^C[SpringApplicationShutdownHook] INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
[SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
[SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.

Cryostat Smoketest

After building the quarkus-test sample application, the usual Cryostat smoketest.sh should also use that rebuilt image. Use ex. podman logs -f quarkus-test-agent-1 and ensure that this has logging output from the Agent that indicates it is working as usual.

@andrewazores andrewazores requested a review from ebaron as a code owner October 6, 2023 20:02
@github-actions github-actions bot added the needs-triage Needs thorough attention from code reviewers label Oct 6, 2023
@mergify mergify bot added the safe-to-test label Oct 6, 2023
@andrewazores andrewazores added build backport high-priority and removed needs-triage Needs thorough attention from code reviewers labels Oct 6, 2023
@andrewazores
Copy link
Member Author

@andrewazores andrewazores marked this pull request as draft October 6, 2023 20:44
@andrewazores andrewazores marked this pull request as ready for review October 10, 2023 20:31
@andrewazores
Copy link
Member Author

@ebaron I think this is mostly done. There could still be more work to do to clean up the SLF4J stuff, but I'm wary of over-investing time into this now when I think the current state will already (mostly?) solve the original user report that led to this work. If I have time in the release window I may continue looking at this but for now I think it's enough.

@andrewazores
Copy link
Member Author

@Josh-Matsuoka if any of this shaded JAR stuff looks at all familiar, please do give it a readover and let me know if there is anything I missed. Otherwise I would appreciate if you could also verify building it and testing it with the two sample applications and check that it works for you as well.

@Josh-Matsuoka
Copy link
Contributor

Not entirely familiar with the shaded jar stuff but from what I understand it looks fine. Verified that both sample applications work nicely.

@andrewazores
Copy link
Member Author

I'll wait a while longer and see if @ebaron will have time for review too, since this is a fairly substantial change to the way that the artifact gets built and distributed.

Copy link
Member

@ebaron ebaron left a comment

Choose a reason for hiding this comment

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

This looks fine to me 👍

@andrewazores andrewazores merged commit a497104 into cryostatio:main Oct 19, 2023
@andrewazores andrewazores deleted the shaded-deps branch October 19, 2023 20:19
mergify bot pushed a commit that referenced this pull request Oct 19, 2023
andrewazores added a commit that referenced this pull request Oct 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[Bug] pom.xml uses slf4j-jdk14
3 participants