/* 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; import static org.freedesktop.dbus.Gettext._; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.text.ParseException; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Properties; import java.util.Vector; import java.util.regex.Pattern; import org.freedesktop.DBus; import org.freedesktop.dbus.exceptions.NotConnected; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.exceptions.FatalDBusException; import org.freedesktop.dbus.exceptions.FatalException; import cx.ath.matthew.debug.Debug; /** Handles a connection to DBus. */ public abstract class AbstractConnection { protected class FallbackContainer { private Map<String[], ExportedObject> fallbacks = new HashMap<String[], ExportedObject>(); public synchronized void add(String path, ExportedObject eo) { if (Debug.debug) Debug.print(Debug.DEBUG, "Adding fallback on "+path+" of "+eo); fallbacks.put(path.split("/"), eo); } public synchronized void remove(String path) { if (Debug.debug) Debug.print(Debug.DEBUG, "Removing fallback on "+path); fallbacks.remove(path.split("/")); } public synchronized ExportedObject get(String path) { int best = 0; int i = 0; ExportedObject bestobject = null; String[] pathel = path.split("/"); for (String[] fbpath: fallbacks.keySet()) { if (Debug.debug) Debug.print(Debug.VERBOSE, "Trying fallback path "+Arrays.deepToString(fbpath)+" to match "+Arrays.deepToString(pathel)); for (i = 0; i < pathel.length && i < fbpath.length; i++) if (!pathel[i].equals(fbpath[i])) break; if (i > 0 && i == fbpath.length && i > best) bestobject = fallbacks.get(fbpath); if (Debug.debug) Debug.print(Debug.VERBOSE, "Matches "+i+" bestobject now "+bestobject); } if (Debug.debug) Debug.print(Debug.DEBUG, "Found fallback for "+path+" of "+bestobject); return bestobject; } } protected class _thread extends Thread { public _thread() { setName("DBusConnection"); } public void run() { try { Message m = null; while (_run) { m = null; // read from the wire try { // this blocks on outgoing being non-empty or a message being available. m = readIncoming(); if (m != null) { if (Debug.debug) Debug.print(Debug.VERBOSE, "Got Incoming Message: "+m); synchronized (this) { notifyAll(); } if (m instanceof DBusSignal) handleMessage((DBusSignal) m); else if (m instanceof MethodCall) handleMessage((MethodCall) m); else if (m instanceof MethodReturn) handleMessage((MethodReturn) m); else if (m instanceof Error) handleMessage((Error) m); m = null; } } catch (Exception e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); if (e instanceof FatalException) { try { handleMessage(new org.freedesktop.DBus.Local.Disconnected("/")); } catch (Exception ee) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ee); } disconnect(); } } } synchronized (this) { notifyAll(); } } catch (Exception e) { if (Debug.debug && EXCEPTION_DEBUG) Debug.print(Debug.ERR, e); } } } private class _globalhandler implements org.freedesktop.DBus.Peer, org.freedesktop.DBus.Introspectable { private String objectpath; public _globalhandler() { this.objectpath = null; } public _globalhandler(String objectpath) { this.objectpath = objectpath; } public boolean isRemote() { return false; } public void Ping() { return; } public String Introspect() { String intro = objectTree.Introspect(objectpath); if (null == intro) { ExportedObject eo = fallbackcontainer.get(objectpath); if (null != eo) intro = eo.introspectiondata; } if (null == intro) throw new DBus.Error.UnknownObject("Introspecting on non-existant object"); else return "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"+intro; } } protected class _workerthread extends Thread { private boolean _run = true; public void halt() { _run = false; } public void run() { while (_run) { Runnable r = null; synchronized (runnables) { while (runnables.size() == 0 && _run) try { runnables.wait(); } catch (InterruptedException Ie) {} if (runnables.size() > 0) r = runnables.removeFirst(); } if (null != r) r.run(); } } } private class _sender extends Thread { public _sender() { setName("Sender"); } public void run() { Message m = null; if (Debug.debug) Debug.print(Debug.INFO, "Monitoring outbound queue"); // block on the outbound queue and send from it while (_run) { if (null != outgoing) synchronized (outgoing) { if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking"); while (outgoing.size() == 0 && _run) try { outgoing.wait(); } catch (InterruptedException Ie) {} if (Debug.debug) Debug.print(Debug.VERBOSE, "Notified"); if (outgoing.size() > 0) m = outgoing.remove(); if (Debug.debug) Debug.print(Debug.DEBUG, "Got message: "+m); } if (null != m) sendMessage(m); m = null; } if (Debug.debug) Debug.print(Debug.INFO, "Flushing outbound queue and quitting"); // flush the outbound queue before disconnect. if (null != outgoing) do { synchronized (outgoing) { if (!outgoing.isEmpty()) m = outgoing.remove(); else m = null; } sendMessage(m); } while (null != m); // close the underlying streams } } /** * Timeout in us on checking the BUS for incoming messages and sending outgoing messages */ protected static final int TIMEOUT = 100000; /** Initial size of the pending calls map */ private static final int PENDING_MAP_INITIAL_SIZE = 10; static final String BUSNAME_REGEX = "^[-_a-zA-Z][-_a-zA-Z0-9]*(\\.[-_a-zA-Z][-_a-zA-Z0-9]*)*$"; static final String CONNID_REGEX = "^:[0-9]*\\.[0-9]*$"; static final String OBJECT_REGEX = "^/([-_a-zA-Z0-9]+(/[-_a-zA-Z0-9]+)*)?$"; static final byte THREADCOUNT = 4; static final int MAX_ARRAY_LENGTH = 67108864; static final int MAX_NAME_LENGTH = 255; protected Map<String,ExportedObject> exportedObjects; private ObjectTree objectTree; private _globalhandler _globalhandlerreference; protected Map<DBusInterface,RemoteObject> importedObjects; protected Map<SignalTuple,Vector<DBusSigHandler<? extends DBusSignal>>> handledSignals; protected EfficientMap pendingCalls; protected Map<MethodCall, CallbackHandler<? extends Object>> pendingCallbacks; protected Map<MethodCall, DBusAsyncReply<? extends Object>> pendingCallbackReplys; protected LinkedList<Runnable> runnables; protected LinkedList<_workerthread> workers; protected FallbackContainer fallbackcontainer; protected boolean _run; EfficientQueue outgoing; LinkedList<Error> pendingErrors; private static final Map<Thread,DBusCallInfo> infomap = new HashMap<Thread,DBusCallInfo>(); protected _thread thread; protected _sender sender; protected Transport transport; protected String addr; protected boolean weakreferences = false; static final Pattern dollar_pattern = Pattern.compile("[$]"); public static final boolean EXCEPTION_DEBUG; static final boolean FLOAT_SUPPORT; static { FLOAT_SUPPORT = (null != System.getenv("DBUS_JAVA_FLOATS")); EXCEPTION_DEBUG = (null != System.getenv("DBUS_JAVA_EXCEPTION_DEBUG")); if (EXCEPTION_DEBUG) { Debug.print("Debugging of internal exceptions enabled"); Debug.setThrowableTraces(true); } if (Debug.debug) { File f = new File("debug.conf"); if (f.exists()) { Debug.print("Loading debug config file: "+f); try { Debug.loadConfig(f); } catch (IOException IOe) {} } else { Properties p = new Properties(); p.setProperty("ALL", "INFO"); Debug.print("debug config file "+f+" does not exist, not loading."); } Debug.setHexDump(true); } } protected AbstractConnection(String address) throws DBusException { exportedObjects = new HashMap<String,ExportedObject>(); importedObjects = new HashMap<DBusInterface,RemoteObject>(); _globalhandlerreference = new _globalhandler(); synchronized (exportedObjects) { exportedObjects.put(null, new ExportedObject(_globalhandlerreference, weakreferences)); } handledSignals = new HashMap<SignalTuple,Vector<DBusSigHandler<? extends DBusSignal>>>(); pendingCalls = new EfficientMap(PENDING_MAP_INITIAL_SIZE); outgoing = new EfficientQueue(PENDING_MAP_INITIAL_SIZE); pendingCallbacks = new HashMap<MethodCall, CallbackHandler<? extends Object>>(); pendingCallbackReplys = new HashMap<MethodCall, DBusAsyncReply<? extends Object>>(); pendingErrors = new LinkedList<Error>(); runnables = new LinkedList<Runnable>(); workers = new LinkedList<_workerthread>(); objectTree = new ObjectTree(); fallbackcontainer = new FallbackContainer(); synchronized (workers) { for (int i = 0; i < THREADCOUNT; i++) { _workerthread t = new _workerthread(); t.start(); workers.add(t); } } _run = true; addr = address; } protected void listen() { // start listening thread = new _thread(); thread.start(); sender = new _sender(); sender.start(); } /** * Change the number of worker threads to receive method calls and handle signals. * Default is 4 threads * @param newcount The new number of worker Threads to use. */ public void changeThreadCount(byte newcount) { synchronized (workers) { if (workers.size() > newcount) { int n = workers.size() - newcount; for (int i = 0; i < n; i++) { _workerthread t = workers.removeFirst(); t.halt(); } } else if (workers.size() < newcount) { int n = newcount-workers.size(); for (int i = 0; i < n; i++) { _workerthread t = new _workerthread(); t.start(); workers.add(t); } } } } private void addRunnable(Runnable r) { synchronized(runnables) { runnables.add(r); runnables.notifyAll(); } } String getExportedObject(DBusInterface i) throws DBusException { synchronized (exportedObjects) { for (String s: exportedObjects.keySet()) if (i.equals(exportedObjects.get(s).object.get())) return s; } String s = importedObjects.get(i).objectpath; if (null != s) return s; throw new DBusException("Not an object exported or imported by this connection"); } abstract DBusInterface getExportedObject(String source, String path) throws DBusException; /** * Returns a structure with information on the current method call. * @return the DBusCallInfo for this method call, or null if we are not in a method call. */ public static DBusCallInfo getCallInfo() { DBusCallInfo info; synchronized (infomap) { info = infomap.get(Thread.currentThread()); } return info; } /** * 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. */ public void setWeakReferences(boolean weakreferences) { this.weakreferences = 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 { if (null == objectpath || "".equals(objectpath)) throw new DBusException(_("Must Specify an Object Path")); if (!objectpath.matches(OBJECT_REGEX)||objectpath.length() > MAX_NAME_LENGTH) throw new DBusException(_("Invalid object path: ")+objectpath); synchronized (exportedObjects) { if (null != exportedObjects.get(objectpath)) throw new DBusException(_("Object already exported")); ExportedObject eo = new ExportedObject(object, weakreferences); exportedObjects.put(objectpath, eo); objectTree.add(objectpath, eo, eo.introspectiondata); } } /** * 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 { if (null == objectprefix || "".equals(objectprefix)) throw new DBusException(_("Must Specify an Object Path")); if (!objectprefix.matches(OBJECT_REGEX)||objectprefix.length() > MAX_NAME_LENGTH) throw new DBusException(_("Invalid object path: ")+objectprefix); ExportedObject eo = new ExportedObject(object, weakreferences); fallbackcontainer.add(objectprefix, eo); } /** * Remove a fallback * @param objectprefix The prefix to remove the fallback for. */ public void removeFallback(String objectprefix) { fallbackcontainer.remove(objectprefix); } /** * Stop Exporting an object * @param objectpath The objectpath to stop exporting. */ public void unExportObject(String objectpath) { synchronized (exportedObjects) { exportedObjects.remove(objectpath); objectTree.remove(objectpath); } } /** * Return a reference to a remote object. * This method will resolve the well known name (if given) to a unique bus name when you call it. * This means that if a well known name is released by one process and acquired by another calls to * objects gained from this method will continue to operate on the original process. * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") * or may be a DBus address such as ":1-16". * @param objectpath The path on which the process is exporting the object.$ * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures * as the interface the remote object is exporting. * @return A reference to a remote object. * @throws ClassCastException If type is not a sub-type of DBusInterface * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. */ /** * Send a signal. * @param signal The signal to send. */ public void sendSignal(DBusSignal signal) { queueOutgoing(signal); } void queueOutgoing(Message m) { if (null == outgoing) return; synchronized (outgoing) { outgoing.add(m); if (Debug.debug) Debug.print(Debug.DEBUG, "Notifying outgoing thread"); outgoing.notifyAll(); } } /** * Remove a Signal Handler. * Stops listening for this signal. * @param type The signal to watch for. * @throws DBusException If listening for the signal on the bus failed. * @throws ClassCastException If type is not a sub-type of DBusSignal. */ public <T extends DBusSignal> void removeSigHandler(Class<T> type, DBusSigHandler<T> handler) throws DBusException { if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(_("Not A DBus Signal")); removeSigHandler(new DBusMatchRule(type), handler); } /** * Remove a Signal Handler. * Stops listening for this signal. * @param type The signal to watch for. * @param object The object emitting the signal. * @throws DBusException If listening for the signal on the bus failed. * @throws ClassCastException If type is not a sub-type of DBusSignal. */ public <T extends DBusSignal> void removeSigHandler(Class<T> type, DBusInterface object, DBusSigHandler<T> handler) throws DBusException { if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(_("Not A DBus Signal")); String objectpath = importedObjects.get(object).objectpath; if (!objectpath.matches(OBJECT_REGEX)||objectpath.length() > MAX_NAME_LENGTH) throw new DBusException(_("Invalid object path: ")+objectpath); removeSigHandler(new DBusMatchRule(type, null, objectpath), handler); } protected abstract <T extends DBusSignal> void removeSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException; /** * Add a Signal Handler. * Adds a signal handler to call when a signal is received which matches the specified type and name. * @param type The signal to watch for. * @param handler The handler to call when a signal is received. * @throws DBusException If listening for the signal on the bus failed. * @throws ClassCastException If type is not a sub-type of DBusSignal. */ @SuppressWarnings("unchecked") public <T extends DBusSignal> void addSigHandler(Class<T> type, DBusSigHandler<T> handler) throws DBusException { if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(_("Not A DBus Signal")); addSigHandler(new DBusMatchRule(type), (DBusSigHandler<? extends DBusSignal>) 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 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. * @throws DBusException If listening for the signal on the bus failed. * @throws ClassCastException If type is not a sub-type of DBusSignal. */ @SuppressWarnings("unchecked") public <T extends DBusSignal> void addSigHandler(Class<T> type, DBusInterface object, DBusSigHandler<T> handler) throws DBusException { if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(_("Not A DBus Signal")); String objectpath = importedObjects.get(object).objectpath; if (!objectpath.matches(OBJECT_REGEX)||objectpath.length() > MAX_NAME_LENGTH) throw new DBusException(_("Invalid object path: ")+objectpath); addSigHandler(new DBusMatchRule(type, null, objectpath), (DBusSigHandler<? extends DBusSignal>) handler); } protected abstract <T extends DBusSignal> void addSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException; protected <T extends DBusSignal> void addSigHandlerWithoutMatch(Class<? extends DBusSignal> signal, DBusSigHandler<T> handler) throws DBusException { DBusMatchRule rule = new DBusMatchRule(signal); SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); synchronized (handledSignals) { Vector<DBusSigHandler<? extends DBusSignal>> v = handledSignals.get(key); if (null == v) { v = new Vector<DBusSigHandler<? extends DBusSignal>>(); v.add(handler); handledSignals.put(key, v); } else v.add(handler); } } /** * Disconnect from the Bus. */ public void disconnect() { if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Abstract Connection"); // run all pending tasks. while (runnables.size() > 0) synchronized (runnables) { runnables.notifyAll(); } // stop the main thread _run = false; // unblock the sending thread. synchronized (outgoing) { outgoing.notifyAll(); } // disconnect from the trasport layer try { if (null != transport) transport.disconnect(); } catch (IOException IOe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); } // stop all the workers synchronized(workers) { for (_workerthread t: workers) t.halt(); } // make sure none are blocking on the runnables queue still synchronized (runnables) { runnables.notifyAll(); } } public void finalize() { disconnect(); } /** * Return any DBus error which has been received. * @return A DBusExecutionException, or null if no error is pending. */ public DBusExecutionException getError() { synchronized (pendingErrors) { if (pendingErrors.size() == 0) return null; else return pendingErrors.removeFirst().getException(); } } /** * Call a method asynchronously and set a callback. * This handler will be called in a separate thread. * @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. */ @SuppressWarnings("unchecked") public <A> void callWithCallback(DBusInterface object, String m, CallbackHandler<A> callback, Object... parameters) { if (Debug.debug) Debug.print(Debug.VERBOSE, "callWithCallback("+object+","+m+", "+callback); Class[] types = new Class[parameters.length]; for (int i = 0; i < parameters.length; i++) types[i] = parameters[i].getClass(); RemoteObject ro = importedObjects.get(object); try { Method me; if (null == ro.iface) me = object.getClass().getMethod(m, types); else me = ro.iface.getMethod(m, types); RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_CALLBACK, callback, parameters); } catch (DBusExecutionException DBEe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); throw DBEe; } catch (Exception e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusExecutionException(e.getMessage()); } } /** * 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 m 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. */ @SuppressWarnings("unchecked") public DBusAsyncReply callMethodAsync(DBusInterface object, String m, Object... parameters) { Class<?>[] types = new Class[parameters.length]; for (int i = 0; i < parameters.length; i++) types[i] = parameters[i].getClass(); RemoteObject ro = importedObjects.get(object); try { Method me; if (null == ro.iface) me = object.getClass().getMethod(m, types); else me = ro.iface.getMethod(m, types); return (DBusAsyncReply) RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_ASYNC, null, parameters); } catch (DBusExecutionException DBEe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); throw DBEe; } catch (Exception e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusExecutionException(e.getMessage()); } } private void handleMessage(final MethodCall m) throws DBusException { if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming method call: "+m); ExportedObject eo = null; Method meth = null; Object o = null; if (null == m.getInterface() || m.getInterface().equals("org.freedesktop.DBus.Peer") || m.getInterface().equals("org.freedesktop.DBus.Introspectable")) { synchronized (exportedObjects) { eo = exportedObjects.get(null); } if (null != eo && null == eo.object.get()) { unExportObject(null); eo = null; } if (null != eo) { meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); } if (null != meth) o = new _globalhandler(m.getPath()); else eo = null; } if (null == o) { // now check for specific exported functions synchronized (exportedObjects) { eo = exportedObjects.get(m.getPath()); } if (null != eo && null == eo.object.get()) { if (Debug.debug) Debug.print(Debug.INFO, "Unexporting "+m.getPath()+" implicitly"); unExportObject(m.getPath()); eo = null; } if (null == eo) { eo = fallbackcontainer.get(m.getPath()); } if (null == eo) { try { queueOutgoing(new Error(m, new DBus.Error.UnknownObject(m.getPath()+_(" is not an object provided by this process.")))); } catch (DBusException DBe) {} return; } if (Debug.debug) { Debug.print(Debug.VERBOSE, "Searching for method "+m.getName()+" with signature "+m.getSig()); Debug.print(Debug.VERBOSE, "List of methods on "+eo+":"); for (MethodTuple mt: eo.methods.keySet()) Debug.print(Debug.VERBOSE, " "+mt+" => "+eo.methods.get(mt)); } meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); if (null == meth) { try { queueOutgoing(new Error(m, new DBus.Error.UnknownMethod(MessageFormat.format(_("The method `{0}.{1}' does not exist on this object."), new Object[] { m.getInterface(), m.getName() })))); } catch (DBusException DBe) {} return; } o = eo.object.get(); } // now execute it final Method me = meth; final Object ob = o; final boolean noreply = (1 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)); final DBusCallInfo info = new DBusCallInfo(m); final AbstractConnection conn = this; if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for method "+meth); addRunnable(new Runnable() { private boolean run = false; public synchronized void run() { if (run) return; run = true; if (Debug.debug) Debug.print(Debug.DEBUG, "Running method "+me+" for remote call"); try { Type[] ts = me.getGenericParameterTypes(); m.setArgs(Marshalling.deSerializeParameters(m.getParameters(), ts, conn)); if (Debug.debug) Debug.print(Debug.VERBOSE, "Deserialised "+Arrays.deepToString(m.getParameters())+" to types "+Arrays.deepToString(ts)); } catch (Exception e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); try { conn.queueOutgoing(new Error(m, new DBus.Error.UnknownMethod(_("Failure in de-serializing message: ")+e))); } catch (DBusException DBe) {} return; } try { synchronized (infomap) { infomap.put(Thread.currentThread(), info); } Object result; try { if (Debug.debug) Debug.print(Debug.VERBOSE, "Invoking Method: "+me+" on "+ob+" with parameters "+Arrays.deepToString(m.getParameters())); result = me.invoke(ob, m.getParameters()); } catch (InvocationTargetException ITe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ITe.getCause()); throw ITe.getCause(); } synchronized (infomap) { infomap.remove(Thread.currentThread()); } if (!noreply) { MethodReturn reply; if (Void.TYPE.equals(me.getReturnType())) reply = new MethodReturn(m, null); else { StringBuffer sb = new StringBuffer(); for (String s: Marshalling.getDBusType(me.getGenericReturnType())) sb.append(s); Object[] nr = Marshalling.convertParameters(new Object[] { result }, new Type[] {me.getGenericReturnType()}, conn); reply = new MethodReturn(m, sb.toString(),nr); } conn.queueOutgoing(reply); } } catch (DBusExecutionException DBEe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); try { conn.queueOutgoing(new Error(m, DBEe)); } catch (DBusException DBe) {} } catch (Throwable e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); try { conn.queueOutgoing(new Error(m, new DBusExecutionException(MessageFormat.format(_("Error Executing Method {0}.{1}: {2}"), new Object[] { m.getInterface(), m.getName(), e.getMessage() })))); } catch (DBusException DBe) {} } } }); } @SuppressWarnings({"unchecked","deprecation"}) private void handleMessage(final DBusSignal s) { if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming signal: "+s); Vector<DBusSigHandler<? extends DBusSignal>> v = new Vector<DBusSigHandler<? extends DBusSignal>>(); synchronized(handledSignals) { Vector<DBusSigHandler<? extends DBusSignal>> t; t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), null, null)); if (null != t) v.addAll(t); t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), s.getPath(), null)); if (null != t) v.addAll(t); t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), null, s.getSource())); if (null != t) v.addAll(t); t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), s.getPath(), s.getSource())); if (null != t) v.addAll(t); } if (0 == v.size()) return; final AbstractConnection conn = this; for (final DBusSigHandler<? extends DBusSignal> h: v) { if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for signal "+s+" with handler "+h); addRunnable(new Runnable() { private boolean run = false; public synchronized void run() { if (run) return; run = true; try { DBusSignal rs; if (s instanceof DBusSignal.internalsig || s.getClass().equals(DBusSignal.class)) rs = s.createReal(conn); else rs = s; ((DBusSigHandler<DBusSignal>)h).handle(rs); } catch (DBusException DBe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); try { conn.queueOutgoing(new Error(s, new DBusExecutionException("Error handling signal "+s.getInterface()+"."+s.getName()+": "+DBe.getMessage()))); } catch (DBusException DBe2) {} } } }); } } private void handleMessage(final Error err) { if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming error: "+err); MethodCall m = null; if (null == pendingCalls) return; synchronized (pendingCalls) { if (pendingCalls.contains(err.getReplySerial())) m = pendingCalls.remove(err.getReplySerial()); } if (null != m) m.setReply(err); else synchronized (pendingErrors) { pendingErrors.addLast(err); } } @SuppressWarnings("unchecked") private void handleMessage(final MethodReturn mr) { if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming method return: "+mr); MethodCall m = null; if (null == pendingCalls) return; synchronized (pendingCalls) { if (pendingCalls.contains(mr.getReplySerial())) m = pendingCalls.remove(mr.getReplySerial()); } if (null != m) { m.setReply(mr); mr.setCall(m); CallbackHandler cbh = null; DBusAsyncReply asr = null; synchronized (pendingCallbacks) { cbh = pendingCallbacks.remove(m); if (Debug.debug) Debug.print(Debug.VERBOSE, cbh+" = pendingCallbacks.remove("+m+")"); asr = pendingCallbackReplys.remove(m); } // queue callback for execution if (null != cbh) { final CallbackHandler fcbh = cbh; final DBusAsyncReply fasr = asr; if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for method "+fasr.getMethod()+" with callback handler "+fcbh); addRunnable(new Runnable() { private boolean run = false; public synchronized void run() { if (run) return; run = true; try { if (Debug.debug) Debug.print(Debug.VERBOSE, "Running Callback for "+mr); DBusCallInfo info = new DBusCallInfo(mr); synchronized (infomap) { infomap.put(Thread.currentThread(), info); } fcbh.handle(RemoteInvocationHandler.convertRV(mr.getSig(), mr.getParameters(), fasr.getMethod(), fasr.getConnection())); synchronized (infomap) { infomap.remove(Thread.currentThread()); } } catch (Exception e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); } } }); } } else try { queueOutgoing(new Error(mr, new DBusExecutionException(_("Spurious reply. No message with the given serial id was awaiting a reply.")))); } catch (DBusException DBe) {} } protected void sendMessage(Message m) { try { if (m instanceof DBusSignal) ((DBusSignal) m).appendbody(this); if (m instanceof MethodCall) { if (0 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)) if (null == pendingCalls) ((MethodCall) m).setReply(new Error("org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.Disconnected", 0, "s", new Object[] { _("Disconnected") })); else synchronized (pendingCalls) { pendingCalls.put(m.getSerial(),(MethodCall) m); } } transport.mout.writeMessage(m); } catch (Exception e) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); if (m instanceof MethodCall && e instanceof DBusExecutionException) try { ((MethodCall)m).setReply(new Error(m, e)); } catch (DBusException DBe) {} else if (m instanceof MethodCall) try { if (Debug.debug) Debug.print(Debug.INFO, "Setting reply to "+m+" as an error"); ((MethodCall)m).setReply(new Error(m, new DBusExecutionException(_("Message Failed to Send: ")+e.getMessage()))); } catch (DBusException DBe) {} else if (m instanceof MethodReturn) try { transport.mout.writeMessage(new Error(m, e)); } catch(IOException IOe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); } catch(DBusException IOe) { if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); } if (e instanceof IOException) disconnect(); } } private Message readIncoming() throws DBusException { if (null == transport) throw new NotConnected(_("No transport present")); Message m = null; try { m = transport.min.readMessage(); } catch (IOException IOe) { throw new FatalDBusException(IOe.getMessage()); } return m; } /** * Returns the address this connection is connected to. */ public BusAddress getAddress() throws ParseException { return new BusAddress(addr); } }