Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makes it possible to map DBUS properties directly to setter / getters #235

Merged
merged 8 commits into from
Oct 20, 2023
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package org.freedesktop.dbus;

import org.freedesktop.dbus.annotations.DBusBoundProperty;
import org.freedesktop.dbus.annotations.DBusProperty.Access;
import org.freedesktop.dbus.annotations.MethodNoReply;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.errors.NoReply;
import org.freedesktop.dbus.exceptions.*;
import org.freedesktop.dbus.interfaces.CallbackHandler;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.interfaces.Properties;
import org.freedesktop.dbus.messages.Error;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.utils.DBusNamingUtil;
import org.freedesktop.dbus.utils.LoggingHelper;
import org.freedesktop.dbus.utils.PropertyRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -78,6 +82,18 @@ public Object invoke(Object _proxy, Method _method, Object[] _args) throws Throw
}
} else if (_method.getName().equals("toString")) {
return remote.toString();
} else if (_method.getAnnotation(DBusBoundProperty.class) != null) {
String name = DBusNamingUtil.getPropertyName(_method);
Access access = PropertyRef.accessForMethod(_method);
if (access == Access.READ) {
Method propGetMethod = Properties.class.getMethod("Get", String.class, String.class);
return executeRemoteMethod(remote, propGetMethod,
new Type[] {_method.getGenericReturnType()}, conn, CALL_TYPE_SYNC, null, DBusNamingUtil.getInterfaceName(_method.getDeclaringClass()), name);
} else {
Method propSetMethod = Properties.class.getMethod("Set", String.class, String.class, Object.class);
return executeRemoteMethod(remote, propSetMethod,
new Type[] {_method.getGenericReturnType()}, conn, CALL_TYPE_SYNC, null, DBusNamingUtil.getInterfaceName(_method.getDeclaringClass()), name, _args[0]);
}
}

return executeRemoteMethod(remote, _method, conn, CALL_TYPE_SYNC, null, _args);
Expand All @@ -86,6 +102,10 @@ public Object invoke(Object _proxy, Method _method, Object[] _args) throws Throw
// CHECKSTYLE:ON

public static Object convertRV(String _sig, Object[] _rp, Method _m, AbstractConnection _conn) throws DBusException {
return convertRV(_sig, _rp, new Type[] {_m.getGenericReturnType()}, _m, _conn);
}

public static Object convertRV(String _sig, Object[] _rp, Type[] _types, Method _m, AbstractConnection _conn) throws DBusException {
Class<? extends Object> c = _m.getReturnType();
Object[] rp = _rp;
if (rp == null) {
Expand All @@ -99,12 +119,10 @@ public static Object convertRV(String _sig, Object[] _rp, Method _m, AbstractCon
LoggingHelper.logIf(LOGGER.isTraceEnabled(), () -> LOGGER.trace("Converting return parameters from {} to type {}",
Arrays.deepToString(_rp), _m.getGenericReturnType()));

rp = Marshalling.deSerializeParameters(rp, new Type[] {
_m.getGenericReturnType()
}, _conn);
rp = Marshalling.deSerializeParameters(rp, _types, _conn);
} catch (Exception _ex) {
LOGGER.debug("Wrong return type.", _ex);
throw new DBusException(String.format("Wrong return type (failed to de-serialize correct types: %s )", _ex.getMessage()));
throw new DBusException(String.format("Wrong return type (failed to de-serialize correct types: %s )", _ex.getMessage()), _ex);
}
}

Expand Down Expand Up @@ -134,7 +152,13 @@ public static Object convertRV(String _sig, Object[] _rp, Method _m, AbstractCon
}
}

public static Object executeRemoteMethod(RemoteObject _ro, Method _m, AbstractConnection _conn, int _syncmethod, CallbackHandler<?> _callback, Object... _args) throws DBusException {
public static Object executeRemoteMethod(final RemoteObject _ro, final Method _m,
final AbstractConnection _conn, final int _syncmethod, final CallbackHandler<?> _callback, Object... _args) throws DBusException {
return executeRemoteMethod(_ro, _m, new Type[] {_m.getGenericReturnType()}, _conn, _syncmethod, _callback, _args);
}

public static Object executeRemoteMethod(final RemoteObject _ro, final Method _m,
final Type[] _types, final AbstractConnection _conn, final int _syncmethod, final CallbackHandler<?> _callback, Object... _args) throws DBusException {
Type[] ts = _m.getGenericParameterTypes();
String sig = null;
Object[] args = _args;
Expand Down Expand Up @@ -201,10 +225,10 @@ public static Object executeRemoteMethod(RemoteObject _ro, Method _m, AbstractCo
}

try {
return convertRV(reply.getSig(), reply.getParameters(), _m, _conn);
return convertRV(reply.getSig(), reply.getParameters(), _types, _m, _conn);
} catch (DBusException _ex) {
LOGGER.debug("", _ex);
throw new DBusExecutionException(_ex.getMessage());
throw new DBusExecutionException(_ex.getMessage(), _ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.freedesktop.dbus.annotations;

import org.freedesktop.dbus.annotations.DBusProperty.Access;
import org.freedesktop.dbus.interfaces.Properties;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Binds a <strong>setter</strong> or <strong>getter</strong> method to a DBus property, in
* a similar manner to the familiar JavaBeans pattern.
* <p>
* Using this annotation means you do not need to implement the {@link Properties}
* interface and provide your own handling of {@link Properties#Get(String, String)},
* {@link Properties#GetAll(String)} and {@link Properties#Set(String, String, Object)}.
* <p>
* Each DBus property should map to either one or two methods. If it has
* {@link DBusBoundProperty#access()} of {@link Access#READ} then a single <strong>getter</strong>
* method should be created with no parameters. The type of property will be determined by
* the return type of the method, and the name of the property will be derived from the method name,
* with either the <code>get</code> or the <code>is</code> stripped off.
* <pre>
* {@literal @}DBusBoundProperty
* public String getMyStringProperty();
*
* {@literal @}DBusBoundProperty
* public boolean isMyBooleanProperty();
* </pre>
* If it has {@link DBusBoundProperty#access()} of {@link Access#WRITE} then a single <strong>setter</strong>
* method should be created with a single parameter and no return type. The type of the property
* will be determined by that parameter, and the name of the property will be derived from the
* method name, with either the <code>get</code> or the <code>is</code> stripped off.
* <pre>
* {@literal @}DBusBoundProperty
* public void setMyStringProperty(String _property);
* </pre>
* If it has {@link DBusBoundProperty#access()} of {@link Access#READ_WRITE}, the both of
* the above methods should be provided.
* <p>
* Any of the <code>name</code>, <code>type</code> and <code>access</code> attributes that would
* normally be automatically determined, may be overridden using the corresponding annotation attributes.
* <p>
* It is allowed if you wish to mix use of {@link DBusProperty} and {@link DBusBoundProperty} as
* long as individual properties are not repeated.
*
* @see org.freedesktop.dbus.interfaces.DBusInterface
* @see org.freedesktop.dbus.annotations.DBusProperty
* @see org.freedesktop.dbus.annotations.DBusProperties
* @see org.freedesktop.dbus.TypeRef
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DBusBoundProperty {

/**
* Property name. If not supplied, the property name will be inferred from the method. See
* class documentation for semantics.
*
* @return name
*/
String name() default "";

/**
* Type of the property, in case of complex types please create custom interface that extends {@link org.freedesktop.dbus.TypeRef}.
* If not supplied, then the type will be inferred from either the return value of a getter, or
* the first parameter of a setter.
*
* @return type
*/
Class<?> type() default Void.class;

/**
* Property access type. When {@link Access#READ_WRITE}, the access will be inferred from
* the method name, whether it is a setter or a getter.
*
* @return access
*/
Access access() default Access.READ_WRITE;

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
* }
* }
* </pre>
* <p>As an alternative to this annotation, you might consider using {@link DBusBoundProperty}. This
* allows you to achieve the same results with less code.</p>.
*
* @see org.freedesktop.dbus.interfaces.DBusInterface
* @see org.freedesktop.dbus.annotations.DBusBoundProperty
* @see org.freedesktop.dbus.TypeRef
*/
@Target(ElementType.TYPE)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DBusProperties.class)
public @interface DBusProperty {
Expand Down
Loading