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

org.freedesktop.dbus.connections.AbstractConnection Maven / Gradle / Ivy

Go to download

Improved version of the DBus-Java library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/). This is the OSGi compliant bundle of all required libraries in one bundle.

The newest version!
package org.freedesktop.dbus.connections;

import org.freedesktop.dbus.DBusAsyncReply;
import org.freedesktop.dbus.DBusMatchRule;
import org.freedesktop.dbus.RemoteInvocationHandler;
import org.freedesktop.dbus.RemoteObject;
import org.freedesktop.dbus.connections.base.ConnectionMessageHandler;
import org.freedesktop.dbus.connections.base.IncomingMessageThread;
import org.freedesktop.dbus.connections.config.ReceivingServiceConfig;
import org.freedesktop.dbus.connections.config.TransportConfig;
import org.freedesktop.dbus.connections.impl.ConnectionConfig;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.InvalidSignalException;
import org.freedesktop.dbus.interfaces.CallbackHandler;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.interfaces.DBusSigHandler;
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.messages.ExportedObject;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.utils.DBusObjects;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Pattern;

/**
 * Handles a connection to DBus.
 */
public abstract non-sealed class AbstractConnection extends ConnectionMessageHandler {

    public static final boolean      FLOAT_SUPPORT          = null != System.getenv("DBUS_JAVA_FLOATS");
    public static final Pattern      DOLLAR_PATTERN         = Pattern.compile("[$]");

    public static final int          MAX_ARRAY_LENGTH       = 67108864;
    public static final int          MAX_NAME_LENGTH        = 255;

    protected AbstractConnection(ConnectionConfig _conCfg, TransportConfig _transportConfig, ReceivingServiceConfig _rsCfg) throws DBusException {
        super(_conCfg, _transportConfig, _rsCfg);
    }

    @Override
    protected IncomingMessageThread createReaderThread(BusAddress _busAddress) {
        return new IncomingMessageThread(this, _busAddress);
    }

    /**
     * Remove a match rule with the given {@link DBusSigHandler}.
     * The rule will only be removed from DBus if no other additional handlers are registered to the same rule.
     *
     * @param _rule rule to remove
     * @param _handler handler to remove
     *
     * @param  signal type
     *
     * @throws DBusException on error
     */
    protected abstract  void removeSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException;

    /**
     * Add a signal handler with the given {@link DBusMatchRule} to DBus.
     * The rule will be added to DBus if it was not added before.
     * If the rule was already added, the signal handler is added to the internal map receiving
     * the same signal as the first (and additional) handlers for this rule.
     *
     * @param _rule rule to add
     * @param _handler handler to use
     *
     * @param  signal type
     * @return closeable that removes signal handler
     *
     * @throws DBusException on error
     */
    protected abstract  AutoCloseable addSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException;

    /**
     * Remove a generic signal handler with the given {@link DBusMatchRule}.
     * The rule will only be removed from DBus if no other additional handlers are registered to the same rule.
     *
     * @param _rule rule to remove
     * @param _handler handler to remove
     * @throws DBusException on error
     */
    protected abstract void removeGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException;

    /**
     * Adds a {@link DBusMatchRule} to with a generic signal handler.
     * Generic signal handlers allow receiving different signals with the same handler.
     * If the rule was already added, the signal handler is added to the internal map receiving
     * the same signal as the first (and additional) handlers for this rule.
     *
     * @param _rule rule to add
     * @param _handler handler to use
     * @return closeable that removes signal handler
     * @throws DBusException on error
     */
    protected abstract AutoCloseable addGenericSigHandler(DBusMatchRule _rule, DBusSigHandler _handler) throws DBusException;

    /**
     * If given type is null, will try to find suitable types by examining the given ifaces.
     * If a non-null type is given, returns the given type.
     *
     * @param  any DBusInterface compatible object
     * @param _type type or null
     * @param _ifaces interfaces to examining when type is null
     *
     * @return List
     */
    protected  List> findMatchingTypes(Class _type, List _ifaces) {
        List> ifcs = new ArrayList<>();
        if (_type == null) {
            for (String iface : _ifaces) {

                getLogger().debug("Trying interface {}", iface);
                int j = 0;
                while (j >= 0) {
                    try {
                        Class ifclass = Class.forName(iface);
                        if (!ifcs.contains(ifclass)) {
                            ifcs.add(ifclass);
                        }
                        break;
                    } catch (Exception _ex) {
                        getLogger().trace("No class found for {}", iface, _ex);
                    }
                    j = iface.lastIndexOf('.');
                    char[] cs = iface.toCharArray();
                    if (j >= 0) {
                        cs[j] = '$';
                        iface = String.valueOf(cs);
                    }
                }
            }
        } else {
            ifcs.add(_type);
        }
        return ifcs;
    }

    /**
     * If set to true the bus will not hold a strong reference to exported objects. If they go out of scope they will
     * automatically be unexported from the bus. The default is to hold a strong reference, which means objects must be
     * explicitly unexported before they will be garbage collected.
     *
     * @param _weakreferences reference
     * @deprecated should be set during construction time (using the builder), will be removed in future
     */
    @Deprecated(since = "5.1.0 - 2024-07-12", forRemoval = true)
    public void setWeakReferences(boolean _weakreferences) {
        getConnectionConfig().setExportWeakReferences(_weakreferences);
    }

    /**
     * Export an object so that its methods can be called on DBus.
     *
     * @param _objectPath
     *            The path to the object we are exposing. MUST be in slash-notation, like "/org/freedesktop/Local", and
     *            SHOULD end with a capitalised term. Only one object may be exposed on each path at any one time, but
     *            an object may be exposed on several paths at once.
     * @param _object
     *            The object to export.
     * @throws DBusException
     *             If the objectpath is already exporting an object. or if objectpath is incorrectly formatted,
     */
    public void exportObject(String _objectPath, DBusInterface _object) throws DBusException {

        DBusObjects.requireObjectPath(_objectPath, "Must Specify an Object Path");
        DBusObjects.ensurePublicInterfaces(_object);

        doWithExportedObjects(DBusException.class, eos -> {
            if (null != eos.get(_objectPath)) {
                throw new DBusException("Object already exported");
            }
            ExportedObject eo = new ExportedObject(_object, getConnectionConfig().isExportWeakReferences());
            eos.put(_objectPath, eo);
            synchronized (getObjectTree()) {
                getObjectTree().add(_objectPath, eo, eo.getIntrospectiondata());
            }
        });
    }

    /**
     * Export an object so that its methods can be called on DBus. The path to the object will be taken from the
     * {@link DBusInterface#getObjectPath()} method, make sure it is implemented and returns immutable value.
     * If you want export object with multiple paths, please use {@link AbstractConnection#exportObject(String, DBusInterface)}.
     *
     * @param _object
     *            The object to export.
     * @throws DBusException
     *             If the object path is already exporting an object or if object path is incorrectly formatted.
     */
    public void exportObject(DBusInterface _object) throws DBusException {
        Objects.requireNonNull(_object, "object must not be null");
        exportObject(_object.getObjectPath(), _object);
    }

    /**
     * Export an object as a fallback object. This object will have it's methods invoked for all paths starting with
     * this object path.
     *
     * @param _objectPrefix
     *            The path below which the fallback handles calls. MUST be in slash-notation, like
     *            "/org/freedesktop/Local",
     * @param _object
     *            The object to export.
     * @throws DBusException
     *             If the objectpath is incorrectly formatted,
     */
    public void addFallback(String _objectPrefix, DBusInterface _object) throws DBusException {
        DBusObjects.requireObjectPath(_objectPrefix);
        ExportedObject eo = new ExportedObject(_object, getConnectionConfig().isExportWeakReferences());
        getFallbackContainer().add(_objectPrefix, eo);
    }

    /**
     * Remove a fallback
     *
     * @param _objectprefix
     *            The prefix to remove the fallback for.
     */
    public void removeFallback(String _objectprefix) {
        getFallbackContainer().remove(_objectprefix);
    }

    /**
     * Remove a Signal Handler. Stops listening for this signal.
     *
     * @param 
     *            class extending {@link DBusSignal}
     * @param _type
     *            The signal to watch for.
     * @param _handler
     *            the handler
     * @throws DBusException
     *             If listening for the signal on the bus failed.
     * @throws ClassCastException
     *             If type is not a sub-type of DBusSignal.
     */
    public  void removeSigHandler(Class _type, DBusSigHandler _handler) throws DBusException {
        assertSignal(_type);
        removeSigHandler(new DBusMatchRule(_type), _handler);
    }

    /**
     * Remove a Signal Handler. Stops listening for this signal.
     *
     * @param 
     *            class extending {@link DBusSignal}
     * @param _type
     *            The signal to watch for.
     * @param _object
     *            The object emitting the signal.
     * @param _handler
     *            the handler
     * @throws DBusException
     *             If listening for the signal on the bus failed.
     * @throws ClassCastException
     *             If type is not a sub-type of DBusSignal.
     */
    public  void removeSigHandler(Class _type, DBusInterface _object, DBusSigHandler _handler)
            throws DBusException {
        assertSignal(_type);
        String objectPath = getImportedObjects().get(_object).getObjectPath();
        DBusObjects.requireObjectPath(objectPath);
        removeSigHandler(new DBusMatchRule(_type, null, objectPath), _handler);
    }

    /**
     * Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type
     * and name.
     *
     * @param 
     *            class extending {@link DBusSignal}
     * @param _type
     *            The signal to watch for.
     * @param _handler
     *            The handler to call when a signal is received.
     * @return closeable that removes signal handler
     * @throws DBusException
     *             If listening for the signal on the bus failed.
     * @throws ClassCastException
     *             If type is not a sub-type of DBusSignal.
     */
    public  AutoCloseable addSigHandler(Class _type, DBusSigHandler _handler) throws DBusException {
        assertSignal(_type);
        return addSigHandler(new DBusMatchRule(_type), _handler);
    }

    /**
     * Add a Signal Handler. Adds a signal handler to call when a signal is received which matches the specified type,
     * name and object.
     *
     * @param 
     *            class extending {@link DBusSignal}
     * @param _type
     *            The signal to watch for.
     * @param _object
     *            The object from which the signal will be emitted
     * @param _handler
     *            The handler to call when a signal is received.
     * @return closeable that removes signal handler
     * @throws DBusException
     *             If listening for the signal on the bus failed.
     * @throws ClassCastException
     *             If type is not a sub-type of DBusSignal.
     */
    public  AutoCloseable addSigHandler(Class _type, DBusInterface _object, DBusSigHandler _handler)
            throws DBusException {
        assertSignal(_type);
        RemoteObject rObj = getImportedObjects().get(_object);
        if (rObj == null) {
            throw new DBusException("Not an object exported or imported by this connection");
        }
        String objectPath = rObj.getObjectPath();
        DBusObjects.requireObjectPath(objectPath);
        return addSigHandler(new DBusMatchRule(_type, null, objectPath), _handler);
    }

    private  void assertSignal(Class _type) throws InvalidSignalException {
        if (!DBusSignal.class.isAssignableFrom(_type)) {
            throw new InvalidSignalException(_type);
        }
    }

    protected  void addSigHandlerWithoutMatch(Class _signal, DBusSigHandler _handler) throws DBusException {
        DBusMatchRule rule = new DBusMatchRule(_signal);
        synchronized (getHandledSignals()) {
            Queue> v = getHandledSignals().get(rule);
            if (null == v) {
                v = new ConcurrentLinkedQueue<>();
                v.add(_handler);
                getHandledSignals().put(rule, v);
            } else {
                v.add(_handler);
            }
        }
    }

    /**
     * Call a method asynchronously and set a callback. This handler will be called in a separate thread.
     *
     * @param 
     *            whatever
     * @param _object
     *            The remote object on which to call the method.
     * @param _m
     *            The name of the method on the interface to call.
     * @param _callback
     *            The callback handler.
     * @param _parameters
     *            The parameters to call the method with.
     */
    public  void callWithCallback(DBusInterface _object, String _m, CallbackHandler _callback,
            Object... _parameters) {
        getLogger().trace("callWithCallback({}, {}, {})", _object, _m, _callback);
        Class[] types = createTypesArray(_parameters);
        RemoteObject ro = getImportedObjects().get(_object);

        try {
            Method me;
            if (null == ro.getInterface()) {
                me = _object.getClass().getMethod(_m, types);
            } else {
                me = ro.getInterface().getMethod(_m, types);
            }
            RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_CALLBACK,
                    _callback, _parameters);
        } catch (DBusExecutionException _ex) {
            getLogger().debug("Error calling callback", _ex);
            throw _ex;
        } catch (Exception _ex) {
            getLogger().debug("Failed to call callback", _ex);
            throw new DBusExecutionException(_ex.getMessage(), _ex);
        }
    }

    /**
     * Call a method asynchronously and get a handle with which to get the reply.
     *
     * @param _object
     *            The remote object on which to call the method.
     * @param _method
     *            The name of the method on the interface to call.
     * @param _parameters
     *            The parameters to call the method with.
     * @return A handle to the call.
     */
    public DBusAsyncReply callMethodAsync(DBusInterface _object, String _method, Object... _parameters) {
        Class[] types = createTypesArray(_parameters);
        RemoteObject ro = getImportedObjects().get(_object);

        try {
            Method me;
            if (null == ro.getInterface()) {
                me = _object.getClass().getMethod(_method, types);
            } else {
                me = ro.getInterface().getMethod(_method, types);
            }
            return (DBusAsyncReply) RemoteInvocationHandler.executeRemoteMethod(ro, me, this,
                    RemoteInvocationHandler.CALL_TYPE_ASYNC, null, _parameters);
        } catch (DBusExecutionException _ex) {
            getLogger().debug("Error calling async method", _ex);
            throw _ex;
        } catch (Exception _ex) {
            getLogger().debug("Failed to execute async method", _ex);
            throw new DBusExecutionException(_ex.getMessage(), _ex);
        }
    }

    private static Class[] createTypesArray(Object... _parameters) {
        if (_parameters == null) {
            return null;
        }
        return Arrays.stream(_parameters)
                .filter(Objects::nonNull) // do no try to convert null values to concrete class
                .map(p -> {
                    if (List.class.isAssignableFrom(p.getClass())) { // turn possible List subclasses (e.g. ArrayList) to interface class List
                        return List.class;
                    } else if (Map.class.isAssignableFrom(p.getClass())) { // do the same for Map subclasses
                        return Map.class;
                    } else if (Set.class.isAssignableFrom(p.getClass())) { // and also for Set subclasses
                        return Set.class;
                    } else {
                        return p.getClass();
                    }
                })
                .toArray(Class[]::new);
    }

    public void queueCallback(MethodCall _call, Method _method, CallbackHandler _callback) {
        getCallbackManager().queueCallback(_call, _method, _callback, this);
    }

    public boolean isFileDescriptorSupported() {
        return getTransport().isFileDescriptorSupported();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy