GetAll is not callable when using @DBusBoundProperty on a List<DBusPath> property
Opened this issue · 1 comments
Given the following dbus interface and implementation:
@DBusInterfaceName("dev.tutetaki.FruitManager")
public interface FruitManager extends DBusInterface {
List<DBusPath> ListFruits();
@DBusBoundProperty(type = PropertyFruitType.class)
List<DBusPath> getFruits();
public static interface PropertyFruitType extends TypeRef<List<DBusPath>> {
}
}
public class FruitManagerImpl implements FruitManager {
@Override
public List<DBusPath> ListFruits() {
return List.of(
new DBusPath("/dev/tutetaki/FruitManager/1"),
new DBusPath("/dev/tutetaki/FruitManager/2"),
new DBusPath("/dev/tutetaki/FruitManager/3")
);
}
@Override
public List<DBusPath> getFruits() {
return ListFruits();
}
@Override
public String getObjectPath() {
return "/dev/tutetaki/FruitManager";
}
}
The property "Fruits" returns list of DBusPath correctly when requesting:
- the dbus method
ListFruits
via the java methodListFruits()
- the dbus property
Fruits
via the java methodgetFruits()
- the dbus property
Fruits
via the java methodGet()
But not when invoking GetAll
created by the library
public static void main(String[] args) throws DBusException {
BasicConfigurator.configure();
DBusConnection dbus = DBusConnectionBuilder.forSessionBus().build();
FruitManager fruitManager = new FruitManagerImpl();
dbus.exportObject(fruitManager.getObjectPath(), fruitManager);
dbus.requestBusName("dev.tutetaki.FruitManager");
Properties remotePropObj = dbus.getRemoteObject(
"dev.tutetaki.FruitManager",
"/dev/tutetaki/FruitManager",
Properties.class);
// outputs a result OK
// >>> [/dev/tutetaki/FruitManager/1, /dev/tutetaki/FruitManager/2, /dev/tutetaki/FruitManager/3]
Object getResult = remotePropObj.Get("dev.tutetaki.FruitManager", "Fruits");
System.out.println(getResult);
// Error
Object getAllResult = remotePropObj.GetAll("dev.tutetaki.FruitManager");
System.out.println(getAllResult);
}
> Task :MainFruit.main()
24/05/17 12:47:53 INFO transports.TransportBuilder: Using transport dbus-java-transport-native-unixsocket for address unix:path=/run/user/0/bus
0 [main] INFO org.freedesktop.dbus.connections.transports.TransportBuilder - Using transport dbus-java-transport-native-unixsocket for address unix:path=/run/user/0/bus
[/dev/tutetaki/FruitManager/1, /dev/tutetaki/FruitManager/2, /dev/tutetaki/FruitManager/3]
Exception in thread "main" org.freedesktop.dbus.exceptions.DBusExecutionException: Error Executing Method org.freedesktop.DBus.Properties.GetAll: Exporting non-exportable type: class java.util.ImmutableCollections$ListN
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.freedesktop.dbus.messages.Error.getException(Error.java:111)
at org.freedesktop.dbus.messages.Error.throwException(Error.java:138)
at org.freedesktop.dbus.RemoteInvocationHandler.executeRemoteMethod(RemoteInvocationHandler.java:237)
at org.freedesktop.dbus.RemoteInvocationHandler.executeRemoteMethod(RemoteInvocationHandler.java:250)
at org.freedesktop.dbus.RemoteInvocationHandler.executeRemoteMethod(RemoteInvocationHandler.java:153)
at org.freedesktop.dbus.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:97)
at jdk.proxy2/jdk.proxy2.$Proxy8.GetAll(Unknown Source)
at dev.tutetaki.MainFruit.main(MainFruit.java:54)
> Task :MainFruit.main() FAILED
The issue appears to be around DBusBoundPropertyHandler.java#L125. The type of Object val
is java.util.ImmutableCollections$ListN
rather than the TypeRef
PropertyFruitType
?
I have pushed my sample here https://github.com/tutetaki/sample-dbus-object/blob/master/src/main/java/dev/tutetaki/MainFruit.java
This was a little bigger problem than previously assumed and should be fixed now.
The "GetAll" method did fail when the annotation were used. But it was also failing without annotations when using types which are also available as primitives (e.g. Integer/int).
In that case some optimization transformed the de-serialized list of Integer
to an int array.
This always happened when a collection or map was used in a Variant<?>
, therefore this was an really big issue.
The problem is, that DBus itself does not know Collections of any sort, it only support arrays. So it is impossible to know if a Variant should be containing a Collection or an array when de-serializing it from the bus.
After some brainstorming I decided to change the behavior of Variant
to always use a List
instead of an array.
First argument to do so is to get the GetAll
call working correctly.
Second argument is that Collections are more commonly used in Java than arrays.
I assume that Variant
will be used as Variant<List<X>>
more often than Variant<X[]>
.
I am aware that this change may break code which expects to receive an array but I think the behavior of Variant is now more consistent.
I also decided to bump the version to 5.1.0 to show that this version will contain more than just some bugfixes.