Geert Olaerts
Geert Olaerts

Reputation: 81

Inline classes generate strange names on OpenJDK

I am building a Spring Boot application with Kotlin and wanted to use inline classes. I made a Spring data repository that had a method as follows:

fun getBy(name: GameName)

where GameName is an inline class

inline class GameName(val value: String)

And everything works locally, where I am running JDK 1.8.0_181-b13. But when I deploy it to Google Cloud, the Spring Boot app doesn't start up. Google Cloud seems to be running OpenJDK 1.8.0_181.

When I decompile the code locally, it looks like this:

public GameConfigurationEntity getBy_00UsoVY/* $FF was: getBy-00UsoVY*/(@NotNull String gameName, @NotNull UUID id) { ... }

But on Google Cloud Platform, I get the following error:

Caused by: java.lang.ClassFormatError: Illegal method name "getByName-MZoZWhM"

So it seems locally it uses _, while on Google Cloud it uses - and - is not a valid character in a method name.

How can I fix this?

Full stack trace:

[INFO] GCLOUD: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gameConfigurationService' defined in URL [jar:file:/app.jar!/BOOT-INF/classes!/com/hexigames/configurationservice/domain/game/GameConfigurationService.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.hexigames.configurationservice.domain.game.GameConfigurationService: Common causes of this problem include using a final class or a non-visible class; nested exception is org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:581) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548) ~[spring-context-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.6.RELEASE.jar!/:2.0.6.RELEASE]
[INFO] GCLOUD:     at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-2.0.6.RELEASE.jar!/:2.0.6.RELEASE]
[INFO] GCLOUD:     at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386) [spring-boot-2.0.6.RELEASE.jar!/:2.0.6.RELEASE]
[INFO] GCLOUD:     at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.0.6.RELEASE.jar!/:2.0.6.RELEASE]
[INFO] GCLOUD:     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242) [spring-boot-2.0.6.RELEASE.jar!/:2.0.6.RELEASE]
[INFO] GCLOUD:     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230) [spring-boot-2.0.6.RELEASE.jar!/:2.0.6.RELEASE]
[INFO] GCLOUD:     at com.hexigames.configurationservice.ConfigurationServiceApplicationKt.main(ConfigurationServiceApplication.kt:12) [classes!/:0.0.1-SNAPSHOT]
[INFO] GCLOUD:     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
[INFO] GCLOUD:     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
[INFO] GCLOUD:     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
[INFO] GCLOUD:     at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
[INFO] GCLOUD:     at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) [app.jar:0.0.1-SNAPSHOT]
[INFO] GCLOUD:     at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) [app.jar:0.0.1-SNAPSHOT]
[INFO] GCLOUD:     at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) [app.jar:0.0.1-SNAPSHOT]
[INFO] GCLOUD:     at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) [app.jar:0.0.1-SNAPSHOT]
[INFO] GCLOUD: Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.hexigames.configurationservice.domain.game.GameConfigurationService: Common causes of this problem include using a final class or a non-visible class; nested exception is org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
[INFO] GCLOUD:     at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:208) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:473) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:355) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:304) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:431) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1698) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:573) ~[spring-beans-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     ... 23 common frames omitted
[INFO] GCLOUD: Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
[INFO] GCLOUD:     at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:492) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_181]
[INFO] GCLOUD:     at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:480) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:337) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:58) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:205) ~[spring-aop-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     ... 30 common frames omitted
[INFO] GCLOUD: Caused by: java.lang.reflect.InvocationTargetException: null
[INFO] GCLOUD:     at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) ~[na:na]
[INFO] GCLOUD:     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
[INFO] GCLOUD:     at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
[INFO] GCLOUD:     at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:459) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:     ... 43 common frames omitted
[INFO] GCLOUD: Caused by: java.lang.ClassFormatError: Illegal method name "createConfiguration-tQynZQ0" in class com/hexigames/configurationservice/domain/game/GameConfigurationService$$EnhancerBySpringCGLIB$$76757398
[INFO] GCLOUD:     at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_181]
[INFO] GCLOUD:     at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_181]
[INFO] GCLOUD:     ... 48 common frames omitted
[INFO] GCLOUD:
[INFO] GCLOUD: I1101 09:02:28.364653    25 jvmti_agent.cc:225] Java VM termination
[INFO] GCLOUD: I1101 09:02:28.377487    36 jvmti_agent_thread.cc:103] Agent thread exited: CloudDebugger_main_worker_thread
[INFO] GCLOUD: I1101 09:02:28.398113    25 worker.cc:116] Debugger threads terminated
[INFO] GCLOUD: I1101 09:02:28.405902    25 jvmti_agent.cc:239] JvmtiAgent::JvmtiOnVMDeath cleanup time: 44024 microseconds

Upvotes: 3

Views: 861

Answers (2)

Paul de Vrieze
Paul de Vrieze

Reputation: 4918

The key points are in the bottom of the stack trace:

org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336) ~[spring-core-5.0.10.RELEASE.jar!/:5.0.10.RELEASE]
[INFO] GCLOUD:  ... 43 common frames omitted
[INFO] GCLOUD: Caused by: java.lang.ClassFormatError: Illegal method name "createConfiguration-tQynZQ0" in class com/hexigames/configurationservice/domain/game/GameConfigurationService$$EnhancerBySpringCGLIB$$76757398
[INFO] GCLOUD:  at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_181]
[INFO] GCLOUD:  at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_181]

In other words, Spring uses a custom class loader to generate a class on demand. This class uses the method name with a - in it. The defineClass1 method in the class loader chokes on it. As classloaders are used for loading any class, my only assumption is that somehow Google Cloud uses an adjusted class loader. As classloaders are highly security sensitive, this makes sense, but obviously your Kotlin code is not security sensitive.

According to the JVM specification the - character is not an illegal character in a method name at JVM level: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.2.2. As such, this appears to be a bug in the Google Cloud platform. Btw., the character is illegal in Java, but the JVM is much more lenient.

Upvotes: 2

ilia_b
ilia_b

Reputation: 139

This happens due to name mangling done by Kotlin compiler. Functions accepting inline classes are not callable from Java. (Official documentation)

Upvotes: 0

Related Questions