diff options
30 files changed, 1401 insertions, 846 deletions
@@ -97,7 +97,7 @@ doc/dbus-java/index.html: dbus-java.tex .doc rm -f doc/dbus-java/*{4ct,4tc,aux,dvi,idv,lg,log,tmp,xref} cp doc/dbus-java/dbus-java.html doc/dbus-java/index.html doc/api/index.html: $(SRCDIR)/*.java $(SRCDIR)/dbus/*.java .doc - $(JAVADOC) -quiet -author -link http://java.sun.com/j2se/1.5.0/docs/api/ -d doc/api $(SRCDIR)/*.java $(SRCDIR)/dbus/*.java + $(JAVADOC) -quiet -author -link http://java.sun.com/j2se/1.5.0/docs/api/ -classpath $(JAVAUNIXJARDIR)/unix.jar:$(JAVAUNIXJARDIR)/hexdump.jar:$(JAVAUNIXJARDIR)/debug-$(DEBUG).jar -d doc/api $(SRCDIR)/*.java $(SRCDIR)/dbus/*.java $(SRCDIR)/dbus/{types,exceptions}/*.java %.1: %.sgml docbook-to-man $< > $@ @@ -114,6 +114,12 @@ cross-test-server: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar cross-test-client: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar $(JAVA) $(JFLAGS) $(CPFLAG) $(CLASSPATH):$(JAVAUNIXJARDIR)/unix.jar:$(JAVAUNIXJARDIR)/hexdump.jar:$(JAVAUNIXJARDIR)/debug-$(DEBUG).jar:libdbus-java-$(VERSION).jar:dbus-java-test-$(VERSION).jar org.freedesktop.dbus.test.cross_test_client +peer-server: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar + $(JAVA) $(JFLAGS) $(CPFLAG) $(CLASSPATH):$(JAVAUNIXJARDIR)/unix.jar:$(JAVAUNIXJARDIR)/hexdump.jar:$(JAVAUNIXJARDIR)/debug-$(DEBUG).jar:libdbus-java-$(VERSION).jar:dbus-java-test-$(VERSION).jar org.freedesktop.dbus.test.test_p2p_server + +peer-client: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar + $(JAVA) $(JFLAGS) $(CPFLAG) $(CLASSPATH):$(JAVAUNIXJARDIR)/unix.jar:$(JAVAUNIXJARDIR)/hexdump.jar:$(JAVAUNIXJARDIR)/debug-$(DEBUG).jar:libdbus-java-$(VERSION).jar:dbus-java-test-$(VERSION).jar org.freedesktop.dbus.test.test_p2p_client + two-part-server: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar $(JAVA) $(JFLAGS) $(CPFLAG) $(CLASSPATH):$(JAVAUNIXJARDIR)/unix.jar:$(JAVAUNIXJARDIR)/hexdump.jar:$(JAVAUNIXJARDIR)/debug-$(DEBUG).jar:libdbus-java-$(VERSION).jar:dbus-java-test-$(VERSION).jar org.freedesktop.dbus.test.two_part_test_server @@ -145,6 +151,11 @@ internal-cross-test: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar $(MAKE) -s cross-test-client | tee client.log ;\ kill $$(cat pid) ; ) +peer-to-peer-test: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar + ( $(MAKE) DBUS_JAVA_FLOATS=true -s peer-server 2>&1 | tee server.log &\ + sleep 1;\ + $(MAKE) DBUS_JAVA_FLOATS=true -s peer-client 2>&1 | tee client.log ) + two-part-test: libdbus-java-$(VERSION).jar dbus-java-test-$(VERSION).jar ( dbus-daemon --config-file=tmp-session.conf --print-pid --print-address=5 --fork >pid 5>address ; \ export DBUS_SESSION_BUS_ADDRESS=$$(cat address) ;\ @@ -1,10 +1,12 @@ - * test floats and make them degrade nicely + * document string comparisons + * document peer2peer + * document low level * autolaunch * dbus-sha-1 auth * test tcp * write a bus daemon - * peer2peer * test on windbus + * autodetection of float support * allow 'virtual' object handlers, so that all object paths under a certain hierarchy are handled by the same object * fix dbus-viewer to recurse on paths * extend dbus-viewer to make calls @@ -16,6 +16,13 @@ Version 2.0: associated with this connection. * Access to the low-level API added. * Change all documentation to refer to 'implementation' not 'binding' + * Add peer 2 peer support with DirectConnection class + * add peer to peer test + * split some code from DBusConnection into common super-class + AbstractConnection + * Support float ('f') type with tests + * Degrade floats to doubles when DBUS_JAVA_FLOATS is not set + * add DBus.Peer interface to standard introspection data Version 1.13: diff --git a/dbus-java.tex b/dbus-java.tex index 36e493a..a433e91 100644 --- a/dbus-java.tex +++ b/dbus-java.tex @@ -641,11 +641,11 @@ Type$[$~$]$ & DBUS\_TYPE\_SIGNATURE \\ \subsubsection{float} Currently the D-Bus reference implementation does not support a native -single-precision floating point type. Along with the C# implementation of the -protocol, the Java implementation supports this extension to the protocol. When -communicating with the reference implementation, however, these will all be -converted to doubles for transmission on the wire. In the case of systems -supporting this extension, the type code `f' is used for floats. +single-precision floating point type. Along with the C\# implementation of the +protocol, the Java implementation supports this extension to the protocol. By +default, however, the library operates in compatibility mode and converts all +floats to the double type. To disable compatibility mode export the environment +variable {\tt DBUS\_JAVA\_FLOATS=true}. \section{Annotations} diff --git a/org/freedesktop/dbus/AbstractConnection.java b/org/freedesktop/dbus/AbstractConnection.java new file mode 100644 index 0000000..1cb3421 --- /dev/null +++ b/org/freedesktop/dbus/AbstractConnection.java @@ -0,0 +1,761 @@ +/* + 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 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 java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import java.io.File; +import java.io.IOException; + +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 org.freedesktop.dbus.exceptions.NonFatalException; + +import cx.ath.matthew.debug.Debug; + + +/** Handles a connection to DBus. + */ +public abstract class AbstractConnection +{ + 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(TIMEOUT, outgoing); + 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(); + } + } + + // write to the wire + if (null != outgoing) synchronized (outgoing) { + if (!outgoing.isEmpty()) + m = outgoing.remove(); } + while (null != m) { + sendMessage(m); + m = null; + if (null != outgoing) synchronized (outgoing) { + if (!outgoing.isEmpty()) + m = outgoing.remove(); } + } + } + if (null != outgoing) synchronized (outgoing) { + if (!outgoing.isEmpty()) + m = outgoing.remove(); + } + while (null != m) { + sendMessage(m); + m = null; + if (null != outgoing) synchronized (outgoing) { + if (!outgoing.isEmpty()) + m = outgoing.remove(); } + } + synchronized (this) { notifyAll(); } + } catch (NotConnected NC) {} + } + } + 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) + 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(); + } + } + } + /** + * Timeout in ms on checking the BUS for incoming messages and sending outgoing messages + */ + private static final int TIMEOUT = 1; + /** 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; + protected Map<DBusInterface,RemoteObject> importedObjects; + protected Map<SignalTuple,Vector<DBusSigHandler>> handledSignals; + protected EfficientMap pendingCalls; + protected LinkedList<Runnable> runnables; + protected LinkedList<_workerthread> workers; + protected boolean _run; + EfficientQueue outgoing; + LinkedList<Error> pendingErrors; + private static final Map<Thread,DBusCallInfo> infomap = new HashMap<Thread,DBusCallInfo>(); + protected _thread thread; + protected Transport transport; + protected String addr; + 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>(); + exportedObjects.put(null, new ExportedObject(new _globalhandler())); + handledSignals = new HashMap<SignalTuple,Vector<DBusSigHandler>>(); + pendingCalls = new EfficientMap(PENDING_MAP_INITIAL_SIZE); + outgoing = new EfficientQueue(PENDING_MAP_INITIAL_SIZE); + pendingErrors = new LinkedList<Error>(); + runnables = new LinkedList<Runnable>(); + workers = new LinkedList<_workerthread>(); + objectTree = new ObjectTree(); + 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(); + } + + /** + * 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 + { + for (String s: exportedObjects.keySet()) + if (exportedObjects.get(s).object.equals(i)) + 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; + } + + /** + * 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 (!objectpath.matches(OBJECT_REGEX)||objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException("Invalid object path ("+objectpath+")"); + if (null == objectpath || "".equals(objectpath)) + throw new DBusException("Must Specify an Object Path"); + synchronized (exportedObjects) { + if (null != exportedObjects.get(objectpath)) + throw new DBusException("Object already exported"); + ExportedObject eo = new ExportedObject(object); + exportedObjects.put(objectpath, eo); + objectTree.add(objectpath, object, eo.introspectiondata); + } + } + /** + * Stop Exporting an object + * @param objectpath The objectpath to stop exporting. + */ + public void unExportObject(String objectpath) + { + synchronized (exportedObjects) { + exportedObjects.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) + { + if (null == outgoing) return; + synchronized (outgoing) { + outgoing.add(signal); } + } + /** + * 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. + */ + 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), 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. + */ + 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), handler); + } + + protected abstract <T extends DBusSignal> void addSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException; + + protected void addSigHandlerWithoutMatch(Class signal, DBusSigHandler handler) throws DBusException + { + DBusMatchRule rule = new DBusMatchRule(signal); + SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); + synchronized (handledSignals) { + Vector<DBusSigHandler> v = handledSignals.get(key); + if (null == v) { + v = new Vector<DBusSigHandler>(); + v.add(handler); + handledSignals.put(key, v); + } else + v.add(handler); + } + } + + /** + * Disconnect from the Bus. + */ + public void disconnect() + { + // run all pending tasks. + while (runnables.size() > 0) + synchronized (runnables) { + runnables.notifyAll(); + } + + // stop the main thread + _run = false; + + // disconnect from the trasport layer + try { + 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 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. + */ + 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, true, 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.ERR, "Handling incoming method call: "+m); + // get the method signature + Object[] params = m.getParameters(); + + ExportedObject eo = null; + Method meth = null; + Object o; + + synchronized (exportedObjects) { + eo = exportedObjects.get(null); + } + if (null != eo) { + meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); + } + if (null != meth) + o = new _globalhandler(m.getPath()); + + else { + // now check for specific exported functions + + synchronized (exportedObjects) { + eo = exportedObjects.get(m.getPath()); + } + + if (null == eo) { + try { + if (null != outgoing) synchronized (outgoing) { + outgoing.add(new Error(m, new DBus.Error.UnknownObject(m.getPath()+" is not an object provided by this process."))); } + } catch (DBusException DBe) {} + return; + } + meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); + if (null == meth) { + try { + if (null != outgoing) synchronized (outgoing) { + outgoing.add(new Error(m, new DBus.Error.UnknownMethod("The method `"+m.getInterface()+"."+m.getName()+"' does not exist on this object."))); } + } catch (DBusException DBe) {} + return; + } + o = eo.object; + } + + // now execute it + final Method me = meth; + final Object ob = o; + final EfficientQueue outqueue = outgoing; + final boolean noreply = (1 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)); + final DBusCallInfo info = new DBusCallInfo(m); + final AbstractConnection conn = this; + addRunnable(new Runnable() + { + public void run() + { + 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 { + synchronized (outqueue) { + outqueue.add(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 { + 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); + } + synchronized (outqueue) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Queuing reply"); + outqueue.add(reply); + } + } + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + try { + synchronized (outqueue) { + outqueue.add(new Error(m, DBEe)); + } + } catch (DBusException DBe) {} + } catch (Throwable e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + try { + synchronized (outqueue) { + outqueue.add(new Error(m, new DBusExecutionException("Error Executing Method "+m.getInterface()+"."+m.getName()+": "+e.getMessage()))); + } + } catch (DBusException DBe) {} + } + } + }); + } + @SuppressWarnings({"unchecked","deprecation"}) + private void handleMessage(final DBusSignal s) + { + if (Debug.debug) Debug.print(Debug.ERR, "Handling incoming signal: "+s); + Vector<DBusSigHandler> v = new Vector<DBusSigHandler>(); + synchronized(handledSignals) { + Vector<DBusSigHandler> 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 EfficientQueue outqueue = outgoing; + final AbstractConnection conn = this; + for (final DBusSigHandler h: v) + addRunnable(new Runnable() { public void run() { + { + try { + DBusSignal rs; + if (s instanceof DBusSignal.internalsig || s.getClass().equals(DBusSignal.class)) + rs = s.createReal(conn); + else + rs = s; + h.handle(rs); + } catch (DBusException DBe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + try { + synchronized (outqueue) { + outqueue.add(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.ERR, "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); } + } + private void handleMessage(final MethodReturn mr) + { + if (Debug.debug) Debug.print(Debug.ERR, "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); + } else + try { + if (null != outgoing) synchronized (outgoing) { + outgoing.add(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); + + transport.mout.writeMessage(m); + + 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); + } + } + } 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); + } + } + } + private Message readIncoming(int timeoutms, EfficientQueue outgoing) throws DBusException + { + if (null == transport) throw new NotConnected("No transport present"); + // TODO do something with timeoutms and outgoing + 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() { return new BusAddress(addr); } +} diff --git a/org/freedesktop/dbus/ArrayFrob.java b/org/freedesktop/dbus/ArrayFrob.java index d2eadc9..6b4347e 100644 --- a/org/freedesktop/dbus/ArrayFrob.java +++ b/org/freedesktop/dbus/ArrayFrob.java @@ -152,7 +152,7 @@ class ArrayFrob return type((Object[]) o, c.getComponentType()); } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new IllegalArgumentException(e); } diff --git a/org/freedesktop/dbus/DBusAsyncReply.java b/org/freedesktop/dbus/DBusAsyncReply.java index 4fb55d0..d5597e1 100644 --- a/org/freedesktop/dbus/DBusAsyncReply.java +++ b/org/freedesktop/dbus/DBusAsyncReply.java @@ -44,8 +44,8 @@ public class DBusAsyncReply<ReturnType> private DBusExecutionException error = null; private MethodCall mc; private Method me; - private DBusConnection conn; - DBusAsyncReply(MethodCall mc, Method me, DBusConnection conn) + private AbstractConnection conn; + DBusAsyncReply(MethodCall mc, Method me, AbstractConnection conn) { this.mc = mc; this.me = me; @@ -64,7 +64,7 @@ public class DBusAsyncReply<ReturnType> } catch (DBusExecutionException DBEe) { error = DBEe; } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); error = new DBusExecutionException(DBe.getMessage()); } } diff --git a/org/freedesktop/dbus/DBusConnection.java b/org/freedesktop/dbus/DBusConnection.java index 34ceff8..7f85718 100644 --- a/org/freedesktop/dbus/DBusConnection.java +++ b/org/freedesktop/dbus/DBusConnection.java @@ -10,38 +10,19 @@ */ package org.freedesktop.dbus; -import java.lang.annotation.Annotation; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.TreeSet; 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; @@ -56,7 +37,7 @@ import cx.ath.matthew.debug.Debug; * Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues. * </p> */ -public class DBusConnection +public class DBusConnection extends AbstractConnection { private class _sighandler implements DBusSigHandler { @@ -84,121 +65,7 @@ public class DBusConnection } } } - private 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(TIMEOUT, outgoing); - 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 (IOException IOe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); - try { - handleMessage(new org.freedesktop.DBus.Local.Disconnected("/")); - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - } - disconnect(); - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - } - - // write to the wire - if (null != outgoing) synchronized (outgoing) { - if (!outgoing.isEmpty()) - m = outgoing.remove(); } - while (null != m) { - sendMessage(m); - m = null; - if (null != outgoing) synchronized (outgoing) { - if (!outgoing.isEmpty()) - m = outgoing.remove(); } - } - } - if (null != outgoing) synchronized (outgoing) { - if (!outgoing.isEmpty()) - m = outgoing.remove(); - } - while (null != m) { - sendMessage(m); - m = null; - if (null != outgoing) synchronized (outgoing) { - if (!outgoing.isEmpty()) - m = outgoing.remove(); } - } - synchronized (this) { notifyAll(); } - } catch (NotConnected NC) {} - } - } - 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) - 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; - } - } - private 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(); - } - } - } /** * System Bus */ @@ -207,66 +74,16 @@ public class DBusConnection * Session Bus */ public static final int SESSION = 1; - /** - * Timeout in ms on checking the BUS for incoming messages and sending outgoing messages - */ - private static final int TIMEOUT = 1; - /** 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; public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"; - private Map<String,ExportedObject> exportedObjects; - private ObjectTree objectTree; - private Map<DBusInterface,RemoteObject> importedObjects; - private Map<SignalTuple,Vector<DBusSigHandler>> handledSignals; - private EfficientMap pendingCalls; private List<String> busnames; - private LinkedList<Runnable> runnables; - private LinkedList<_workerthread> workers; - private boolean _run; - private int connid; - EfficientQueue outgoing; - LinkedList<Error> pendingErrors; private static final Map<Object,DBusConnection> conn = new HashMap<Object,DBusConnection>(); - private static final Map<Thread,DBusCallInfo> infomap = new HashMap<Thread,DBusCallInfo>(); private int _refcount = 0; private Object _reflock = new Object(); private Object connkey; private DBus _dbus; - private _thread thread; - private Transport transport; - private String addr; - static final Pattern dollar_pattern = Pattern.compile("[$]"); - public static final boolean EXCEPTION_DEBUG; - static { - 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); - } - } /** * Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. @@ -326,29 +143,12 @@ public class DBusConnection @SuppressWarnings("unchecked") private DBusConnection(String address) throws DBusException { - exportedObjects = new HashMap<String,ExportedObject>(); - importedObjects = new HashMap<DBusInterface,RemoteObject>(); - exportedObjects.put(null, new ExportedObject(new _globalhandler())); - handledSignals = new HashMap<SignalTuple,Vector<DBusSigHandler>>(); - pendingCalls = new EfficientMap(PENDING_MAP_INITIAL_SIZE); + super(address); busnames = new Vector<String>(); - outgoing = new EfficientQueue(PENDING_MAP_INITIAL_SIZE); - pendingErrors = new LinkedList<Error>(); - runnables = new LinkedList<Runnable>(); - workers = new LinkedList<_workerthread>(); - objectTree = new ObjectTree(); - synchronized (workers) { - for (int i = 0; i < THREADCOUNT; i++) { - _workerthread t = new _workerthread(); - t.start(); - workers.add(t); - } - } - _run = true; + synchronized (_reflock) { _refcount = 1; } - addr = address; try { transport = new Transport(addr); @@ -357,10 +157,9 @@ public class DBusConnection throw new DBusException("Failed to connect to bus "+IOe.getMessage()); } - // start listening - thread = new _thread(); - thread.start(); - + // start listening for calls + listen(); + // register disconnect handlers DBusSigHandler h = new _sighandler(); addSigHandlerWithoutMatch(org.freedesktop.DBus.Local.Disconnected.class, h); @@ -376,50 +175,6 @@ public class DBusConnection } } - /** - * 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 - { - for (String s: exportedObjects.keySet()) - if (exportedObjects.get(s).object.equals(i)) - 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"); - } - DBusInterface dynamicProxy(String source, String path) throws DBusException { try { @@ -470,19 +225,6 @@ public class DBusConnection return dynamicProxy(source, path); } - /** - * 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; - } - /** * Request a bus name. * Request the well known name that this should respond to on the Bus. @@ -530,53 +272,6 @@ public class DBusConnection names.addAll(busnames); return names.toArray(new String[0]); } - /** - * 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 (!objectpath.matches(OBJECT_REGEX)||objectpath.length() > MAX_NAME_LENGTH) - throw new DBusException("Invalid object path ("+objectpath+")"); - if (null == objectpath || "".equals(objectpath)) - throw new DBusException("Must Specify an Object Path"); - synchronized (exportedObjects) { - if (null != exportedObjects.get(objectpath)) - throw new DBusException("Object already exported"); - ExportedObject eo = new ExportedObject(object); - exportedObjects.put(objectpath, eo); - objectTree.add(objectpath, object, eo.introspectiondata); - } - } - /** - * Stop Exporting an object - * @param objectpath The objectpath to stop exporting. - */ - public void unExportObject(String objectpath) - { - synchronized (exportedObjects) { - exportedObjects.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. - */ public DBusInterface getPeerRemoteObject(String busname, String objectpath, Class<? extends DBusInterface> type) throws DBusException { return getPeerRemoteObject(busname, objectpath, type, true); @@ -742,44 +437,6 @@ public class DBusConnection return i; } /** - * Send a signal. - * @param signal The signal to send. - */ - public void sendSignal(DBusSignal signal) - { - if (null == outgoing) return; - synchronized (outgoing) { - outgoing.add(signal); } - } - /** - * 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); - } - /** * Remove a Signal Handler. * Stops listening for this signal. * @param type The signal to watch for. @@ -815,7 +472,7 @@ public class DBusConnection throw new DBusException("Invalid object path ("+objectpath+")"); removeSigHandler(new DBusMatchRule(type, source, objectpath), handler); } - private <T extends DBusSignal> void removeSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException + protected <T extends DBusSignal> void removeSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException { SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); @@ -837,36 +494,6 @@ public class DBusConnection } /** * 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. - */ - 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), 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. - */ - 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), handler); - } - /** - * Add a Signal Handler. * Adds a signal handler to call when a signal is received which matches the specified type, name and source. * @param type The signal to watch for. * @param source The process which will send the signal. This <b>MUST</b> be a unique bus name and not a well known name. @@ -903,7 +530,7 @@ public class DBusConnection throw new DBusException("Invalid object path ("+objectpath+")"); addSigHandler(new DBusMatchRule(type, source, objectpath), handler); } - private <T extends DBusSignal> void addSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException + protected <T extends DBusSignal> void addSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException { try { _dbus.AddMatch(rule.toString()); @@ -922,20 +549,6 @@ public class DBusConnection v.add(handler); } } - private void addSigHandlerWithoutMatch(Class signal, DBusSigHandler handler) throws DBusException - { - DBusMatchRule rule = new DBusMatchRule(signal); - SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); - synchronized (handledSignals) { - Vector<DBusSigHandler> v = handledSignals.get(key); - if (null == v) { - v = new Vector<DBusSigHandler>(); - v.add(handler); - handledSignals.put(key, v); - } else - v.add(handler); - } - } /** * Disconnect from the Bus. * This only disconnects when the last reference to the bus has disconnect called on it @@ -972,319 +585,10 @@ public class DBusConnection } } catch (DBusException DBe) {} - // run all pending tasks. - while (runnables.size() > 0) - synchronized (runnables) { - runnables.notifyAll(); - } - - // stop the main thread - _run = false; - try { - synchronized (thread) { thread.wait(); } - } catch (InterruptedException Ie) {} - - // disconnect from the trasport layer - try { - transport.disconnect(); - } catch (IOException IOe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); - } conn.remove(addr); - - // 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(); - } + super.disconnect(); } } } } - 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 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. - */ - 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, true, 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.ERR, "Handling incoming method call: "+m); - // get the method signature - Object[] params = m.getParameters(); - - ExportedObject eo = null; - Method meth = null; - Object o; - - synchronized (exportedObjects) { - eo = exportedObjects.get(null); - } - if (null != eo) { - meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); - } - if (null != meth) - o = new _globalhandler(m.getPath()); - - else { - // now check for specific exported functions - - synchronized (exportedObjects) { - eo = exportedObjects.get(m.getPath()); - } - - if (null == eo) { - try { - if (null != outgoing) synchronized (outgoing) { - outgoing.add(new Error(m, new DBus.Error.UnknownObject(m.getPath()+" is not an object provided by this process."))); } - } catch (DBusException DBe) {} - return; - } - meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); - if (null == meth) { - try { - if (null != outgoing) synchronized (outgoing) { - outgoing.add(new Error(m, new DBus.Error.UnknownMethod("The method `"+m.getInterface()+"."+m.getName()+"' does not exist on this object."))); } - } catch (DBusException DBe) {} - return; - } - o = eo.object; - } - - // now execute it - final Method me = meth; - final Object ob = o; - final EfficientQueue outqueue = outgoing; - final boolean noreply = (1 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)); - final DBusCallInfo info = new DBusCallInfo(m); - final DBusConnection conn = this; - addRunnable(new Runnable() - { - public void run() - { - try { - Type[] ts = me.getGenericParameterTypes(); - m.setArgs(Marshalling.deSerializeParameters(m.getParameters(), ts, conn)); - if (Debug.debug) Debug.print(Debug.DEBUG, "Deserialised "+Arrays.deepToString(m.getParameters())+" to types "+Arrays.deepToString(ts)); - } catch (Exception e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - try { - synchronized (outqueue) { - outqueue.add(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 { - 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); - } - synchronized (outqueue) { - outqueue.add(reply); - } - } - } catch (DBusExecutionException DBEe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); - try { - synchronized (outqueue) { - outqueue.add(new Error(m, DBEe)); - } - } catch (DBusException DBe) {} - } catch (Throwable e) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - try { - synchronized (outqueue) { - outqueue.add(new Error(m, new DBusExecutionException("Error Executing Method "+m.getInterface()+"."+m.getName()+": "+e.getMessage()))); - } - } catch (DBusException DBe) {} - } - } - }); - } - @SuppressWarnings({"unchecked","deprecation"}) - private void handleMessage(final DBusSignal s) - { - if (Debug.debug) Debug.print(Debug.ERR, "Handling incoming signal: "+s); - Vector<DBusSigHandler> v = new Vector<DBusSigHandler>(); - synchronized(handledSignals) { - Vector<DBusSigHandler> 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 EfficientQueue outqueue = outgoing; - final DBusConnection conn = this; - for (final DBusSigHandler h: v) - addRunnable(new Runnable() { public void run() { - { - try { - DBusSignal rs; - if (s instanceof DBusSignal.internalsig || s.getClass().equals(DBusSignal.class)) - rs = s.createReal(conn); - else - rs = s; - h.handle(rs); - } catch (DBusException DBe) { - if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); - try { - synchronized (outqueue) { - outqueue.add(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.ERR, "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); } - } - private void handleMessage(final MethodReturn mr) - { - if (Debug.debug) Debug.print(Debug.ERR, "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); - } else - try { - if (null != outgoing) synchronized (outgoing) { - outgoing.add(new Error(mr, new DBusExecutionException("Spurious reply. No message with the given serial id was awaiting a reply."))); - } - } catch (DBusException DBe) {} - } - private void sendMessage(Message m) - { - try { - if (m instanceof DBusSignal) - ((DBusSignal) m).appendbody(this); - - transport.mout.writeMessage(m); - - 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); - } - } - } 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); - } - } - } - private Message readIncoming(int timeoutms, EfficientQueue outgoing) throws IOException, DBusException - { - // TODO do something with timeoutms and outgoing - Message m = transport.min.readMessage(); - return m; - } } diff --git a/org/freedesktop/dbus/DBusMatchRule.java b/org/freedesktop/dbus/DBusMatchRule.java index bc36a72..b231e1f 100644 --- a/org/freedesktop/dbus/DBusMatchRule.java +++ b/org/freedesktop/dbus/DBusMatchRule.java @@ -63,14 +63,14 @@ public class DBusMatchRule public DBusMatchRule(Class c) throws DBusException { if (DBusInterface.class.isAssignableFrom(c)) { - iface = DBusConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); + iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); if (!iface.matches(".*\\..*")) throw new DBusException("DBusInterfaces must be defined in a package."); member = null; type = null; } else if (DBusSignal.class.isAssignableFrom(c)) { - iface = DBusConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); + iface = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); // Don't export things which are invalid D-Bus interfaces if (!iface.matches(".*\\..*")) throw new DBusException("DBusInterfaces must be defined in a package."); @@ -78,14 +78,14 @@ public class DBusMatchRule type = "signal"; } else if (Error.class.isAssignableFrom(c)) { - iface = DBusConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); + iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); if (!iface.matches(".*\\..*")) throw new DBusException("DBusInterfaces must be defined in a package."); member = null; type = "error"; } else if (DBusExecutionException.class.isAssignableFrom(c)) { - iface = DBusConnection.dollar_pattern.matcher(c.getClass().getName()).replaceAll("."); + iface = AbstractConnection.dollar_pattern.matcher(c.getClass().getName()).replaceAll("."); if (!iface.matches(".*\\..*")) throw new DBusException("DBusInterfaces must be defined in a package."); member = null; diff --git a/org/freedesktop/dbus/DBusSignal.java b/org/freedesktop/dbus/DBusSignal.java index 67afd3d..8bb8290 100644 --- a/org/freedesktop/dbus/DBusSignal.java +++ b/org/freedesktop/dbus/DBusSignal.java @@ -74,7 +74,7 @@ public class DBusSignal extends Message { String type = ""; if (null != c.getEnclosingClass()) - type = DBusConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); + type = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); DBusSignal s = new internalsig(source, objectpath, type, c.getSimpleName(), sig, parameters, serial); s.c = c; return s; @@ -91,7 +91,7 @@ public class DBusSignal extends Message } while (null == c && name.matches(".*\\..*")); return c; } - DBusSignal createReal(DBusConnection conn) throws DBusException + DBusSignal createReal(AbstractConnection conn) throws DBusException { if (null == c) c = createSignalClass(getInterface()+"$"+getName()); @@ -129,7 +129,7 @@ public class DBusSignal extends Message s.bytecounter = wiredata.length; return s; } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusException(e.getMessage()); } } @@ -144,7 +144,7 @@ public class DBusSignal extends Message { super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0); - if (!objectpath.matches(DBusConnection.OBJECT_REGEX)) throw new DBusException("Invalid object path ("+objectpath+")"); + if (!objectpath.matches(AbstractConnection.OBJECT_REGEX)) throw new DBusException("Invalid object path ("+objectpath+")"); Class tc = getClass(); String member = tc.getSimpleName(); @@ -155,7 +155,7 @@ public class DBusSignal extends Message 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 - iface = DBusConnection.dollar_pattern.matcher(enc.getName()).replaceAll("."); + iface = AbstractConnection.dollar_pattern.matcher(enc.getName()).replaceAll("."); headers.put(Message.HeaderField.PATH,objectpath); headers.put(Message.HeaderField.MEMBER,member); @@ -187,7 +187,7 @@ public class DBusSignal extends Message headers.put(Message.HeaderField.SIGNATURE,sig); setArgs(args); } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusException("Failed to add signal parameters: "+e.getMessage()); } } @@ -197,7 +197,7 @@ public class DBusSignal extends Message append("ua(yv)", ++serial, hargs.toArray()); pad((byte)8); } - void appendbody(DBusConnection conn) throws DBusException + void appendbody(AbstractConnection conn) throws DBusException { if (bodydone) return; diff --git a/org/freedesktop/dbus/DirectConnection.java b/org/freedesktop/dbus/DirectConnection.java new file mode 100644 index 0000000..dedc018 --- /dev/null +++ b/org/freedesktop/dbus/DirectConnection.java @@ -0,0 +1,210 @@ +/* + 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 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 java.lang.reflect.Proxy; +import java.io.File; +import java.io.IOException; +import java.util.Random; +import java.util.Vector; + +import org.freedesktop.DBus; +import org.freedesktop.dbus.exceptions.DBusException; + +import cx.ath.matthew.debug.Debug; + +/** Handles a peer to peer connection between two applications withou a bus daemon. + * <p> + * Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues. + * </p> + */ +public class DirectConnection extends AbstractConnection +{ + /** + * Create a direct connection to another application. + * @param address The address to connect to. This is a standard D-Bus address, except that the additional parameter 'listen=true' should be added in the application which is creating the socket. + */ + public DirectConnection(String address) throws DBusException + { + super(address); + + try { + transport = new Transport(addr); + } catch (IOException IOe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); + throw new DBusException("Failed to connect to bus "+IOe.getMessage()); + } + + listen(); + } + + /** + * Creates a bus address for a randomly generated abstract unix socket. + * @return a random bus address. + */ + public static String createDynamicSession() + { + String address = "unix:listen=true"; + String path = "/tmp/dbus-XXXXXXXXXX"; + Random r = new Random(); + do { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 10; i++) + sb.append((char) ((Math.abs(r.nextInt()) % 26) + 65)); + path = path.replaceAll("..........$", sb.toString()); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Trying path "+path); + } while ((new File(path)).exists()); + address += ",abstract="+path; + address += ",guid="+Transport.genGUID(); + if (Debug.debug) Debug.print("Created Session address: "+address); + return address; + } + DBusInterface dynamicProxy(String path) throws DBusException + { + try { + DBus.Introspectable intro = (DBus.Introspectable) getRemoteObject(path, DBus.Introspectable.class); + String data = intro.Introspect(); + String[] tags = data.split("[<>]"); + Vector<String> ifaces = new Vector<String>(); + for (String tag: tags) { + if (tag.startsWith("interface")) { + ifaces.add(tag.replaceAll("^interface *name *= *['\"]([^'\"]*)['\"].*$", "$1")); + } + } + Vector<Class> ifcs = new Vector<Class>(); + for(String iface: ifaces) { + int j = 0; + while (j >= 0) { + try { + ifcs.add(Class.forName(iface)); + break; + } catch (Exception e) {} + j = iface.lastIndexOf("."); + char[] cs = iface.toCharArray(); + if (j >= 0) { + cs[j] = '$'; + iface = String.valueOf(cs); + } + } + } + + if (ifcs.size() == 0) throw new DBusException("Could not find an interface to cast to"); + + RemoteObject ro = new RemoteObject(null, path, null, false); + DBusInterface newi = (DBusInterface) Proxy.newProxyInstance(ifcs.get(0).getClassLoader(), + (Class[]) ifcs.toArray(new Class[0]), new RemoteInvocationHandler(this, ro)); + importedObjects.put(newi, ro); + return newi; + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusException("Failed to create proxy object for "+path+". Reason: "+e.getMessage()); + } + } + + DBusInterface getExportedObject(String path) throws DBusException + { + ExportedObject o = exportedObjects.get(path); + if (null != o) return o.object; + return dynamicProxy(path); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + * + * This method will use bus introspection to determine the interfaces on a remote object and so + * <b>may block</b> and <b>may fail</b>. The resulting proxy object will, however, be castable + * to any interface it implements. It will also autostart the process if applicable. Also note + * that the resulting proxy may fail to execute the correct method with overloaded methods + * and that complex types may fail in interesting ways. Basically, if something odd happens, + * try specifying the interface explicitly. + * + * @param objectpath The path on which the process is exporting the object. + * @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. + */ + public DBusInterface getRemoteObject(String objectpath) throws DBusException + { + if (null == objectpath) throw new DBusException("Invalid object path (null)"); + + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException("Invalid object path ("+objectpath+")"); + + return dynamicProxy(objectpath); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + * @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. + */ + public DBusInterface getRemoteObject(String objectpath, Class<? extends DBusInterface> type) throws DBusException + { + if (null == objectpath) throw new DBusException("Invalid object path (null)"); + if (null == type) throw new ClassCastException("Not A DBus Interface"); + + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException("Invalid object path ("+objectpath+")"); + + if (!DBusInterface.class.isAssignableFrom(type)) throw new ClassCastException("Not A DBus Interface"); + + // don't let people import things which don't have a + // valid D-Bus interface name + if (type.getName().equals(type.getSimpleName())) + throw new DBusException("DBusInterfaces cannot be declared outside a package"); + + RemoteObject ro = new RemoteObject(null, objectpath, type, false); + DBusInterface i = (DBusInterface) Proxy.newProxyInstance(type.getClassLoader(), + new Class[] { type }, new RemoteInvocationHandler(this, ro)); + importedObjects.put(i, ro); + return i; + } + protected <T extends DBusSignal> void removeSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException + { + SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); + synchronized (handledSignals) { + Vector<DBusSigHandler> v = handledSignals.get(key); + if (null != v) { + v.remove(handler); + if (0 == v.size()) { + handledSignals.remove(key); + } + } + } + } + protected <T extends DBusSignal> void addSigHandler(DBusMatchRule rule, DBusSigHandler<T> handler) throws DBusException + { + SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); + synchronized (handledSignals) { + Vector<DBusSigHandler> v = handledSignals.get(key); + if (null == v) { + v = new Vector<DBusSigHandler>(); + v.add(handler); + handledSignals.put(key, v); + } else + v.add(handler); + } + } + DBusInterface getExportedObject(String source, String path) throws DBusException + { + return getExportedObject(path); + } +} diff --git a/org/freedesktop/dbus/EfficientQueue.java b/org/freedesktop/dbus/EfficientQueue.java index ab2a4e4..533fe5a 100644 --- a/org/freedesktop/dbus/EfficientQueue.java +++ b/org/freedesktop/dbus/EfficientQueue.java @@ -93,4 +93,11 @@ class EfficientQueue // check if find succeeds return start == end; } + public int size() + { + if (end >= start) + return end-start; + else + return mv.length-start+end; + } } diff --git a/org/freedesktop/dbus/Error.java b/org/freedesktop/dbus/Error.java index ac69697..f77d6c6 100644 --- a/org/freedesktop/dbus/Error.java +++ b/org/freedesktop/dbus/Error.java @@ -28,16 +28,20 @@ public class Error extends Message { super(Message.Endian.BIG, Message.MessageType.ERROR, (byte) 0); - if (null == dest || null == errorName) - throw new MessageFormatException("Must specify destination and error name to Errors."); + if (null == errorName) + throw new MessageFormatException("Must specify error name to Errors."); headers.put(Message.HeaderField.REPLY_SERIAL,replyserial); headers.put(Message.HeaderField.ERROR_NAME,errorName); - headers.put(Message.HeaderField.DESTINATION,dest); Vector<Object> hargs = new Vector<Object>(); hargs.add(new Object[] { Message.HeaderField.ERROR_NAME, new Object[] { ArgumentType.STRING_STRING, errorName } }); - hargs.add(new Object[] { Message.HeaderField.DESTINATION, new Object[] { ArgumentType.STRING_STRING, dest } }); hargs.add(new Object[] { Message.HeaderField.REPLY_SERIAL, new Object[] { ArgumentType.UINT32_STRING, replyserial } }); + + if (null != dest) { + headers.put(Message.HeaderField.DESTINATION,dest); + hargs.add(new Object[] { Message.HeaderField.DESTINATION, new Object[] { ArgumentType.STRING_STRING, dest } }); + } + if (null != sig) { hargs.add(new Object[] { Message.HeaderField.SIGNATURE, new Object[] { ArgumentType.SIGNATURE_STRING, sig } }); headers.put(Message.HeaderField.SIGNATURE,sig); @@ -55,7 +59,7 @@ public class Error extends Message } public Error(Message m, Exception e) throws DBusException { - this(m.getSource(), DBusConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage()); + this(m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage()); } @SuppressWarnings("unchecked") private static Class<? extends DBusExecutionException> createExceptionClass(String name) @@ -91,8 +95,8 @@ public class Error extends Message ex.setType(getName()); return ex; } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug && null != e.getCause()) + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug && null != e.getCause()) Debug.print(Debug.ERR, e.getCause()); DBusExecutionException ex; Object[] args = null; diff --git a/org/freedesktop/dbus/ExportedObject.java b/org/freedesktop/dbus/ExportedObject.java index 25d50d9..ecd3d7b 100644 --- a/org/freedesktop/dbus/ExportedObject.java +++ b/org/freedesktop/dbus/ExportedObject.java @@ -39,7 +39,7 @@ class ExportedObject } catch (InvocationTargetException ITe) { } catch (IllegalAccessException IAe) {} - ans += " <annotation name=\""+DBusConnection.dollar_pattern.matcher(t.getName()).replaceAll(".")+"\" value=\""+value+"\" />\n"; + ans += " <annotation name=\""+AbstractConnection.dollar_pattern.matcher(t.getName()).replaceAll(".")+"\" value=\""+value+"\" />\n"; } return ans; } @@ -56,7 +56,7 @@ class ExportedObject // add this class's public methods if (c.getName().length() > DBusConnection.MAX_NAME_LENGTH) throw new DBusException("Introspected interface name exceeds 255 characters. Cannot export objects of type "+c.getName()+"."); - introspectiondata += " <interface name=\""+DBusConnection.dollar_pattern.matcher(c.getName()).replaceAll(".")+"\">\n"; + introspectiondata += " <interface name=\""+AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".")+"\">\n"; introspectiondata += getAnnotations(c); for (Method meth: c.getDeclaredMethods()) if (Modifier.isPublic(meth.getModifiers())) { @@ -68,7 +68,7 @@ class ExportedObject for (Class ex: meth.getExceptionTypes()) if (DBusExecutionException.class.isAssignableFrom(ex)) introspectiondata += - " <annotation name=\"org.freedesktop.DBus.Method.Error\" value=\""+DBusConnection.dollar_pattern.matcher(ex.getName()).replaceAll(".")+"\" />\n"; + " <annotation name=\"org.freedesktop.DBus.Method.Error\" value=\""+AbstractConnection.dollar_pattern.matcher(ex.getName()).replaceAll(".")+"\" />\n"; for (Type pt: meth.getGenericParameterTypes()) for (String s: Marshalling.getDBusType(pt)) { introspectiondata += " <arg type=\""+s+"\" direction=\"in\"/>\n"; @@ -127,6 +127,11 @@ class ExportedObject " <arg type=\"s\" direction=\"out\"/>\n"+ " </method>\n"+ " </interface>\n"; + introspectiondata += + " <interface name=\"org.freedesktop.DBus.Peer\">\n"+ + " <method name=\"Ping\">\n"+ + " </method>\n"+ + " </interface>\n"; } } diff --git a/org/freedesktop/dbus/Marshalling.java b/org/freedesktop/dbus/Marshalling.java index 1e293a7..a2cd4ab 100644 --- a/org/freedesktop/dbus/Marshalling.java +++ b/org/freedesktop/dbus/Marshalling.java @@ -96,9 +96,9 @@ public class Marshalling if (basic && !(c instanceof Class)) throw new DBusException(c+" is not a basic type"); - if (c instanceof TypeVariable) out[level].append('v'); + if (c instanceof TypeVariable) out[level].append((char) Message.ArgumentType.VARIANT); else if (c instanceof GenericArrayType) { - out[level].append('a'); + out[level].append((char) Message.ArgumentType.ARRAY); String[] s = recursiveGetDBusType(((GenericArrayType) c).getGenericComponentType(), false, level+1); if (s.length != 1) throw new DBusException("Multi-valued array types not permitted"); out[level].append(s[0]); @@ -116,7 +116,7 @@ public class Marshalling if (s.length != 1) throw new DBusException("Multi-valued array types not permitted"); out[level].append(s[0]); } catch (ArrayIndexOutOfBoundsException AIOOBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); throw new DBusException("Map must have 2 parameters"); } out[level].append('}'); @@ -124,20 +124,20 @@ public class Marshalling else if (List.class.isAssignableFrom((Class) p.getRawType())) { for (Type t: p.getActualTypeArguments()) { if (Type.class.equals(t)) - out[level].append('g'); + out[level].append((char) Message.ArgumentType.SIGNATURE); else { String[] s = recursiveGetDBusType(t, false, level+1); if (s.length != 1) throw new DBusException("Multi-valued array types not permitted"); - out[level].append('a'); + out[level].append((char) Message.ArgumentType.ARRAY); out[level].append(s[0]); } } } else if (p.getRawType().equals(Variant.class)) { - out[level].append('v'); + out[level].append((char) Message.ArgumentType.VARIANT); } else if (DBusInterface.class.isAssignableFrom((Class) p.getRawType())) { - out[level].append('o'); + out[level].append((char) Message.ArgumentType.OBJECT_PATH); } else if (Tuple.class.isAssignableFrom((Class) p.getRawType())) { Type[] ts = p.getActualTypeArguments(); @@ -151,39 +151,43 @@ public class Marshalling throw new DBusException("Exporting non-exportable parameterized type "+c); } - else if (c.equals(Byte.class)) out[level].append('y'); - else if (c.equals(Byte.TYPE)) out[level].append('y'); - else if (c.equals(Boolean.class)) out[level].append('b'); - else if (c.equals(Boolean.TYPE)) out[level].append('b'); - else if (c.equals(Short.class)) out[level].append('n'); - else if (c.equals(Short.TYPE)) out[level].append('n'); - else if (c.equals(UInt16.class)) out[level].append('q'); - else if (c.equals(Integer.class)) out[level].append('i'); - else if (c.equals(Integer.TYPE)) out[level].append('i'); - else if (c.equals(UInt32.class)) out[level].append('u'); - else if (c.equals(Long.class)) out[level].append('x'); - else if (c.equals(Long.TYPE)) out[level].append('x'); - else if (c.equals(UInt64.class)) out[level].append('t'); - else if (c.equals(Double.class)) out[level].append('d'); - else if (c.equals(Double.TYPE)) out[level].append('d'); - else if (c.equals(String.class)) out[level].append('s'); - else if (c.equals(Variant.class)) out[level].append('v'); + else if (c.equals(Byte.class)) out[level].append((char) Message.ArgumentType.BYTE); + else if (c.equals(Byte.TYPE)) out[level].append((char) Message.ArgumentType.BYTE); + else if (c.equals(Boolean.class)) out[level].append((char) Message.ArgumentType.BOOLEAN); + else if (c.equals(Boolean.TYPE)) out[level].append((char) Message.ArgumentType.BOOLEAN); + else if (c.equals(Short.class)) out[level].append((char) Message.ArgumentType.INT16); + else if (c.equals(Short.TYPE)) out[level].append((char) Message.ArgumentType.INT16); + else if (c.equals(UInt16.class)) out[level].append((char) Message.ArgumentType.UINT16); + else if (c.equals(Integer.class)) out[level].append((char) Message.ArgumentType.INT32); + else if (c.equals(Integer.TYPE)) out[level].append((char) Message.ArgumentType.INT32); + else if (c.equals(UInt32.class)) out[level].append((char) Message.ArgumentType.UINT32); + else if (c.equals(Long.class)) out[level].append((char) Message.ArgumentType.INT64); + else if (c.equals(Long.TYPE)) out[level].append((char) Message.ArgumentType.INT64); + else if (c.equals(UInt64.class)) out[level].append((char) Message.ArgumentType.UINT64); + else if (c.equals(Double.class)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(Double.TYPE)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(Float.class) && AbstractConnection.FLOAT_SUPPORT) out[level].append((char) Message.ArgumentType.FLOAT); + else if (c.equals(Float.class)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(Float.TYPE) && AbstractConnection.FLOAT_SUPPORT) out[level].append((char) Message.ArgumentType.FLOAT); + else if (c.equals(Float.TYPE)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(String.class)) out[level].append((char) Message.ArgumentType.STRING); + else if (c.equals(Variant.class)) out[level].append((char) Message.ArgumentType.VARIANT); else if (c instanceof Class && - DBusInterface.class.isAssignableFrom((Class) c)) out[level].append('o'); + DBusInterface.class.isAssignableFrom((Class) c)) out[level].append((char) Message.ArgumentType.OBJECT_PATH); else if (c instanceof Class && - Path.class.equals((Class) c)) out[level].append('o'); + Path.class.equals((Class) c)) out[level].append((char) Message.ArgumentType.OBJECT_PATH); else if (c instanceof Class && ((Class) c).isArray()) { if (Type.class.equals(((Class) c).getComponentType())) - out[level].append('g'); + out[level].append((char) Message.ArgumentType.SIGNATURE); else { - out[level].append('a'); + out[level].append((char) Message.ArgumentType.ARRAY); String[] s = recursiveGetDBusType(((Class) c).getComponentType(), false, level+1); if (s.length != 1) throw new DBusException("Multi-valued array types not permitted"); out[level].append(s[0]); } } else if (c instanceof Class && Struct.class.isAssignableFrom((Class) c)) { - out[level].append('('); + out[level].append((char) Message.ArgumentType.STRUCT1); Type[] ts = Container.getTypeCache(c); if (null == ts) { Field[] fs = ((Class) c).getDeclaredFields(); @@ -230,6 +234,8 @@ public class Marshalling throw new DBusException("Exporting non-exportable type "+c); } + if (Debug.debug) Debug.print(Debug.VERBOSE, "Converted Java type: "+c+" to D-Bus Type: "+out[level]); + return new String[] { out[level].toString() }; } @@ -248,11 +254,11 @@ public class Marshalling int i = 0; for (; i < dbus.length() && (-1 == limit || limit > rv.size()); i++) switch(dbus.charAt(i)) { - case '(': + case Message.ArgumentType.STRUCT1: int j = i+1; for (int c = 1; c > 0; j++) { if (')' == dbus.charAt(j)) c--; - else if ('(' == dbus.charAt(j)) c++; + else if (Message.ArgumentType.STRUCT1 == dbus.charAt(j)) c++; } Vector<Type> contained = new Vector<Type>(); @@ -260,8 +266,8 @@ public class Marshalling rv.add(new DBusStructType(contained.toArray(new Type[0]))); i = j; break; - case 'a': - if ('{' == dbus.charAt(i+1)) { + case Message.ArgumentType.ARRAY: + if (Message.ArgumentType.DICT_ENTRY1 == dbus.charAt(i+1)) { contained = new Vector<Type>(); c = getJavaType(dbus.substring(i+2), contained, 2); rv.add(new DBusMapType(contained.get(0), contained.get(1))); @@ -273,46 +279,49 @@ public class Marshalling i += c; } break; - case 'v': + case Message.ArgumentType.VARIANT: rv.add(Variant.class); break; - case 'b': + case Message.ArgumentType.BOOLEAN: rv.add(Boolean.class); break; - case 'n': + case Message.ArgumentType.INT16: rv.add(Short.class); break; - case 'y': + case Message.ArgumentType.BYTE: rv.add(Byte.class); break; - case 'o': + case Message.ArgumentType.OBJECT_PATH: rv.add(DBusInterface.class); break; - case 'q': + case Message.ArgumentType.UINT16: rv.add(UInt16.class); break; - case 'i': + case Message.ArgumentType.INT32: rv.add(Integer.class); break; - case 'u': + case Message.ArgumentType.UINT32: rv.add(UInt32.class); break; - case 'x': + case Message.ArgumentType.INT64: rv.add(Long.class); break; - case 't': + case Message.ArgumentType.UINT64: rv.add(UInt64.class); break; - case 'd': + case Message.ArgumentType.DOUBLE: rv.add(Double.class); break; - case 's': + case Message.ArgumentType.FLOAT: + rv.add(Float.class); + break; + case Message.ArgumentType.STRING: rv.add(String.class); break; - case 'g': + case Message.ArgumentType.SIGNATURE: rv.add(Type[].class); break; - case '{': + case Message.ArgumentType.DICT_ENTRY1: rv.add(Map.Entry.class); contained = new Vector<Type>(); c = getJavaType(dbus.substring(i+1), contained, 2); @@ -323,7 +332,7 @@ public class Marshalling } return i; } catch (IndexOutOfBoundsException IOOBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOOBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOOBe); throw new DBusException("Failed to parse DBus type signature: "+dbus); } } @@ -335,7 +344,7 @@ public class Marshalling * @throws DBusException Thrown if there is an error in converting the objects. */ @SuppressWarnings("unchecked") - public static Object[] convertParameters(Object[] parameters, Type[] types, DBusConnection conn) throws DBusException + public static Object[] convertParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws DBusException { if (null == parameters) return null; for (int i = 0; i < parameters.length; i++) { @@ -384,7 +393,7 @@ public class Marshalling return parameters; } @SuppressWarnings("unchecked") - static Object deSerializeParameter(Object parameter, Type type, DBusConnection conn) throws Exception + static Object deSerializeParameter(Object parameter, Type type, AbstractConnection conn) throws Exception { if (null == parameter) return null; @@ -450,6 +459,11 @@ public class Marshalling ts, conn); } + // correct floats if appropriate + if (type.equals(Float.class) || type.equals(Float.TYPE)) + if (!(parameter instanceof Float)) + parameter = ((Number) parameter).floatValue(); + // make sure arrays are in the correct format if (parameter instanceof Object[] || parameter instanceof List || @@ -470,6 +484,14 @@ public class Marshalling } else if (type instanceof Class && ((Class) type).isArray()) { Class cc = ((Class) type).getComponentType(); + if ((cc.equals(Float.class) || cc.equals(Float.TYPE)) + && (parameter instanceof double[])) { + double[] tmp1 = (double[]) parameter; + float[] tmp2 = new float[tmp1.length]; + for (int i = 0; i < tmp1.length; i++) + tmp2[i] = (float) tmp1[i]; + parameter = tmp2; + } Object o = Array.newInstance(cc, 0); parameter = ArrayFrob.convert(parameter, o.getClass()); @@ -477,7 +499,7 @@ public class Marshalling } return parameter; } - static Object[] deSerializeParameters(Object[] parameters, Type[] types, DBusConnection conn) throws Exception + static Object[] deSerializeParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws Exception { if (null == parameters) return null; for (int i = 0; i < parameters.length; i++) { @@ -501,7 +523,7 @@ public class Marshalling System.arraycopy(parameters, i + newtypes.length, compress, i+1, parameters.length - i - newtypes.length); parameters = compress; } catch (ArrayIndexOutOfBoundsException AIOOBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); throw new DBusException("Not enough elements to create custom object from serialized data ("+(parameters.length-i)+" < "+(newtypes.length)+")"); } } diff --git a/org/freedesktop/dbus/Message.java b/org/freedesktop/dbus/Message.java index 138bb14..3666818 100644 --- a/org/freedesktop/dbus/Message.java +++ b/org/freedesktop/dbus/Message.java @@ -174,6 +174,7 @@ public class Message big = (Endian.BIG == endian); bytecounter = 0; serial = ++globalserial; + if (Debug.debug) Debug.print(Debug.DEBUG, "Creating message with serial "+serial); this.type = type; this.flags = flags; preallocate(4); @@ -408,7 +409,7 @@ public class Message try { args = getParameters(); } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); } if (null == args || 0 == args.length) sb.append('}'); @@ -473,11 +474,11 @@ public class Message appendint(((Boolean) data).booleanValue() ? 1 : 0, 4); break; case ArgumentType.DOUBLE: - long l = Double.doubleToLongBits((Double) data); + long l = Double.doubleToLongBits(((Number) data).doubleValue()); appendint(l, 8); break; case ArgumentType.FLOAT: - int rf = Float.floatToIntBits((Float) data); + int rf = Float.floatToIntBits(((Number) data).floatValue()); appendint(rf, 4); break; case ArgumentType.UINT32: @@ -513,7 +514,7 @@ public class Message try { payloadbytes = payload.getBytes("UTF-8"); } catch (UnsupportedEncodingException UEe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); throw new DBusException("System does not support UTF-8 encoding"); } if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending String of length "+payloadbytes.length); @@ -576,10 +577,14 @@ public class Message break; case ArgumentType.DOUBLE: primbuf = new byte[len*algn]; - for (int j = 0, k = 0; j < len; j++, k += algn) - marshallint( - Double.doubleToRawLongBits(((double[])data)[j]), - primbuf, k, algn); + if (data instanceof float[]) + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint(Double.doubleToRawLongBits(((float[])data)[j]), + primbuf, k, algn); + else + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint(Double.doubleToRawLongBits(((double[])data)[j]), + primbuf, k, algn); break; case ArgumentType.FLOAT: primbuf = new byte[len*algn]; @@ -670,7 +675,7 @@ public class Message } return i; } catch (ClassCastException CCe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, CCe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, CCe); throw new MarshallingException("Trying to marshall to unconvertable type (from "+data.getClass().getName()+" to "+sigb[sigofs]+")"); } } @@ -948,7 +953,7 @@ public class Message try { rv = new String(buf, ofs[1], length, "UTF-8"); } catch (UnsupportedEncodingException UEe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); throw new DBusException("System does not support UTF-8 encoding"); } ofs[1] += length + 1; diff --git a/org/freedesktop/dbus/MessageReader.java b/org/freedesktop/dbus/MessageReader.java index 75aa1de..d90d3df 100644 --- a/org/freedesktop/dbus/MessageReader.java +++ b/org/freedesktop/dbus/MessageReader.java @@ -135,14 +135,14 @@ public class MessageReader try { m.populate(buf, header, body); } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); buf = null; tbuf = null; body = null; header = null; throw DBe; } catch (RuntimeException Re) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Re); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Re); buf = null; tbuf = null; body = null; diff --git a/org/freedesktop/dbus/MethodCall.java b/org/freedesktop/dbus/MethodCall.java index c275d84..75d333a 100644 --- a/org/freedesktop/dbus/MethodCall.java +++ b/org/freedesktop/dbus/MethodCall.java @@ -23,16 +23,19 @@ public class MethodCall extends Message { super(Message.Endian.BIG, Message.MessageType.METHOD_CALL, flags); - if (null == dest || null == member || null == path) + if (null == member || null == path) throw new MessageFormatException("Must specify destination, path and function name to MethodCalls."); headers.put(Message.HeaderField.PATH,path); - headers.put(Message.HeaderField.DESTINATION,dest); headers.put(Message.HeaderField.MEMBER,member); Vector<Object> hargs = new Vector<Object>(); hargs.add(new Object[] { Message.HeaderField.PATH, new Object[] { ArgumentType.OBJECT_PATH_STRING, path } }); - hargs.add(new Object[] { Message.HeaderField.DESTINATION, new Object[] { ArgumentType.STRING_STRING, dest } }); + + if (null != dest) { + headers.put(Message.HeaderField.DESTINATION,dest); + hargs.add(new Object[] { Message.HeaderField.DESTINATION, new Object[] { ArgumentType.STRING_STRING, dest } }); + } if (null != iface) { hargs.add(new Object[] { Message.HeaderField.INTERFACE, new Object[] { ArgumentType.STRING_STRING, iface } }); diff --git a/org/freedesktop/dbus/MethodReturn.java b/org/freedesktop/dbus/MethodReturn.java index 8fcb4b9..26799a5 100644 --- a/org/freedesktop/dbus/MethodReturn.java +++ b/org/freedesktop/dbus/MethodReturn.java @@ -21,15 +21,16 @@ public class MethodReturn extends Message { super(Message.Endian.BIG, Message.MessageType.METHOD_RETURN, (byte) 0); - if (null == dest) - throw new MessageFormatException("Must specify destination to Method Returns."); - headers.put(Message.HeaderField.DESTINATION,dest); headers.put(Message.HeaderField.REPLY_SERIAL,replyserial); Vector<Object> hargs = new Vector<Object>(); - hargs.add(new Object[] { Message.HeaderField.DESTINATION, new Object[] { ArgumentType.STRING_STRING, dest } }); hargs.add(new Object[] { Message.HeaderField.REPLY_SERIAL, new Object[] { ArgumentType.UINT32_STRING, replyserial } }); + if (null != dest) { + headers.put(Message.HeaderField.DESTINATION,dest); + hargs.add(new Object[] { Message.HeaderField.DESTINATION, new Object[] { ArgumentType.STRING_STRING, dest } }); + } + if (null != sig) { hargs.add(new Object[] { Message.HeaderField.SIGNATURE, new Object[] { ArgumentType.SIGNATURE_STRING, sig } }); headers.put(Message.HeaderField.SIGNATURE,sig); @@ -38,7 +39,7 @@ public class MethodReturn extends Message byte[] blen = new byte[4]; appendBytes(blen); - append("ua(yv)", ++serial, hargs.toArray()); + append("ua(yv)", serial, hargs.toArray()); pad((byte)8); long c = bytecounter; diff --git a/org/freedesktop/dbus/RemoteInvocationHandler.java b/org/freedesktop/dbus/RemoteInvocationHandler.java index ce10bde..ee70984 100644 --- a/org/freedesktop/dbus/RemoteInvocationHandler.java +++ b/org/freedesktop/dbus/RemoteInvocationHandler.java @@ -30,7 +30,7 @@ import cx.ath.matthew.debug.Debug; class RemoteInvocationHandler implements InvocationHandler { - public static Object convertRV(String sig, Object[] rp, Method m, DBusConnection conn) throws DBusException + public static Object convertRV(String sig, Object[] rp, Method m, AbstractConnection conn) throws DBusException { Class c = m.getReturnType(); @@ -50,7 +50,7 @@ class RemoteInvocationHandler implements InvocationHandler new Type[] { m.getGenericReturnType() }, conn); } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusExecutionException("Wrong return type (failed to de-serialize correct types: "+e.getMessage()+")"); } @@ -63,19 +63,19 @@ class RemoteInvocationHandler implements InvocationHandler try { rp = Marshalling.deSerializeParameters(rp, ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments(), conn); } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusExecutionException("Wrong return type (failed to de-serialize correct types: "+e.getMessage()+")"); } Constructor cons = c.getConstructors()[0]; try { return cons.newInstance(rp); } catch (Exception e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusException(e.getMessage()); } } } - public static Object executeRemoteMethod(RemoteObject ro, Method m, DBusConnection conn, boolean async, Object... args) throws DBusExecutionException + public static Object executeRemoteMethod(RemoteObject ro, Method m, AbstractConnection conn, boolean async, Object... args) throws DBusExecutionException { Type[] ts = m.getGenericParameterTypes(); String sig = null; @@ -94,13 +94,14 @@ class RemoteInvocationHandler implements InvocationHandler if (null == ro.iface) call = new MethodCall(ro.busname, ro.objectpath, null, m.getName(),flags, sig, args); else - call = new MethodCall(ro.busname, ro.objectpath, DBusConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), m.getName(), flags, sig, args); + call = new MethodCall(ro.busname, ro.objectpath, AbstractConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), m.getName(), flags, sig, args); } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); throw new DBusExecutionException("Failed to construct outgoing method call: "+DBe.getMessage()); } if (null == conn.outgoing) throw new NotConnected("Not Connected"); synchronized (conn.outgoing) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding method call to outgoing queue"); conn.outgoing.add(call); } @@ -118,14 +119,14 @@ class RemoteInvocationHandler implements InvocationHandler try { return convertRV(reply.getSig(), reply.getParameters(), m, conn); } catch (DBusException e) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); throw new DBusExecutionException(e.getMessage()); } } - DBusConnection conn; + AbstractConnection conn; RemoteObject remote; - public RemoteInvocationHandler(DBusConnection conn, RemoteObject remote) + public RemoteInvocationHandler(AbstractConnection conn, RemoteObject remote) { this.remote = remote; this.conn = conn; diff --git a/org/freedesktop/dbus/RemoteObject.java b/org/freedesktop/dbus/RemoteObject.java index 8460586..46b7025 100644 --- a/org/freedesktop/dbus/RemoteObject.java +++ b/org/freedesktop/dbus/RemoteObject.java @@ -27,14 +27,22 @@ class RemoteObject { if (!(o instanceof RemoteObject)) return false; RemoteObject them = (RemoteObject) o; - if (!them.busname.equals(this.busname)) return false; + if (!them.objectpath.equals(this.objectpath)) return false; - if (null != this.iface && !them.iface.equals(this.iface)) return false; + + if (null == this.busname && null != them.busname) return false; + if (null != this.busname && null == them.busname) return false; + if (null != them.busname && !them.busname.equals(this.busname)) return false; + + if (null == this.iface && null != them.iface) return false; + if (null != this.iface && null == them.iface) return false; + if (null != them.iface && !them.iface.equals(this.iface)) return false; + return true; } public int hashCode() { - return busname.hashCode() + objectpath.hashCode() + + return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + (null == iface ? 0 : iface.hashCode()); } public boolean autoStarting() { return autostart; } diff --git a/org/freedesktop/dbus/Transport.java b/org/freedesktop/dbus/Transport.java index 783f173..159046c 100644 --- a/org/freedesktop/dbus/Transport.java +++ b/org/freedesktop/dbus/Transport.java @@ -10,14 +10,18 @@ */ package org.freedesktop.dbus; -import java.net.InetSocketAddress; -import java.net.Socket; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.text.Collator; +import java.util.Random; import cx.ath.matthew.unix.UnixSocket; +import cx.ath.matthew.unix.UnixServerSocket; import cx.ath.matthew.unix.UnixSocketAddress; import cx.ath.matthew.utils.Hexdump; import cx.ath.matthew.debug.Debug; @@ -29,6 +33,14 @@ public class Transport public MessageReader min; public MessageWriter mout; public Transport() {} + public static String genGUID() + { + Random r = new Random(); + byte[] buf = new byte[16]; + r.nextBytes(buf); + String guid = Hexdump.toHex(buf); + return guid.replaceAll(" ", ""); + } public Transport(BusAddress address) throws IOException { connect(address); @@ -42,23 +54,45 @@ public class Transport out.write(new byte[] { 0 }); BufferedReader r = new BufferedReader(new InputStreamReader(in)); if ("unix".equals(address.getType())) { - UnixSystem uns = new UnixSystem(); - long uid = uns.getUid(); - String Uid = Hexdump.toHex((""+uid).getBytes()).replaceAll(" ",""); - out.write(("AUTH EXTERNAL "+Uid+"\r\n").getBytes()); - if (Debug.debug) Debug.print(Debug.VERBOSE, "AUTH EXTERNAL "+Uid+"\r\n"); + if (null == address.getParameter("listen")) { + UnixSystem uns = new UnixSystem(); + long uid = uns.getUid(); + String Uid = Hexdump.toHex((""+uid).getBytes()).replaceAll(" ",""); + out.write(("AUTH EXTERNAL "+Uid+"\r\n").getBytes()); + if (Debug.debug) Debug.print(Debug.VERBOSE, "AUTH EXTERNAL "+Uid+"\r\n"); + } else { + String s = r.readLine(); + if (Debug.debug) Debug.print(Debug.VERBOSE, "reading:"+s); + String guid = address.getParameter("guid"); + if (null == guid) guid = genGUID(); + out.write(("OK "+guid+"\r\n").getBytes()); + if (Debug.debug) Debug.print(Debug.VERBOSE, "OK "+guid); + s = r.readLine(); + if (Debug.debug) Debug.print(Debug.VERBOSE, "reading"+s); + if ("BEGIN".equals(s)) return true; + } } else { out.write(("AUTH DBUS_COOKIE_SHA1\r\n").getBytes()); } String s = r.readLine(); if (Debug.debug) Debug.print(Debug.VERBOSE, s); String[] reply=s.split(" "); - if (!"OK".equals(reply[0])) return false; - if (null == address.getParameter("guid") || reply[1].equals(address.getParameter("guid"))) { + Collator col = Collator.getInstance(); + col.setDecomposition(Collator.FULL_DECOMPOSITION); + col.setStrength(Collator.PRIMARY); + if (0 != col.compare("OK", reply[0])) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "reply[0] = `"+reply[0]+"'"); + return false; + } + if (null == address.getParameter("guid") || (reply.length > 1 && reply[1].equals(address.getParameter("guid")))) { if (Debug.debug) Debug.print(Debug.VERBOSE, "BEGIN\r\n"); out.write("BEGIN\r\n".getBytes()); return true; - } + } else if (Debug.debug) + if (reply.length == 1) + Debug.print(Debug.ERR, "GUID Mismatch, expecting: "+address.getParameter("guid")+", got no guid"); + else + Debug.print(Debug.ERR, "GUID Mismatch, expecting: "+address.getParameter("guid")+", got: "+reply[1]); return false; } public void connect(String address) throws IOException @@ -71,19 +105,36 @@ public class Transport this.address = address; OutputStream out = null; InputStream in = null; + UnixSocket us = null; if ("unix".equals(address.getType())) { - UnixSocket us = new UnixSocket(); - us.setBlocking(false); - if (null != address.getParameter("abstract")) - us.connect(new UnixSocketAddress(address.getParameter("abstract"), true)); - else if (null != address.getParameter("path")) - us.connect(new UnixSocketAddress(address.getParameter("path"), false)); + if (null != address.getParameter("listen")) { + UnixServerSocket uss = new UnixServerSocket(); + if (null != address.getParameter("abstract")) + uss.bind(new UnixSocketAddress(address.getParameter("abstract"), true)); + else if (null != address.getParameter("path")) + uss.bind(new UnixSocketAddress(address.getParameter("path"), false)); + us = uss.accept(); + } else { + us = new UnixSocket(); + if (null != address.getParameter("abstract")) + us.connect(new UnixSocketAddress(address.getParameter("abstract"), true)); + else if (null != address.getParameter("path")) + us.connect(new UnixSocketAddress(address.getParameter("path"), false)); + } us.setPassCred(true); in = us.getInputStream(); out = us.getOutputStream(); } else if ("tcp".equals(address.getType())) { - Socket s = new Socket(); - s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); + Socket s = null; + if (null != address.getParameter("listen")) { + ServerSocket ss = new ServerSocket(); + ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); + s = ss.accept(); + } else { + s = new Socket(); + s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); + } + s.setSoTimeout(0); in = s.getInputStream(); out = s.getOutputStream(); } else { @@ -95,6 +146,10 @@ public class Transport out.close(); throw new IOException("Failed to auth"); } + if (null != us) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting non-blocking on UnixSocket"); + us.setBlocking(false); + } mout = new MessageWriter(out); min = new MessageReader(in); } diff --git a/org/freedesktop/dbus/Variant.java b/org/freedesktop/dbus/Variant.java index d0bbf52..73b52d0 100644 --- a/org/freedesktop/dbus/Variant.java +++ b/org/freedesktop/dbus/Variant.java @@ -42,7 +42,7 @@ public class Variant<T> throw new IllegalArgumentException("Can't wrap a multi-valued type in a Variant ("+type+")"); this.sig = ss[0]; } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); throw new IllegalArgumentException("Can't wrap "+o.getClass()+" in an unqualified Variant ("+DBe.getMessage()+")"); } this.o = o; @@ -63,7 +63,7 @@ public class Variant<T> throw new IllegalArgumentException("Can't wrap a multi-valued type in a Variant ("+type+")"); this.sig = ss[0]; } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); throw new IllegalArgumentException("Can't wrap a "+type+" in a Variant ("+DBe.getMessage()+")"); } this.o = o; @@ -85,7 +85,7 @@ public class Variant<T> throw new IllegalArgumentException("Can't wrap multiple or no types in a Variant ("+sig+")"); this.type = ts.get(0); } catch (DBusException DBe) { - if (DBusConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); throw new IllegalArgumentException("Can't wrap a "+sig+" in a Variant ("+DBe.getMessage()+")"); } this.o = o; diff --git a/org/freedesktop/dbus/exceptions/FatalDBusException.java b/org/freedesktop/dbus/exceptions/FatalDBusException.java new file mode 100644 index 0000000..3fca524 --- /dev/null +++ b/org/freedesktop/dbus/exceptions/FatalDBusException.java @@ -0,0 +1,20 @@ +/* + 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 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.exceptions; + +@SuppressWarnings("serial") +public class FatalDBusException extends DBusException implements FatalException +{ + public FatalDBusException(String message) + { + super(message); + } +} diff --git a/org/freedesktop/dbus/exceptions/InternalMessageException.java b/org/freedesktop/dbus/exceptions/InternalMessageException.java index 03b0a9e..ff6e80f 100644 --- a/org/freedesktop/dbus/exceptions/InternalMessageException.java +++ b/org/freedesktop/dbus/exceptions/InternalMessageException.java @@ -11,7 +11,7 @@ package org.freedesktop.dbus.exceptions; @SuppressWarnings("serial") -public class InternalMessageException extends DBusExecutionException +public class InternalMessageException extends DBusExecutionException implements NonFatalException { public InternalMessageException(String message) { diff --git a/org/freedesktop/dbus/exceptions/NotConnected.java b/org/freedesktop/dbus/exceptions/NotConnected.java index 9b6f553..2367209 100644 --- a/org/freedesktop/dbus/exceptions/NotConnected.java +++ b/org/freedesktop/dbus/exceptions/NotConnected.java @@ -14,7 +14,7 @@ package org.freedesktop.dbus.exceptions; * Thrown if a DBus action is called when not connected to the Bus. */ @SuppressWarnings("serial") -public class NotConnected extends DBusExecutionException +public class NotConnected extends DBusExecutionException implements FatalException { public NotConnected(String message) { diff --git a/org/freedesktop/dbus/test/TestRemoteInterface.java b/org/freedesktop/dbus/test/TestRemoteInterface.java index 03a042e..6556071 100644 --- a/org/freedesktop/dbus/test/TestRemoteInterface.java +++ b/org/freedesktop/dbus/test/TestRemoteInterface.java @@ -43,4 +43,6 @@ public interface TestRemoteInterface extends DBusInterface public void sig(Type[] s); @Description("Testing object paths as Path objects") public void newpathtest(Path p); + @Description("Testing the float type") + public float testfloat(float[] f); } diff --git a/org/freedesktop/dbus/test/test.java b/org/freedesktop/dbus/test/test.java index 18d85c1..29e6c0c 100644 --- a/org/freedesktop/dbus/test/test.java +++ b/org/freedesktop/dbus/test/test.java @@ -59,6 +59,16 @@ class testclass implements TestRemoteInterface, TestRemoteInterface2, TestSignal { this.conn = conn; } + public float testfloat(float[] f) + { + if (f.length < 4 || + f[0] != 17.093f || + f[1] != -23f || + f[2] != 0.0f || + f[3] != 31.42f) + test.fail("testfloat got incorrect array"); + return f[0]; + } public void newpathtest(Path p) { if (!p.toString().equals("/new/path/test")) @@ -438,6 +448,9 @@ public class test fail("getName return value incorrect"); System.out.println("sending it to sleep"); tri.waitawhile(); + System.out.println("testing floats"); + if (17.093f != tri.testfloat(new float[] { 17.093f, -23f, 0.0f, 31.42f })) + fail("testfloat returned the wrong thing"); System.out.println("frobnicating"); List<Long> ls = new Vector<Long>(); ls.add(2L); diff --git a/org/freedesktop/dbus/test/test_p2p_client.java b/org/freedesktop/dbus/test/test_p2p_client.java new file mode 100644 index 0000000..aa6d64f --- /dev/null +++ b/org/freedesktop/dbus/test/test_p2p_client.java @@ -0,0 +1,42 @@ +/* + 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 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.test; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.InputStreamReader; + +import org.freedesktop.DBus; +import org.freedesktop.dbus.DirectConnection; + +public class test_p2p_client +{ + public static void main(String[] args) throws Exception + { + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream("address"))); + String address = r.readLine(); + DirectConnection dc = new DirectConnection(address); + System.out.println("Connected"); + TestRemoteInterface tri = (TestRemoteInterface) dc.getRemoteObject("/Test"); + System.out.println(tri.getName()); + System.out.println(tri.testfloat(new float[] { 17.093f, -23f, 0.0f, 31.42f })); + + try { + tri.throwme(); + } catch (TestException Te) { + System.out.println("Caught TestException"); + } + ((DBus.Peer) tri).Ping(); + System.out.println(((DBus.Introspectable) tri).Introspect()); + dc.disconnect(); + System.out.println("Disconnected"); + } +} diff --git a/org/freedesktop/dbus/test/test_p2p_server.java b/org/freedesktop/dbus/test/test_p2p_server.java new file mode 100644 index 0000000..04c0856 --- /dev/null +++ b/org/freedesktop/dbus/test/test_p2p_server.java @@ -0,0 +1,72 @@ +/* + 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 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.test; + +import java.lang.reflect.Type; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.freedesktop.dbus.DirectConnection; +import org.freedesktop.dbus.Path; +import org.freedesktop.dbus.UInt16; + +public class test_p2p_server implements TestRemoteInterface +{ + public String getName() + { + System.out.println("getName called"); + return "Peer2Peer Server"; + } + public <T> int frobnicate(List<Long> n, Map<String,Map<UInt16,Short>> m, T v) + { + return 3; + } + public void throwme() throws TestException + { + System.out.println("throwme called"); + throw new TestException("BOO"); + } + public void waitawhile() + { + return; + } + public int overload() + { + return 1; + } + public void sig(Type[] s) + { + } + public void newpathtest(Path p) + { + } + public boolean isRemote() { return false; } + public float testfloat(float[] f) + { + System.out.println("got float: "+Arrays.toString(f)); + return f[0]; + } + + public static void main(String[] args) throws Exception + { + String address = DirectConnection.createDynamicSession(); + PrintWriter w = new PrintWriter(new FileOutputStream("address")); + w.println(address.replaceAll("listen=true,","")); + w.flush(); + w.close(); + DirectConnection dc = new DirectConnection(address); + System.out.println("Connected"); + dc.exportObject("/Test", new test_p2p_server()); + } +} |