Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 1 | // +build android |
| 2 | |
| 3 | package main |
| 4 | |
| 5 | import ( |
| 6 | "flag" |
| 7 | "fmt" |
| 8 | "unsafe" |
| 9 | |
| 10 | "veyron2/ipc" |
| 11 | "veyron2/rt" |
| 12 | "veyron2/verror" |
| 13 | ) |
| 14 | |
| 15 | // #include <stdlib.h> |
| 16 | // #include "jni_wrapper.h" |
| 17 | import "C" |
| 18 | |
| 19 | var ( |
| 20 | // Global reference for com.veyron2.ipc.VeyronException class. |
| 21 | jVeyronExceptionClass C.jclass |
| 22 | // Global reference for com.veyron.runtimes.google.ipc.IDLInvoker class. |
| 23 | jIDLInvokerClass C.jclass |
| 24 | // Global reference for java.lang.Throwable class. |
| 25 | jThrowableClass C.jclass |
| 26 | // Global reference for java.lang.String class. |
| 27 | jStringClass C.jclass |
| 28 | ) |
| 29 | |
| 30 | // refs stores references to instances of various Go types, namely instances |
| 31 | // that are referenced only by the Java code. The only purpose of this store |
| 32 | // is to prevent Go runtime from garbage collecting those instances. |
| 33 | var refs = newSafeSet() |
| 34 | |
| 35 | // serverPtr returns the pointer to the provided server instance, as a Java's |
| 36 | // C.jlong type. |
| 37 | func serverPtr(s ipc.Server) C.jlong { |
| 38 | return C.jlong(uintptr(unsafe.Pointer(&s))) |
| 39 | } |
| 40 | |
| 41 | // getServer returns the server referenced by the provided pointer, or nil if |
| 42 | // the pointer is 0. |
| 43 | func getServer(env *C.JNIEnv, ptr C.jlong) ipc.Server { |
| 44 | if ptr == C.jlong(0) { |
| 45 | jThrow(env, "Go server pointer is nil") |
| 46 | return nil |
| 47 | } |
| 48 | return *(*ipc.Server)(unsafe.Pointer(uintptr(ptr))) |
| 49 | } |
| 50 | |
| 51 | // serverPtr returns the pointer to the provided client instance, as a Java's |
| 52 | // C.jlong type. |
| 53 | func clientPtr(c *client) C.jlong { |
| 54 | return C.jlong(uintptr(unsafe.Pointer(c))) |
| 55 | } |
| 56 | |
| 57 | // getClient returns the client referenced by the provided pointer, or nil if |
| 58 | // the pointer is 0. |
| 59 | func getClient(env *C.JNIEnv, ptr C.jlong) *client { |
| 60 | if ptr == C.jlong(0) { |
| 61 | jThrow(env, "Go client pointer is nil") |
| 62 | return nil |
| 63 | } |
| 64 | return (*client)(unsafe.Pointer(uintptr(ptr))) |
| 65 | } |
| 66 | |
| 67 | // serverPtr returns the pointer to the provided clientCall instance, as a |
| 68 | // Java's C.jlong type. |
| 69 | func clientCallPtr(c *clientCall) C.jlong { |
| 70 | return C.jlong(uintptr(unsafe.Pointer(c))) |
| 71 | } |
| 72 | |
Matt Rosencrantz | f5afcaf | 2014-06-02 11:31:22 -0700 | [diff] [blame] | 73 | // getCall returns the clientCall referenced by the provided pointer, |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 74 | // or nil if the pointer is 0. |
Matt Rosencrantz | f5afcaf | 2014-06-02 11:31:22 -0700 | [diff] [blame] | 75 | func getCall(env *C.JNIEnv, ptr C.jlong) *clientCall { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 76 | if ptr == C.jlong(0) { |
| 77 | jThrow(env, "Go client call pointer is nil") |
| 78 | return nil |
| 79 | } |
| 80 | return (*clientCall)(unsafe.Pointer(uintptr(ptr))) |
| 81 | } |
| 82 | |
| 83 | //export JNI_OnLoad |
| 84 | func JNI_OnLoad(jVM *C.JavaVM, reserved unsafe.Pointer) C.jint { |
| 85 | return C.JNI_VERSION_1_6 |
| 86 | } |
| 87 | |
| 88 | //export Java_com_veyron_runtimes_google_ipc_Runtime_nativeInit |
| 89 | func Java_com_veyron_runtimes_google_ipc_Runtime_nativeInit(env *C.JNIEnv, jRuntime C.jclass) { |
| 90 | // Cache global references to all Java classes used by the package. This is |
| 91 | // necessary because JNI gets access to the class loader only in the system |
| 92 | // thread, so we aren't able to invoke FindClass in other threads. |
| 93 | jVeyronExceptionClass = jFindClassOrDie(env, "com/veyron2/ipc/VeyronException") |
| 94 | jIDLInvokerClass = jFindClassOrDie(env, "com/veyron/runtimes/google/ipc/IDLInvoker") |
| 95 | jThrowableClass = jFindClassOrDie(env, "java/lang/Throwable") |
| 96 | jStringClass = jFindClassOrDie(env, "java/lang/String") |
| 97 | } |
| 98 | |
| 99 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeInit |
| 100 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeInit(env *C.JNIEnv, jServer C.jobject) C.jlong { |
| 101 | s, err := rt.R().NewServer() |
| 102 | if err != nil { |
| 103 | jThrow(env, fmt.Sprintf("Couldn't get new server from go runtime: %v", err)) |
| 104 | return C.jlong(0) |
| 105 | } |
| 106 | |
| 107 | // Ref. |
| 108 | refs.insert(s) |
| 109 | return serverPtr(s) |
| 110 | } |
| 111 | |
| 112 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeRegister |
| 113 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeRegister(env *C.JNIEnv, jServer C.jobject, goServerPtr C.jlong, prefix C.jstring, dispatcher C.jobject) { |
| 114 | s := getServer(env, goServerPtr) |
| 115 | if s == nil { |
| 116 | jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr))) |
| 117 | return |
| 118 | } |
| 119 | // Create a new Dispatcher |
| 120 | d, err := newJNIDispatcher(env, dispatcher) |
| 121 | if err != nil { |
| 122 | jThrow(env, err.Error()) |
| 123 | return |
| 124 | } |
| 125 | s.Register(goString(env, prefix), d) |
| 126 | } |
| 127 | |
| 128 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeListen |
| 129 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativeListen(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong, protocol C.jstring, address C.jstring) C.jstring { |
| 130 | s := getServer(env, goServerPtr) |
| 131 | if s == nil { |
| 132 | jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr))) |
| 133 | return nil |
| 134 | } |
| 135 | ep, err := s.Listen(goString(env, protocol), goString(env, address)) |
| 136 | if err != nil { |
| 137 | jThrow(env, err.Error()) |
| 138 | return nil |
| 139 | } |
| 140 | return jString(env, ep.String()) |
| 141 | } |
| 142 | |
| 143 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativePublish |
| 144 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Server_nativePublish(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong, name C.jstring) { |
| 145 | s := getServer(env, goServerPtr) |
| 146 | if s == nil { |
| 147 | jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr))) |
| 148 | return |
| 149 | } |
| 150 | if err := s.Publish(goString(env, name)); err != nil { |
| 151 | jThrow(env, err.Error()) |
| 152 | return |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | //export Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeStop |
| 157 | func Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeStop(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong) { |
| 158 | s := getServer(env, goServerPtr) |
| 159 | if s == nil { |
| 160 | jThrow(env, fmt.Sprintf("Couldn't find Go server with pointer: %d", int(goServerPtr))) |
| 161 | return |
| 162 | } |
| 163 | if err := s.Stop(); err != nil { |
| 164 | jThrow(env, err.Error()) |
| 165 | return |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | //export Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeFinalize |
| 170 | func Java_com_veyron_runtimes_google_ipc_jni_Runtime_00024Server_nativeFinalize(env *C.JNIEnv, server C.jobject, goServerPtr C.jlong) { |
| 171 | s := getServer(env, goServerPtr) |
| 172 | if s != nil { |
| 173 | // Unref. |
| 174 | refs.delete(s) |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeInit |
| 179 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeInit(env *C.JNIEnv, jClient C.jobject) C.jlong { |
| 180 | c, err := newClient() |
| 181 | if err != nil { |
| 182 | jThrow(env, fmt.Sprintf("Couldn't get new client from go runtime: %v", err)) |
| 183 | return C.jlong(0) |
| 184 | } |
| 185 | // Ref. |
| 186 | refs.insert(c) |
| 187 | return clientPtr(c) |
| 188 | } |
| 189 | |
| 190 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeStartCall |
| 191 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeStartCall(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong, name C.jstring, method C.jstring, jsonArgs C.jobjectArray, jPath C.jstring, timeoutMillis C.jlong) C.jlong { |
| 192 | c := getClient(env, goClientPtr) |
| 193 | if c == nil { |
| 194 | jThrow(env, fmt.Sprintf("Couldn't find Go client with pointer: %d", int(goClientPtr))) |
| 195 | return C.jlong(0) |
| 196 | } |
| 197 | call, err := c.StartCall(env, goString(env, name), goString(env, method), jsonArgs, jPath, timeoutMillis) |
| 198 | if err != nil { |
| 199 | jThrow(env, fmt.Sprintf("Couldn't start Go call: %v", err)) |
| 200 | return C.jlong(0) |
| 201 | } |
| 202 | return clientCallPtr(call) |
| 203 | } |
| 204 | |
| 205 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeClose |
| 206 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeClose(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong) { |
| 207 | c := getClient(env, goClientPtr) |
| 208 | if c != nil { |
| 209 | c.Close() |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeFinalize |
| 214 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Client_nativeFinalize(env *C.JNIEnv, jClient C.jobject, goClientPtr C.jlong) { |
| 215 | c := getClient(env, goClientPtr) |
| 216 | if c != nil { |
| 217 | // Unref. |
| 218 | refs.delete(c) |
| 219 | } |
| 220 | } |
| 221 | |
Matt Rosencrantz | f5afcaf | 2014-06-02 11:31:22 -0700 | [diff] [blame] | 222 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinish |
| 223 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinish(env *C.JNIEnv, jClient C.jobject, goCallPtr C.jlong) C.jobjectArray { |
| 224 | c := getCall(env, goCallPtr) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 225 | if c == nil { |
Matt Rosencrantz | f5afcaf | 2014-06-02 11:31:22 -0700 | [diff] [blame] | 226 | jThrow(env, fmt.Sprintf("Couldn't find Go client with pointer: %d", int(goCallPtr))) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 227 | return nil |
| 228 | } |
| 229 | ret, err := c.Finish(env) |
| 230 | if err != nil { |
| 231 | // Could be an application error, so we throw it with jThrowV. |
| 232 | jThrowV(env, verror.Convert(err)) |
| 233 | return nil |
| 234 | } |
| 235 | // Unref. |
| 236 | refs.delete(c) |
| 237 | return ret |
| 238 | } |
| 239 | |
Matt Rosencrantz | f5afcaf | 2014-06-02 11:31:22 -0700 | [diff] [blame] | 240 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeCancel |
| 241 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeCancel(env *C.JNIEnv, jClient C.jobject, goCallPtr C.jlong) { |
| 242 | c := getCall(env, goCallPtr) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 243 | if c != nil { |
| 244 | c.Cancel() |
| 245 | } |
| 246 | } |
| 247 | |
Matt Rosencrantz | f5afcaf | 2014-06-02 11:31:22 -0700 | [diff] [blame] | 248 | //export Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinalize |
| 249 | func Java_com_veyron_runtimes_google_ipc_Runtime_00024Call_nativeFinalize(env *C.JNIEnv, jClient C.jobject, goCallPtr C.jlong) { |
| 250 | c := getCall(env, goCallPtr) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 251 | if c != nil { |
| 252 | refs.delete(c) |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | func main() { |
| 257 | // Send all logging to stderr, so that the output is visible in Android. Note that if this |
| 258 | // flag is removed, the process will likely crash as android requires that all logs are written |
| 259 | // into a specific directory. |
| 260 | flag.Set("logtostderr", "true") |
| 261 | rt.Init() |
| 262 | } |