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

Make it possible to run Spring ConversionService with Substrate VM #507

Closed
sdeleuze opened this issue Jul 2, 2018 · 10 comments
Closed

Make it possible to run Spring ConversionService with Substrate VM #507

sdeleuze opened this issue Jul 2, 2018 · 10 comments
Assignees

Comments

@sdeleuze
Copy link
Collaborator

sdeleuze commented Jul 2, 2018

With this simple project that has only a dependency on spring-core-5.0.7.RELEASE:

import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;

public class Converter {

	private ConversionService service = new DefaultConversionService();

	public static void main(String[] args) {
		new Converter().convert("12345", Integer.class);
	}

	private void convert(String string, Class<Integer> type) {
		assert service.canConvert(String.class, Integer.class);
		System.err.println(service.convert(string, type));
	}
}

With following proxy configuration:

[
  ["java.lang.reflect.ParameterizedType", "org.springframework.core.SerializableTypeWrapper$SerializableTypeProxy", "java.io.Serializable"]
]

Using on GraalVM master, it complies fine but generates this error when running:

Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.lang.Throwable.<init>(Throwable.java:310)
	at java.lang.Exception.<init>(Exception.java:102)
	at java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:89)
	at java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:72)
	at com.oracle.svm.reflect.proxies.Proxy_1_Converter_main.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:173)
Caused by: java.lang.IllegalArgumentException: Unsupported Type class: java.lang.Class
	at java.lang.Throwable.<init>(Throwable.java:265)
	at java.lang.Exception.<init>(Exception.java:66)
	at java.lang.RuntimeException.<init>(RuntimeException.java:62)
	at java.lang.IllegalArgumentException.<init>(IllegalArgumentException.java:52)
	at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:161)
	at org.springframework.core.SerializableTypeWrapper.forGenericInterfaces(SerializableTypeWrapper.java:102)
	at org.springframework.core.ResolvableType.getInterfaces(ResolvableType.java:476)
	at org.springframework.core.ResolvableType.as(ResolvableType.java:436)
	at org.springframework.core.convert.support.GenericConversionService.getRequiredTypeInfo(GenericConversionService.java:292)
	at org.springframework.core.convert.support.GenericConversionService.addConverterFactory(GenericConversionService.java:111)
	at org.springframework.core.convert.support.DefaultConversionService.addScalarConverters(DefaultConversionService.java:135)
	at org.springframework.core.convert.support.DefaultConversionService.addDefaultConverters(DefaultConversionService.java:88)
	at org.springframework.core.convert.support.DefaultConversionService.<init>(DefaultConversionService.java:52)
	at Converter.<init>(Converter.java:6)
	at Converter.main(Converter.java:9)
	... 3 more

Any chance to fix this or to give us some hints about how to find to right configuration to avoid this error?

@dsyer
Copy link

dsyer commented Jul 2, 2018

I think this is because Class is not Serializable in a native image. This program behaves differently when run using java and as a native image:

public class Types {

    public static void main(String[] args) {
        new Types().run();
    }

	private void run() {
        System.err.println(Class.class instanceof Serializable);
	}

}

@sdeleuze
Copy link
Collaborator Author

sdeleuze commented Jul 2, 2018

Good catch, we are going to avoid making such assumption in Spring Framework 5.1, see SPR-16992.

@sdeleuze sdeleuze closed this as completed Jul 2, 2018
@sdeleuze
Copy link
Collaborator Author

sdeleuze commented Jul 2, 2018

I am reopening this issue since we found a very strange behavior.

Code bellow

final class SerializableTypeWrapper {

	private static final boolean javaLangClassSerializable = Serializable.class.isAssignableFrom(Class.class);

	static Type forTypeProvider(final TypeProvider provider) {
		System.out.println("Init: " + javaLangClassSerializable);
		System.out.println("Runtime: " + Serializable.class.isAssignableFrom(Class.class));	
	}
}

Prints:

Init: true
Runtime: false

Isn't that strange that Serializable.class.isAssignableFrom(Class.class) returns different results depending on the lifecycle?

@sdeleuze sdeleuze reopened this Jul 2, 2018
@sdeleuze
Copy link
Collaborator Author

sdeleuze commented Jul 4, 2018

In addition to the point raised above, I would be interested to know what conditional test we should use to detect that we are compiler/running as native images. Ideally something that does not require GraalVM specific API.

@christianwimmer
Copy link

The static initializer that initializes javaLangClassSerializable runs during image generation, i.e., on the Java HotSpot VM with the normal java.lang.Class implementation of the JDK - it implements Serializable.

The code in the method forTypeProvider runs at image run time, i.e., with the Class implementation of Substrate VM - it does not (yet) implement Serializable. Therefore you are seeing this difference.

I'm changing the Class implementation of Substrate VM (the class DynamicHub if you are interested in the implementation details) to also implement Serializable. There is no reason to have an observable difference here.

Note that Substrate VM does not support Java serialization yet.

@christianwimmer
Copy link

Regarding the conditional test: Currently there is nothing like that. The only option I see that works without GraalVM API is to have a system property that has a different value during image generation and at image run time.

@christianwimmer christianwimmer self-assigned this Jul 6, 2018
@sdeleuze
Copy link
Collaborator Author

sdeleuze commented Jul 7, 2018

For the conditional test, for such need we usually detect classes with Class.forName(), maybe we can just do that with one of the class from GraalVM API like RuntimeReflection if such class is automatically present in the classpath when compiling/running as native image?

@christianwimmer
Copy link

We merged a new API last week that adds official support for detecting if you are running in the image builder or at image runtime using a system property:

8c07a0f

Since the property name and its values are considered API (and not just the static field names), you can add a system property based check (basically copying the contents of the methods of the ImageInfo class.

The properties will be in the August release of GraalVM (probably going to be online in the first week of August).

@christianwimmer
Copy link

christianwimmer commented Jul 16, 2018

And the fix for Serializable of java.lang.Class will also be in the August release.

@n0mer
Copy link

n0mer commented Nov 9, 2018

@christianwimmer is this fix deployed ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants