Skip to content

Commit

Permalink
Expose the low-level api
Browse files Browse the repository at this point in the history
fixes #20
  • Loading branch information
Tony Crisci committed Apr 12, 2019
1 parent 5234d19 commit 2280d4e
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 59 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,47 @@ To emit a signal, just call the method marked with the `signal` decorator and th

If you have an interface xml description, which can be gotten from the `org.freedesktop.DBus.Introspect` method on an exported interface, you can generate dbus-next JavaScript classes from the xml file with the `bin/generate-interfaces.js` utility.

## The Low-Level Interface

The low-level interface can be used to interact with messages directly. Create new messages with the `Message` class to be sent on the bus as method calls, signals, method returns, or errors. Method calls can be called with the `call()` method of the `MessageBus` to await a reply and `send()` can be use for messages that don't expect a reply.

```js
let dbus = require('dbus-next');
let Message = dbus.Message;

let bus = dbus.sessionBus();

// send a method call to list the names on the bus
let methodCall = new Message({
destination: 'org.freedesktop.DBus',
path: '/org/freedesktop/DBus',
interface: 'org.freedesktop.DBus',
member: 'ListNames'
});

let reply = await bus.call(message);
console.log('names on the bus: ', reply.body[0]);

// add a custom handler for a particular method
bus.addMethodHandler((msg) => {
if (msg.path === '/org/test/path' &&
msg.interface === 'org.test.interface'
&& msg.member === 'SomeMethod') {
// handle the method by sending a reply
let someMethodReply = Message.newMethodReturn(msg, 's', ['hello']);
bus.send(someMethodReply);
return true;
}
});

// listen to any messages that are sent to the bus
bus.on('message', (msg) => {
console.log('got a message: ', msg);
});
```

For a complete example of how to use the low-level interface to send messages, see the `dbus-next-send.js` script in the `bin` directory.

## Contributing

Contributions are welcome. Development happens on [Github](https://github.com/acrisci/node-dbus-next).
Expand Down
4 changes: 2 additions & 2 deletions bin/dbus-next-send.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ let message = new Message({
});

if (type === MESSAGE_TYPE_METHOD_CALL) {
bus._call(message)
bus.call(message)
.then((reply) => {
console.log(JSON.stringify(reply, null, 2));
process.exit(0);
Expand All @@ -138,6 +138,6 @@ if (type === MESSAGE_TYPE_METHOD_CALL) {
process.exit(1);
});
} else {
bus._send(message);
bus.send(message);
process.exit(0);
}
99 changes: 81 additions & 18 deletions lib/bus.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ let { Interface } = require('./service/interface');
* created with `dbus.sessionBus()` or `dbus.systemBus()` methods of the
* dbus-next module.
*
* The `MessageBus` is an `EventEmitter` which may receive an `error` event
* with the underlying connection error as the argument. After receiving an
* `error` event, the `MessageBus` may be disconnected.
* The `MessageBus` is an `EventEmitter` which emits the following events:
* * `error` - The underlying connection to the bus has errored. After
* receiving an `error` event, the `MessageBus` may be disconnected.
* * `connected` - The bus is connected and ready to send and receive messages.
* Before this event, messages are buffered.
* * `message` - The bus has received a message. Called with the {@link
* Message} that was received. This is part of the low-level api.
*
* @example
* const dbus = require('dbus-next');
Expand Down Expand Up @@ -103,7 +107,7 @@ class MessageBus extends EventEmitter {
}

if (!handled) {
this._send(Message.newError(msg,
this.send(Message.newError(msg,
'org.freedesktop.DBus.Error.UnknownMethod',
`Method '${msg.member}' on interface '${msg.interface || '(none)'}' does not exist`));
}
Expand All @@ -116,7 +120,7 @@ class MessageBus extends EventEmitter {
this.emit('message', msg);
handleMessage(msg);
} catch (e) {
this._send(Message.newError(msg, 'com.github.dbus_next.Error', `The DBus library encountered an error.\n${e.stack}`));
this.send(Message.newError(msg, 'com.github.dbus_next.Error', `The DBus library encountered an error.\n${e.stack}`));
}
});

Expand All @@ -132,7 +136,7 @@ class MessageBus extends EventEmitter {
member: 'Hello'
});

this._call(helloMessage)
this.call(helloMessage)
.then((msg) => {
this.name = msg.body[0];
// TODO document this signal
Expand All @@ -145,9 +149,9 @@ class MessageBus extends EventEmitter {
}

/**
* Get a `ProxyObject` on the bus for the given name and path for interacting
* Get a {@link ProxyObject} on the bus for the given name and path for interacting
* with a service as a client. The proxy object contains a list of the
* `ProxyInterface`s exported at the name and object path as well as a list
* [`ProxyInterface`s]{@link ProxyInterface} exported at the name and object path as well as a list
* of `node`s.
*
* @param name {string} - the well-known name on the bus.
Expand Down Expand Up @@ -187,7 +191,7 @@ class MessageBus extends EventEmitter {
signature: 'su',
body: [name, flags]
});
this._call(requestNameMessage)
this.call(requestNameMessage)
.then((msg) => {
let result = msg.body[0];
if (result === constants.DBUS_REQUEST_NAME_REPLY_EXISTS) {
Expand Down Expand Up @@ -215,33 +219,81 @@ class MessageBus extends EventEmitter {
this._connection.stream.end();
}

_newSerial() {
/**
* Get a new serial for this bus. These can be used to set the {@link
* Message#serial} member to send the message on this bus.
*
* @returns {int} - A new serial for this bus.
*/
newSerial() {
return this._serial++;
}

_addMethodHandler(fn) {
/**
* A function to call when a message of type {@link
* MESSAGE_TYPE_METHOD_RETURN} is received. User handlers are run before
* default handlers.
*
* @callback methodHandler
* @param {Message} msg - The message to handle.
* @returns {boolean} Return `true` if the message is handled and no further
* handlers will run.
*/

/**
* Add a user method return handler. Remove the handler with {@link
* MessageBus#removeMethodHandler}
*
* @param {methodHandler} - A function to handle a {@link Message} of type
* {@link MESSAGE_TYPE_METHOD_RETURN}. Takes the `Message` as the first
* argument. Return `true` if the method is handled and no further handlers
* will run.
*/
addMethodHandler(fn) {
this._methodHandlers.push(fn);
}

_removeMethodHandler(fn) {
/**
* Remove a user method return handler that was previously added with {@link
* MessageBus#addMethodHandler}.
*
* @param {methodHandler} - A function that was previously added as a method handler with {@link
*/
removeMethodHandler(fn) {
for (let i = 0; i < this._methodHandlers.length; ++i) {
if (this._methodHandlers[i] === fn) {
this._methodHandlers.splice(i, 1);
}
}
}

_call(msg) {
/**
* Send a {@link Message} of type {@link MESSAGE_TYPE_METHOD_CALL} to the bus
* and wait for the reply.
*
* @example
* let message = new Message({
* destination: 'org.freedesktop.DBus',
* path: '/org/freedesktop/DBus',
* interface: 'org.freedesktop.DBus',
* member: 'ListNames'
* });
* let reply = await bus.call(message);
*
* @param {Message} msg - The message to send. Must be a METHOD_CALL.
* @returns {Promise} reply - A `Promise` that resolves to the `Message`
* which is a reply to the call.
*/
call(msg) {
return new Promise((resolve, reject) => {
// TODO: if the NO_REPLY_EXPECTED flag is set, resolve immediately after sending the message.
if (!(msg instanceof Message)) {
throw new Error('The call() method takes a Message class as the first argument.');
}
if (msg.type !== constants.messageType.METHOD_CALL) {
throw new Error('Only messages of type METHOD_CALL can expect a call reply.');
}
if (msg.serial === null || msg._sent) {
msg.serial = this._newSerial();
msg.serial = this.newSerial();
}
msg._sent = true;
if (msg.flags & constants.flags.noReplyExpected) {
Expand All @@ -260,12 +312,23 @@ class MessageBus extends EventEmitter {
});
};

_send(msg) {
/**
* Send a {@link Message} on the bus that does not expect a reply.
*
* @example
* let message = Message.newSignal('/org/test/path/,
* 'org.test.interface',
* 'SomeSignal');
* bus.send(message);
*
* @param {Message} msg - The message to send.
*/
send(msg) {
if (!(msg instanceof Message)) {
throw new Error('The send() method takes a Message class as the first argument.');
}
if (msg.serial === null || msg._sent) {
msg.serial = this._newSerial();
msg.serial = this.newSerial();
}
this._connection.message(msg);
}
Expand All @@ -279,7 +342,7 @@ class MessageBus extends EventEmitter {
signature: 's',
body: [match]
});
return this._call(msg);
return this.call(msg);
};
};

Expand Down
4 changes: 2 additions & 2 deletions lib/client/proxy-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class ProxyObject {
body: []
});

this.bus._call(introspectMessage)
this.bus.call(introspectMessage)
.then((msg) => {
let xml = msg.body[0];
this._parser.parseString(xml, (err, data) => {
Expand Down Expand Up @@ -172,7 +172,7 @@ class ProxyObject {
body: args
});

this.bus._call(methodCallMessage)
this.bus.call(methodCallMessage)
.then((msg) => {
let outSignatureTree = parseSignature(outSignature);
if (outSignatureTree.length === 0) {
Expand Down
Loading

0 comments on commit 2280d4e

Please sign in to comment.