blob: 63b0748190f971cc24d5ac8ffac7519390a9e980 [file] [log] [blame]
yoshi28bac132014-01-22 11:00:17 -08001/* Copyright (c) 2013 Stanford University
2 *
3 * Permission to use, copy, modify, and distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15
16package edu.stanford.ramcloud;
Yoshi Muroi2c170602014-02-15 08:31:28 -080017import java.util.Arrays;
Yoshi Muroie7693b12014-02-19 19:41:17 -080018import java.util.LinkedList;
yoshi28bac132014-01-22 11:00:17 -080019
20/*
21 * This class provides Java bindings for RAMCloud. Right now it is a rather
22 * simple subset of what RamCloud.h defines.
23 *
24 * Running ``javah'' on this file will generate a C header file with the
25 * appropriate JNI function definitions. The glue interfacing to the C++
26 * RAMCloud library can be found in JRamCloud.cc.
27 *
28 * For JNI information, the IBM tutorials and Android developer docs are much
29 * better than Sun's at giving an overall intro:
30 * http://www.ibm.com/developerworks/java/tutorials/j-jni/section4.html
31 * http://developer.android.com/training/articles/perf-jni.html
32 */
33public class JRamCloud {
34 static {
35 System.loadLibrary("edu_stanford_ramcloud_JRamCloud");
36 }
37
38 /// Pointer to the underlying C++ RAMCloud object associated with this
39 /// object.
40 private long ramcloudObjectPointer = 0;
41
42 /**
43 * See src/RejectRules.h.
44 */
45 public class RejectRules {
46 private long givenVersion;
47 private boolean doesntExist;
48 private boolean exists;
49 private boolean versionLeGiven;
50 private boolean versionNeGiven;
51
52 public RejectRules() {
53 this.givenVersion = -1;
54 this.exists = this.doesntExist = this.versionLeGiven = this.versionNeGiven = false;
55 }
56
57 public void setLeVersion(long version) {
58 setVersion(version);
59 this.versionLeGiven = true;
60 }
61
62 public void setExists() {
63 this.exists = true;
64 }
65
66 public void setDoesntExists() {
67 this.doesntExist = true;
68 }
69
70 public void setNeVersion(long version) {
71 setVersion(version);
72 this.versionNeGiven = true;
73 }
74
75 private void setVersion(long version) {
76 this.givenVersion = version;
77 }
78 }
79
Yoshi Muroie7693b12014-02-19 19:41:17 -080080 public static class MultiReadObject {
81 public long[] tableId;
82 public byte[] key[];
83 public short[] keyLength;
yoshi28bac132014-01-22 11:00:17 -080084
Yoshi Muroie7693b12014-02-19 19:41:17 -080085 public MultiReadObject(int size){
86 this.tableId = new long[size];
87 this.key = new byte[size][];
88 this.keyLength = new short[size];
yoshi28bac132014-01-22 11:00:17 -080089 }
Yoshi Muroie7693b12014-02-19 19:41:17 -080090
91 public void setObject(int num, long tableId, byte key[]){
92 this.tableId[num] = tableId;
93 this.key[num] = key;
94 this.keyLength[num] = (short) this.key[num].length;
95 }
yoshi28bac132014-01-22 11:00:17 -080096 }
97
98 public static class MultiWriteObject {
Yoshi Muroie7693b12014-02-19 19:41:17 -080099 public long[] tableId;
100 public byte[] key[];
101 public short[] keyLength;
102 public byte[] value[];
103 public short[] valueLength;
104 public RejectRules[] rules;
yoshi28bac132014-01-22 11:00:17 -0800105
Yoshi Muroie7693b12014-02-19 19:41:17 -0800106 public MultiWriteObject(int size) {
107 this.tableId = new long[size];
108 this.key = new byte[size][];
109 this.keyLength = new short[size];
110 this.value = new byte[size][];
111 this.valueLength = new short[size];
112 this.rules = new RejectRules[size];
yoshi28bac132014-01-22 11:00:17 -0800113 }
Yoshi Muroie7693b12014-02-19 19:41:17 -0800114
115 public void setObject(int num, long tableId, byte key[], byte value[], RejectRules rules){
116 this.tableId[num] = tableId;
117 this.key[num] = key;
118 this.keyLength[num] = (short) key.length;
119 this.value[num] = value;
120 this.valueLength[num] = (short) value.length;
121 this.rules[num] = rules;
122 }
123
yoshi28bac132014-01-22 11:00:17 -0800124 }
125
Yoshi Muroie7693b12014-02-19 19:41:17 -0800126 public static class MultiWriteRspObject {
yoshi28bac132014-01-22 11:00:17 -0800127 private int status;
128 private long version;
129
130 public MultiWriteRspObject(int status, long version) {
131 this.status = status;
132 this.version = version;
133 }
134 public int getStatus() {
135 return status;
136 }
137
138 public long getVersion() {
139 return version;
140 }
141 }
142
143 /**
144 * This class is returned by Read operations. It encapsulates the entire
145 * object, including the key, value, and version.
146 *
147 * It mostly exists because Java doesn't support primitive out parameters
148 * or multiple return values, and we don't know the object's size ahead of
149 * time, so passing in a fixed-length array would be problematic.
150 */
Yoshi Muroie7693b12014-02-19 19:41:17 -0800151 public static class Object {
yoshi28bac132014-01-22 11:00:17 -0800152 Object(byte[] _key, byte[] _value, long _version)
153 {
154 key = _key;
155 value = _value;
156 version = _version;
157 }
158
159 public String
160 getKey()
161 {
162 return new String(key);
163 }
164
165 public String
166 getValue()
167 {
168 return new String(value);
169 }
170
171 final public byte[] key;
172 final public byte[] value;
173 final public long version;
174 }
Yoshi Muroie7693b12014-02-19 19:41:17 -0800175
176 public static class TableEnumeratorObject {
177 TableEnumeratorObject(Object[] _object, long _nextHash)
178 {
179 object = _object;
180 nextHash = _nextHash;
181 }
182
183 final public Object[] object;
184 final public long nextHash;
185 }
yoshi28bac132014-01-22 11:00:17 -0800186
187 public class TableEnumerator {
188 private long tableEnumeratorObjectPointer = 0;
189 private long ramCloudObjectPointer = 0;
190
191 public TableEnumerator(long tableId)
192 {
193 ramCloudObjectPointer = ramcloudObjectPointer;
194 tableEnumeratorObjectPointer = init(tableId);
195 }
196
197 private native long init(long tableId);
198 public native boolean hasNext();
199 public native Object next();
200 }
201
Yoshi Muroie7693b12014-02-19 19:41:17 -0800202 public class TableEnumerator2 {
203
204 protected long tableId;
205 protected LinkedList<JRamCloud.Object> rcobjs = null;
206 protected long nextHash = 0;
207 protected boolean done = false;
208
209 public TableEnumerator2(long tableId)
210 {
211 this.tableId = tableId;
212 rcobjs = new LinkedList<>();
213 }
214 public boolean hasNext() {
215 if (rcobjs.isEmpty())
216 {
217 if (done) {
218 return false;
219 }
220 JRamCloud.TableEnumeratorObject o = getTableObjects(this.tableId, this.nextHash);
221 if (o.nextHash == 0L) {
222 done = true;
223 }
224 this.nextHash = o.nextHash;
225 rcobjs.addAll(Arrays.asList(o.object));
226 if (rcobjs.isEmpty()) {
227 return false;
228 }
229 }
230 return true;
231 }
232
233 public Object next()
234 {
235 return rcobjs.pop();
236 }
237 }
238
yoshi28bac132014-01-22 11:00:17 -0800239 /**
240 * Connect to the RAMCloud cluster specified by the given coordinator's
241 * service locator string. This causes the JNI code to instantiate the
242 * underlying RamCloud C++ object.
243 */
244 public
245 JRamCloud(String coordinatorLocator)
246 {
247 ramcloudObjectPointer = connect(coordinatorLocator);
248 }
249
250 /**
251 * Disconnect from the RAMCloud cluster. This causes the JNI code to
252 * destroy the underlying RamCloud C++ object.
253 */
254 public void
255 disconnect()
256 {
257 if (ramcloudObjectPointer != 0) {
258 disconnect(ramcloudObjectPointer);
259 ramcloudObjectPointer = 0;
260 }
261 }
262
263 /**
264 * This method is called by the garbage collector before destroying the
265 * object. The user really should have called disconnect, but in case
266 * they did not, be sure to clean up after them.
267 */
268 public void
269 finalize()
270 {
271 System.err.println("warning: JRamCloud::disconnect() was not called " +
272 "prior to the finalizer. You should disconnect " +
273 "your JRamCloud object when you're done with it.");
274 disconnect();
275 }
276
277 /**
278 * Convenience read() wrapper that take a String key argument.
279 */
280 public Object
281 read(long tableId, String key)
282 {
283 return read(tableId, key.getBytes());
284 }
285
286 /**
287 * Convenience read() wrapper that take a String key argument.
288 */
289 public Object
290 read(long tableId, String key, RejectRules rules)
291 {
292 return read(tableId, key.getBytes(), rules);
293 }
294
295 /**
296 * Convenience remove() wrapper that take a String key argument.
297 */
298 public long
299 remove(long tableId, String key)
300 {
301 return remove(tableId, key.getBytes());
302 }
303
304 /**
305 * Convenience remove() wrapper that take a String key argument.
306 */
307 public long
308 remove(long tableId, String key, RejectRules rules)
309 {
310 return remove(tableId, key.getBytes(), rules);
311 }
312
313 /**
314 * Convenience write() wrapper that take String key and value arguments.
315 */
316 public long
317 write(long tableId, String key, String value)
318 {
319 return write(tableId, key.getBytes(), value.getBytes());
320 }
321
322 /**
323 * Convenience write() wrapper that take String key and value arguments.
324 */
325 public long
326 write(long tableId, String key, String value, RejectRules rules)
327 {
328 return write(tableId, key.getBytes(), value.getBytes(), rules);
329 }
330
331 /**
332 * Convenience write() wrapper that takes a String key and a byte[] value
333 * argument.
334 */
335 public long
336 write(long tableId, String key, byte[] value)
337 {
338 return write(tableId, key.getBytes(), value);
339 }
340
341 /**
342 * Convenience write() wrapper that takes a String key and a byte[] value
343 * argument.
344 */
345 public long
346 write(long tableId, String key, byte[] value, RejectRules rules)
347 {
348 return write(tableId, key.getBytes(), value, rules);
349 }
350
351 private static native long connect(String coordinatorLocator);
352 private static native void disconnect(long ramcloudObjectPointer);
353
354 public native long createTable(String name);
355 public native long createTable(String name, int serverSpan);
356 public native void dropTable(String name);
357 public native long getTableId(String name);
358 public native Object read(long tableId, byte[] key);
359 public native Object read(long tableId, byte[] key, RejectRules rules);
Yoshi Muroi2c170602014-02-15 08:31:28 -0800360 public native Object[] multiRead(long[] tableId, byte[] keydata[], short[] keyDataSize, int requestNum);
yoshi28bac132014-01-22 11:00:17 -0800361 public native long remove(long tableId, byte[] key);
362 public native long remove(long tableId, byte[] key, RejectRules rules);
363 public native long write(long tableId, byte[] key, byte[] value);
364 public native long write(long tableId, byte[] key, byte[] value, RejectRules rules);
365 public native long writeRule(long tableId, byte[] key, byte[] value, RejectRules rules);
Yoshi Muroie7693b12014-02-19 19:41:17 -0800366 public native MultiWriteRspObject[] multiWrite(long[] tableId, byte[] key[], short[] keyDataSize, byte[] value[], short[] valueDataSize, int requestNum, RejectRules[] rules);
367 public native TableEnumeratorObject getTableObjects(long tableId, long nextHash);
yoshi28bac132014-01-22 11:00:17 -0800368
369 /*
370 * The following exceptions may be thrown by the JNI functions:
371 */
372
373 public class TableDoesntExistException extends Exception {
374 public TableDoesntExistException(String message)
375 {
376 super(message);
377 }
378 }
379
380 public class ObjectDoesntExistException extends Exception {
381 public ObjectDoesntExistException(String message)
382 {
383 super(message);
384 }
385 }
386
387 public class ObjectExistsException extends Exception {
388 public ObjectExistsException(String message)
389 {
390 super(message);
391 }
392 }
393
394 public class WrongVersionException extends Exception {
395 public WrongVersionException(String message)
396 {
397 super(message);
398 }
399 }
400
401 public class InvalidObjectException extends Exception {
402 public InvalidObjectException(String message) {
403 super(message);
404 }
405 }
406
407 public class RejectRulesException extends Exception {
408 public RejectRulesException(String message) {
409 super(message);
410 }
411 }
Yoshi Muroie7693b12014-02-19 19:41:17 -0800412
413 public static void tableEnumeratorTest(JRamCloud ramcloud) {
414 long startTime = 0;
415 for (int x = 0 ; x < 2 ; x ++){
416 for(int N = 1000; N < 10000; N += 1000) {
417 long EnumerateTesttable = ramcloud.createTable("EnumerateTest");
418 for(int i = 0 ; i < N ; ++i) {
419 String key = new Integer(i).toString();
420 ramcloud.write(EnumerateTesttable, key.getBytes(), "Hello, World!".getBytes());
421 }
yoshi28bac132014-01-22 11:00:17 -0800422
Yoshi Muroie7693b12014-02-19 19:41:17 -0800423 MultiReadObject mread[] = new MultiReadObject[N];
424 long tableIdList[] = new long[N];
425 byte[] keydata[] = new byte[N][];
426 short keydataSize[] = new short[N];
427 startTime = System.nanoTime();
428 for (int j = 0 ; j < N ; ++j) {
429 tableIdList[j] = EnumerateTesttable;
430 String key = new Integer(j).toString();
431 keydata[j] = key.getBytes();
432 keydataSize[j] = (short) keydata[j].length;
433 }
434 JRamCloud.Object out[] = ramcloud.multiRead(tableIdList, keydata, keydataSize, N);
435 for (int i = 0; i < N; ++i) {
436 if(out[i].version == 0) {
437 System.out.println("Verify fail " + out[i].getKey() + " V:" + out[i].getValue());
438 }
439 }
440
441 System.out.println("multiRead : " + N + " Time : " + (System.nanoTime()-startTime));
442
443 startTime = System.nanoTime();
444 JRamCloud.TableEnumerator tableEnum = ramcloud.new TableEnumerator(EnumerateTesttable);
445 while (tableEnum.hasNext()) {
446 Object tableEntry = tableEnum.next();
447 if (tableEntry != null) {
448 System.out.println("tableEnumerator object: key = [" + tableEntry.getKey() + "], value = [" + tableEntry.getValue() + "]");
449 }
450 }
451 System.out.println("old TableEnumerator : " + N + " Time : " + (System.nanoTime()-startTime));
452
453 startTime = System.nanoTime();
454 JRamCloud.TableEnumerator2 tableEnum2 = ramcloud.new TableEnumerator2(EnumerateTesttable);
455 while (tableEnum2.hasNext()) {
456 Object tableEntry2 = tableEnum2.next();
457 if (tableEntry2 != null) {
458 System.out.println("tableEnumerator2 object: key = [" + tableEntry2.getKey() + "], value = [" + tableEntry2.getValue() + "]");
459 }
460 }
461 System.out.println("new TableEnumerator : " + N + " Time : " + (System.nanoTime()-startTime));
462 ramcloud.dropTable("EnumerateTest");
463 }
464 }
465 }
yoshi28bac132014-01-22 11:00:17 -0800466 /**
467 * A simple end-to-end test of the java bindings.
468 */
469 public static void
470 main(String argv[])
471 {
472 JRamCloud ramcloud = new JRamCloud(argv[0]);
473 long tableId = ramcloud.createTable("hi");
474 System.out.println("created table, id = " + tableId);
475 long tableId2 = ramcloud.getTableId("hi");
476 System.out.println("getTableId says tableId = " + tableId2);
477
478 System.out.println("wrote obj version = " +
479 ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue"));
480
481 JRamCloud.Object o = ramcloud.read(tableId, "thisIsTheKey");
482 System.out.println("read object: key = [" + o.getKey() + "], value = ["
483 + o.getValue() + "], version = " + o.version);
484
485 ramcloud.remove(tableId, "thisIsTheKey");
486
487 try {
488 ramcloud.read(tableId, "thisIsTheKey");
489 System.out.println("Error: shouldn't have read successfully!");
490 } catch (Exception e) {
491 // OK
492 }
493
494 ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue");
495
496 long before = System.nanoTime();
497 for (int i = 0; i < 1000; i++) {
498 JRamCloud.Object unused = ramcloud.read(tableId, "thisIsTheKey");
499 }
500 long after = System.nanoTime();
501 System.out.println("Avg read latency: " +
502 ((double)(after - before) / 1000 / 1000) + " usec");
503
504 // multiRead test
505 long tableId4 = ramcloud.createTable("table4");
506 System.out.println("table4 id " + tableId4);
507 ramcloud.write(tableId4, "object1-1", "value:1-1");
508 ramcloud.write(tableId4, "object1-2", "value:1-2");
509 ramcloud.write(tableId4, "object1-3", "value:1-3");
510 long tableId5 = ramcloud.createTable("table5");
511 System.out.println("table5 id " + tableId5);
512 ramcloud.write(tableId5, "object2-1", "value:2-1");
513 long tableId6 = ramcloud.createTable("table6");
514 ramcloud.write(tableId6, "object3-1", "value:3-1");
515 ramcloud.write(tableId6, "object3-2", "value:3-2");
516
Yoshi Muroie7693b12014-02-19 19:41:17 -0800517 MultiReadObject mr = new MultiReadObject(2);
518 MultiWriteObject mw = new MultiWriteObject(2);
519
520 mr.setObject(0, tableId4, "object1-1".getBytes());
521 mr.setObject(1, tableId5, "object2-1".getBytes());
Yoshi Muroi2c170602014-02-15 08:31:28 -0800522
Yoshi Muroie7693b12014-02-19 19:41:17 -0800523 JRamCloud.Object out[] = ramcloud.multiRead(mr.tableId, mr.key, mr.keyLength, 2);
yoshi28bac132014-01-22 11:00:17 -0800524 for (int i = 0 ; i < 2 ; i++){
525 System.out.println("multi read object: key = [" + out[i].getKey() + "], value = ["
526 + out[i].getValue() + "]");
yoshi28bac132014-01-22 11:00:17 -0800527 }
Yoshi Muroi2c170602014-02-15 08:31:28 -0800528
yoshi28bac132014-01-22 11:00:17 -0800529 for (int i = 0; i < 1000; i++) {
530 String key1 = "key1" + new Integer(i).toString();
531 String key2 = "key2" + new Integer(i).toString();
Yoshi Muroie7693b12014-02-19 19:41:17 -0800532
533 mw.setObject(0, tableId4, key1.getBytes(), "v0-value".getBytes(), null);
534 mw.setObject(1, tableId5, key2.getBytes(), "v1".getBytes(), null);
535
536 MultiWriteRspObject[] rsp = ramcloud.multiWrite(mw.tableId, mw.key, mw.keyLength, mw.value, mw.valueLength, 2, mw.rules);
yoshi28bac132014-01-22 11:00:17 -0800537 if (rsp != null) {
538 for (int j = 0; j < rsp.length; j++) {
539 System.out.println("multi write rsp(" + j + ") status:version " + rsp[j].getStatus() + ":" + rsp[j].getVersion());
540 }
541 }
542 }
543 for (int i = 0; i < 1000; i++) {
544 String key1 = "key1" + new Integer(i).toString();
545 String key2 = "key2" + new Integer(i).toString();
Yoshi Muroi2c170602014-02-15 08:31:28 -0800546
Yoshi Muroie7693b12014-02-19 19:41:17 -0800547 mr.setObject(0, tableId4, key1.getBytes());
548 mr.setObject(1, tableId5, key2.getBytes());
549
550 out = ramcloud.multiRead(mr.tableId, mr.key, mr.keyLength, 2);
yoshi28bac132014-01-22 11:00:17 -0800551 for (int j = 0; j < 2; j++) {
552 System.out.println("multi read object: key = [" + out[j].getKey() + "], value = [" + out[j].getValue() + "]");
553 }
554 }
Yoshi Muroi2c170602014-02-15 08:31:28 -0800555
Yoshi Muroie7693b12014-02-19 19:41:17 -0800556 tableEnumeratorTest(ramcloud);
557
yoshi28bac132014-01-22 11:00:17 -0800558 ramcloud.dropTable("table4");
559 ramcloud.dropTable("table5");
560 ramcloud.dropTable("table6");
561 ramcloud.disconnect();
562 }
563}