From 39a57f31fd498794c419a20ce648cb9d23017ddf Mon Sep 17 00:00:00 2001 From: David M Date: Sat, 16 Nov 2024 16:06:26 +0100 Subject: [PATCH] Refactorings, Reduced duplicate code/documentation --- README.md | 4 + .../java/org/freedesktop/dbus/DBusPath.java | 46 +++- .../org/freedesktop/dbus/Marshalling.java | 9 + .../java/org/freedesktop/dbus/ObjectPath.java | 5 + .../dbus/connections/impl/DBusConnection.java | 175 +------------ .../connections/impl/DirectConnection.java | 26 +- .../connections/impl/IRemoteObjectGetter.java | 229 ++++++++++++++++++ .../ClassOutsideOfPackageException.java | 18 ++ ...ssingInterfaceImplementationException.java | 20 ++ .../freedesktop/dbus/messages/DBusSignal.java | 11 +- .../freedesktop/dbus/messages/Message.java | 2 +- .../freedesktop/dbus/utils/DBusObjects.java | 116 ++++++++- .../dbus/test/MarshallingTest.java | 7 +- 13 files changed, 464 insertions(+), 204 deletions(-) create mode 100644 dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/IRemoteObjectGetter.java create mode 100644 dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/ClassOutsideOfPackageException.java create mode 100644 dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/MissingInterfaceImplementationException.java diff --git a/README.md b/README.md index 82186be7..0649d62a 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,10 @@ The library will remain open source and MIT licensed and can still be used, fork - Fixed issue with arrays, primitive arrays and `Collection` when used in signal constructors ([#268](https://github.com/hypfvieh/dbus-java/issues/268)) - Improvements when using library in Kotlin projects ([PR#270](https://github.com/hypfvieh/dbus-java/issues/270)), thanks to [vicr123](https://github.com/vicr123) - Fixed exporting of methods which used a `Tuple` return type caused `ClassCastException` ([#271](https://github.com/hypfvieh/dbus-java/issues/271)) + - Deprecated `ObjectPath`, use `DBusPath` instead + - Added `of(String...)` factory method to `DBusPath` + - Smaller refactorings to reduce duplicated code + - Added additional `getRemoteObject` methods which uses `DBusPath` as argument ##### Changes in 5.1.0 (2024-08-01): - Use Junit BOM thanks to [spannm](https://github.com/spannm) ([PR#248](https://github.com/hypfvieh/dbus-java/issues/248)) diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/DBusPath.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/DBusPath.java index 4e3dd9b9..7d3d278e 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/DBusPath.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/DBusPath.java @@ -3,16 +3,34 @@ import java.util.Objects; public class DBusPath implements Comparable { + private final String source; private String path; + public DBusPath(String _source, String _path) { + source = _source; + path = _path; + } + public DBusPath(String _path) { - setPath(_path); + this(null, _path); } public String getPath() { return path; } + /** + * @deprecated path will be final in future, please do no longer use the setter. + */ + @Deprecated(forRemoval = true, since = "5.1.1 - 2024-11-16") + public void setPath(String _path) { + path = _path; + } + + public String getSource() { + return source; + } + @Override public String toString() { return getPath(); @@ -20,7 +38,7 @@ public String toString() { @Override public int hashCode() { - return Objects.hash(path); + return Objects.hash(source, path); } @Override @@ -30,7 +48,11 @@ public boolean equals(Object _obj) { } else if (_obj == null || getClass() != _obj.getClass()) { return false; } - return Objects.equals(path, ((DBusPath) _obj).path); + + DBusPath dbusPath = (DBusPath) _obj; + + return Objects.equals(path, dbusPath.path) + && Objects.equals(source, dbusPath.source); } @Override @@ -41,7 +63,21 @@ public int compareTo(DBusPath _that) { return getPath().compareTo(_that.getPath()); } - public void setPath(String _path) { - path = _path; + /** + * Create a DBusPath object using one or multiple string parts. + * Leading slash will automatically appended if missing. + * + * @param _parts parts to build DBusPath + * @return DBusPath + * @throws IllegalArgumentException when no parts are given + */ + public static DBusPath of(String... _parts) { + if (_parts == null || _parts.length == 0) { + throw new IllegalArgumentException("No Strings given to build DBusPath"); + } + + String pathStr = _parts[0].indexOf('/') == 0 ? "" : "/" + String.join("/", _parts); + + return new DBusPath(pathStr); } } diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/Marshalling.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/Marshalling.java index a70c3323..b0d47923 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/Marshalling.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/Marshalling.java @@ -590,6 +590,15 @@ static Object deSerializeParameter(Object _parameter, Type _type, AbstractConnec } } + if (parameter instanceof DBusPath op) { + LOGGER.trace("Parameter is DBusPath"); + if (_type instanceof Class && DBusInterface.class.isAssignableFrom((Class) _type)) { + parameter = _conn.getExportedObject(op.getSource(), op.getPath(), (Class) _type); + } else { + parameter = new DBusPath(op.getPath()); + } + } + // its an enum, parse either as the string name or the ordinal if (parameter instanceof String str && _type instanceof Class && Enum.class.isAssignableFrom((Class) _type)) { LOGGER.trace("Type seems to be an enum"); diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/ObjectPath.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/ObjectPath.java index d46656ac..88efc1a0 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/ObjectPath.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/ObjectPath.java @@ -2,6 +2,10 @@ import java.util.Objects; +/** + * @deprecated please use {@link DBusPath} instead + */ +@Deprecated(forRemoval = true, since = "5.1.1 - 2024-11-16") public class ObjectPath extends DBusPath { private String source; @@ -10,6 +14,7 @@ public ObjectPath(String _source, String _path) { this.source = _source; } + @Override public String getSource() { return source; } diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java index ca042fdf..789e17d8 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnection.java @@ -43,7 +43,7 @@ * issues. *

*/ -public final class DBusConnection extends AbstractConnection { +public final class DBusConnection extends AbstractConnection implements IRemoteObjectGetter { static final ConcurrentMap CONNECTIONS = new ConcurrentHashMap<>(); @@ -317,70 +317,18 @@ public String[] getNames() { }).toArray(String[]::new); } - public I getPeerRemoteObject(String _busname, String _objectpath, Class _type) - throws DBusException { - return getPeerRemoteObject(_busname, _objectpath, _type, true); + @Override + public String getDBusOwnerName(String _busName) { + return dbus == null ? null : dbus.GetNameOwner(_busName); } - /** - * Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus - * name when you call it. This means that if a well known name is released by one process and acquired by another - * calls to objects gained from this method will continue to operate on the original process. - * - * This method will use bus introspection to determine the interfaces on a remote object and so may block and - * may fail. The resulting proxy object will, however, be castable to any interface it implements. It will - * also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct - * method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd - * happens, try specifying the interface explicitly. - * - * @param _busname - * The bus name to connect to. Usually a well known bus name in dot-notation (such as - * "org.freedesktop.local") or may be a DBus address such as ":1-16". - * @param _objectpath - * The path on which the process is exporting the object.$ - * @return A reference to a remote object. - * @throws ClassCastException - * If type is not a sub-type of DBusInterface - * @throws InvalidBusNameException - * If busname or objectpath are incorrectly formatted. - * @throws DBusException - * If retrieving remote object fails - */ + @Override public DBusInterface getPeerRemoteObject(String _busname, String _objectpath) throws InvalidBusNameException, DBusException { - DBusObjects.requireBusNameOrConnectionId(_busname); - String unique = dbus.GetNameOwner(_busname); - - return dynamicProxy(unique, _objectpath, null); + return dynamicProxy(getDBusOwnerName(_busname), _objectpath, null); } - /** - * Return a reference to a remote object. This method will always refer to the well known name (if given) rather - * than resolving it to a unique bus name. In particular this means that if a process providing the well known name - * disappears and is taken over by another process proxy objects gained by this method will make calls on the new - * proccess. - * - * This method will use bus introspection to determine the interfaces on a remote object and so may block and - * may fail. The resulting proxy object will, however, be castable to any interface it implements. It will - * also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct - * method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd - * happens, try specifying the interface explicitly. - * - * @param _busname - * The bus name to connect to. Usually a well known bus name name in dot-notation (such as - * "org.freedesktop.local") or may be a DBus address such as ":1-16". - * @param _objectpath - * The path on which the process is exporting the object. - * @return A reference to a remote object. - * @throws ClassCastException - * If type is not a sub-type of DBusInterface - * @throws DBusException - * If remote object cannot be retrieved - * @throws InvalidBusNameException - * If busname is incorrectly formatted - * @throws InvalidObjectPathException - * If objectpath is incorrectly formatted - */ + @Override public DBusInterface getRemoteObject(String _busname, String _objectpath) throws DBusException, InvalidBusNameException, InvalidObjectPathException { DBusObjects.requireBusNameOrConnectionId(_busname); DBusObjects.requireObjectPath(_objectpath); @@ -388,118 +336,15 @@ public DBusInterface getRemoteObject(String _busname, String _objectpath) throws return dynamicProxy(_busname, _objectpath, null); } - /** - * Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus - * name when you call it. This means that if a well known name is released by one process and acquired by another - * calls to objects gained from this method will continue to operate on the original process. - * - * @param - * class extending {@link DBusInterface} - * @param _busname - * The bus name to connect to. Usually a well known bus name in dot-notation (such as - * "org.freedesktop.local") or may be a DBus address such as ":1-16". - * @param _objectpath - * The path on which the process is exporting the object.$ - * @param _type - * The interface they are exporting it on. This type must have the same full class name and exposed - * method signatures as the interface the remote object is exporting. - * @param _autostart - * Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when - * calling a method with auto-start enabled, if the destination is a well-known name and is not owned the - * bus will attempt to start a process to take the name. When disabled an error is returned immediately. - * @return A reference to a remote object. - * @throws ClassCastException - * If type is not a sub-type of DBusInterface - * @throws DBusException - * If busname or objectpath are incorrectly formatted or type is not in a package. - * @throws InvalidBusNameException - * If busname is incorrectly formatted - */ - public I getPeerRemoteObject(String _busname, String _objectpath, Class _type, - boolean _autostart) throws DBusException, InvalidBusNameException { - if (null == _busname) { - throw new InvalidBusNameException(); - } - - DBusObjects.requireBusNameOrConnectionId(_busname); - - String unique = dbus.GetNameOwner(_busname); - - return getRemoteObject(unique, _objectpath, _type, _autostart); - } - - /** - * Return a reference to a remote object. This method will always refer to the well known name (if given) rather - * than resolving it to a unique bus name. In particular this means that if a process providing the well known name - * disappears and is taken over by another process proxy objects gained by this method will make calls on the new - * proccess. - * - * @param - * class extending {@link DBusInterface} - * @param _busname - * The bus name to connect to. Usually a well known bus name name in dot-notation (such as - * "org.freedesktop.local") or may be a DBus address such as ":1-16". - * @param _objectpath - * The path on which the process is exporting the object. - * @param _type - * The interface they are exporting it on. This type must have the same full class name and exposed - * method signatures as the interface the remote object is exporting. - * @return A reference to a remote object. - * @throws ClassCastException - * If type is not a sub-type of DBusInterface - * @throws DBusException - * If busname or objectpath are incorrectly formatted or type is not in a package. - */ - public I getRemoteObject(String _busname, String _objectpath, Class _type) - throws DBusException { - return getRemoteObject(_busname, _objectpath, _type, true); - } - - /** - * Return a reference to a remote object. This method will always refer to the well known name (if given) rather - * than resolving it to a unique bus name. In particular this means that if a process providing the well known name - * disappears and is taken over by another process proxy objects gained by this method will make calls on the new - * proccess. - * - * @param - * class extending {@link DBusInterface} - * @param _busname - * The bus name to connect to. Usually a well known bus name name in dot-notation (such as - * "org.freedesktop.local") or may be a DBus address such as ":1-16". - * @param _objectpath - * The path on which the process is exporting the object. - * @param _type - * The interface they are exporting it on. This type must have the same full class name and exposed - * method signatures as the interface the remote object is exporting. - * @param _autostart - * Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when - * calling a method with auto-start enabled, if the destination is a well-known name and is not owned the - * bus will attempt to start a process to take the name. When disabled an error is returned immediately. - * @return A reference to a remote object. - * @throws ClassCastException - * If type is not a sub-type of DBusInterface - * @throws DBusException - * If busname or objectpath are incorrectly formatted or type is not in a package. - */ + @Override @SuppressWarnings("unchecked") public I getRemoteObject(String _busname, String _objectpath, Class _type, boolean _autostart) throws DBusException { - if (_type == null) { - throw new ClassCastException("Not A DBus Interface"); - } DBusObjects.requireBusNameOrConnectionId(_busname); DBusObjects.requireObjectPath(_objectpath); - - if (!DBusInterface.class.isAssignableFrom(_type)) { - throw new ClassCastException("Not A DBus Interface"); - } - - // don't let people import things which don't have a - // valid D-Bus interface name - if (_type.getName().equals(_type.getSimpleName())) { - throw new DBusException("DBusInterfaces cannot be declared outside a package"); - } + DBusObjects.requireDBusInterface(_type); + DBusObjects.requirePackage(_type); RemoteObject ro = new RemoteObject(_busname, _objectpath, _type, _autostart); I i = (I) Proxy.newProxyInstance(_type.getClassLoader(), new Class[] { diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java index 13fda124..c39a1689 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DirectConnection.java @@ -10,6 +10,8 @@ import org.freedesktop.dbus.connections.config.ReceivingServiceConfig; import org.freedesktop.dbus.connections.config.TransportConfig; import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.InvalidObjectPathException; +import org.freedesktop.dbus.exceptions.MissingInterfaceImplementationException; import org.freedesktop.dbus.interfaces.DBusInterface; import org.freedesktop.dbus.interfaces.DBusSigHandler; import org.freedesktop.dbus.interfaces.Introspectable; @@ -145,28 +147,14 @@ public DBusInterface getRemoteObject(String _objectPath) throws DBusException { * as the interface the remote object is exporting. * @param class which extends DBusInterface * @return A reference to a remote object. - * @throws ClassCastException If type is not a sub-type of DBusInterface - * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. + * @throws MissingInterfaceImplementationException If type is not a sub-type of DBusInterface + * @throws InvalidObjectPathException If busname or objectpath are invalid + * */ public T getRemoteObject(String _objectPath, Class _type) throws DBusException { - if (null == _objectPath) { - throw new DBusException("Invalid object path: null"); - } - if (null == _type) { - throw new ClassCastException("Not A DBus Interface"); - } - DBusObjects.requireObjectPath(_objectPath); - - if (!DBusInterface.class.isAssignableFrom(_type)) { - throw new ClassCastException("Not A DBus Interface"); - } - - // don't let people import things which don't have a - // valid D-Bus interface name - if (_type.getName().equals(_type.getSimpleName())) { - throw new DBusException("DBusInterfaces cannot be declared outside a package"); - } + DBusObjects.requireDBusInterface(_type); + DBusObjects.requirePackage(_type); RemoteObject ro = new RemoteObject(null, _objectPath, _type, false); diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/IRemoteObjectGetter.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/IRemoteObjectGetter.java new file mode 100644 index 00000000..3417a4f4 --- /dev/null +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/IRemoteObjectGetter.java @@ -0,0 +1,229 @@ +package org.freedesktop.dbus.connections.impl; + +import org.freedesktop.dbus.DBusPath; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.InvalidBusNameException; +import org.freedesktop.dbus.exceptions.InvalidObjectPathException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.utils.DBusObjects; + +/** + * Interface which contains all methods to retrieve any object from DBus. + * @since 5.1.1 - 2024-11-16 + */ +public sealed interface IRemoteObjectGetter permits DBusConnection { + + default I getPeerRemoteObject(String _busname, String _objectpath, Class _type) + throws DBusException { + + return getPeerRemoteObject(_busname, _objectpath, _type, true); + } + + default I getPeerRemoteObject(String _busname, DBusPath _objectpath, Class _type) + throws DBusException { + DBusObjects.requireObjectPath(_objectpath); + return getPeerRemoteObject(_busname, _objectpath.getPath(), _type); + } + + /** + * Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus + * name when you call it. This means that if a well known name is released by one process and acquired by another + * calls to objects gained from this method will continue to operate on the original process. + * + * This method will use bus introspection to determine the interfaces on a remote object and so may block and + * may fail. The resulting proxy object will, however, be castable to any interface it implements. It will + * also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct + * method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd + * happens, try specifying the interface explicitly. + * + * @param _busname + * The bus name to connect to. Usually a well known bus name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object.$ + * @return A reference to a remote object. + * @throws MissingInterfaceImplementationException + * If type is not a sub-type of DBusInterface + * @throws InvalidObjectPathException + * When object path is invalid + * @throws ClassOutsideOfPackageException + * When given type class has not package + * @throws DBusException + * on any other errors + */ + DBusInterface getPeerRemoteObject(String _busname, String _objectpath) throws InvalidBusNameException, DBusException; + + /** + * Return a reference to a remote object. This method will always refer to the well known name (if given) rather + * than resolving it to a unique bus name. In particular this means that if a process providing the well known name + * disappears and is taken over by another process proxy objects gained by this method will make calls on the new + * proccess. + * + * This method will use bus introspection to determine the interfaces on a remote object and so may block and + * may fail. The resulting proxy object will, however, be castable to any interface it implements. It will + * also autostart the process if applicable. Also note that the resulting proxy may fail to execute the correct + * method with overloaded methods and that complex types may fail in interesting ways. Basically, if something odd + * happens, try specifying the interface explicitly. + * + * @param _busname + * The bus name to connect to. Usually a well known bus name name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object. + * @return A reference to a remote object. + * @throws MissingInterfaceImplementationException + * If type is not a sub-type of DBusInterface + * @throws DBusException + * If remote object cannot be retrieved + * @throws InvalidBusNameException + * If busname is incorrectly formatted + * @throws InvalidObjectPathException + * If objectpath is incorrectly formatted + */ + DBusInterface getRemoteObject(String _busname, String _objectpath) throws DBusException, InvalidBusNameException, InvalidObjectPathException; + + /** + * @see #getRemoteObject(String, String) + */ + default DBusInterface getRemoteObject(String _busname, DBusPath _objectpath) throws DBusException, InvalidBusNameException, InvalidObjectPathException { + DBusObjects.requireBusNameOrConnectionId(_busname); + DBusObjects.requireObjectPath(_objectpath); + + return getRemoteObject(_busname, _objectpath.toString()); + } + + /** + * Return a reference to a remote object. This method will resolve the well known name (if given) to a unique bus + * name when you call it. This means that if a well known name is released by one process and acquired by another + * calls to objects gained from this method will continue to operate on the original process. + * + * @param + * class extending {@link DBusInterface} + * @param _busname + * The bus name to connect to. Usually a well known bus name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object.$ + * @param _type + * The interface they are exporting it on. This type must have the same full class name and exposed + * method signatures as the interface the remote object is exporting. + * @param _autostart + * Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when + * calling a method with auto-start enabled, if the destination is a well-known name and is not owned the + * bus will attempt to start a process to take the name. When disabled an error is returned immediately. + * @return A reference to a remote object. + * @throws MissingInterfaceImplementationException + * If type is not a sub-type of DBusInterface + * @throws InvalidObjectPathException + * When object path is invalid + * @throws ClassOutsideOfPackageException + * When given type class has not package + * @throws DBusException + * on any other errors + */ + default I getPeerRemoteObject(String _busname, String _objectpath, Class _type, + boolean _autostart) throws DBusException { + + DBusObjects.requireBusNameOrConnectionId(_busname); + return getRemoteObject(getDBusOwnerName(_busname), _objectpath, _type, _autostart); + } + + /** + * @see #getPeerRemoteObject(String, String, Class, boolean) + */ + default I getPeerRemoteObject(String _busname, DBusPath _objectpath, Class _type, + boolean _autostart) throws DBusException { + + DBusObjects.requireBusNameOrConnectionId(_busname); + DBusObjects.requireObjectPath(_objectpath); + return getRemoteObject(getDBusOwnerName(_busname), _objectpath.getPath(), _type, _autostart); + } + + /** + * Return a reference to a remote object. This method will always refer to the well known name (if given) rather + * than resolving it to a unique bus name. In particular this means that if a process providing the well known name + * disappears and is taken over by another process proxy objects gained by this method will make calls on the new + * proccess. + * + * @param + * class extending {@link DBusInterface} + * @param _busname + * The bus name to connect to. Usually a well known bus name name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object. + * @param _type + * The interface they are exporting it on. This type must have the same full class name and exposed + * method signatures as the interface the remote object is exporting. + * @return A reference to a remote object. + * @throws MissingInterfaceImplementationException + * If type is not a sub-type of DBusInterface + * @throws InvalidObjectPathException + * When object path is invalid + * @throws ClassOutsideOfPackageException + * When given type class has not package + * @throws DBusException + * on any other errors + */ + default I getRemoteObject(String _busname, String _objectpath, Class _type) + throws DBusException { + return getRemoteObject(_busname, _objectpath, _type, true); + } + + /** + * @see #getRemoteObject(String, String, Class) + */ + default I getRemoteObject(String _busname, DBusPath _objectpath, Class _type) + throws DBusException { + DBusObjects.requireObjectPath(_objectpath); + return getRemoteObject(_busname, _objectpath.getPath(), _type, true); + } + /** + * Return a reference to a remote object. This method will always refer to the well known name (if given) rather + * than resolving it to a unique bus name. In particular this means that if a process providing the well known name + * disappears and is taken over by another process proxy objects gained by this method will make calls on the new + * process. + * + * @param + * class extending {@link DBusInterface} + * @param _busname + * The bus name to connect to. Usually a well known bus name name in dot-notation (such as + * "org.freedesktop.local") or may be a DBus address such as ":1-16". + * @param _objectpath + * The path on which the process is exporting the object. + * @param _type + * The interface they are exporting it on. This type must have the same full class name and exposed + * method signatures as the interface the remote object is exporting. + * @param _autostart + * Disable/Enable auto-starting of services in response to calls on this object. Default is enabled; when + * calling a method with auto-start enabled, if the destination is a well-known name and is not owned the + * bus will attempt to start a process to take the name. When disabled an error is returned immediately. + * @return A reference to a remote object. + * @throws MissingInterfaceImplementationException + * If type is not a sub-type of DBusInterface + * @throws InvalidObjectPathException + * When object path is invalid + * @throws ClassOutsideOfPackageException + * When given type class has not package + * @throws DBusException + * on any other errors + */ + I getRemoteObject(String _busname, String _objectpath, Class _type, + boolean _autostart) throws DBusException; + + /** + * @see #getRemoteObject(String, String, Class, boolean) + */ + default I getRemoteObject(String _busname, DBusPath _objectpath, Class _type, + boolean _autostart) throws DBusException { + DBusObjects.requireObjectPath(_objectpath); + return getRemoteObject(_busname, _objectpath.getPath(), _type, _autostart); + } + + /** + * Returns name of the current owning dbus session. + * @param _busName bus name + * @return String or null + */ + String getDBusOwnerName(String _busName); +} diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/ClassOutsideOfPackageException.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/ClassOutsideOfPackageException.java new file mode 100644 index 00000000..53a5e9b3 --- /dev/null +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/ClassOutsideOfPackageException.java @@ -0,0 +1,18 @@ +package org.freedesktop.dbus.exceptions; + +/** + * @since 5.1.1 - 2024-11-16 + * @author hypfvieh + */ +public class ClassOutsideOfPackageException extends DBusException { + private static final long serialVersionUID = 1L; + + public ClassOutsideOfPackageException(Class _clz) { + super("DBusInterfaces cannot be declared outside a package but " + (_clz == null ? null : _clz.getName()) + " has no package"); + } + + public ClassOutsideOfPackageException(String _msg) { + super(_msg); + } + +} diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/MissingInterfaceImplementationException.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/MissingInterfaceImplementationException.java new file mode 100644 index 00000000..e3bafb14 --- /dev/null +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/exceptions/MissingInterfaceImplementationException.java @@ -0,0 +1,20 @@ +package org.freedesktop.dbus.exceptions; + +import org.freedesktop.dbus.interfaces.DBusInterface; + +/** + * @since 5.1.1 - 2024-11-16 + * @author hypfvieh + */ +public class MissingInterfaceImplementationException extends DBusException { + private static final long serialVersionUID = 1L; + + public MissingInterfaceImplementationException(Class _clz) { + super("Given class " + (_clz == null ? null : _clz.getName()) + " does not implement " + DBusInterface.class.getName()); + } + + public MissingInterfaceImplementationException(String _msg) { + super(_msg); + } + +} diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java index eacafb2b..233e4be9 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/DBusSignal.java @@ -1,9 +1,6 @@ package org.freedesktop.dbus.messages; -import org.freedesktop.dbus.DBusMatchRule; -import org.freedesktop.dbus.Marshalling; -import org.freedesktop.dbus.ObjectPath; -import org.freedesktop.dbus.Struct; +import org.freedesktop.dbus.*; import org.freedesktop.dbus.connections.base.AbstractConnectionBase; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.MessageFormatException; @@ -330,9 +327,11 @@ public boolean matchesParameters(List> _wantedArgs) { if (Enum.class.isAssignableFrom(class1) && String.class.equals(_wantedArgs.get(i))) { continue; - } else if (DBusInterface.class.isAssignableFrom(class1) && ObjectPath.class.equals(_wantedArgs.get(i))) { + } else if (DBusInterface.class.isAssignableFrom(class1) && ObjectPath.class.equals(_wantedArgs.get(i))) { continue; - } else if (Struct.class.isAssignableFrom(class1) && Object[].class.equals(_wantedArgs.get(i))) { + } else if (DBusInterface.class.isAssignableFrom(class1) && DBusPath.class.equals(_wantedArgs.get(i))) { + continue; + } else if (Struct.class.isAssignableFrom(class1) && Object[].class.equals(_wantedArgs.get(i))) { continue; } else if (class1.isAssignableFrom(_wantedArgs.get(i))) { continue; diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/Message.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/Message.java index 91189752..4f2ac87c 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/Message.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/Message.java @@ -929,7 +929,7 @@ private Object extractOne(byte[] _signatureBuf, byte[] _dataBuf, int[] _offsets, case OBJECT_PATH: length = (int) demarshallint(_dataBuf, _offsets[OFFSET_DATA], 4); _offsets[OFFSET_DATA] += 4; - rv = new ObjectPath(getSource(), new String(_dataBuf, _offsets[OFFSET_DATA], length)); + rv = new DBusPath(getSource(), new String(_dataBuf, _offsets[OFFSET_DATA], length)); _offsets[OFFSET_DATA] += length + 1; break; case SIGNATURE: diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/DBusObjects.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/DBusObjects.java index fcb35499..68ef3a40 100644 --- a/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/DBusObjects.java +++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/DBusObjects.java @@ -1,9 +1,8 @@ package org.freedesktop.dbus.utils; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.exceptions.InvalidBusNameException; -import org.freedesktop.dbus.exceptions.InvalidInterfaceSignature; -import org.freedesktop.dbus.exceptions.InvalidObjectPathException; +import org.freedesktop.dbus.DBusPath; +import org.freedesktop.dbus.exceptions.*; +import org.freedesktop.dbus.interfaces.DBusInterface; import java.lang.reflect.Modifier; import java.util.LinkedHashSet; @@ -51,6 +50,30 @@ private static T requireBase(T _input, Predicate return _input; } + /** + * Ensures that the given class is part of a package. + * + * @param _clz class to check + * @return input if valid + * + * @throws ClassOutsideOfPackageException when class has no package + */ + public static Class requirePackage(Class _clz) throws ClassOutsideOfPackageException { + return requirePackage(_clz, null); + } + + /** + * Ensures that the given class is implements or extends {@link DBusInterface} class. + * + * @param _clz class to check + * @return input if valid + * + * @throws MissingInterfaceImplementationException when class is incompatible + */ + public static Class requireDBusInterface(Class _clz) throws MissingInterfaceImplementationException { + return requireDBusInterface(_clz, null); + } + /** * Ensures given string is a valid object path. * @@ -64,6 +87,56 @@ public static String requireObjectPath(String _objectPath) throws InvalidObjectP return requireObjectPath(_objectPath, null); } + /** + * Ensures given DBusPath is a valid object path. + * + * @param _dbusPath to check + * + * @return input DBusPath if valid + * + * @throws InvalidObjectPathException when input is not a valid object path + */ + public static DBusPath requireObjectPath(DBusPath _dbusPath) throws InvalidObjectPathException { + return requireObjectPath(_dbusPath, null); + } + + /** + * Ensures that the given class is part of a package. + * + * @param _clz class to check + * @param _customMsg custom error message + * @return input if valid + * + * @throws ClassOutsideOfPackageException when class has no package + */ + public static Class requirePackage(Class _clz, String _customMsg) throws ClassOutsideOfPackageException { + return requireBase(_clz, DBusObjects::validateClassHasPackage, msg -> { + if (Util.isBlank(_customMsg)) { + return new ClassOutsideOfPackageException(_clz); + } + return new ClassOutsideOfPackageException(msg); + + }, _customMsg); + } + + /** + * Ensures that the given class is implements or extends {@link DBusInterface} class. + * + * @param _clz class to check + * @param _customMsg custom error message + * @return input if valid + * + * @throws MissingInterfaceImplementationException when class is incompatible + */ + public static Class requireDBusInterface(Class _clz, String _customMsg) throws MissingInterfaceImplementationException { + return requireBase(_clz, DBusObjects::validateDBusInterface, msg -> { + if (Util.isBlank(_customMsg)) { + return new MissingInterfaceImplementationException(_clz); + } + return new MissingInterfaceImplementationException(msg); + }, _customMsg); + } + /** * Ensures given string is a valid object path. * @@ -77,6 +150,19 @@ public static String requireObjectPath(String _objectPath, String _customMsg) th return requireBase(_objectPath, DBusObjects::validateObjectPath, InvalidObjectPathException::new, _customMsg); } + /** + * Ensures given DBusPath is a valid object path. + * + * @param _dbusPath to check + * @param _customMsg custom error message + * @return input DBusPath if valid + * + * @throws InvalidObjectPathException when input is not a valid object path + */ + public static DBusPath requireObjectPath(DBusPath _dbusPath, String _customMsg) throws InvalidObjectPathException { + return requireBase(_dbusPath, x -> DBusObjects.validateNotObjectPath(x.getPath()), InvalidObjectPathException::new, _customMsg); + } + /** * Ensures given string is a valid bus name. * @@ -181,6 +267,28 @@ public static boolean validateObjectPath(String _objectPath) { return !validateNotObjectPath(_objectPath); } + /** + * Checks if given class is compatible with {@link DBusInterface} class. + * + * @param _clz class to check + * + * @return true if class is compatible + */ + public static boolean validateDBusInterface(Class _clz) { + return DBusInterface.class.isAssignableFrom(_clz); + } + + /** + * Checks if given class has/is in a package. + * + * @param _clz class to check + * + * @return true if class has a package + */ + public static boolean validateClassHasPackage(Class _clz) { + return !_clz.getName().equals(_clz.getSimpleName()); + } + /** * Checks if input is NOT a valid object path. * diff --git a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MarshallingTest.java b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MarshallingTest.java index eb5a234d..53483317 100644 --- a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MarshallingTest.java +++ b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MarshallingTest.java @@ -2,7 +2,6 @@ import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.Marshalling; -import org.freedesktop.dbus.ObjectPath; import org.freedesktop.dbus.annotations.DBusInterfaceName; import org.freedesktop.dbus.annotations.Position; import org.freedesktop.dbus.exceptions.DBusException; @@ -190,9 +189,9 @@ class ServicesChanged extends DBusSignal { final String objectPath; final List changed; - final List removed; + final List removed; - ServicesChanged(String _objectPath, List _k, List _removedItems) throws DBusException { + ServicesChanged(String _objectPath, List _k, List _removedItems) throws DBusException { super(_objectPath, _k, _removedItems); objectPath = _objectPath; @@ -208,7 +207,7 @@ List getChanged() { return changed; } - List getRemoved() { + List getRemoved() { return removed; }