Dave Syer opened SPR-17138 and commented
GraalVM (and possibly other reflection sensitive tools) barfs really badly on the standard BeanInfo
:
./target/bunc
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::
[ [ SubstrateSegfaultHandler caught signal 11 ] ]
General Purpose Register Set Values:
RAX 000055f1af7d6478
RBX 00007f00afa6e1e8
RCX 00007f00afa6e1e8
RDX 000055f1af51d7a8
RBP 000055f1aeb6ba80
RSI 0000000000000010
RDI 000055f1af7d5d08
RSP 00007fff306e0860
R8 0000000000000010
R9 00000000c0100701
R10 00007f00afa6e158
R11 000000000000000e
R12 0000000000000010
R13 0000000000000026
R14 000000000000002e
R15 000055f1b1409260
EFL 0000000000010246
RIP 000055f1aead90fc
...
etc.
I guess it might just be a bug in GraalVM (https://github.com/oracle/graal/issues/522), but it is also possible to "fix" it by using a custom BeanInfo
:
class SpringBootBeanInfo implements BeanInfo {
private final PropertyDescriptor[] propertyDescriptors;
public SpringBootBeanInfo(Class<?> beanClass) throws IntrospectionException {
this.propertyDescriptors = extractPropertyDescriptors(beanClass);
}
private PropertyDescriptor[] extractPropertyDescriptors(Class<?> beanClass)
throws IntrospectionException {
Map<String, Method> getters = new LinkedHashMap<>();
Map<String, Method> setters = new LinkedHashMap<>();
Method[] methods = ReflectionUtils.getAllDeclaredMethods(beanClass);
for (Method method : methods) {
collectGetterSetterMethod(method, getters, setters);
}
List<PropertyDescriptor> descriptors = new ArrayList<>(methods.length);
for (Map.Entry<String, Method> entry : getters.entrySet()) {
String name = entry.getKey();
Method getter = entry.getValue();
Method setter = setters.remove(name);
if (setter != null && !getter.getReturnType()
.isAssignableFrom(setter.getParameterTypes()[0])) {
setter = null;
}
descriptors.add(new SlimPropertyDescriptor(name, getter, setter));
}
for (Map.Entry<String, Method> entry : setters.entrySet()) {
Method setter = entry.getValue();
String name = entry.getKey();
// System.err.println("**************** " + setter);
descriptors.add(new SlimPropertyDescriptor(name, null, setter));
}
return descriptors.toArray(new SlimPropertyDescriptor[descriptors.size()]);
}
private void collectGetterSetterMethod(Method method, Map<String, Method> getters,
Map<String, Method> setters) {
int argSize = method.getParameterTypes().length;
if (!Modifier.isStatic(method.getModifiers())
&& Modifier.isPublic(method.getModifiers()) && argSize <= 1) {
String name = method.getName();
if (argSize == 0 && name.length() > 3 && name.startsWith("get")
&& !name.equals("getClass")) {
getters.putIfAbsent(name.substring(3), method);
}
else if (argSize == 0 && name.length() > 2 && name.startsWith("is")
&& method.getReturnType() == boolean.class) {
getters.putIfAbsent(name.substring(2), method);
}
else if (argSize == 1 && name.length() > 3 && name.startsWith("set")) {
setters.putIfAbsent(name.substring(3), method);
}
}
}
@Override
public BeanDescriptor getBeanDescriptor() {
throw new UnsupportedOperationException();
}
@Override
public EventSetDescriptor[] getEventSetDescriptors() {
throw new UnsupportedOperationException();
}
@Override
public int getDefaultEventIndex() {
throw new UnsupportedOperationException();
}
@Override
public int getDefaultPropertyIndex() {
throw new UnsupportedOperationException();
}
@Override
public MethodDescriptor[] getMethodDescriptors() {
throw new UnsupportedOperationException();
}
@Override
public BeanInfo[] getAdditionalBeanInfo() {
throw new UnsupportedOperationException();
}
@Override
public Image getIcon(int iconKind) {
throw new UnsupportedOperationException();
}
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors;
}
}
class SlimPropertyDescriptor extends PropertyDescriptor {
private Method readMethod;
private Method writeMethod;
public SlimPropertyDescriptor(String name, Method getter, Method setter)
throws IntrospectionException {
super(name, getter, setter);
}
@Override
public synchronized void setReadMethod(Method readMethod)
throws IntrospectionException {
this.readMethod = readMethod;
}
@Override
public synchronized void setWriteMethod(Method writeMethod)
throws IntrospectionException {
this.writeMethod = writeMethod;
}
@Override
public Method getReadMethod() {
return readMethod;
}
@Override
public Method getWriteMethod() {
return writeMethod;
}
}
The key is to override the getters and setters there in PropertyDescriptor
.
Affects: 5.0.8
Comment From: spring-projects-issues
Sébastien Deleuze commented
I think I would wait Graal issue to be closed before moving on that one.
Comment From: dsyer
The Graal issue is closed. Do we need to do something here or not (it might not hurt to be more defensive)?
Comment From: sdeleuze
I think we can close it, the fix on GraalVM side seems to be enough.