Closed brett-smith closed 10 months ago
As always thanks for your PR.
I like the idea of making the usage of properties/getters/setters more straight forward.
The reason why the Properties
interface uses Get
and Set
with upper case is because DBus specification defines it that way.
I'm thinking of adding get
and set
with lowercase as default implementation in the interface to be more Java like. But I have to think a bit about that (does this cause side effects? Break existing code?).
Anyway, there are some points I would prefer to change. First: It would be great to have this patch for 5x-develop branch instead of master. As Java 21 was just released, I'm planing to release 4.3.1 soon and switch development to 5.x (requiring Java 17 as minimum version). Therefore I want to avoid bigger changes for 4.x and espacially for 4.3.1 right now.
Second: It does not feel so good to use DBusProperty
annotation for both, some sort of method-magic and for properties.
I would prefer to use a extra annotation like DBusPropertyMethod
or similar. Then we don't need to support empty name parameter or other ugly workarounds and can completely focus on the options we need for this method magic.
Third: I'm not a fan of the var
type. Please use proper variable type when ever possible.
Fourth: As you said: Test cases.
And last but not least: Additional documentation in maven site. This feature feels valuable and so we should add a page to maven site for this. The description in this PR might be a good starting point.
record
could be used. Records are immutable (sorta), so you would need to use some kind of wrapper for properties that can be written to. E.g.public interface MyInterface extends DBusInterface {
record Props(String prop1, int prop2, boolean prop3, Mutable<String> propWithGetSet);
void myMethod();
@DBusPropertyRecordOrSomething
Props props();
}
This would eliminate yet more code, both in the interface and particularly the implementation. Exactly how this might look would depend on if it's possible to create Proxy record
s. I'll do a bit of research.
var
keyword. Heh yeh. I personally love it, it saves so much typing and I personally think it usually makes more readable code if used consistently. But I understand it's not universally loved, so will of course bow to your wishes. I released 4.3.1 and updated master to 5x-develop branch. Would you please resolve the conflicts introduced by the 5x merge so that I can merge this PR?
That's great (been waiting for 4.3.1 for other reasons too!).
Re this PR, I've got the Maven site documentation written, just got the tests to do now. I should be able to get some time on this later today.
Nearly there. To recap what is done so far,
DBusBoundProperty
ExportObjectWithProperties
properties.md
)BoundPropertiesTest
)What remains is what the tests highlighted, a problem with List<SomeType>
and Map<SomeKey, SomeVal>
. This is certainly something to do with type erasure, but I am not yet sure exactly what is going on or how to correct it.
By using TypeRef
, I can see that the types have correct introspection data (looking in d-feet), but attempt to set the value fails in RemoteInvocationHandler
.
java.lang.IllegalArgumentException: Can't wrap class java.util.Arrays$ArrayList in an unqualified Variant (Exporting non-exportable type: class java.util.Arrays$ArrayList).
at org.freedesktop.dbus.types.Variant.<init>(Variant.java:41)
at org.freedesktop.dbus.Marshalling.convertParameters(Marshalling.java:461)
at org.freedesktop.dbus.RemoteInvocationHandler.executeRemoteMethod(RemoteInvocationHandler.java:168)
at org.freedesktop.dbus.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:94)
at jdk.proxy2/jdk.proxy2.$Proxy33.setAList(Unknown Source)
at org.freedesktop.dbus.test.BoundPropertiesTest.testProperties(BoundPropertiesTest.java:57)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
I'll keep at it, but any pointers you might have would be useful. I think https://github.com/hypfvieh/dbus-java/issues/74 seems sort of related, so I wonder if there are some extra hoops you have to jump through with these types on properties using the existing method.
This problem look familiar... But I don't have any idea on how to fix that.
That's what I've try to explain at different occasions. It's the same issue that libraries like Jackson have. Without setting up something like TypeRef
(Jackson has the same thing called TypeReference
) you don't get any hint on which type was used in a container parametrized by generics.
I guess that this will be a limitation one cannot fix, because it is the way Java deals with generics (= type erasure)
Yeh, this is proving quite hard. If you are happy for this PR to go in as it is, I'm fine with that. If this collections problem gets too annoying I'll take another shot at it.
I'm sure it is possible, as I said the introspect data seems correct when you do use TypeRef
, so something has got the type right! The problem appears to be in convertParameters()
. I think this needs some special handling for collections at the very least. I did try to convert List
to an array there, but then got stuck with some error about the signature when actually writing the message.
The issue is the use of Variant<?>
.
I used your unit test enabled the setAList()>
method (disabled everything unrelated for easier debugging) and then started debugging the method call.
First of all, the Introspection says the expected format for the method is "as" (Array of String). This is correct.
In the background DBus Properties interface is used so the call to setAList()
will be mapped to Properties.Set(String, String, Object)
.
At that point, the passed in List of setAList()
must be wrapped to a Variant
because the DBus Properties interface is defined that Set or Get will take/return a Variant.
Wrapping a Map/Collection will then fail because it is not possible to determine the correct type.
Variant
constructor is called with the value which should be wrapped. Inside the constructor Marshalling.getDBusType()
is used and only the class of the given value is provided.
Calling getClass()
on the provided list will return e.g. ArrayList
class without any information about the containing type.
I tried to get around this by determining the generic type using the given value object instead of just asking for the class. But I did not find any solution getting the actually used wrapped type.
There is another constructor for Variant
allowing to provide the DBus signature to use for the wrapping type. The information is present in RemoteInvokationHandler.invoke
by using the type()
information of the annotation. The problem is, there is no way to pass this information to the point the Variant is created ... -.-
[Edit] There is also a bug in the unit test. The annotation for 'getAList' and 'setAList' are defined as 'StringList.class', but the lists used are containing integers. This will probably fail when we find a way to provide annotation information to Variant constructor.
I think I got it working. I will cleanup my mess and will merge the results upstream
Wow! Good job. I'll have to review your patch see exactly what was done :)
I've already ported several project to use the new annotation, and they've all gone really well so far. None have required using this list / map pattern yet, but I can think of one that is yet to be done that does.
I just noticed I made a stupid typo in an exception message in PropertyRef.checkMethod()
.
throw new IllegalArgumentException("WRITE properties must have exaclty 1 parameter, and return void.");
should read ..
throw new IllegalArgumentException("WRITE properties must have exactly 1 parameter, and return void.");
I just noticed I made a stupid typo in an exception message in
PropertyRef.checkMethod()
.throw new IllegalArgumentException("WRITE properties must have exaclty 1 parameter, and return void.");
should read ..
throw new IllegalArgumentException("WRITE properties must have exactly 1 parameter, and return void.");
fixed
This patch makes it possible to map DBUS properties directly to setter / getter methods in any
DBusInterface
. This has a number of advantages.Get()
,Set()
andGetAll()
fromorg.freedesktop.DBus.Properties
. In fact, you do not need to implement this interface at all.Variant
on one side, but not the other.If there is a disadvantage, it's that there is no equivalant of
GetAll()
. However, there is nothing stopping you mixing this new API with the old. So just implementGetAll()
and stub outGet()
andSet()
.This is what an interface with properties would now look like.
And the implementation ..
Some points to note about the above.
@DBusProperty
now may be attached to methods.@DBusProperty
will not be exported as a method, instead it and it's related setter or getter will be presented as a property.Other changes made.
--propertyMethods
that will generate codes using this method instead of class annotations.name
attribute of@DBusProperty
was given a default value of""
. Runtime checks are now used instead. This may be slightly controversial, but the alternative was using a totally separate annotation.--package
option to generate in a different namespace (and inject@DBusInterfaceName
).AbstractConnection
. This is where the most complex part of this patch was concentrated. Im 99% certain its functionally identical (apart from the new property features), but any review should pay particular attention here.A new example has been added, but not yet tests. If you are happy with how this looks, I'll finish up with adding appropraite unit tests.