hypfvieh/dbus-java

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 method ListFruits()
  • the dbus property Fruits via the java method getFruits()
  • the dbus property Fruits via the java method Get()

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.