TBR
v: renaming the v directory to go
Change-Id: I4fd9f6ee2895d8034c23b65927eb118980b3c17a
diff --git a/examples/boxes/android/build.sh b/examples/boxes/android/build.sh
new file mode 100755
index 0000000..6b406e0
--- /dev/null
+++ b/examples/boxes/android/build.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+# Path of the go binary that was built with the goandroid patches
+GOANDROID=$1
+# Path of where eclipse installs the libs for the DragAndDraw android app
+SHAREDLIB=$2
+
+CC="$NDK_ROOT/bin/arm-linux-androideabi-gcc"
+CC=$CC GOPATH="`pwd`:$GOPATH" GOROOT="" GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 $GOANDROID install $GOFLAGS -v -ldflags="-android -shared -extld $CC -extldflags '-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'" -tags android boxesp2p
+cp bin/linux_arm/boxesp2p $SHAREDLIB
diff --git a/examples/boxes/android/src/boxesp2p/jni.h b/examples/boxes/android/src/boxesp2p/jni.h
new file mode 100644
index 0000000..262c137
--- /dev/null
+++ b/examples/boxes/android/src/boxesp2p/jni.h
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI specification, as defined by Sun:
+ * http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
+ *
+ * Everything here is expected to be VM-neutral.
+ */
+
+#ifndef JNI_H_
+#define JNI_H_
+
+#include <sys/cdefs.h>
+#include <stdarg.h>
+
+/*
+ * Primitive types that match up with Java equivalents.
+ */
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h> /* C99 */
+typedef uint8_t jboolean; /* unsigned 8 bits */
+typedef int8_t jbyte; /* signed 8 bits */
+typedef uint16_t jchar; /* unsigned 16 bits */
+typedef int16_t jshort; /* signed 16 bits */
+typedef int32_t jint; /* signed 32 bits */
+typedef int64_t jlong; /* signed 64 bits */
+typedef float jfloat; /* 32-bit IEEE 754 */
+typedef double jdouble; /* 64-bit IEEE 754 */
+#else
+typedef unsigned char jboolean; /* unsigned 8 bits */
+typedef signed char jbyte; /* signed 8 bits */
+typedef unsigned short jchar; /* unsigned 16 bits */
+typedef short jshort; /* signed 16 bits */
+typedef int jint; /* signed 32 bits */
+typedef long long jlong; /* signed 64 bits */
+typedef float jfloat; /* 32-bit IEEE 754 */
+typedef double jdouble; /* 64-bit IEEE 754 */
+#endif
+
+/* "cardinal indices and sizes" */
+typedef jint jsize;
+
+#ifdef __cplusplus
+/*
+ * Reference types, in C++
+ */
+class _jobject {};
+class _jclass : public _jobject {};
+class _jstring : public _jobject {};
+class _jarray : public _jobject {};
+class _jobjectArray : public _jarray {};
+class _jbooleanArray : public _jarray {};
+class _jbyteArray : public _jarray {};
+class _jcharArray : public _jarray {};
+class _jshortArray : public _jarray {};
+class _jintArray : public _jarray {};
+class _jlongArray : public _jarray {};
+class _jfloatArray : public _jarray {};
+class _jdoubleArray : public _jarray {};
+class _jthrowable : public _jobject {};
+
+typedef _jobject* jobject;
+typedef _jclass* jclass;
+typedef _jstring* jstring;
+typedef _jarray* jarray;
+typedef _jobjectArray* jobjectArray;
+typedef _jbooleanArray* jbooleanArray;
+typedef _jbyteArray* jbyteArray;
+typedef _jcharArray* jcharArray;
+typedef _jshortArray* jshortArray;
+typedef _jintArray* jintArray;
+typedef _jlongArray* jlongArray;
+typedef _jfloatArray* jfloatArray;
+typedef _jdoubleArray* jdoubleArray;
+typedef _jthrowable* jthrowable;
+typedef _jobject* jweak;
+
+
+#else /* not __cplusplus */
+
+/*
+ * Reference types, in C.
+ */
+typedef void* jobject;
+typedef jobject jclass;
+typedef jobject jstring;
+typedef jobject jarray;
+typedef jarray jobjectArray;
+typedef jarray jbooleanArray;
+typedef jarray jbyteArray;
+typedef jarray jcharArray;
+typedef jarray jshortArray;
+typedef jarray jintArray;
+typedef jarray jlongArray;
+typedef jarray jfloatArray;
+typedef jarray jdoubleArray;
+typedef jobject jthrowable;
+typedef jobject jweak;
+
+#endif /* not __cplusplus */
+
+struct _jfieldID; /* opaque structure */
+typedef struct _jfieldID* jfieldID; /* field IDs */
+
+struct _jmethodID; /* opaque structure */
+typedef struct _jmethodID* jmethodID; /* method IDs */
+
+struct JNIInvokeInterface;
+
+typedef union jvalue {
+ jboolean z;
+ jbyte b;
+ jchar c;
+ jshort s;
+ jint i;
+ jlong j;
+ jfloat f;
+ jdouble d;
+ jobject l;
+} jvalue;
+
+typedef enum jobjectRefType {
+ JNIInvalidRefType = 0,
+ JNILocalRefType = 1,
+ JNIGlobalRefType = 2,
+ JNIWeakGlobalRefType = 3
+} jobjectRefType;
+
+typedef struct {
+ const char* name;
+ const char* signature;
+ void* fnPtr;
+} JNINativeMethod;
+
+struct _JNIEnv;
+struct _JavaVM;
+typedef const struct JNINativeInterface* C_JNIEnv;
+
+#if defined(__cplusplus)
+typedef _JNIEnv JNIEnv;
+typedef _JavaVM JavaVM;
+#else
+typedef const struct JNINativeInterface* JNIEnv;
+typedef const struct JNIInvokeInterface* JavaVM;
+#endif
+
+/*
+ * Table of interface function pointers.
+ */
+struct JNINativeInterface {
+ void* reserved0;
+ void* reserved1;
+ void* reserved2;
+ void* reserved3;
+
+ jint (*GetVersion)(JNIEnv *);
+
+ jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
+ jsize);
+ jclass (*FindClass)(JNIEnv*, const char*);
+
+ jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
+ jfieldID (*FromReflectedField)(JNIEnv*, jobject);
+ /* spec doesn't show jboolean parameter */
+ jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
+
+ jclass (*GetSuperclass)(JNIEnv*, jclass);
+ jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
+
+ /* spec doesn't show jboolean parameter */
+ jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
+
+ jint (*Throw)(JNIEnv*, jthrowable);
+ jint (*ThrowNew)(JNIEnv *, jclass, const char *);
+ jthrowable (*ExceptionOccurred)(JNIEnv*);
+ void (*ExceptionDescribe)(JNIEnv*);
+ void (*ExceptionClear)(JNIEnv*);
+ void (*FatalError)(JNIEnv*, const char*);
+
+ jint (*PushLocalFrame)(JNIEnv*, jint);
+ jobject (*PopLocalFrame)(JNIEnv*, jobject);
+
+ jobject (*NewGlobalRef)(JNIEnv*, jobject);
+ void (*DeleteGlobalRef)(JNIEnv*, jobject);
+ void (*DeleteLocalRef)(JNIEnv*, jobject);
+ jboolean (*IsSameObject)(JNIEnv*, jobject, jobject);
+
+ jobject (*NewLocalRef)(JNIEnv*, jobject);
+ jint (*EnsureLocalCapacity)(JNIEnv*, jint);
+
+ jobject (*AllocObject)(JNIEnv*, jclass);
+ jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
+ jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
+ jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+ jclass (*GetObjectClass)(JNIEnv*, jobject);
+ jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass);
+ jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+ jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
+ void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+
+ jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+
+ jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
+
+ jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
+ jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
+ jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
+ jchar (*GetCharField)(JNIEnv*, jobject, jfieldID);
+ jshort (*GetShortField)(JNIEnv*, jobject, jfieldID);
+ jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
+ jlong (*GetLongField)(JNIEnv*, jobject, jfieldID);
+ jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID);
+ jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID);
+
+ void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
+ void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
+ void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
+ void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
+ void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
+ void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
+ void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
+ void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);
+ void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);
+
+ jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+ jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
+ va_list);
+ jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
+ jvalue*);
+ jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
+ void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+ jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*,
+ const char*);
+
+ jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
+ jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
+ jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
+ jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
+ jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
+ jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
+ jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
+ jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID);
+ jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID);
+
+ void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
+ void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
+ void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
+ void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
+ void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
+ void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
+ void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
+ void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat);
+ void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble);
+
+ jstring (*NewString)(JNIEnv*, const jchar*, jsize);
+ jsize (*GetStringLength)(JNIEnv*, jstring);
+ const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
+ void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
+ jstring (*NewStringUTF)(JNIEnv*, const char*);
+ jsize (*GetStringUTFLength)(JNIEnv*, jstring);
+ /* JNI spec says this returns const jbyte*, but that's inconsistent */
+ const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
+ void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
+ jsize (*GetArrayLength)(JNIEnv*, jarray);
+ jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
+ jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
+ void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
+
+ jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
+ jbyteArray (*NewByteArray)(JNIEnv*, jsize);
+ jcharArray (*NewCharArray)(JNIEnv*, jsize);
+ jshortArray (*NewShortArray)(JNIEnv*, jsize);
+ jintArray (*NewIntArray)(JNIEnv*, jsize);
+ jlongArray (*NewLongArray)(JNIEnv*, jsize);
+ jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
+ jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
+
+ jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
+ jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
+ jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
+ jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
+ jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
+ jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
+ jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
+ jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
+
+ void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
+ jboolean*, jint);
+ void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
+ jbyte*, jint);
+ void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
+ jchar*, jint);
+ void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
+ jshort*, jint);
+ void (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
+ jint*, jint);
+ void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
+ jlong*, jint);
+ void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
+ jfloat*, jint);
+ void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
+ jdouble*, jint);
+
+ void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+ jsize, jsize, jboolean*);
+ void (*GetByteArrayRegion)(JNIEnv*, jbyteArray,
+ jsize, jsize, jbyte*);
+ void (*GetCharArrayRegion)(JNIEnv*, jcharArray,
+ jsize, jsize, jchar*);
+ void (*GetShortArrayRegion)(JNIEnv*, jshortArray,
+ jsize, jsize, jshort*);
+ void (*GetIntArrayRegion)(JNIEnv*, jintArray,
+ jsize, jsize, jint*);
+ void (*GetLongArrayRegion)(JNIEnv*, jlongArray,
+ jsize, jsize, jlong*);
+ void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,
+ jsize, jsize, jfloat*);
+ void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+ jsize, jsize, jdouble*);
+
+ /* spec shows these without const; some jni.h do, some don't */
+ void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+ jsize, jsize, const jboolean*);
+ void (*SetByteArrayRegion)(JNIEnv*, jbyteArray,
+ jsize, jsize, const jbyte*);
+ void (*SetCharArrayRegion)(JNIEnv*, jcharArray,
+ jsize, jsize, const jchar*);
+ void (*SetShortArrayRegion)(JNIEnv*, jshortArray,
+ jsize, jsize, const jshort*);
+ void (*SetIntArrayRegion)(JNIEnv*, jintArray,
+ jsize, jsize, const jint*);
+ void (*SetLongArrayRegion)(JNIEnv*, jlongArray,
+ jsize, jsize, const jlong*);
+ void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,
+ jsize, jsize, const jfloat*);
+ void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+ jsize, jsize, const jdouble*);
+
+ jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
+ jint);
+ jint (*UnregisterNatives)(JNIEnv*, jclass);
+ jint (*MonitorEnter)(JNIEnv*, jobject);
+ jint (*MonitorExit)(JNIEnv*, jobject);
+ jint (*GetJavaVM)(JNIEnv*, JavaVM**);
+
+ void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*);
+ void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*);
+
+ void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*);
+ void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint);
+
+ const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*);
+ void (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*);
+
+ jweak (*NewWeakGlobalRef)(JNIEnv*, jobject);
+ void (*DeleteWeakGlobalRef)(JNIEnv*, jweak);
+
+ jboolean (*ExceptionCheck)(JNIEnv*);
+
+ jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
+ void* (*GetDirectBufferAddress)(JNIEnv*, jobject);
+ jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject);
+
+ /* added in JNI 1.6 */
+ jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
+};
+
+/*
+ * C++ object wrapper.
+ *
+ * This is usually overlaid on a C struct whose first element is a
+ * JNINativeInterface*. We rely somewhat on compiler behavior.
+ */
+struct _JNIEnv {
+ /* do not rename this; it does not seem to be entirely opaque */
+ const struct JNINativeInterface* functions;
+
+#if defined(__cplusplus)
+
+ jint GetVersion()
+ { return functions->GetVersion(this); }
+
+ jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
+ jsize bufLen)
+ { return functions->DefineClass(this, name, loader, buf, bufLen); }
+
+ jclass FindClass(const char* name)
+ { return functions->FindClass(this, name); }
+
+ jmethodID FromReflectedMethod(jobject method)
+ { return functions->FromReflectedMethod(this, method); }
+
+ jfieldID FromReflectedField(jobject field)
+ { return functions->FromReflectedField(this, field); }
+
+ jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic)
+ { return functions->ToReflectedMethod(this, cls, methodID, isStatic); }
+
+ jclass GetSuperclass(jclass clazz)
+ { return functions->GetSuperclass(this, clazz); }
+
+ jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
+ { return functions->IsAssignableFrom(this, clazz1, clazz2); }
+
+ jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic)
+ { return functions->ToReflectedField(this, cls, fieldID, isStatic); }
+
+ jint Throw(jthrowable obj)
+ { return functions->Throw(this, obj); }
+
+ jint ThrowNew(jclass clazz, const char* message)
+ { return functions->ThrowNew(this, clazz, message); }
+
+ jthrowable ExceptionOccurred()
+ { return functions->ExceptionOccurred(this); }
+
+ void ExceptionDescribe()
+ { functions->ExceptionDescribe(this); }
+
+ void ExceptionClear()
+ { functions->ExceptionClear(this); }
+
+ void FatalError(const char* msg)
+ { functions->FatalError(this, msg); }
+
+ jint PushLocalFrame(jint capacity)
+ { return functions->PushLocalFrame(this, capacity); }
+
+ jobject PopLocalFrame(jobject result)
+ { return functions->PopLocalFrame(this, result); }
+
+ jobject NewGlobalRef(jobject obj)
+ { return functions->NewGlobalRef(this, obj); }
+
+ void DeleteGlobalRef(jobject globalRef)
+ { functions->DeleteGlobalRef(this, globalRef); }
+
+ void DeleteLocalRef(jobject localRef)
+ { functions->DeleteLocalRef(this, localRef); }
+
+ jboolean IsSameObject(jobject ref1, jobject ref2)
+ { return functions->IsSameObject(this, ref1, ref2); }
+
+ jobject NewLocalRef(jobject ref)
+ { return functions->NewLocalRef(this, ref); }
+
+ jint EnsureLocalCapacity(jint capacity)
+ { return functions->EnsureLocalCapacity(this, capacity); }
+
+ jobject AllocObject(jclass clazz)
+ { return functions->AllocObject(this, clazz); }
+
+ jobject NewObject(jclass clazz, jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ jobject result = functions->NewObjectV(this, clazz, methodID, args);
+ va_end(args);
+ return result;
+ }
+
+ jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
+ { return functions->NewObjectV(this, clazz, methodID, args); }
+
+ jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
+ { return functions->NewObjectA(this, clazz, methodID, args); }
+
+ jclass GetObjectClass(jobject obj)
+ { return functions->GetObjectClass(this, obj); }
+
+ jboolean IsInstanceOf(jobject obj, jclass clazz)
+ { return functions->IsInstanceOf(this, obj, clazz); }
+
+ jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetMethodID(this, clazz, name, sig); }
+
+#define CALL_TYPE_METHOD(_jtype, _jname) \
+ _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...) \
+ { \
+ _jtype result; \
+ va_list args; \
+ va_start(args, methodID); \
+ result = functions->Call##_jname##MethodV(this, obj, methodID, \
+ args); \
+ va_end(args); \
+ return result; \
+ }
+#define CALL_TYPE_METHODV(_jtype, _jname) \
+ _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID, \
+ va_list args) \
+ { return functions->Call##_jname##MethodV(this, obj, methodID, args); }
+#define CALL_TYPE_METHODA(_jtype, _jname) \
+ _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \
+ jvalue* args) \
+ { return functions->Call##_jname##MethodA(this, obj, methodID, args); }
+
+#define CALL_TYPE(_jtype, _jname) \
+ CALL_TYPE_METHOD(_jtype, _jname) \
+ CALL_TYPE_METHODV(_jtype, _jname) \
+ CALL_TYPE_METHODA(_jtype, _jname)
+
+ CALL_TYPE(jobject, Object)
+ CALL_TYPE(jboolean, Boolean)
+ CALL_TYPE(jbyte, Byte)
+ CALL_TYPE(jchar, Char)
+ CALL_TYPE(jshort, Short)
+ CALL_TYPE(jint, Int)
+ CALL_TYPE(jlong, Long)
+ CALL_TYPE(jfloat, Float)
+ CALL_TYPE(jdouble, Double)
+
+ void CallVoidMethod(jobject obj, jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ functions->CallVoidMethodV(this, obj, methodID, args);
+ va_end(args);
+ }
+ void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
+ { functions->CallVoidMethodV(this, obj, methodID, args); }
+ void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)
+ { functions->CallVoidMethodA(this, obj, methodID, args); }
+
+#define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \
+ _jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz, \
+ jmethodID methodID, ...) \
+ { \
+ _jtype result; \
+ va_list args; \
+ va_start(args, methodID); \
+ result = functions->CallNonvirtual##_jname##MethodV(this, obj, \
+ clazz, methodID, args); \
+ va_end(args); \
+ return result; \
+ }
+#define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \
+ _jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz, \
+ jmethodID methodID, va_list args) \
+ { return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz, \
+ methodID, args); }
+#define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) \
+ _jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz, \
+ jmethodID methodID, jvalue* args) \
+ { return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz, \
+ methodID, args); }
+
+#define CALL_NONVIRT_TYPE(_jtype, _jname) \
+ CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \
+ CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \
+ CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)
+
+ CALL_NONVIRT_TYPE(jobject, Object)
+ CALL_NONVIRT_TYPE(jboolean, Boolean)
+ CALL_NONVIRT_TYPE(jbyte, Byte)
+ CALL_NONVIRT_TYPE(jchar, Char)
+ CALL_NONVIRT_TYPE(jshort, Short)
+ CALL_NONVIRT_TYPE(jint, Int)
+ CALL_NONVIRT_TYPE(jlong, Long)
+ CALL_NONVIRT_TYPE(jfloat, Float)
+ CALL_NONVIRT_TYPE(jdouble, Double)
+
+ void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args);
+ va_end(args);
+ }
+ void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args)
+ { functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); }
+ void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, jvalue* args)
+ { functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); }
+
+ jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetFieldID(this, clazz, name, sig); }
+
+ jobject GetObjectField(jobject obj, jfieldID fieldID)
+ { return functions->GetObjectField(this, obj, fieldID); }
+ jboolean GetBooleanField(jobject obj, jfieldID fieldID)
+ { return functions->GetBooleanField(this, obj, fieldID); }
+ jbyte GetByteField(jobject obj, jfieldID fieldID)
+ { return functions->GetByteField(this, obj, fieldID); }
+ jchar GetCharField(jobject obj, jfieldID fieldID)
+ { return functions->GetCharField(this, obj, fieldID); }
+ jshort GetShortField(jobject obj, jfieldID fieldID)
+ { return functions->GetShortField(this, obj, fieldID); }
+ jint GetIntField(jobject obj, jfieldID fieldID)
+ { return functions->GetIntField(this, obj, fieldID); }
+ jlong GetLongField(jobject obj, jfieldID fieldID)
+ { return functions->GetLongField(this, obj, fieldID); }
+ jfloat GetFloatField(jobject obj, jfieldID fieldID)
+ { return functions->GetFloatField(this, obj, fieldID); }
+ jdouble GetDoubleField(jobject obj, jfieldID fieldID)
+ { return functions->GetDoubleField(this, obj, fieldID); }
+
+ void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
+ { functions->SetObjectField(this, obj, fieldID, value); }
+ void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
+ { functions->SetBooleanField(this, obj, fieldID, value); }
+ void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
+ { functions->SetByteField(this, obj, fieldID, value); }
+ void SetCharField(jobject obj, jfieldID fieldID, jchar value)
+ { functions->SetCharField(this, obj, fieldID, value); }
+ void SetShortField(jobject obj, jfieldID fieldID, jshort value)
+ { functions->SetShortField(this, obj, fieldID, value); }
+ void SetIntField(jobject obj, jfieldID fieldID, jint value)
+ { functions->SetIntField(this, obj, fieldID, value); }
+ void SetLongField(jobject obj, jfieldID fieldID, jlong value)
+ { functions->SetLongField(this, obj, fieldID, value); }
+ void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
+ { functions->SetFloatField(this, obj, fieldID, value); }
+ void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
+ { functions->SetDoubleField(this, obj, fieldID, value); }
+
+ jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetStaticMethodID(this, clazz, name, sig); }
+
+#define CALL_STATIC_TYPE_METHOD(_jtype, _jname) \
+ _jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID, \
+ ...) \
+ { \
+ _jtype result; \
+ va_list args; \
+ va_start(args, methodID); \
+ result = functions->CallStatic##_jname##MethodV(this, clazz, \
+ methodID, args); \
+ va_end(args); \
+ return result; \
+ }
+#define CALL_STATIC_TYPE_METHODV(_jtype, _jname) \
+ _jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID, \
+ va_list args) \
+ { return functions->CallStatic##_jname##MethodV(this, clazz, methodID, \
+ args); }
+#define CALL_STATIC_TYPE_METHODA(_jtype, _jname) \
+ _jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID, \
+ jvalue* args) \
+ { return functions->CallStatic##_jname##MethodA(this, clazz, methodID, \
+ args); }
+
+#define CALL_STATIC_TYPE(_jtype, _jname) \
+ CALL_STATIC_TYPE_METHOD(_jtype, _jname) \
+ CALL_STATIC_TYPE_METHODV(_jtype, _jname) \
+ CALL_STATIC_TYPE_METHODA(_jtype, _jname)
+
+ CALL_STATIC_TYPE(jobject, Object)
+ CALL_STATIC_TYPE(jboolean, Boolean)
+ CALL_STATIC_TYPE(jbyte, Byte)
+ CALL_STATIC_TYPE(jchar, Char)
+ CALL_STATIC_TYPE(jshort, Short)
+ CALL_STATIC_TYPE(jint, Int)
+ CALL_STATIC_TYPE(jlong, Long)
+ CALL_STATIC_TYPE(jfloat, Float)
+ CALL_STATIC_TYPE(jdouble, Double)
+
+ void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ functions->CallStaticVoidMethodV(this, clazz, methodID, args);
+ va_end(args);
+ }
+ void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
+ { functions->CallStaticVoidMethodV(this, clazz, methodID, args); }
+ void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args)
+ { functions->CallStaticVoidMethodA(this, clazz, methodID, args); }
+
+ jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetStaticFieldID(this, clazz, name, sig); }
+
+ jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticObjectField(this, clazz, fieldID); }
+ jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticBooleanField(this, clazz, fieldID); }
+ jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticByteField(this, clazz, fieldID); }
+ jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticCharField(this, clazz, fieldID); }
+ jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticShortField(this, clazz, fieldID); }
+ jint GetStaticIntField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticIntField(this, clazz, fieldID); }
+ jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticLongField(this, clazz, fieldID); }
+ jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticFloatField(this, clazz, fieldID); }
+ jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticDoubleField(this, clazz, fieldID); }
+
+ void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
+ { functions->SetStaticObjectField(this, clazz, fieldID, value); }
+ void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
+ { functions->SetStaticBooleanField(this, clazz, fieldID, value); }
+ void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
+ { functions->SetStaticByteField(this, clazz, fieldID, value); }
+ void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
+ { functions->SetStaticCharField(this, clazz, fieldID, value); }
+ void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
+ { functions->SetStaticShortField(this, clazz, fieldID, value); }
+ void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
+ { functions->SetStaticIntField(this, clazz, fieldID, value); }
+ void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
+ { functions->SetStaticLongField(this, clazz, fieldID, value); }
+ void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
+ { functions->SetStaticFloatField(this, clazz, fieldID, value); }
+ void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
+ { functions->SetStaticDoubleField(this, clazz, fieldID, value); }
+
+ jstring NewString(const jchar* unicodeChars, jsize len)
+ { return functions->NewString(this, unicodeChars, len); }
+
+ jsize GetStringLength(jstring string)
+ { return functions->GetStringLength(this, string); }
+
+ const jchar* GetStringChars(jstring string, jboolean* isCopy)
+ { return functions->GetStringChars(this, string, isCopy); }
+
+ void ReleaseStringChars(jstring string, const jchar* chars)
+ { functions->ReleaseStringChars(this, string, chars); }
+
+ jstring NewStringUTF(const char* bytes)
+ { return functions->NewStringUTF(this, bytes); }
+
+ jsize GetStringUTFLength(jstring string)
+ { return functions->GetStringUTFLength(this, string); }
+
+ const char* GetStringUTFChars(jstring string, jboolean* isCopy)
+ { return functions->GetStringUTFChars(this, string, isCopy); }
+
+ void ReleaseStringUTFChars(jstring string, const char* utf)
+ { functions->ReleaseStringUTFChars(this, string, utf); }
+
+ jsize GetArrayLength(jarray array)
+ { return functions->GetArrayLength(this, array); }
+
+ jobjectArray NewObjectArray(jsize length, jclass elementClass,
+ jobject initialElement)
+ { return functions->NewObjectArray(this, length, elementClass,
+ initialElement); }
+
+ jobject GetObjectArrayElement(jobjectArray array, jsize index)
+ { return functions->GetObjectArrayElement(this, array, index); }
+
+ void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
+ { functions->SetObjectArrayElement(this, array, index, value); }
+
+ jbooleanArray NewBooleanArray(jsize length)
+ { return functions->NewBooleanArray(this, length); }
+ jbyteArray NewByteArray(jsize length)
+ { return functions->NewByteArray(this, length); }
+ jcharArray NewCharArray(jsize length)
+ { return functions->NewCharArray(this, length); }
+ jshortArray NewShortArray(jsize length)
+ { return functions->NewShortArray(this, length); }
+ jintArray NewIntArray(jsize length)
+ { return functions->NewIntArray(this, length); }
+ jlongArray NewLongArray(jsize length)
+ { return functions->NewLongArray(this, length); }
+ jfloatArray NewFloatArray(jsize length)
+ { return functions->NewFloatArray(this, length); }
+ jdoubleArray NewDoubleArray(jsize length)
+ { return functions->NewDoubleArray(this, length); }
+
+ jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
+ { return functions->GetBooleanArrayElements(this, array, isCopy); }
+ jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
+ { return functions->GetByteArrayElements(this, array, isCopy); }
+ jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy)
+ { return functions->GetCharArrayElements(this, array, isCopy); }
+ jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy)
+ { return functions->GetShortArrayElements(this, array, isCopy); }
+ jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
+ { return functions->GetIntArrayElements(this, array, isCopy); }
+ jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy)
+ { return functions->GetLongArrayElements(this, array, isCopy); }
+ jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy)
+ { return functions->GetFloatArrayElements(this, array, isCopy); }
+ jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
+ { return functions->GetDoubleArrayElements(this, array, isCopy); }
+
+ void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems,
+ jint mode)
+ { functions->ReleaseBooleanArrayElements(this, array, elems, mode); }
+ void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,
+ jint mode)
+ { functions->ReleaseByteArrayElements(this, array, elems, mode); }
+ void ReleaseCharArrayElements(jcharArray array, jchar* elems,
+ jint mode)
+ { functions->ReleaseCharArrayElements(this, array, elems, mode); }
+ void ReleaseShortArrayElements(jshortArray array, jshort* elems,
+ jint mode)
+ { functions->ReleaseShortArrayElements(this, array, elems, mode); }
+ void ReleaseIntArrayElements(jintArray array, jint* elems,
+ jint mode)
+ { functions->ReleaseIntArrayElements(this, array, elems, mode); }
+ void ReleaseLongArrayElements(jlongArray array, jlong* elems,
+ jint mode)
+ { functions->ReleaseLongArrayElements(this, array, elems, mode); }
+ void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems,
+ jint mode)
+ { functions->ReleaseFloatArrayElements(this, array, elems, mode); }
+ void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems,
+ jint mode)
+ { functions->ReleaseDoubleArrayElements(this, array, elems, mode); }
+
+ void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+ jboolean* buf)
+ { functions->GetBooleanArrayRegion(this, array, start, len, buf); }
+ void GetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+ jbyte* buf)
+ { functions->GetByteArrayRegion(this, array, start, len, buf); }
+ void GetCharArrayRegion(jcharArray array, jsize start, jsize len,
+ jchar* buf)
+ { functions->GetCharArrayRegion(this, array, start, len, buf); }
+ void GetShortArrayRegion(jshortArray array, jsize start, jsize len,
+ jshort* buf)
+ { functions->GetShortArrayRegion(this, array, start, len, buf); }
+ void GetIntArrayRegion(jintArray array, jsize start, jsize len,
+ jint* buf)
+ { functions->GetIntArrayRegion(this, array, start, len, buf); }
+ void GetLongArrayRegion(jlongArray array, jsize start, jsize len,
+ jlong* buf)
+ { functions->GetLongArrayRegion(this, array, start, len, buf); }
+ void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+ jfloat* buf)
+ { functions->GetFloatArrayRegion(this, array, start, len, buf); }
+ void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+ jdouble* buf)
+ { functions->GetDoubleArrayRegion(this, array, start, len, buf); }
+
+ void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+ const jboolean* buf)
+ { functions->SetBooleanArrayRegion(this, array, start, len, buf); }
+ void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+ const jbyte* buf)
+ { functions->SetByteArrayRegion(this, array, start, len, buf); }
+ void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
+ const jchar* buf)
+ { functions->SetCharArrayRegion(this, array, start, len, buf); }
+ void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
+ const jshort* buf)
+ { functions->SetShortArrayRegion(this, array, start, len, buf); }
+ void SetIntArrayRegion(jintArray array, jsize start, jsize len,
+ const jint* buf)
+ { functions->SetIntArrayRegion(this, array, start, len, buf); }
+ void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
+ const jlong* buf)
+ { functions->SetLongArrayRegion(this, array, start, len, buf); }
+ void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+ const jfloat* buf)
+ { functions->SetFloatArrayRegion(this, array, start, len, buf); }
+ void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+ const jdouble* buf)
+ { functions->SetDoubleArrayRegion(this, array, start, len, buf); }
+
+ jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
+ jint nMethods)
+ { return functions->RegisterNatives(this, clazz, methods, nMethods); }
+
+ jint UnregisterNatives(jclass clazz)
+ { return functions->UnregisterNatives(this, clazz); }
+
+ jint MonitorEnter(jobject obj)
+ { return functions->MonitorEnter(this, obj); }
+
+ jint MonitorExit(jobject obj)
+ { return functions->MonitorExit(this, obj); }
+
+ jint GetJavaVM(JavaVM** vm)
+ { return functions->GetJavaVM(this, vm); }
+
+ void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
+ { functions->GetStringRegion(this, str, start, len, buf); }
+
+ void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
+ { return functions->GetStringUTFRegion(this, str, start, len, buf); }
+
+ void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy)
+ { return functions->GetPrimitiveArrayCritical(this, array, isCopy); }
+
+ void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode)
+ { functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); }
+
+ const jchar* GetStringCritical(jstring string, jboolean* isCopy)
+ { return functions->GetStringCritical(this, string, isCopy); }
+
+ void ReleaseStringCritical(jstring string, const jchar* carray)
+ { functions->ReleaseStringCritical(this, string, carray); }
+
+ jweak NewWeakGlobalRef(jobject obj)
+ { return functions->NewWeakGlobalRef(this, obj); }
+
+ void DeleteWeakGlobalRef(jweak obj)
+ { functions->DeleteWeakGlobalRef(this, obj); }
+
+ jboolean ExceptionCheck()
+ { return functions->ExceptionCheck(this); }
+
+ jobject NewDirectByteBuffer(void* address, jlong capacity)
+ { return functions->NewDirectByteBuffer(this, address, capacity); }
+
+ void* GetDirectBufferAddress(jobject buf)
+ { return functions->GetDirectBufferAddress(this, buf); }
+
+ jlong GetDirectBufferCapacity(jobject buf)
+ { return functions->GetDirectBufferCapacity(this, buf); }
+
+ /* added in JNI 1.6 */
+ jobjectRefType GetObjectRefType(jobject obj)
+ { return functions->GetObjectRefType(this, obj); }
+#endif /*__cplusplus*/
+};
+
+
+/*
+ * JNI invocation interface.
+ */
+struct JNIInvokeInterface {
+ void* reserved0;
+ void* reserved1;
+ void* reserved2;
+
+ jint (*DestroyJavaVM)(JavaVM*);
+ jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
+ jint (*DetachCurrentThread)(JavaVM*);
+ jint (*GetEnv)(JavaVM*, void**, jint);
+ jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
+};
+
+/*
+ * C++ version.
+ */
+struct _JavaVM {
+ const struct JNIInvokeInterface* functions;
+
+#if defined(__cplusplus)
+ jint DestroyJavaVM()
+ { return functions->DestroyJavaVM(this); }
+ jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
+ { return functions->AttachCurrentThread(this, p_env, thr_args); }
+ jint DetachCurrentThread()
+ { return functions->DetachCurrentThread(this); }
+ jint GetEnv(void** env, jint version)
+ { return functions->GetEnv(this, env, version); }
+ jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
+ { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
+#endif /*__cplusplus*/
+};
+
+struct JavaVMAttachArgs {
+ jint version; /* must be >= JNI_VERSION_1_2 */
+ const char* name; /* NULL or name of thread as modified UTF-8 str */
+ jobject group; /* global ref of a ThreadGroup object, or NULL */
+};
+typedef struct JavaVMAttachArgs JavaVMAttachArgs;
+
+/*
+ * JNI 1.2+ initialization. (As of 1.6, the pre-1.2 structures are no
+ * longer supported.)
+ */
+typedef struct JavaVMOption {
+ const char* optionString;
+ void* extraInfo;
+} JavaVMOption;
+
+typedef struct JavaVMInitArgs {
+ jint version; /* use JNI_VERSION_1_2 or later */
+
+ jint nOptions;
+ JavaVMOption* options;
+ jboolean ignoreUnrecognized;
+} JavaVMInitArgs;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * VM initialization functions.
+ *
+ * Note these are the only symbols exported for JNI by the VM.
+ */
+#if 0 /* In practice, these are not exported by the NDK so don't declare them */
+jint JNI_GetDefaultJavaVMInitArgs(void*);
+jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
+jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
+#endif
+
+#define JNIIMPORT
+#define JNIEXPORT __attribute__ ((visibility ("default")))
+#define JNICALL __NDK_FPABI__
+
+/*
+ * Prototypes for functions exported by loadable shared libs. These are
+ * called by JNI, not provided by JNI.
+ */
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * Manifest constants.
+ */
+#define JNI_FALSE 0
+#define JNI_TRUE 1
+
+#define JNI_VERSION_1_1 0x00010001
+#define JNI_VERSION_1_2 0x00010002
+#define JNI_VERSION_1_4 0x00010004
+#define JNI_VERSION_1_6 0x00010006
+
+#define JNI_OK (0) /* no error */
+#define JNI_ERR (-1) /* generic error */
+#define JNI_EDETACHED (-2) /* thread detached from the VM */
+#define JNI_EVERSION (-3) /* JNI version error */
+
+#define JNI_COMMIT 1 /* copy content, do not free buffer */
+#define JNI_ABORT 2 /* free buffer w/o copying back */
+
+#endif /* JNI_H_ */
diff --git a/examples/boxes/android/src/boxesp2p/main.go b/examples/boxes/android/src/boxesp2p/main.go
new file mode 100644
index 0000000..7bd3cd8
--- /dev/null
+++ b/examples/boxes/android/src/boxesp2p/main.go
@@ -0,0 +1,224 @@
+// +build !darwin
+
+package main
+
+/*
+// NOTE: This example builds a shared-library that gets used in android. We use the goandroid
+// binary to build this and need the Android NDK to be installed. We create a local copy of jni.h
+// to get this compiling for normal builds (source: android-ndk-r9d-linux-x86_64.tar.bz2 downloaded
+// from https://developer.android.com/tools/sdk/ndk/index.html).
+#include "jni.h"
+#include <stdlib.h>
+
+// Use GetEnv to discover the thread's JNIEnv. JNIEnv cannot be shared
+// between threads. Once we are done we need to detach the thread.
+static JNIEnv* getJNIEnv(JavaVM *jvm) {
+ JNIEnv *env;
+ (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6);
+ (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ return env;
+}
+
+// Free the thread that has been attached to the JNIEnv.
+static void freeJNIEnv(JavaVM *jvm, JNIEnv* env) {
+ (*jvm)->DetachCurrentThread(jvm);
+}
+
+// Conversions from JString to CString and vice-versa.
+static jstring CToJString(JNIEnv *env, const char* c) {
+ return (*env)->NewStringUTF(env, c);
+}
+
+static const char* JToCString(JNIEnv *env, jstring jstr) {
+ return (*env)->GetStringUTFChars(env, jstr, NULL);
+}
+
+// Get the Method ID for method "name" with given args.
+static jmethodID getMethodID(JNIEnv *env, jobject obj, const char* name, const char* args) {
+ jclass jc = (*env)->GetObjectClass(env, obj);
+ return (*env)->GetMethodID(env, jc, name, args);
+}
+
+// Calls the Java method in Android with the box description.
+static void callMethod(JNIEnv *env, jobject obj, jmethodID mid, jstring boxId, jfloat points[4]) {
+ (*env)->CallVoidMethod(env, obj, mid, boxId, points[0], points[1], points[2], points[3]);
+}
+
+// Gets the global reference for a given object.
+static jobject newGlobalRef(JNIEnv *env, jobject obj) {
+ return (*env)->NewGlobalRef(env, obj);
+}
+
+// Delete the global reference to a given object
+static void freeGlobalRef(JNIEnv *env, jobject obj) {
+ (*env)->DeleteGlobalRef(env, obj);
+}
+*/
+import "C"
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "unsafe"
+
+ "veyron/examples/boxes"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/rt"
+)
+
+type jniState struct {
+ jVM *C.JavaVM
+ jObj C.jobject
+ jMID C.jmethodID
+}
+
+type veyronState struct {
+ runtime veyron2.Runtime
+ drawStream boxes.DrawInterfaceDrawStream
+ signalling boxes.BoxSignalling
+ boxList chan boxes.Box
+}
+
+var (
+ veyron veyronState
+ nativeJava jniState
+)
+
+const (
+ signallingServer = "@1@tcp@54.200.34.44:8509@9282acd6784022b1945ee1044bddb804@@"
+ signallingService = "signalling"
+ drawService = "draw"
+ drawServicePort = ":8509"
+)
+
+func (jni *jniState) registerAddBox(env *C.JNIEnv, obj C.jobject) {
+ jni.jObj = C.newGlobalRef(env, obj)
+ jni.jMID = C.getMethodID(env, nativeJava.jObj, C.CString("AddBox"), C.CString("(Ljava/lang/String;FFFF)V"))
+}
+
+func (jni *jniState) addBox(box *boxes.Box) {
+ env := C.getJNIEnv(jni.jVM)
+ defer C.freeJNIEnv(jni.jVM, env)
+ // Convert box id/coordinates to java types
+ var jPoints [4]C.jfloat
+ for i := 0; i < 4; i++ {
+ jPoints[i] = C.jfloat(box.Points[i])
+ }
+ jBoxId := C.CToJString(env, C.CString(box.BoxId))
+ // Invoke the AddBox method in Java
+ C.callMethod(env, jni.jObj, jni.jMID, jBoxId, &jPoints[0])
+}
+
+func (v *veyronState) Draw(_ ipc.Context, stream boxes.DrawInterfaceServiceDrawStream) error {
+ for {
+ box, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return err
+ }
+ nativeJava.addBox(&box)
+ }
+ return nil
+}
+
+func (v *veyronState) sendBox(box boxes.Box) {
+ v.boxList <- box
+}
+
+func (v *veyronState) sendDrawLoop() {
+ v.boxList = make(chan boxes.Box, 256)
+ for {
+ if err := v.drawStream.Send(<-v.boxList); err != nil {
+ break
+ }
+ }
+}
+
+func (v *veyronState) registerAsPeer() {
+ var registerIP string
+ // Get an IP that can be published to the signal server
+ ifaces, err := net.InterfaceAddrs()
+ for _, addr := range ifaces {
+ if ipa, ok := addr.(*net.IPNet); ok {
+ if ip4 := ipa.IP.To4(); ip4 != nil && ip4.String() != "127.0.0.1" {
+ registerIP = ip4.String()
+ break
+ }
+ }
+ }
+
+ rtServer, err := veyron.runtime.NewServer()
+ if err != nil {
+ panic(fmt.Errorf("failed runtime.NewServer:%v\n", err))
+ }
+ drawServer := boxes.NewServerDrawInterface(&veyron)
+ if err := rtServer.Register(drawService, ipc.SoloDispatcher(drawServer, nil)); err != nil {
+ panic(fmt.Errorf("failed Register:%v\n", err))
+ }
+ endPt, err := rtServer.Listen("tcp", registerIP+drawServicePort)
+ if err != nil {
+ panic(fmt.Errorf("failed to Listen:%v\n", err))
+ }
+ if err := v.signalling.Add(endPt.String()); err != nil {
+ panic(fmt.Errorf("failed to Add endpoint to signalling server:%v\n", err))
+ }
+ if err := rtServer.Publish("/" + drawService); err != nil {
+ panic(fmt.Errorf("failed to Publish:%v\n", err))
+ }
+}
+
+func (v *veyronState) connectPeer() {
+ endPt, err := v.signalling.Get()
+ if err != nil {
+ panic(fmt.Errorf("failed to Get peer endpoint from signalling server:%v\n", err))
+ }
+ drawInterface, err := boxes.BindDrawInterface(naming.JoinAddressName(endPt, drawService))
+ if err != nil {
+ panic(fmt.Errorf("failed BindDrawInterface:%v\n", err))
+ }
+ if v.drawStream, err = drawInterface.Draw(); err != nil {
+ panic(fmt.Errorf("failed to get handle to Draw stream:%v\n", err))
+ }
+ go v.sendDrawLoop()
+}
+
+//export JNI_OnLoad
+func JNI_OnLoad(vm *C.JavaVM, reserved unsafe.Pointer) C.jint {
+ nativeJava.jVM = vm
+ return C.JNI_VERSION_1_6
+}
+
+//export Java_com_boxes_GoNative_registerAsPeer
+func Java_com_boxes_GoNative_registerAsPeer(env *C.JNIEnv) {
+ veyron.registerAsPeer()
+}
+
+//export Java_com_boxes_GoNative_connectPeer
+func Java_com_boxes_GoNative_connectPeer(env *C.JNIEnv) {
+ veyron.connectPeer()
+}
+
+//export Java_com_boxes_GoNative_sendDrawBox
+func Java_com_boxes_GoNative_sendDrawBox(env *C.JNIEnv, class C.jclass,
+ boxId C.jstring, ox C.jfloat, oy C.jfloat, cx C.jfloat, cy C.jfloat) {
+ veyron.sendBox(boxes.Box{BoxId: C.GoString(C.JToCString(env, boxId)), Points: [4]float32{float32(ox), float32(oy), float32(cx), float32(cy)}})
+}
+
+//export Java_com_boxes_GoNative_registerAddBox
+func Java_com_boxes_GoNative_registerAddBox(env *C.JNIEnv, thisObj C.jobject, obj C.jobject) {
+ nativeJava.registerAddBox(env, obj)
+}
+
+func main() {
+ veyron.runtime = rt.Init()
+ var err error
+ if veyron.signalling, err = boxes.BindBoxSignalling(naming.JoinAddressName(signallingServer, signallingService)); err != nil {
+ panic(fmt.Errorf("failed to bind to signalling server:%v\n", err))
+ }
+}
diff --git a/examples/boxes/android/src/com/boxes/AndroidManifest.xml b/examples/boxes/android/src/com/boxes/AndroidManifest.xml
new file mode 100644
index 0000000..444165a
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.bignerdranch.android.draganddraw"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="18" />
+ <!-- Permission required to use the TCP transport -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <!-- Permission required to use the Bluetooth transport -->
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.boxes.DragAndDrawActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/examples/boxes/android/src/com/boxes/Box.java b/examples/boxes/android/src/com/boxes/Box.java
new file mode 100644
index 0000000..2238fbd
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/Box.java
@@ -0,0 +1,83 @@
+package com.boxes;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import android.graphics.PointF;
+import android.os.Bundle;
+
+/*
+ * Box represents a square on a canvas that has been drawn. The
+ * ACTION_DOWN event becomes the origin of the box and the final
+ * position is when the ACTION_UP event is detected.
+ */
+public class Box {
+ // Start/End positions of the square box
+ private PointF mOrigin;
+ private PointF mCurrent;
+
+ // Unique name of this box
+ private String mBoxId;
+
+ // UniqueID that identifies which device a box came from
+ // when there are multiple peers.
+ private static AtomicInteger idCounter = new AtomicInteger(0);
+ private static String uniqueID;
+ static {
+ uniqueID = "box:" + Math.abs(new Random().nextInt());
+ }
+
+ // Initialize a new box with a given start co-ordinate.
+ public Box(PointF origin) {
+ mOrigin = mCurrent = origin;
+ mBoxId = uniqueID + "" + idCounter.incrementAndGet();
+ }
+
+ // Initialize a new box with given attributes
+ public Box(String boxId, PointF origin, PointF current) {
+ mBoxId = boxId;
+ mOrigin = origin;
+ mCurrent = current;
+ }
+
+ // Initialize a box from a packaged bundle.
+ public Box(Bundle b) {
+ final float[] points = b.getFloatArray("mPoints");
+ setOrigin(new PointF(points[0], points[1]));
+ setCurrent(new PointF(points[2], points[3]));
+ setBoxId(b.getString("mBoxId"));
+ }
+
+ // Package a box into a bundle that can be stored.
+ public Bundle toBundle() {
+ final float[] points = { mOrigin.x, mOrigin.y, mCurrent.x, mCurrent.y };
+ final Bundle bundle = new Bundle();
+ bundle.putFloatArray("mPoints", points);
+ bundle.putString("mBoxId", mBoxId);
+ return bundle;
+ }
+
+ public String getBoxId() {
+ return mBoxId;
+ }
+
+ public void setBoxId(String boxId) {
+ mBoxId = boxId;
+ }
+
+ public void setCurrent(PointF current) {
+ mCurrent = current;
+ }
+
+ public PointF getCurrent() {
+ return mCurrent;
+ }
+
+ public void setOrigin(PointF origin) {
+ mOrigin = origin;
+ }
+
+ public PointF getOrigin() {
+ return mOrigin;
+ }
+}
diff --git a/examples/boxes/android/src/com/boxes/BoxDrawingController.java b/examples/boxes/android/src/com/boxes/BoxDrawingController.java
new file mode 100644
index 0000000..f7d0556
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/BoxDrawingController.java
@@ -0,0 +1,170 @@
+package com.boxes;
+
+import java.util.ArrayList;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/*
+ * BoxDrawingController is the controller for our View. It handles touch events
+ * and updates the model as needed. If peer sync has been activated the controller
+ * is also responsible for communicating the changes to the remote device through
+ * GoRemoteService.
+ */
+public class BoxDrawingController extends View {
+ public static final String PARCELKEY = "BoxViewParcel";
+
+ private Box mCurrentBox;
+ private ArrayList<Box> mBoxes = new ArrayList<Box>();
+ private GoRemoteService mGoRemoteService;
+
+ // Color codes for the box, box boundary-line and the background canvas
+ private Paint mBoxPaint;
+ private Paint mLinePaint;
+ private Paint mBackgroundPaint;
+
+ // This method is registered with the go-shared library through JNI. When the
+ // go shared-library receives an update from a remote-device it notifies the
+ // application via this callback.
+ public void AddBox(String boxId, float oX, float oY, float cX, float cY) {
+ final Box box = new Box(boxId, new PointF(oX, oY), new PointF(cX, cY));
+ // The View can only be changed in the UI thread.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ AddRemoteBox(box);
+ }
+ });
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ final PointF current = new PointF(event.getX(), event.getY());
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mCurrentBox = new Box(current);
+ mBoxes.add(mCurrentBox);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mCurrentBox != null) {
+ mCurrentBox.setCurrent(current);
+ // Send the box over to any peer device.
+ RemoteDraw(mCurrentBox);
+ // View has changed, re-draw it.
+ invalidate();
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ mCurrentBox = null;
+ break;
+ }
+ return true;
+ }
+
+ public BoxDrawingController(Context context) {
+ this(context, null);
+ }
+
+ public BoxDrawingController(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.setSaveEnabled(true);
+
+ mBoxPaint = new Paint();
+ mBoxPaint.setColor(Color.GREEN);
+
+ mLinePaint = new Paint();
+ mLinePaint.setStrokeWidth(4f);
+ mLinePaint.setColor(Color.BLACK);
+
+ mBackgroundPaint = new Paint();
+ mBackgroundPaint.setColor(0xfff8efe0);
+
+ // Register the AddBox method with the shared-libary so that it knows
+ // which callback to use.
+ GoNative.registerAddBox(this);
+ }
+
+ // SetGoRemoteService sets the service that uses an underlying
+ // go shared-library to talk to other devices with Veyron.
+ void SetGoRemoteService(GoRemoteService goService) {
+ mGoRemoteService = goService;
+ }
+
+ // We got a remote box from another peer device. Add it to my current
+ // state and refresh the view.
+ void AddRemoteBox(Box newBox) {
+ for (Box box : mBoxes) {
+ if (newBox.getBoxId().equals(box.getBoxId())) {
+ // The box already exists. We just need to update its
+ // coordinates.
+ box.setOrigin(newBox.getOrigin());
+ box.setCurrent(newBox.getCurrent());
+ invalidate();
+ return;
+ }
+ }
+ mBoxes.add(newBox);
+ // refresh the view.
+ invalidate();
+ }
+
+ // Need to send over my entire current state to the
+ // remote device.
+ public void RemoteSync() {
+ if (mGoRemoteService != null) {
+ for (Box box : mBoxes) {
+ mGoRemoteService.RemoteDraw(box);
+ }
+ }
+ }
+
+ // Send box information over to any peer device.
+ public void RemoteDraw(Box box) {
+ if (mGoRemoteService != null) {
+ mGoRemoteService.RemoteDraw(box);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawPaint(mBackgroundPaint);
+ for (Box box : mBoxes) {
+ float left = Math.min(box.getOrigin().x, box.getCurrent().x);
+ float right = Math.max(box.getOrigin().x, box.getCurrent().x);
+ float top = Math.min(box.getOrigin().y, box.getCurrent().y);
+ float bottom = Math.max(box.getOrigin().y, box.getCurrent().y);
+ canvas.drawRect(left, top, right, bottom, mBoxPaint);
+ canvas.drawLine(box.getOrigin().x, box.getOrigin().y, box.getCurrent().x, box.getCurrent().y, mLinePaint);
+ canvas.drawLine(box.getCurrent().x, box.getOrigin().y, box.getOrigin().x, box.getCurrent().y, mLinePaint);
+ }
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable p = super.onSaveInstanceState();
+ Bundle bundle = new Bundle();
+ for (Box box : mBoxes) {
+ bundle.putBundle(box.getBoxId(), box.toBundle());
+ }
+ bundle.putParcelable(PARCELKEY, p);
+ return (Parcelable)bundle;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ Bundle bundle = (Bundle)state;
+ super.onRestoreInstanceState(bundle.getParcelable(PARCELKEY));
+ for (String s : bundle.keySet()) {
+ if (s == PARCELKEY) continue;
+ mBoxes.add(new Box(bundle.getBundle(s)));
+ }
+ }
+}
diff --git a/examples/boxes/android/src/com/boxes/DragAndDrawActivity.java b/examples/boxes/android/src/com/boxes/DragAndDrawActivity.java
new file mode 100644
index 0000000..d8c04ed
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/DragAndDrawActivity.java
@@ -0,0 +1,73 @@
+package com.boxes;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class DragAndDrawActivity extends SingleFragmentActivity {
+
+ private class DragAndDrawFragment extends Fragment {
+ private GoRemoteService mGoRemoteService;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ mGoRemoteService = new GoRemoteService();
+ mGoRemoteService.start();
+ mGoRemoteService.getLooper();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_drag_and_draw, parent, false);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_dragdraw, menu);
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final BoxDrawingController bdc =
+ (BoxDrawingController)getActivity().findViewById(R.id.boxDrawingView);
+ bdc.SetGoRemoteService(mGoRemoteService);
+
+ switch (item.getItemId()) {
+ case R.id.menu_start:
+ // Register this device with a signalling server so that other
+ // peers can find it.
+ GoNative.registerAsPeer();
+ item.setEnabled(false);
+ return true;
+ case R.id.menu_join:
+ // Join with any peer that has already registered itself with
+ // the signalling server.
+ GoNative.connectPeer();
+ // Sync my state with the remote peer.
+ bdc.RemoteSync();
+ item.setEnabled(false);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public Fragment createFragment() {
+ return new DragAndDrawFragment();
+ }
+}
diff --git a/examples/boxes/android/src/com/boxes/GoNative.java b/examples/boxes/android/src/com/boxes/GoNative.java
new file mode 100644
index 0000000..edcec6e
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/GoNative.java
@@ -0,0 +1,14 @@
+package com.boxes;
+
+import android.util.Log;
+
+public class GoNative {
+ static native void registerAsPeer();
+ static native void connectPeer();
+ static native void sendDrawBox(String boxId, float oX, float oY, float cX, float cY);
+ static native void registerAddBox(BoxDrawingController bdc);
+
+ static {
+ System.loadLibrary("boxesp2p");
+ }
+}
diff --git a/examples/boxes/android/src/com/boxes/GoRemoteService.java b/examples/boxes/android/src/com/boxes/GoRemoteService.java
new file mode 100644
index 0000000..782daf6
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/GoRemoteService.java
@@ -0,0 +1,34 @@
+package com.boxes;
+
+import android.graphics.PointF;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+
+public class GoRemoteService extends HandlerThread {
+ private static final int MESSAGE_DRAW = 0;
+ private final Handler mRequestHandler;
+
+ // Handle the send to the peer device in a separate thread.
+ public GoRemoteService() {
+ super("GoRemoteService");
+ mRequestHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_DRAW) {
+ final Box box = new Box(msg.getData());
+ final PointF origin = box.getOrigin();
+ final PointF current = box.getCurrent();
+ GoNative.sendDrawBox(box.getBoxId(), origin.x, origin.y, current.x, current.y);
+ }
+ }
+ };
+ }
+
+ // Send a box to a peer device via the go native shared library.
+ public void RemoteDraw(Box box) {
+ final Message msg = mRequestHandler.obtainMessage(MESSAGE_DRAW);
+ msg.setData(box.toBundle());
+ msg.sendToTarget();
+ }
+}
diff --git a/examples/boxes/android/src/com/boxes/SingleFragmentActivity.java b/examples/boxes/android/src/com/boxes/SingleFragmentActivity.java
new file mode 100644
index 0000000..3788895
--- /dev/null
+++ b/examples/boxes/android/src/com/boxes/SingleFragmentActivity.java
@@ -0,0 +1,28 @@
+package com.boxes;
+
+import com.boxes.R;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+
+public abstract class SingleFragmentActivity extends FragmentActivity {
+ protected abstract Fragment createFragment();
+
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+ final FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+ }
+ }
+}
diff --git a/examples/boxes/boxes.idl b/examples/boxes/boxes.idl
new file mode 100644
index 0000000..224732b
--- /dev/null
+++ b/examples/boxes/boxes.idl
@@ -0,0 +1,26 @@
+// Boxes is an android app that uses veyron to share views
+// between peer devices.
+package boxes
+
+// BoxSignalling allows peers to rendezvous with each other
+type BoxSignalling interface {
+ // Add endpoint information to the signalling server.
+ Add(Endpoint string) (Err error)
+ // Get endpoint information about a peer.
+ Get() (Endpoint string, Err error)
+}
+
+// Box describes the name and co-ordinates of a given box that
+// is displayed in the View of a peer device.
+type Box struct {
+ // BoxId is a unique name for a box
+ BoxId string
+ // Points are the co-ordinates of a given box
+ Points [4]float32
+}
+
+// DrawInterface enables adding a box on another peer
+type DrawInterface interface {
+ // Send/Receive a stream of boxes with another peer
+ Draw() stream<Box, Box> (Err error)
+}
diff --git a/examples/boxes/boxes.idl.go b/examples/boxes/boxes.idl.go
new file mode 100644
index 0000000..8de494c
--- /dev/null
+++ b/examples/boxes/boxes.idl.go
@@ -0,0 +1,451 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: boxes.idl
+
+// Boxes is an android app that uses veyron to share views
+// between peer devices.
+package boxes
+
+import (
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_idl "veyron2/idl"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt/r"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// Box describes the name and co-ordinates of a given box that
+// is displayed in the View of a peer device.
+type Box struct {
+ // BoxId is a unique name for a box
+ BoxId string
+ // Points are the co-ordinates of a given box
+ Points [4]float32
+}
+
+// BoxSignalling allows peers to rendezvous with each other
+// BoxSignalling is the interface the client binds and uses.
+// BoxSignalling_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type BoxSignalling_InternalNoTagGetter interface {
+
+ // Add endpoint information to the signalling server.
+ Add(Endpoint string, opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Get endpoint information about a peer.
+ Get(opts ..._gen_ipc.ClientCallOpt) (reply string, err error)
+}
+type BoxSignalling interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ BoxSignalling_InternalNoTagGetter
+}
+
+// BoxSignallingService is the interface the server implements.
+type BoxSignallingService interface {
+
+ // Add endpoint information to the signalling server.
+ Add(context _gen_ipc.Context, Endpoint string) (err error)
+
+ // Get endpoint information about a peer.
+ Get(context _gen_ipc.Context) (reply string, err error)
+}
+
+// BindBoxSignalling returns the client stub implementing the BoxSignalling
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindBoxSignalling(name string, opts ..._gen_ipc.BindOpt) (BoxSignalling, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubBoxSignalling{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerBoxSignalling creates a new server stub.
+//
+// It takes a regular server implementing the BoxSignallingService
+// interface, and returns a new server stub.
+func NewServerBoxSignalling(server BoxSignallingService) interface{} {
+ return &ServerStubBoxSignalling{
+ service: server,
+ }
+}
+
+// clientStubBoxSignalling implements BoxSignalling.
+type clientStubBoxSignalling struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubBoxSignalling) GetMethodTags(method string) []interface{} {
+ return GetBoxSignallingMethodTags(method)
+}
+
+func (__gen_c *clientStubBoxSignalling) Add(Endpoint string, opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Add", []interface{}{Endpoint}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBoxSignalling) Get(opts ..._gen_ipc.ClientCallOpt) (reply string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Get", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *clientStubBoxSignalling) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubBoxSignalling wraps a server that implements
+// BoxSignallingService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubBoxSignalling struct {
+ service BoxSignallingService
+}
+
+func (s *ServerStubBoxSignalling) GetMethodTags(method string) []interface{} {
+ return GetBoxSignallingMethodTags(method)
+}
+
+func (s *ServerStubBoxSignalling) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Add"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "Endpoint", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "Err", Type: 65},
+ },
+ }
+ result.Methods["Get"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "Endpoint", Type: 3},
+ {Name: "Err", Type: 65},
+ },
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (s *ServerStubBoxSignalling) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubBoxSignalling) Add(call _gen_ipc.ServerCall, Endpoint string) (err error) {
+ err = __gen_s.service.Add(call, Endpoint)
+ return
+}
+
+func (__gen_s *ServerStubBoxSignalling) Get(call _gen_ipc.ServerCall) (reply string, err error) {
+ reply, err = __gen_s.service.Get(call)
+ return
+}
+
+func GetBoxSignallingMethodTags(method string) []interface{} {
+ switch method {
+ case "Add":
+ return []interface{}{}
+ case "Get":
+ return []interface{}{}
+ default:
+ return nil
+ }
+}
+
+// DrawInterface enables adding a box on another peer
+// DrawInterface is the interface the client binds and uses.
+// DrawInterface_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type DrawInterface_InternalNoTagGetter interface {
+
+ // Send/Receive a stream of boxes with another peer
+ Draw(opts ..._gen_ipc.ClientCallOpt) (reply DrawInterfaceDrawStream, err error)
+}
+type DrawInterface interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ DrawInterface_InternalNoTagGetter
+}
+
+// DrawInterfaceService is the interface the server implements.
+type DrawInterfaceService interface {
+
+ // Send/Receive a stream of boxes with another peer
+ Draw(context _gen_ipc.Context, stream DrawInterfaceServiceDrawStream) (err error)
+}
+
+// DrawInterfaceDrawStream is the interface for streaming responses of the method
+// Draw in the service interface DrawInterface.
+type DrawInterfaceDrawStream interface {
+
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item Box) error
+
+ // CloseSend indicates to the server that no more items will be sent; server
+ // Recv calls will receive io.EOF after all sent items. Subsequent calls to
+ // Send on the client will fail. This is an optional call - it's used by
+ // streaming clients that need the server to receive the io.EOF terminator.
+ CloseSend() error
+
+ // Recv returns the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item Box, err error)
+
+ // Finish closes the stream and returns the positional return values for
+ // call.
+ Finish() (err error)
+
+ // Cancel cancels the RPC, notifying the server to stop processing.
+ Cancel()
+}
+
+// Implementation of the DrawInterfaceDrawStream interface that is not exported.
+type implDrawInterfaceDrawStream struct {
+ clientCall _gen_ipc.ClientCall
+}
+
+func (c *implDrawInterfaceDrawStream) Send(item Box) error {
+ return c.clientCall.Send(item)
+}
+
+func (c *implDrawInterfaceDrawStream) CloseSend() error {
+ return c.clientCall.CloseSend()
+}
+
+func (c *implDrawInterfaceDrawStream) Recv() (item Box, err error) {
+ err = c.clientCall.Recv(&item)
+ return
+}
+
+func (c *implDrawInterfaceDrawStream) Finish() (err error) {
+ if ierr := c.clientCall.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *implDrawInterfaceDrawStream) Cancel() {
+ c.clientCall.Cancel()
+}
+
+// DrawInterfaceServiceDrawStream is the interface for streaming responses of the method
+// Draw in the service interface DrawInterface.
+type DrawInterfaceServiceDrawStream interface {
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item Box) error
+
+ // Recv fills itemptr with the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item Box, err error)
+}
+
+// Implementation of the DrawInterfaceServiceDrawStream interface that is not exported.
+type implDrawInterfaceServiceDrawStream struct {
+ serverCall _gen_ipc.ServerCall
+}
+
+func (s *implDrawInterfaceServiceDrawStream) Send(item Box) error {
+ return s.serverCall.Send(item)
+}
+
+func (s *implDrawInterfaceServiceDrawStream) Recv() (item Box, err error) {
+ err = s.serverCall.Recv(&item)
+ return
+}
+
+// BindDrawInterface returns the client stub implementing the DrawInterface
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindDrawInterface(name string, opts ..._gen_ipc.BindOpt) (DrawInterface, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubDrawInterface{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerDrawInterface creates a new server stub.
+//
+// It takes a regular server implementing the DrawInterfaceService
+// interface, and returns a new server stub.
+func NewServerDrawInterface(server DrawInterfaceService) interface{} {
+ return &ServerStubDrawInterface{
+ service: server,
+ }
+}
+
+// clientStubDrawInterface implements DrawInterface.
+type clientStubDrawInterface struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubDrawInterface) GetMethodTags(method string) []interface{} {
+ return GetDrawInterfaceMethodTags(method)
+}
+
+func (__gen_c *clientStubDrawInterface) Draw(opts ..._gen_ipc.ClientCallOpt) (reply DrawInterfaceDrawStream, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Draw", nil, opts...); err != nil {
+ return
+ }
+ reply = &implDrawInterfaceDrawStream{clientCall: call}
+ return
+}
+
+func (c *clientStubDrawInterface) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubDrawInterface wraps a server that implements
+// DrawInterfaceService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubDrawInterface struct {
+ service DrawInterfaceService
+}
+
+func (s *ServerStubDrawInterface) GetMethodTags(method string) []interface{} {
+ return GetDrawInterfaceMethodTags(method)
+}
+
+func (s *ServerStubDrawInterface) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Draw"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "Err", Type: 65},
+ },
+ InStream: 67,
+ OutStream: 67,
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}, _gen_wiretype.ArrayType{Elem: 0x19, Len: 0x4, Name: "", Tags: []string(nil)}, _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x3, Name: "BoxId"},
+ _gen_wiretype.FieldType{Type: 0x42, Name: "Points"},
+ },
+ "Box", []string(nil)},
+ }
+
+ return result, nil
+}
+
+func (s *ServerStubDrawInterface) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubDrawInterface) Draw(call _gen_ipc.ServerCall) (err error) {
+ stream := &implDrawInterfaceServiceDrawStream{serverCall: call}
+ err = __gen_s.service.Draw(call, stream)
+ return
+}
+
+func GetDrawInterfaceMethodTags(method string) []interface{} {
+ switch method {
+ case "Draw":
+ return []interface{}{}
+ default:
+ return nil
+ }
+}
diff --git a/examples/boxes/signallingserver/main.go b/examples/boxes/signallingserver/main.go
new file mode 100644
index 0000000..08bbd0f
--- /dev/null
+++ b/examples/boxes/signallingserver/main.go
@@ -0,0 +1,60 @@
+// Binary signallingserver is a simple implementation of the BoxSignalling
+// rendezvous service.
+package main
+
+import (
+ "log"
+
+ "veyron/examples/boxes"
+ "veyron/lib/signals"
+
+ "veyron2/ipc"
+ "veyron2/rt"
+)
+
+const (
+ signallingServiceName = "signalling"
+ signallingServicePort = ":8509"
+)
+
+type boxAppEndpoint string
+
+func (b *boxAppEndpoint) Add(_ ipc.Context, Endpoint string) (err error) {
+ *b = boxAppEndpoint(Endpoint)
+ log.Printf("Added endpoint %v to signalling service", *b)
+ return nil
+}
+
+func (b *boxAppEndpoint) Get(_ ipc.Context) (Endpoint string, err error) {
+ log.Printf("Returning endpoints:%v from signalling service", *b)
+ return string(*b), nil
+}
+
+func main() {
+ r := rt.Init()
+
+ // Create a new server instance.
+ s, err := r.NewServer()
+ if err != nil {
+ log.Fatal("failed NewServer: ", err)
+ }
+
+ var boxApp boxAppEndpoint
+ srv := boxes.NewServerBoxSignalling(&boxApp)
+ if err := s.Register(signallingServiceName, ipc.SoloDispatcher(srv, nil)); err != nil {
+ log.Fatal("failed to Register signalling service: ", err)
+ }
+
+ // Create an endpoint and begin listening.
+ if endPt, err := s.Listen("tcp", signallingServicePort); err == nil {
+ log.Printf("SignallingService listening at: %v\n", endPt)
+ } else {
+ log.Fatal("failed Listen: ", err)
+ }
+
+ if err := s.Publish("/" + signallingServiceName); err != nil {
+ log.Fatal("failed Publish:", err)
+ }
+
+ <-signals.ShutdownOnSignals()
+}
diff --git a/examples/fortune/doc.go b/examples/fortune/doc.go
new file mode 100644
index 0000000..a8a09f9
--- /dev/null
+++ b/examples/fortune/doc.go
@@ -0,0 +1,3 @@
+// Package fortune contains the most basic client/server application using the
+// Veyron API. See http://go/veyron:code-lab for a thorough walk-through.
+package fortune
diff --git a/examples/fortune/fortune.idl b/examples/fortune/fortune.idl
new file mode 100644
index 0000000..7df86e2
--- /dev/null
+++ b/examples/fortune/fortune.idl
@@ -0,0 +1,11 @@
+package fortune
+
+import "veyron2/security"
+
+// Fortune allows clients to Get and Add fortune strings.
+type Fortune interface {
+ // Get returns a random fortune.
+ Get() (Fortune string, Err error) {security.ReadLabel}
+ // Add stores a fortune in the set used by Get.
+ Add(Fortune string) error {security.WriteLabel}
+}
diff --git a/examples/fortune/fortune.idl.go b/examples/fortune/fortune.idl.go
new file mode 100644
index 0000000..1def498
--- /dev/null
+++ b/examples/fortune/fortune.idl.go
@@ -0,0 +1,201 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: fortune.idl
+
+package fortune
+
+import (
+ "veyron2/security"
+
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_idl "veyron2/idl"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt/r"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// Fortune allows clients to Get and Add fortune strings.
+// Fortune is the interface the client binds and uses.
+// Fortune_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type Fortune_InternalNoTagGetter interface {
+
+ // Get returns a random fortune.
+ Get(opts ..._gen_ipc.ClientCallOpt) (reply string, err error)
+
+ // Add stores a fortune in the set used by Get.
+ Add(Fortune string, opts ..._gen_ipc.ClientCallOpt) (err error)
+}
+type Fortune interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ Fortune_InternalNoTagGetter
+}
+
+// FortuneService is the interface the server implements.
+type FortuneService interface {
+
+ // Get returns a random fortune.
+ Get(context _gen_ipc.Context) (reply string, err error)
+
+ // Add stores a fortune in the set used by Get.
+ Add(context _gen_ipc.Context, Fortune string) (err error)
+}
+
+// BindFortune returns the client stub implementing the Fortune
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindFortune(name string, opts ..._gen_ipc.BindOpt) (Fortune, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubFortune{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerFortune creates a new server stub.
+//
+// It takes a regular server implementing the FortuneService
+// interface, and returns a new server stub.
+func NewServerFortune(server FortuneService) interface{} {
+ return &ServerStubFortune{
+ service: server,
+ }
+}
+
+// clientStubFortune implements Fortune.
+type clientStubFortune struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubFortune) GetMethodTags(method string) []interface{} {
+ return GetFortuneMethodTags(method)
+}
+
+func (__gen_c *clientStubFortune) Get(opts ..._gen_ipc.ClientCallOpt) (reply string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Get", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubFortune) Add(Fortune string, opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Add", []interface{}{Fortune}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *clientStubFortune) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubFortune wraps a server that implements
+// FortuneService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubFortune struct {
+ service FortuneService
+}
+
+func (s *ServerStubFortune) GetMethodTags(method string) []interface{} {
+ return GetFortuneMethodTags(method)
+}
+
+func (s *ServerStubFortune) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Add"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "Fortune", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["Get"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "Fortune", Type: 3},
+ {Name: "Err", Type: 65},
+ },
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (s *ServerStubFortune) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubFortune) Get(call _gen_ipc.ServerCall) (reply string, err error) {
+ reply, err = __gen_s.service.Get(call)
+ return
+}
+
+func (__gen_s *ServerStubFortune) Add(call _gen_ipc.ServerCall, Fortune string) (err error) {
+ err = __gen_s.service.Add(call, Fortune)
+ return
+}
+
+func GetFortuneMethodTags(method string) []interface{} {
+ switch method {
+ case "Get":
+ return []interface{}{security.Label(1)}
+ case "Add":
+ return []interface{}{security.Label(2)}
+ default:
+ return nil
+ }
+}
diff --git a/examples/fortune/fortune/main.go b/examples/fortune/fortune/main.go
new file mode 100644
index 0000000..05021bf
--- /dev/null
+++ b/examples/fortune/fortune/main.go
@@ -0,0 +1,52 @@
+// Binary fortune is a simple client of the fortune service. See
+// http://go/veyron:code-lab for a thorough explanation.
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "veyron2"
+ "veyron2/naming"
+ "veyron2/rt"
+
+ "veyron/examples/fortune"
+)
+
+var (
+ address = flag.String("address", "", "the address/endpoint of the fortune server")
+ newFortune = flag.String("new_fortune", "", "an optional, new fortune to add to the server's set")
+ serverPattern = flag.String("server_pattern", "*", "server_pattern is an optional pattern for the expected identity of the server. It is provided as an option to all RPCs made by the client. The server's identity must match this pattern otherwise the client would abort the call (see veyron2/security.PublicID.Match). For e.g., the pattern \"veyron/fooService\" only matches servers that either have the identity \"veyron/fooService\" or the identity \"veyron\". On the other hand the pattern \"veyron/*\" matches all servers whose identities have the root name \"veyron\". If the flag is absent then the default pattern \"*\" matches all identities.")
+)
+
+func main() {
+ runtime := rt.Init()
+ log := runtime.Logger()
+ if *address == "" {
+ log.Fatal("--address needs to be specified")
+ }
+
+ // Construct a new stub that binds to serverEndpoint without
+ // using the name service
+ s, err := fortune.BindFortune(naming.JoinAddressNameFixed(*address, "fortune"))
+ if err != nil {
+ log.Fatal("error binding to server: ", err)
+ }
+
+ // Issue a Get() rpc specifying the provided pattern for the server's identity as
+ // an option. If no pattern is provided then the default pattern "*" matches all
+ // identities.
+ fortune, err := s.Get(veyron2.RemoteID(*serverPattern))
+ if err != nil {
+ log.Fatal("error getting fortune: ", err)
+ }
+ fmt.Println(fortune)
+
+ // If the user specified --new_fortune, Add() it to the server’s set of fortunes.
+ // Again, the provided pattern on the server's identity is passed as an option.
+ if *newFortune != "" {
+ if err := s.Add(*newFortune, veyron2.RemoteID(*serverPattern)); err != nil {
+ log.Fatal("error adding fortune: ", err)
+ }
+ }
+}
diff --git a/examples/fortune/fortuned/main.go b/examples/fortune/fortuned/main.go
new file mode 100644
index 0000000..a629251
--- /dev/null
+++ b/examples/fortune/fortuned/main.go
@@ -0,0 +1,85 @@
+// Binary fortuned is a simple implementation of the fortune service. See
+// http://go/veyron:code-lab for a thorough explanation.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "math/rand"
+
+ "veyron/lib/signals"
+ "veyron2/ipc"
+ "veyron2/rt"
+ "veyron2/security"
+
+ "veyron/examples/fortune"
+
+ isecurity "veyron/runtimes/google/security"
+)
+
+var acl = flag.String("acl", "", "acl is an optional JSON-encoded security.ACL. The ACL is used to construct an authorizer for the fortune server. The behavior of the authorizer can be changed at runtime by simply changing the ACL stored in the file. If the flag is absent then a nil authorizer is constructed which results in default authorization for the server. Default authorization (provided by the Veyron framework) only permits clients that have either blessed the server or have been blessed by the server.")
+
+type fortuned struct {
+ // The set of all fortunes.
+ fortunes []string
+ // Used to pick a random index in 'fortunes'.
+ random *rand.Rand
+}
+
+// newFortuned creates a new fortuned and seeds it with a few fortunes.
+func newFortuned() *fortuned {
+ return &fortuned{
+ fortunes: []string{
+ "You will reach the height of success in whatever you do.",
+ "You have remarkable power which you are not using.",
+ "Everything will now come your way.",
+ },
+ random: rand.New(rand.NewSource(99)),
+ }
+}
+
+func (f *fortuned) Get(_ ipc.Context) (Fortune string, err error) {
+ return f.fortunes[f.random.Intn(len(f.fortunes))], nil
+}
+
+func (f *fortuned) Add(_ ipc.Context, Fortune string) error {
+ f.fortunes = append(f.fortunes, Fortune)
+ return nil
+}
+
+func main() {
+ // Create the runtime
+ r := rt.Init()
+
+ // Create a new server instance.
+ s, err := r.NewServer()
+ if err != nil {
+ log.Fatal("failure creating server: ", err)
+ }
+
+ // Construct an Authorizer for the server based on the provided ACL. If
+ // no ACL is provided then a nil Authorizer is used.
+ var authorizer security.Authorizer
+ if len(*acl) != 0 {
+ authorizer = isecurity.NewFileACLAuthorizer(*acl)
+ }
+
+ // Create the fortune server stub.
+ serverFortune := fortune.NewServerFortune(newFortuned())
+
+ // Register the "fortune" prefix with a fortune dispatcher.
+ if err := s.Register("fortune", ipc.SoloDispatcher(serverFortune, authorizer)); err != nil {
+ log.Fatal("error registering service: ", err)
+ }
+
+ // Create an endpoint and begin listening.
+ if endpoint, err := s.Listen("tcp", "127.0.0.1:0"); err == nil {
+ fmt.Printf("Listening at: %v\n", endpoint)
+ } else {
+ log.Fatal("error listening to service: ", err)
+ }
+
+ // Wait forever.
+ <-signals.ShutdownOnSignals()
+}
diff --git a/examples/inspector/inspector.idl b/examples/inspector/inspector.idl
new file mode 100644
index 0000000..adeb0ad
--- /dev/null
+++ b/examples/inspector/inspector.idl
@@ -0,0 +1,18 @@
+package inspector
+
+type Details struct {
+ Name string
+ Size int64
+ Mode uint32
+ // TODO: add native time format to the idl+vom
+ // Seconds since the start of the Unix epoch
+ ModUnixSecs int64
+ // Nanoseconds in the current second
+ ModNano int32
+ IsDir bool
+}
+
+type Inspector interface {
+ Ls(Glob string) stream<_, string> error
+ LsDetails(Glob string) stream<_, Details> error
+}
diff --git a/examples/inspector/inspector.idl.go b/examples/inspector/inspector.idl.go
new file mode 100644
index 0000000..875d5e1
--- /dev/null
+++ b/examples/inspector/inspector.idl.go
@@ -0,0 +1,325 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: inspector.idl
+
+package inspector
+
+import (
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_idl "veyron2/idl"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt/r"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+type Details struct {
+ Name string
+ Size int64
+ Mode uint32
+ // TODO: add native time format to the idl+vom
+ // Seconds since the start of the Unix epoch
+ ModUnixSecs int64
+ // Nanoseconds in the current second
+ ModNano int32
+ IsDir bool
+}
+
+// Inspector is the interface the client binds and uses.
+// Inspector_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type Inspector_InternalNoTagGetter interface {
+ Ls(Glob string, opts ..._gen_ipc.ClientCallOpt) (reply InspectorLsStream, err error)
+
+ LsDetails(Glob string, opts ..._gen_ipc.ClientCallOpt) (reply InspectorLsDetailsStream, err error)
+}
+type Inspector interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ Inspector_InternalNoTagGetter
+}
+
+// InspectorService is the interface the server implements.
+type InspectorService interface {
+ Ls(context _gen_ipc.Context, Glob string, stream InspectorServiceLsStream) (err error)
+
+ LsDetails(context _gen_ipc.Context, Glob string, stream InspectorServiceLsDetailsStream) (err error)
+}
+
+// InspectorLsStream is the interface for streaming responses of the method
+// Ls in the service interface Inspector.
+type InspectorLsStream interface {
+
+ // Recv returns the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item string, err error)
+
+ // Finish closes the stream and returns the positional return values for
+ // call.
+ Finish() (err error)
+
+ // Cancel cancels the RPC, notifying the server to stop processing.
+ Cancel()
+}
+
+// Implementation of the InspectorLsStream interface that is not exported.
+type implInspectorLsStream struct {
+ clientCall _gen_ipc.ClientCall
+}
+
+func (c *implInspectorLsStream) Recv() (item string, err error) {
+ err = c.clientCall.Recv(&item)
+ return
+}
+
+func (c *implInspectorLsStream) Finish() (err error) {
+ if ierr := c.clientCall.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *implInspectorLsStream) Cancel() {
+ c.clientCall.Cancel()
+}
+
+// InspectorServiceLsStream is the interface for streaming responses of the method
+// Ls in the service interface Inspector.
+type InspectorServiceLsStream interface {
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item string) error
+}
+
+// Implementation of the InspectorServiceLsStream interface that is not exported.
+type implInspectorServiceLsStream struct {
+ serverCall _gen_ipc.ServerCall
+}
+
+func (s *implInspectorServiceLsStream) Send(item string) error {
+ return s.serverCall.Send(item)
+}
+
+// InspectorLsDetailsStream is the interface for streaming responses of the method
+// LsDetails in the service interface Inspector.
+type InspectorLsDetailsStream interface {
+
+ // Recv returns the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item Details, err error)
+
+ // Finish closes the stream and returns the positional return values for
+ // call.
+ Finish() (err error)
+
+ // Cancel cancels the RPC, notifying the server to stop processing.
+ Cancel()
+}
+
+// Implementation of the InspectorLsDetailsStream interface that is not exported.
+type implInspectorLsDetailsStream struct {
+ clientCall _gen_ipc.ClientCall
+}
+
+func (c *implInspectorLsDetailsStream) Recv() (item Details, err error) {
+ err = c.clientCall.Recv(&item)
+ return
+}
+
+func (c *implInspectorLsDetailsStream) Finish() (err error) {
+ if ierr := c.clientCall.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *implInspectorLsDetailsStream) Cancel() {
+ c.clientCall.Cancel()
+}
+
+// InspectorServiceLsDetailsStream is the interface for streaming responses of the method
+// LsDetails in the service interface Inspector.
+type InspectorServiceLsDetailsStream interface {
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item Details) error
+}
+
+// Implementation of the InspectorServiceLsDetailsStream interface that is not exported.
+type implInspectorServiceLsDetailsStream struct {
+ serverCall _gen_ipc.ServerCall
+}
+
+func (s *implInspectorServiceLsDetailsStream) Send(item Details) error {
+ return s.serverCall.Send(item)
+}
+
+// BindInspector returns the client stub implementing the Inspector
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindInspector(name string, opts ..._gen_ipc.BindOpt) (Inspector, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubInspector{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerInspector creates a new server stub.
+//
+// It takes a regular server implementing the InspectorService
+// interface, and returns a new server stub.
+func NewServerInspector(server InspectorService) interface{} {
+ return &ServerStubInspector{
+ service: server,
+ }
+}
+
+// clientStubInspector implements Inspector.
+type clientStubInspector struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubInspector) GetMethodTags(method string) []interface{} {
+ return GetInspectorMethodTags(method)
+}
+
+func (__gen_c *clientStubInspector) Ls(Glob string, opts ..._gen_ipc.ClientCallOpt) (reply InspectorLsStream, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Ls", []interface{}{Glob}, opts...); err != nil {
+ return
+ }
+ reply = &implInspectorLsStream{clientCall: call}
+ return
+}
+
+func (__gen_c *clientStubInspector) LsDetails(Glob string, opts ..._gen_ipc.ClientCallOpt) (reply InspectorLsDetailsStream, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "LsDetails", []interface{}{Glob}, opts...); err != nil {
+ return
+ }
+ reply = &implInspectorLsDetailsStream{clientCall: call}
+ return
+}
+
+func (c *clientStubInspector) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubInspector wraps a server that implements
+// InspectorService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubInspector struct {
+ service InspectorService
+}
+
+func (s *ServerStubInspector) GetMethodTags(method string) []interface{} {
+ return GetInspectorMethodTags(method)
+}
+
+func (s *ServerStubInspector) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Ls"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "Glob", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+
+ OutStream: 3,
+ }
+ result.Methods["LsDetails"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "Glob", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+
+ OutStream: 66,
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}, _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x3, Name: "Name"},
+ _gen_wiretype.FieldType{Type: 0x25, Name: "Size"},
+ _gen_wiretype.FieldType{Type: 0x34, Name: "Mode"},
+ _gen_wiretype.FieldType{Type: 0x25, Name: "ModUnixSecs"},
+ _gen_wiretype.FieldType{Type: 0x24, Name: "ModNano"},
+ _gen_wiretype.FieldType{Type: 0x2, Name: "IsDir"},
+ },
+ "Details", []string(nil)},
+ }
+
+ return result, nil
+}
+
+func (s *ServerStubInspector) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubInspector) Ls(call _gen_ipc.ServerCall, Glob string) (err error) {
+ stream := &implInspectorServiceLsStream{serverCall: call}
+ err = __gen_s.service.Ls(call, Glob, stream)
+ return
+}
+
+func (__gen_s *ServerStubInspector) LsDetails(call _gen_ipc.ServerCall, Glob string) (err error) {
+ stream := &implInspectorServiceLsDetailsStream{serverCall: call}
+ err = __gen_s.service.LsDetails(call, Glob, stream)
+ return
+}
+
+func GetInspectorMethodTags(method string) []interface{} {
+ switch method {
+ case "Ls":
+ return []interface{}{}
+ case "LsDetails":
+ return []interface{}{}
+ default:
+ return nil
+ }
+}
diff --git a/examples/inspector/inspector/main.go b/examples/inspector/inspector/main.go
new file mode 100644
index 0000000..1683b16
--- /dev/null
+++ b/examples/inspector/inspector/main.go
@@ -0,0 +1,183 @@
+// This example client illustrates how to use both the IDL stubbed API for
+// talking to a simple service (inspector) as well as the stubbles API.
+// It decides which to use based on the name of the service. If the name
+// contains a component (anywhere with it, other than the address)
+// "stubbed" then it will use the IDL stubs or if it contains a component
+// "stubless" then it will use the naked API. If neither is found then
+// an error encountered.
+// The inspector service supports three subtrees: files, proc and dev
+// representing the file system, /proc and /dev on the target system. The
+// filesystem is relative to the current working directory of the server.
+// The client will list either just the names, or optionally (-l) various
+// metadata for each item returned.
+// The results are streamed back to the client.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "time"
+
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/rt"
+ "veyron2/vlog"
+
+ "veyron/lib/signals"
+
+ "veyron/examples/inspector"
+)
+
+var (
+ service string
+ glob string
+ details bool
+)
+
+func init() {
+ flag.StringVar(&service, "service", "", "service to use")
+ flag.StringVar(&glob, "glob", "", "glob")
+ flag.BoolVar(&details, "l", false, "details")
+}
+
+func main() {
+ rt.Init()
+ _, name := naming.SplitAddressName(service)
+ // The presence of a name component of 'stubbed' or 'stubless'
+ // determines whether the client should use IDL stubs or not.
+ for _, component := range strings.Split(name, "/") {
+ switch component {
+ case "stubbed":
+ stubbed()
+ return
+ case "stubless":
+ stubless()
+ return
+ }
+ }
+ fmt.Fprintf(os.Stderr, "need to use a one of '.../stubbed/...' or '.../stubless/...'\n")
+ os.Exit(1)
+}
+
+// ls and lsdashl are idiomatic for use without stubs
+func ls(call ipc.ClientCall) {
+ for {
+ var n string
+ if err := call.Recv(&n); err != nil {
+ if err == io.EOF {
+ break
+ }
+ vlog.Fatalf("unexpected streaming error: %q", err)
+ } else {
+ fmt.Printf("%s\n", n)
+ }
+ }
+}
+
+func lsdashl(call ipc.ClientCall) {
+ type details struct {
+ Name string
+ Size int64
+ Mode os.FileMode
+ ModTime time.Time
+ IsDir bool
+ }
+ for {
+ var n details
+ if err := call.Recv(&n); err != nil {
+ if err == io.EOF {
+ break
+ }
+ vlog.Fatalf("unexpected streaming error: %q", err)
+ } else {
+ fmt.Printf("%s: %d %s %s%s\n", n.Name, n.Size, n.Mode, n.ModTime, map[bool]string{false: "", true: "/"}[n.IsDir])
+ }
+ }
+}
+
+func stubless() {
+ client, err := rt.R().NewClient()
+ if err != nil {
+ vlog.Fatalf("failed to create new client: %q", err)
+ }
+ defer client.Close()
+ call, err := client.StartCall(service, "List", []interface{}{glob, details})
+ if err != nil {
+ vlog.Fatalf("failed to start call: %q", err)
+ }
+ go func() {
+ <-signals.ShutdownOnSignals()
+ call.Cancel()
+ }()
+ if details {
+ lsdashl(call)
+ } else {
+ ls(call)
+ }
+ if err := call.Finish(); err != nil && err != io.EOF {
+ vlog.Fatalf("%q", err)
+ }
+}
+
+// streamNames and streamDetails are idiomatic for use with stubs
+func streamNames(stream inspector.InspectorLsStream) {
+ for {
+ if name, err := stream.Recv(); err != nil {
+ if err == io.EOF {
+ break
+ }
+ vlog.Fatalf("unexpected streaming error: %q", err)
+ } else {
+ fmt.Printf("%s\n", name)
+ }
+ }
+ if err := stream.Finish(); err != nil && err != io.EOF {
+ vlog.Fatalf("%q", err)
+ }
+}
+
+func streamDetails(stream inspector.InspectorLsDetailsStream) {
+ for {
+ if details, err := stream.Recv(); err != nil {
+ if err == io.EOF {
+ break
+ }
+ vlog.Fatalf("unexpected streaming error: %q", err)
+ } else {
+ mode := os.FileMode(details.Mode)
+ modtime := time.Unix(details.ModUnixSecs, int64(details.ModNano))
+ fmt.Printf("%s: %d %s %s%s\n", details.Name, details.Size, mode, modtime, map[bool]string{false: "", true: "/"}[details.IsDir])
+ }
+ }
+ if err := stream.Finish(); err != nil && err != io.EOF {
+ vlog.Fatalf("%q", err)
+ }
+
+}
+
+func stubbed() {
+ inspector, err := inspector.BindInspector(service)
+ if err != nil {
+ vlog.Fatalf("failed to create new client: %q", err)
+ }
+ bail := func(err error) {
+ vlog.Fatalf("failed to start call: %q", err)
+ }
+ if details {
+ if stream, err := inspector.LsDetails(glob); err != nil {
+ bail(err)
+ } else {
+ streamDetails(stream)
+ }
+ } else {
+ if stream, err := inspector.Ls(glob); err != nil {
+ bail(err)
+ } else {
+ streamNames(stream)
+ }
+ }
+ // TODO(cnicolaou): stubs need to expose cancel method.
+}
diff --git a/examples/inspector/inspectord/main.go b/examples/inspector/inspectord/main.go
new file mode 100644
index 0000000..662ee29
--- /dev/null
+++ b/examples/inspector/inspectord/main.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/rt"
+ "veyron2/vlog"
+)
+
+var (
+ log vlog.Logger
+ r veyron2.Runtime
+)
+
+func main() {
+ var err error
+ r = rt.Init()
+ log = r.Logger()
+ if err != nil {
+ log.Fatalf("failed to init runtime: %q", err)
+ }
+ server, err := r.NewServer()
+ if err != nil {
+ log.Fatalf("failed to create server: %q", err)
+ }
+ for stubbed, prefix := range map[bool]string{true: "stubbed/", false: "stubless/"} {
+ servers := []struct {
+ name string
+ disp ipc.Dispatcher
+ }{
+ {"files", NewFileSvc(stubbed)},
+ {"proc", NewProcSvc(stubbed)},
+ {"devices", NewDeviceSvc(stubbed)},
+ }
+ for _, s := range servers {
+ if err := server.Register(prefix+s.name, s.disp); err != nil {
+ log.Fatalf("failed to register %q: %q\n", s.name, err)
+ }
+ }
+ }
+ ep, err := server.Listen("tcp", "localhost:0")
+ if err != nil {
+ log.Fatalf("listen failed: %q", err)
+ }
+ hostname, _ := os.Hostname()
+ if err := server.Publish(hostname); err != nil {
+ log.Fatalf("publish of %q failed: %q", hostname, err)
+ }
+ fmt.Printf("%s\n", ep)
+
+ // Wait forever.
+ done := make(chan struct{})
+ <-done
+}
diff --git a/examples/inspector/inspectord/services.go b/examples/inspector/inspectord/services.go
new file mode 100644
index 0000000..b39a073
--- /dev/null
+++ b/examples/inspector/inspectord/services.go
@@ -0,0 +1,224 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "time"
+
+ "veyron2/ipc"
+ "veyron2/security"
+
+ "veyron/examples/inspector"
+)
+
+// There are three service types: files, /proc and /dev.
+type serviceType int
+
+const (
+ fileSvc serviceType = iota
+ procSvc
+ deviceSvc
+)
+
+// Dispatchers create servers, based on the names used. Dispatchers
+// create stubbed or stubless servers.
+type dispatcher struct {
+ service serviceType
+ stubbed bool
+}
+
+// A service represents one of the file, proc or device service
+type server struct {
+ service serviceType
+ root, suffix string
+}
+
+// ServerInterface defines the common methods that both stubbed and stubless
+// implementations must provided.
+type serverInterface interface {
+ send(fi os.FileInfo, details bool) error
+ cancelled() bool
+}
+
+type stublessServer struct {
+ call ipc.ServerCall
+}
+
+func (s *stublessServer) send(fi os.FileInfo, details bool) error {
+ if !details {
+ return s.call.Send(fi.Name())
+ }
+ d := struct {
+ Name string
+ Size int64
+ Mode os.FileMode
+ ModTime time.Time
+ IsDir bool
+ }{
+ Name: fi.Name(),
+ Size: fi.Size(),
+ Mode: fi.Mode(),
+ ModTime: fi.ModTime(),
+ IsDir: fi.IsDir(),
+ }
+ return s.call.Send(d)
+}
+
+func (s *stublessServer) cancelled() bool {
+ return s.call.IsClosed()
+}
+
+type stubbedServer struct {
+ context ipc.Context
+ names inspector.InspectorServiceLsStream
+ details inspector.InspectorServiceLsDetailsStream
+}
+
+func (s *stubbedServer) send(fi os.FileInfo, details bool) error {
+ if !details {
+ return s.names.Send(fi.Name())
+ }
+ return s.details.Send(inspector.Details{
+ Name: fi.Name(),
+ Size: fi.Size(),
+ Mode: uint32(fi.Mode()),
+ ModUnixSecs: fi.ModTime().Unix(),
+ ModNano: int32(fi.ModTime().Nanosecond()),
+ IsDir: fi.IsDir(),
+ })
+}
+
+func (s *stubbedServer) cancelled() bool {
+ return s.context.IsClosed()
+}
+
+// ls is shared by both stubbed and stubless calls and contains
+// the bulk of the actual server code.
+func (s *server) ls(glob string, details bool, impl serverInterface) error {
+ // validate the glob pattern
+ if _, err := filepath.Match(glob, ""); err != nil {
+ return err
+ }
+ ch := make(chan []os.FileInfo, 10)
+ errch := make(chan error, 1)
+ go readdir(filepath.Join(s.root, s.suffix), glob, ch, errch)
+ for {
+ select {
+ case fs := <-ch:
+ if len(fs) == 0 {
+ return nil
+ }
+ for _, f := range fs {
+ if err := impl.send(f, details); err != nil {
+ return err
+ }
+ }
+ case err := <-errch:
+ // Flush any buffered data in the data channel when
+ // an error is encountered.
+ for fs := range ch {
+ for _, f := range fs {
+ impl.send(f, details)
+ }
+ }
+ return err
+ case <-time.After(time.Second):
+ return fmt.Errorf("timed out reading info")
+ }
+ if impl.cancelled() {
+ // TODO(cnicolaou): we most likely need to flush
+ // and clear channels here, otherwise we're leaking
+ // goroutines and channels.
+ // TODO(cnicolaou): test this...
+ return fmt.Errorf("cancelled")
+ }
+ }
+ return nil
+}
+
+// List is a stubless server method
+func (s *server) List(call ipc.ServerCall, glob string, details bool) error {
+ log.Infof("List: %q details %t", glob, details)
+ return s.ls(glob, details, &stublessServer{call})
+}
+
+// Ls is a stubbed server method
+func (s *server) Ls(context ipc.Context, Glob string, Stream inspector.InspectorServiceLsStream) error {
+ log.Infof("Ls %q", Glob)
+ return s.ls(Glob, false, &stubbedServer{context: context, names: Stream})
+}
+
+// LsDetails is a stubbed server method
+func (s *server) LsDetails(context ipc.Context, Glob string, Stream inspector.InspectorServiceLsDetailsStream) error {
+ log.Infof("LsDetails %q", Glob)
+ return s.ls(Glob, true, &stubbedServer{context: context, details: Stream})
+}
+
+func (d *dispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
+ s := &server{service: d.service, suffix: suffix}
+ switch d.service {
+ case fileSvc:
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, nil, err
+ }
+ s.root = cwd
+ case deviceSvc:
+ s.root = "/dev"
+ case procSvc:
+ s.root = "/proc"
+ }
+ if d.stubbed {
+ return ipc.ReflectInvoker(inspector.NewServerInspector(s)), nil, nil
+ } else {
+ return ipc.ReflectInvoker(s), nil, nil
+ }
+}
+
+func NewFileSvc(stubbed bool) ipc.Dispatcher {
+ return &dispatcher{service: fileSvc, stubbed: stubbed}
+}
+
+func NewProcSvc(stubbed bool) ipc.Dispatcher {
+ return &dispatcher{service: procSvc, stubbed: stubbed}
+}
+
+func NewDeviceSvc(stubbed bool) ipc.Dispatcher {
+ return &dispatcher{service: deviceSvc, stubbed: stubbed}
+}
+
+func readdir(dirname, glob string, ch chan []os.FileInfo, errch chan error) {
+ defer close(ch)
+ defer close(errch)
+ dir, err := os.Open(dirname)
+ if err != nil {
+ errch <- err
+ return
+ }
+ n := cap(ch)
+ for {
+ entries, err := dir.Readdir(n)
+ if err != nil && err != io.EOF {
+ errch <- err
+ return
+ }
+ if len(glob) == 0 {
+ ch <- entries
+ } else {
+ matches := make([]os.FileInfo, 0, len(entries))
+ for _, e := range entries {
+ if m, _ := filepath.Match(glob, e.Name()); m {
+ matches = append(matches, e)
+ }
+ }
+ if len(matches) > 0 {
+ ch <- matches
+ }
+ }
+ if err == io.EOF {
+ return
+ }
+ }
+}
diff --git a/examples/runtime/complex_server.go b/examples/runtime/complex_server.go
new file mode 100644
index 0000000..4d60a24
--- /dev/null
+++ b/examples/runtime/complex_server.go
@@ -0,0 +1,145 @@
+package runtime
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+
+ "veyron2/rt"
+)
+
+// complexServerProgram demonstrates the recommended way to write a more complex
+// server application (with several servers, a mix of interruptible and blocking
+// cleanup, and parallel and sequential cleanup execution). For a more typical
+// server, see simpleServerProgram.
+func complexServerProgram() {
+ // Initialize the runtime. This is boilerplate.
+ r := rt.Init()
+ // r.Shutdown is optional, but it's a good idea to clean up, especially
+ // since it takes care of flushing the logs before exiting.
+ defer r.Shutdown()
+
+ // Create a couple servers, and start serving.
+ server1 := makeServer()
+ server2 := makeServer()
+
+ // This is how to wait for a shutdown. In this example, a shutdown
+ // comes from a signal or a stop command.
+ var done sync.WaitGroup
+ done.Add(1)
+
+ // This is how to configure signal handling to allow clean shutdown.
+ sigChan := make(chan os.Signal, 2)
+ signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
+
+ // This is how to configure handling of stop commands to allow clean
+ // shutdown.
+ stopChan := make(chan string, 2)
+ r.WaitForStop(stopChan)
+
+ // Blocking is used to prevent the process from exiting upon receiving a
+ // second signal or stop command while critical cleanup code is
+ // executing.
+ var blocking sync.WaitGroup
+ blocking.Add(1)
+
+ // This is how to wait for a signal or stop command and initiate the
+ // clean shutdown steps.
+ go func() {
+ // First signal received.
+ select {
+ case sig := <-sigChan:
+ // If the developer wants to take different actions
+ // depending on the type of signal, they can do it here.
+ fmt.Println("Received signal", sig)
+ case stop := <-stopChan:
+ fmt.Println("Stop", stop)
+ }
+ // This commences the cleanup stage.
+ done.Done()
+ // Wait for a second signal or stop command, and force an exit,
+ // but only once all blocking cleanup code (if any) has
+ // completed.
+ select {
+ case <-sigChan:
+ case <-stopChan:
+ }
+ blocking.Wait()
+ os.Exit(1)
+ }()
+
+ // This communicates to the parent test driver process in our unit test
+ // that this server is ready and waiting on signals or stop commands.
+ // It's purely an artifact of our test setup.
+ fmt.Println("Ready")
+
+ // Wait for shutdown.
+ done.Wait()
+
+ // Stop the servers. In this example we stop them in goroutines to
+ // parallelize the wait, but if there was a dependency between the
+ // servers, the developer can simply stop them sequentially.
+ var waitServerStop sync.WaitGroup
+ waitServerStop.Add(2)
+ go func() {
+ server1.Stop()
+ waitServerStop.Done()
+ }()
+ go func() {
+ server2.Stop()
+ waitServerStop.Done()
+ }()
+ waitServerStop.Wait()
+
+ // This is where all cleanup code should go. By placing it at the end,
+ // we make its purpose and order of execution clear.
+
+ // This is an example of how to mix parallel and sequential cleanup
+ // steps. Most real-world servers will likely be simpler, with either
+ // just sequential or just parallel cleanup stages.
+
+ // parallelCleanup is used to wait for all goroutines executing cleanup
+ // code in parallel to finish.
+ var parallelCleanup sync.WaitGroup
+
+ // Simulate four parallel cleanup steps, two blocking and two
+ // interruptible.
+ parallelCleanup.Add(1)
+ blocking.Add(1)
+ go func() {
+ fmt.Println("Parallel blocking cleanup1")
+ blocking.Done()
+ parallelCleanup.Done()
+ }()
+
+ parallelCleanup.Add(1)
+ blocking.Add(1)
+ go func() {
+ fmt.Println("Parallel blocking cleanup2")
+ blocking.Done()
+ parallelCleanup.Done()
+ }()
+
+ parallelCleanup.Add(1)
+ go func() {
+ fmt.Println("Parallel interruptible cleanup1")
+ parallelCleanup.Done()
+ }()
+
+ parallelCleanup.Add(1)
+ go func() {
+ fmt.Println("Parallel interruptible cleanup2")
+ parallelCleanup.Done()
+ }()
+
+ // Simulate two sequential cleanup steps, one blocking and one
+ // interruptible.
+ fmt.Println("Sequential blocking cleanup")
+ blocking.Done()
+
+ fmt.Println("Sequential interruptible cleanup")
+
+ parallelCleanup.Wait()
+}
diff --git a/examples/runtime/doc.go b/examples/runtime/doc.go
new file mode 100644
index 0000000..4a334b3
--- /dev/null
+++ b/examples/runtime/doc.go
@@ -0,0 +1,7 @@
+// Package runtime contains example Veyron programs to demonstrate correct,
+// idiomatic usage of the Veyron runtime APIs.
+package runtime
+
+// TODO(caprita): Using these as a basis, develop a suite of codelab examples
+// that introduce developers to writing servers (stateful and stateless),
+// setting up their shutdown, etc.
diff --git a/examples/runtime/runtime_test.go b/examples/runtime/runtime_test.go
new file mode 100644
index 0000000..0b172d6
--- /dev/null
+++ b/examples/runtime/runtime_test.go
@@ -0,0 +1,202 @@
+package runtime
+
+import (
+ "fmt"
+ "syscall"
+ "testing"
+ "time"
+
+ "veyron/lib/signals"
+ _ "veyron/lib/testutil"
+ "veyron/lib/testutil/blackbox"
+ "veyron2/mgmt"
+)
+
+// TestHelperProcess is boilerplate for the blackbox setup.
+func TestHelperProcess(t *testing.T) {
+ blackbox.HelperProcess(t)
+}
+
+func init() {
+ blackbox.CommandTable["simpleServerProgram"] = func([]string) {
+ // This is part of the test setup -- we need a way to accept
+ // commands from the parent process to simulate Stop and
+ // RemoteStop commands that would normally be issued from
+ // application code.
+ defer remoteCmdLoop()()
+ simpleServerProgram()
+ }
+}
+
+// TestSimpleServerSignal verifies that sending a signal to the simple server
+// causes it to exit cleanly.
+func TestSimpleServerSignal(t *testing.T) {
+ c := blackbox.HelperCommand(t, "simpleServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGINT)
+ c.Expect("Received signal interrupt")
+ c.Expect("Interruptible cleanup")
+ c.Expect("Deferred cleanup")
+ c.WriteLine("close")
+ c.ExpectEOFAndWait()
+}
+
+// TestSimpleServerLocalStop verifies that sending a local stop command to the
+// simple server causes it to exit cleanly.
+func TestSimpleServerLocalStop(t *testing.T) {
+ c := blackbox.HelperCommand(t, "simpleServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ c.WriteLine("stop")
+ c.Expect(fmt.Sprintf("Received signal %s", mgmt.LocalStop))
+ c.Expect("Interruptible cleanup")
+ c.Expect("Deferred cleanup")
+ c.WriteLine("close")
+ c.ExpectEOFAndWait()
+}
+
+// TestSimpleServerDoubleSignal verifies that sending a succession of two
+// signals to the simple server causes it to initiate the cleanup sequence on
+// the first signal and then exit immediately on the second signal.
+func TestSimpleServerDoubleSignal(t *testing.T) {
+ c := blackbox.HelperCommand(t, "simpleServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGINT)
+ c.Expect("Received signal interrupt")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGINT)
+ c.WaitForEOF(time.Second)
+ c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", signals.DoubleStopExitCode))
+}
+
+// TestSimpleServerLocalForceStop verifies that sending a local ForceStop
+// command to the simple server causes it to exit immediately.
+func TestSimpleServerLocalForceStop(t *testing.T) {
+ c := blackbox.HelperCommand(t, "simpleServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ c.WriteLine("forcestop")
+ c.Expect("straight exit")
+ c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", mgmt.ForceStopExitCode))
+}
+
+// TestSimpleServerKill demonstrates that a SIGKILL still forces the server
+// to exit regardless of our signal handling.
+func TestSimpleServerKill(t *testing.T) {
+ c := blackbox.HelperCommand(t, "simpleServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGKILL)
+ c.ExpectEOFAndWaitForExitCode(fmt.Errorf("signal: killed"))
+}
+
+func init() {
+ blackbox.CommandTable["complexServerProgram"] = func([]string) {
+ // This is part of the test setup -- we need a way to accept
+ // commands from the parent process to simulate Stop and
+ // RemoteStop commands that would normally be issued from
+ // application code.
+ defer remoteCmdLoop()()
+ complexServerProgram()
+ }
+}
+
+// TestComplexServerSignal verifies that sending a signal to the complex server
+// initiates the cleanup sequence in that server (we observe the printouts
+// corresponding to all the simulated sequential/parallel and
+// blocking/interruptible shutdown steps), and then exits cleanly.
+func TestComplexServerSignal(t *testing.T) {
+ c := blackbox.HelperCommand(t, "complexServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGINT)
+ c.Expect("Received signal interrupt")
+ c.ExpectSet([]string{
+ "Sequential blocking cleanup",
+ "Sequential interruptible cleanup",
+ "Parallel blocking cleanup1",
+ "Parallel blocking cleanup2",
+ "Parallel interruptible cleanup1",
+ "Parallel interruptible cleanup2",
+ })
+ c.WriteLine("close")
+ c.ExpectEOFAndWait()
+}
+
+// TestComplexServerLocalStop verifies that sending a local stop command to the
+// complex server initiates the cleanup sequence in that server (we observe the
+// printouts corresponding to all the simulated sequential/parallel and
+// blocking/interruptible shutdown steps), and then exits cleanly.
+func TestComplexServerLocalStop(t *testing.T) {
+ c := blackbox.HelperCommand(t, "complexServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ c.WriteLine("stop")
+ c.Expect(fmt.Sprintf("Stop %s", mgmt.LocalStop))
+ c.ExpectSet([]string{
+ "Sequential blocking cleanup",
+ "Sequential interruptible cleanup",
+ "Parallel blocking cleanup1",
+ "Parallel blocking cleanup2",
+ "Parallel interruptible cleanup1",
+ "Parallel interruptible cleanup2",
+ })
+ c.WriteLine("close")
+ c.ExpectEOFAndWait()
+}
+
+// TestComplexServerDoubleSignal verifies that sending a succession of two
+// signals to the complex server has the expected effect: the first signal
+// initiates the cleanup steps and the second signal kills the process, but only
+// after the blocking shutdown steps were allowed to complete (as observed by
+// the corresponding printouts from the server). Note that we have no
+// expectations on whether or not the interruptible shutdown steps execute.
+func TestComplexServerDoubleSignal(t *testing.T) {
+ c := blackbox.HelperCommand(t, "complexServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGINT)
+ c.Expect("Received signal interrupt")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGINT)
+ c.ExpectSetEventually([]string{
+ "Sequential blocking cleanup",
+ "Parallel blocking cleanup1",
+ "Parallel blocking cleanup2",
+ }, time.Second)
+ c.WaitForEOF(time.Second)
+ c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", signals.DoubleStopExitCode))
+}
+
+// TestComplexServerLocalForceStop verifies that sending a local ForceStop
+// command to the complex server forces it to exit immediately.
+func TestComplexServerLocalForceStop(t *testing.T) {
+ c := blackbox.HelperCommand(t, "complexServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ c.WriteLine("forcestop")
+ c.Expect("straight exit")
+ c.ExpectEOFAndWaitForExitCode(fmt.Errorf("exit status %d", mgmt.ForceStopExitCode))
+}
+
+// TestComplexServerKill demonstrates that a SIGKILL still forces the server to
+// exit regardless of our signal handling.
+func TestComplexServerKill(t *testing.T) {
+ c := blackbox.HelperCommand(t, "complexServerProgram")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("Ready")
+ syscall.Kill(c.Cmd.Process.Pid, syscall.SIGKILL)
+ c.ExpectEOFAndWaitForExitCode(fmt.Errorf("signal: killed"))
+}
+
+// TODO(caprita): Also demonstrate an example client application.
diff --git a/examples/runtime/simple_server.go b/examples/runtime/simple_server.go
new file mode 100644
index 0000000..3fc9a85
--- /dev/null
+++ b/examples/runtime/simple_server.go
@@ -0,0 +1,64 @@
+package runtime
+
+import (
+ "fmt"
+
+ "veyron/lib/signals"
+
+ "veyron2/rt"
+)
+
+// simpleServerProgram demonstrates the recommended way to write a typical
+// simple server application (with one server and a clean shutdown triggered by
+// a signal or a stop command). For an example of something more involved, see
+// complexServerProgram.
+func simpleServerProgram() {
+ // Initialize the runtime. This is boilerplate.
+ r := rt.Init()
+
+ // r.Shutdown is optional, but it's a good idea to clean up, especially
+ // since it takes care of flushing the logs before exiting.
+ //
+ // We use defer to ensure this is the last thing in the program (to
+ // avoid shutting down the runtime while it may still be in use), and to
+ // allow it to execute even if a panic occurs down the road.
+ defer r.Shutdown()
+
+ // Create a server, and start serving.
+ server := makeServer()
+
+ // This is how to wait for a shutdown. In this example, a shutdown
+ // comes from a signal or a stop command.
+ //
+ // Note, if the developer wants to exit immediately upon receiving a
+ // signal or stop command, they can skip this, in which case the default
+ // behavior is for the process to exit.
+ waiter := signals.ShutdownOnSignals()
+
+ // This communicates to the parent test driver process in our unit test
+ // that this server is ready and waiting on signals or stop commands.
+ // It's purely an artifact of our test setup.
+ fmt.Println("Ready")
+
+ // Use defer for anything that should still execute even if a panic
+ // occurs.
+ defer fmt.Println("Deferred cleanup")
+
+ // Wait for shutdown.
+ sig := <-waiter
+ // The developer could take different actions depending on the type of
+ // signal.
+ fmt.Println("Received signal", sig)
+
+ // Cleanup code starts here. Alternatively, these steps could be
+ // invoked through defer, but we list them here to make the order of
+ // operations obvious.
+
+ // Stop the server.
+ server.Stop()
+
+ // Note, this will not execute in cases of forced shutdown
+ // (e.g. SIGSTOP), when the process calls os.Exit (e.g. via log.Fatal),
+ // or when a panic occurs.
+ fmt.Println("Interruptible cleanup")
+}
diff --git a/examples/runtime/utils.go b/examples/runtime/utils.go
new file mode 100644
index 0000000..2b4540c
--- /dev/null
+++ b/examples/runtime/utils.go
@@ -0,0 +1,57 @@
+package runtime
+
+import (
+ "fmt"
+
+ "veyron2/ipc"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/vlog"
+
+ _ "veyron/lib/testutil"
+ "veyron/lib/testutil/blackbox"
+)
+
+// dispatcher is a simple no-op dispatcher we use for setting up example
+// servers.
+type dispatcher struct{}
+
+func (dispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
+ return nil, nil, nil
+}
+
+// makeServer sets up a simple dummy server.
+func makeServer() ipc.Server {
+ server, err := rt.R().NewServer()
+ if err != nil {
+ vlog.Fatalf("r.NewServer error: %s", err)
+ }
+ if err := server.Register("", new(dispatcher)); err != nil {
+ vlog.Fatalf("server.Register error: %s", err)
+ }
+ if _, err := server.Listen("tcp", "127.0.0.1:0"); err != nil {
+ vlog.Fatalf("server.Listen error: %s", err)
+ }
+ return server
+}
+
+// remoteCmdLoop listens on stdin and interprets commands sent over stdin (from
+// the parent process).
+func remoteCmdLoop() func() {
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ for {
+ switch blackbox.ReadLineFromStdin() {
+ case "stop":
+ rt.R().Stop()
+ case "forcestop":
+ fmt.Println("straight exit")
+ rt.R().ForceStop()
+ case "close":
+ return
+ }
+ }
+ }()
+ return func() { <-done }
+}
diff --git a/examples/storage/mdb/mdb_init/main.go b/examples/storage/mdb/mdb_init/main.go
new file mode 100644
index 0000000..0cd0f7b
--- /dev/null
+++ b/examples/storage/mdb/mdb_init/main.go
@@ -0,0 +1,390 @@
+// mdb_init is a tool to initialize the store with an initial database. This is
+// really for demo purposes; in a real database, the contents would be persistant.
+//
+// The contents are loaded from JSON format. See mdb/templates/contents.json
+// for the actual input.
+//
+// Since JSON doesn't support all of the store types, there is a translation
+// phase, where the contents are loaded into a string form, then converted to
+// the mdb/schema schema.
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "veyron/examples/storage/mdb/schema"
+ "veyron2"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/storage"
+ "veyron2/storage/vstore"
+ "veyron2/vlog"
+)
+
+var (
+ storeName string
+ templatesDir = flag.String("templates", "templates", "Name of the templates directory")
+
+ loadContents = flag.Bool("load-contents", false, "Load contents")
+ loadTemplates = flag.Bool("load-templates", false, "Load templates")
+ loadAll = flag.Bool("load-all", false, "Load everything")
+)
+
+// Movie is the JSON representation for schema.Movie.
+type Movie struct {
+ Image string
+ Title string
+ Summary string
+ Language string
+ ReleaseDate string
+ Runtime uint
+ Genre string
+ Director string
+}
+
+// Part is the JSON representation for schema.Part.
+type Part struct {
+ Movie string
+ Actor string
+ Character string
+}
+
+// Person is the JSON representation for schema.Person.
+type Person struct {
+ Name string
+ BirthDate string
+ Image string
+}
+
+// Review is the JSON representation for schema.Review.
+type Review struct {
+ Movie string
+ Rating uint8
+ Text string
+}
+
+// Contents is the JSON object containing the initial store state.
+type Contents struct {
+ People map[string]*Person
+ Parts map[string]*Part
+ Movies map[string]*Movie
+ Reviews map[string]*Review
+}
+
+// state is the initial store state.
+type state struct {
+ store storage.Store
+ transaction storage.Transaction
+ idTable map[string]*value
+}
+
+// value holds the ID and name of a stored value.
+type value struct {
+ id storage.ID
+ path string
+}
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ dir := "global/vstore/" + hostname + "/" + username
+ flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
+}
+
+// parseDate converts from a string data <day>/<month>/<year> UTC to a numeric
+// date represented in nanoseconds since the Unix epoch.
+func parseDate(date string) (int64, error) {
+ parts := strings.Split(date, "/")
+ if len(parts) != 3 {
+ return 0, fmt.Errorf("Bad date: %s", date)
+ }
+ day, err := strconv.Atoi(parts[0])
+ if err != nil {
+ return 0, err
+ }
+ month, err := strconv.Atoi(parts[1])
+ if err != nil {
+ return 0, err
+ }
+ year, err := strconv.Atoi(parts[2])
+ if err != nil {
+ return 0, err
+ }
+ t := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
+ return t.UnixNano(), nil
+}
+
+// parseDuration converts from seconds to nanoseconds.
+func parseDuration(dur uint) int64 {
+ return int64(dur) * 1000000000
+}
+
+// newState returns a fresh state.
+func newState(st storage.Store) *state {
+ return &state{store: st, idTable: make(map[string]*value)}
+}
+
+// find fetches a value from the store.
+func (st *state) find(name string) *value {
+ return st.idTable[name]
+}
+
+// put adds a value to the store, creating the path to the value if it doesn't
+// already exist.
+func (st *state) put(path string, v interface{}) {
+ vlog.Infof("Storing %q = %+v", path, v)
+ st.makeParentDirs(path)
+ if _, err := st.store.Bind(path).Put(st.transaction, v); err != nil {
+ vlog.Infof("put failed: %s: %s", path, err)
+ return
+ }
+}
+
+// putNamed adds a value to the store, similar to put, but it also adds the
+// value to the idTable using a symbolic name.
+func (st *state) putNamed(name, path string, v interface{}) {
+ vlog.Infof("Storing %s: %q = %+v", name, path, v)
+ st.makeParentDirs(path)
+ s, err := st.store.Bind(path).Put(st.transaction, v)
+ if err != nil {
+ vlog.Infof("Put failed: %s: %s", path, err)
+ return
+ }
+ st.idTable[name] = &value{id: s.ID, path: path}
+}
+
+// makeParentDirs creates the directories in a path if they do not already
+// exiast.
+func (st *state) makeParentDirs(path string) {
+ l := strings.Split(path, "/")
+ for i, _ := range l {
+ prefix := filepath.Join(l[:i]...)
+ o := st.store.Bind(prefix)
+ if _, err := o.Get(st.transaction); err != nil {
+ if _, err := o.Put(st.transaction, &schema.Dir{}); err != nil {
+ vlog.Infof("Error creating parent %q: %s", prefix, err)
+ }
+ }
+ }
+}
+
+// newTransaction starts a new transaction.
+func (st *state) newTransaction() {
+ st.transaction = vstore.NewTransaction()
+}
+
+// commit commits the current transaction.
+func (st *state) commit() {
+ if err := st.transaction.Commit(); err != nil {
+ vlog.Infof("Failed to commit transaction: %s", err)
+ }
+ st.transaction = nil
+}
+
+// storeContents saves each of the values in the Contents to the store.
+func (st *state) storeContents(c *Contents) {
+ for name, p := range c.People {
+ st.storePerson(name, p)
+ }
+ for name, m := range c.Movies {
+ st.storeMovie(name, m)
+ }
+ for name, p := range c.Parts {
+ st.storePart(name, p)
+ }
+ for name, r := range c.Reviews {
+ st.storeReview(name, r)
+ }
+}
+
+// storePerson saves a schema.Person to the store with the name /people/<Name>.
+func (st *state) storePerson(name string, p *Person) {
+ date, err := parseDate(p.BirthDate)
+ if err != nil {
+ vlog.Infof("Invalid date: %s", err)
+ return
+ }
+ x := &schema.Person{
+ Name: p.Name,
+ BirthDate: date,
+ Image: p.Image,
+ }
+ path := "/people/" + p.Name
+ st.putNamed(name, path, x)
+}
+
+// storeMovie saves a schema.Movie to the store with the name /movie/<Title>.
+func (st *state) storeMovie(name string, m *Movie) {
+ releaseDate, err := parseDate(m.ReleaseDate)
+ if err != nil {
+ vlog.Infof("Invalid date: %s", err)
+ return
+ }
+ runtime := parseDuration(m.Runtime)
+ director := st.find(m.Director)
+ if director == nil {
+ vlog.Infof("Can't find director: %s", m.Director)
+ return
+ }
+ x := &schema.Movie{
+ Image: m.Image,
+ Title: m.Title,
+ Summary: m.Summary,
+ Language: m.Language,
+ ReleaseDate: releaseDate,
+ Runtime: runtime,
+ Genre: m.Genre,
+ Director: director.id,
+ }
+ path := "/movies/" + x.Title
+ st.putNamed(name, path, x)
+}
+
+// storePart saves a schema.Part to the store, with the name /movie/<Title>/Cast/<id>.
+func (st *state) storePart(name string, p *Part) {
+ movie := st.find(p.Movie)
+ if movie == nil {
+ vlog.Infof("Can't find movie %s", p.Movie)
+ return
+ }
+ actor := st.find(p.Actor)
+ if movie == nil {
+ vlog.Infof("Can't find actor %s", p.Actor)
+ return
+ }
+ x := &schema.Part{
+ Actor: actor.id,
+ Character: p.Character,
+ }
+ path := fmt.Sprintf("%s/Cast/%s", movie.path, name)
+ st.putNamed(name, path, x)
+}
+
+// storeReview saves a review to the store, with the name /movie/<Title>/Reviews/<id>.
+func (st *state) storeReview(name string, r *Review) {
+ movie := st.find(r.Movie)
+ if movie == nil {
+ vlog.Infof("Can't find movie %s", r.Movie)
+ return
+ }
+ x := &schema.Review{
+ Rating: r.Rating,
+ Text: r.Text,
+ }
+ path := fmt.Sprintf("%s/Reviews/%s", movie.path, name)
+ st.putNamed(name, path, x)
+}
+
+// processJSONFile saves the contents of the JSON file to the store.
+func (st *state) processJSONFile(path string) error {
+ vlog.Infof("Loading file %s", path)
+ file, err := os.Open(path)
+ if err != nil {
+ return fmt.Errorf("Can't open %q: %s", path, err)
+ }
+ defer file.Close()
+
+ contents := &Contents{}
+ decoder := json.NewDecoder(file)
+ if err := decoder.Decode(contents); err != nil {
+ return fmt.Errorf("Can't decode: %s", err)
+ }
+
+ st.newTransaction()
+ st.storeContents(contents)
+ st.commit()
+ return nil
+}
+
+// processTemplateFile saves a template file to the store as a string. The name
+// is /templates/<path> where <path> is the filesystem path to the template
+// file.
+func (st *state) processTemplateFile(path, name string) error {
+ vlog.Infof("Adding template %s", path)
+ s, err := ioutil.ReadFile(path)
+ if err != nil {
+ return fmt.Errorf("Can't read %q: %s", path, err)
+ }
+
+ templateName := filepath.ToSlash("templates" + name)
+ st.newTransaction()
+ st.put(templateName, string(s))
+ st.commit()
+ return nil
+}
+
+// processRawFile saves the file contents to the store as a string, using the
+// filesystem path as the store name.
+func (st *state) processRawFile(path, name string) error {
+ vlog.Infof("Adding raw file %s", path)
+ s, err := ioutil.ReadFile(path)
+ if err != nil {
+ return fmt.Errorf("Can't read %q: %s", path, err)
+ }
+
+ st.newTransaction()
+ st.put(name, string(s))
+ st.commit()
+ return nil
+}
+
+// processFile stores the contens of the file to the store.
+func (st *state) processFile(path, name string) error {
+ switch filepath.Ext(path) {
+ case ".json":
+ if *loadAll || *loadContents {
+ return st.processJSONFile(path)
+ }
+ case ".tmpl":
+ if *loadAll || *loadTemplates {
+ return st.processTemplateFile(path, strings.TrimSuffix(name, ".tmpl"))
+ }
+ case ".css":
+ if *loadAll || *loadTemplates {
+ return st.processRawFile(path, name)
+ }
+ }
+ return nil
+}
+
+// main reads all the files in the templates directory and adds them to the
+// store.
+func main() {
+ // The client's identity needs to match the Admin ACLs at the empty
+ // store (since only the admin can put data). The identity here
+ // matches with that used for server.ServerConfig.Admin in
+ // mdb_stored/main.go. An alternative would be to relax the ACLs on
+ // the store.
+ rt.Init(veyron2.LocalID(security.FakePrivateID("anonymous")))
+
+ vlog.Infof("Binding to store on %s", storeName)
+ st, err := vstore.New(storeName)
+ if err != nil {
+ vlog.Fatalf("Can't connect to store: %s: %s", storeName, err)
+ }
+ state := newState(st)
+
+ // Store all templates.
+ filepath.Walk(*templatesDir, func(path string, _ os.FileInfo, _ error) error {
+ err := state.processFile(path, strings.TrimPrefix(path, *templatesDir))
+ if err != nil {
+ vlog.Infof("%s: %s", path, err)
+ }
+ return err
+ })
+}
diff --git a/examples/storage/mdb/mdb_stored/main.go b/examples/storage/mdb/mdb_stored/main.go
new file mode 100644
index 0000000..e067ff3
--- /dev/null
+++ b/examples/storage/mdb/mdb_stored/main.go
@@ -0,0 +1,99 @@
+// Package mdb_stored is a storage server using the mdb/schema schema.
+// The reason why we have a schema-specific store is because the current store
+// implementation does not support unregistered types.
+//
+// TODO(jyh): Support unregistered types and remove this server.
+//
+// Usage:
+//
+// stored [--name=<mount>] [--db=<dbName>]
+//
+// - <name> is the Veyron mount point name, default /global/vstore/<hostname>/<username>.
+// - <dbName> is the filename in which to store the data.
+//
+// The Store service has Veyron name, <name>/.store. Individual values with
+// path <path> have name <name>/<path>.
+package main
+
+import (
+ "flag"
+ "log"
+ "os"
+ "os/user"
+
+ _ "veyron/examples/storage/mdb/schema"
+ "veyron/services/store/server"
+
+ "veyron2/rt"
+ "veyron2/security"
+)
+
+var (
+ mountName string
+ dbName = flag.String("db", "/var/tmp/mdb.db", "Metadata database")
+
+ // TODO(jyh): Figure out how to get a real public ID.
+ rootPublicID security.PublicID = security.FakePrivateID("anonymous").PublicID()
+)
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ dir := "global/vstore/" + hostname + "/" + username
+ flag.StringVar(&mountName, "name", dir, "Mount point for media")
+}
+
+// Main starts the content service, taking arguments from the command line
+// flags.
+func main() {
+ flag.Parse()
+ r := rt.Init()
+
+ // Create a new server instance.
+ s, err := r.NewServer()
+ if err != nil {
+ log.Fatal("r.NewServer() failed: ", err)
+ }
+
+ // Create a new StoreService.
+ storeService, err := server.New(server.ServerConfig{Admin: rootPublicID, DBName: *dbName})
+ if err != nil {
+ log.Fatal("server.New() failed: ", err)
+ }
+ defer storeService.Close()
+
+ // Register the services.
+ storeDisp := server.NewStoreDispatcher(storeService)
+ objectDisp := server.NewObjectDispatcher(storeService)
+ if err := s.Register(".store", storeDisp); err != nil {
+ log.Fatal("s.Register(storeDisp) failed: ", err)
+ }
+ if err := s.Register("", objectDisp); err != nil {
+ log.Fatal("s.Register(objectDisp) failed: ", err)
+ }
+
+ // Create an endpoint and start listening.
+ ep, err := s.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ log.Fatal("s.Listen() failed: ", err)
+ }
+
+ // Publish the service in the mount table.
+ log.Printf("Mounting store on %s, endpoint /%s", mountName, ep)
+ log.Printf("Example commands:")
+ log.Printf(`# bin/mdb_init --templates=src/veyron/examples/storage/mdb/templates "--store=/%s" --load-all`, ep)
+ log.Printf(`# bin/mdbd "--store=/%s"`, ep)
+ if err := s.Publish(mountName); err != nil {
+ log.Fatal("s.Publish() failed: ", err)
+ }
+
+ // Wait forever.
+ done := make(chan struct{})
+ <-done
+}
diff --git a/examples/storage/mdb/mdbd/main.go b/examples/storage/mdb/mdbd/main.go
new file mode 100644
index 0000000..1cd2735
--- /dev/null
+++ b/examples/storage/mdb/mdbd/main.go
@@ -0,0 +1,49 @@
+// mdbd provides a UI for the mdb/schema movie database.
+//
+// The main purpose of this server is to register the types for the mdb/schema
+// so that the html/templates work correctly.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "os/user"
+
+ // Register the mdb/schema types.
+ _ "veyron/examples/storage/mdb/schema"
+ "veyron/examples/storage/viewer"
+ "veyron2/rt"
+ "veyron2/storage/vstore"
+)
+
+var (
+ storeName string
+ port = flag.Int("port", 10000, "IPV4 port number to serve")
+)
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ dir := "global/vstore/" + hostname + "/" + username
+ flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
+}
+
+func main() {
+ rt.Init()
+
+ log.Printf("Binding to store on %s", storeName)
+ st, err := vstore.New(storeName)
+ if err != nil {
+ log.Fatalf("Can't connect to store: %s: %s", storeName, err)
+ }
+
+ viewer.ListenAndServe(fmt.Sprintf(":%d", *port), st)
+}
diff --git a/examples/storage/mdb/schema/init.go b/examples/storage/mdb/schema/init.go
new file mode 100644
index 0000000..8ed77b7
--- /dev/null
+++ b/examples/storage/mdb/schema/init.go
@@ -0,0 +1,23 @@
+// Package schema defines a schema for a movie rating database. It contains
+// Movies, Actors, Parts, etc.
+//
+// This is designed as a demo application. The target is that each user would
+// have a local copy of the store, and the movie database would be shared
+// through a replication group. User would create new content, movies, reviews,
+// etc. and the content would be merged by synchronization.
+//
+// At the moment, synchronization and queries are not yet implemented, so
+// these features are missing. However, the store provides a basic UI.
+package schema
+
+import (
+ "veyron2/vom"
+)
+
+func init() {
+ vom.Register(&Dir{})
+ vom.Register(&Movie{})
+ vom.Register(&Part{})
+ vom.Register(&Person{})
+ vom.Register(&Review{})
+}
diff --git a/examples/storage/mdb/schema/schema.idl b/examples/storage/mdb/schema/schema.idl
new file mode 100644
index 0000000..5862583
--- /dev/null
+++ b/examples/storage/mdb/schema/schema.idl
@@ -0,0 +1,52 @@
+package schema
+
+import (
+ "veyron2/storage"
+)
+
+// Dir is used to represent directories.
+type Dir struct{
+ // TODO(jyh): The IDL does not recognize empty structs. Fix it and remove this
+ // useless field.
+ X byte
+}
+
+// Movie represents a movie.
+type Movie struct {
+ Image string // URL
+ Title string
+ Summary string
+ Language string
+ // TODO(jyh): Replace these times with IDL types when they are implemented.
+ ReleaseDate int64 // ns since the Unix epoch.
+ Runtime int64 // ns
+ Genre string
+ Director storage.ID
+ // Subdirectories:
+ //
+ // Cast/ contains values of type Part.
+ // Reviews/ contains values of type Review.
+}
+
+// Part represents the role of an actor.
+type Part struct {
+ Actor storage.ID // Person
+ Character string
+}
+
+// Review is a movie review.
+type Review struct {
+ Rating byte // 1-10.
+ Text string
+}
+
+// Person represents a person, in any role, including producers, director,
+// actor, etc.
+type Person struct {
+ Image string // URL
+ Name string
+ BirthDate int64 // ns since the Unix epoch.
+ // Subdirectories:
+ //
+ // Roles/ contains values of type Role.
+}
diff --git a/examples/storage/mdb/schema/schema.idl.go b/examples/storage/mdb/schema/schema.idl.go
new file mode 100644
index 0000000..3f7d253
--- /dev/null
+++ b/examples/storage/mdb/schema/schema.idl.go
@@ -0,0 +1,48 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: schema.idl
+
+package schema
+
+import (
+ "veyron2/storage"
+)
+
+// Dir is used to represent directories.
+type Dir struct {
+ // TODO(jyh): The IDL does not recognize empty structs. Fix it and remove this
+ // useless field.
+ X byte
+}
+
+// Movie represents a movie.
+type Movie struct {
+ Image string // URL
+ Title string
+ Summary string
+ Language string
+ // TODO(jyh): Replace these times with IDL types when they are implemented.
+ ReleaseDate int64 // ns since the Unix epoch.
+ Runtime int64 // ns
+ Genre string
+ Director storage.ID
+}
+
+// Part represents the role of an actor.
+type Part struct {
+ Actor storage.ID // Person
+ Character string
+}
+
+// Review is a movie review.
+type Review struct {
+ Rating byte // 1-10.
+ Text string
+}
+
+// Person represents a person, in any role, including producers, director,
+// actor, etc.
+type Person struct {
+ Image string // URL
+ Name string
+ BirthDate int64 // ns since the Unix epoch.
+}
diff --git a/examples/storage/mdb/templates/contents.json b/examples/storage/mdb/templates/contents.json
new file mode 100644
index 0000000..7db56dd
--- /dev/null
+++ b/examples/storage/mdb/templates/contents.json
@@ -0,0 +1,147 @@
+{
+ "People": {
+ "P1": { "Name": "Catherine Deneuve",
+ "BirthDate": "22/10/1943",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Catherine_Deneuve_1995.jpg/388px-Catherine_Deneuve_1995.jpg"
+ },
+ "P2": { "Name": "Nino Castelnuovo",
+ "BirthDate": "28/10/1936",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/4/4f/Quella_et%C3%A0_maliziosa.png"
+ },
+ "P3": { "Name": "Anne Vernon",
+ "BirthDate": "24/1/1925",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/6/6e/Anne_Vernon_1957.jpg"
+ },
+ "P4": { "Name": "Marc Michel",
+ "BirthDate": "4/12/1932"
+ },
+ "P5": { "Name": "Leonardo DiCaprio",
+ "BirthDate": "11/11/1974",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Leonardo_DiCaprio_%28Berlin_Film_Festival_2010%29_2_%28cropped%29.jpg/711px-Leonardo_DiCaprio_%28Berlin_Film_Festival_2010%29_2_%28cropped%29.jpg"
+ },
+ "P6": { "Name": "Claire Danes",
+ "BirthDate": "4/12/1979",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Claire_Danes_2012_Shankbone.JPG/479px-Claire_Danes_2012_Shankbone.JPG"
+ },
+ "P7": { "Name": "John Leguizamo",
+ "BirthDate": "6/22/1964",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/7/7c/John_Leguizamo_by_Gage_Skidmore.jpg"
+ },
+ "P8": { "Name": "Harold Perrineau",
+ "BirthDate": "8/7/1963",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/Harold_Perrineau%2C_Jr.jpg/549px-Harold_Perrineau%2C_Jr.jpg"
+ },
+ "P9": { "Name": "Joseph Gordon-Levitt",
+ "BirthDate": "17/2/1981",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Joseph_Gordon-Levitt.jpg/480px-Joseph_Gordon-Levitt.jpg"
+ },
+ "P10": { "Name": "Ellen Page",
+ "BirthDate": "21/2/1987",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Ellen_Page_at_TIFF_2009_cropped.jpg/449px-Ellen_Page_at_TIFF_2009_cropped.jpg"
+ },
+ "P11": { "Name": "Michael Caine",
+ "BirthDate": "14/3/1933",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Michael_Caine_-_Viennale_2012_a.jpg/400px-Michael_Caine_-_Viennale_2012_a.jpg"
+ },
+ "P12": { "Name": "Jacques Demy",
+ "BirthDate": "5/6/1931",
+ "Image": "http://upload.wikimedia.org/wikipedia/en/3/3a/Jacques_Demy.jpg"
+ },
+ "P13": { "Name": "Baz Luhrmann",
+ "BirthDate": "17/9/1962",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Baz_Luhrmann.jpg/399px-Baz_Luhrmann.jpg"
+ },
+ "P14": { "Name": "Christopher Nolan",
+ "BirthDate": "30/7/1970",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/c/c4/Christopher_Nolan%2C_London%2C_2013_%28crop%29.jpg/409px-Christopher_Nolan%2C_London%2C_2013_%28crop%29.jpg"
+ },
+ "P15": { "Name": "Jason Reitman",
+ "BirthDate": "19/10/1977",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Jason_Reitman_2013_TIFF.jpg/428px-Jason_Reitman_2013_TIFF.jpg"
+ },
+ "P16": { "Name": "John Lasseter",
+ "BirthDate": "12/1/1957",
+ "Image": "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/JohnLasseterOct2011.jpg/512px-JohnLasseterOct2011.jpg"
+ }
+ },
+
+ "Movies": {
+ "M1": { "Title": "The Umbrellas of Cherbourg",
+ "Image": "http://upload.wikimedia.org/wikipedia/en/3/39/ParapluiePoster.jpg",
+ "Summary": "A young girl separated from her lover by war faces a life altering decision.",
+ "Language": "French",
+ "ReleaseDate": "16/12/1964",
+ "Runtime": 91,
+ "Genre": "musical",
+ "Director": "P12"
+ },
+ "M2": { "Title": "Romeo + Juliet",
+ "Image": "http://upload.wikimedia.org/wikipedia/en/b/b4/William_shakespeares_romeo_and_juliet_movie_poster.jpg",
+ "Summary": "Shakespeare's famous play is updated to the hip modern suburb of Verona still retaining its original dialogue.",
+ "Language": "English",
+ "ReleaseDate": "28/2/1997",
+ "Runtime": 120,
+ "Genre": "drama",
+ "Director": "P13"
+ },
+ "M3": { "Title": "Inception",
+ "Image": "http://upload.wikimedia.org/wikipedia/en/7/7f/Inception_ver3.jpg",
+ "Summary": "A skilled extractor is offered a chance to regain his old life as payment for a task considered to be impossible.",
+ "Language": "English",
+ "ReleaseDate": "16/6/2010",
+ "Runtime": 148,
+ "Genre": "action",
+ "Director": "P14"
+ },
+ "M4": { "Title": "Juno",
+ "Image": "http://upload.wikimedia.org/wikipedia/en/e/ec/Junoposter2007.jpg",
+ "Summary": "Faced with an unplanned pregnancy, an offbeat young woman makes an unusual decision regarding her unborn child.",
+ "Language": "English",
+ "ReleaseDate": "25/12/2007",
+ "Runtime": 96,
+ "Genre": "comedy",
+ "Director": "P15"
+ },
+ "M5": { "Title": "Cars 2",
+ "Image": "http://upload.wikimedia.org/wikipedia/en/7/7f/Cars_2_Poster.jpg",
+ "Summary": "Star race car Lightning McQueen and his pal Mater head overseas to compete in the World Grand Prix race. But the road to the championship becomes rocky as Mater gets caught up in an intriguing adventure of his own: international espionage.",
+ "Language": "English",
+ "ReleaseDate": "12/6/2011",
+ "Runtime": 0,
+ "Genre": "animation",
+ "Director": "P16"
+ }
+ },
+
+ "Parts": {
+ "p1": { "Movie": "M1", "Actor": "P1", "Character": "Geneviève Emery" },
+ "p2": { "Movie": "M1", "Actor": "P2", "Character": "Guy Foucher" },
+ "p3": { "Movie": "M1", "Actor": "P3", "Character": "Madame Emery" },
+ "p4": { "Movie": "M1", "Actor": "P4", "Character": "Roland Cassard" },
+ "p5": { "Movie": "M2", "Actor": "P5", "Character": "Romeo" },
+ "p6": { "Movie": "M2", "Actor": "P6", "Character": "Juliet" },
+ "p7": { "Movie": "M2", "Actor": "P7", "Character": "Tybalt" },
+ "p8": { "Movie": "M2", "Actor": "P8", "Character": "Mercutio" },
+ "p9": { "Movie": "M3", "Actor": "P5", "Character": "Cobb" },
+ "p10": { "Movie": "M3", "Actor": "P9", "Character": "Arthur" },
+ "p11": { "Movie": "M3", "Actor": "P10", "Character": "Ariadne" },
+ "p12": { "Movie": "M3", "Actor": "P11", "Character": "Miles" },
+ "p13": { "Movie": "M4", "Actor": "P10", "Character": "Juno" },
+ "p14": { "Movie": "M5", "Actor": "P11", "Character": "Finn McMissile" }
+ },
+
+ "Reviews": {
+ "r1": { "Movie": "M1", "Rating": 10, "Text": "It makes my heart ache just to think about it..." },
+ "r2": { "Movie": "M1", "Rating": 10, "Text": "I Will Wait for You" },
+ "r3": { "Movie": "M1", "Rating": 10, "Text": "Toto, I've a feeling we're not in Kansas anymore." },
+ "r4": { "Movie": "M2", "Rating": 9, "Text": "Moderized Without Losing the Shakespeare" },
+ "r5": { "Movie": "M2", "Rating": 1, "Text": "Oh Please!! The 1968 version is a whole lot better than this crap!" },
+ "r6": { "Movie": "M2", "Rating": 8, "Text": "Very surprisingly very good" },
+ "r7": { "Movie": "M3", "Rating": 9, "Text": "Matrix but in dreamworld? Nah." },
+ "r8": { "Movie": "M3", "Rating": 10, "Text": "Insanely Brilliant ! Nolan has outdone himself !!" },
+ "r9": { "Movie": "M3", "Rating": 1, "Text": "an intellectual challenging movie for people, who can't speak in relative clauses." },
+ "r10": { "Movie": "M4", "Rating": 9, "Text": "Every good thing you've heard and more" },
+ "r11": { "Movie": "M5", "Rating": 8, "Text": "An action-filled fun change of pace" },
+ "r12": { "Movie": "M5", "Rating": 3, "Text": "Such a disappointment" }
+ }
+}
diff --git a/examples/storage/mdb/templates/css/movie.css b/examples/storage/mdb/templates/css/movie.css
new file mode 100644
index 0000000..eeb3043
--- /dev/null
+++ b/examples/storage/mdb/templates/css/movie.css
@@ -0,0 +1,83 @@
+body {
+ font-family: sans-serif;
+}
+.title {
+ font-weight: bolder;
+ font-size: 5em;
+ font-family: Gill Sans Extrabold, sans-serif;
+ font-style: oblique;
+}
+.genre {
+ font-family: sans-serif;
+ font-style: oblique;
+}
+.debug {
+ font-style: oblique;
+ color: gray;
+}
+.castbox {
+ border: 1px solid #bbb;
+ border-radius: 10px;
+ background-color: #fbfbfb;
+ width: 95%;
+}
+.cast {
+ border-collapse: collapse;
+ background-color: white;
+ margin: 5px;
+ width: 98%;
+}
+.cast tr {
+ margin: 5px;
+ height: 32px;
+}
+.cast tr:nth-child(even) {
+ background-color: #f6f6f5;
+}
+.cast tr:nth-child(odd) {
+ background-color: #f2f2f8;
+}
+.castchar {
+ width: 200px;
+}
+.reviewbox {
+ border: 1px solid #bbb;
+ border-radius: 4px;
+ background-color: #fbfbfb;
+ margin: 5px;
+ width: 95%;
+}
+.rating {
+ font-weight: bolder;
+}
+.reviewtext {
+}
+.moviesbox {
+ border: 1px solid #bbb;
+ border-radius: 10px;
+ background-color: #fbfbfb;
+ width: 95%;
+}
+.moviestable {
+ border-collapse: collapse;
+ background-color: white;
+ margin: 5px;
+ width: 98%;
+}
+.moviestable tr {
+ margin: 5px;
+ height: 32px;
+}
+.moviestable tr:nth-child(even) {
+ background-color: #f6f6f5;
+}
+.moviestable tr:nth-child(odd) {
+ background-color: #f2f2f8;
+}
+.moviesentry {
+}
+.peoplebox {
+ background-color: #fbfbfb;
+ margin: 5px;
+ width: 95%;
+}
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl b/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl
new file mode 100644
index 0000000..1146748
--- /dev/null
+++ b/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl
@@ -0,0 +1,37 @@
+<html>
+<head>
+<title>{{.Name}}</title>
+<link rel="stylesheet" href="/css/movie.css">
+</head>
+<body>
+<p><a href="/movies">Movies</a> <a href="/people">People</a></p>
+{{$value := .}}
+
+<!-- Movie listing -->
+{{with $value.Glob "movies/*"}}
+<div class="moviesbox">
+<h2>Movies<h2>
+<table class="moviestable">
+{{range .}}
+<tr>
+<td class="movieentry"><a href="{{.}}">{{$value.Base .}}</a>
+</tr>
+{{end}}
+</table>
+</div>
+{{end}}
+
+<!-- People listing -->
+{{with $value.Glob "people/*"}}
+<h2>People</h2>
+{{range .}}
+<div class="peoplebox">
+<p><a href="{{.}}">{{$value.Base .}}</a></p>
+</div>
+{{end}}
+{{end}}
+
+<!-- Debugging -->
+<span class="debug">{{.Name}} <a href="?raw">Raw</a></span>
+</body>
+</html>
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl b/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl
new file mode 100644
index 0000000..faaf5bb
--- /dev/null
+++ b/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl
@@ -0,0 +1,63 @@
+<html>
+<head>
+<title>{{.Value.Title}}</title>
+<link rel="stylesheet" href="/css/movie.css">
+</head>
+<body>
+<p><a href="/movies">Movies</a> <a href="/people">People</a></p>
+{{$value := .}}
+{{$releaseDate := $value.Date .Value.ReleaseDate}}
+<p>
+<table>
+<tr>
+<td valign="top">
+<img src="{{.Value.Image}}" align="bottom">
+<td valign="top">
+<div>
+<span class="title">{{.Value.Title}}</span>
+<span class="date">({{$releaseDate.Year}})</span>
+<span class="genre">{{.Value.Genre}}</span>
+<p>
+{{$directorName := $value.Join $value.Name "Director"}}
+{{$director := $value.Get $directorName}}
+Director: <a href="{{$directorName}}">{{$director.Name}}</a>
+</div>
+</tr>
+</table>
+
+<!-- Table to display the cast -->
+{{with $value.Glob "Cast/*"}}
+<div class="castbox">
+<h2>Cast<h2>
+<table class="cast">
+{{range .}}
+<tr>
+{{$name := .}}
+{{$part := $value.Get $name}}
+{{$path := $value.Join $name "Actor"}}
+{{$actor := $value.Get $path}}
+<td class="castactor"><a href="{{$path}}">{{$actor.Name}}</a>
+<td class="castchar">{{$part.Character}}
+</tr>
+{{end}}
+</table>
+</div>
+{{end}}
+
+<!-- Reviews -->
+{{with $value.Glob "Reviews/*"}}
+<h2>Reviews</h2>
+{{range .}}
+{{$review := $value.Get .}}
+<div class="reviewbox">
+<span class="rating">Rating: {{$review.Rating}}/10</span>
+<p>
+<span class="reviewtext">{{$review.Text}}</span>
+</div>
+{{end}}
+{{end}}
+
+<!-- Debugging -->
+<span class="debug">{{.Name}} <a href="?raw">Raw</a></span>
+</body>
+</html>
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl b/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl
new file mode 100644
index 0000000..f0db02f
--- /dev/null
+++ b/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl
@@ -0,0 +1,29 @@
+<html>
+<head>
+<title>{{.Value.Name}}</title>
+<link rel="stylesheet" href="/css/movie.css">
+</head>
+<body>
+<p><a href="/movies">Movies</a> <a href="/people">People</a></p>
+{{$value := .}}
+<p>
+<table>
+<tr>
+<td valign="top">
+<img src="{{.Value.Image}}" align="bottom">
+<td valign="top">
+<div>
+<span class="title">{{.Value.Name}}</span>
+</div>
+</tr>
+</table>
+
+<p>
+{{$date := $value.Date .Value.BirthDate}}
+Born: <span class="date">{{$date.Month}} {{$date.Day}}, {{$date.Year}}</span>
+</p>
+
+<!-- Debugging -->
+<span class="debug">{{.Name}} <a href="?raw">Raw</a></span>
+</body>
+</html>
diff --git a/examples/storage/viewer/reflect.go b/examples/storage/viewer/reflect.go
new file mode 100644
index 0000000..c7b4359
--- /dev/null
+++ b/examples/storage/viewer/reflect.go
@@ -0,0 +1,150 @@
+package viewer
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+
+ "veyron2/storage"
+)
+
+// printer defines a pretty printer for Go values, using reflection to traverse
+// the values.
+type printer struct {
+ buf bytes.Buffer
+}
+
+var (
+ tyID = reflect.TypeOf(storage.ID{})
+)
+
+// stringTypePointers removes the outer Ptr and Interface types.
+func stripTypePointers(ty reflect.Type) reflect.Type {
+ kind := ty.Kind()
+ for kind == reflect.Ptr || kind == reflect.Interface {
+ ty = ty.Elem()
+ kind = ty.Kind()
+ }
+ return ty
+}
+
+// templatePath returns the path to the templates value, defined as
+// /templates/<pkgPath>/<typeName>.
+func templatePath(v interface{}) string {
+ ty := stripTypePointers(reflect.TypeOf(v))
+ pkgPath := ty.PkgPath()
+ tyName := ty.Name()
+ if pkgPath == "" || tyName == "" {
+ return ""
+ }
+ return fmt.Sprintf("templates/%s/%s", pkgPath, tyName)
+}
+
+// print formats the argument.
+func (p *printer) print(v interface{}) {
+ p.printValue(0, reflect.ValueOf(v))
+}
+
+// printType formats the type.
+func (p *printer) printType(indent int, v reflect.Type) {
+ p.printString(v.Name())
+}
+
+// printValue is the main pretty-printer method. It formats the value at the
+// indentation level specified by the argument.
+func (p *printer) printValue(indent int, v reflect.Value) {
+ if !v.IsValid() {
+ p.printString("<invalid>")
+ return
+ }
+ ty := v.Type()
+ if ty == tyID {
+ p.printString(v.Interface().(storage.ID).String())
+ return
+ }
+ switch ty.Kind() {
+ case reflect.Ptr:
+ p.printString("&")
+ fallthrough
+ case reflect.Interface:
+ p.printValue(indent, v.Elem())
+ case reflect.Array, reflect.Slice:
+ p.printSliceValue(indent, v)
+ case reflect.Map:
+ p.printMapValue(indent, v)
+ case reflect.Struct:
+ p.printStructValue(indent, v)
+ case reflect.String:
+ fmt.Fprintf(&p.buf, "%q", v.Interface())
+ default:
+ fmt.Fprintf(&p.buf, "%+v", v.Interface())
+ }
+}
+
+// printSliceValue formats a slice or array.
+func (p *printer) printSliceValue(indent int, v reflect.Value) {
+ p.printType(indent, v.Type())
+ p.printString("{")
+ l := v.Len()
+ for i := 0; i < l; i++ {
+ p.printIndent(indent + 1)
+ p.printValue(indent+1, v.Index(i))
+ p.printString(",")
+ }
+ p.printIndent(indent)
+ p.printString("}")
+}
+
+// printMapValue formats a map.
+func (p *printer) printMapValue(indent int, v reflect.Value) {
+ p.printType(indent, v.Type())
+ p.printString("{")
+ for _, key := range v.MapKeys() {
+ p.printIndent(indent + 1)
+ p.printValue(indent+1, key)
+ p.printString(": ")
+ p.printValue(indent+2, v.MapIndex(key))
+ p.printString(",")
+ }
+ p.printIndent(indent)
+ p.printString("}")
+}
+
+// printStructValue formats a struct.
+func (p *printer) printStructValue(indent int, v reflect.Value) {
+ ty := v.Type()
+ p.printType(indent, ty)
+ p.printString("{")
+ l := ty.NumField()
+ for i := 0; i < l; i++ {
+ field := ty.Field(i)
+ if field.PkgPath != "" {
+ continue
+ }
+ p.printIndent(indent + 1)
+ p.printString(field.Name)
+ p.printString(": ")
+ p.printValue(indent+1, v.Field(i))
+ p.printString(",")
+ }
+ p.printIndent(indent)
+ p.printString("}")
+}
+
+// printString adds a string to the output.
+func (p *printer) printString(s string) {
+ p.buf.WriteString(s)
+}
+
+// printIndent prints a newline and then indents to the specified indentation.
+func (p *printer) printIndent(indent int) {
+ p.buf.WriteRune('\n')
+ for i := 0; i < indent; i++ {
+ p.buf.WriteString(" ")
+ }
+}
+
+// String returns the formatted text.
+func (p *printer) String() string {
+ return p.buf.String()
+}
diff --git a/examples/storage/viewer/value.go b/examples/storage/viewer/value.go
new file mode 100644
index 0000000..7bb2dfc
--- /dev/null
+++ b/examples/storage/viewer/value.go
@@ -0,0 +1,78 @@
+package viewer
+
+import (
+ "path/filepath"
+ "sort"
+ "time"
+
+ "veyron2/storage"
+)
+
+// Value is the type used to pass store values to the template. The Value
+// contains the name of the value, the actual value, and a list of
+// subdirectories.
+type Value struct {
+ store storage.Store
+ Name string
+ Value interface{}
+ Subdirs []string
+}
+
+// glob performs a glob expansion of the pattern. The results are sorted.
+func glob(st storage.Store, path, pattern string) ([]string, error) {
+ results, err := st.Bind(path).GlobT(nil, pattern)
+ if err != nil {
+ return nil, err
+ }
+ defer results.Finish()
+ names := []string{}
+ for {
+ name, err := results.Recv()
+ if err != nil {
+ break
+ }
+ names = append(names, "/"+name)
+ }
+ sort.Strings(names)
+ return names, nil
+}
+
+// fullpath returns the absolute path from a relative path.
+func (v *Value) fullpath(path string) string {
+ if len(path) == 0 || path[0] != '/' {
+ return v.Name + "/" + path
+ }
+ return path
+}
+
+// Date performs a Time conversion, given an integer argument that represents a
+// time in nanoseconds since the Unix epoch.
+func (v *Value) Date(ns int64) time.Time {
+ return time.Unix(0, ns)
+}
+
+// Join joins the path elements.
+func (v *Value) Join(elem ...string) string {
+ return filepath.ToSlash(filepath.Join(elem...))
+}
+
+// Base returns the last element of the path.
+func (v *Value) Base(path string) string {
+ return filepath.Base(path)
+}
+
+// Glob performs a glob expansion of a pattern.
+func (v *Value) Glob(pattern string) ([]string, error) {
+ return glob(v.store, v.Name, pattern)
+}
+
+// Get fetches a value from the store. The result is nil if the value does not
+// exist.
+func (v *Value) Get(path string) interface{} {
+ path = v.fullpath(path)
+ e, err := v.store.Bind(path).Get(nil)
+ if err != nil {
+ return nil
+ }
+ return e.Value
+}
diff --git a/examples/storage/viewer/viewer.go b/examples/storage/viewer/viewer.go
new file mode 100644
index 0000000..9237b90
--- /dev/null
+++ b/examples/storage/viewer/viewer.go
@@ -0,0 +1,156 @@
+// package viewer exports a store through an HTTP server, with the following features.
+//
+// URL paths correspond to store paths. For example, if the URL is
+// http://myhost/a/b/c, the store value that is fetched is /a/b/c.
+//
+// Values can be formatted using html templates (documented in the
+// text/templates package). Templates are named according to the type of the
+// value that they format, using a path /templates/<pkgPath>/<typeName>. For
+// example, suppose we are viewing the page for /movies/Inception, and it
+// contains a value of type *examples/store/mdb/schema.Movie. We fetch the
+// template /templates/examples/store/mdb/schema/Movie, which must be a string in
+// html/template format. If it exists, the template is compiled and used to
+// print the value. If the template does not exist, the value is formatted in
+// raw form.
+//
+// String values that have a path ending with suffix .css are printed in raw form.
+package viewer
+
+import (
+ "fmt"
+ "html"
+ "html/template"
+ "io"
+ "net/http"
+ "path/filepath"
+
+ "veyron2/storage"
+ "veyron2/vlog"
+)
+
+// server is the HTTP server handler.
+type server struct {
+ store storage.Store
+}
+
+var _ http.Handler = (*server)(nil)
+
+const (
+ // rawTemplateText is used to format the output in a raw textual form.
+ rawTemplateText = `<html>
+{{$value := .}}
+<head>
+<title>{{.Name}}</title>
+</head>
+<body>
+<h1>{{.Name}}</h1>
+<pre>{{.Value}}</pre>
+{{with .Subdirs}}
+<h3>Subdirectories</h3>
+{{range .}}
+<p><a href="{{.}}">{{$value.Base .}}</a></p>
+{{end}}
+{{end}}
+</body>
+</html>`
+)
+
+var (
+ rawTemplate = mustParse("raw", rawTemplateText)
+)
+
+// mustParse parses the template text. It panics on error.
+func mustParse(name, text string) *template.Template {
+ tmpl, err := template.New(name).Parse(text)
+ if err != nil {
+ panic(fmt.Sprintf("Error parsing template %q: %s", text, err))
+ }
+ return tmpl
+}
+
+// loadTemplate fetches the template for the value from the store. The template
+// is based on the type of the value, under /template/<pkgPath>/<typeName>.
+func (s *server) loadTemplate(v interface{}) *template.Template {
+ path := templatePath(v)
+ e, err := s.store.Bind(path).Get(nil)
+ if err != nil {
+ return nil
+ }
+ str, ok := e.Value.(string)
+ if !ok {
+ return nil
+ }
+ tmpl, err := template.New(path).Parse(str)
+ if err != nil {
+ vlog.Infof("Template error: %s: %s", path, err)
+ return nil
+ }
+ return tmpl
+}
+
+// printRawValuePage prints the value in raw format.
+func (s *server) printRawValuePage(w http.ResponseWriter, path string, v interface{}) {
+ var p printer
+ p.print(v)
+ subdirs, _ := glob(s.store, path, "*")
+ x := &Value{Name: path, Value: p.String(), Subdirs: subdirs}
+ if err := rawTemplate.Execute(w, x); err != nil {
+ w.Write([]byte(html.EscapeString(err.Error())))
+ }
+}
+
+// printValuePage prints the value using a template if possible. If a template
+// is not found, the value is printed in raw format instead.
+func (s *server) printValuePage(w http.ResponseWriter, path string, v interface{}) {
+ if tmpl := s.loadTemplate(v); tmpl != nil {
+ if err := tmpl.Execute(w, &Value{store: s.store, Name: path, Value: v}); err != nil {
+ w.Write([]byte(html.EscapeString(err.Error())))
+ }
+ return
+ }
+ s.printRawValuePage(w, path, v)
+}
+
+// printRawPage prints a string value directly, without processing.
+func (s *server) printRawPage(w http.ResponseWriter, v interface{}) {
+ str, ok := v.(string)
+ if !ok {
+ fmt.Fprintf(w, "%s", v)
+ } else {
+ io.WriteString(w, str)
+ }
+}
+
+// ServeHTTP is the main HTTP handler.
+func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ path := req.URL.Path
+ e, err := s.store.Bind(path).Get(nil)
+ if err != nil {
+ msg := fmt.Sprintf("<html><body><h1>%s</h1><h2>Error: %s</h2></body></html>",
+ html.EscapeString(path),
+ html.EscapeString(err.Error()))
+ w.WriteHeader(http.StatusNotFound)
+ w.Write([]byte(msg))
+ return
+ }
+
+ q := req.URL.Query()
+ switch filepath.Ext(path) {
+ case ".css":
+ s.printRawPage(w, e.Value)
+ default:
+ if q["raw"] != nil {
+ s.printRawValuePage(w, path, e.Value)
+ } else {
+ s.printValuePage(w, path, e.Value)
+ }
+ }
+}
+
+// ListenAndServe is the main entry point. It serves store at the specified
+// network address.
+func ListenAndServe(addr string, st storage.Store) error {
+ s := &server{store: st}
+ vlog.Infof("Viewer running at http://localhost%s", addr)
+ return http.ListenAndServe(addr, s)
+}
diff --git a/examples/todos/.gitignore b/examples/todos/.gitignore
new file mode 100644
index 0000000..63042e8
--- /dev/null
+++ b/examples/todos/.gitignore
@@ -0,0 +1,4 @@
+log
+node_modules
+todos_appd/js/env.js
+todos_appd/js/veyron.*
diff --git a/examples/todos/.jshintrc b/examples/todos/.jshintrc
new file mode 100644
index 0000000..ebf6d18
--- /dev/null
+++ b/examples/todos/.jshintrc
@@ -0,0 +1,30 @@
+{
+ "bitwise": true,
+ "camelcase": false,
+ "curly": false,
+ "eqeqeq": true,
+ "es3": false,
+ "forin": true,
+ "freeze": true,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "noempty": true,
+ "nonbsp": true,
+ "nonew": true,
+ "strict": true,
+ "trailing": true,
+ "undef": true,
+ "unused": true,
+
+ "indent": 2,
+ "maxlen": 80,
+ "quotmark": "single",
+
+ "browser": true,
+ "node": false,
+ "globals": {
+ "React": true,
+ "React": true,
+ }
+}
diff --git a/examples/todos/Gruntfile.js b/examples/todos/Gruntfile.js
new file mode 100644
index 0000000..c5977e0
--- /dev/null
+++ b/examples/todos/Gruntfile.js
@@ -0,0 +1,42 @@
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+
+module.exports = function(grunt) {
+ require('load-grunt-tasks')(grunt);
+ grunt.task.loadTasks('grunt_tasks');
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ jshint: {
+ files: ['**/*.js'],
+ options: {
+ ignores: ['**/veyron*.js',
+ 'node_modules/**/*.js']
+ }
+ }
+ });
+
+ var vIndex = __dirname.indexOf('/v/');
+ assert.notEqual(vIndex, -1, 'Failed to find Veyron root dir');
+
+ grunt.constants = {
+ LOG_DIR: path.resolve('log'),
+ VEYRON_BIN_DIR: __dirname.substr(0, vIndex) + '/v/bin',
+ VEYRON_IDENTITY_PORT: 3000,
+ VEYRON_PROXY_PORT: 3001,
+ VEYRON_WSPR_PORT: 3002
+ };
+ var c = grunt.constants;
+
+ // Make dirs as needed.
+ [c.LOG_DIR].forEach(function(dir) {
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir);
+ });
+
+ // Starts all needed daemons and blocks. On Ctrl-C, kills all spawned
+ // subprocesses and then exits.
+ grunt.registerTask('start', [
+ 'subtask_spawnSubprocesses'
+ ]);
+};
diff --git a/examples/todos/Makefile b/examples/todos/Makefile
new file mode 100644
index 0000000..166001d
--- /dev/null
+++ b/examples/todos/Makefile
@@ -0,0 +1,37 @@
+GO_BIN=../../../../../scripts/build/go
+VEYRON_JS_API=../../../../../javascript/api
+
+GRUNT=./node_modules/.bin/grunt
+NPM=${VEYRON_ROOT}/environment/cout/node/bin/npm
+
+# TODO(sadovsky): For some reason the first line below fixes 'start' and 'lint'
+# but breaks 'buildgo' and 'echopath', while the second does not fix 'start' and
+# 'lint'. For now, we require users to already have node in their paths.
+#PATH:=${VEYRON_ROOT}/environment/cout/node/bin:${PATH} # for node
+#PATH:=${PATH}:${VEYRON_ROOT}/environment/cout/node/bin # for node
+
+default: build
+build: buildgo buildnode buildbrowser
+
+buildgo:
+ ${GO_BIN} install {veyron,veyron2}/...
+
+buildnode:
+ ${NPM} install
+
+buildbrowser:
+ (cd ${VEYRON_JS_API} && ./vgrunt build) && \
+ mkdir -p ./todos_appd/js && \
+ cp -rf ${VEYRON_JS_API}/dist/*.* ./todos_appd/js
+
+start:
+ ${GRUNT} start
+
+gofmt:
+ gofmt -w ./
+
+lint:
+ ${GRUNT} jshint
+
+echopath:
+ @echo ${PATH}
diff --git a/examples/todos/README.md b/examples/todos/README.md
new file mode 100644
index 0000000..333a3d0
--- /dev/null
+++ b/examples/todos/README.md
@@ -0,0 +1,13 @@
+todos
+=====
+export VEYRON_ROOT=/usr/local/google/home/sadovsky/veyron
+export PATH=./node_modules/.bin:${VEYRON_ROOT}/environment/cout/node/bin:${PATH}
+
+make build
+make start
+make lint
+
+vgo install {veyron,veyron2}/...
+npm install
+gofmt -w v/src/veyron/examples/todos
+grunt start
diff --git a/examples/todos/grunt_tasks/subprocesses.js b/examples/todos/grunt_tasks/subprocesses.js
new file mode 100644
index 0000000..8f0dc6a
--- /dev/null
+++ b/examples/todos/grunt_tasks/subprocesses.js
@@ -0,0 +1,107 @@
+// Provides tasks to spawn and kill subprocesses needed for todos app.
+// See also: javascript/api/grunt_tasks/integration_test_environment_setup.js
+
+var assert = require('assert');
+var fs = require('fs');
+var spawn = require('child_process').spawn;
+
+module.exports = function(grunt) {
+ var subprocesses = []; // List of subprocesses we've spawned.
+
+ // Kills all subprocesses we've spawned.
+ var tearDown = function() {
+ subprocesses.forEach(function(p) {
+ p.kill('SIGTERM');
+ });
+ };
+
+ // Calls tearDown, then fails the current task.
+ var fail = function(msg) {
+ tearDown();
+ grunt.fail.fatal(msg);
+ done(false);
+ };
+
+ // Starts all subprocesses and sets some grunt.config vars.
+ grunt.registerTask('subtask_spawnSubprocesses', function() {
+ // Tell grunt that this is an async task.
+ // Note, we never actually call done() -- see comment below.
+ var done = this.async();
+
+ // Define constants.
+ var c = grunt.constants;
+ var VEYRON_IDENTITY_BIN = c.VEYRON_BIN_DIR + '/identityd';
+ var VEYRON_PROXY_BIN = c.VEYRON_BIN_DIR + '/proxy';
+ var VEYRON_WSPR_BIN = c.VEYRON_BIN_DIR + '/wsprd';
+ var STORE_BIN = c.VEYRON_BIN_DIR + '/todos_stored';
+ var VEYRON_PROXY_ADDR = '127.0.0.1:' + c.VEYRON_PROXY_PORT;
+
+ // Ensure binaries exist.
+ var bins = [VEYRON_IDENTITY_BIN, VEYRON_PROXY_BIN, VEYRON_WSPR_BIN];
+ bins.forEach(function(bin) {
+ if (!fs.existsSync(bin)) {
+ fail('Missing binary: ' + bin);
+ }
+ });
+
+ // Start subprocesses.
+ var startSubprocess = function(command, args) {
+ var p = spawn(command, args);
+ p.stderr.on('data', grunt.log.debug);
+ subprocesses.push(p);
+ return p;
+ };
+ var veyronIdentitySubprocess = startSubprocess(
+ VEYRON_IDENTITY_BIN,
+ ['-port=' + c.VEYRON_IDENTITY_PORT]);
+ var veyronProxySubprocess = startSubprocess(
+ VEYRON_PROXY_BIN,
+ ['-log_dir=' + c.LOG_DIR, '-addr=' + VEYRON_PROXY_ADDR]);
+ var veyronWsprSubprocess = startSubprocess(
+ VEYRON_WSPR_BIN,
+ ['-v=3', '-log_dir=' + c.LOG_DIR, '-port=' + c.VEYRON_WSPR_PORT,
+ '-vproxy=' + VEYRON_PROXY_ADDR]);
+ var storeSubprocess = startSubprocess(
+ STORE_BIN, ['-log_dir=' + c.LOG_DIR]);
+
+ // Capture todos_stored's endpoint, then write env.js file.
+ storeSubprocess.stdout.on('data', function(data) {
+ var endpoint;
+ data.toString().split('\n').some(function(line) {
+ line = line.replace('Endpoint: ', '').trim();
+ if (line.match(/^@.*@$/)) {
+ endpoint = line;
+ return true;
+ }
+ });
+ assert(endpoint, 'Failed to extract store endpoint');
+
+ // Write JS file that sets env vars for client-side JS.
+ var env = {
+ VEYRON_WSPR_SERVER_URL: 'http://localhost:' + c.VEYRON_WSPR_PORT,
+ STORE_ENDPOINT: endpoint
+ };
+ console.log(JSON.stringify(env, null, 2));
+ var code = 'var env = JSON.parse(\'' + JSON.stringify(env) + '\');';
+ // TODO(sadovsky): This is super hacky -- we shouldn't be writing files
+ // into the webserver's dir. It'd be better to communicate these vars to
+ // todos_appd via env vars or flags.
+ grunt.file.write('todos_appd/js/env.js', code);
+
+ console.log('Now run this: ./v/bin/todos_appd');
+ });
+
+ // Note, we do not call done() here, because we want to block until the user
+ // hits Ctrl-C. We rely on the Node process exit handler to kill our spawned
+ // subprocesses.
+ });
+
+ // Kills all subprocesses we started. Not currently used, since spawn blocks.
+ grunt.registerTask('subtask_killSubprocesses', function() {
+ tearDown();
+ });
+
+ process.on('exit', function() {
+ tearDown();
+ });
+};
diff --git a/examples/todos/package.json b/examples/todos/package.json
new file mode 100644
index 0000000..1e4717d
--- /dev/null
+++ b/examples/todos/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "todos",
+ "version": "0.0.1",
+ "devDependencies": {
+ "grunt": "~0.4.4",
+ "grunt-cli": "~0.1.13",
+ "grunt-contrib-jshint": "~0.10.0",
+ "load-grunt-tasks": "~0.4.0"
+ }
+}
diff --git a/examples/todos/schema/init.go b/examples/todos/schema/init.go
new file mode 100644
index 0000000..9b6695a
--- /dev/null
+++ b/examples/todos/schema/init.go
@@ -0,0 +1,12 @@
+// Package schema defines a schema for a todos application.
+package schema
+
+import (
+ "veyron2/vom"
+)
+
+func init() {
+ vom.Register(&Dir{})
+ vom.Register(&List{})
+ vom.Register(&Item{})
+}
diff --git a/examples/todos/schema/schema.idl b/examples/todos/schema/schema.idl
new file mode 100644
index 0000000..049daff
--- /dev/null
+++ b/examples/todos/schema/schema.idl
@@ -0,0 +1,23 @@
+package schema
+
+// Dir represents a directory.
+type Dir struct{
+ // TODO(jyh): The IDL does not recognize empty structs. Fix it and remove this
+ // useless field.
+ X byte
+}
+
+// List is a list of items.
+type List struct {
+ Name string
+ // Subdirectories:
+ //
+ // Items/ contains values of type Item.
+}
+
+// Item is a single task to be done.
+type Item struct {
+ Text string
+ Done bool
+ Tags []string
+}
diff --git a/examples/todos/schema/schema.idl.go b/examples/todos/schema/schema.idl.go
new file mode 100644
index 0000000..9a6c338
--- /dev/null
+++ b/examples/todos/schema/schema.idl.go
@@ -0,0 +1,23 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: schema.idl
+
+package schema
+
+// Dir represents a directory.
+type Dir struct {
+ // TODO(jyh): The IDL does not recognize empty structs. Fix it and remove this
+ // useless field.
+ X byte
+}
+
+// List is a list of items.
+type List struct {
+ Name string
+}
+
+// Item is a single task to be done.
+type Item struct {
+ Text string
+ Done bool
+ Tags []string
+}
diff --git a/examples/todos/test/store_test.go b/examples/todos/test/store_test.go
new file mode 100644
index 0000000..2d4e541
--- /dev/null
+++ b/examples/todos/test/store_test.go
@@ -0,0 +1,237 @@
+// Exercises basic store operations under various conditions.
+//
+// NOTE(sadovsky):
+// - It would be nice if Dir were a builtin (provided by store).
+// - It would be nice to have "mkdir -p" functionality for "put" commands.
+// - It's not clear how to decide between map[string]store.ID and the implicit
+// subdir mechanism.
+// - It'd be nice to not have to specify names for list items in the case where
+// we use the implicit subdir mechanism (rather than a slice).
+// - Why does "vgo test" take so long to compile? I thought Go compilation was
+// supposed to be fast...
+
+package test
+
+import (
+ "fmt"
+ "log"
+ "runtime"
+ "testing"
+
+ bb "veyron/lib/testutil/blackbox"
+
+ "veyron2/storage"
+ "veyron2/storage/vstore"
+ "veyron2/vom"
+)
+
+func init() {
+ bb.CommandTable["startServerRemote"] = startServerRemote
+}
+
+func vomRegister() {
+ vom.Register(&List{})
+ vom.Register(&Todo{})
+}
+
+func TestHelperProcess(t *testing.T) {
+ bb.HelperProcess(t)
+}
+
+func startServerRemote(argv []string) {
+ getRuntime() // initialize Runtime
+
+ if len(argv) > 1 || (len(argv) == 1 && argv[0] != "vomRegister") {
+ log.Fatal("Failed to start remote server: ", argv)
+ } else if len(argv) == 1 {
+ vomRegister()
+ }
+ name, cl := startServer()
+ fmt.Println("ready")
+ fmt.Println(name)
+
+ bb.WaitForEOFOnStdin()
+ fmt.Println("done")
+ cl()
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Structs and helpers
+
+// Dir is a "directory" in the store.
+type Dir struct{}
+
+// List is a list of Todo items.
+type List struct {
+ Dir
+ // TODO(sadovsky): Should we hold a slice (or map) of Todo names here, or
+ // simply prefix Todo names with List names, e.g.
+ // /lists/[list_name]/[todo_name]? For now, we do the former, since the query
+ // and name server listdir APIs are not yet implemented.
+ Todos []storage.ID
+}
+
+// Todo is a single task to be done.
+type Todo struct {
+ Dir
+ Text string
+ Done bool
+ Tags []string
+}
+
+func newDir() *Dir {
+ return &Dir{}
+}
+
+func newList() *List {
+ return &List{Dir: *newDir()}
+}
+
+func newTodo(text string) *Todo {
+ return &Todo{Dir: *newDir(), Text: text}
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Type-specific helpers
+
+func getList(t *testing.T, st storage.Store, tr storage.Transaction, path string) *List {
+ _, file, line, _ := runtime.Caller(1)
+ v := get(t, st, tr, path)
+ res, ok := v.(*List)
+ if !ok {
+ t.Fatalf("%s(%d): %s: not a List: %v", file, line, path, v)
+ }
+ return res
+}
+
+func getTodo(t *testing.T, st storage.Store, tr storage.Transaction, path string) *Todo {
+ _, file, line, _ := runtime.Caller(1)
+ v := get(t, st, tr, path)
+ res, ok := v.(*Todo)
+ if !ok {
+ t.Fatalf("%s(%d): %s: not a Todo: %v", file, line, path, v)
+ }
+ return res
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Test cases
+
+func testTodos(t *testing.T, st storage.Store) {
+ // Create lists.
+ {
+ // NOTE(sadovsky): Currently, we can't put /x/y until we put / and /x.
+ tr := vstore.NewTransaction()
+ put(t, st, tr, "/", newDir())
+ put(t, st, tr, "/lists", newDir())
+ put(t, st, tr, "/lists/drinks", newList())
+ put(t, st, tr, "/lists/snacks", newList())
+ commit(t, tr)
+ }
+
+ // Add some todos.
+ {
+ tr := vstore.NewTransaction()
+ // NOTE(sadovsky): It feels awkward to create my own names (ids) for these
+ // Todo objects. I'd like some way to create them in some "directory"
+ // without explicitly naming them. I.e. in this case I want to think of the
+ // directory as a list, not a map.
+ put(t, st, tr, "/lists/drinks/Todos/@", newTodo("milk"))
+ put(t, st, tr, "/lists/drinks/Todos/@", newTodo("beer"))
+ put(t, st, tr, "/lists/snacks/Todos/@", newTodo("chips"))
+ commit(t, tr)
+ }
+
+ // Verify some of the photos.
+ {
+ tr := vstore.NewTransaction()
+ todo := getTodo(t, st, tr, "/lists/drinks/Todos/0")
+ if todo.Text != "milk" {
+ t.Errorf("Expected %q, got %q", "milk", todo.Text)
+ }
+ }
+
+ {
+ tr := vstore.NewTransaction()
+ todo := getTodo(t, st, tr, "/lists/snacks/Todos/0")
+ if todo.Text != "chips" {
+ t.Errorf("Expected %q, got %q", "chips", todo.Text)
+ }
+ }
+
+ // Move a todo item from one list to another.
+ {
+ tr := vstore.NewTransaction()
+ todo := getTodo(t, st, tr, "/lists/drinks/Todos/1")
+ // NOTE(sadovsky): Remove works for map entries, but not yet for slices.
+ // Instead, we read the list, prune it, and write it back.
+ //remove(t, st, tr, "/lists/drinks/Todos/1")
+ list := getList(t, st, tr, "/lists/drinks")
+ list.Todos = list.Todos[:1]
+ put(t, st, tr, "lists/drinks", list)
+ put(t, st, tr, "/lists/snacks/Todos/@", todo)
+ commit(t, tr)
+ }
+
+ // Verify that the original todo is no longer there.
+ // TODO(sadovsky): Use queries to verify that both lists have changed.
+ {
+ tr := vstore.NewTransaction()
+ // Note, this will be much prettier in veyron2.
+ _, file, line, _ := runtime.Caller(1)
+ path := "/lists/drinks/1"
+ if _, err := st.Bind(path).Get(tr); err == nil {
+ t.Fatalf("%s(%d): got removed object %s", file, line, path)
+ }
+ }
+}
+
+func TestTodosWithLocalServer(t *testing.T) {
+ // Initialize Runtime and register vom types (for both client and server).
+ getRuntime()
+ vomRegister()
+
+ st, cl := startServerAndMakeClient()
+ defer cl()
+ testTodos(t, st)
+}
+
+func testTodosWithRemoteServer(t *testing.T, doVomRegister bool) {
+ // Initialize Runtime and register vom types (for client but not server).
+ getRuntime()
+ vomRegister()
+
+ args := []string{}
+ if doVomRegister {
+ args = append(args, "vomRegister")
+ }
+ server := bb.HelperCommand(t, "startServerRemote", args...)
+ defer server.Cleanup()
+ server.Cmd.Start()
+ server.Expect("ready") // wait for server to be ready
+
+ oa, err := server.ReadLineFromChild()
+ if err != nil {
+ t.Fatal("Failed to read server OA: %v", err)
+ }
+
+ st, cl := makeClient(oa)
+ defer cl()
+ testTodos(t, st)
+
+ server.CloseStdin()
+ server.Expect("done")
+ server.ExpectEOFAndWait()
+}
+
+func TestTodosWithRemoteServer(t *testing.T) {
+ testTodosWithRemoteServer(t, true)
+}
+
+// TODO(sadovsky): This test fails with the following error because the vom
+// types aren't registered with the store server. We need to fix the store to
+// support unregistered types.
+// store_test.go(146): can't put /: ipc: response decoding failed: EOF
+func DisabledTestTodosWithRemoteServerNoRegister(t *testing.T) {
+ testTodosWithRemoteServer(t, false)
+}
diff --git a/examples/todos/test/store_util.go b/examples/todos/test/store_util.go
new file mode 100644
index 0000000..503ea73
--- /dev/null
+++ b/examples/todos/test/store_util.go
@@ -0,0 +1,39 @@
+package test
+
+import (
+ "runtime"
+ "testing"
+
+ "veyron2/storage"
+)
+
+func get(t *testing.T, st storage.Store, tr storage.Transaction, path string) interface{} {
+ _, file, line, _ := runtime.Caller(1)
+ e, err := st.Bind(path).Get(tr)
+ if err != nil {
+ t.Fatalf("%s(%d): can't get %s: %s", file, line, path, err)
+ }
+ return e.Value
+}
+
+func put(t *testing.T, st storage.Store, tr storage.Transaction, path string, v interface{}) storage.ID {
+ _, file, line, _ := runtime.Caller(1)
+ stat, err := st.Bind(path).Put(tr, v)
+ if err != nil || !stat.ID.IsValid() {
+ t.Fatalf("%s(%d): can't put %s: %s", file, line, path, err)
+ }
+ return stat.ID
+}
+
+func remove(t *testing.T, st storage.Store, tr storage.Transaction, path string) {
+ if err := st.Bind(path).Remove(tr); err != nil {
+ _, file, line, _ := runtime.Caller(1)
+ t.Errorf("%s(%d): can't remove %s: %s", file, line, path, err)
+ }
+}
+
+func commit(t *testing.T, tr storage.Transaction) {
+ if err := tr.Commit(); err != nil {
+ t.Fatalf("Transaction aborted: %s", err)
+ }
+}
diff --git a/examples/todos/test/util.go b/examples/todos/test/util.go
new file mode 100644
index 0000000..576aed8
--- /dev/null
+++ b/examples/todos/test/util.go
@@ -0,0 +1,103 @@
+// TODO(sadovsky): Use utils from veyron/rt/blackbox/util_test.go.
+
+package test
+
+import (
+ "crypto/rand"
+ "io/ioutil"
+ "log"
+ "os"
+
+ "veyron/services/store/server"
+
+ "veyron2"
+ "veyron2/naming"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/storage"
+ "veyron2/storage/vstore"
+)
+
+// getRuntime initializes the veyron2.Runtime if needed, then returns it.
+func getRuntime() veyron2.Runtime {
+ // returns Runtime if already initialized
+ return rt.Init(veyron2.LocalID(security.FakePrivateID("todos")))
+}
+
+// startServer starts a store server and returns the server name as well as a
+// function to close the server.
+func startServer() (string, func()) {
+ r := getRuntime()
+
+ // Create a new server instance.
+ s, err := r.NewServer()
+ if err != nil {
+ log.Fatal("r.NewServer() failed: ", err)
+ }
+
+ // Make dbName.
+ var buf [16]byte
+ if _, err := rand.Read(buf[:]); err != nil {
+ log.Fatal("rand.Read() failed: ", err)
+ }
+ dbDir, err := ioutil.TempDir("", "db")
+ if err != nil {
+ log.Fatal("ioutil.TempDir() failed: ", err)
+ }
+
+ // Create a new StoreService.
+ // TODO(ashankar): This "anonymous" is a special string that VCs
+ // default to. This really should be the identity used by the runtime.
+ storeService, err := server.New(server.ServerConfig{Admin: r.Identity().PublicID(), DBName: dbDir})
+ if err != nil {
+ log.Fatal("server.New() failed: ", err)
+ }
+
+ // Register the services.
+ storeDisp := server.NewStoreDispatcher(storeService)
+ objectDisp := server.NewObjectDispatcher(storeService)
+ if err := s.Register(".store", storeDisp); err != nil {
+ log.Fatal("s.Register(storeDisp) failed: ", err)
+ }
+ if err := s.Register("", objectDisp); err != nil {
+ log.Fatal("s.Register(objectDisp) failed: ", err)
+ }
+
+ // Create an endpoint and start listening.
+ ep, err := s.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ log.Fatal("s.Listen() failed: ", err)
+ }
+
+ return naming.JoinAddressName(ep.String(), ""), func() {
+ s.Stop()
+ os.Remove(dbDir)
+ }
+}
+
+// makeClient creates a client and returns a function to close the client.
+func makeClient(name string) (storage.Store, func()) {
+ getRuntime() // verify that Runtime was initialized
+ st, err := vstore.New(name)
+ if err != nil {
+ log.Fatal("vstore.New() failed: ", err)
+ }
+ cl := func() {
+ if err := st.Close(); err != nil {
+ log.Fatal("st.Close() failed: ", err)
+ }
+ }
+ return st, cl
+}
+
+// startServerAndMakeClient calls startServer and makeClient. It returns the
+// client as well as a function to close everything.
+func startServerAndMakeClient() (storage.Store, func()) {
+ mount, clServer := startServer()
+ st, clClient := makeClient(mount)
+ cl := func() {
+ clServer()
+ clClient()
+ }
+ return st, cl
+}
diff --git a/examples/todos/todos_appd/css/index.css b/examples/todos/todos_appd/css/index.css
new file mode 100644
index 0000000..9c93858
--- /dev/null
+++ b/examples/todos/todos_appd/css/index.css
@@ -0,0 +1,3 @@
+*, *:before, *:after {
+ box-sizing: border-box;
+}
diff --git a/examples/todos/todos_appd/js/index.js b/examples/todos/todos_appd/js/index.js
new file mode 100644
index 0000000..0e17f50
--- /dev/null
+++ b/examples/todos/todos_appd/js/index.js
@@ -0,0 +1,5 @@
+(function() {
+ 'use strict';
+
+ document.querySelector('#env').innerText = JSON.stringify(env, null, 2);
+}());
diff --git a/examples/todos/todos_appd/main.go b/examples/todos/todos_appd/main.go
new file mode 100644
index 0000000..70a461c
--- /dev/null
+++ b/examples/todos/todos_appd/main.go
@@ -0,0 +1,96 @@
+// todos_appd is a web application backed by a Veyron store.
+//
+// For now, it simply displays the raw contents of the store.
+
+// TODO(sadovsky): Implement actual app, using Veyron {store,query,watch,sync}
+// over veyron.js.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "html/template"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "os/user"
+ "path"
+
+ "veyron/examples/storage/viewer"
+ _ "veyron/examples/todos/schema" // Register the todos/schema types.
+ "veyron2/rt"
+ "veyron2/storage/vstore"
+)
+
+var (
+ storeName string
+ port = flag.Int("port", 10000, "IPV4 port number to serve")
+ useViewer = flag.Bool("useViewer", false, "If true, serve viewer instead")
+)
+
+var rootDir = path.Join(
+ "/usr/local/google/home/sadovsky",
+ "veyron/v0/v/src/veyron/examples/todos/todos_appd")
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ // TODO(sadovsky): Change this to be the correct veyron2 path.
+ dir := "global/vstore/" + hostname + "/" + username
+ flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
+}
+
+func serveViewer() {
+ log.Printf("Binding to store on %s", storeName)
+ st, err := vstore.New(storeName)
+ if err != nil {
+ log.Fatalf("Can't connect to store: %s: %s", storeName, err)
+ }
+
+ viewer.ListenAndServe(fmt.Sprintf(":%d", *port), st)
+}
+
+func renderTemplate(w io.Writer, basename string, data interface{}) {
+ filename := path.Join(rootDir, "templates", basename)
+ t, err := template.ParseFiles(filename)
+ if err != nil {
+ panic(fmt.Sprintf("ParseFiles failed: %s", filename))
+ }
+ t.Execute(w, data)
+}
+
+func handleHome(w http.ResponseWriter, r *http.Request) {
+ renderTemplate(w, "index.html", nil)
+}
+
+func wrap(fn http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if data := recover(); data != nil {
+ http.Error(w, fmt.Sprint(data), http.StatusInternalServerError)
+ }
+ }()
+ fn(w, r)
+ }
+}
+
+func main() {
+ rt.Init()
+
+ if *useViewer {
+ serveViewer()
+ } else {
+ http.HandleFunc("/", wrap(handleHome))
+ http.Handle("/css/", http.FileServer(http.Dir(rootDir)))
+ http.Handle("/js/", http.FileServer(http.Dir(rootDir)))
+ fmt.Printf("Server running at http://localhost:%d\n", *port)
+ http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
+ }
+}
diff --git a/examples/todos/todos_appd/templates/index.html b/examples/todos/todos_appd/templates/index.html
new file mode 100644
index 0000000..9592bdf
--- /dev/null
+++ b/examples/todos/todos_appd/templates/index.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" href="/css/index.css">
+ </head>
+ <body>
+ <h1>Todos</h1>
+ <pre id="env"></pre>
+ <!-- HACK -->
+ <script src="/js/env.js"></script>
+ <!-- <script src="veyron.min.js"></script> -->
+ <script src="/js/veyron.js"></script>
+ <script src="/js/index.js"></script>
+ </body>
+</html>
diff --git a/examples/todos/todos_init/data.json b/examples/todos/todos_init/data.json
new file mode 100644
index 0000000..f548dd1
--- /dev/null
+++ b/examples/todos/todos_init/data.json
@@ -0,0 +1,118 @@
+[
+ {
+ "name": "Meteor Principles",
+ "contents": [
+ [
+ "Data on the Wire",
+ "Simplicity",
+ "Better UX",
+ "Fun"
+ ],
+ [
+ "One Language",
+ "Simplicity",
+ "Fun"
+ ],
+ [
+ "Database Everywhere",
+ "Simplicity"
+ ],
+ [
+ "Latency Compensation",
+ "Better UX"
+ ],
+ [
+ "Full Stack Reactivity",
+ "Better UX",
+ "Fun"
+ ],
+ [
+ "Embrace the Ecosystem",
+ "Fun"
+ ],
+ [
+ "Simplicity Equals Productivity",
+ "Simplicity",
+ "Fun"
+ ]
+ ]
+ },
+ {
+ "name": "Languages",
+ "contents": [
+ [
+ "Lisp",
+ "GC"
+ ],
+ [
+ "C",
+ "Linked"
+ ],
+ [
+ "C++",
+ "Objects",
+ "Linked"
+ ],
+ [
+ "Python",
+ "GC",
+ "Objects"
+ ],
+ [
+ "Ruby",
+ "GC",
+ "Objects"
+ ],
+ [
+ "JavaScript",
+ "GC",
+ "Objects"
+ ],
+ [
+ "Scala",
+ "GC",
+ "Objects"
+ ],
+ [
+ "Erlang",
+ "GC"
+ ],
+ [
+ "6502 Assembly",
+ "Linked"
+ ]
+ ]
+ },
+ {
+ "name": "Favorite Scientists",
+ "contents": [
+ [
+ "Ada Lovelace",
+ "Computer Science"
+ ],
+ [
+ "Grace Hopper",
+ "Computer Science"
+ ],
+ [
+ "Marie Curie",
+ "Physics",
+ "Chemistry"
+ ],
+ [
+ "Carl Friedrich Gauss",
+ "Math",
+ "Physics"
+ ],
+ [
+ "Nikola Tesla",
+ "Physics"
+ ],
+ [
+ "Claude Shannon",
+ "Math",
+ "Computer Science"
+ ]
+ ]
+ }
+]
diff --git a/examples/todos/todos_init/main.go b/examples/todos/todos_init/main.go
new file mode 100644
index 0000000..68a233c
--- /dev/null
+++ b/examples/todos/todos_init/main.go
@@ -0,0 +1,180 @@
+// todos_init is a tool to initialize the store with an initial database. This
+// is really for demo purposes; in a real database, the contents would be
+// persistant.
+//
+// The data is loaded from a JSON file, todos_init/data.json.
+//
+// Since JSON doesn't support all of the store types, there is a translation
+// phase, where the contents are loaded into a string form, then converted to
+// the todos/schema schema.
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strings"
+
+ "veyron/examples/todos/schema"
+ "veyron2"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/storage"
+ "veyron2/storage/vstore"
+ "veyron2/vlog"
+)
+
+var (
+ storeName string
+ dataPath = flag.String("data-path", "data.json", "Path to data JSON file")
+)
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ // TODO(sadovsky): Change this to be the correct veyron2 path.
+ dir := "global/vstore/" + hostname + "/" + username
+ flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
+}
+
+// List is the JSON representation for schema.List.
+// Note, we use this representation for parity with the Meteor example.
+// https://www.meteor.com/examples/todos
+type List struct {
+ Name string
+ // Each element corresponds to a schema.Item; each item is represented as a
+ // list where the first element is the item name and the remaining elements
+ // are tags.
+ Contents [][]string
+}
+
+// state is the initial store state.
+type state struct {
+ store storage.Store
+ transaction storage.Transaction
+}
+
+// newState returns a fresh state.
+func newState(st storage.Store) *state {
+ return &state{store: st}
+}
+
+// put adds a value to the store, creating the path to the value if it doesn't
+// already exist.
+func (st *state) put(path string, v interface{}) {
+ vlog.Infof("Storing %q = %+v", path, v)
+ st.makeParentDirs(path)
+ if _, err := st.store.Bind(path).Put(st.transaction, v); err != nil {
+ vlog.Errorf("put failed: %s: %s", path, err)
+ return
+ }
+}
+
+// makeParentDirs creates the directories in a path if they do not already
+// exist.
+func (st *state) makeParentDirs(path string) {
+ l := strings.Split(path, "/")
+ for i, _ := range l {
+ prefix := filepath.Join(l[:i]...)
+ o := st.store.Bind(prefix)
+ if _, err := o.Get(st.transaction); err != nil {
+ if _, err := o.Put(st.transaction, &schema.Dir{}); err != nil {
+ vlog.Errorf("Error creating parent %q: %s", prefix, err)
+ }
+ }
+ }
+}
+
+// newTransaction starts a new transaction.
+func (st *state) newTransaction() {
+ st.transaction = vstore.NewTransaction()
+}
+
+// commit commits the current transaction.
+func (st *state) commit() {
+ if err := st.transaction.Commit(); err != nil {
+ vlog.Errorf("Failed to commit transaction: %s", err)
+ }
+ st.transaction = nil
+}
+
+// storeList saves a schema.List to the store with name /lists/<Name>, and also
+// saves the list's child items.
+func (st *state) storeList(l *List) {
+ x := &schema.List{
+ Name: l.Name,
+ }
+ path := "/lists/" + x.Name
+ st.put(path, x)
+
+ // Store this list's child items.
+ // TODO(sadovsky): Ensure that order is preserved, and is reflected in the UI.
+ for i, v := range l.Contents {
+ st.storeItem(path, i, v[0], v[1:])
+ }
+}
+
+// storeItem saves a schema.Item to the store with name /<listPath>/Items/<id>.
+// Note that <id> is defined by storeList to be the position of the item in its
+// parent list.
+func (st *state) storeItem(listPath string, id int, text string, tags []string) {
+ x := &schema.Item{
+ Text: text,
+ Done: false,
+ Tags: tags,
+ }
+ path := fmt.Sprintf("%s/Items/%d", listPath, id)
+ st.put(path, x)
+}
+
+// processJSONFile saves the contents of the JSON file to the store.
+func (st *state) processJSONFile(path string) error {
+ vlog.Infof("Loading file %s", path)
+ file, err := os.Open(path)
+ if err != nil {
+ return fmt.Errorf("Can't open %q: %s", path, err)
+ }
+ defer file.Close()
+
+ lists := make([]*List, 0)
+ decoder := json.NewDecoder(file)
+ if err := decoder.Decode(&lists); err != nil {
+ return fmt.Errorf("Can't decode: %s", err)
+ }
+
+ st.newTransaction()
+ for _, v := range lists {
+ st.storeList(v)
+ }
+ st.commit()
+ return nil
+}
+
+// main reads the data JSON file and populates the store.
+func main() {
+ // The client's identity needs to match the Admin ACLs at the empty store
+ // (since only the admin can put data). The identity here matches with that
+ // used for server.ServerConfig.Admin in todos_stored/main.go. An alternative
+ // would be to relax the ACLs on the store.
+ rt.Init(veyron2.LocalID(security.FakePrivateID("anonymous")))
+
+ vlog.Infof("Binding to store on %s", storeName)
+ st, err := vstore.New(storeName)
+ if err != nil {
+ vlog.Fatalf("Can't connect to store: %s: %s", storeName, err)
+ }
+ state := newState(st)
+
+ if err := state.processJSONFile(*dataPath); err != nil {
+ vlog.Errorf("Failed to write data: %s", err)
+ }
+}
diff --git a/examples/todos/todos_stored/main.go b/examples/todos/todos_stored/main.go
new file mode 100644
index 0000000..b282ca4
--- /dev/null
+++ b/examples/todos/todos_stored/main.go
@@ -0,0 +1,102 @@
+// Package stored is a storage server using the todos/schema schema.
+//
+// We need a schema-specific store because the current store implementation does
+// not support unregistered types.
+// TODO(jyh): Support unregistered types and remove this server.
+//
+// Usage:
+// stored [--name=<mount>] [--db=<dbName>]
+// - <name> is the Veyron mount point name, default /global/vstore/<hostname>/<username>.
+// - <dbName> is the filename in which to store the data.
+//
+// NOTE: For now, to make this demo work, replace "return ErrUntrustedKey" with
+// "return nil" in "IsTrusted" in
+// veyron/runtimes/google/security/keys/trusted_keys.go.
+//
+// The Store service has Veyron name, <name>/.store. Individual values with
+// path <path> have name <name>/<path>.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "os/user"
+
+ _ "veyron/examples/todos/schema" // Register the todos/schema types.
+ "veyron/services/store/server"
+
+ "veyron2/rt"
+ "veyron2/security"
+)
+
+var (
+ mountName string
+ dbName = flag.String("db", "/var/tmp/todos.db", "Store database dir")
+
+ // TODO(jyh): Figure out how to get a real public ID.
+ rootPublicID security.PublicID = security.FakePrivateID("anonymous").PublicID()
+)
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ // TODO(sadovsky): Change this to be the correct veyron2 path.
+ dir := "global/vstore/" + hostname + "/" + username
+ flag.StringVar(&mountName, "name", dir, "Mount point for store")
+}
+
+// main starts the store service, taking arguments from the command line flags.
+func main() {
+ r := rt.Init()
+
+ // Create a new server instance.
+ s, err := r.NewServer()
+ if err != nil {
+ log.Fatal("r.NewServer() failed: ", err)
+ }
+
+ // Create a new StoreService.
+ storeService, err := server.New(server.ServerConfig{Admin: rootPublicID, DBName: *dbName})
+ if err != nil {
+ log.Fatal("server.New() failed: ", err)
+ }
+ defer storeService.Close()
+
+ // Register the services.
+ storeDisp := server.NewStoreDispatcher(storeService)
+ objectDisp := server.NewObjectDispatcher(storeService)
+ if err := s.Register(".store", storeDisp); err != nil {
+ log.Fatal("s.Register(storeDisp) failed: ", err)
+ }
+ if err := s.Register("", objectDisp); err != nil {
+ log.Fatal("s.Register(objectDisp) failed: ", err)
+ }
+
+ // Create an endpoint and start listening.
+ ep, err := s.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ log.Fatal("s.Listen() failed: ", err)
+ }
+
+ // Publish the service in the mount table.
+ fmt.Printf("Endpoint: %s\n", ep)
+ fmt.Printf("Name: %s\n", mountName)
+ fmt.Printf("Example commands:\n")
+ fmt.Printf("./bin/todos_init --data-path=src/veyron/examples/todos/todos_init/data.json \"--store=/%s\"\n", ep)
+ fmt.Printf("./bin/todos_appd \"--store=/%s\"\n", ep)
+ if err := s.Publish(mountName); err != nil {
+ log.Fatal("s.Publish() failed: ", err)
+ }
+
+ // Wait forever.
+ done := make(chan struct{})
+ <-done
+}
diff --git a/examples/tunnel/lib/forward.go b/examples/tunnel/lib/forward.go
new file mode 100644
index 0000000..5a7c21f
--- /dev/null
+++ b/examples/tunnel/lib/forward.go
@@ -0,0 +1,71 @@
+package lib
+
+import (
+ "fmt"
+ "io"
+ "net"
+)
+
+type sender interface {
+ Send([]uint8) error
+}
+type receiver interface {
+ Recv() ([]uint8, error)
+}
+
+// stream is the interface common to TunnelForwardStream and TunnelServiceForwardStream.
+type stream interface {
+ sender
+ receiver
+}
+
+// Forward forwards data read from net.Conn to a TunnelForwardStream or a TunnelServiceForwardStream.
+func Forward(conn net.Conn, stream stream) error {
+ defer conn.Close()
+ // Both conn2stream and stream2conn will write to the channel exactly
+ // once.
+ // Forward reads from the channel exactly once.
+ // A buffered channel is used to prevent the other write to the channel
+ // from blocking.
+ done := make(chan error, 1)
+ go conn2stream(conn, stream, done)
+ go stream2conn(stream, conn, done)
+ return <-done
+}
+
+func conn2stream(r io.Reader, s sender, done chan error) {
+ var buf [2048]byte
+ for {
+ n, err := r.Read(buf[:])
+ if err == io.EOF {
+ done <- nil
+ return
+ }
+ if err != nil {
+ done <- err
+ return
+ }
+ if err := s.Send(buf[:n]); err != nil {
+ done <- err
+ return
+ }
+ }
+}
+
+func stream2conn(r receiver, w io.Writer, done chan error) {
+ for {
+ buf, err := r.Recv()
+ if err == io.EOF {
+ done <- nil
+ return
+ }
+ if err != nil {
+ done <- err
+ return
+ }
+ if n, err := w.Write(buf); n != len(buf) || err != nil {
+ done <- fmt.Errorf("conn.Write returned (%d, %v) want (%d, nil)", n, err, len(buf))
+ return
+ }
+ }
+}
diff --git a/examples/tunnel/lib/terminal.go b/examples/tunnel/lib/terminal.go
new file mode 100644
index 0000000..1e33bb1
--- /dev/null
+++ b/examples/tunnel/lib/terminal.go
@@ -0,0 +1,93 @@
+package lib
+
+import (
+ "errors"
+ "os/exec"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "veyron2/vlog"
+)
+
+// Used with ioctl TIOCGWINSZ and TIOCSWINSZ.
+type Winsize struct {
+ Row uint16
+ Col uint16
+ Xpixel uint16
+ Ypixel uint16
+}
+
+// SetWindowSize sets the terminal's window size.
+func SetWindowSize(fd uintptr, ws Winsize) error {
+ vlog.Infof("Setting window size: %v", ws)
+ ret, _, _ := syscall.Syscall(
+ syscall.SYS_IOCTL,
+ fd,
+ uintptr(syscall.TIOCSWINSZ),
+ uintptr(unsafe.Pointer(&ws)))
+ if int(ret) == -1 {
+ return errors.New("ioctl(TIOCSWINSZ) failed")
+ }
+ return nil
+}
+
+// GetWindowSize gets the terminal's window size.
+func GetWindowSize() (*Winsize, error) {
+ ws := &Winsize{}
+ ret, _, _ := syscall.Syscall(
+ syscall.SYS_IOCTL,
+ uintptr(syscall.Stdin),
+ uintptr(syscall.TIOCGWINSZ),
+ uintptr(unsafe.Pointer(ws)))
+ if int(ret) == -1 {
+ return nil, errors.New("ioctl(TIOCGWINSZ) failed")
+ }
+ return ws, nil
+}
+
+func EnterRawTerminalMode() string {
+ var savedBytes []byte
+ var err error
+ if savedBytes, err = exec.Command("stty", "-F", "/dev/tty", "-g").Output(); err != nil {
+ vlog.Infof("Failed to save terminal settings: %q (%v)", savedBytes, err)
+ }
+ saved := strings.TrimSpace(string(savedBytes))
+
+ args := []string{
+ "-F", "/dev/tty",
+ // Don't buffer stdin. Read characters as they are typed.
+ "-icanon", "min", "1", "time", "0",
+ // Turn off local echo of input characters.
+ "-echo", "-echoe", "-echok", "-echonl",
+ // Disable interrupt, quit, and suspend special characters.
+ "-isig",
+ // Ignore characters with parity errors.
+ "ignpar",
+ // Disable translate newline to carriage return.
+ "-inlcr",
+ // Disable ignore carriage return.
+ "-igncr",
+ // Disable translate carriage return to newline.
+ "-icrnl",
+ // Disable flow control.
+ "-ixon", "-ixany", "-ixoff",
+ // Disable non-POSIX special characters.
+ "-iexten",
+ }
+ if out, err := exec.Command("stty", args...).CombinedOutput(); err != nil {
+ vlog.Infof("stty failed (%v) (%q)", err, out)
+ }
+
+ return string(saved)
+}
+
+func RestoreTerminalSettings(saved string) {
+ args := []string{
+ "-F", "/dev/tty",
+ saved,
+ }
+ if out, err := exec.Command("stty", args...).CombinedOutput(); err != nil {
+ vlog.Infof("stty failed (%v) (%q)", err, out)
+ }
+}
diff --git a/examples/tunnel/tunnel.idl b/examples/tunnel/tunnel.idl
new file mode 100644
index 0000000..88eb2bf
--- /dev/null
+++ b/examples/tunnel/tunnel.idl
@@ -0,0 +1,40 @@
+package tunnel
+
+import "veyron2/security"
+
+// Tunnel creates a network tunnel from the client to the server.
+
+type Tunnel interface {
+ // The Forward method is used for network forwarding. All the data sent over
+ // the byte stream is forwarded to the requested network address and all the
+ // data received from that network connection is sent back in the reply
+ // stream.
+ Forward(network, address string) stream<[]byte, []byte> error {security.AdminLabel}
+
+ // The Shell method is used to either run shell commands remotely, or to open
+ // an interactive shell. The data received over the byte stream is sent to the
+ // shell's stdin, and the data received from the shell's stdout and stderr is
+ // sent back in the reply stream. It returns the exit status of the shell
+ // command.
+ Shell(command string, shellOpts ShellOpts) stream<ClientShellPacket, ServerShellPacket> (int32, error) {security.AdminLabel}
+}
+
+type ShellOpts struct {
+ UsePty bool // Whether to open a pseudo-terminal
+ Environment []string // Environment variables to pass to the remote shell.
+ Rows, Cols uint32 // Window size.
+}
+
+type ClientShellPacket struct {
+ // Bytes going to the shell's stdin.
+ Stdin []byte
+ // A dynamic update of the window size. The default value of 0 means no-change.
+ Rows, Cols uint32
+}
+
+type ServerShellPacket struct {
+ // Bytes coming from the shell's stdout.
+ Stdout []byte
+ // Bytes coming from the shell's stderr.
+ Stderr []byte
+}
diff --git a/examples/tunnel/tunnel.idl.go b/examples/tunnel/tunnel.idl.go
new file mode 100644
index 0000000..14c039c
--- /dev/null
+++ b/examples/tunnel/tunnel.idl.go
@@ -0,0 +1,423 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: tunnel.idl
+
+package tunnel
+
+import (
+ "veyron2/security"
+
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_idl "veyron2/idl"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt/r"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+type ShellOpts struct {
+ UsePty bool // Whether to open a pseudo-terminal
+ Environment []string // Environment variables to pass to the remote shell.
+ Rows uint32 // Window size.
+ Cols uint32
+}
+type ClientShellPacket struct {
+ // Bytes going to the shell's stdin.
+ Stdin []byte
+ // A dynamic update of the window size. The default value of 0 means no-change.
+ Rows uint32
+ Cols uint32
+}
+type ServerShellPacket struct {
+ // Bytes coming from the shell's stdout.
+ Stdout []byte
+ // Bytes coming from the shell's stderr.
+ Stderr []byte
+}
+
+// Tunnel is the interface the client binds and uses.
+// Tunnel_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type Tunnel_InternalNoTagGetter interface {
+
+ // The Forward method is used for network forwarding. All the data sent over
+ // the byte stream is forwarded to the requested network address and all the
+ // data received from that network connection is sent back in the reply
+ // stream.
+ Forward(network string, address string, opts ..._gen_ipc.ClientCallOpt) (reply TunnelForwardStream, err error)
+
+ // The Shell method is used to either run shell commands remotely, or to open
+ // an interactive shell. The data received over the byte stream is sent to the
+ // shell's stdin, and the data received from the shell's stdout and stderr is
+ // sent back in the reply stream. It returns the exit status of the shell
+ // command.
+ Shell(command string, shellOpts ShellOpts, opts ..._gen_ipc.ClientCallOpt) (reply TunnelShellStream, err error)
+}
+type Tunnel interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ Tunnel_InternalNoTagGetter
+}
+
+// TunnelService is the interface the server implements.
+type TunnelService interface {
+
+ // The Forward method is used for network forwarding. All the data sent over
+ // the byte stream is forwarded to the requested network address and all the
+ // data received from that network connection is sent back in the reply
+ // stream.
+ Forward(context _gen_ipc.Context, network string, address string, stream TunnelServiceForwardStream) (err error)
+
+ // The Shell method is used to either run shell commands remotely, or to open
+ // an interactive shell. The data received over the byte stream is sent to the
+ // shell's stdin, and the data received from the shell's stdout and stderr is
+ // sent back in the reply stream. It returns the exit status of the shell
+ // command.
+ Shell(context _gen_ipc.Context, command string, shellOpts ShellOpts, stream TunnelServiceShellStream) (reply int32, err error)
+}
+
+// TunnelForwardStream is the interface for streaming responses of the method
+// Forward in the service interface Tunnel.
+type TunnelForwardStream interface {
+
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item []byte) error
+
+ // CloseSend indicates to the server that no more items will be sent; server
+ // Recv calls will receive io.EOF after all sent items. Subsequent calls to
+ // Send on the client will fail. This is an optional call - it's used by
+ // streaming clients that need the server to receive the io.EOF terminator.
+ CloseSend() error
+
+ // Recv returns the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item []byte, err error)
+
+ // Finish closes the stream and returns the positional return values for
+ // call.
+ Finish() (err error)
+
+ // Cancel cancels the RPC, notifying the server to stop processing.
+ Cancel()
+}
+
+// Implementation of the TunnelForwardStream interface that is not exported.
+type implTunnelForwardStream struct {
+ clientCall _gen_ipc.ClientCall
+}
+
+func (c *implTunnelForwardStream) Send(item []byte) error {
+ return c.clientCall.Send(item)
+}
+
+func (c *implTunnelForwardStream) CloseSend() error {
+ return c.clientCall.CloseSend()
+}
+
+func (c *implTunnelForwardStream) Recv() (item []byte, err error) {
+ err = c.clientCall.Recv(&item)
+ return
+}
+
+func (c *implTunnelForwardStream) Finish() (err error) {
+ if ierr := c.clientCall.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *implTunnelForwardStream) Cancel() {
+ c.clientCall.Cancel()
+}
+
+// TunnelServiceForwardStream is the interface for streaming responses of the method
+// Forward in the service interface Tunnel.
+type TunnelServiceForwardStream interface {
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item []byte) error
+
+ // Recv fills itemptr with the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item []byte, err error)
+}
+
+// Implementation of the TunnelServiceForwardStream interface that is not exported.
+type implTunnelServiceForwardStream struct {
+ serverCall _gen_ipc.ServerCall
+}
+
+func (s *implTunnelServiceForwardStream) Send(item []byte) error {
+ return s.serverCall.Send(item)
+}
+
+func (s *implTunnelServiceForwardStream) Recv() (item []byte, err error) {
+ err = s.serverCall.Recv(&item)
+ return
+}
+
+// TunnelShellStream is the interface for streaming responses of the method
+// Shell in the service interface Tunnel.
+type TunnelShellStream interface {
+
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item ClientShellPacket) error
+
+ // CloseSend indicates to the server that no more items will be sent; server
+ // Recv calls will receive io.EOF after all sent items. Subsequent calls to
+ // Send on the client will fail. This is an optional call - it's used by
+ // streaming clients that need the server to receive the io.EOF terminator.
+ CloseSend() error
+
+ // Recv returns the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item ServerShellPacket, err error)
+
+ // Finish closes the stream and returns the positional return values for
+ // call.
+ Finish() (reply int32, err error)
+
+ // Cancel cancels the RPC, notifying the server to stop processing.
+ Cancel()
+}
+
+// Implementation of the TunnelShellStream interface that is not exported.
+type implTunnelShellStream struct {
+ clientCall _gen_ipc.ClientCall
+}
+
+func (c *implTunnelShellStream) Send(item ClientShellPacket) error {
+ return c.clientCall.Send(item)
+}
+
+func (c *implTunnelShellStream) CloseSend() error {
+ return c.clientCall.CloseSend()
+}
+
+func (c *implTunnelShellStream) Recv() (item ServerShellPacket, err error) {
+ err = c.clientCall.Recv(&item)
+ return
+}
+
+func (c *implTunnelShellStream) Finish() (reply int32, err error) {
+ if ierr := c.clientCall.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *implTunnelShellStream) Cancel() {
+ c.clientCall.Cancel()
+}
+
+// TunnelServiceShellStream is the interface for streaming responses of the method
+// Shell in the service interface Tunnel.
+type TunnelServiceShellStream interface {
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item ServerShellPacket) error
+
+ // Recv fills itemptr with the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item ClientShellPacket, err error)
+}
+
+// Implementation of the TunnelServiceShellStream interface that is not exported.
+type implTunnelServiceShellStream struct {
+ serverCall _gen_ipc.ServerCall
+}
+
+func (s *implTunnelServiceShellStream) Send(item ServerShellPacket) error {
+ return s.serverCall.Send(item)
+}
+
+func (s *implTunnelServiceShellStream) Recv() (item ClientShellPacket, err error) {
+ err = s.serverCall.Recv(&item)
+ return
+}
+
+// BindTunnel returns the client stub implementing the Tunnel
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindTunnel(name string, opts ..._gen_ipc.BindOpt) (Tunnel, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubTunnel{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerTunnel creates a new server stub.
+//
+// It takes a regular server implementing the TunnelService
+// interface, and returns a new server stub.
+func NewServerTunnel(server TunnelService) interface{} {
+ return &ServerStubTunnel{
+ service: server,
+ }
+}
+
+// clientStubTunnel implements Tunnel.
+type clientStubTunnel struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubTunnel) GetMethodTags(method string) []interface{} {
+ return GetTunnelMethodTags(method)
+}
+
+func (__gen_c *clientStubTunnel) Forward(network string, address string, opts ..._gen_ipc.ClientCallOpt) (reply TunnelForwardStream, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Forward", []interface{}{network, address}, opts...); err != nil {
+ return
+ }
+ reply = &implTunnelForwardStream{clientCall: call}
+ return
+}
+
+func (__gen_c *clientStubTunnel) Shell(command string, shellOpts ShellOpts, opts ..._gen_ipc.ClientCallOpt) (reply TunnelShellStream, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Shell", []interface{}{command, shellOpts}, opts...); err != nil {
+ return
+ }
+ reply = &implTunnelShellStream{clientCall: call}
+ return
+}
+
+func (c *clientStubTunnel) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubTunnel wraps a server that implements
+// TunnelService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubTunnel struct {
+ service TunnelService
+}
+
+func (s *ServerStubTunnel) GetMethodTags(method string) []interface{} {
+ return GetTunnelMethodTags(method)
+}
+
+func (s *ServerStubTunnel) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Forward"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "network", Type: 3},
+ {Name: "address", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ InStream: 67,
+ OutStream: 67,
+ }
+ result.Methods["Shell"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "command", Type: 3},
+ {Name: "shellOpts", Type: 68},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 36},
+ {Name: "", Type: 65},
+ },
+ InStream: 69,
+ OutStream: 70,
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x32, Name: "byte", Tags: []string(nil)}, _gen_wiretype.SliceType{Elem: 0x42, Name: "", Tags: []string(nil)}, _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x2, Name: "UsePty"},
+ _gen_wiretype.FieldType{Type: 0x3d, Name: "Environment"},
+ _gen_wiretype.FieldType{Type: 0x34, Name: "Rows"},
+ _gen_wiretype.FieldType{Type: 0x34, Name: "Cols"},
+ },
+ "ShellOpts", []string(nil)},
+ _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x43, Name: "Stdin"},
+ _gen_wiretype.FieldType{Type: 0x34, Name: "Rows"},
+ _gen_wiretype.FieldType{Type: 0x34, Name: "Cols"},
+ },
+ "ClientShellPacket", []string(nil)},
+ _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x43, Name: "Stdout"},
+ _gen_wiretype.FieldType{Type: 0x43, Name: "Stderr"},
+ },
+ "ServerShellPacket", []string(nil)},
+ }
+
+ return result, nil
+}
+
+func (s *ServerStubTunnel) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubTunnel) Forward(call _gen_ipc.ServerCall, network string, address string) (err error) {
+ stream := &implTunnelServiceForwardStream{serverCall: call}
+ err = __gen_s.service.Forward(call, network, address, stream)
+ return
+}
+
+func (__gen_s *ServerStubTunnel) Shell(call _gen_ipc.ServerCall, command string, shellOpts ShellOpts) (reply int32, err error) {
+ stream := &implTunnelServiceShellStream{serverCall: call}
+ reply, err = __gen_s.service.Shell(call, command, shellOpts, stream)
+ return
+}
+
+func GetTunnelMethodTags(method string) []interface{} {
+ switch method {
+ case "Forward":
+ return []interface{}{security.Label(4)}
+ case "Shell":
+ return []interface{}{security.Label(4)}
+ default:
+ return nil
+ }
+}
diff --git a/examples/tunnel/tunneld/impl/impl.go b/examples/tunnel/tunneld/impl/impl.go
new file mode 100644
index 0000000..cd210ed
--- /dev/null
+++ b/examples/tunnel/tunneld/impl/impl.go
@@ -0,0 +1,162 @@
+package impl
+
+import (
+ "github.com/kr/pty"
+
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+ "syscall"
+
+ "veyron/examples/tunnel"
+ "veyron/examples/tunnel/lib"
+ "veyron2/ipc"
+ "veyron2/vlog"
+)
+
+// T implements tunnel.TunnelService
+type T struct {
+}
+
+func (t *T) Forward(ctx ipc.Context, network, address string, stream tunnel.TunnelServiceForwardStream) error {
+ conn, err := net.Dial(network, address)
+ if err != nil {
+ return err
+ }
+ name := fmt.Sprintf("RemoteID:%v LocalAddr:%v RemoteAddr:%v", ctx.RemoteID(), conn.LocalAddr(), conn.RemoteAddr())
+ vlog.Infof("TUNNEL START: %v", name)
+ err = lib.Forward(conn, stream)
+ vlog.Infof("TUNNEL END : %v (%v)", name, err)
+ return err
+}
+
+func (t *T) Shell(ctx ipc.Context, command string, shellOpts tunnel.ShellOpts, stream tunnel.TunnelServiceShellStream) (int32, error) {
+ vlog.Infof("SHELL START for %v: %q", ctx.RemoteID(), command)
+
+ const nonShellErrorCode = 255
+
+ shell, err := findShell()
+ if err != nil {
+ return nonShellErrorCode, err
+ }
+
+ var c *exec.Cmd
+ // An empty command means that we need an interactive shell.
+ if len(command) == 0 {
+ c = exec.Command(shell, "-i")
+ sendMotd(stream)
+ } else {
+ c = exec.Command(shell, "-c", command)
+ }
+
+ c.Env = []string{
+ fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
+ fmt.Sprintf("VEYRON_LOCAL_IDENTITY=%s", ctx.LocalID()),
+ fmt.Sprintf("VEYRON_REMOTE_IDENTITY=%s", ctx.RemoteID()),
+ }
+ c.Env = append(c.Env, shellOpts.Environment...)
+ vlog.Infof("Shell environment: %v", c.Env)
+
+ c.Dir = os.Getenv("HOME")
+ vlog.Infof("Shell CWD: %v", c.Dir)
+
+ var (
+ stdin io.WriteCloser // We write to stdin.
+ stdout, stderr io.ReadCloser // We read from stdout and stderr.
+ ptyFd uintptr // File descriptor for pty.
+ )
+
+ if shellOpts.UsePty {
+ f, err := pty.Start(c)
+ if err != nil {
+ return nonShellErrorCode, err
+ }
+ stdin = f
+ stdout = f
+ stderr = nil
+ ptyFd = f.Fd()
+
+ defer f.Close()
+
+ setWindowSize(ptyFd, shellOpts.Rows, shellOpts.Cols)
+ } else {
+ var err error
+ if stdin, err = c.StdinPipe(); err != nil {
+ return nonShellErrorCode, err
+ }
+ defer stdin.Close()
+
+ if stdout, err = c.StdoutPipe(); err != nil {
+ return nonShellErrorCode, err
+ }
+ defer stdout.Close()
+
+ if stderr, err = c.StderrPipe(); err != nil {
+ return nonShellErrorCode, err
+ }
+ defer stderr.Close()
+
+ if err = c.Start(); err != nil {
+ vlog.Infof("Cmd.Start failed: %v", err)
+ return nonShellErrorCode, err
+ }
+ }
+
+ defer c.Process.Kill()
+
+ ferr := runIOManager(stdin, stdout, stderr, ptyFd, stream)
+ vlog.Infof("SHELL END for %v: %q (%v)", ctx.RemoteID(), command, ferr)
+
+ // Check the exit status.
+ var status syscall.WaitStatus
+ if _, err := syscall.Wait4(c.Process.Pid, &status, syscall.WNOHANG, nil); err != nil {
+ return nonShellErrorCode, err
+ }
+ if status.Signaled() {
+ return int32(status), fmt.Errorf("process killed by signal %d (%v)", int(status.Signal()), status.Signal())
+ }
+ if status.Exited() {
+ if status.ExitStatus() == 0 {
+ return 0, nil
+ }
+ return int32(status.ExitStatus()), fmt.Errorf("process exited with exit status %d", status.ExitStatus())
+ }
+
+ // The process has not exited. Use the error from ForwardStdIO.
+ return nonShellErrorCode, ferr
+}
+
+// findShell returns the path to the first usable shell binary.
+func findShell() (string, error) {
+ shells := []string{"/bin/bash", "/bin/sh"}
+ for _, s := range shells {
+ if _, err := os.Stat(s); err == nil {
+ return s, nil
+ }
+ }
+ return "", errors.New("could not find any shell binary")
+}
+
+// sendMotd sends the content of the MOTD file to the stream, if it exists.
+func sendMotd(s tunnel.TunnelServiceShellStream) {
+ data, err := ioutil.ReadFile("/etc/motd")
+ if err != nil {
+ // No MOTD. That's OK.
+ return
+ }
+ packet := tunnel.ServerShellPacket{Stdout: []byte(data)}
+ if err = s.Send(packet); err != nil {
+ vlog.Infof("Send failed: %v", err)
+ }
+}
+
+func setWindowSize(fd uintptr, row, col uint32) {
+ ws := lib.Winsize{Row: uint16(row), Col: uint16(col)}
+ if err := lib.SetWindowSize(fd, ws); err != nil {
+ vlog.Infof("Failed to set window size: %v", err)
+ }
+}
diff --git a/examples/tunnel/tunneld/impl/iomanager.go b/examples/tunnel/tunneld/impl/iomanager.go
new file mode 100644
index 0000000..fe422db
--- /dev/null
+++ b/examples/tunnel/tunneld/impl/iomanager.go
@@ -0,0 +1,113 @@
+package impl
+
+import (
+ "fmt"
+ "io"
+
+ "veyron/examples/tunnel"
+ "veyron2/vlog"
+)
+
+func runIOManager(stdin io.Writer, stdout, stderr io.Reader, ptyFd uintptr, stream tunnel.TunnelServiceShellStream) error {
+ m := ioManager{stdin: stdin, stdout: stdout, stderr: stderr, ptyFd: ptyFd, stream: stream}
+ return m.run()
+}
+
+// ioManager manages the forwarding of all the data between the shell and the
+// stream.
+type ioManager struct {
+ stdin io.Writer
+ stdout, stderr io.Reader
+ ptyFd uintptr
+ stream tunnel.TunnelServiceShellStream
+
+ // done receives any error from chan2stream, user2stream, or
+ // stream2user.
+ done chan error
+ // outchan is used to serialize the output to the stream. This is
+ // needed because stream.Send is not thread-safe.
+ outchan chan tunnel.ServerShellPacket
+}
+
+func (m *ioManager) run() error {
+ // done receives any error from chan2stream, stdout2stream, or
+ // stream2stdin.
+ m.done = make(chan error, 3)
+
+ // outchan is used to serialize the output to the stream.
+ // chan2stream() receives data sent by stdout2outchan() and
+ // stderr2outchan() and sends it to the stream.
+ m.outchan = make(chan tunnel.ServerShellPacket)
+ defer close(m.outchan)
+ go m.chan2stream()
+
+ // Forward data between the shell's stdio and the stream.
+ go m.stdout2outchan()
+ if m.stderr != nil {
+ go m.stderr2outchan()
+ }
+ go m.stream2stdin()
+
+ // Block until something reports an error.
+ return <-m.done
+}
+
+// chan2stream receives ServerShellPacket from outchan and sends it to stream.
+func (m *ioManager) chan2stream() {
+ for packet := range m.outchan {
+ if err := m.stream.Send(packet); err != nil {
+ m.done <- err
+ return
+ }
+ }
+ m.done <- io.EOF
+}
+
+// stdout2stream reads data from the shell's stdout and sends it to the outchan.
+func (m *ioManager) stdout2outchan() {
+ for {
+ buf := make([]byte, 2048)
+ n, err := m.stdout.Read(buf[:])
+ if err != nil {
+ vlog.VI(2).Infof("stdout2outchan: %v", err)
+ m.done <- err
+ return
+ }
+ m.outchan <- tunnel.ServerShellPacket{Stdout: buf[:n]}
+ }
+}
+
+// stderr2stream reads data from the shell's stderr and sends it to the outchan.
+func (m *ioManager) stderr2outchan() {
+ for {
+ buf := make([]byte, 2048)
+ n, err := m.stderr.Read(buf[:])
+ if err != nil {
+ vlog.VI(2).Infof("stderr2outchan: %v", err)
+ m.done <- err
+ return
+ }
+ m.outchan <- tunnel.ServerShellPacket{Stderr: buf[:n]}
+ }
+}
+
+// stream2stdin reads data from the stream and sends it to the shell's stdin.
+func (m *ioManager) stream2stdin() {
+ for {
+ packet, err := m.stream.Recv()
+ if err != nil {
+ vlog.VI(2).Infof("stream2stdin: %v", err)
+ m.done <- err
+ return
+ }
+ if len(packet.Stdin) > 0 {
+ if n, err := m.stdin.Write(packet.Stdin); n != len(packet.Stdin) || err != nil {
+ m.done <- fmt.Errorf("stdin.Write returned (%d, %v) want (%d, nil)", n, err, len(packet.Stdin))
+ return
+ }
+ }
+ if packet.Rows > 0 && packet.Cols > 0 && m.ptyFd != 0 {
+ setWindowSize(m.ptyFd, packet.Rows, packet.Cols)
+ }
+ }
+}
diff --git a/examples/tunnel/tunneld/main.go b/examples/tunnel/tunneld/main.go
new file mode 100644
index 0000000..44aaf0e
--- /dev/null
+++ b/examples/tunnel/tunneld/main.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "net"
+ "os"
+ "strings"
+
+ "veyron/examples/tunnel"
+ "veyron/examples/tunnel/tunneld/impl"
+ "veyron/lib/signals"
+ isecurity "veyron/runtimes/google/security"
+ "veyron2/ipc"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/vlog"
+)
+
+var (
+ protocol = flag.String("protocol", "tcp", "network to listen on. For example, set to 'veyron' and set --address to the endpoint/name of a proxy to have this tunnel service proxied.")
+ address = flag.String("address", ":0", "address to listen on")
+
+ users = flag.String("users", "", "A comma-separated list of principal patterns allowed to use this service.")
+)
+
+// firstHardwareAddrInUse returns the hwaddr of the first network interface
+// that is up, excluding loopback.
+func firstHardwareAddrInUse() (string, error) {
+ interfaces, err := net.Interfaces()
+ if err != nil {
+ return "", err
+ }
+ for _, i := range interfaces {
+ if i.Name != "lo" && i.Flags&net.FlagUp != 0 {
+ name := i.HardwareAddr.String()
+ vlog.Infof("Using %q (from %v)", name, i.Name)
+ return name, nil
+ }
+ }
+ return "", errors.New("No usable network interfaces")
+}
+
+func authorizer() security.Authorizer {
+ ACL := make(security.ACL)
+ principals := strings.Split(*users, ",")
+ for _, p := range principals {
+ ACL[security.PrincipalPattern(p)] = security.LabelSet(security.AdminLabel)
+ }
+ return isecurity.NewACLAuthorizer(ACL)
+}
+
+func main() {
+ r := rt.Init()
+ defer r.Shutdown()
+ server, err := r.NewServer()
+ if err != nil {
+ vlog.Fatalf("NewServer failed: %v", err)
+ }
+ defer server.Stop()
+
+ if err := server.Register("", ipc.SoloDispatcher(tunnel.NewServerTunnel(&impl.T{}), authorizer())); err != nil {
+ vlog.Fatalf("Register failed: %v", err)
+ }
+ ep, err := server.Listen(*protocol, *address)
+ if err != nil {
+ vlog.Fatalf("Listen(%q, %q) failed: %v", "tcp", *address, err)
+ }
+ hwaddr, err := firstHardwareAddrInUse()
+ if err != nil {
+ vlog.Fatalf("Couldn't find a good hw address: %v", err)
+ }
+ hostname, err := os.Hostname()
+ if err != nil {
+ vlog.Fatalf("os.Hostname failed: %v", err)
+ }
+ // TODO(rthellend): This is not secure. We should use
+ // rt.R().Product().ID() and the associated verification, when it is
+ // ready.
+ names := []string{
+ fmt.Sprintf("tunnel/hostname/%s", hostname),
+ fmt.Sprintf("tunnel/hwaddr/%s", hwaddr),
+ fmt.Sprintf("tunnel/id/%s", rt.R().Identity().PublicID()),
+ }
+ published := false
+ for _, n := range names {
+ if err := server.Publish(n); err != nil {
+ vlog.Infof("Publish(%v) failed: %v", n, err)
+ continue
+ }
+ published = true
+ }
+ if !published {
+ vlog.Fatalf("Failed to publish with any of %v", names)
+ }
+ vlog.Infof("Listening on endpoint /%s (published as %v)", ep, names)
+ <-signals.ShutdownOnSignals()
+}
diff --git a/examples/tunnel/vsh/iomanager.go b/examples/tunnel/vsh/iomanager.go
new file mode 100644
index 0000000..00cd29d
--- /dev/null
+++ b/examples/tunnel/vsh/iomanager.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "veyron/examples/tunnel"
+ "veyron/examples/tunnel/lib"
+ "veyron2/vlog"
+)
+
+func runIOManager(stdin io.Reader, stdout, stderr io.Writer, stream tunnel.TunnelShellStream) error {
+ m := ioManager{stdin: stdin, stdout: stdout, stderr: stderr, stream: stream}
+ return m.run()
+}
+
+// ioManager manages the forwarding of all the data between the shell and the
+// stream.
+type ioManager struct {
+ stdin io.Reader
+ stdout, stderr io.Writer
+ stream tunnel.TunnelShellStream
+
+ // done receives any error from chan2stream, user2outchan, or
+ // stream2user.
+ done chan error
+ // outchan is used to serialize the output to the stream. This is
+ // needed because stream.Send is not thread-safe.
+ outchan chan tunnel.ClientShellPacket
+}
+
+func (m *ioManager) run() error {
+ m.done = make(chan error, 3)
+ // outchan is used to serialize the output to the stream.
+ // chan2stream() receives data sent by handleWindowResize() and
+ // user2outchan() and sends it to the stream.
+ m.outchan = make(chan tunnel.ClientShellPacket)
+ defer close(m.outchan)
+ go m.chan2stream()
+ // When the terminal window is resized, we receive a SIGWINCH. Then we
+ // send the new window size to the server.
+ winch := make(chan os.Signal, 1)
+ signal.Notify(winch, syscall.SIGWINCH)
+ defer signal.Stop(winch)
+ go m.handleWindowResize(winch)
+ // Forward data between the user and the remote shell.
+ go m.user2outchan()
+ go m.stream2user()
+ // Block until something reports an error.
+ return <-m.done
+}
+
+// chan2stream receives ClientShellPacket from outchan and sends it to stream.
+func (m *ioManager) chan2stream() {
+ for packet := range m.outchan {
+ if err := m.stream.Send(packet); err != nil {
+ m.done <- err
+ return
+ }
+ }
+ m.done <- io.EOF
+}
+
+func (m *ioManager) handleWindowResize(winch chan os.Signal) {
+ for _ = range winch {
+ ws, err := lib.GetWindowSize()
+ if err != nil {
+ vlog.Infof("GetWindowSize failed: %v", err)
+ continue
+ }
+ m.outchan <- tunnel.ClientShellPacket{Rows: uint32(ws.Row), Cols: uint32(ws.Col)}
+ }
+}
+
+// user2stream reads input from stdin and sends it to the outchan.
+func (m *ioManager) user2outchan() {
+ for {
+ buf := make([]byte, 2048)
+ n, err := m.stdin.Read(buf[:])
+ if err != nil {
+ vlog.VI(2).Infof("user2stream: %v", err)
+ m.done <- err
+ return
+ }
+ m.outchan <- tunnel.ClientShellPacket{Stdin: buf[:n]}
+ }
+}
+
+// stream2user reads data from the stream and sends it to either stdout or stderr.
+func (m *ioManager) stream2user() {
+ for {
+ packet, err := m.stream.Recv()
+ if err != nil {
+ vlog.VI(2).Infof("stream2user: %v", err)
+ m.done <- err
+ return
+ }
+ if len(packet.Stdout) > 0 {
+ if n, err := m.stdout.Write(packet.Stdout); n != len(packet.Stdout) || err != nil {
+ m.done <- fmt.Errorf("stdout.Write returned (%d, %v) want (%d, nil)", n, err, len(packet.Stdout))
+ return
+ }
+ }
+ if len(packet.Stderr) > 0 {
+ if n, err := m.stderr.Write(packet.Stderr); n != len(packet.Stderr) || err != nil {
+ m.done <- fmt.Errorf("stderr.Write returned (%d, %v) want (%d, nil)", n, err, len(packet.Stderr))
+ return
+ }
+ }
+ }
+}
diff --git a/examples/tunnel/vsh/main.go b/examples/tunnel/vsh/main.go
new file mode 100644
index 0000000..943835e
--- /dev/null
+++ b/examples/tunnel/vsh/main.go
@@ -0,0 +1,213 @@
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "net"
+ "os"
+ "path"
+ "strings"
+ "time"
+
+ "veyron/examples/tunnel"
+ "veyron/examples/tunnel/lib"
+ "veyron/lib/signals"
+ "veyron2"
+ "veyron2/rt"
+ "veyron2/vlog"
+)
+
+var (
+ disablePty = flag.Bool("T", false, "Disable pseudo-terminal allocation.")
+ forcePty = flag.Bool("t", false, "Force allocation of pseudo-terminal.")
+ vname = flag.String("vname", "", "Veyron name (or endpoint) for tunneling service.")
+
+ portforward = flag.String("L", "", "localaddr,remoteaddr Forward local 'localaddr' to 'remoteaddr'")
+ lprotocol = flag.String("local_protocol", "tcp", "Local network protocol for port forwarding")
+ rprotocol = flag.String("remote_protocol", "tcp", "Remote network protocol for port forwarding")
+
+ noshell = flag.Bool("N", false, "Do not execute a shell. Only do port forwarding.")
+)
+
+func init() {
+ flag.Usage = func() {
+ bname := path.Base(os.Args[0])
+ fmt.Fprintf(os.Stderr, `%s: Veyron SHell.
+
+This tool is used to run shell commands or an interactive shell on a remote
+tunneld service.
+
+To open an interactive shell, use:
+ %s --host=<veyron name or endpoint>
+
+To run a shell command, use:
+ %s --host=<veyron name or endpoint> <command to run>
+
+The -L flag will forward connections from a local port to a remote address
+through the tunneld service. The flag value is localaddr,remoteaddr. E.g.
+ -L :14141,www.google.com:80
+
+%s can't be used directly with tools like rsync because veyron addresses don't
+look like traditional hostnames, which rsync doesn't understand. For
+compatibility with such tools, %s has a special feature that allows passing the
+veyron address via the VSH_NAME environment variable.
+
+ $ VSH_NAME=<veyron address> rsync -avh -e %s /foo/* veyron:/foo/
+
+In this example, the "veyron" host will be substituted with $VSH_NAME by %s
+and rsync will work as expected.
+
+Full flags:
+`, os.Args[0], bname, bname, bname, bname, os.Args[0], bname)
+ flag.PrintDefaults()
+ }
+}
+
+func main() {
+ // Work around the fact that os.Exit doesn't run deferred functions.
+ os.Exit(realMain())
+}
+
+func realMain() int {
+ r := rt.Init()
+ defer r.Shutdown()
+
+ host, cmd, err := veyronNameAndCommandLine()
+ if err != nil {
+ flag.Usage()
+ fmt.Fprintf(os.Stderr, "\n%v\n", err)
+ return 1
+ }
+
+ t, err := tunnel.BindTunnel(host)
+ if err != nil {
+ vlog.Fatalf("BindTunnel(%q) failed: %v", host, err)
+ }
+
+ if len(*portforward) > 0 {
+ go runPortForwarding(t, host)
+ }
+
+ if *noshell {
+ <-signals.ShutdownOnSignals()
+ return 0
+ }
+
+ opts := shellOptions(cmd)
+
+ stream, err := t.Shell(cmd, opts, veyron2.CallTimeout(24*time.Hour))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ return 1
+ }
+ saved := lib.EnterRawTerminalMode()
+ defer lib.RestoreTerminalSettings(saved)
+ runIOManager(os.Stdin, os.Stdout, os.Stderr, stream)
+
+ exitMsg := fmt.Sprintf("Connection to %s closed.", host)
+ exitStatus, err := stream.Finish()
+ if err != nil {
+ exitMsg += fmt.Sprintf(" (%v)", err)
+ }
+ vlog.VI(1).Info(exitMsg)
+ // Only show the exit message on stdout for interactive shells.
+ // Otherwise, the exit message might get confused with the output
+ // of the command that was run.
+ if err != nil {
+ fmt.Fprintln(os.Stderr, exitMsg)
+ } else if len(cmd) == 0 {
+ fmt.Println(exitMsg)
+ }
+ return int(exitStatus)
+}
+
+func shellOptions(cmd string) (opts tunnel.ShellOpts) {
+ opts.UsePty = (len(cmd) == 0 || *forcePty) && !*disablePty
+ opts.Environment = environment()
+ ws, err := lib.GetWindowSize()
+ if err != nil {
+ vlog.VI(1).Infof("GetWindowSize failed: %v", err)
+ } else {
+ opts.Rows = uint32(ws.Row)
+ opts.Cols = uint32(ws.Col)
+ }
+ return
+}
+
+func environment() []string {
+ env := []string{}
+ for _, name := range []string{"TERM", "COLORTERM"} {
+ if value := os.Getenv(name); value != "" {
+ env = append(env, fmt.Sprintf("%s=%s", name, value))
+ }
+ }
+ return env
+}
+
+// veyronNameAndCommandLine extracts the veyron name and the remote command to
+// send to the server. The name can be specified with the --vname flag or as the
+// first non-flag argument. The command line is the concatenation of all the
+// non-flag arguments, minus the veyron name.
+func veyronNameAndCommandLine() (string, string, error) {
+ name := *vname
+ args := flag.Args()
+ if len(name) == 0 {
+ if len(args) > 0 {
+ name = args[0]
+ args = args[1:]
+ }
+ }
+ if len(name) == 0 {
+ return "", "", errors.New("veyron name missing")
+ }
+ // For compatibility with tools like rsync. Because veyron addresses
+ // don't look like traditional hostnames, tools that work with rsh and
+ // ssh can't work directly with vsh. This trick makes the following
+ // possible:
+ // $ VSH_NAME=<veyron address> rsync -avh -e vsh /foo/* veyron:/foo/
+ // The "veyron" host will be substituted with <veyron address>.
+ if envName := os.Getenv("VSH_NAME"); len(envName) > 0 && name == "veyron" {
+ name = envName
+ }
+ cmd := strings.Join(args, " ")
+ return name, cmd, nil
+}
+
+func runPortForwarding(t tunnel.Tunnel, host string) {
+ // *portforward is localaddr,remoteaddr
+ parts := strings.Split(*portforward, ",")
+ var laddr, raddr string
+ if len(parts) != 2 {
+ vlog.Fatalf("-L flag expects 2 values separated by a comma")
+ }
+ laddr = parts[0]
+ raddr = parts[1]
+
+ ln, err := net.Listen(*lprotocol, laddr)
+ if err != nil {
+ vlog.Fatalf("net.Listen(%q, %q) failed: %v", *lprotocol, laddr, err)
+ }
+ defer ln.Close()
+ vlog.VI(1).Infof("Listening on %q", ln.Addr())
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ vlog.Infof("Accept failed: %v", err)
+ continue
+ }
+ stream, err := t.Forward(*rprotocol, raddr, veyron2.CallTimeout(24*time.Hour))
+ if err != nil {
+ vlog.Infof("Tunnel(%q, %q) failed: %v", *rprotocol, raddr, err)
+ conn.Close()
+ continue
+ }
+ name := fmt.Sprintf("%v-->%v-->(%v)-->%v", conn.RemoteAddr(), conn.LocalAddr(), host, raddr)
+ go func() {
+ vlog.VI(1).Infof("TUNNEL START: %v", name)
+ errf := lib.Forward(conn, stream)
+ err := stream.Finish()
+ vlog.VI(1).Infof("TUNNEL END : %v (%v, %v)", name, errf, err)
+ }()
+ }
+}
diff --git a/examples/unresolve/test_util.go b/examples/unresolve/test_util.go
new file mode 100644
index 0000000..94895f2
--- /dev/null
+++ b/examples/unresolve/test_util.go
@@ -0,0 +1,221 @@
+package unresolve
+
+import (
+ "fmt"
+ "testing"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/rt"
+ mtidl "veyron2/services/mounttable"
+ "veyron2/vlog"
+
+ _ "veyron/lib/testutil"
+ "veyron/lib/testutil/blackbox"
+ mounttable "veyron/services/mounttable/lib"
+
+ fortuneidl "veyron/examples/fortune"
+)
+
+func initRT(opts ...veyron2.ROpt) func() {
+ return rt.Init(opts...).Shutdown
+}
+
+func newServer() ipc.Server {
+ server, err := rt.R().NewServer()
+ if err != nil {
+ panic(fmt.Sprintf("r.NewServer failed with %v", err))
+ }
+ return server
+}
+
+func createServer(server ipc.Server, prefix string, dispatcher ipc.Dispatcher) string {
+ if err := server.Register(prefix, dispatcher); err != nil {
+ panic(fmt.Sprintf("server.Register failed with %v", err))
+ }
+ ep, err := server.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ panic(fmt.Sprintf("server.Listen failed with %v", err))
+ }
+ return naming.JoinAddressName(ep.String(), prefix)
+}
+
+func serverMain(serviceCreator func(ipc.Server) string, args []string) {
+ defer initRT()()
+ server := newServer()
+ defer server.Stop()
+ service := serviceCreator(server)
+ vlog.Infof("created %v", service)
+ for _, arg := range args {
+ if err := server.Publish(arg); err != nil {
+ panic(fmt.Sprintf("server.Publish(%q) failed with %v", arg, err))
+ }
+ }
+ fmt.Println("ready")
+ blackbox.WaitForEOFOnStdin()
+}
+
+func createMT(server ipc.Server) string {
+ return createServer(server, "mt", mounttable.NewMountTable())
+}
+
+func childMT(args []string) {
+ serverMain(createMT, args)
+}
+
+func createMTClient(name string) mtidl.MountTable {
+ client, err := mtidl.BindMountTable(name)
+ if err != nil {
+ panic(fmt.Sprintf("BindMountTable failed with %v", err))
+ }
+ return client
+}
+
+const fixedFortuneMessage = "Sooner than you think, you will be deeply dissatisfied with a fortune."
+
+type fortune struct{}
+
+func (*fortune) Get(ipc.Context) (string, error) {
+ return fixedFortuneMessage, nil
+}
+
+func (*fortune) Add(ipc.Context, string) error {
+ return nil
+}
+
+func createFortune(server ipc.Server) string {
+ return createServer(server, "fortune", ipc.SoloDispatcher(fortuneidl.NewServerFortune(new(fortune)), nil))
+}
+
+func childFortune(args []string) {
+ serverMain(createFortune, args)
+}
+
+type fortuneCustomUnresolve struct {
+ custom string
+}
+
+func (*fortuneCustomUnresolve) Get(ipc.Context) (string, error) {
+ return fixedFortuneMessage, nil
+}
+
+func (*fortuneCustomUnresolve) Add(ipc.Context, string) error {
+ return nil
+}
+
+func (*fortuneCustomUnresolve) UnresolveStep(context ipc.Context) ([]string, error) {
+ servers, err := rt.R().MountTable().ResolveToMountTable("I/want/to/know")
+ if err != nil {
+ return nil, err
+ }
+ var reply []string
+ for _, s := range servers {
+ reply = append(reply, naming.Join(naming.MakeNotFixed(s), "the/future"))
+ }
+ return reply, nil
+}
+
+func createFortuneCustomUnresolve(server ipc.Server) string {
+ oa := createServer(server, "tell/me/the/future", ipc.SoloDispatcher(fortuneidl.NewServerFortune(new(fortuneCustomUnresolve)), nil))
+ ep, _ := naming.SplitAddressName(oa)
+ oa = naming.JoinAddressName(ep, "tell/me")
+ // Doesn't get unmounted. Fine for a test.
+ rt.R().MountTable().Mount("I/want/to/know", oa, 0)
+ return oa
+}
+
+func childFortuneCustomUnresolve(args []string) {
+ serverMain(createFortuneCustomUnresolve, args)
+}
+
+func createFortuneClient(rt veyron2.Runtime, name string) fortuneidl.Fortune {
+ client, err := fortuneidl.BindFortune(name, veyron2.RuntimeOpt{rt})
+ if err != nil {
+ panic(fmt.Sprintf("BindFortune failed with %v", err))
+ }
+ return client
+}
+
+type fortuneNoIDL struct{}
+
+func (*fortuneNoIDL) Get(ipc.ServerCall) (string, error) {
+ return fixedFortuneMessage, nil
+}
+
+func (*fortuneNoIDL) UnresolveStep(ipc.ServerCall) ([]string, error) {
+ servers, err := rt.R().MountTable().ResolveToMountTable("g")
+ if err != nil {
+ return nil, err
+ }
+ var reply []string
+ for _, s := range servers {
+ reply = append(reply, naming.Join(naming.MakeNotFixed(s), "fortune"))
+ }
+ return reply, nil
+}
+
+func createFortuneNoIDL(server ipc.Server) string {
+ return createServer(server, "fortune", ipc.SoloDispatcher(new(fortuneNoIDL), nil))
+}
+
+func childFortuneNoIDL(args []string) {
+ serverMain(createFortuneNoIDL, args)
+}
+
+func resolveStep(t *testing.T, name string) string {
+ client := createMTClient(name)
+ results, suffix, err := client.ResolveStep()
+ if err != nil {
+ t.Errorf("ResolveStep on %q failed with %v", name, err)
+ return ""
+ }
+ if len(results) != 1 {
+ t.Errorf("Expected one result when resolving %q, got %q", name, results)
+ return ""
+ }
+ return naming.Join(results[0].Server, suffix)
+}
+
+func resolve(t *testing.T, mt naming.MountTable, name string) string {
+ results, err := mt.Resolve(name)
+ if err != nil {
+ t.Errorf("Resolve failed with %v", err)
+ return ""
+ }
+ if len(results) != 1 {
+ t.Errorf("Expected one result when resolving %q, got %q", name, results)
+ return ""
+ }
+ return results[0]
+}
+
+type unresolver interface {
+ UnresolveStep(...ipc.ClientCallOpt) ([]string, error)
+}
+
+func unresolveStep(t *testing.T, c unresolver) string {
+ unres, err := c.UnresolveStep()
+ if err != nil {
+ t.Errorf("UnresolveStep failed with %v", err)
+ return ""
+ }
+ if len(unres) != 1 {
+ t.Errorf("c.UnresolveStep wanted 1 result, got: %q", unres)
+ return ""
+ }
+ return unres[0]
+}
+
+func unresolve(t *testing.T, mt naming.MountTable, name string) string {
+ results, err := mt.Unresolve(name)
+ if err != nil {
+ t.Errorf("Unresolve failed with %v", err)
+ return ""
+ }
+ if len(results) != 1 {
+ t.Errorf("Expected one result when unresolving %q, got %q", name, results)
+ return ""
+ }
+ return results[0]
+}
diff --git a/examples/unresolve/unresolve_test.go b/examples/unresolve/unresolve_test.go
new file mode 100644
index 0000000..eeafcb3
--- /dev/null
+++ b/examples/unresolve/unresolve_test.go
@@ -0,0 +1,300 @@
+package unresolve
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+ "testing"
+
+ "veyron/lib/testutil"
+ "veyron/lib/testutil/blackbox"
+
+ "veyron2"
+ "veyron2/naming"
+ "veyron2/rt"
+ "veyron2/vlog"
+)
+
+// The test sets up a topology of mounttables and servers (the latter configured
+// in various ways, with/without custom UnresolveStep methods, mounting
+// themselves with one or several names under a mounttable (directly or via
+// a name that resolves to a mounttable). The "ASCII" art below illustrates the
+// setup, with a description of each node in the topology following.
+//
+// (A)
+// |
+// |"b"
+// |
+// (B)
+// \_____________________________________________
+// | | | | | | \
+// |"c" |"d" | "I/want/to/know" |"e1" |"e2" |"f" |"g"
+// | | | | | | |
+// (C) (D) (D/tell/me") (E) (E) (F) (G)
+//
+// A mounttable service (A) with OA "aEP/mt" acting as a root mounttable.
+//
+// A mounttable service (B) with OA "bEP/mt", mounting its ep "bEP" as "b" under
+// mounttable A.
+//
+// A fortune service (C) with OA "eEP/fortune", mouting its ep "eEP" as "c"
+// under mounttable B. [ This is the vanilla case. ]
+//
+// A fortune service (D) with OA "dEP/tell/me/the/future", mounting its ep "dEP"
+// automatically as "d" under mounttable B, and also mounting the OA
+// "dEP/tell/me" manually as "I/want/to/know" under mounttable B. It implements
+// its own custom UnresolveStep. [ This demonstrates using a custom
+// UnresolveStep implementation with an IDL-based service. ]
+//
+// A fortune service (E) with OA "eEP/fortune", mounting its ep "eEP" as "e1"
+// and as "e2" under mounttable B. [ This shows a service published under more
+// than one name. ]
+//
+// A fortune service (F) with OA "fEP/fortune", with mounttable root "aOA/b/mt"
+// mounting its ep "fEP" as "f" under "aOA/b/mt" (essentially, under mounttable
+// B). [ This shows a service whose local root is a name that resolves to a
+// mounttable via another mounttable (so local root is not just an OA ].
+//
+// A fortune service (G) with OA "gEP/fortune" that is not based on an IDL. G
+// mounts itself as "g" under mounttable B, and defines its own UnresolveStep.
+// [ This demonstrates defining UnresolveStep for a service that is not
+// IDL-based. ]
+
+func TestHelperProcess(t *testing.T) {
+ blackbox.HelperProcess(t)
+}
+
+func init() {
+ blackbox.CommandTable["childMT"] = childMT
+ blackbox.CommandTable["childFortune"] = childFortune
+ blackbox.CommandTable["childFortuneCustomUnresolve"] = childFortuneCustomUnresolve
+ blackbox.CommandTable["childFortuneNoIDL"] = childFortuneNoIDL
+}
+
+// TODO(caprita): Consider making shutdown part of blackbox.Child.
+func shutdown(child *blackbox.Child) {
+ child.CloseStdin()
+ child.ExpectEOFAndWait()
+ child.Cleanup()
+}
+
+func TestUnresolve(t *testing.T) {
+ // Create the set of servers and ensure that the right mounttables act
+ // as namespace roots for each. We run all servers with an identity blessed
+ // by the root mounttable's identity under the name "test", i.e., if <rootMT>
+ // is the identity of the root mounttable then the servers run with the identity
+ // <rootMT>/test.
+ // TODO(ataly): Eventually we want to use the same identities the servers
+ // would have if they were running in production.
+ defer initRT()()
+ server := newServer()
+ defer server.Stop()
+
+ // Create mounttable A.
+ aOA := createMT(server)
+ if len(aOA) == 0 {
+ t.Fatalf("aOA is empty")
+ }
+ vlog.Infof("aOA=%v", aOA)
+ idA := rt.R().Identity()
+ vlog.Infof("idA=%v", idA)
+ // Create mounttable B.
+ b := blackbox.HelperCommand(t, "childMT", "b")
+ defer shutdown(b)
+
+ idFile := testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ b.Cmd.Env = append(b.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile), fmt.Sprintf("MOUNTTABLE_ROOT=%v", aOA))
+ b.Cmd.Start()
+ b.Expect("ready")
+ bOA := naming.Join(resolveStep(t, naming.MakeFixed(naming.Join(aOA, "b"))), "mt")
+ vlog.Infof("bOA=%v", bOA)
+
+ // Create server C.
+ c := blackbox.HelperCommand(t, "childFortune", "c")
+ defer shutdown(c)
+ idFile = testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ c.Cmd.Env = append(c.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile), fmt.Sprintf("MOUNTTABLE_ROOT=%v", bOA))
+ c.Cmd.Start()
+ c.Expect("ready")
+ cEP := resolveStep(t, naming.MakeFixed(naming.Join(bOA, "c")))
+ vlog.Infof("cEP=%v", cEP)
+
+ // Create server D.
+ d := blackbox.HelperCommand(t, "childFortuneCustomUnresolve", "d")
+ defer shutdown(d)
+ idFile = testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ d.Cmd.Env = append(d.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile), fmt.Sprintf("MOUNTTABLE_ROOT=%v", bOA))
+ d.Cmd.Start()
+ d.Expect("ready")
+ dEP := resolveStep(t, naming.MakeFixed(naming.Join(bOA, "d")))
+ vlog.Infof("dEP=%v", dEP)
+
+ // Create server E.
+ e := blackbox.HelperCommand(t, "childFortune", "e1", "e2")
+ defer shutdown(e)
+ idFile = testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ e.Cmd.Env = append(e.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile), fmt.Sprintf("MOUNTTABLE_ROOT=%v", bOA))
+ e.Cmd.Start()
+ e.Expect("ready")
+ eEP := resolveStep(t, naming.MakeFixed(naming.Join(bOA, "e1")))
+ vlog.Infof("eEP=%v", eEP)
+
+ // Create server F.
+ f := blackbox.HelperCommand(t, "childFortune", "f")
+ defer shutdown(f)
+ idFile = testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ f.Cmd.Env = append(f.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile), fmt.Sprintf("MOUNTTABLE_ROOT=%v", naming.Join(aOA, "b/mt")))
+ f.Cmd.Start()
+ f.Expect("ready")
+ fEP := resolveStep(t, naming.MakeFixed(naming.Join(bOA, "f")))
+ vlog.Infof("fEP=%v", fEP)
+
+ // Create server G.
+ g := blackbox.HelperCommand(t, "childFortuneNoIDL", "g")
+ defer shutdown(g)
+ idFile = testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ g.Cmd.Env = append(g.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile), fmt.Sprintf("MOUNTTABLE_ROOT=%v", bOA))
+ g.Cmd.Start()
+ g.Expect("ready")
+ gEP := resolveStep(t, naming.MakeFixed(naming.Join(bOA, "g")))
+ vlog.Infof("gEP=%v", gEP)
+
+ // Check that things resolve correctly.
+
+ // Create a client runtime with oOA as its root.
+ idFile = testutil.SaveIdentityToFile(testutil.NewBlessedIdentity(idA, "test"))
+ defer os.Remove(idFile)
+ os.Setenv("VEYRON_IDENTITY", idFile)
+ r, _ := rt.New(veyron2.MountTableRoots([]string{aOA}))
+ resolveCases := []struct {
+ name, resolved string
+ }{
+ {"b/mt/c", cEP},
+ {"b/mt/d", dEP},
+ {"b/mt/I/want/to/know", naming.JoinAddressNameFixed(dEP, "tell/me")},
+ {"b/mt/e1", eEP},
+ {"b/mt/e2", eEP},
+ {"b/mt/f", fEP},
+ {"b/mt/g", gEP},
+ }
+ for _, c := range resolveCases {
+ if want, got := c.resolved, resolve(t, r.MountTable(), c.name); want != got {
+ t.Errorf("resolve %q expected %q, got %q instead", c.name, want, got)
+ }
+ }
+
+ // Verify that we can talk to the servers we created, and that unresolve
+ // one step at a time works.
+
+ unresolveStepCases := []struct {
+ name, unresStep1, unresStep2 string
+ }{
+ {
+ "b/mt/c/fortune",
+ naming.Join(bOA, "c/fortune"),
+ naming.Join(aOA, "b/mt/c/fortune"),
+ },
+ {
+ "b/mt/d/tell/me/the/future",
+ naming.Join(bOA, "I/want/to/know/the/future"),
+ naming.Join(aOA, "b/mt/I/want/to/know/the/future"),
+ },
+ {
+ "b/mt/f/fortune",
+ naming.Join(bOA, "f/fortune"),
+ naming.Join(aOA, "b/mt/f/fortune"),
+ },
+ {
+ "b/mt/g/fortune",
+ naming.Join(bOA, "g/fortune"),
+ naming.Join(aOA, "b/mt/g/fortune"),
+ },
+ }
+ for _, c := range unresolveStepCases {
+ // Verify that we can talk to the server.
+ client := createFortuneClient(r, c.name)
+ if fortuneMessage, err := client.Get(); err != nil {
+ t.Errorf("fortune.Get failed with %v", err)
+ } else if fortuneMessage != fixedFortuneMessage {
+ t.Errorf("fortune expected %q, got %q instead", fixedFortuneMessage, fortuneMessage)
+ }
+
+ // Unresolve, one step.
+ if want, got := c.unresStep1, unresolveStep(t, client); want != got {
+ t.Errorf("fortune.UnresolveStep expected %q, got %q instead", want, got)
+ }
+
+ // Go up the tree, unresolve another step.
+ if want, got := c.unresStep2, unresolveStep(t, createMTClient(naming.MakeFixed(c.unresStep1))); want != got {
+ t.Errorf("mt.UnresolveStep expected %q, got %q instead", want, got)
+ }
+ }
+ // We handle (E) separately since its UnresolveStep returns two names
+ // instead of one.
+
+ // Verify that we can talk to server E.
+ eClient := createFortuneClient(r, "b/mt/e1/fortune")
+ if fortuneMessage, err := eClient.Get(); err != nil {
+ t.Errorf("fortune.Get failed with %v", err)
+ } else if fortuneMessage != fixedFortuneMessage {
+ t.Errorf("fortune expected %q, got %q instead", fixedFortuneMessage, fortuneMessage)
+ }
+
+ // Unresolve E, one step.
+ eUnres, err := eClient.UnresolveStep()
+ if err != nil {
+ t.Errorf("UnresolveStep failed with %v", err)
+ }
+ if want, got := []string{naming.Join(bOA, "e1/fortune"), naming.Join(bOA, "e2/fortune")}, eUnres; !reflect.DeepEqual(want, got) {
+ t.Errorf("e.UnresolveStep expected %q, got %q instead", want, got)
+ }
+
+ // Try unresolve step on a random name in B.
+ if want, got := naming.Join(aOA, "b/mt/some/random/name"),
+ unresolveStep(t, createMTClient(naming.MakeFixed(naming.Join(bOA, "some/random/name")))); want != got {
+ t.Errorf("b.UnresolveStep expected %q, got %q instead", want, got)
+ }
+
+ // Try unresolve step on a random name in A.
+ if unres, err := createMTClient(naming.MakeFixed(naming.Join(aOA, "another/random/name"))).UnresolveStep(); err != nil {
+ t.Errorf("UnresolveStep failed with %v", err)
+ } else if len(unres) > 0 {
+ t.Errorf("b.UnresolveStep expected no results, got %q instead", unres)
+ }
+
+ // Verify that full unresolve works.
+
+ unresolveCases := []struct {
+ name, want string
+ }{
+ {"b/mt/c/fortune", naming.Join(aOA, "b/mt/c/fortune")},
+ {naming.Join(bOA, "c/fortune"), naming.Join(aOA, "b/mt/c/fortune")},
+ {naming.Join(cEP, "fortune"), naming.Join(aOA, "b/mt/c/fortune")},
+
+ {"b/mt/d/tell/me/the/future", naming.Join(aOA, "b/mt/I/want/to/know/the/future")},
+ {naming.Join(bOA, "d/tell/me/the/future"), naming.Join(aOA, "b/mt/I/want/to/know/the/future")},
+ {"b/mt/I/want/to/know/the/future", naming.Join(aOA, "b/mt/I/want/to/know/the/future")},
+ {naming.Join(bOA, "I/want/to/know/the/future"), naming.Join(aOA, "b/mt/I/want/to/know/the/future")},
+ {naming.Join(dEP, "tell/me/the/future"), naming.Join(aOA, "b/mt/I/want/to/know/the/future")},
+
+ {"b/mt/e1/fortune", naming.Join(aOA, "b/mt/e1/fortune")},
+ {"b/mt/e2/fortune", naming.Join(aOA, "b/mt/e1/fortune")},
+
+ {"b/mt/f/fortune", naming.Join(aOA, "b/mt/f/fortune")},
+ {naming.Join(fEP, "fortune"), naming.Join(aOA, "b/mt/f/fortune")},
+ {"b/mt/g/fortune", naming.Join(aOA, "b/mt/g/fortune")},
+ {naming.Join(bOA, "g/fortune"), naming.Join(aOA, "b/mt/g/fortune")},
+ {naming.Join(gEP, "fortune"), naming.Join(aOA, "b/mt/g/fortune")},
+ }
+ for _, c := range unresolveCases {
+ if want, got := c.want, unresolve(t, r.MountTable(), c.name); want != got {
+ t.Errorf("unresolve %q expected %q, got %q instead", c.name, want, got)
+ }
+ }
+}
diff --git a/examples/veyron_browser/.gitignore b/examples/veyron_browser/.gitignore
new file mode 100644
index 0000000..ba46267
--- /dev/null
+++ b/examples/veyron_browser/.gitignore
@@ -0,0 +1 @@
+browser/lib
diff --git a/examples/veyron_browser/Makefile b/examples/veyron_browser/Makefile
new file mode 100644
index 0000000..4c11345
--- /dev/null
+++ b/examples/veyron_browser/Makefile
@@ -0,0 +1,39 @@
+#
+# TODO(aghassemi) These commands are just for convenience.
+# Remove this file and references to it from README before release.
+#
+
+PATH:=$(PATH):$(VEYRON_ENV)/cout/node/bin
+VEYRON_WSPR= ../../../../bin/wsprd
+VEYRON_PROXY= ../../../../bin/proxy
+VEYRON_IDENTITY= ../../../../bin/identityd
+VEYRON_PROXY_PORT= 5164
+VEYRON_PROXY_ADDR= 127.0.0.1:$(VEYRON_PROXY_PORT)
+VEYRON_WSPR_PORT= 5165
+VEYRON_IDENTITY_PORT= 5163
+VEYRON_JS_API= ../../../../../javascript/api
+
+# Builds everything
+default: build
+build: buildbrowser
+
+# Builds browser version of FortuneJS
+buildbrowser:
+ (cd $(VEYRON_JS_API) && ./vgrunt build) && \
+ mkdir -p ./browser/lib && \
+ cp -rf $(VEYRON_JS_API)/dist/*.* ./browser/lib
+
+# Deploys Veyron daemons
+daemons:
+ @if [[ ! -e $(VEYRON_PROXY) ]]; then \
+ echo "Veyron proxy could not be found in $(VEYRON_PROXY). Please build and install veyron2 and services first"; \
+ exit 1; \
+ fi
+ $(VEYRON_IDENTITY) --port=$(VEYRON_IDENTITY_PORT) | \
+ $(VEYRON_PROXY) -addr=$(VEYRON_PROXY_ADDR) | \
+ $(VEYRON_WSPR) --v=3 -logtostderr=true -vproxy=$(VEYRON_PROXY_ADDR) --port $(VEYRON_WSPR_PORT) &
+
+killdaemons:
+ kill `lsof -t -i:$(VEYRON_IDENTITY_PORT)` ; \
+ kill `lsof -t -i:$(VEYRON_WSPR_PORT)` ; \
+ kill `lsof -t -i:$(VEYRON_PROXY_PORT)`
diff --git a/examples/veyron_browser/README b/examples/veyron_browser/README
new file mode 100644
index 0000000..96bd7ae
--- /dev/null
+++ b/examples/veyron_browser/README
@@ -0,0 +1,27 @@
+# Mounttable Browser
+Mountable browser is a browser application that lets the user view and traverse mounttables.
+
+## Building
+Before you can run Mounttable Browser, you need to build. Simply run:
+
+```sh
+make
+```
+or
+
+```sh
+make build
+```
+
+## Running
+
+### Daemons
+Before you can run mounttable browser, a number of servers must be started.
+
+To run these daemons, simply run:
+
+```sh
+make daemons
+```
+
+after the daemons are started, simply open browser/client/client.html in a browser.
\ No newline at end of file
diff --git a/examples/veyron_browser/browser/client/client.html b/examples/veyron_browser/browser/client/client.html
new file mode 100644
index 0000000..9060a48
--- /dev/null
+++ b/examples/veyron_browser/browser/client/client.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Mounttable browser</title>
+ <style>
+ a.remote {
+ color: #ff0000;
+ }
+ a.local {
+ color: #0000ff;
+ }
+ a.service{
+ color: #000000;
+ }
+ a.error{
+ color: #ffaa00;
+ }
+ </style>
+</head>
+<body>
+ <div>
+ Enter mounttable: <input id="mt" type="text"></input><input type="submit" value="connect" onclick="init()"></input>
+ </div>
+ <div>
+ Enter query: <input id="query" type="text" onkeyup="handleChange()" value="*"></input>
+ </div>
+ <div id="results"></div>
+ <script src="../lib/veyron.js"></script>
+ <script src="../config.js"></script>
+ <script src="promises.js"></script>
+ <script src="mounttable.js"></script>
+ <script src="client.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/veyron_browser/browser/client/client.js b/examples/veyron_browser/browser/client/client.js
new file mode 100644
index 0000000..ff46225
--- /dev/null
+++ b/examples/veyron_browser/browser/client/client.js
@@ -0,0 +1,81 @@
+var veyron = new Veyron(veyronConfig);
+var client = veyron.newClient();
+var mp;
+
+//TODO(bprosnitz) Remove setup mount. This is used to populate the mount table.
+var setupMounts = function() {
+ return Promise.all([
+ mp.appendToPath('other').mount('/@2@tcp@[::]:111@e73f89469f6ec8252f9e0c7bbe5dd516@1@1@@/FAKE/ADDRESS'),
+ mp.appendToPath('recurse').mount(mp.addr),
+ mp.appendToPath('a/b/recurse').mount(mp.addr)]);
+}
+
+var promiseChain = null;
+var init = function() {
+ var mtAddr = document.getElementById('mt').value;
+ mp = new MountPoint(client, mtAddr);
+ promiseChain = setupMounts();
+ handleChange();
+}
+
+var updateResults = function(query) {
+ var results = document.getElementById('results');
+ results.innerHTML = '<i>Loading results...</i>';
+
+ promiseChain.then(function() {
+ // perform query.
+ mp.glob(query).then(function(items) {
+ // generate the results list.
+ var list = document.createElement('ul');
+ for (var i in items) {
+ var item = items[i];
+ var li = document.createElement('li');
+ var a = document.createElement('a');
+
+ a.innerHTML = item.name + ' - ' + JSON.stringify(item);
+ (function(item, a) {
+ isMounttable(client, item).then(function(isMt) {
+ if (!isMt) {
+ a.className = "service";
+ } else {
+ a.href = '#';
+ if (item.servers.length > 0) {
+ // switch mounttable address if this is a remote server.
+ a.className = 'remote';
+ a.onclick = function() {
+ mt = item.servers[0].server;
+ document.getElementById('mt').value = mt;
+ document.getElementById('query').value = '*';
+ handleChange();
+ };
+ } else {
+ // if this is in the current mounttable, modify the query.
+ a.className = 'local';
+ a.onclick = function() {
+ document.getElementById('query').value = item.name + '/*';
+ handleChange();
+ };
+ }
+ }
+ }, function(reason) {
+ console.error('Error testing mounttable: ', reason);
+ a.className = "error";
+ });
+ })(item, a);
+
+ li.appendChild(a);
+ list.appendChild(li);
+ }
+ results.innerHTML = '';
+ results.appendChild(list);
+ }).catch(function(msg) {
+ console.error('Problem loading page: ' + msg);
+ });
+ }).catch(function(err) {
+ console.error(err);
+ });
+}
+
+var handleChange = function() {
+ updateResults(document.getElementById('query').value);
+}
\ No newline at end of file
diff --git a/examples/veyron_browser/browser/client/mounttable.js b/examples/veyron_browser/browser/client/mounttable.js
new file mode 100644
index 0000000..ad3bdbd
--- /dev/null
+++ b/examples/veyron_browser/browser/client/mounttable.js
@@ -0,0 +1,99 @@
+(function(exports) {
+/**
+ * MountPoint handles manipulating and querying from
+ * a mounttable.
+ * @param {object} client A veyron client.
+ * @param {...string} addressParts Parts of the address to join
+ * @constructor
+ */
+var MountPoint = function(client, addressParts) {
+ this.client = client
+ this.addr = Array.prototype.slice.call(arguments, 1).join('/');
+}
+
+/**
+ * appendToPath appends to the mountpoint path
+ * @param {...string} toAdd strings to add to the path.
+ * @return {MountPoint} a new mount point with the path args appended
+ * to the current path.
+ */
+MountPoint.prototype.appendToPath = function(toAdd) {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(this.addr);
+ return new MountPoint(this.client, args.join('/'));
+}
+
+/**
+ * mount mounts a target to the current mount point.
+ * @param {string} target The target to be mounted.
+ * @return {promise} a promise that completes when it is mounted
+ */
+MountPoint.prototype.mount = function(target) {
+ return this.client.bind(this.addr).then(function(mtService) {
+ return mtService.mount(target, 0);
+ });
+}
+
+/**
+ * glob makes a glob request to a server relative to the current mountpoint.
+ * @param {string} expr The glob expression e.g. A/B/*.
+ * @return {promise} a promise to a list of results
+ */
+MountPoint.prototype.glob = function(expr) {
+ var results = [];
+ return this.client.bind(this.addr).then(function(mtService) {
+ var promise = mtService.glob(expr);
+ var stream = promise.stream;
+
+ stream.on('readable', function() {
+ var val = stream.read();
+ if (val) {
+ results.push(val);
+ }
+ });
+
+ return promise.then(function() {
+ return results;
+ });
+ });
+};
+
+/**
+ * isMounttable determines if a specific address refers to a
+ * mounttable.
+ * @param {object} client the veyron client to use.
+ * @param {string} globResult result of glob to check.
+ * @return {promise} promise to a boolean indicating if it is
+ * a mounttable.
+ */
+ // TODO(bprosnitz) Remove dependency on _proxyConnection.
+ // TODO(bprosnitz) Consider adding interface name to signature and using that.
+var isMounttable = function(client, globResult) {
+ if (globResult.servers.length === 0) {
+ // This is on the same mounttable as the globResult.
+ return new Promise(function(resolve) {
+ resolve(true);
+ });
+ }
+
+ var pconn = client._proxyConnection;
+ var promises = [];
+ for (var i in globResult.servers) {
+ promises.push((function(server) {
+ return pconn.getServiceSignature(server)
+ })(globResult.servers[i].server).then(function(sig) {
+ if (sig['glob'] !== undefined) {
+ if (sig['glob'].inArgs.length == 1) {
+ return true;
+ }
+ }
+ return false;
+ }));
+ }
+
+ return resolveRace(promises);
+}
+
+exports.MountPoint = MountPoint;
+exports.isMounttable = isMounttable;
+})(window);
\ No newline at end of file
diff --git a/examples/veyron_browser/browser/client/promises.js b/examples/veyron_browser/browser/client/promises.js
new file mode 100644
index 0000000..3b604ae
--- /dev/null
+++ b/examples/veyron_browser/browser/client/promises.js
@@ -0,0 +1,28 @@
+(function(exports) {
+/**
+ * resolveRace returns a promise that resolves when the first promise
+ * resolves and rejects only after every promise has rejected.
+ * @param {promise[]} promises a list of promises.
+ * @return {promse} a promise that resolves when any of the inputs resolve, or
+ * when all of the inputs reject.
+ */
+function resolveRace(promises) {
+ var resolve, reject;
+ var promise = new Promise(function(pResolve, pReject) {
+ resolve = pResolve;
+ reject = pReject;
+ });
+ var numRejects = 0;
+ for (var i in promises) {
+ promises[i].then(resolve, function(reason) {
+ numRejects++;
+ if (numRejects == promises.length) {
+ reject(reason);
+ }
+ });
+ }
+ return promise;
+};
+
+exports.resolveRace = resolveRace;
+})(window);
\ No newline at end of file
diff --git a/examples/veyron_browser/browser/config.js b/examples/veyron_browser/browser/config.js
new file mode 100644
index 0000000..6147fda
--- /dev/null
+++ b/examples/veyron_browser/browser/config.js
@@ -0,0 +1,19 @@
+/*
+ * Optional configuration to be used to create the Veyron object.
+ * It specifies location of services that Veyron depends on such as identity
+ * server and proxy daemons.
+ *
+ * If not specified, public Google-hosted daemons will be used.
+ * TODO(aghassemi) Use Vonery and remove this before release
+ */
+var veyronConfig = {
+ // Log severity, INFO means log anything equal or more severe than INFO
+ // One of NOLOG, ERROR, WARNING, DEBUG, INFO
+ 'logLevel': Veyron.logLevels.INFO,
+
+ // Server to use for identity
+ 'identityServer': 'http://localhost:5163/random/',
+
+ // Daemon that handles JavaScript communication with the rest of Veyron
+ 'proxy': 'http://localhost:5165'
+};
\ No newline at end of file
diff --git a/examples/wspr_sample/cache.idl b/examples/wspr_sample/cache.idl
new file mode 100644
index 0000000..e7279ef
--- /dev/null
+++ b/examples/wspr_sample/cache.idl
@@ -0,0 +1,59 @@
+package sample
+
+// A Cache service mimics the memcache interface.
+type Cache interface {
+ // Set sets a value for a key.
+ Set(key string, value anydata) error
+
+ // Get returns the value for a key. If the value is not found, returns
+ // a not found error.
+ Get(key string) (anydata, error)
+
+ // Same as Get, but casts the return argument to an byte.
+ GetAsByte(key string) (byte, error)
+ // Same as Get, but casts the return argument to an int32.
+ GetAsInt32(key string) (int32, error)
+ // Same as Get, but casts the return argument to an int64.
+ GetAsInt64(key string) (int64, error)
+ // Same as Get, but casts the return argument to an uint32.
+ GetAsUint32(key string) (uint32, error)
+ // Same as Get, but casts the return argument to an uint64.
+ GetAsUint64(key string) (uint64, error)
+ // Same as Get, but casts the return argument to an float32.
+ GetAsFloat32(key string) (float32, error)
+ // Same as Get, but casts the return argument to an float64.
+ GetAsFloat64(key string) (float64, error)
+ // Same as Get, but casts the return argument to a string.
+ GetAsString(key string) (string, error)
+ // Same as Get, but casts the return argument to a bool.
+ GetAsBool(key string) (bool, error)
+ // Same as Get, but casts the return argument to an error.
+ GetAsError(key string) (error, error)
+
+ // AsMap returns the full contents of the cache as a map.
+ AsMap() (map[string]anydata, error)
+
+ // KeyValuePairs returns the full contents of the cache as a slice of pairs.
+ KeyValuePairs() ([]KeyValuePair, error)
+
+ // MostRecentSet returns the key and value and the timestamp for the most
+ // recent set operation
+ // TODO(bprosnitz) support type types and change time to native time type
+ MostRecentSet() (value KeyValuePair, time int64, err error)
+
+ // KeyPage indexes into the keys (in alphanumerically sorted order) and
+ // returns the indexth page of 10 keys.
+ KeyPage(index int64) ([10]string, error)
+
+ // Size returns the total number of entries in the cache.
+ Size() (int64, error)
+
+ // MultiGet sets up a stream that allows fetching multiple keys.
+ MultiGet() stream<string, anydata> error
+}
+
+// KeyValuePair is a representation of a cached key and value pair.
+type KeyValuePair struct {
+ Key string
+ Value anydata
+}
diff --git a/examples/wspr_sample/cache.idl.go b/examples/wspr_sample/cache.idl.go
new file mode 100644
index 0000000..c95b559
--- /dev/null
+++ b/examples/wspr_sample/cache.idl.go
@@ -0,0 +1,822 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: cache.idl
+
+package sample
+
+import (
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_idl "veyron2/idl"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt/r"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// KeyValuePair is a representation of a cached key and value pair.
+type KeyValuePair struct {
+ Key string
+ Value _gen_idl.AnyData
+}
+
+// A Cache service mimics the memcache interface.
+// Cache is the interface the client binds and uses.
+// Cache_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type Cache_InternalNoTagGetter interface {
+
+ // Set sets a value for a key.
+ Set(key string, value _gen_idl.AnyData, opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Get returns the value for a key. If the value is not found, returns
+ // a not found error.
+ Get(key string, opts ..._gen_ipc.ClientCallOpt) (reply _gen_idl.AnyData, err error)
+
+ // Same as Get, but casts the return argument to an byte.
+ GetAsByte(key string, opts ..._gen_ipc.ClientCallOpt) (reply byte, err error)
+
+ // Same as Get, but casts the return argument to an int32.
+ GetAsInt32(key string, opts ..._gen_ipc.ClientCallOpt) (reply int32, err error)
+
+ // Same as Get, but casts the return argument to an int64.
+ GetAsInt64(key string, opts ..._gen_ipc.ClientCallOpt) (reply int64, err error)
+
+ // Same as Get, but casts the return argument to an uint32.
+ GetAsUint32(key string, opts ..._gen_ipc.ClientCallOpt) (reply uint32, err error)
+
+ // Same as Get, but casts the return argument to an uint64.
+ GetAsUint64(key string, opts ..._gen_ipc.ClientCallOpt) (reply uint64, err error)
+
+ // Same as Get, but casts the return argument to an float32.
+ GetAsFloat32(key string, opts ..._gen_ipc.ClientCallOpt) (reply float32, err error)
+
+ // Same as Get, but casts the return argument to an float64.
+ GetAsFloat64(key string, opts ..._gen_ipc.ClientCallOpt) (reply float64, err error)
+
+ // Same as Get, but casts the return argument to a string.
+ GetAsString(key string, opts ..._gen_ipc.ClientCallOpt) (reply string, err error)
+
+ // Same as Get, but casts the return argument to a bool.
+ GetAsBool(key string, opts ..._gen_ipc.ClientCallOpt) (reply bool, err error)
+
+ // Same as Get, but casts the return argument to an error.
+ GetAsError(key string, opts ..._gen_ipc.ClientCallOpt) (reply error, err error)
+
+ // AsMap returns the full contents of the cache as a map.
+ AsMap(opts ..._gen_ipc.ClientCallOpt) (reply map[string]_gen_idl.AnyData, err error)
+
+ // KeyValuePairs returns the full contents of the cache as a slice of pairs.
+ KeyValuePairs(opts ..._gen_ipc.ClientCallOpt) (reply []KeyValuePair, err error)
+
+ // MostRecentSet returns the key and value and the timestamp for the most
+ // recent set operation
+ // TODO(bprosnitz) support type types and change time to native time type
+ MostRecentSet(opts ..._gen_ipc.ClientCallOpt) (value KeyValuePair, time int64, err error)
+
+ // KeyPage indexes into the keys (in alphanumerically sorted order) and
+ // returns the indexth page of 10 keys.
+ KeyPage(index int64, opts ..._gen_ipc.ClientCallOpt) (reply [10]string, err error)
+
+ // Size returns the total number of entries in the cache.
+ Size(opts ..._gen_ipc.ClientCallOpt) (reply int64, err error)
+
+ // MultiGet sets up a stream that allows fetching multiple keys.
+ MultiGet(opts ..._gen_ipc.ClientCallOpt) (reply CacheMultiGetStream, err error)
+}
+type Cache interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ Cache_InternalNoTagGetter
+}
+
+// CacheService is the interface the server implements.
+type CacheService interface {
+
+ // Set sets a value for a key.
+ Set(context _gen_ipc.Context, key string, value _gen_idl.AnyData) (err error)
+
+ // Get returns the value for a key. If the value is not found, returns
+ // a not found error.
+ Get(context _gen_ipc.Context, key string) (reply _gen_idl.AnyData, err error)
+
+ // Same as Get, but casts the return argument to an byte.
+ GetAsByte(context _gen_ipc.Context, key string) (reply byte, err error)
+
+ // Same as Get, but casts the return argument to an int32.
+ GetAsInt32(context _gen_ipc.Context, key string) (reply int32, err error)
+
+ // Same as Get, but casts the return argument to an int64.
+ GetAsInt64(context _gen_ipc.Context, key string) (reply int64, err error)
+
+ // Same as Get, but casts the return argument to an uint32.
+ GetAsUint32(context _gen_ipc.Context, key string) (reply uint32, err error)
+
+ // Same as Get, but casts the return argument to an uint64.
+ GetAsUint64(context _gen_ipc.Context, key string) (reply uint64, err error)
+
+ // Same as Get, but casts the return argument to an float32.
+ GetAsFloat32(context _gen_ipc.Context, key string) (reply float32, err error)
+
+ // Same as Get, but casts the return argument to an float64.
+ GetAsFloat64(context _gen_ipc.Context, key string) (reply float64, err error)
+
+ // Same as Get, but casts the return argument to a string.
+ GetAsString(context _gen_ipc.Context, key string) (reply string, err error)
+
+ // Same as Get, but casts the return argument to a bool.
+ GetAsBool(context _gen_ipc.Context, key string) (reply bool, err error)
+
+ // Same as Get, but casts the return argument to an error.
+ GetAsError(context _gen_ipc.Context, key string) (reply error, err error)
+
+ // AsMap returns the full contents of the cache as a map.
+ AsMap(context _gen_ipc.Context) (reply map[string]_gen_idl.AnyData, err error)
+
+ // KeyValuePairs returns the full contents of the cache as a slice of pairs.
+ KeyValuePairs(context _gen_ipc.Context) (reply []KeyValuePair, err error)
+
+ // MostRecentSet returns the key and value and the timestamp for the most
+ // recent set operation
+ // TODO(bprosnitz) support type types and change time to native time type
+ MostRecentSet(context _gen_ipc.Context) (value KeyValuePair, time int64, err error)
+
+ // KeyPage indexes into the keys (in alphanumerically sorted order) and
+ // returns the indexth page of 10 keys.
+ KeyPage(context _gen_ipc.Context, index int64) (reply [10]string, err error)
+
+ // Size returns the total number of entries in the cache.
+ Size(context _gen_ipc.Context) (reply int64, err error)
+
+ // MultiGet sets up a stream that allows fetching multiple keys.
+ MultiGet(context _gen_ipc.Context, stream CacheServiceMultiGetStream) (err error)
+}
+
+// CacheMultiGetStream is the interface for streaming responses of the method
+// MultiGet in the service interface Cache.
+type CacheMultiGetStream interface {
+
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item string) error
+
+ // CloseSend indicates to the server that no more items will be sent; server
+ // Recv calls will receive io.EOF after all sent items. Subsequent calls to
+ // Send on the client will fail. This is an optional call - it's used by
+ // streaming clients that need the server to receive the io.EOF terminator.
+ CloseSend() error
+
+ // Recv returns the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item _gen_idl.AnyData, err error)
+
+ // Finish closes the stream and returns the positional return values for
+ // call.
+ Finish() (err error)
+
+ // Cancel cancels the RPC, notifying the server to stop processing.
+ Cancel()
+}
+
+// Implementation of the CacheMultiGetStream interface that is not exported.
+type implCacheMultiGetStream struct {
+ clientCall _gen_ipc.ClientCall
+}
+
+func (c *implCacheMultiGetStream) Send(item string) error {
+ return c.clientCall.Send(item)
+}
+
+func (c *implCacheMultiGetStream) CloseSend() error {
+ return c.clientCall.CloseSend()
+}
+
+func (c *implCacheMultiGetStream) Recv() (item _gen_idl.AnyData, err error) {
+ err = c.clientCall.Recv(&item)
+ return
+}
+
+func (c *implCacheMultiGetStream) Finish() (err error) {
+ if ierr := c.clientCall.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *implCacheMultiGetStream) Cancel() {
+ c.clientCall.Cancel()
+}
+
+// CacheServiceMultiGetStream is the interface for streaming responses of the method
+// MultiGet in the service interface Cache.
+type CacheServiceMultiGetStream interface {
+ // Send places the item onto the output stream, blocking if there is no buffer
+ // space available.
+ Send(item _gen_idl.AnyData) error
+
+ // Recv fills itemptr with the next item in the input stream, blocking until
+ // an item is available. Returns io.EOF to indicate graceful end of input.
+ Recv() (item string, err error)
+}
+
+// Implementation of the CacheServiceMultiGetStream interface that is not exported.
+type implCacheServiceMultiGetStream struct {
+ serverCall _gen_ipc.ServerCall
+}
+
+func (s *implCacheServiceMultiGetStream) Send(item _gen_idl.AnyData) error {
+ return s.serverCall.Send(item)
+}
+
+func (s *implCacheServiceMultiGetStream) Recv() (item string, err error) {
+ err = s.serverCall.Recv(&item)
+ return
+}
+
+// BindCache returns the client stub implementing the Cache
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindCache(name string, opts ..._gen_ipc.BindOpt) (Cache, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubCache{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerCache creates a new server stub.
+//
+// It takes a regular server implementing the CacheService
+// interface, and returns a new server stub.
+func NewServerCache(server CacheService) interface{} {
+ return &ServerStubCache{
+ service: server,
+ }
+}
+
+// clientStubCache implements Cache.
+type clientStubCache struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubCache) GetMethodTags(method string) []interface{} {
+ return GetCacheMethodTags(method)
+}
+
+func (__gen_c *clientStubCache) Set(key string, value _gen_idl.AnyData, opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Set", []interface{}{key, value}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) Get(key string, opts ..._gen_ipc.ClientCallOpt) (reply _gen_idl.AnyData, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Get", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsByte(key string, opts ..._gen_ipc.ClientCallOpt) (reply byte, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsByte", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsInt32(key string, opts ..._gen_ipc.ClientCallOpt) (reply int32, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsInt32", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsInt64(key string, opts ..._gen_ipc.ClientCallOpt) (reply int64, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsInt64", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsUint32(key string, opts ..._gen_ipc.ClientCallOpt) (reply uint32, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsUint32", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsUint64(key string, opts ..._gen_ipc.ClientCallOpt) (reply uint64, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsUint64", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsFloat32(key string, opts ..._gen_ipc.ClientCallOpt) (reply float32, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsFloat32", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsFloat64(key string, opts ..._gen_ipc.ClientCallOpt) (reply float64, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsFloat64", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsString(key string, opts ..._gen_ipc.ClientCallOpt) (reply string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsString", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsBool(key string, opts ..._gen_ipc.ClientCallOpt) (reply bool, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsBool", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) GetAsError(key string, opts ..._gen_ipc.ClientCallOpt) (reply error, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "GetAsError", []interface{}{key}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) AsMap(opts ..._gen_ipc.ClientCallOpt) (reply map[string]_gen_idl.AnyData, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "AsMap", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) KeyValuePairs(opts ..._gen_ipc.ClientCallOpt) (reply []KeyValuePair, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "KeyValuePairs", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) MostRecentSet(opts ..._gen_ipc.ClientCallOpt) (value KeyValuePair, time int64, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "MostRecentSet", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&value, &time, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) KeyPage(index int64, opts ..._gen_ipc.ClientCallOpt) (reply [10]string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "KeyPage", []interface{}{index}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) Size(opts ..._gen_ipc.ClientCallOpt) (reply int64, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "Size", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubCache) MultiGet(opts ..._gen_ipc.ClientCallOpt) (reply CacheMultiGetStream, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "MultiGet", nil, opts...); err != nil {
+ return
+ }
+ reply = &implCacheMultiGetStream{clientCall: call}
+ return
+}
+
+func (c *clientStubCache) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubCache wraps a server that implements
+// CacheService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubCache struct {
+ service CacheService
+}
+
+func (s *ServerStubCache) GetMethodTags(method string) []interface{} {
+ return GetCacheMethodTags(method)
+}
+
+func (s *ServerStubCache) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["AsMap"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 68},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["Get"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsBool"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 2},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsByte"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 67},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsError"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 66},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsFloat32"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 25},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsFloat64"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 26},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsInt32"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 36},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsInt64"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 37},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsString"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 3},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsUint32"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 52},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["GetAsUint64"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 53},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["KeyPage"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "index", Type: 37},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 71},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["KeyValuePairs"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 70},
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["MostRecentSet"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "value", Type: 69},
+ {Name: "time", Type: 37},
+ {Name: "err", Type: 66},
+ },
+ }
+ result.Methods["MultiGet"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 66},
+ },
+ InStream: 3,
+ OutStream: 65,
+ }
+ result.Methods["Set"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "key", Type: 3},
+ {Name: "value", Type: 65},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 66},
+ },
+ }
+ result.Methods["Size"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 37},
+ {Name: "", Type: 66},
+ },
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "anydata", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x32, Name: "byte", Tags: []string(nil)}, _gen_wiretype.MapType{Key: 0x3, Elem: 0x41, Name: "", Tags: []string(nil)}, _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x3, Name: "Key"},
+ _gen_wiretype.FieldType{Type: 0x41, Name: "Value"},
+ },
+ "KeyValuePair", []string(nil)},
+ _gen_wiretype.SliceType{Elem: 0x45, Name: "", Tags: []string(nil)}, _gen_wiretype.ArrayType{Elem: 0x3, Len: 0xa, Name: "", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (s *ServerStubCache) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubCache) Set(call _gen_ipc.ServerCall, key string, value _gen_idl.AnyData) (err error) {
+ err = __gen_s.service.Set(call, key, value)
+ return
+}
+
+func (__gen_s *ServerStubCache) Get(call _gen_ipc.ServerCall, key string) (reply _gen_idl.AnyData, err error) {
+ reply, err = __gen_s.service.Get(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsByte(call _gen_ipc.ServerCall, key string) (reply byte, err error) {
+ reply, err = __gen_s.service.GetAsByte(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsInt32(call _gen_ipc.ServerCall, key string) (reply int32, err error) {
+ reply, err = __gen_s.service.GetAsInt32(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsInt64(call _gen_ipc.ServerCall, key string) (reply int64, err error) {
+ reply, err = __gen_s.service.GetAsInt64(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsUint32(call _gen_ipc.ServerCall, key string) (reply uint32, err error) {
+ reply, err = __gen_s.service.GetAsUint32(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsUint64(call _gen_ipc.ServerCall, key string) (reply uint64, err error) {
+ reply, err = __gen_s.service.GetAsUint64(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsFloat32(call _gen_ipc.ServerCall, key string) (reply float32, err error) {
+ reply, err = __gen_s.service.GetAsFloat32(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsFloat64(call _gen_ipc.ServerCall, key string) (reply float64, err error) {
+ reply, err = __gen_s.service.GetAsFloat64(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsString(call _gen_ipc.ServerCall, key string) (reply string, err error) {
+ reply, err = __gen_s.service.GetAsString(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsBool(call _gen_ipc.ServerCall, key string) (reply bool, err error) {
+ reply, err = __gen_s.service.GetAsBool(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) GetAsError(call _gen_ipc.ServerCall, key string) (reply error, err error) {
+ reply, err = __gen_s.service.GetAsError(call, key)
+ return
+}
+
+func (__gen_s *ServerStubCache) AsMap(call _gen_ipc.ServerCall) (reply map[string]_gen_idl.AnyData, err error) {
+ reply, err = __gen_s.service.AsMap(call)
+ return
+}
+
+func (__gen_s *ServerStubCache) KeyValuePairs(call _gen_ipc.ServerCall) (reply []KeyValuePair, err error) {
+ reply, err = __gen_s.service.KeyValuePairs(call)
+ return
+}
+
+func (__gen_s *ServerStubCache) MostRecentSet(call _gen_ipc.ServerCall) (value KeyValuePair, time int64, err error) {
+ value, time, err = __gen_s.service.MostRecentSet(call)
+ return
+}
+
+func (__gen_s *ServerStubCache) KeyPage(call _gen_ipc.ServerCall, index int64) (reply [10]string, err error) {
+ reply, err = __gen_s.service.KeyPage(call, index)
+ return
+}
+
+func (__gen_s *ServerStubCache) Size(call _gen_ipc.ServerCall) (reply int64, err error) {
+ reply, err = __gen_s.service.Size(call)
+ return
+}
+
+func (__gen_s *ServerStubCache) MultiGet(call _gen_ipc.ServerCall) (err error) {
+ stream := &implCacheServiceMultiGetStream{serverCall: call}
+ err = __gen_s.service.MultiGet(call, stream)
+ return
+}
+
+func GetCacheMethodTags(method string) []interface{} {
+ switch method {
+ case "Set":
+ return []interface{}{}
+ case "Get":
+ return []interface{}{}
+ case "GetAsByte":
+ return []interface{}{}
+ case "GetAsInt32":
+ return []interface{}{}
+ case "GetAsInt64":
+ return []interface{}{}
+ case "GetAsUint32":
+ return []interface{}{}
+ case "GetAsUint64":
+ return []interface{}{}
+ case "GetAsFloat32":
+ return []interface{}{}
+ case "GetAsFloat64":
+ return []interface{}{}
+ case "GetAsString":
+ return []interface{}{}
+ case "GetAsBool":
+ return []interface{}{}
+ case "GetAsError":
+ return []interface{}{}
+ case "AsMap":
+ return []interface{}{}
+ case "KeyValuePairs":
+ return []interface{}{}
+ case "MostRecentSet":
+ return []interface{}{}
+ case "KeyPage":
+ return []interface{}{}
+ case "Size":
+ return []interface{}{}
+ case "MultiGet":
+ return []interface{}{}
+ default:
+ return nil
+ }
+}
diff --git a/examples/wspr_sample/error_thrower.idl b/examples/wspr_sample/error_thrower.idl
new file mode 100644
index 0000000..baee28f
--- /dev/null
+++ b/examples/wspr_sample/error_thrower.idl
@@ -0,0 +1,34 @@
+package sample
+
+// A testing interface with methods that throw various types of errors
+type ErrorThrower interface {
+ // Throws veyron2/vError.Aborted error
+ ThrowAborted() error
+
+ // Throws veyron2/vError.BadArg error
+ ThrowBadArg() error
+
+ // Throws veyron2/vError.BadProtocol error
+ ThrowBadProtocol() error
+
+ // Throws veyron2/vError.Internal error
+ ThrowInternal() error
+
+ // Throws veyron2/vError.NotAuthorized error
+ ThrowNotAuthorized() error
+
+ // Throws veyron2/vError.NotFound error
+ ThrowNotFound() error
+
+ // Throws veyron2/vError.Unknown error
+ ThrowUnknown() error
+
+ // Throws normal Go error
+ ThrowGoError() error
+
+ // Throws custom error created by using Standard
+ ThrowCustomStandardError() error
+
+ // Lists all errors Ids available in veyron2/verror
+ ListAllBuiltInErrorIDs() ([]string, error)
+}
\ No newline at end of file
diff --git a/examples/wspr_sample/error_thrower.idl.go b/examples/wspr_sample/error_thrower.idl.go
new file mode 100644
index 0000000..77aad7d
--- /dev/null
+++ b/examples/wspr_sample/error_thrower.idl.go
@@ -0,0 +1,437 @@
+// This file was auto-generated by the veyron idl tool.
+// Source: error_thrower.idl
+
+package sample
+
+import (
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_idl "veyron2/idl"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt/r"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// A testing interface with methods that throw various types of errors
+// ErrorThrower is the interface the client binds and uses.
+// ErrorThrower_InternalNoTagGetter is the interface without the TagGetter
+// and UnresolveStep methods (both framework-added, rathern than user-defined),
+// to enable embedding without method collisions. Not to be used directly by
+// clients.
+type ErrorThrower_InternalNoTagGetter interface {
+
+ // Throws veyron2/vError.Aborted error
+ ThrowAborted(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws veyron2/vError.BadArg error
+ ThrowBadArg(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws veyron2/vError.BadProtocol error
+ ThrowBadProtocol(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws veyron2/vError.Internal error
+ ThrowInternal(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws veyron2/vError.NotAuthorized error
+ ThrowNotAuthorized(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws veyron2/vError.NotFound error
+ ThrowNotFound(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws veyron2/vError.Unknown error
+ ThrowUnknown(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws normal Go error
+ ThrowGoError(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Throws custom error created by using Standard
+ ThrowCustomStandardError(opts ..._gen_ipc.ClientCallOpt) (err error)
+
+ // Lists all errors Ids available in veyron2/verror
+ ListAllBuiltInErrorIDs(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error)
+}
+type ErrorThrower interface {
+ _gen_idl.TagGetter
+ // UnresolveStep returns the names for the remote service, rooted at the
+ // service's immediate namespace ancestor.
+ UnresolveStep(opts ..._gen_ipc.ClientCallOpt) ([]string, error)
+ ErrorThrower_InternalNoTagGetter
+}
+
+// ErrorThrowerService is the interface the server implements.
+type ErrorThrowerService interface {
+
+ // Throws veyron2/vError.Aborted error
+ ThrowAborted(context _gen_ipc.Context) (err error)
+
+ // Throws veyron2/vError.BadArg error
+ ThrowBadArg(context _gen_ipc.Context) (err error)
+
+ // Throws veyron2/vError.BadProtocol error
+ ThrowBadProtocol(context _gen_ipc.Context) (err error)
+
+ // Throws veyron2/vError.Internal error
+ ThrowInternal(context _gen_ipc.Context) (err error)
+
+ // Throws veyron2/vError.NotAuthorized error
+ ThrowNotAuthorized(context _gen_ipc.Context) (err error)
+
+ // Throws veyron2/vError.NotFound error
+ ThrowNotFound(context _gen_ipc.Context) (err error)
+
+ // Throws veyron2/vError.Unknown error
+ ThrowUnknown(context _gen_ipc.Context) (err error)
+
+ // Throws normal Go error
+ ThrowGoError(context _gen_ipc.Context) (err error)
+
+ // Throws custom error created by using Standard
+ ThrowCustomStandardError(context _gen_ipc.Context) (err error)
+
+ // Lists all errors Ids available in veyron2/verror
+ ListAllBuiltInErrorIDs(context _gen_ipc.Context) (reply []string, err error)
+}
+
+// BindErrorThrower returns the client stub implementing the ErrorThrower
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindErrorThrower(name string, opts ..._gen_ipc.BindOpt) (ErrorThrower, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_ipc.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_idl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_idl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubErrorThrower{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerErrorThrower creates a new server stub.
+//
+// It takes a regular server implementing the ErrorThrowerService
+// interface, and returns a new server stub.
+func NewServerErrorThrower(server ErrorThrowerService) interface{} {
+ return &ServerStubErrorThrower{
+ service: server,
+ }
+}
+
+// clientStubErrorThrower implements ErrorThrower.
+type clientStubErrorThrower struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (c *clientStubErrorThrower) GetMethodTags(method string) []interface{} {
+ return GetErrorThrowerMethodTags(method)
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowAborted(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowAborted", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowBadArg(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowBadArg", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowBadProtocol(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowBadProtocol", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowInternal(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowInternal", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowNotAuthorized(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowNotAuthorized", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowNotFound(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowNotFound", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowUnknown(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowUnknown", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowGoError(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowGoError", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ThrowCustomStandardError(opts ..._gen_ipc.ClientCallOpt) (err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ThrowCustomStandardError", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubErrorThrower) ListAllBuiltInErrorIDs(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = __gen_c.client.StartCall(__gen_c.name, "ListAllBuiltInErrorIDs", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (c *clientStubErrorThrower) UnresolveStep(opts ..._gen_ipc.ClientCallOpt) (reply []string, err error) {
+ var call _gen_ipc.ClientCall
+ if call, err = c.client.StartCall(c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubErrorThrower wraps a server that implements
+// ErrorThrowerService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubErrorThrower struct {
+ service ErrorThrowerService
+}
+
+func (s *ServerStubErrorThrower) GetMethodTags(method string) []interface{} {
+ return GetErrorThrowerMethodTags(method)
+}
+
+func (s *ServerStubErrorThrower) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["ListAllBuiltInErrorIDs"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 61},
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowAborted"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowBadArg"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowBadProtocol"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowCustomStandardError"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowGoError"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowInternal"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowNotAuthorized"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowNotFound"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["ThrowUnknown"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+
+ result.TypeDefs = []_gen_idl.AnyData{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (s *ServerStubErrorThrower) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowAborted(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowAborted(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowBadArg(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowBadArg(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowBadProtocol(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowBadProtocol(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowInternal(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowInternal(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowNotAuthorized(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowNotAuthorized(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowNotFound(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowNotFound(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowUnknown(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowUnknown(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowGoError(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowGoError(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ThrowCustomStandardError(call _gen_ipc.ServerCall) (err error) {
+ err = __gen_s.service.ThrowCustomStandardError(call)
+ return
+}
+
+func (__gen_s *ServerStubErrorThrower) ListAllBuiltInErrorIDs(call _gen_ipc.ServerCall) (reply []string, err error) {
+ reply, err = __gen_s.service.ListAllBuiltInErrorIDs(call)
+ return
+}
+
+func GetErrorThrowerMethodTags(method string) []interface{} {
+ switch method {
+ case "ThrowAborted":
+ return []interface{}{}
+ case "ThrowBadArg":
+ return []interface{}{}
+ case "ThrowBadProtocol":
+ return []interface{}{}
+ case "ThrowInternal":
+ return []interface{}{}
+ case "ThrowNotAuthorized":
+ return []interface{}{}
+ case "ThrowNotFound":
+ return []interface{}{}
+ case "ThrowUnknown":
+ return []interface{}{}
+ case "ThrowGoError":
+ return []interface{}{}
+ case "ThrowCustomStandardError":
+ return []interface{}{}
+ case "ListAllBuiltInErrorIDs":
+ return []interface{}{}
+ default:
+ return nil
+ }
+}
diff --git a/examples/wspr_sample/sampled/lib/cache_impl.go b/examples/wspr_sample/sampled/lib/cache_impl.go
new file mode 100644
index 0000000..9ce3e79
--- /dev/null
+++ b/examples/wspr_sample/sampled/lib/cache_impl.go
@@ -0,0 +1,201 @@
+package lib
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "time"
+
+ "veyron2/idl"
+ "veyron2/ipc"
+ "veyron2/verror"
+
+ sample "veyron/examples/wspr_sample"
+)
+
+var typedNil []int
+
+// A simple in-memory implementation of a Cache service.
+type cacheImpl struct {
+ cache map[string]idl.AnyData
+ mostRecent sample.KeyValuePair
+ lastUpdateTime time.Time
+}
+
+// NewCached returns a new implementation of the CacheService.
+func NewCached() sample.CacheService {
+ return &cacheImpl{cache: make(map[string]idl.AnyData)}
+}
+
+// Set sets a value for a key. This should never return an error.
+func (c *cacheImpl) Set(_ ipc.Context, key string, value idl.AnyData) error {
+ c.cache[key] = value
+ c.mostRecent = sample.KeyValuePair{Key: key, Value: value}
+ c.lastUpdateTime = time.Now()
+ return nil
+}
+
+// Get returns the value for a key. If the key is not in the map, it returns
+// an error.
+func (c *cacheImpl) Get(_ ipc.Context, key string) (idl.AnyData, error) {
+ if value, ok := c.cache[key]; ok {
+ return value, nil
+ }
+
+ return typedNil, verror.NotFoundf("key not found: %v", key)
+}
+
+// getWithTypeCheck gets the key and tests if its type matches the given time, erroring if it does
+// not.
+// This exists mostly to shorted the Get* methods below.
+func (c *cacheImpl) getWithTypeCheck(key string, rt reflect.Type) (interface{}, error) {
+ v, err := c.Get(nil, key)
+ if err != nil {
+ return reflect.Zero(rt).Interface(), err
+ }
+ if !reflect.TypeOf(v).AssignableTo(rt) {
+ return reflect.Zero(rt).Interface(),
+ fmt.Errorf("Cannot convert %v (type %v) to type %v", v, reflect.TypeOf(v), rt)
+ }
+ return v, nil
+}
+
+// Same as Get, but casts the return argument to an int32.
+func (c *cacheImpl) GetAsInt32(_ ipc.Context, key string) (int32, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(int32(0)))
+ return v.(int32), err
+}
+
+// Same as Get, but casts the return argument to an int64.
+func (c *cacheImpl) GetAsInt64(_ ipc.Context, key string) (int64, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(int64(0)))
+ return v.(int64), err
+}
+
+// Same as Get, but casts the return argument to an uint8.
+func (c *cacheImpl) GetAsByte(_ ipc.Context, key string) (byte, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(byte(0)))
+ return v.(uint8), err
+}
+
+// Same as Get, but casts the return argument to an uint32.
+func (c *cacheImpl) GetAsUint32(_ ipc.Context, key string) (uint32, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(uint32(0)))
+ return v.(uint32), err
+}
+
+// Same as Get, but casts the return argument to an uint64.
+func (c *cacheImpl) GetAsUint64(_ ipc.Context, key string) (uint64, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(uint64(0)))
+ return v.(uint64), err
+}
+
+// Same as Get, but casts the return argument to a float32.
+func (c *cacheImpl) GetAsFloat32(_ ipc.Context, key string) (float32, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(float32(0)))
+ return v.(float32), err
+}
+
+// Same as Get, but casts the return argument to a float64.
+func (c *cacheImpl) GetAsFloat64(_ ipc.Context, key string) (float64, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(float64(0)))
+ return v.(float64), err
+}
+
+// Same as Get, but casts the return argument to a string.
+func (c *cacheImpl) GetAsString(_ ipc.Context, key string) (string, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(""))
+ return v.(string), err
+}
+
+// Same as Get, but casts the return argument to a bool.
+func (c *cacheImpl) GetAsBool(_ ipc.Context, key string) (bool, error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf(false))
+ return v.(bool), err
+}
+
+// Same as Get, but converts the string return argument to an error.
+func (c *cacheImpl) GetAsError(_ ipc.Context, key string) (storedError error, callError error) {
+ v, err := c.getWithTypeCheck(key, reflect.TypeOf([]error{}).Elem())
+ return v.(error), err
+}
+
+// AsMap returns the full contents of the cache as a map.
+func (c *cacheImpl) AsMap(ipc.Context) (map[string]idl.AnyData, error) {
+ return c.cache, nil
+}
+
+// KeyValuePairs returns the full contents of the cache as a slice of pairs.
+func (c *cacheImpl) KeyValuePairs(ipc.Context) ([]sample.KeyValuePair, error) {
+ kvp := make([]sample.KeyValuePair, 0, len(c.cache))
+ for key, val := range c.cache {
+ kvp = append(kvp, sample.KeyValuePair{key, val})
+ }
+ return kvp, nil
+}
+
+// MostRecentSet returns the key and value and the timestamp for the most
+// recent set operation
+// TODO(bprosnitz) support type types and change time to native time type
+func (c *cacheImpl) MostRecentSet(ipc.Context) (sample.KeyValuePair, int64, error) {
+ var err error
+ if c.lastUpdateTime.IsZero() {
+ err = verror.NotFoundf("no values in the cache so cannot return most recent.")
+ }
+ return c.mostRecent, c.lastUpdateTime.Unix(), err
+}
+
+// KeyPage indexes into the keys (in alphanumerically sorted order) and
+// returns the indexth page of 10 keys.
+func (c *cacheImpl) KeyPage(_ ipc.Context, index int64) ([10]string, error) {
+ results := [10]string{}
+
+ keys := sort.StringSlice{}
+ for key, _ := range c.cache {
+ keys = append(keys, key)
+ }
+ keys.Sort()
+
+ lowIndex := int(index) * 10
+ if index < 0 || len(keys) <= lowIndex {
+ return results, verror.BadArgf("Page index out of bounds: %d", index)
+ }
+ highIndex := lowIndex + 9
+ if highIndex > len(keys)-1 {
+ highIndex = len(keys) - 1
+ }
+
+ for i := 0; lowIndex+i <= highIndex; i++ {
+ results[i] = keys[lowIndex+i]
+ }
+
+ return results, nil
+}
+
+// Size returns the total number of entries in the cache.
+func (c *cacheImpl) Size(ipc.Context) (int64, error) {
+ return int64(len(c.cache)), nil
+}
+
+// MultiGet handles a stream of get requests. Returns an error if one of the
+// keys in the stream is not in the map or if there was an issue reading
+// the stream.
+func (c *cacheImpl) MultiGet(_ ipc.Context, stream sample.CacheServiceMultiGetStream) error {
+ for {
+ key, err := stream.Recv()
+ if err == io.EOF {
+ return nil
+ }
+
+ if err != nil {
+ return err
+ }
+
+ value, ok := c.cache[key]
+ if !ok {
+ return fmt.Errorf("key not found: %v", key)
+ }
+ stream.Send(value)
+ }
+}
diff --git a/examples/wspr_sample/sampled/lib/error_thrower_impl.go b/examples/wspr_sample/sampled/lib/error_thrower_impl.go
new file mode 100644
index 0000000..3ad7e68
--- /dev/null
+++ b/examples/wspr_sample/sampled/lib/error_thrower_impl.go
@@ -0,0 +1,65 @@
+package lib
+
+import (
+ "errors"
+
+ "veyron2/ipc"
+ "veyron2/verror"
+
+ sample "veyron/examples/wspr_sample"
+)
+
+// NewCached returns a new implementation of the ErrorThrowerService.
+func NewErrorThrower() sample.ErrorThrowerService {
+ return &errorThrowerImpl{}
+}
+
+type errorThrowerImpl struct{}
+
+func (e *errorThrowerImpl) ThrowAborted(_ ipc.Context) error {
+ return verror.Abortedf("Aborted!")
+}
+
+func (e *errorThrowerImpl) ThrowBadArg(_ ipc.Context) error {
+ return verror.BadArgf("BadArg!")
+}
+
+func (e *errorThrowerImpl) ThrowBadProtocol(_ ipc.Context) error {
+ return verror.BadProtocolf("BadProtocol!")
+}
+
+func (e *errorThrowerImpl) ThrowInternal(_ ipc.Context) error {
+ return verror.Internalf("Internal!")
+}
+
+func (e *errorThrowerImpl) ThrowNotAuthorized(_ ipc.Context) error {
+ return verror.NotAuthorizedf("NotAuthorized!")
+}
+
+func (e *errorThrowerImpl) ThrowNotFound(_ ipc.Context) error {
+ return verror.NotFoundf("NotFound!")
+}
+
+func (e *errorThrowerImpl) ThrowUnknown(_ ipc.Context) error {
+ return verror.Unknownf("Unknown!")
+}
+
+func (e *errorThrowerImpl) ThrowGoError(_ ipc.Context) error {
+ return errors.New("GoError!")
+}
+
+func (e *errorThrowerImpl) ThrowCustomStandardError(_ ipc.Context) error {
+ return verror.Standard{
+ ID: "MyCustomError",
+ Msg: "CustomStandard!",
+ }
+}
+
+func (e *errorThrowerImpl) ListAllBuiltInErrorIDs(_ ipc.Context) ([]string, error) {
+ // TODO(aghassemi) Use when we have enum for error IDs in IDL
+ // This is not used yet but the idea is to pass all error types in veyron2/verror to
+ // JavaScript so if a new one is added, this test would break and we add the new one to
+ // JavaScript as well. There is no way to enumerate all error IDs right now since they
+ // are constants and not an Enum. Enum support is coming later.
+ return nil, nil
+}
diff --git a/examples/wspr_sample/sampled/lib/sampled.go b/examples/wspr_sample/sampled/lib/sampled.go
new file mode 100644
index 0000000..2a3dc43
--- /dev/null
+++ b/examples/wspr_sample/sampled/lib/sampled.go
@@ -0,0 +1,55 @@
+package lib
+
+import (
+ "fmt"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/security"
+
+ sample "veyron/examples/wspr_sample"
+)
+
+type cacheDispatcher struct {
+ cached interface{}
+}
+
+func (cd *cacheDispatcher) Lookup(string) (ipc.Invoker, security.Authorizer, error) {
+ return ipc.ReflectInvoker(cd.cached), nil, nil
+}
+
+func StartServer(r veyron2.Runtime) (ipc.Server, naming.Endpoint, error) {
+
+ // Create a new server instance.
+ s, err := r.NewServer()
+ if err != nil {
+ return nil, nil, fmt.Errorf("failure creating server: %v", err)
+ }
+
+ // Register the "cache" prefix with the cache dispatcher.
+ serverCache := sample.NewServerCache(NewCached())
+ if err := s.Register("cache", &cacheDispatcher{cached: serverCache}); err != nil {
+ return nil, nil, fmt.Errorf("error registering cache service: %v", err)
+ }
+
+ // Register the "errorthrower" prefix with the errorthrower dispatcher.
+ errorThrower := sample.NewServerErrorThrower(NewErrorThrower())
+ if err := s.Register("errorthrower", &cacheDispatcher{cached: errorThrower}); err != nil {
+ return nil, nil, fmt.Errorf("error registering error thrower service: %v", err)
+ }
+
+ // Create an endpoint and begin listening.
+ endpoint, err := s.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ return nil, nil, fmt.Errorf("error listening to service: %v", err)
+ }
+
+ // Publish the cache service. This will register it in the mount table and maintain the
+ // registration until StopServing is called.
+ if err := s.Publish("/cache"); err != nil {
+ return nil, nil, fmt.Errorf("error publishing service: %v", err)
+ }
+
+ return s, endpoint, nil
+}
diff --git a/examples/wspr_sample/sampled/lib/sampled_test.go b/examples/wspr_sample/sampled/lib/sampled_test.go
new file mode 100644
index 0000000..79c8c06
--- /dev/null
+++ b/examples/wspr_sample/sampled/lib/sampled_test.go
@@ -0,0 +1,306 @@
+package lib
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+ "time"
+
+ "veyron2/idl"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/rt"
+ "veyron2/verror"
+
+ hps "veyron/examples/wspr_sample"
+)
+
+// getCacheClient initializes the runtime and creates a client binding.
+func getCacheClient(address string) (hps.Cache, error) {
+ rt.Init()
+
+ s, err := hps.BindCache(naming.JoinAddressName(address, "cache"))
+ if err != nil {
+ return nil, err
+ }
+
+ return s, nil
+}
+
+// TestValueSetGet tests setting values and then calling various Get* functions.
+func TestValueSetGet(t *testing.T) {
+ type testCase struct {
+ mapFieldName string
+ nameOfGetMethod string
+ v interface{}
+ shouldGetError bool
+ }
+ tests := []testCase{
+ testCase{"val", "Get", "Test", false},
+ testCase{"val", "Get", 4, false},
+ testCase{"val", "Get", struct {
+ X int
+ Y string
+ }{4, "Test"}, false},
+ testCase{"i32", "GetAsInt32", int32(1), false},
+ testCase{"i64", "GetAsInt64", int64(1), false},
+ testCase{"byt", "GetAsByte", byte(1), false},
+ testCase{"ui32", "GetAsUint32", uint32(1), false},
+ testCase{"ui64", "GetAsUint64", uint64(1), false},
+ testCase{"fl32", "GetAsFloat32", float32(1), false},
+ testCase{"fl64", "GetAsFloat64", float64(1), false},
+ testCase{"b", "GetAsBool", true, false},
+ testCase{"s", "GetAsString", "Test", false},
+ testCase{"err", "GetAsError", verror.ToStandard(errors.New("Test Error")), false},
+ testCase{"err_i8", "GetAsByte", int64(4), true},
+ testCase{"err_i64", "GetAsInt64", int8(1), true},
+ testCase{"err_string", "GetAsString", true, true},
+ }
+ r := rt.Init()
+
+ s, endpoint, err := StartServer(r)
+ if err != nil {
+ t.Fatal("failed to start server: ", err)
+ }
+ defer s.Stop()
+ c, err := getCacheClient(endpoint.String())
+ if err != nil {
+ t.Fatal("failed to connect client: ", err)
+ }
+ for _, test := range tests {
+ // Call Set().
+ if err := c.Set(test.mapFieldName, test.v); err != nil {
+ t.Errorf("error setting: %v (test case: %v)", err, test)
+ continue
+ }
+
+ meth := reflect.ValueOf(c).MethodByName(test.nameOfGetMethod)
+ out := meth.Call([]reflect.Value{reflect.ValueOf(test.mapFieldName)})
+ if !test.shouldGetError {
+ if out[1].Interface() != nil {
+ t.Errorf("error getting: %v (test case: %v)", err, test)
+ continue
+ }
+ if out[0].Interface() != test.v {
+ t.Errorf("returned result does not match")
+ }
+ } else if out[1].Interface() == nil {
+ t.Errorf("expected error in case %v", test)
+ }
+
+ }
+}
+
+// settable mirrors the cache's Set method to provide a consistent way to populate test cases.
+type settable interface {
+ Set(key string, val idl.AnyData, opts ...ipc.ClientCallOpt) error
+}
+
+// populateObject populates a settable with 12 values.
+func populateObject(s settable) error {
+ if err := s.Set("A", int8(3)); err != nil {
+ return err
+ }
+ // Set "A" again to ensure it takes the second value.
+ if err := s.Set("A", "A"); err != nil {
+ return err
+ }
+ if err := s.Set("B", uint16(5)); err != nil {
+ return err
+ }
+ if err := s.Set("C", uint32(7)); err != nil {
+ return err
+ }
+ if err := s.Set("D", verror.ToStandard(errors.New("Err"))); err != nil {
+ return err
+ }
+ if err := s.Set("E", true); err != nil {
+ return err
+ }
+ if err := s.Set("F", float32(5.4)); err != nil {
+ return err
+ }
+ if err := s.Set("G", struct {
+ X int
+ Y string
+ }{4, "G"}); err != nil {
+ return err
+ }
+ if err := s.Set("H", uint64(8)); err != nil {
+ return err
+ }
+ if err := s.Set("I", "I"); err != nil {
+ return err
+ }
+ if err := s.Set("J", float64(8.3)); err != nil {
+ return err
+ }
+ if err := s.Set("K", int64(2)); err != nil {
+ return err
+ }
+ if err := s.Set("L", int8(9)); err != nil {
+ return err
+ }
+ return nil
+}
+
+// setupManyResults starts a server and client and populates the server with the values in populateObject.
+func setupManyResults(t *testing.T) (hps.Cache, ipc.Server) {
+ r := rt.Init()
+ s, endpoint, err := StartServer(r)
+ if err != nil {
+ t.Fatal("failed to start server: ", err)
+ }
+ c, err := getCacheClient(endpoint.String())
+ if err != nil {
+ t.Fatal("failed to connect client: ", err)
+ }
+
+ if err := populateObject(c.(settable)); err != nil {
+ t.Fatal("error populating cache: ", err)
+ }
+
+ return c, s
+}
+
+// settableMap is a map that implements the settable interface.
+type settableMap map[string]idl.AnyData
+
+func (sm settableMap) Set(key string, val idl.AnyData, opts ...ipc.ClientCallOpt) error {
+ sm[key] = val
+ return nil
+}
+
+// TestAsMap tests that AsMap returns the correct results.
+func TestAsMap(t *testing.T) {
+ c, s := setupManyResults(t)
+ defer s.Stop()
+
+ res, err := c.AsMap()
+ if err != nil {
+ t.Fatal("error calling AsMap: ", err)
+ }
+
+ m := settableMap(make(map[string]idl.AnyData))
+ if err := populateObject(m); err != nil {
+ t.Fatal("error populating map: ", err)
+ }
+
+ for key, val := range m {
+ otherVal := res[key]
+ if val != otherVal {
+ t.Errorf("didn't match: %v and %v", val, otherVal)
+ }
+ }
+}
+
+// TestKeyValuePairs tests that KeyValuePairs returns the correct results.
+func TestKeyValuePairs(t *testing.T) {
+ c, s := setupManyResults(t)
+ defer s.Stop()
+
+ res, err := c.KeyValuePairs()
+ if err != nil {
+ t.Fatal("error calling KeyValuePairs: ", err)
+ }
+
+ m := settableMap(make(map[string]idl.AnyData))
+ if err := populateObject(m); err != nil {
+ t.Fatal("error populating map: ", err)
+ }
+
+ for _, kvp := range res {
+ otherVal := m[kvp.Key]
+ if kvp.Value != otherVal {
+ t.Errorf("didn't match: %v and %v", kvp.Value, otherVal)
+ }
+ }
+}
+
+// TestKeyPageAndSize tests the KeyPage and size methods.
+func TestKeyPageAndSize(t *testing.T) {
+ c, s := setupManyResults(t)
+ defer s.Stop()
+
+ sz, err := c.Size()
+ if err != nil {
+ t.Fatal("error calling Size: ", err)
+ }
+ if sz != 12 {
+ t.Fatal("wrong number of results: ", sz)
+ }
+
+ res, err := c.KeyPage(1)
+ if err != nil {
+ t.Fatal("error calling AsMap: ", err)
+ }
+
+ if res[0] != "K" || res[1] != "L" || res[2] != "" {
+ t.Fatalf("incorrect page results: %v", res)
+ }
+}
+
+// TestMostRecentSet tests the MostRecentSet method.
+func TestMostRecentSet(t *testing.T) {
+ c, s := setupManyResults(t)
+ defer s.Stop()
+
+ timeBefore := time.Now().Unix()
+ if err := c.Set("B", int32(8)); err != nil {
+ t.Fatal("error calling Set: ", err)
+ }
+ timeAfter := time.Now().Unix()
+
+ kvp, setTime, err := c.MostRecentSet()
+ if err != nil {
+ t.Fatal("error calling MostRecentSet: ", err)
+ }
+
+ if kvp.Key != "B" || kvp.Value != int32(8) {
+ t.Errorf("unexpected key value pair: %v", kvp)
+ }
+
+ if setTime < timeBefore || setTime > timeAfter {
+ t.Errorf("time %v out of range [%v, %v]", setTime, timeBefore, timeAfter)
+ }
+}
+
+// TestMultiGet tests the MultiGet method.
+func TestMultiGet(t *testing.T) {
+ c, s := setupManyResults(t)
+ defer s.Stop()
+
+ stream, err := c.MultiGet()
+ if err != nil {
+ t.Fatal("error calling MultiGet: ", err)
+ }
+ stream.Send("A")
+ stream.Send("C")
+ stream.Send("E")
+
+ if item, err := stream.Recv(); err == nil {
+ if item != "A" {
+ t.Errorf("value for 'A' didn't match")
+ }
+ } else {
+ t.Fatal("error on recv: %v", err)
+ }
+
+ if item, err := stream.Recv(); err == nil {
+ if item != uint32(7) {
+ t.Errorf("value for 'C' didn't match")
+ }
+ } else {
+ t.Fatal("error on recv: %v", err)
+ }
+
+ if item, err := stream.Recv(); err == nil {
+ if item != true {
+ t.Errorf("value for 'E' didn't match")
+ }
+ } else {
+ t.Fatal("error on recv: %v", err)
+ }
+
+ stream.CloseSend()
+}
diff --git a/examples/wspr_sample/sampled/main.go b/examples/wspr_sample/sampled/main.go
new file mode 100644
index 0000000..fce02eb
--- /dev/null
+++ b/examples/wspr_sample/sampled/main.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "veyron/examples/wspr_sample/sampled/lib"
+ "veyron/lib/signals"
+ "veyron2/rt"
+)
+
+func main() {
+ // Create the runtime
+ r := rt.Init()
+ defer r.Shutdown()
+
+ s, endpoint, err := lib.StartServer(r)
+ if err != nil {
+ log.Fatal("", err)
+ }
+ defer s.Stop()
+
+ fmt.Printf("Listening at: %v\n", endpoint)
+ <-signals.ShutdownOnSignals()
+}