java: Implement the Database.WatchPatterns and Collection.Scan

I wasn't able to see that the onChange is called (which matches what
zinman@ also observed).

This change also contains two fixes to the ScanCallbacks interface:

 - the onValue method is renamed to onKeyValue
 - the onDone is updated to take a VError argument.

Due to some new "inconsistent definitions" I also had to move the
jFindClass from jni_util to jni.go. The bottom of the jni.go and the
jni_lib.go are also sorted.

MultiPart: 2/2
Change-Id: I4fba94a06b0398e46623ec39e1350ff179dec52a
diff --git a/syncbase/src/main/java/io/v/syncbase/internal/Collection.java b/syncbase/src/main/java/io/v/syncbase/internal/Collection.java
index 4710ece..4ba5e69 100644
--- a/syncbase/src/main/java/io/v/syncbase/internal/Collection.java
+++ b/syncbase/src/main/java/io/v/syncbase/internal/Collection.java
@@ -19,8 +19,8 @@
     }
 
     public interface ScanCallbacks {
-        void onValue(KeyValue keyValue);
-        void onDone();
+        void onKeyValue(KeyValue keyValue);
+        void onDone(VError vError);
     }
 
     public static native void Scan(String name, String batchHandle, byte[] start, byte[] limit, ScanCallbacks callbacks) throws VError;
diff --git a/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java b/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
index 06f31d2..4a07d4f 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/CollectionTest.java
@@ -4,14 +4,18 @@
 
 package io.v.syncbase.internal;
 
+import com.google.common.util.concurrent.SettableFuture;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import static io.v.syncbase.internal.TestConstants.anyCollectionPermissions;
 import static io.v.syncbase.internal.TestConstants.anyDbPermissions;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -146,4 +150,44 @@
             fail(vError.toString());
         }
     }
+
+    @Test
+    public void scan() throws Exception {
+        Id dbId = new Id("idp:a:angrybirds", "scan_collection");
+        String dbName = dbId.encode();
+        Id collectionId = new Id("...", "collection");
+        String collectionName = Util.NamingJoin(Arrays.asList(dbName, collectionId.encode()));
+        final String keyName = Util.NamingJoin(Arrays.asList(collectionName, "key"));
+        // Reference: release/go/src/v.io/v23/vom/testdata/data81/vomdata.vdl
+        final byte[] vomValue = {(byte)0x81, 0x06, 0x03, 'a', 'b', 'c'};
+        try {
+            Database.Create(dbName, anyDbPermissions());
+            String batchHandle = Database.BeginBatch(dbId.encode(), null);
+            Collection.Create(collectionName, batchHandle, anyCollectionPermissions());
+            Row.Put(keyName, batchHandle, vomValue);
+            Database.Commit(dbName, batchHandle);
+
+            batchHandle = Database.BeginBatch(dbName, null);
+            assertTrue(Row.Exists(keyName, batchHandle));
+            final SettableFuture<Void> done = SettableFuture.create();
+            Collection.Scan(collectionName, batchHandle, new byte[]{}, new byte[]{},
+                    new Collection.ScanCallbacks() {
+                @Override
+                public void onKeyValue(Collection.KeyValue keyValue) {
+                    assertEquals("key", keyValue.key);
+                    assertArrayEquals(vomValue, keyValue.value);
+                }
+
+                @Override
+                public void onDone(VError vError) {
+                    assertEquals(null, vError);
+                    done.set(null);
+                }
+            });
+            done.get(1, TimeUnit.SECONDS);
+        } 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 9827359..d1b3955 100644
--- a/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
+++ b/syncbase/src/test/java/io/v/syncbase/internal/DatabaseTest.java
@@ -4,6 +4,8 @@
 
 package io.v.syncbase.internal;
 
+import com.google.common.util.concurrent.SettableFuture;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -11,6 +13,9 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import static io.v.syncbase.internal.TestConstants.anyCollectionPermissions;
 import static io.v.syncbase.internal.TestConstants.anyDbPermissions;
@@ -299,4 +304,88 @@
         }
         assertTrue(exceptionThrown);
     }
+
+    @Test
+    public void watchPattersEmptyPattern() {
+        Id dbId = new Id("idp:a:angrybirds", "watch_patterns_empty");
+        String dbName = dbId.encode();
+        final SettableFuture<Void> done = SettableFuture.create();
+        try {
+            Database.Create(dbName, anyDbPermissions());
+            String batchHandle = Database.BeginBatch(dbName, null);
+            byte[] marker = Database.GetResumeMarker(dbName, batchHandle);
+            List<Database.CollectionRowPattern> patterns =
+                    Arrays.asList(new Database.CollectionRowPattern());
+            Database.WatchPatterns(dbName, marker, patterns, new Database.WatchPatternsCallbacks() {
+                @Override
+                public void onChange(Database.WatchChange watchChange) {
+                    fail("Unexpected onChange: " + watchChange);
+                }
+
+                @Override
+                public void onError(VError vError) {
+                    assertEquals("v.io/v23/verror.BadArg", vError.id);
+                    done.set(null);
+                }
+            });
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+        try {
+            done.get(1, TimeUnit.SECONDS);
+        } catch (InterruptedException | TimeoutException | ExecutionException e) {
+            fail("Timeout waiting for onError");
+        }
+    }
+
+    @Test
+    public void watchPatterns() {
+        Id dbId = new Id("idp:a:angrybirds", "watch_patterns");
+        String dbName = dbId.encode();
+        Id collectionId = new Id("...", "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'};
+        final SettableFuture<Void> done = SettableFuture.create();
+        try {
+            Database.Create(dbName, anyDbPermissions());
+            String batchHandle = Database.BeginBatch(dbName, null);
+            Collection.Create(collectionName, batchHandle, anyCollectionPermissions());
+            Database.CollectionRowPattern pattern = new Database.CollectionRowPattern();
+            pattern.collectionBlessing = collectionId.blessing;
+            pattern.collectionName = collectionId.name;
+            List<Database.CollectionRowPattern> patterns = Arrays.asList(pattern);
+            Database.WatchPatterns(dbName, new byte[]{}, patterns,
+                    new Database.WatchPatternsCallbacks() {
+                @Override
+                public void onChange(Database.WatchChange watchChange) {
+                    // TODO(razvanm): Really check the answer once the onChange starts working.
+                    fail("Unexpected onChange: " + watchChange);
+                }
+
+                @Override
+                public void onError(VError vError) {
+                    System.err.print(vError);
+                    done.set(null);
+                }
+            });
+            Database.Commit(dbName, batchHandle);
+
+            batchHandle = Database.BeginBatch(dbName, null);
+            Row.Put(keyName, batchHandle, vomValue);
+            Database.Commit(dbName, batchHandle);
+        } catch (VError vError) {
+            vError.printStackTrace();
+            fail(vError.toString());
+        }
+        try {
+            done.get(10, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException|ExecutionException e) {
+            fail("Timeout waiting for onError");
+        } catch (TimeoutException e) {
+            // TODO(razvanm): Remove this after the onChange starts working.
+        }
+    }
 }