summaryrefslogtreecommitdiff
path: root/org/freedesktop/dbus/RemoteInvocationHandler.java
blob: 89e60e8d00e294df99584bf23df0df96513b17bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
   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.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;

import org.freedesktop.DBus;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.exceptions.NotConnected;

import cx.ath.matthew.debug.Debug;

class RemoteInvocationHandler implements InvocationHandler
{
   public static final int CALL_TYPE_SYNC = 0;
   public static final int CALL_TYPE_ASYNC = 1;
   public static final int CALL_TYPE_CALLBACK = 2;
   public static Object convertRV(String sig, Object[] rp, Method m, AbstractConnection conn) throws DBusException
   {
      Class c = m.getReturnType();

      if (null == rp) { 
         if(null == c || Void.TYPE.equals(c)) return null;
         else throw new DBusExecutionException("Wrong return type (got void, expected a value)");
      }

      switch (rp.length) {
         case 0:
            if (null == c || Void.TYPE.equals(c))
               return null;
            else throw new DBusExecutionException("Wrong return type (got void, expected a value)");
         case 1:
            try { 
               rp = Marshalling.deSerializeParameters(rp, 
                     new Type[] { m.getGenericReturnType() }, conn);
            }
            catch (Exception 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()+")");
            }

            return rp[0];
         default:

            // check we are meant to return multiple values
            if (!Tuple.class.isAssignableFrom(c))
               throw new DBusExecutionException("Wrong return type (not expecting Tuple)");
            try {
               rp = Marshalling.deSerializeParameters(rp, ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments(), conn);
            } catch (Exception 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 (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
               throw new DBusException(e.getMessage());
            }
      }
   }
   public static Object executeRemoteMethod(RemoteObject ro, Method m, AbstractConnection conn, int syncmethod, CallbackHandler callback, Object... args) throws DBusExecutionException
   {
      Type[] ts = m.getGenericParameterTypes();
      String sig = null;
      if (ts.length > 0) try {
         sig = Marshalling.getDBusType(ts);
         args = Marshalling.convertParameters(args, ts, conn);
      } catch (DBusException DBe) {
         throw new DBusExecutionException("Failed to construct D-Bus type: "+DBe.getMessage());
      }
      MethodCall call;
      byte flags = 0;
      if (!ro.autostart) flags |= Message.Flags.NO_AUTO_START;
      if (syncmethod == CALL_TYPE_ASYNC) flags |= Message.Flags.ASYNC;
      if (m.isAnnotationPresent(DBus.Method.NoReply.class)) flags |= Message.Flags.NO_REPLY_EXPECTED;
      try {
         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, AbstractConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), m.getName(), flags, sig, args);
      } catch (DBusException 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");

      switch (syncmethod) {
         case CALL_TYPE_ASYNC: 
            conn.queueOutgoing(call);
            return new DBusAsyncReply(call, m, conn);
         case CALL_TYPE_CALLBACK:
             synchronized (conn.pendingCallbacks) {
                if (Debug.debug) Debug.print(Debug.VERBOSE, "Queueing Callback "+callback+" for "+call);
                conn.pendingCallbacks.put(call, callback);
                conn.pendingCallbackReplys.put(call, new DBusAsyncReply(call, m, conn));
             }
             conn.queueOutgoing(call);
             return null;
         case CALL_TYPE_SYNC:
             conn.queueOutgoing(call);
             break;
      }

      // get reply
      if (m.isAnnotationPresent(DBus.Method.NoReply.class)) return null;

      Message reply = call.getReply();
      if (null == reply) throw new DBus.Error.NoReply("No reply within specified time");
               
      if (reply instanceof Error)
         ((Error) reply).throwException();

      try {
         return convertRV(reply.getSig(), reply.getParameters(), m, conn);
      } catch (DBusException e) {
         if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
         throw new DBusExecutionException(e.getMessage());
      }
   }

   AbstractConnection conn;
   RemoteObject remote;
   public RemoteInvocationHandler(AbstractConnection conn, RemoteObject remote)
   {
      this.remote = remote;
      this.conn = conn;
   }
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
      if (method.getName().equals("isRemote")) return true;
      else if (method.getName().equals("clone")) return null;
      else if (method.getName().equals("equals")) {
         try { 
            if (1 == args.length)   
               return new Boolean(remote.equals(((RemoteInvocationHandler) Proxy.getInvocationHandler(args[0])).remote));
         } catch (IllegalArgumentException IAe) {
            return Boolean.FALSE;
         }
      }
      else if (method.getName().equals("finalize")) return null;
      else if (method.getName().equals("getClass")) return DBusInterface.class;
      else if (method.getName().equals("hashCode")) return remote.hashCode();
      else if (method.getName().equals("notify")) {
         remote.notify();
         return null;
      } else if (method.getName().equals("notifyAll")) {
         remote.notifyAll();
         return null;
      } else if (method.getName().equals("wait"))  {
         if (0 == args.length) remote.wait();
         else if (1 == args.length 
               && args[0] instanceof Long) remote.wait((Long) args[0]);
         else if (2 == args.length 
               && args[0] instanceof Long
               && args[1] instanceof Integer) 
            remote.wait((Long) args[0], (Integer) args[1]);
         if (args.length <= 2)
            return null;
      }
      else if (method.getName().equals("toString"))
         return remote.toString();

      return executeRemoteMethod(remote, method, conn, CALL_TYPE_SYNC, null, args);
   }
}