This guide explains how to use the standard D-Bus interfaces provided by the library.
The library provides two ways to implement standard D-Bus interfaces:
- Manual Implementation - Direct handling of method calls in your handler
- Annotation-Based - Use annotations and let the framework handle the details
The library provides Java interfaces for the following standard D-Bus interfaces:
-
Introspectable (
org.freedesktop.DBus.Introspectable)- Discover interfaces, methods, signals, and properties of D-Bus objects
- Single method:
introspect()returns XML description
-
Properties (
org.freedesktop.DBus.Properties)- Get, set, and monitor properties of D-Bus objects
- Methods:
getProperty(),setProperty(),getProperties() - Signal:
PropertiesChanged
-
Peer (
org.freedesktop.DBus.Peer)- Basic peer-to-peer functionality
- Methods:
ping(),getMachineId()
-
ObjectManager (
org.freedesktop.DBus.ObjectManager)- Efficiently enumerate object hierarchies
- Method:
getManagedObjects() - Signals:
InterfacesAdded,InterfacesRemoved
// Create connection
Connection connection = NettyConnection.newConnection(socketAddress, config);
connection.connect().toCompletableFuture().get();
// 1. Introspection
OutboundMethodCall introspectCall = OutboundMethodCall.Builder.create()
.withPath(DBusObjectPath.valueOf("/org/example/Object"))
.withInterface(DBusString.valueOf("org.freedesktop.DBus.Introspectable"))
.withMember(DBusString.valueOf("Introspect"))
.withDestination(DBusString.valueOf("org.example.Service"))
.withReplyExpected(true)
.build();
CompletableFuture<InboundMessage> response = connection.sendRequest(introspectCall);
// Process XML response
// 2. Get Property
OutboundMethodCall getPropertyCall = OutboundMethodCall.Builder.create()
.withPath(DBusObjectPath.valueOf("/org/example/Object"))
.withInterface(DBusString.valueOf("org.freedesktop.DBus.Properties"))
.withMember(DBusString.valueOf("Get"))
.withDestination(DBusString.valueOf("org.example.Service"))
.withBody(Arrays.asList(
DBusString.valueOf("org.example.Interface"),
DBusString.valueOf("PropertyName")
))
.withReplyExpected(true)
.build();
// 3. Ping
OutboundMethodCall pingCall = OutboundMethodCall.Builder.create()
.withPath(DBusObjectPath.valueOf("/"))
.withInterface(DBusString.valueOf("org.freedesktop.DBus.Peer"))
.withMember(DBusString.valueOf("Ping"))
.withDestination(DBusString.valueOf("org.example.Service"))
.withReplyExpected(true)
.build();./gradlew :examples:run -PmainClass=com.lucimber.dbus.examples.StandardInterfacesExampleWhile the library provides the interface definitions, you need to handle the method calls in your service:
public class StandardInterfacesHandler implements InboundHandler {
@Override
public void handleInboundMessage(PipelineContext ctx, InboundMessage msg) {
if (msg instanceof InboundMethodCall) {
InboundMethodCall call = (InboundMethodCall) msg;
String interfaceName = call.getInterfaceName()
.map(DBusString::toString)
.orElse("");
switch (interfaceName) {
case "org.freedesktop.DBus.Introspectable":
handleIntrospectable(ctx, call);
break;
case "org.freedesktop.DBus.Properties":
handleProperties(ctx, call);
break;
case "org.freedesktop.DBus.Peer":
handlePeer(ctx, call);
break;
case "org.freedesktop.DBus.ObjectManager":
handleObjectManager(ctx, call);
break;
default:
ctx.propagateInboundMessage(msg);
}
}
}
}Use annotations to define your D-Bus service and let the framework handle standard interfaces:
@DBusInterface("com.example.MyService")
public class MyService {
@DBusProperty(name = "Version")
private String version = "1.0.0";
@DBusProperty
private int count = 0;
@DBusMethod(name = "Echo")
public String echo(String message) {
count++;
return message;
}
@DBusMethod
public void reset() {
count = 0;
}
@DBusSignal(name = "CountChanged")
public void emitCountChanged(int oldValue, int newValue) {
// Signal emission handled by framework
}
}
// Register the service
MyService service = new MyService();
StandardInterfaceHandler handler = new StandardInterfaceHandler(
"/com/example/MyService", service);
connection.getPipeline().addLast("myservice", handler);The StandardInterfaceHandler automatically provides:
- Introspectable: Generates XML from annotations
- Properties: Get/Set/GetAll for
@DBusPropertyfields/methods - Peer: Standard Ping and GetMachineId implementation
- Start the manual implementation server:
./gradlew :examples:run -PmainClass=com.lucimber.dbus.examples.StandardInterfacesServer- Or start the annotation-based server:
./gradlew :examples:run -PmainClass=com.lucimber.dbus.examples.AnnotationBasedService- In another terminal, test with the client:
./gradlew :examples:run -PmainClass=com.lucimber.dbus.examples.StandardInterfacesExample- Or use D-Bus tools:
# Introspect
dbus-send --session --print-reply --dest=com.example.StandardInterfacesDemo \
/com/example/Demo org.freedesktop.DBus.Introspectable.Introspect
# Get property
dbus-send --session --print-reply --dest=com.example.StandardInterfacesDemo \
/com/example/Demo org.freedesktop.DBus.Properties.Get \
string:"com.example.Demo" string:"Version"
# Ping
dbus-send --session --print-reply --dest=com.example.StandardInterfacesDemo \
/ org.freedesktop.DBus.Peer.Ping
# Get managed objects
dbus-send --session --print-reply --dest=com.example.StandardInterfacesDemo \
/com/example/Demo org.freedesktop.DBus.ObjectManager.GetManagedObjects- Always implement Introspectable - It helps clients discover your service's capabilities
- Use Properties for configuration - It provides a standard way to get/set values
- Implement Peer.Ping for health checks - Clients can verify your service is responsive
- Use ObjectManager for complex hierarchies - More efficient than multiple introspection calls
// Send PropertiesChanged signal when a property changes
OutboundSignal signal = OutboundSignal.Builder.create()
.withPath(DBusObjectPath.valueOf("/com/example/Object"))
.withInterface(DBusString.valueOf("org.freedesktop.DBus.Properties"))
.withMember(DBusString.valueOf("PropertiesChanged"))
.withBody(Arrays.asList(
DBusString.valueOf("com.example.Interface"),
changedProperties, // Dict of changed properties
invalidatedProperties // Array of invalidated property names
))
.build();// List all services
OutboundMethodCall listNames = OutboundMethodCall.Builder.create()
.withPath(DBusObjectPath.valueOf("/org/freedesktop/DBus"))
.withInterface(DBusString.valueOf("org.freedesktop.DBus"))
.withMember(DBusString.valueOf("ListNames"))
.withDestination(DBusString.valueOf("org.freedesktop.DBus"))
.withReplyExpected(true)
.build();When working with standard interfaces, use these D-Bus type mappings:
| D-Bus Type | Java Type | Example |
|---|---|---|
s (string) |
DBusString |
Method names, property names |
v (variant) |
DBusVariant |
Property values |
o (object path) |
DBusObjectPath |
Object paths |
a{sv} |
DBusDict<DBusString, DBusVariant> |
Property dictionaries |
as |
DBusArray<DBusString> |
String arrays |
Standard D-Bus errors to handle:
org.freedesktop.DBus.Error.UnknownInterface- Interface not foundorg.freedesktop.DBus.Error.UnknownMethod- Method not foundorg.freedesktop.DBus.Error.UnknownProperty- Property not foundorg.freedesktop.DBus.Error.PropertyReadOnly- Attempt to set read-only propertyorg.freedesktop.DBus.Error.InvalidArgs- Invalid method arguments
The standard interfaces work alongside your custom interfaces:
<!-- Introspection XML includes both standard and custom interfaces -->
<node>
<interface name="org.freedesktop.DBus.Properties">
<!-- Standard properties interface -->
</interface>
<interface name="com.example.MyCustomInterface">
<!-- Your custom interface -->
</interface>
</node>This allows clients to use standard tools and libraries to interact with your service while still providing custom functionality.