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

org.freedesktop.dbus.messages.DBusSignal Maven / Gradle / Ivy

Go to download

Slightly improved version of the Java DBus library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/). Changes: - Fixed lot's of Java warnings - Fixed broken 'Gettext' feature (Exceptions on unsupported languages, usage of "_" as method name). - Renamed some classes/methods/variables to comply with Java naming scheme. - Removed usage of proprietary logger and replaced it with sl4fj. - Renamed/refactored some parts to be more 'Java' like (e.g. naming, shadowing) - Fixed problems with DbusConnection.getConnection(SESSION) when using display export (e.g. SSH X11 forward)

There is a newer version: 3.3.2
Show newest version
/*
   D-Bus Java Implementation
   Copyright (c) 2005-2006 Matthew Johnson

   This program is free software; you can redistribute it and/or modify it
   under the terms of either the GNU Lesser General Public License Version 2 or the
   Academic Free Licence Version 2.1.

   Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus.messages;

import java.lang.reflect.Constructor;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.freedesktop.dbus.DBusMatchRule;
import org.freedesktop.dbus.InternalSignal;
import org.freedesktop.dbus.Marshalling;
import org.freedesktop.dbus.annotations.DBusInterfaceName;
import org.freedesktop.dbus.annotations.DBusMemberName;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.MessageFormatException;
import org.freedesktop.dbus.interfaces.DBusInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DBusSignal extends Message {
    private static final Map>                            CLASS_CACHE       =
            new ConcurrentHashMap<>();

    private static final Map, Type[]>                            TYPE_CACHE        =
            new ConcurrentHashMap<>();

    private static final Map, Constructor> CONSTRUCTOR_CACHE =
            new ConcurrentHashMap<>();

    private static final Map                                                 SIGNAL_NAMES      =
            new ConcurrentHashMap<>();
    private static final Map                                                 INT_NAMES         =
            new ConcurrentHashMap<>();
    
    private final Logger                                                                     logger            =
            LoggerFactory.getLogger(getClass());

    private Class                                                      clazz;
    private boolean                                                                          bodydone          = false;
    private byte[]                                                                           blen;

    DBusSignal() {
    }

    public DBusSignal(String source, String path, String iface, String member, String sig, Object... args) throws DBusException {
        super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0);

        if (null == path || null == member || null == iface) {
            throw new MessageFormatException("Must specify object path, interface and signal name to Signals.");
        }
        getHeaders().put(Message.HeaderField.PATH, path);
        getHeaders().put(Message.HeaderField.MEMBER, member);
        getHeaders().put(Message.HeaderField.INTERFACE, iface);

        List hargs = new ArrayList<>();
        hargs.add(new Object[] {
                Message.HeaderField.PATH, new Object[] {
                        ArgumentType.OBJECT_PATH_STRING, path
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.INTERFACE, new Object[] {
                        ArgumentType.STRING_STRING, iface
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.MEMBER, new Object[] {
                        ArgumentType.STRING_STRING, member
                }
        });

        if (null != source) {
            getHeaders().put(Message.HeaderField.SENDER, source);
            hargs.add(new Object[] {
                    Message.HeaderField.SENDER, new Object[] {
                            ArgumentType.STRING_STRING, source
                    }
            });
        }

        if (null != sig) {
            hargs.add(new Object[] {
                    Message.HeaderField.SIGNATURE, new Object[] {
                            ArgumentType.SIGNATURE_STRING, sig
                    }
            });
            getHeaders().put(Message.HeaderField.SIGNATURE, sig);
            setArgs(args);
        }

        blen = new byte[4];
        appendBytes(blen);
        long newSerial = getSerial() + 1;
        setSerial(newSerial);
        append("ua(yv)", newSerial, hargs.toArray());
        pad((byte) 8);

        long counter = getByteCounter();
        if (null != sig) {
            append(sig, args);
        }
        marshallint(getByteCounter() - counter, blen, 0, 4);
        bodydone = true;
    }
  
    static void addInterfaceMap(String java, String dbus) {
        INT_NAMES.put(dbus, java);
    }

    static void addSignalMap(String java, String dbus) {
        SIGNAL_NAMES.put(dbus, java);
    }

    static DBusSignal createSignal(Class c, String source, String objectpath, String sig, long serial, Object... parameters) throws DBusException {
        String type = "";
        if (null != c.getEnclosingClass()) {
            if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) {
                type = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value();
            } else {
                type = AbstractConnection.DOLLAR_PATTERN.matcher(c.getEnclosingClass().getName()).replaceAll(".");
            }

        } else {
            throw new DBusException("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package.");
        }
        DBusSignal s = new InternalSignal(source, objectpath, type, c.getSimpleName(), sig, serial, parameters);
        s.clazz = c;
        return s;
    }
    
    @SuppressWarnings("unchecked")
    private static Class createSignalClass(String intname, String signame) throws DBusException {
        String name = intname + '$' + signame;
        Class c = CLASS_CACHE.get(name);
        if (null == c) {
            c = DBusMatchRule.getCachedSignalType(name);
        }
        if (null != c) {
            return c;
        }
        do {
            try {
                c = (Class) Class.forName(name);
            } catch (ClassNotFoundException exCnf) {
            }
            name = name.replaceAll("\\.([^\\.]*)$", "\\$$1");
        } while (null == c && name.matches(".*\\..*"));
        if (null == c) {
            throw new DBusException("Could not create class from signal " + intname + '.' + signame);
        }
        CLASS_CACHE.put(name, c);
        return c;
    }

    @SuppressWarnings("unchecked")
    public DBusSignal createReal(AbstractConnection conn) throws DBusException {
        String intname = INT_NAMES.get(getInterface());
        String signame = SIGNAL_NAMES.get(getName());
        if (null == intname) {
            intname = getInterface();
        }
        if (null == signame) {
            signame = getName();
        }
        if (null == clazz) {
            clazz = createSignalClass(intname, signame);
        }

        logger.debug("Converting signal to type: {}", clazz);
        Type[] types = TYPE_CACHE.get(clazz);
        Constructor con = CONSTRUCTOR_CACHE.get(clazz);
        if (null == types) {
            con = (Constructor) clazz.getDeclaredConstructors()[0];
            CONSTRUCTOR_CACHE.put(clazz, con);
            Type[] ts = con.getGenericParameterTypes();
            types = new Type[ts.length - 1];
            for (int i = 1; i < ts.length; i++) {
                if (ts[i] instanceof TypeVariable) {
                    for (Type b : ((TypeVariable) ts[i]).getBounds()) {
                        types[i - 1] = b;
                    }
                } else {
                    types[i - 1] = ts[i];
                }
            }
            TYPE_CACHE.put(clazz, types);
        }

        try {
            DBusSignal s;
            Object[] args = Marshalling.deSerializeParameters(getParameters(), types, conn);
            if (null == args) {
                s = con.newInstance(getPath());
            } else {
                Object[] params = new Object[args.length + 1];
                params[0] = getPath();
                System.arraycopy(args, 0, params, 1, args.length);

                logger.debug("Creating signal of type {} with parameters {}", clazz , Arrays.deepToString(params));
                s = con.newInstance(params);
            }
            s.getHeaders().putAll(getHeaders());
            s.setWiredata(getWireData());
            s.setByteCounter(getWireData().length);
            return s;
        } catch (Exception _ex) {
            throw new DBusException(_ex);
        }
    }

    /**
    * Create a new signal.
    * This contructor MUST be called by all sub classes.
    * @param objectpath The path to the object this is emitted from.
    * @param args The parameters of the signal.
    * @throws DBusException This is thrown if the subclass is incorrectly defined.
    */
    @SuppressWarnings("unchecked")
    protected DBusSignal(String objectpath, Object... args) throws DBusException {
        super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0);

        if (!objectpath.matches(AbstractConnection.OBJECT_REGEX)) {
            throw new DBusException("Invalid object path: " + objectpath);
        }

        Class tc = getClass();
        String member;
        if (tc.isAnnotationPresent(DBusMemberName.class)) {
            member = tc.getAnnotation(DBusMemberName.class).value();
        } else {
            member = tc.getSimpleName();
        }
        String iface = null;
        Class enc = tc.getEnclosingClass();
        if (null == enc || !DBusInterface.class.isAssignableFrom(enc) || enc.getName().equals(enc.getSimpleName())) {
            throw new DBusException("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package.");
        } else if (null != enc.getAnnotation(DBusInterfaceName.class)) {
            iface = enc.getAnnotation(DBusInterfaceName.class).value();
        } else {
            iface = AbstractConnection.DOLLAR_PATTERN.matcher(enc.getName()).replaceAll(".");
        }

        getHeaders().put(Message.HeaderField.PATH, objectpath);
        getHeaders().put(Message.HeaderField.MEMBER, member);
        getHeaders().put(Message.HeaderField.INTERFACE, iface);

        List hargs = new ArrayList<>();
        hargs.add(new Object[] {
                Message.HeaderField.PATH, new Object[] {
                        ArgumentType.OBJECT_PATH_STRING, objectpath
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.INTERFACE, new Object[] {
                        ArgumentType.STRING_STRING, iface
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.MEMBER, new Object[] {
                        ArgumentType.STRING_STRING, member
                }
        });

        String sig = null;
        if (0 < args.length) {
            try {
                Type[] types = TYPE_CACHE.get(tc);
                if (null == types) {
                    Constructor con = (Constructor) tc.getDeclaredConstructors()[0];
                    CONSTRUCTOR_CACHE.put(tc, con);
                    Type[] ts = con.getGenericParameterTypes();
                    types = new Type[ts.length - 1];
                    for (int i = 1; i <= types.length; i++) {
                        if (ts[i] instanceof TypeVariable) {
                            types[i - 1] = ((TypeVariable) ts[i]).getBounds()[0];
                        } else {
                            types[i - 1] = ts[i];
                        }
                    }
                    TYPE_CACHE.put(tc, types);
                }
                sig = Marshalling.getDBusType(types);
                hargs.add(new Object[] {
                        Message.HeaderField.SIGNATURE, new Object[] {
                                ArgumentType.SIGNATURE_STRING, sig
                        }
                });
                getHeaders().put(Message.HeaderField.SIGNATURE, sig);
                setArgs(args);
            } catch (Exception e) {
                logger.debug("", e);
                throw new DBusException("Failed to add signal parameters: " + e.getMessage());
            }
        }

        blen = new byte[4];
        appendBytes(blen);
        long newSerial = getSerial() + 1;
        setSerial(newSerial);
        append("ua(yv)", newSerial, hargs.toArray());
        pad((byte) 8);
    }

    public void appendbody(AbstractConnection conn) throws DBusException {
        if (bodydone) {
            return;
        }

        Type[] types = TYPE_CACHE.get(getClass());
        Object[] args = Marshalling.convertParameters(getParameters(), types, conn);
        setArgs(args);
        String sig = getSig();

        long counter = getByteCounter();
        if (null != args && 0 < args.length) {
            append(sig, args);
        }
        marshallint(getByteCounter() - counter, blen, 0, 4);
        bodydone = true;
    }

    @Override
    public String toString() {
        return "DBusSignal [clazz=" + clazz + "]";
    }
    
    
}