-
Notifications
You must be signed in to change notification settings - Fork 78
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
fix(java): add configurations for Storage tests #1305
Conversation
...orage/src/test/resources/META-INF/native-image/com/google/cloud/storage/resource-config.json
Show resolved
Hide resolved
{ | ||
"name":"org.apache.commons.logging.LogFactory", | ||
"allDeclaredFields":true, | ||
"allDeclaredMethods":true, | ||
"allDeclaredConstructors": true | ||
}, | ||
{ | ||
"name":"org.apache.commons.logging.impl.Jdk14Logger", | ||
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]} | ||
, | ||
{ | ||
"name":"org.apache.commons.logging.impl.LogFactoryImpl", | ||
"allDeclaredFields":true, | ||
"allDeclaredMethods":true, | ||
"methods":[{"name":"<init>","parameterTypes":[] }]} | ||
, |
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.
We're not supposed to have any dependency on apache logging. Do you know how I might find the reference path for these classes to try and eliminate them?
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 looked into this a bit by removing the LogFactoryImpl
configuration and running mvn clean install -DskipTests
and mvn test -Pnative
. This results in the following stacktrace:
JUnit Vintage:ITStorageTest:testWriteChannelWithConnectionPool
MethodSource [className = 'com.google.cloud.storage.it.ITStorageTest', methodName = 'testWriteChannelWithConnectionPool', methodParameterTypes = '']
=> java.lang.ExceptionInInitializerError
org.apache.http.conn.ssl.SSLConnectionSocketFactory.<clinit>(SSLConnectionSocketFactory.java:151)
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.getDefaultRegistry(PoolingHttpClientConnectionManager.java:116)
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.<init>(PoolingHttpClientConnectionManager.java:123)
com.google.cloud.storage.it.ITStorageTest$CustomHttpTransportFactory.create(ITStorageTest.java:282)
com.google.cloud.storage.spi.v1.HttpStorageRpc.<init>(HttpStorageRpc.java:110)
[...]
Caused by: org.apache.commons.logging.LogConfigurationException: java.lang.ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl (Caused by java.lang.ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl)
org.apache.commons.logging.LogFactory.createFactory(LogFactory.java:1158)
org.apache.commons.logging.LogFactory$2.run(LogFactory.java:960)
java.security.AccessController.doPrivileged(AccessController.java:87)
org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:957)
org.apache.commons.logging.LogFactory.getFactory(LogFactory.java:624)
[...]
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl
jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:53)
jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
java.lang.ClassLoader.loadClass(ClassLoader.java:139)
org.apache.commons.logging.LogFactory.createFactory(LogFactory.java:1020)
[...]
It looks the usage of LogFactory is coming from PoolingHttpClientConnectionManager
which is referenced in ITStorageTest
.
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.
Ah okay, so that's testing for an optional feature. Thanks for trying it out
...-storage/src/main/resources/META-INF/native-image/com/google/cloud/storage/proxy-config.json
Show resolved
Hide resolved
com.google.gson.LongSerializationPolicy$2 | ||
|
||
|
||
|
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.
Does this mean that com.google.cloud.storage.conformance.retry.ITRetryConformanceTest#testCases
is being invoked during the building of the native image?
We're using @RunWith(ParallelParameterized.class)
to resolve test cases relative to a classpath resource, and run them in parallel using a threadpool.
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.
While standard Java initializes classes the first time they are accessed at run-time, Native image compilation tries to do some optimizations were it initializes some classes at image build time to decrease the start-up time of an application.
In our case, the JunitFeature
which is brought in by the native-maven-plugin (plugin we use to run tests with native image compilation) explicitly initializes Parameterized
at image build time. This causes ParallelParameterized
and subsequently com.google.cloud.storage.conformance.retry.ITRetryConformanceTest
and other classes ITRetryConformanceTest
references to also be initialized at build time. While the class itself is initialized at build time, it still runs at run-time (after the native image has been built).
This article has been helping me understand class initialization a bit in graal.
...torage/src/main/resources/META-INF/native-image/com/google/cloud/storage/reflect-config.json
Outdated
Show resolved
Hide resolved
@BenWhitehead any idea why some checks are stalled at the moment? |
@@ -0,0 +1,26 @@ | |||
Args = \ |
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 file that tells build-time initialization of classes of the library shouldn't be in test. Because the file is not shipped as part of the library.
As discussed today, this file being under the test directory means the users would have to write something similar to use google-cloud-storage with native-image.
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 only reason why these configurations are initialized at build-time is because com.google.cloud.storage.conformance.retry.ITRetryConformanceTest
which references them is initialized at build-time (following this logic). I'm not sure if this will apply to the regular application? WDYT
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.
That's my misunderstanding then. Sorry I missed the discussion. Would you add that comment in the file about the configuration being for the test classes?
An explanation on why so many classes (not test classes) are needed here is nice.
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.
Thanks for the suggestion! Added a comment to summarize this.
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.
Now the --initialize-at-build-time
doesn't have google-cloud-storage's main package. Nice.
"methods":[{"name":"<init>","parameterTypes":[] }]} | ||
, | ||
{ | ||
"name":"com.google.cloud.storage.BlobInfo$ImmutableEmptyMap", |
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.
Would you add comment explaining why this configuration is not in main?
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.
Sorry, my bad. I unintentionally placed this class config in the test directory. This configuration is used outside of the tests so needs to in the main/resources directory.
@@ -0,0 +1,5 @@ | |||
[ | |||
{ | |||
"interfaces":["com.google.cloud.storage.spi.v1.StorageRpc"] |
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.
Would you add comment explaining why this configuration is not in main?
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.
Thanks for point this out! I realized that this class also needs to be in the main config directory since it is referenced outside of tests. For example:
java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
Line 90 in 3716105
public class HttpStorageRpc implements StorageRpc { |
Apologies for the confusion @BenWhitehead!
Now the checks are running (Kokoro sample failed) |
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.
Verified config by running:
export JAVA_HOME=~/opt/java/11-native
mvn -Pnative clean verify
Feel free to merge when you're ready. The test failure in Samples is being dealt with separately and isn't related to any change here. |
Thank you! |
This PR adds a few reflection configurations for native image support in Storage.
prox-config.json
:The
reflect-config.json
file registers some Java classes for reflection:The
resource-config.json
file makes certain test resource accessible to the native image builder. Specifically,testNamesWhichCanFail.txt
. It is currently located under src/test as it applies to only tests.The
native-image.properties
file is located under src/test as it only applies to the tests :This file handles class initialization during native image generation. Classes can be initialized at either run-time or image build time and class initialization can result in recursive initialization of other classes. The tests in the Storage repo use
Parameterized
which is explicitly initialized at image build time by the configurations provided by GraalVM. This leads to a bunch of other unexpected classes being initialized at build time as well and leads to the following error:And initializing those same classes at run-time results in image generation failing with inconclusive results:
Initializing these classes explicitly at build-time results in a successful build.