I have a situation where I am sharing some code across a few modules, and my DBusInterface interface and implementation extend a generic interface that is used outside of the dbus components.
Something like this ..
public interface RootItem {
}
public interface Root<ITEM_TYPE extends RootItem> {
public ITEM_TYPE getItem(long itemId);
}
public interface DBusItem extends RootItem {
}
public interface DBusAPI extends Root<DBusItem>, DBusInterface {
@Override
public DBusItem getItem(long itemId);
}
public class DBusAPIImpl implements DBusAPI {
@Override
public DBusItem getItem(long itemId) {
/// do stuff to get item
return theItem;
}
}
This will fail to export the interface.
org.freedesktop.dbus.exceptions.DBusException: Exporting non-exportable type: interface com.logonbox.vpn.client.common.api.IVPNConnection
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.Marshalling.recursiveGetDBusType(Marshalling.java:281)
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.Marshalling.getDBusType(Marshalling.java:118)
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.Marshalling.getDBusType(Marshalling.java:103)
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.messages.ExportedObject.generateMethodsXml(ExportedObject.java:227)
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.messages.ExportedObject.generateIntrospectionXml(ExportedObject.java:341)
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.messages.ExportedObject.<init>(ExportedObject.java:37)
at org.freedesktop.dbus@5.0.0-SNAPSHOT/org.freedesktop.dbus.connections.AbstractConnection.exportObject(AbstractConnection.java:329)
What appears to be happening, is that the getItem() method is being found twice. What is is interesting, is that one occurrence is being identified as a default method even though it's not, while the other is being identified as abstract (like all the other methods in the interface that do not have this generic type).
Doing a System.out of the two methods that are found results in ...
public default com.acme.root.RootItem com.acme.impl.DBusAPI.getItemlong)
public abstract com.acme.impl.DBusItem com.acme.impl.DBusAPI.getItem(long)
I could fix this by adding a @DBusIgnore to Root, but Root is in a module that does not have dbus-java on the CLASSPATH.
So instead, I changed dbus-javas ExportedObject.isExcluded() method.
public static boolean isExcluded(Method _meth) {
return !Modifier.isPublic(_meth.getModifiers())
|| _meth.isDefault() /* <--- Added this */
|| _meth.getAnnotation(DBusIgnore.class) != null
|| _meth.getAnnotation(DBusBoundProperty.class) != null
|| _meth.getName().equals("getObjectPath") && _meth.getReturnType().equals(String.class)
&& _meth.getParameterCount() == 0;
}
And now it all works again.
This doesn't seem to have any other ill effects to the DBus API. Everything else is still getting exported correctly. It seems that it is only identified as default with this particular arrangement of interfaces, OR if you do actually use a default method in the DBusInterface.
I think this may be a valid fix, because using a default method doesn't make sense anyway. If you are acting as a client, accessing an external DBus service from Java, then default methods don't work anyway (RemoteInvocationHandler tries to run it as if it were a remote method). It might be valid if you are exporting an API from Java, but then you cannot share those interface definitions with client code.
TLDR; I think default methods in DBus interfaces should be excluded by default
I have a situation where I am sharing some code across a few modules, and my
DBusInterfaceinterface and implementation extend a generic interface that is used outside of the dbus components.Something like this ..
This will fail to export the interface.
What appears to be happening, is that the
getItem()method is being found twice. What is is interesting, is that one occurrence is being identified as adefaultmethod even though it's not, while the other is being identified asabstract(like all the other methods in the interface that do not have this generic type).Doing a
System.outof the two methods that are found results in ...I could fix this by adding a
@DBusIgnoretoRoot, butRootis in a module that does not havedbus-javaon the CLASSPATH.So instead, I changed
dbus-javasExportedObject.isExcluded()method.And now it all works again.
This doesn't seem to have any other ill effects to the DBus API. Everything else is still getting exported correctly. It seems that it is only identified as
defaultwith this particular arrangement of interfaces, OR if you do actually use adefaultmethod in theDBusInterface.I think this may be a valid fix, because using a
defaultmethod doesn't make sense anyway. If you are acting as a client, accessing an external DBus service from Java, then default methods don't work anyway (RemoteInvocationHandlertries to run it as if it were a remote method). It might be valid if you are exporting an API from Java, but then you cannot share those interface definitions with client code.TLDR; I think
defaultmethods in DBus interfaces should be excluded by default