dbus-java
dbus-java copied to clipboard
DisplayConfig Imperfect class generation
I have used InterfaceCodeGenerator to generate classes for org.gnome.Mutter.DisplayConfig. The resulting classes are helpful but imperfect. For example, this crashes with a ClassCastException.
DisplayConfig displayConfig = connection.getRemoteObject("org.gnome.Mutter.DisplayConfig",
"/org/gnome/Mutter/DisplayConfig", DisplayConfig.class);
GetCurrentStateTuple state = displayConfig.GetCurrentState();
List<GetCurrentStateMonitorsStruct> monitors = state.getMonitors();
GetCurrentStateMonitorsStruct m0 = monitors.get(0);
I read that “the InterfaceCodeGenerator is far from being perfect. It's just a helper to get started somehow”, but I thought I’d let you know in case you want to investigate. (I can share an example project if this would be helpful.)
Thanks for this project.
Thanks for the report. Sample Source is always welcome. In this case the Stacktrace of the exception would be helpful.
I don't use Gnome, so I do not have a Mutter instance to query. I got the XML file defining DisplayConfig, but to understand what the generator creates and what is actually expected, I need the stack trace and complete error message.
Thank you for this fast reaction. Here is an example project. Running DBusAttempt yields:
16:27:32.888 [main] INFO o.f.d.c.transports.TransportBuilder - Using transport dbus-java-transport-junixsocket for address unix:path=/run/user/1000/bus,guid=e6517c6ad48b6ed234d07a096856681a
16:27:32.946 [main] INFO i.g.o.jdisplayconfig.DBusAttempt - ApplyMonitorsConfigAllowed: true
16:27:32.957 [main] INFO i.g.o.jdisplayconfig.DBusAttempt - Monitors: 2
Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class org.freedesktop.dbus.Struct ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; org.freedesktop.dbus.Struct is in unnamed module of loader 'app')
at io.github.oliviercailloux.jdisplayconfig.DBusAttempt.main(DBusAttempt.java:29)
I updated the InterfaceCodeGenerator it should now be capable of creating proper Tuple-extending classes and use them in interfaces.
I also updated the Signal-class generating code to allow creating empty signal classes (like the MonitorsChanged signal used in DisplayConfig).
Maybe you can take a look if it is now usable for you. The changes can be tested using the current master-branch or using the most recent snapshot found in maven central snapshot repository (5.2.0-SNAPSHOT)
Great! It works.
Hoping that I am not abusing, can you by chance make the types non generic again, as they used to be, unless necessary? Having to deal, for example, with a type GetCurrentStateTuple<UInt32, List<GetCurrentStateMonitorsStruct>, List<GetCurrentStateLogicalMonitorsStruct>, Map<String, Variant<?>>> instead of the much shorter GetCurrentStateTuple makes the code more difficult to read. The generics looks superfluous here, as GetCurrentStateTuple is always used with these specific types as type variables. (But it’s quite possible that I am missing some subtelties.)
Sorry that is currently not supported.
Tuples are used when a method needs to return multiple values. Due to the nature of generics in Java, generic-type parameters used on class level are removed during compilation (type erasure). Therefore it is impossible to know the proper type during runtime when extending a generic-typed class.
Example: class SpecialMap extends HashMap<String, Integer>
During runtime, you will see SpecialMap and you can get the super class HashMap but when you ask for the generic types, you will get K, V - you cannot determine that K is String and V is Integer.
When a method is defined like this:
MyTuple<String, Integer, List<Long>> myMethod()
The types are not erased because the method return value is "fixed" and will not change during runtime (not like classes, where it is theoretically possible to contain any type when creating an arbitrary instance of that class).
So the only way for dbus-java to handle this is to use a Tuple subclass with proper generics and define the method which returns an instance of that Tuple with concrete types.
I may add support to use Struct as return value instead of Tuple which would deprecate Tuple class in some future release. This would require some modification in marshalling code but should be possible.
Anyway changing the code generator to create Struct instead of Tuple would break generated code when used with older dbus-java versions (maybe I add a feature flag for that).
Just to make sure we are on the same page, here is the change that I had in mind.
Yes I understood that. The generic types are required to deserialize a Tuple properly especially when the Tuple contains Structs as well. Otherwise you will always end up in an deserialization error.
You may expiriment with that on your own, but as far as I know the current code cannot handle Tuples like the ones used in DisplayConfig without the generics.
Indeed, the code as shown does not work.
Thanks for the quick patch anyway!