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;
}