/*
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 cx.ath.matthew.debug.Debug;
import org.freedesktop.DBus;
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 org.freedesktop.dbus.exceptions.NotConnected;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
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 static org.freedesktop.dbus.Gettext.getString;
/**
* 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) {
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 {
EfficientQueue ogq = outgoing;
synchronized (ogq) {
outgoing = null;
}
if (!ogq.isEmpty())
m = ogq.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;
protected boolean connected = false;
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(getString("missingObjectPath"));
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + objectpath);
synchronized (exportedObjects) {
if (null != exportedObjects.get(objectpath))
throw new DBusException(getString("objectAlreadyExported"));
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(getString("missingObjectPath"));
if (!objectprefix.matches(OBJECT_REGEX) || objectprefix.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + 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) {
synchronized (outgoing) {
if (null == outgoing) return;
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(getString("notDBusSignal"));
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(getString("notDBusSignal"));
String objectpath = importedObjects.get(object).objectpath;
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + 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(getString("notDBusSignal"));
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(getString("notDBusSignal"));
String objectpath = importedObjects.get(object).objectpath;
if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH)
throw new DBusException(getString("invalidObjectPath") + 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() {
connected = false;
if (Debug.debug) Debug.print(Debug.INFO, "Sending disconnected signal");
try {
handleMessage(new org.freedesktop.DBus.Local.Disconnected("/"));
} catch (Exception ee) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ee);
}
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();
transport = null;
}
} 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() + getString("notObjectProvidedByProcess"))));
} 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(getString("methodDoesNotExist"), 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(getString("deSerializationFailure") + 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(getString("errorExecutingMethod"), 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);
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;
if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Error Runnable 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 Error Callback for " + err);
DBusCallInfo info = new DBusCallInfo(err);
synchronized (infomap) {
infomap.put(Thread.currentThread(), info);
}
fcbh.handleError(err.getException());
synchronized (infomap) {
infomap.remove(Thread.currentThread());
}
} catch (Exception e) {
if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
}
}
});
}
} 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(getString("spuriousReply"))));
} catch (DBusException DBe) {
}
}
protected void sendMessage(Message m) {
try {
if (!connected) throw new NotConnected(getString("disconnected"));
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[]{getString("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 NotConnected)
try {
((MethodCall) m).setReply(new Error("org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")}));
} catch (DBusException DBe) {
}
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(getString("messageFailedSend") + 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 (!connected) throw new NotConnected(getString("missingTransport"));
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);
}
}