commit
26f0a78b03
@ -0,0 +1,162 @@ |
|||||||
|
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
package org.rocksdb.test; |
||||||
|
|
||||||
|
import org.rocksdb.RocksDB; |
||||||
|
import org.rocksdb.RocksDBException; |
||||||
|
import org.rocksdb.WriteBatch; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class WriteBatchHandlerTest { |
||||||
|
static { |
||||||
|
RocksDB.loadLibrary(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(final String[] args) throws IOException, RocksDBException { |
||||||
|
|
||||||
|
// setup test data
|
||||||
|
final List<Tuple<Action, Tuple<byte[], byte[]>>> testEvents = new ArrayList<>(); |
||||||
|
testEvents.add(new Tuple<>(Action.DELETE, |
||||||
|
new Tuple<byte[], byte[]>("k0".getBytes(), null))); |
||||||
|
testEvents.add(new Tuple<>(Action.PUT, |
||||||
|
new Tuple<>("k1".getBytes(), "v1".getBytes()))); |
||||||
|
testEvents.add(new Tuple<>(Action.PUT, |
||||||
|
new Tuple<>("k2".getBytes(), "v2".getBytes()))); |
||||||
|
testEvents.add(new Tuple<>(Action.PUT, |
||||||
|
new Tuple<>("k3".getBytes(), "v3".getBytes()))); |
||||||
|
testEvents.add(new Tuple<>(Action.LOG, |
||||||
|
new Tuple<byte[], byte[]>(null, "log1".getBytes()))); |
||||||
|
testEvents.add(new Tuple<>(Action.MERGE, |
||||||
|
new Tuple<>("k2".getBytes(), "v22".getBytes()))); |
||||||
|
testEvents.add(new Tuple<>(Action.DELETE, |
||||||
|
new Tuple<byte[], byte[]>("k3".getBytes(), null))); |
||||||
|
|
||||||
|
// load test data to the write batch
|
||||||
|
final WriteBatch batch = new WriteBatch(); |
||||||
|
for(final Tuple<Action, Tuple<byte[], byte[]>> testEvent : testEvents) { |
||||||
|
final Tuple<byte[], byte[]> data = testEvent.value; |
||||||
|
switch(testEvent.key) { |
||||||
|
|
||||||
|
case PUT: |
||||||
|
batch.put(data.key, data.value); |
||||||
|
break; |
||||||
|
|
||||||
|
case MERGE: |
||||||
|
batch.merge(data.key, data.value); |
||||||
|
break; |
||||||
|
|
||||||
|
case DELETE: |
||||||
|
batch.remove(data.key); |
||||||
|
break; |
||||||
|
|
||||||
|
case LOG: |
||||||
|
batch.putLogData(data.value); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// attempt to read test data back from the WriteBatch by iterating with a handler
|
||||||
|
final CapturingWriteBatchHandler handler = new CapturingWriteBatchHandler(); |
||||||
|
batch.iterate(handler); |
||||||
|
|
||||||
|
// compare the results to the test data
|
||||||
|
final List<Tuple<Action, Tuple<byte[], byte[]>>> actualEvents = handler.getEvents(); |
||||||
|
assert(testEvents.size() == actualEvents.size()); |
||||||
|
|
||||||
|
for(int i = 0; i < testEvents.size(); i++) { |
||||||
|
assert(equals(testEvents.get(i), actualEvents.get(i))); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println("Passed WriteBatchHandler Test"); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean equals(final Tuple<Action, Tuple<byte[], byte[]>> expected, |
||||||
|
final Tuple<Action, Tuple<byte[], byte[]>> actual) { |
||||||
|
if(!expected.key.equals(actual.key)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
final Tuple<byte[], byte[]> expectedData = expected.value; |
||||||
|
final Tuple<byte[], byte[]> actualData = actual.value; |
||||||
|
|
||||||
|
if(equals(expectedData.key, actualData.key)) { |
||||||
|
return equals(expectedData.value, actualData.value); |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean equals(byte[] expected, byte[] actual) { |
||||||
|
if(expected != null) { |
||||||
|
return Arrays.equals(expected, actual); |
||||||
|
} else { |
||||||
|
return actual == null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class Tuple<K, V> { |
||||||
|
public final K key; |
||||||
|
public final V value; |
||||||
|
|
||||||
|
public Tuple(final K key, final V value) { |
||||||
|
this.key = key; |
||||||
|
this.value = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Enumeration of Write Batch |
||||||
|
* event actions |
||||||
|
*/ |
||||||
|
private enum Action { |
||||||
|
PUT, |
||||||
|
MERGE, |
||||||
|
DELETE, |
||||||
|
LOG |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple WriteBatch Handler which adds a record |
||||||
|
* of each event that it receives to a list |
||||||
|
*/ |
||||||
|
private static class CapturingWriteBatchHandler extends WriteBatch.Handler { |
||||||
|
|
||||||
|
private final List<Tuple<Action, Tuple<byte[], byte[]>>> events = new ArrayList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a copy of the current events list |
||||||
|
* |
||||||
|
* @return a list of the events which have happened upto now |
||||||
|
*/ |
||||||
|
public List<Tuple<Action, Tuple<byte[], byte[]>>> getEvents() { |
||||||
|
return new ArrayList<>(events); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void put(final byte[] key, final byte[] value) { |
||||||
|
events.add(new Tuple<>(Action.PUT, new Tuple<>(key, value))); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void merge(final byte[] key, final byte[] value) { |
||||||
|
events.add(new Tuple<>(Action.MERGE, new Tuple<>(key, value))); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void delete(final byte[] key) { |
||||||
|
events.add(new Tuple<>(Action.DELETE, new Tuple<byte[], byte[]>(key, null))); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void logData(final byte[] blob) { |
||||||
|
events.add(new Tuple<>(Action.LOG, new Tuple<byte[], byte[]>(null, blob))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// This file implements the callback "bridge" between Java and C++ for
|
||||||
|
// rocksdb::Comparator.
|
||||||
|
|
||||||
|
#include "rocksjni/writebatchhandlerjnicallback.h" |
||||||
|
#include "rocksjni/portal.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
WriteBatchHandlerJniCallback::WriteBatchHandlerJniCallback( |
||||||
|
JNIEnv* env, jobject jWriteBatchHandler) |
||||||
|
: m_env(env) { |
||||||
|
|
||||||
|
// Note: we want to access the Java WriteBatchHandler instance
|
||||||
|
// across multiple method calls, so we create a global ref
|
||||||
|
m_jWriteBatchHandler = env->NewGlobalRef(jWriteBatchHandler); |
||||||
|
|
||||||
|
m_jPutMethodId = WriteBatchHandlerJni::getPutMethodId(env); |
||||||
|
m_jMergeMethodId = WriteBatchHandlerJni::getMergeMethodId(env); |
||||||
|
m_jDeleteMethodId = WriteBatchHandlerJni::getDeleteMethodId(env); |
||||||
|
m_jLogDataMethodId = WriteBatchHandlerJni::getLogDataMethodId(env); |
||||||
|
m_jContinueMethodId = WriteBatchHandlerJni::getContinueMethodId(env); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteBatchHandlerJniCallback::Put(const Slice& key, const Slice& value) { |
||||||
|
const jbyteArray j_key = sliceToJArray(key); |
||||||
|
const jbyteArray j_value = sliceToJArray(value); |
||||||
|
|
||||||
|
m_env->CallVoidMethod( |
||||||
|
m_jWriteBatchHandler, |
||||||
|
m_jPutMethodId, |
||||||
|
j_key, |
||||||
|
j_value); |
||||||
|
|
||||||
|
m_env->DeleteLocalRef(j_value); |
||||||
|
m_env->DeleteLocalRef(j_key); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteBatchHandlerJniCallback::Merge(const Slice& key, const Slice& value) { |
||||||
|
const jbyteArray j_key = sliceToJArray(key); |
||||||
|
const jbyteArray j_value = sliceToJArray(value); |
||||||
|
|
||||||
|
m_env->CallVoidMethod( |
||||||
|
m_jWriteBatchHandler, |
||||||
|
m_jMergeMethodId, |
||||||
|
j_key, |
||||||
|
j_value); |
||||||
|
|
||||||
|
m_env->DeleteLocalRef(j_value); |
||||||
|
m_env->DeleteLocalRef(j_key); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteBatchHandlerJniCallback::Delete(const Slice& key) { |
||||||
|
const jbyteArray j_key = sliceToJArray(key); |
||||||
|
|
||||||
|
m_env->CallVoidMethod( |
||||||
|
m_jWriteBatchHandler, |
||||||
|
m_jDeleteMethodId, |
||||||
|
j_key); |
||||||
|
|
||||||
|
m_env->DeleteLocalRef(j_key); |
||||||
|
} |
||||||
|
|
||||||
|
void WriteBatchHandlerJniCallback::LogData(const Slice& blob) { |
||||||
|
const jbyteArray j_blob = sliceToJArray(blob); |
||||||
|
|
||||||
|
m_env->CallVoidMethod( |
||||||
|
m_jWriteBatchHandler, |
||||||
|
m_jLogDataMethodId, |
||||||
|
j_blob); |
||||||
|
|
||||||
|
m_env->DeleteLocalRef(j_blob); |
||||||
|
} |
||||||
|
|
||||||
|
bool WriteBatchHandlerJniCallback::Continue() { |
||||||
|
jboolean jContinue = m_env->CallBooleanMethod( |
||||||
|
m_jWriteBatchHandler, |
||||||
|
m_jContinueMethodId); |
||||||
|
|
||||||
|
return static_cast<bool>(jContinue == JNI_TRUE); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a Java Byte Array from the data in a Slice |
||||||
|
* |
||||||
|
* When calling this function |
||||||
|
* you must remember to call env->DeleteLocalRef |
||||||
|
* on the result after you have finished with it |
||||||
|
*/ |
||||||
|
jbyteArray WriteBatchHandlerJniCallback::sliceToJArray(const Slice& s) { |
||||||
|
jbyteArray ja = m_env->NewByteArray(s.size()); |
||||||
|
m_env->SetByteArrayRegion( |
||||||
|
ja, 0, s.size(), |
||||||
|
reinterpret_cast<const jbyte*>(s.data())); |
||||||
|
return ja; |
||||||
|
} |
||||||
|
|
||||||
|
WriteBatchHandlerJniCallback::~WriteBatchHandlerJniCallback() { |
||||||
|
m_env->DeleteGlobalRef(m_jWriteBatchHandler); |
||||||
|
} |
||||||
|
} // namespace rocksdb
|
@ -0,0 +1,46 @@ |
|||||||
|
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
//
|
||||||
|
// This file implements the callback "bridge" between Java and C++ for
|
||||||
|
// rocksdb::WriteBatch::Handler.
|
||||||
|
|
||||||
|
#ifndef JAVA_ROCKSJNI_WRITEBATCHHANDLERJNICALLBACK_H_ |
||||||
|
#define JAVA_ROCKSJNI_WRITEBATCHHANDLERJNICALLBACK_H_ |
||||||
|
|
||||||
|
#include <jni.h> |
||||||
|
#include "rocksdb/write_batch.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
/**
|
||||||
|
* This class acts as a bridge between C++ |
||||||
|
* and Java. The methods in this class will be |
||||||
|
* called back from the RocksDB storage engine (C++) |
||||||
|
* which calls the appropriate Java method. |
||||||
|
* This enables Write Batch Handlers to be implemented in Java. |
||||||
|
*/ |
||||||
|
class WriteBatchHandlerJniCallback : public WriteBatch::Handler { |
||||||
|
public: |
||||||
|
WriteBatchHandlerJniCallback( |
||||||
|
JNIEnv* env, jobject jWriteBackHandler); |
||||||
|
~WriteBatchHandlerJniCallback(); |
||||||
|
void Put(const Slice& key, const Slice& value); |
||||||
|
void Merge(const Slice& key, const Slice& value); |
||||||
|
void Delete(const Slice& key); |
||||||
|
void LogData(const Slice& blob); |
||||||
|
bool Continue(); |
||||||
|
|
||||||
|
private: |
||||||
|
JNIEnv* m_env; |
||||||
|
jobject m_jWriteBatchHandler; |
||||||
|
jbyteArray sliceToJArray(const Slice& s); |
||||||
|
jmethodID m_jPutMethodId; |
||||||
|
jmethodID m_jMergeMethodId; |
||||||
|
jmethodID m_jDeleteMethodId; |
||||||
|
jmethodID m_jLogDataMethodId; |
||||||
|
jmethodID m_jContinueMethodId; |
||||||
|
}; |
||||||
|
} // namespace rocksdb
|
||||||
|
|
||||||
|
#endif // JAVA_ROCKSJNI_WRITEBATCHHANDLERJNICALLBACK_H_
|
Loading…
Reference in new issue