blob: dfb4b6acc74526e6322c76b1befa815c5ca03a6b [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.ArrayList;
18import java.util.Arrays;
19import java.lang.Integer;
yoshi28bac132014-01-22 11:00:17 -080020
21/*
22 * This class provides Java bindings for RAMCloud. Right now it is a rather
23 * simple subset of what RamCloud.h defines.
24 *
25 * Running ``javah'' on this file will generate a C header file with the
26 * appropriate JNI function definitions. The glue interfacing to the C++
27 * RAMCloud library can be found in JRamCloud.cc.
28 *
29 * For JNI information, the IBM tutorials and Android developer docs are much
30 * better than Sun's at giving an overall intro:
31 * http://www.ibm.com/developerworks/java/tutorials/j-jni/section4.html
32 * http://developer.android.com/training/articles/perf-jni.html
33 */
34public class JRamCloud {
35 static {
36 System.loadLibrary("edu_stanford_ramcloud_JRamCloud");
37 }
38
39 /// Pointer to the underlying C++ RAMCloud object associated with this
40 /// object.
41 private long ramcloudObjectPointer = 0;
42
43 /**
44 * See src/RejectRules.h.
45 */
46 public class RejectRules {
47 private long givenVersion;
48 private boolean doesntExist;
49 private boolean exists;
50 private boolean versionLeGiven;
51 private boolean versionNeGiven;
52
53 public RejectRules() {
54 this.givenVersion = -1;
55 this.exists = this.doesntExist = this.versionLeGiven = this.versionNeGiven = false;
56 }
57
58 public void setLeVersion(long version) {
59 setVersion(version);
60 this.versionLeGiven = true;
61 }
62
63 public void setExists() {
64 this.exists = true;
65 }
66
67 public void setDoesntExists() {
68 this.doesntExist = true;
69 }
70
71 public void setNeVersion(long version) {
72 setVersion(version);
73 this.versionNeGiven = true;
74 }
75
76 private void setVersion(long version) {
77 this.givenVersion = version;
78 }
79 }
80
81 public static class multiReadObject {
82 long tableId;
83 byte[] key;
84
85 public multiReadObject(long _tableId, byte[] _key){
86 tableId = _tableId;
87 key = _key;
88 }
89 }
90
91 public static class MultiWriteObject {
92 long tableId;
93 byte[] key;
94 byte[] value;
95 RejectRules rules;
96
97 public MultiWriteObject(long tableId, byte[] key, byte[] value, RejectRules rules) {
98 this.tableId = tableId;
99 this.key = key;
100 this.value = value;
101 this.rules = rules;
102 }
103 }
104
105 public class MultiWriteRspObject {
106 private int status;
107 private long version;
108
109 public MultiWriteRspObject(int status, long version) {
110 this.status = status;
111 this.version = version;
112 }
113 public int getStatus() {
114 return status;
115 }
116
117 public long getVersion() {
118 return version;
119 }
120 }
121
122 /**
123 * This class is returned by Read operations. It encapsulates the entire
124 * object, including the key, value, and version.
125 *
126 * It mostly exists because Java doesn't support primitive out parameters
127 * or multiple return values, and we don't know the object's size ahead of
128 * time, so passing in a fixed-length array would be problematic.
129 */
130 public class Object {
131 Object(byte[] _key, byte[] _value, long _version)
132 {
133 key = _key;
134 value = _value;
135 version = _version;
136 }
137
138 public String
139 getKey()
140 {
141 return new String(key);
142 }
143
144 public String
145 getValue()
146 {
147 return new String(value);
148 }
149
150 final public byte[] key;
151 final public byte[] value;
152 final public long version;
153 }
154
155 public class TableEnumerator {
156 private long tableEnumeratorObjectPointer = 0;
157 private long ramCloudObjectPointer = 0;
158
159 public TableEnumerator(long tableId)
160 {
161 ramCloudObjectPointer = ramcloudObjectPointer;
162 tableEnumeratorObjectPointer = init(tableId);
163 }
164
165 private native long init(long tableId);
166 public native boolean hasNext();
167 public native Object next();
168 }
169
170 /**
171 * Connect to the RAMCloud cluster specified by the given coordinator's
172 * service locator string. This causes the JNI code to instantiate the
173 * underlying RamCloud C++ object.
174 */
175 public
176 JRamCloud(String coordinatorLocator)
177 {
178 ramcloudObjectPointer = connect(coordinatorLocator);
179 }
180
181 /**
182 * Disconnect from the RAMCloud cluster. This causes the JNI code to
183 * destroy the underlying RamCloud C++ object.
184 */
185 public void
186 disconnect()
187 {
188 if (ramcloudObjectPointer != 0) {
189 disconnect(ramcloudObjectPointer);
190 ramcloudObjectPointer = 0;
191 }
192 }
193
194 /**
195 * This method is called by the garbage collector before destroying the
196 * object. The user really should have called disconnect, but in case
197 * they did not, be sure to clean up after them.
198 */
199 public void
200 finalize()
201 {
202 System.err.println("warning: JRamCloud::disconnect() was not called " +
203 "prior to the finalizer. You should disconnect " +
204 "your JRamCloud object when you're done with it.");
205 disconnect();
206 }
207
208 /**
209 * Convenience read() wrapper that take a String key argument.
210 */
211 public Object
212 read(long tableId, String key)
213 {
214 return read(tableId, key.getBytes());
215 }
216
217 /**
218 * Convenience read() wrapper that take a String key argument.
219 */
220 public Object
221 read(long tableId, String key, RejectRules rules)
222 {
223 return read(tableId, key.getBytes(), rules);
224 }
225
226 /**
227 * Convenience remove() wrapper that take a String key argument.
228 */
229 public long
230 remove(long tableId, String key)
231 {
232 return remove(tableId, key.getBytes());
233 }
234
235 /**
236 * Convenience remove() wrapper that take a String key argument.
237 */
238 public long
239 remove(long tableId, String key, RejectRules rules)
240 {
241 return remove(tableId, key.getBytes(), rules);
242 }
243
244 /**
245 * Convenience write() wrapper that take String key and value arguments.
246 */
247 public long
248 write(long tableId, String key, String value)
249 {
250 return write(tableId, key.getBytes(), value.getBytes());
251 }
252
253 /**
254 * Convenience write() wrapper that take String key and value arguments.
255 */
256 public long
257 write(long tableId, String key, String value, RejectRules rules)
258 {
259 return write(tableId, key.getBytes(), value.getBytes(), rules);
260 }
261
262 /**
263 * Convenience write() wrapper that takes a String key and a byte[] value
264 * argument.
265 */
266 public long
267 write(long tableId, String key, byte[] value)
268 {
269 return write(tableId, key.getBytes(), value);
270 }
271
272 /**
273 * Convenience write() wrapper that takes a String key and a byte[] value
274 * argument.
275 */
276 public long
277 write(long tableId, String key, byte[] value, RejectRules rules)
278 {
279 return write(tableId, key.getBytes(), value, rules);
280 }
281
282 private static native long connect(String coordinatorLocator);
283 private static native void disconnect(long ramcloudObjectPointer);
284
285 public native long createTable(String name);
286 public native long createTable(String name, int serverSpan);
287 public native void dropTable(String name);
288 public native long getTableId(String name);
289 public native Object read(long tableId, byte[] key);
290 public native Object read(long tableId, byte[] key, RejectRules rules);
Yoshi Muroi2c170602014-02-15 08:31:28 -0800291 public native Object[] multiRead(long[] tableId, byte[] keydata[], short[] keyDataSize, int requestNum);
yoshi28bac132014-01-22 11:00:17 -0800292 public native long remove(long tableId, byte[] key);
293 public native long remove(long tableId, byte[] key, RejectRules rules);
294 public native long write(long tableId, byte[] key, byte[] value);
295 public native long write(long tableId, byte[] key, byte[] value, RejectRules rules);
296 public native long writeRule(long tableId, byte[] key, byte[] value, RejectRules rules);
297 public native MultiWriteRspObject[] multiWrite(MultiWriteObject[] mwrite);
298
299 /*
300 * The following exceptions may be thrown by the JNI functions:
301 */
302
303 public class TableDoesntExistException extends Exception {
304 public TableDoesntExistException(String message)
305 {
306 super(message);
307 }
308 }
309
310 public class ObjectDoesntExistException extends Exception {
311 public ObjectDoesntExistException(String message)
312 {
313 super(message);
314 }
315 }
316
317 public class ObjectExistsException extends Exception {
318 public ObjectExistsException(String message)
319 {
320 super(message);
321 }
322 }
323
324 public class WrongVersionException extends Exception {
325 public WrongVersionException(String message)
326 {
327 super(message);
328 }
329 }
330
331 public class InvalidObjectException extends Exception {
332 public InvalidObjectException(String message) {
333 super(message);
334 }
335 }
336
337 public class RejectRulesException extends Exception {
338 public RejectRulesException(String message) {
339 super(message);
340 }
341 }
342
343 /**
344 * A simple end-to-end test of the java bindings.
345 */
346 public static void
347 main(String argv[])
348 {
349 JRamCloud ramcloud = new JRamCloud(argv[0]);
350 long tableId = ramcloud.createTable("hi");
351 System.out.println("created table, id = " + tableId);
352 long tableId2 = ramcloud.getTableId("hi");
353 System.out.println("getTableId says tableId = " + tableId2);
354
355 System.out.println("wrote obj version = " +
356 ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue"));
357
358 JRamCloud.Object o = ramcloud.read(tableId, "thisIsTheKey");
359 System.out.println("read object: key = [" + o.getKey() + "], value = ["
360 + o.getValue() + "], version = " + o.version);
361
362 ramcloud.remove(tableId, "thisIsTheKey");
363
364 try {
365 ramcloud.read(tableId, "thisIsTheKey");
366 System.out.println("Error: shouldn't have read successfully!");
367 } catch (Exception e) {
368 // OK
369 }
370
371 ramcloud.write(tableId, "thisIsTheKey", "thisIsTheValue");
372
373 long before = System.nanoTime();
374 for (int i = 0; i < 1000; i++) {
375 JRamCloud.Object unused = ramcloud.read(tableId, "thisIsTheKey");
376 }
377 long after = System.nanoTime();
378 System.out.println("Avg read latency: " +
379 ((double)(after - before) / 1000 / 1000) + " usec");
380
381 // multiRead test
382 long tableId4 = ramcloud.createTable("table4");
383 System.out.println("table4 id " + tableId4);
384 ramcloud.write(tableId4, "object1-1", "value:1-1");
385 ramcloud.write(tableId4, "object1-2", "value:1-2");
386 ramcloud.write(tableId4, "object1-3", "value:1-3");
387 long tableId5 = ramcloud.createTable("table5");
388 System.out.println("table5 id " + tableId5);
389 ramcloud.write(tableId5, "object2-1", "value:2-1");
390 long tableId6 = ramcloud.createTable("table6");
391 ramcloud.write(tableId6, "object3-1", "value:3-1");
392 ramcloud.write(tableId6, "object3-2", "value:3-2");
393
Yoshi Muroi2c170602014-02-15 08:31:28 -0800394 long tableIdList[] = new long[2];
395 byte[] keydata[] = new byte[2][];
396 short keydataSize[] = new short[2];
397 tableIdList[0] = tableId4;
398 keydata[0] = "object1-1".getBytes();
399 keydataSize[0] = (short) keydata[0].length;
400 tableIdList[1] = tableId5;
401 keydata[1] = "object2-1".getBytes();
402 keydataSize[1] = (short) keydata[1].length;
403
404 JRamCloud.Object out[] = ramcloud.multiRead(tableIdList, keydata, keydataSize, 2);
yoshi28bac132014-01-22 11:00:17 -0800405 for (int i = 0 ; i < 2 ; i++){
406 System.out.println("multi read object: key = [" + out[i].getKey() + "], value = ["
407 + out[i].getValue() + "]");
yoshi28bac132014-01-22 11:00:17 -0800408 }
Yoshi Muroi2c170602014-02-15 08:31:28 -0800409
yoshi28bac132014-01-22 11:00:17 -0800410 MultiWriteObject mwrite[] = new MultiWriteObject[2];
411 for (int i = 0; i < 1000; i++) {
412 String key1 = "key1" + new Integer(i).toString();
413 String key2 = "key2" + new Integer(i).toString();
414
415 mwrite[0] = new MultiWriteObject(tableId4, key1.getBytes(), "v0-value".getBytes(), null);
416 mwrite[1] = new MultiWriteObject(tableId5, key2.getBytes(), "v1".getBytes(), null);
417 MultiWriteRspObject[] rsp = ramcloud.multiWrite(mwrite);
418 if (rsp != null) {
419 for (int j = 0; j < rsp.length; j++) {
420 System.out.println("multi write rsp(" + j + ") status:version " + rsp[j].getStatus() + ":" + rsp[j].getVersion());
421 }
422 }
423 }
424 for (int i = 0; i < 1000; i++) {
425 String key1 = "key1" + new Integer(i).toString();
426 String key2 = "key2" + new Integer(i).toString();
Yoshi Muroi2c170602014-02-15 08:31:28 -0800427 tableIdList[0] = tableId4;
428 keydata[0] = key1.getBytes();
429 keydataSize[0] = (short) keydata[0].length;
430 tableIdList[1] = tableId5;
431 keydata[1] = key2.getBytes();
432 keydataSize[1] = (short) keydata[1].length;
433
434 out = ramcloud.multiRead(tableIdList, keydata, keydataSize, 2);
yoshi28bac132014-01-22 11:00:17 -0800435 for (int j = 0; j < 2; j++) {
436 System.out.println("multi read object: key = [" + out[j].getKey() + "], value = [" + out[j].getValue() + "]");
437 }
438 }
Yoshi Muroi2c170602014-02-15 08:31:28 -0800439
yoshi28bac132014-01-22 11:00:17 -0800440 ramcloud.dropTable("table4");
441 ramcloud.dropTable("table5");
442 ramcloud.dropTable("table6");
443 ramcloud.disconnect();
444 }
445}