All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.yawk.dbus.client.request.ChannelRequestExecutor Maven / Gradle / Ivy

The newest version!
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package at.yawk.dbus.client.request;

import at.yawk.dbus.protocol.*;
import at.yawk.dbus.protocol.object.BasicObject;
import at.yawk.dbus.protocol.object.DbusObject;
import at.yawk.dbus.protocol.object.StringObject;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;

/**
 * @author yawkat
 */
@Slf4j
public class ChannelRequestExecutor implements RequestExecutor {
    private final DbusChannel channel;
    private final ChannelRequestStateHolder requestHolder = new ChannelRequestStateHolder<>();
    private final ListenerHolder listenerHolder = new ListenerHolder();
    private final EventThreadWatcher eventThreadWatcher = new EventThreadWatcher();

    public ChannelRequestExecutor(DbusChannel channel) {
        this.channel = channel;
        channel.closeStage().thenRun(requestHolder.createCleaner());
        channel.setMessageConsumer(new MessageConsumerImpl());
    }

    @Override
    public Response execute(Request request) throws Exception {
        return executeLaterChecked(request).get();
    }

    @Override
    public Response execute(Request request, long timeout, TimeUnit unit) throws Exception {
        return executeLaterChecked(request).get(timeout, unit);
    }

    private CompletableFuture executeLaterChecked(Request request) {
        eventThreadWatcher.checkLock();
        return executeLater(request);
    }

    @Override
    public Runnable listen(String bus, MatchRule rule, Consumer> listener) {
        String serialized = rule.serialize();
        log.trace("Adding listener {} on match rule {}", listener, serialized);
        StringObject ruleStringObject = BasicObject.createString(serialized);
        DbusMessage registrationMessage = MessageFactory.methodCall(
                "/",
                "org.freedesktop.DBus",
                "org.freedesktop.DBus",
                "AddMatch",
                ruleStringObject
        );
        Consumer listenerH = msg -> {
            MessageBody body = msg.getBody();
            eventThreadWatcher.lock();
            try {
                listener.accept(body == null ? Collections.emptyList() : body.getArguments());
            } finally {
                eventThreadWatcher.unlock();
            }
        };
        if (listenerHolder.addListener(rule, listenerH)) {
            channel.write(registrationMessage);
        }
        return () -> {
            if (listenerHolder.removeListener(rule, listenerH)) {
                DbusMessage removeRegistrationMessage = MessageFactory.methodCall(
                        "/",
                        "org.freedesktop.DBus",
                        "org.freedesktop.DBus",
                        "RemoveMatch",
                        ruleStringObject
                );
                channel.write(removeRegistrationMessage);
            }
        };
    }

    private CompletableFuture executeLater(Request request) {
        int serial = channel.createSerial();

        MessageHeader header = new MessageHeader();
        header.setMessageType(request.getType());
        header.setSerial(serial);

        header.addHeader(HeaderField.INTERFACE, request.getInterfaceName());
        header.addHeader(HeaderField.PATH, request.getObjectPath());
        header.addHeader(HeaderField.MEMBER, request.getMember());
        StringObject destination = request.getDestination();
        if (destination != null) {
            header.addHeader(HeaderField.DESTINATION, destination);
        }

        MessageBody body = new MessageBody();
        body.setArguments(request.getArguments());

        CompletableFuture future = requestHolder.registerPending(serial);

        DbusMessage message = new DbusMessage();
        message.setHeader(header);
        message.setBody(body);
        channel.write(message);
        return future;
    }

    private class MessageConsumerImpl implements MessageConsumer {
        /**
         * @return The reply serial or {@code 0} if unknown.
         */
        private int getReplySerial(MessageHeader header) {
            DbusObject serialObject = header.getHeaderFields().get(HeaderField.REPLY_SERIAL);
            return serialObject == null ? 0 : serialObject.intValue();
        }

        @Override
        public boolean requireAccept(MessageHeader header) {
            return true;
        }

        @Override
        public void accept(DbusMessage message) {
            listenerHolder.post(message);

            int serial = getReplySerial(message.getHeader());
            if (serial == 0) {
                return;
            }
            DbusObject errorNameObject = message.getHeader().getHeaderFields().get(HeaderField.ERROR_NAME);
            MessageBody body = message.getBody();
            List arguments = body == null ? Collections.emptyList() : body.getArguments();
            assert arguments != null;
            Response response = errorNameObject == null ?
                    Response.success(arguments) :
                    Response.error(errorNameObject.stringValue(), arguments);

            requestHolder.complete(serial, response);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy