diff --git a/README.md b/README.md
index f7b470cb..77cf53f2 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,7 @@ The library will remain open source and MIT licensed and can still be used, fork
- Updated minimum required Java version to 17
- Improved handling of listening connections to allow proper bootstrapping the connection before actually starting accepting new connections (thanks to [brett-smith](https://github.com/brett-smith) ([#213](https://github.com/hypfvieh/dbus-java/issues/213)))
- Updated export-object documentation ([#236](https://github.com/hypfvieh/dbus-java/issues/236))
+ - Fixed issues with autoConnect option, added method to register to bus by 'Hello' message manually, thanks to [brett-smith](https://github.com/brett-smith) ([#238](https://github.com/hypfvieh/dbus-java/issues/238))
##### Changes in 4.3.1 (2023-10-03):
- Provide classloader to ServiceLoader in TransportBuilder (for loading actual transports) and AbstractTransport (for loading IMessageReader/Writer implementations), thanks to [cthbleachbit](https://github.com/cthbleachbit) ([#210](https://github.com/hypfvieh/dbus-java/issues/210), [PR#211](https://github.com/hypfvieh/dbus-java/issues/211))
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/FileDescriptor.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/FileDescriptor.java
index 7f329d09..e8aef9d0 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/FileDescriptor.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/FileDescriptor.java
@@ -1,20 +1,17 @@
package org.freedesktop.dbus;
import org.freedesktop.dbus.exceptions.MarshallingException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.freedesktop.dbus.spi.message.ISocketProvider;
+import org.freedesktop.dbus.utils.ReflectionFileDescriptorHelper;
-import java.lang.reflect.*;
+import java.util.Optional;
/**
- * Represents a FileDescriptor to be passed over the bus. Can be created from
- * either an integer(gotten through some JNI/JNA/JNR call) or from a
- * java.io.FileDescriptor.
- *
+ * Represents a FileDescriptor to be passed over the bus.
+ * Can be created from either an integer (gotten through some JNI/JNA/JNR call) or from a
+ * {@link java.io.FileDescriptor}.
*/
-public class FileDescriptor {
-
- private final Logger logger = LoggerFactory.getLogger(getClass());
+public final class FileDescriptor {
private final int fd;
@@ -22,39 +19,76 @@ public FileDescriptor(int _fd) {
fd = _fd;
}
- public FileDescriptor(java.io.FileDescriptor _data) throws MarshallingException {
- fd = getFileDescriptor(_data);
- }
+ /**
+ * Converts this DBus {@link FileDescriptor} to a {@link java.io.FileDescriptor}.
+ * Tries to use the provided ISocketProvider if present first.
+ * If not present or conversion failed, tries to convert using reflection.
+ *
+ * @param _provider provider or null
+ *
+ * @return java file descriptor
+ * @throws MarshallingException when converting fails
+ */
+ public java.io.FileDescriptor toJavaFileDescriptor(ISocketProvider _provider) throws MarshallingException {
+ if (_provider != null) {
+ Optional result = _provider.createFileDescriptor(fd);
+ if (result.isPresent()) {
+ return result.get();
+ }
+ }
- public java.io.FileDescriptor toJavaFileDescriptor() throws MarshallingException {
- return createFileDescriptorByReflection(fd);
+ return ReflectionFileDescriptorHelper.getInstance()
+ .flatMap(helper -> helper.createFileDescriptor(fd))
+ .orElseThrow(() -> new MarshallingException("Could not create new FileDescriptor instance"));
}
public int getIntFileDescriptor() {
return fd;
}
- private int getFileDescriptor(java.io.FileDescriptor _data) throws MarshallingException {
- Field declaredField;
- try {
- declaredField = _data.getClass().getDeclaredField("fd");
- declaredField.setAccessible(true);
- return declaredField.getInt(_data);
- } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException _ex) {
- logger.error("Could not get filedescriptor by reflection.", _ex);
- throw new MarshallingException("Could not get member 'fd' of FileDescriptor by reflection!", _ex);
+ @Override
+ public boolean equals(Object _o) {
+ if (this == _o) {
+ return true;
}
+ if (_o == null || getClass() != _o.getClass()) {
+ return false;
+ }
+ FileDescriptor that = (FileDescriptor) _o;
+ return fd == that.fd;
+ }
+
+ @Override
+ public int hashCode() {
+ return fd;
}
- private java.io.FileDescriptor createFileDescriptorByReflection(long _demarshallint) throws MarshallingException {
- try {
- Constructor constructor = java.io.FileDescriptor.class.getDeclaredConstructor(int.class);
- constructor.setAccessible(true);
- return constructor.newInstance((int) _demarshallint);
- } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException _ex) {
- logger.error("Could not create new FileDescriptor instance by reflection.", _ex);
- throw new MarshallingException("Could not create new FileDescriptor instance by reflection", _ex);
+ @Override
+ public String toString() {
+ return FileDescriptor.class.getSimpleName() + "[fd=" + fd + "]";
+ }
+
+ /**
+ * Utility method to create a DBus {@link FileDescriptor} from a {@link java.io.FileDescriptor}.
+ * Tries to use the provided ISocketProvider if present first.
+ * If not present or conversion failed, tries to convert using reflection.
+ *
+ * @param _data file descriptor
+ * @param _provider socket provider or null
+ *
+ * @return DBus FileDescriptor
+ * @throws MarshallingException when conversion fails
+ */
+ public static FileDescriptor fromJavaFileDescriptor(java.io.FileDescriptor _data, ISocketProvider _provider) throws MarshallingException {
+ if (_provider != null) {
+ Optional result = _provider.getFileDescriptorValue(_data);
+ if (result.isPresent()) {
+ return new FileDescriptor(result.get());
+ }
}
+
+ return new FileDescriptor(ReflectionFileDescriptorHelper.getInstance()
+ .flatMap(helper -> helper.getFileDescriptorValue(_data))
+ .orElseThrow(() -> new MarshallingException("Could not get FileDescriptor value")));
}
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java
index f9c62475..38ce6698 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/AbstractConnection.java
@@ -1445,6 +1445,10 @@ public TransportConfig getTransportConfig() {
return transport.getTransportConfig();
}
+ public boolean isFileDescriptorSupported() {
+ return transport.isFileDescriptorSupported();
+ }
+
@Override
public String toString() {
return getClass().getSimpleName() + "[address=" + busAddress + "]";
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java
index 60762831..ed03cc80 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfig.java
@@ -32,6 +32,7 @@ public final class TransportConfig {
private String fileGroup;
private byte endianess = BaseConnectionBuilder.getSystemEndianness();
+ private boolean registerSelf = true;
/**
* Unix file permissions to set on socket file if this is a server transport (ignored on Windows, does nothing if
@@ -151,6 +152,14 @@ public void setEndianess(byte _endianess) {
endianess = _endianess;
}
+ public boolean isRegisterSelf() {
+ return registerSelf;
+ }
+
+ public void setRegisterSelf(boolean _registerSelf) {
+ registerSelf = _registerSelf;
+ }
+
/**
* Toggles the busaddress to be a listening (server) or non listening (client) connection.
* @param _listening true to be a server connection
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java
index c202867b..974215d5 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/config/TransportConfigBuilder.java
@@ -102,6 +102,19 @@ public X withAutoConnect(boolean _connect) {
return self();
}
+ /**
+ * Register the new connection on DBus using 'hello' message. Default is true.
+ *
+ * @param _register boolean
+ * @return this
+ *
+ * @since 5.0.0 - 2023-10-11
+ */
+ public X withRegisterSelf(boolean _register) {
+ config.setRegisterSelf(_register);
+ return self();
+ }
+
/**
* Switch to the {@link SaslConfigBuilder} to configure the SASL authentication mechanism.
* Use {@link SaslConfigBuilder#back()} to return to this builder when finished.
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 c39c8817..f297ec29 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
@@ -47,6 +47,9 @@ public final class DBusConnection extends AbstractConnection {
private final String machineId;
private DBus dbus;
+ /** Whether the connection was registered using 'Hello' message. */
+ private boolean registered;
+
/** Count how many 'connections' we manage internally.
* This is required because a {@link DBusConnection} to the same address will always return the same object and
* the 'real' disconnection should only occur when there is no second/third/whatever connection is left. */
@@ -73,10 +76,9 @@ private AtomicInteger getConcurrentConnections() {
/**
* Connect to bus and register if asked. Should only be called by Builder.
*
- * @param _registerSelf true to register
* @throws DBusException if registering or connection fails
*/
- void connect(boolean _registerSelf) throws DBusException {
+ void connectImpl() throws DBusException {
// start listening for calls
try {
listen();
@@ -89,14 +91,32 @@ void connect(boolean _registerSelf) throws DBusException {
addSigHandlerWithoutMatch(DBus.NameAcquired.class, h);
// register ourselves if not disabled
- if (_registerSelf) {
- dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class);
- try {
- busnames.add(dbus.Hello());
- } catch (DBusExecutionException _ex) {
- logger.debug("Error while doing 'Hello' handshake", _ex);
- throw new DBusException(_ex.getMessage());
- }
+ if (getTransportConfig().isRegisterSelf() && getTransport().isConnected()) {
+ register();
+ }
+ }
+
+ /**
+ * Register this connection on the bus using 'Hello' message.
+ * Will do nothing if session was already registered.
+ *
+ * @throws DBusException when sending message fails
+ *
+ * @since 5.0.0 - 2023-10-11
+ */
+ public void register() throws DBusException {
+ if (registered) {
+ return;
+ }
+
+ dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class);
+
+ try {
+ busnames.add(dbus.Hello());
+ registered = true;
+ } catch (DBusExecutionException _ex) {
+ logger.debug("Error while doing 'Hello' handshake", _ex);
+ throw new DBusException(_ex.getMessage(), _ex);
}
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java
index 72905e56..3d30e946 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/impl/DBusConnectionBuilder.java
@@ -20,7 +20,6 @@
public final class DBusConnectionBuilder extends BaseConnectionBuilder {
private final String machineId;
- private boolean registerSelf = true;
private boolean shared = true;
private DBusConnectionBuilder(BusAddress _address, String _machineId) {
@@ -146,16 +145,6 @@ private static BusAddress validateTransportAddress(BusAddress _address) {
return address;
}
- /**
- * Register the new connection on DBus using 'hello' message. Default is true.
- *
- * @param _register boolean
- * @return this
- */
- public DBusConnectionBuilder withRegisterSelf(boolean _register) {
- registerSelf = _register;
- return this;
- }
/**
* Use this connection as shared connection. Shared connection means that the same connection is used multiple times
@@ -199,7 +188,7 @@ public DBusConnection build() throws DBusException {
c.setDisconnectCallback(getDisconnectCallback());
c.setWeakReferences(isWeakReference());
- c.connect(registerSelf);
+ c.connectImpl();
return c;
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java
index 7afaec69..ca441e39 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/AbstractTransport.java
@@ -235,6 +235,7 @@ private void authenticate(SocketChannel _sock) throws IOException {
private TransportConnection createInputOutput(SocketChannel _socket) {
IMessageReader reader = null;
IMessageWriter writer = null;
+ ISocketProvider providerImpl = null;
try {
for (ISocketProvider provider : spiLoader) {
logger.debug("Found ISocketProvider {}", provider);
@@ -244,6 +245,7 @@ private TransportConnection createInputOutput(SocketChannel _socket) {
writer = provider.createWriter(_socket);
if (reader != null && writer != null) {
logger.debug("Using ISocketProvider {}", provider);
+ providerImpl = provider;
break;
}
}
@@ -261,7 +263,7 @@ private TransportConnection createInputOutput(SocketChannel _socket) {
// allows it
}
- return new TransportConnection(messageFactory, _socket, writer, reader);
+ return new TransportConnection(messageFactory, _socket, providerImpl, writer, reader);
}
/**
@@ -313,6 +315,10 @@ public TransportConfig getTransportConfig() {
return config;
}
+ public boolean isFileDescriptorSupported() {
+ return fileDescriptorSupported;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java
index 6b0ed378..88945b6a 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/connections/transports/TransportConnection.java
@@ -1,8 +1,7 @@
package org.freedesktop.dbus.connections.transports;
import org.freedesktop.dbus.messages.MessageFactory;
-import org.freedesktop.dbus.spi.message.IMessageReader;
-import org.freedesktop.dbus.spi.message.IMessageWriter;
+import org.freedesktop.dbus.spi.message.*;
import java.io.Closeable;
import java.io.IOException;
@@ -27,12 +26,14 @@ public class TransportConnection implements Closeable {
private final SocketChannel channel;
private final IMessageWriter writer;
private final IMessageReader reader;
+ private final ISocketProvider socketProviderImpl;
private final MessageFactory messageFactory;
- public TransportConnection(MessageFactory _factory, SocketChannel _channel, IMessageWriter _writer, IMessageReader _reader) {
+ public TransportConnection(MessageFactory _factory, SocketChannel _channel, ISocketProvider _socketProviderImpl, IMessageWriter _writer, IMessageReader _reader) {
messageFactory = _factory;
channel = _channel;
+ socketProviderImpl = _socketProviderImpl;
writer = _writer;
reader = _reader;
}
@@ -49,6 +50,10 @@ public IMessageReader getReader() {
return reader;
}
+ public ISocketProvider getSocketProviderImpl() {
+ return socketProviderImpl;
+ }
+
public long getId() {
return id;
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractInputStreamMessageReader.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractInputStreamMessageReader.java
index 9632ab68..91779c6c 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractInputStreamMessageReader.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractInputStreamMessageReader.java
@@ -27,13 +27,14 @@ public abstract class AbstractInputStreamMessageReader implements IMessageReader
private final byte[] buf;
private final byte[] tbuf;
private final SocketChannel inputChannel;
- private final boolean hasFileDescriptorSupport;
private byte[] header;
private byte[] body;
- public AbstractInputStreamMessageReader(final SocketChannel _in, boolean _hasFileDescriptorSupport) {
- hasFileDescriptorSupport = _hasFileDescriptorSupport;
+ private final ISocketProvider socketProviderImpl;
+
+ public AbstractInputStreamMessageReader(final SocketChannel _in, ISocketProvider _socketProviderImpl) {
+ socketProviderImpl = Objects.requireNonNull(_socketProviderImpl, "ISocketProvider implementation required");
inputChannel = Objects.requireNonNull(_in, "SocketChannel required");
len = new int[4];
tbuf = new byte[4];
@@ -168,7 +169,7 @@ public final Message readMessage() throws IOException, DBusException {
try {
List fds = null;
- if (hasFileDescriptorSupport) {
+ if (socketProviderImpl.isFileDescriptorPassingSupported()) {
fds = readFileDescriptors(inputChannel);
}
@@ -203,6 +204,14 @@ public final Message readMessage() throws IOException, DBusException {
*/
protected abstract List readFileDescriptors(SocketChannel _inputChannel) throws DBusException;
+ protected Logger getLogger() {
+ return logger;
+ }
+
+ protected ISocketProvider getSocketProviderImpl() {
+ return socketProviderImpl;
+ }
+
@Override
public void close() throws IOException {
if (inputChannel.isOpen()) {
@@ -218,7 +227,7 @@ public boolean isClosed() {
@Override
public String toString() {
- return getClass().getSimpleName() + " [inputChannel=" + inputChannel + ", hasFileDescriptorSupport=" + hasFileDescriptorSupport + "]";
+ return getClass().getSimpleName() + " [inputChannel=" + inputChannel + ", socketProviderImpl=" + socketProviderImpl + "]";
}
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractOutputStreamMessageWriter.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractOutputStreamMessageWriter.java
index 8e6424a3..f8aae728 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractOutputStreamMessageWriter.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/AbstractOutputStreamMessageWriter.java
@@ -22,11 +22,12 @@ public abstract class AbstractOutputStreamMessageWriter implements IMessageWrite
private final Logger logger = LoggerFactory.getLogger(getClass());
private final SocketChannel outputChannel;
- private final boolean hasFileDescriptorSupport;
- public AbstractOutputStreamMessageWriter(final SocketChannel _out, boolean _fileDescriptorSupport) {
+ private final ISocketProvider socketProviderImpl;
+
+ public AbstractOutputStreamMessageWriter(final SocketChannel _out, ISocketProvider _socketProviderImpl) {
outputChannel = Objects.requireNonNull(_out, "SocketChannel required");
- hasFileDescriptorSupport = _fileDescriptorSupport;
+ socketProviderImpl = Objects.requireNonNull(_socketProviderImpl, "ISocketProvider implementation required");
}
@Override
@@ -40,6 +41,10 @@ public final void writeMessage(Message _msg) throws IOException {
return;
}
+ if (socketProviderImpl.isFileDescriptorPassingSupported()) {
+ writeFileDescriptors(outputChannel, _msg.getFiledescriptors());
+ }
+
for (byte[] buf : _msg.getWireData()) {
if (logger.isTraceEnabled()) {
logger.trace("{}", null == buf ? "(buffer was null)" : Hexdump.format(buf));
@@ -51,10 +56,6 @@ public final void writeMessage(Message _msg) throws IOException {
outputChannel.write(ByteBuffer.wrap(buf));
}
- if (hasFileDescriptorSupport) {
- writeFileDescriptors(outputChannel, _msg.getFiledescriptors());
- }
-
logger.trace("Message sent: {}", _msg);
}
@@ -69,6 +70,14 @@ public final void writeMessage(Message _msg) throws IOException {
*/
protected abstract void writeFileDescriptors(SocketChannel _outputChannel, List _filedescriptors) throws IOException;
+ protected Logger getLogger() {
+ return logger;
+ }
+
+ protected ISocketProvider getSocketProviderImpl() {
+ return socketProviderImpl;
+ }
+
@Override
public void close() throws IOException {
logger.debug("Closing Message Writer");
@@ -85,7 +94,7 @@ public boolean isClosed() {
@Override
public String toString() {
- return getClass().getSimpleName() + " [outputChannel=" + outputChannel + ", hasFileDescriptorSupport=" + hasFileDescriptorSupport + "]";
+ return getClass().getSimpleName() + " [outputChannel=" + outputChannel + ", socketProviderImpl=" + socketProviderImpl + "]";
}
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/DefaultSocketProvider.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/DefaultSocketProvider.java
new file mode 100644
index 00000000..5072c1da
--- /dev/null
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/DefaultSocketProvider.java
@@ -0,0 +1,40 @@
+package org.freedesktop.dbus.spi.message;
+
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+
+/**
+ * Default internally used socket provider implementation.
+ *
+ * @author hypfvieh
+ * @since 5.0.0 - 2023-10-09
+ */
+final class DefaultSocketProvider implements ISocketProvider {
+
+ static final ISocketProvider INSTANCE = new DefaultSocketProvider();
+
+ private DefaultSocketProvider() {
+
+ }
+
+ @Override
+ public IMessageReader createReader(SocketChannel _socket) throws IOException {
+ return new InputStreamMessageReader(_socket);
+ }
+
+ @Override
+ public IMessageWriter createWriter(SocketChannel _socket) throws IOException {
+ return new OutputStreamMessageWriter(_socket);
+ }
+
+ @Override
+ public void setFileDescriptorSupport(boolean _support) {
+ // not supported
+ }
+
+ @Override
+ public boolean isFileDescriptorPassingSupported() {
+ return false;
+ }
+
+}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java
index 0215bf21..5359a011 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/ISocketProvider.java
@@ -2,8 +2,10 @@
import org.freedesktop.dbus.connections.transports.AbstractTransport;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.SocketChannel;
+import java.util.Optional;
public interface ISocketProvider {
/**
@@ -39,4 +41,30 @@ public interface ISocketProvider {
* @return true if file descriptors are supported by this provider, false otherwise
*/
boolean isFileDescriptorPassingSupported();
+
+ /**
+ * Attempts to extract raw FileDescriptor value from {@link FileDescriptor} instance.
+ * Note that not any {@link FileDescriptor} can be represented as int, for example Windows uses HANDLE as descriptor,
+ * which excess range of int values, thus cannot be safely cast to int.
+ *
+ * @param _fd FileDescriptor to extract value from
+ * @return int representation, packed to {@link Optional} if operation succeeded, or {@link Optional#empty()} otherwise
+ * @see #createFileDescriptor(int)
+ * @since 5.0.0 - 2023-10-07
+ */
+ default Optional getFileDescriptorValue(FileDescriptor _fd) {
+ return Optional.empty();
+ }
+
+ /**
+ * Attempts to create native {@link FileDescriptor} from raw int value.
+ *
+ * @param _fd FileDescriptor to extract value from
+ * @return {@link FileDescriptor}, instantiated with provided value, packed to {@link Optional} if operation succeeded, or {@link Optional#empty()} otherwise
+ * @see #getFileDescriptorValue(FileDescriptor)
+ * @since 5.0.0 - 2023-10-07
+ */
+ default Optional createFileDescriptor(int _fd) {
+ return Optional.empty();
+ }
}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java
index 9ab356df..3446c269 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/InputStreamMessageReader.java
@@ -8,7 +8,7 @@
public class InputStreamMessageReader extends AbstractInputStreamMessageReader {
public InputStreamMessageReader(final SocketChannel _in) {
- super(_in, false);
+ super(_in, DefaultSocketProvider.INSTANCE);
}
@Override
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java
index 9fa768b2..be0440a1 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/spi/message/OutputStreamMessageWriter.java
@@ -8,7 +8,7 @@
public class OutputStreamMessageWriter extends AbstractOutputStreamMessageWriter {
public OutputStreamMessageWriter(SocketChannel _out) {
- super(_out, false);
+ super(_out, DefaultSocketProvider.INSTANCE);
}
@Override
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/ReflectionFileDescriptorHelper.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/ReflectionFileDescriptorHelper.java
new file mode 100644
index 00000000..3f90cb32
--- /dev/null
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/utils/ReflectionFileDescriptorHelper.java
@@ -0,0 +1,78 @@
+package org.freedesktop.dbus.utils;
+
+import org.freedesktop.dbus.spi.message.ISocketProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Optional;
+
+/**
+ * Helper to work with {@link FileDescriptor} instances by using reflection
+ *
+ * @author Sergey Shatunov
+ * @since 5.0.0 - 2023-10-07
+ */
+public final class ReflectionFileDescriptorHelper {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionFileDescriptorHelper.class);
+ private static volatile ReflectionFileDescriptorHelper instance;
+
+ private final Field fdField;
+ private final Constructor constructor;
+
+ private ReflectionFileDescriptorHelper() throws ReflectiveOperationException {
+ fdField = FileDescriptor.class.getDeclaredField("fd");
+ fdField.setAccessible(true);
+ constructor = FileDescriptor.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ }
+
+ /**
+ * @return {@link ReflectionFileDescriptorHelper} instance, or {@link Optional#empty()} if it cannot be initialized
+ * (mainly due to missing reflection access)
+ */
+ public static Optional getInstance() {
+ if (instance == null) {
+ synchronized (ReflectionFileDescriptorHelper.class) {
+ if (instance == null) {
+ try {
+ instance = new ReflectionFileDescriptorHelper();
+ } catch (ReflectiveOperationException _ex) {
+ LOGGER.error("Unable to hook up java.io.FileDescriptor by using reflection.", _ex);
+ return Optional.empty();
+ }
+ }
+ }
+ }
+
+ return Optional.ofNullable(instance);
+ }
+
+ /**
+ * @see ISocketProvider#getFileDescriptorValue(FileDescriptor)
+ */
+ public Optional getFileDescriptorValue(FileDescriptor _fd) {
+ try {
+ return Optional.of(fdField.getInt(_fd));
+ } catch (SecurityException | IllegalArgumentException | IllegalAccessException _ex) {
+ LOGGER.error("Could not get file descriptor by reflection.", _ex);
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * @see ISocketProvider#createFileDescriptor(int)
+ */
+ public Optional createFileDescriptor(int _fd) {
+ try {
+ return Optional.of(constructor.newInstance(_fd));
+ } catch (SecurityException | InstantiationException | IllegalAccessException
+ | IllegalArgumentException | InvocationTargetException _ex) {
+ LOGGER.error("Could not create new FileDescriptor instance by reflection.", _ex);
+ return Optional.empty();
+ }
+ }
+}
diff --git a/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/pulseaudio/PulseAudioDbus.java b/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/pulseaudio/PulseAudioDbus.java
index fb401a78..fbaadba3 100755
--- a/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/pulseaudio/PulseAudioDbus.java
+++ b/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/pulseaudio/PulseAudioDbus.java
@@ -35,8 +35,10 @@ public static void main(String[] _args) throws DBusException {
} else {
DBusConnection connection = DBusConnectionBuilder
.forAddress(address)
- .withRegisterSelf(false)
.withShared(false)
+ .transportConfig()
+ .withRegisterSelf(false)
+ .back()
.build();
Properties core1Props = connection.getRemoteObject("org.PulseAudio.Core1", "/org/pulseaudio/core1", Properties.class);
diff --git a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/FileDescriptorsTest.java b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/FileDescriptorsTest.java
new file mode 100644
index 00000000..3389c1c8
--- /dev/null
+++ b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/FileDescriptorsTest.java
@@ -0,0 +1,86 @@
+package org.freedesktop.dbus.test;
+
+import org.freedesktop.dbus.FileDescriptor;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
+import org.freedesktop.dbus.connections.transports.TransportBuilder;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.freedesktop.dbus.exceptions.DBusExecutionException;
+import org.freedesktop.dbus.interfaces.DBusInterface;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+
+@EnabledIf(value = "isFileDescriptorSupported", disabledReason = "file descriptors not supported with the current transport")
+public class FileDescriptorsTest extends AbstractDBusBaseTest {
+ public static final String TEST_OBJECT_PATH = "/FileDescriptorsTest";
+
+ private DBusConnection serverconn = null, clientconn = null;
+
+ @BeforeEach
+ public void setUp() throws DBusException {
+ serverconn = DBusConnectionBuilder.forSessionBus().withShared(false).build();
+ clientconn = DBusConnectionBuilder.forSessionBus().withShared(false).build();
+ serverconn.requestBusName("foo.bar.Test");
+ serverconn.exportObject(TEST_OBJECT_PATH, new FDPassingImpl());
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ logger.debug("Checking for outstanding errors");
+ DBusExecutionException dbee = serverconn.getError();
+ if (null != dbee) {
+ throw dbee;
+ }
+ dbee = clientconn.getError();
+ if (null != dbee) {
+ throw dbee;
+ }
+
+ logger.debug("Disconnecting");
+ /* Disconnect from the bus. */
+ clientconn.disconnect();
+ serverconn.releaseBusName("foo.bar.Test");
+ serverconn.disconnect();
+ }
+
+ public static boolean isFileDescriptorSupported() throws DBusException, IOException {
+ if (!TransportBuilder.getRegisteredBusTypes().contains("UNIX")) {
+ return false;
+ }
+ try (DBusConnection conn = DBusConnectionBuilder.forSessionBus().build()) {
+ return conn.isFileDescriptorSupported();
+ }
+ }
+
+ @Test
+ public void fileDescriptorPassing() throws DBusException {
+ FDPassing remoteObject = clientconn.getRemoteObject("foo.bar.Test", TEST_OBJECT_PATH, FDPassing.class);
+ Stream.of(0, 1, 2).map(FileDescriptor::new).forEach(fd -> {
+ // that's not a mistake of using NotEquals here, as fd passing make a new copy with a new value
+ Assertions.assertNotEquals(fd.getIntFileDescriptor(), remoteObject.doNothing(fd).getIntFileDescriptor());
+ });
+ }
+
+ public interface FDPassing extends DBusInterface {
+ FileDescriptor doNothing(FileDescriptor _fd);
+ }
+
+ private static final class FDPassingImpl implements FDPassing {
+
+ @Override
+ public String getObjectPath() {
+ return TEST_OBJECT_PATH;
+ }
+
+ @Override
+ public FileDescriptor doNothing(FileDescriptor _fd) {
+ return _fd;
+ }
+ }
+}
diff --git a/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageReader.java b/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageReader.java
index ed992f41..2b0326c2 100644
--- a/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageReader.java
+++ b/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageReader.java
@@ -2,22 +2,20 @@
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.spi.message.AbstractInputStreamMessageReader;
+import org.freedesktop.dbus.spi.message.ISocketProvider;
import org.newsclub.net.unix.AFUNIXSocketChannel;
-import org.newsclub.net.unix.FileDescriptorCast;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
public class JUnixSocketMessageReader extends AbstractInputStreamMessageReader {
- private final Logger logger = LoggerFactory.getLogger(getClass());
- public JUnixSocketMessageReader(AFUNIXSocketChannel _socket, boolean _hasFileDescriptorSupport) {
- super(_socket, _hasFileDescriptorSupport);
+ public JUnixSocketMessageReader(AFUNIXSocketChannel _socket, ISocketProvider _socketProviderImpl) {
+ super(_socket, _socketProviderImpl);
}
@Override
@@ -30,10 +28,11 @@ protected List readFileDescriptors(SocketCh
} else {
List fds = new ArrayList<>();
for (FileDescriptor fd : receivedFileDescriptors) {
- fds.add(new org.freedesktop.dbus.FileDescriptor(FileDescriptorCast.using(fd).as(Integer.class)));
+ Optional fileDescriptorValue = getSocketProviderImpl().getFileDescriptorValue(fd);
+ fileDescriptorValue.ifPresent(f -> fds.add(new org.freedesktop.dbus.FileDescriptor(f)));
}
- logger.debug("=> {}", fds);
+ getLogger().debug("=> {}", fds);
return fds;
}
} catch (IOException _ex) {
diff --git a/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageWriter.java b/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageWriter.java
index dd2d54f0..a5604f4c 100644
--- a/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageWriter.java
+++ b/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketMessageWriter.java
@@ -1,8 +1,9 @@
package org.freedesktop.dbus.transport.junixsocket;
+import org.freedesktop.dbus.exceptions.MarshallingException;
import org.freedesktop.dbus.spi.message.AbstractOutputStreamMessageWriter;
+import org.freedesktop.dbus.spi.message.ISocketProvider;
import org.newsclub.net.unix.AFUNIXSocketChannel;
-import org.newsclub.net.unix.FileDescriptorCast;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -11,19 +12,23 @@
public class JUnixSocketMessageWriter extends AbstractOutputStreamMessageWriter {
- public JUnixSocketMessageWriter(AFUNIXSocketChannel _socket, boolean _hasFileDescriptorSupport) {
- super(_socket, _hasFileDescriptorSupport);
+ public JUnixSocketMessageWriter(AFUNIXSocketChannel _socket, ISocketProvider _socketProviderImpl) {
+ super(_socket, _socketProviderImpl);
}
@Override
protected void writeFileDescriptors(SocketChannel _outputChannel, List _filedescriptors) throws IOException {
if (_outputChannel instanceof AFUNIXSocketChannel) {
if (_filedescriptors != null && !_filedescriptors.isEmpty()) {
- FileDescriptor[] fds = new FileDescriptor[_filedescriptors.size()];
- for (int i = 0; i < _filedescriptors.size(); i++) {
- fds[i] = FileDescriptorCast.unsafeUsing(_filedescriptors.get(i).getIntFileDescriptor()).getFileDescriptor();
+ try {
+ FileDescriptor[] fds = new FileDescriptor[_filedescriptors.size()];
+ for (int i = 0; i < _filedescriptors.size(); i++) {
+ fds[i] = _filedescriptors.get(i).toJavaFileDescriptor(getSocketProviderImpl());
+ }
+ ((AFUNIXSocketChannel) _outputChannel).setOutboundFileDescriptors(fds);
+ } catch (MarshallingException _ex) {
+ throw new IOException("unable to marshall file descriptors", _ex);
}
- ((AFUNIXSocketChannel) _outputChannel).setOutboundFileDescriptors(fds);
} else {
((AFUNIXSocketChannel) _outputChannel).setOutboundFileDescriptors((FileDescriptor[]) null);
}
diff --git a/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketSocketProvider.java b/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketSocketProvider.java
index 56230706..38b72eca 100644
--- a/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketSocketProvider.java
+++ b/dbus-java-transport-junixsocket/src/main/java/org/freedesktop/dbus/transport/junixsocket/JUnixSocketSocketProvider.java
@@ -1,16 +1,19 @@
package org.freedesktop.dbus.transport.junixsocket;
-import org.freedesktop.dbus.spi.message.IMessageReader;
-import org.freedesktop.dbus.spi.message.IMessageWriter;
-import org.freedesktop.dbus.spi.message.ISocketProvider;
-import org.newsclub.net.unix.AFSocket;
-import org.newsclub.net.unix.AFSocketCapability;
-import org.newsclub.net.unix.AFUNIXSocketChannel;
+import org.freedesktop.dbus.spi.message.*;
+import org.newsclub.net.unix.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.FileDescriptor;
+import java.io.IOException;
import java.nio.channels.SocketChannel;
+import java.util.Optional;
public class JUnixSocketSocketProvider implements ISocketProvider {
- private boolean hasFileDescriptorSupport = false;
+ private static final Logger LOGGER = LoggerFactory.getLogger(JUnixSocketSocketProvider.class);
+
+ private boolean hasFileDescriptorSupport = true;
@Override
public IMessageReader createReader(SocketChannel _socket) {
@@ -18,7 +21,7 @@ public IMessageReader createReader(SocketChannel _socket) {
return null;
}
if (_socket instanceof AFUNIXSocketChannel) {
- return new JUnixSocketMessageReader((AFUNIXSocketChannel) _socket, hasFileDescriptorSupport);
+ return new JUnixSocketMessageReader((AFUNIXSocketChannel) _socket, this);
}
return null;
}
@@ -29,7 +32,7 @@ public IMessageWriter createWriter(SocketChannel _socket) {
return null;
}
if (_socket instanceof AFUNIXSocketChannel) {
- return new JUnixSocketMessageWriter((AFUNIXSocketChannel) _socket, hasFileDescriptorSupport);
+ return new JUnixSocketMessageWriter((AFUNIXSocketChannel) _socket, this);
}
return null;
}
@@ -41,6 +44,30 @@ public void setFileDescriptorSupport(boolean _support) {
@Override
public boolean isFileDescriptorPassingSupported() {
- return AFSocket.supports(AFSocketCapability.CAPABILITY_FILE_DESCRIPTORS) && AFSocket.supports(AFSocketCapability.CAPABILITY_UNSAFE);
+ return hasFileDescriptorSupport && AFSocket.supports(AFSocketCapability.CAPABILITY_FILE_DESCRIPTORS) && AFSocket.supports(AFSocketCapability.CAPABILITY_UNSAFE);
+ }
+
+ @Override
+ public Optional getFileDescriptorValue(FileDescriptor _fd) {
+ try {
+ return Optional.of(FileDescriptorCast.using(_fd).as(Integer.class));
+ } catch (IOException | ClassCastException _ex) {
+ LOGGER.error("Could not get file descriptor by using junixsocket library", _ex);
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Optional createFileDescriptor(int _fd) {
+ if (!AFSocket.supports(AFSocketCapability.CAPABILITY_UNSAFE)) {
+ LOGGER.debug("Could not create new FileDescriptor instance by using junixsocket library, as unsafe capabilities of that library is disabled.");
+ return Optional.empty();
+ }
+ try {
+ return Optional.of(FileDescriptorCast.unsafeUsing(_fd).getFileDescriptor());
+ } catch (IOException _ex) {
+ LOGGER.error("Could not create new FileDescriptor instance by using junixsocket library.", _ex);
+ return Optional.empty();
+ }
}
}