java: Implement 23 more JNI syncbase internal functions

The functions added in this CL are:

  Database.Destroy
  Database.Exists
  Database.BeginBatch
  Database.Commit
  Database.Abort
  Database.ListCollections
  Database.GetResumeMarker
  Database.ListSyncgroups
  Database.CreateSyncgroup
  Collection.Create
  Collection.Destroy
  Collection.Exists
  Collection.DeleteRange
  Row.Put
  Row.Get
  Row.Exists
  Row.Delete
  Blessings.DebugString
  Blessings.AppBlessingFromContext
  Blessings.UserBlessingFromContext
  Util.Encode
  Util.EncodeId
  Util.NamingJoin

This CL adds idempotent "x.free" methods to types.go and changes the
"x.toFoo" functions to leave the "x" in the same state as "x.free".

This CL also fixes the cExists argument for v23_syncbase_RowExists
to be *C.v23_syncbase_Bool instead of *bool.

MultiPart: 2/2
Change-Id: I5b72bfd8b4754d9fe4ce06f571232dd46da7f383
diff --git a/syncbase/src/main/java/io/v/syncbase/internal/Blessings.java b/syncbase/src/main/java/io/v/syncbase/internal/Blessings.java
new file mode 100644
index 0000000..9302393
--- /dev/null
+++ b/syncbase/src/main/java/io/v/syncbase/internal/Blessings.java
@@ -0,0 +1,11 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.internal;
+
+public class Blessings {
+    public static native String DebugString();
+    public static native String AppBlessingFromContext() throws VError;
+    public static native String UserBlessingFromContext() throws VError;
+}
diff --git a/syncbase/src/main/java/io/v/syncbase/internal/Database.java b/syncbase/src/main/java/io/v/syncbase/internal/Database.java
index 4768646..8a43caf 100644
--- a/syncbase/src/main/java/io/v/syncbase/internal/Database.java
+++ b/syncbase/src/main/java/io/v/syncbase/internal/Database.java
@@ -14,7 +14,7 @@
     public static native void Destroy(String name) throws VError;
     public static native boolean Exists(String name) throws VError;
 
-    public class BatchOptions {
+    public static class BatchOptions {
         String hint;
         boolean readOnly;
     }
@@ -25,20 +25,21 @@
     public static native void Abort(String name, String batchHandle) throws VError;
     public static native byte[] GetResumeMarker(String name, String batchHandle) throws VError;
 
-    public class SyncgroupSpec {
+    public static class SyncgroupSpec {
         String description;
+        String publishSyncbaseName;
         Permissions permissions;
         List<Id> collections;
         List<String> mountTables;
         boolean isPrivate;
     }
 
-    public class VersionedSyncgroupSpec {
+    public static class VersionedSyncgroupSpec {
         String version;
         SyncgroupSpec syncgroupSpec;
     }
 
-    public class SyncgroupMemberInfo {
+    public static class SyncgroupMemberInfo {
         int syncPriority;
         int blobDevType;
     }
diff --git a/syncbase/src/main/java/io/v/syncbase/internal/Id.java b/syncbase/src/main/java/io/v/syncbase/internal/Id.java
index 34e912b..c876ea3 100644
--- a/syncbase/src/main/java/io/v/syncbase/internal/Id.java
+++ b/syncbase/src/main/java/io/v/syncbase/internal/Id.java
@@ -18,9 +18,10 @@
         this.name = name;
     }
 
-    // TODO(razvanm): Switch to v23_syncbase_EncodeId.
-    // Reference: https://github.com/vanadium/go.v23/blob/master/syncbase/util/util.go
-    public String toString() {
-        return blessing + "," + name;
+    // TODO(razvanm): Add a constructor that takes an encoded ID. This requires first extending the
+    // C API from services/syncbase/bridge/cgo/impl.go.
+
+    public String encode() {
+        return Util.EncodeId(this);
     }
 }
diff --git a/syncbase/src/main/java/io/v/syncbase/internal/Util.java b/syncbase/src/main/java/io/v/syncbase/internal/Util.java
new file mode 100644
index 0000000..48da808
--- /dev/null
+++ b/syncbase/src/main/java/io/v/syncbase/internal/Util.java
@@ -0,0 +1,13 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.internal;
+
+import java.util.List;
+
+public class Util {
+    public static native String Encode(String s);
+    public static native String EncodeId(Id id);
+    public static native String NamingJoin(List<String> elements);
+}
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java b/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java
new file mode 100644
index 0000000..a6de221
--- /dev/null
+++ b/syncbase/src/test/java/io/v/syncbase/internal/BlessingsTest.java
@@ -0,0 +1,50 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class BlessingsTest {
+    @Before
+    public void setUp() {
+        System.loadLibrary("syncbase");
+    }
+
+    @Test
+    public void DebugStringTest() {
+        String s = Blessings.DebugString();
+        assertTrue(s.contains("Default Blessings"));
+    }
+
+    @Test
+    public void AppBlessingFromContext() {
+        try {
+            String s = Blessings.AppBlessingFromContext();
+            assertNotNull(s);
+            assertFalse(s.isEmpty());
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void UserBlessingFromContext() {
+        try {
+            String s = Blessings.UserBlessingFromContext();
+            assertNotNull(s);
+            assertFalse(s.isEmpty());
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+}
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java b/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
new file mode 100644
index 0000000..db07d1d
--- /dev/null
+++ b/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
@@ -0,0 +1,122 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class CollectionTest {
+    @Before
+    public void setUp() {
+        System.loadLibrary("syncbase");
+    }
+
+    @Test
+    public void createCollection() {
+        Id dbId = new Id("idp:a:angrybirds", "create_collection");
+        Id collectionId1 = new Id("idp:u:alice", "collection1");
+        Id collectionId2 = new Id("idp:u:alice", "collection2");
+        try {
+            Database.Create(dbId.encode(), null);
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+
+            String name1 = Util.NamingJoin(Arrays.asList(dbId.encode(), collectionId1.encode()));
+            Collection.Create(name1, batchHandle, null);
+
+            String name2 = Util.NamingJoin(Arrays.asList(dbId.encode(), collectionId2.encode()));
+            Collection.Create(name2, batchHandle, null);
+
+            List<Id> collections = Database.ListCollections(dbId.encode(), batchHandle);
+            assertNotNull(collections);
+            assertEquals(2, collections.size());
+            assertEquals(collectionId1.encode(), collections.get(0).encode());
+            assertEquals(collectionId2.encode(), collections.get(1).encode());
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void destroyCollection() {
+        Id dbId = new Id("idp:a:angrybirds", "destroy_collection");
+        String dbName = dbId.encode();
+        Id collectionId = new Id("idp:u:alice", "collection");
+        String collectionName = Util.NamingJoin(Arrays.asList(dbName, collectionId.encode()));
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+            Collection.Create(collectionName, batchHandle, null);
+            Database.Commit(dbName, batchHandle);
+            batchHandle = Database.BeginBatch(dbName, null);
+            Collection.Destroy(collectionName, batchHandle);
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void existsCollection() {
+        Id dbId = new Id("idp:a:angrybirds", "exists_collection");
+        String dbName = dbId.encode();
+        Id collectionId1 = new Id("idp:u:alice", "collection1");
+        String collectionName1 = Util.NamingJoin(Arrays.asList(dbName, collectionId1.encode()));
+        Id collectionId2 = new Id("idp:u:alice", "collection2");
+        String collectionName2 = Util.NamingJoin(Arrays.asList(dbName, collectionId2.encode()));
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+            Collection.Create(collectionName1, batchHandle, null);
+            // We have not committed the batch yet so Exists should fail.
+            assertFalse(Collection.Exists(collectionName1, batchHandle));
+            Database.Commit(dbName, batchHandle);
+            batchHandle = Database.BeginBatch(dbName, null);
+            assertTrue(Collection.Exists(collectionName1, batchHandle));
+            assertFalse(Collection.Exists(collectionName2, batchHandle));
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void deleteRangeCollection() {
+        Id dbId = new Id("idp:a:angrybirds", "delete_range_collection");
+        String dbName = dbId.encode();
+        Id collectionId = new Id("idp:u:alice", "collection");
+        String collectionName = Util.NamingJoin(Arrays.asList(dbName, collectionId.encode()));
+        String keyName = Util.NamingJoin(Arrays.asList(collectionName, "key"));
+        // Reference: release/go/src/v.io/v23/vom/testdata/data81/vomdata.vdl
+        byte[] vomValue = {(byte)0x81, 0x06, 0x03, 'a', 'b', 'c'};
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+            Collection.Create(collectionName, batchHandle, null);
+            Row.Put(keyName, batchHandle, vomValue);
+            Database.Commit(dbName, batchHandle);
+
+            batchHandle = Database.BeginBatch(dbName, null);
+            assertTrue(Row.Exists(keyName, batchHandle));
+            Collection.DeleteRange(collectionName, batchHandle, new byte[]{}, new byte[]{});
+            Database.Commit(dbName, batchHandle);
+
+            batchHandle = Database.BeginBatch(dbName, null);
+            assertFalse(Row.Exists(keyName, batchHandle));
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+}
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java b/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
index 6c5baa4..610dd6b 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
@@ -7,7 +7,11 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Arrays;
+import java.util.List;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -20,18 +24,21 @@
 
     @Test
     public void createDatabase() {
-        Id dbId = new Id("blessing", "db");
+        Id dbId = new Id("idp:a:angrybirds", "create_db");
+        String dbName = dbId.encode();
+
         // The instance is empty so creating of a database should succeed.
         try {
-            Database.Create(dbId.toString(), null);
+            Database.Create(dbName, null);
         } catch (VError vError) {
-            fail();
+            vError.printStackTrace();
+            fail(vError.toString());
         }
 
         // Creating the same database should raise an exception.
         boolean exceptionThrown = false;
         try {
-            Database.Create(dbId.toString(), null);
+            Database.Create(dbName, null);
         } catch (VError vError) {
             assertEquals("v.io/v23/verror.Exist", vError.id);
             assertNotNull(vError.message);
@@ -41,4 +48,125 @@
         }
         assertTrue(exceptionThrown);
     }
+
+    @Test
+    public void destroyDatabase() {
+        Id dbId = new Id("idp:a:angrybirds", "destroy_db");
+        String dbName = dbId.encode();
+        try {
+            // The instance is empty so creating of a database should succeed.
+            Database.Create(dbName, null);
+            Database.Destroy(dbName);
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void existsDatabase() {
+        Id dbId = new Id("idp:a:angrybirds", "exists_db");
+        String dbName = dbId.encode();
+        try {
+            // We have not created the database yet so Exists should fail.
+            assertFalse(Database.Exists(dbName));
+            // The instance is empty so creating of a database should succeed.
+            Database.Create(dbName, null);
+            // Exists should succeed now.
+            assertTrue(Database.Exists(dbName));
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void abortDatabase() {
+        Id dbId = new Id("idp:a:angrybirds", "abort_db");
+        String dbName = dbId.encode();
+        Id collectionId = new Id("idp:u:alice", "collection");
+        String collectionName = Util.NamingJoin(Arrays.asList(dbName, collectionId.encode()));
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbName, null);
+            Collection.Create(collectionName, batchHandle, null);
+            Database.Abort(dbName, batchHandle);
+            batchHandle = Database.BeginBatch(dbName, null);
+            // This should work because we Abort the previous batch.
+            Collection.Create(collectionName, batchHandle, null);
+            Database.Commit(dbName, batchHandle);
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void listCollections() {
+        Id dbId = new Id("idp:a:angrybirds", "list_db");
+        String dbName = dbId.encode();
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbName, null);
+            assertNotNull(batchHandle);
+            List<Id> collections = Database.ListCollections(dbName, batchHandle);
+            assertNotNull(collections);
+            assertEquals(0, collections.size());
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void getResumeMarker() {
+        Id dbId = new Id("idp:a:angrybirds", "get_resume_marker");
+        String dbName = dbId.encode();
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbName, null);
+            byte[] marker = Database.GetResumeMarker(dbName, batchHandle);
+            assertNotNull(marker);
+            assertTrue(marker.length > 0);
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void createSyncgroup() {
+        Id dbId = new Id("idp:a:angrybirds", "create_syncgroups");
+        String dbName = dbId.encode();
+        Id sgId = new Id("idp:u:alice", "syncgroup");
+        Id collectionId = new Id("idp:u:alice", "collection");
+        String collectionName = Util.NamingJoin(Arrays.asList(dbName, collectionId.encode()));
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+            Collection.Create(collectionName, batchHandle, null);
+            Database.Commit(dbName, batchHandle);
+            Database.SyncgroupSpec spec = new Database.SyncgroupSpec();
+            spec.collections = Arrays.asList(collectionId);
+            Database.CreateSyncgroup(dbName, sgId, spec, new Database.SyncgroupMemberInfo());
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+
+    @Test
+    public void listSyncgroups() {
+        Id dbId = new Id("idp:a:angrybirds", "list_syncgroups");
+        String dbName = dbId.encode();
+        try {
+            Database.Create(dbName, null);
+            List<Id> syncgroups = Database.ListSyncgroups(dbName);
+            assertNotNull(syncgroups);
+            assertEquals(0, syncgroups.size());
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
 }
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java b/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java
new file mode 100644
index 0000000..03adeac
--- /dev/null
+++ b/syncbase/src/test/java/io/v/syncbase/internal/RowTest.java
@@ -0,0 +1,47 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class RowTest {
+    @Before
+    public void setUp() {
+        System.loadLibrary("syncbase");
+    }
+
+    @Test
+    public void allTest() {
+        Id dbId = new Id("idp:a:angrybirds", "collection");
+        String dbName = dbId.encode();
+        Id collectionId = new Id("idp:u:alice", "collection");
+        String collectionName = Util.NamingJoin(Arrays.asList(dbName, collectionId.encode()));
+        String keyName = Util.NamingJoin(Arrays.asList(collectionName, "key"));
+        // Reference: release/go/src/v.io/v23/vom/testdata/data81/vomdata.vdl
+        byte[] vomValue = {(byte)0x81, 0x06, 0x03, 'a', 'b', 'c'};
+        try {
+            Database.Create(dbName, null);
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+            Collection.Create(collectionName, batchHandle, null);
+            Row.Put(keyName, batchHandle, vomValue);
+            byte[] r = Row.Get(keyName, batchHandle);
+            assertArrayEquals(vomValue, r);
+            assertTrue(Row.Exists(keyName, batchHandle));
+            Row.Delete(keyName, batchHandle);
+            assertFalse(Row.Exists(keyName, batchHandle));
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+    }
+}
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/UtilTest.java b/syncbase/src/test/java/io/v/syncbase/internal/UtilTest.java
new file mode 100644
index 0000000..ac7902c
--- /dev/null
+++ b/syncbase/src/test/java/io/v/syncbase/internal/UtilTest.java
@@ -0,0 +1,36 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.syncbase.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+public class UtilTest {
+    @Before
+    public void setUp() {
+        System.loadLibrary("syncbase");
+    }
+
+    @Test
+    public void testEncode() {
+        assertEquals("string", Util.Encode("string"));
+        assertEquals("part1%2Fpart2", Util.Encode("part1/part2"));
+        assertEquals("part1%25part2", Util.Encode("part1%part2"));
+    }
+
+    @Test
+    public void testEncodeId() {
+        assertEquals("blessing,name", Util.EncodeId(new Id("blessing", "name")));
+    }
+
+    @Test
+    public void testNamingJoin() {
+        assertEquals("a/b/c", Util.NamingJoin(Arrays.asList("a", "b", "c")));
+    }
+}