com.basho.riak.pbc.RiakClient Maven / Gradle / Ivy
/**
* This file is part of riak-java-pb-client
*
* Copyright (c) 2010 by Trifork
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package com.basho.riak.pbc;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import org.apache.commons.codec.binary.Base64;
import org.json.JSONObject;
import com.basho.riak.client.http.util.Constants;
import com.basho.riak.client.util.CharsetUtils;
import com.basho.riak.protobuf.RiakKvPB;
import com.basho.riak.protobuf.RiakKvPB.RpbCounterGetResp;
import com.basho.riak.protobuf.RiakKvPB.RpbCounterUpdateResp;
import com.basho.riak.protobuf.RiakPB;
import com.basho.riak.protobuf.RiakKvPB.RpbDelReq;
import com.basho.riak.protobuf.RiakKvPB.RpbGetClientIdResp;
import com.basho.riak.protobuf.RiakKvPB.RpbGetReq;
import com.basho.riak.protobuf.RiakKvPB.RpbGetResp;
import com.basho.riak.protobuf.RiakPB.RpbGetServerInfoResp;
import com.basho.riak.protobuf.RiakKvPB.RpbListBucketsResp;
import com.basho.riak.protobuf.RiakKvPB.RpbMapRedReq;
import com.basho.riak.protobuf.RiakKvPB.RpbPutReq;
import com.basho.riak.protobuf.RiakKvPB.RpbPutResp;
import com.google.protobuf.ByteString;
import java.util.ArrayList;
import java.util.List;
/**
* Low level protocol buffers client.
*/
public class RiakClient implements RiakMessageCodes {
private static final int BUFFER_SIZE_KB;
static {
BUFFER_SIZE_KB = Integer.parseInt(System.getProperty("com.basho.riak.client.pbc,buffer", "16"));
}
private static final RiakObject[] NO_RIAK_OBJECTS = new RiakObject[0];
private static final ByteString[] NO_BYTE_STRINGS = new ByteString[0];
//private static final String[] NO_STRINGS = new String[0];
//private static final MapReduceResponse[] NO_MAP_REDUCE_RESPONSES = new MapReduceResponse[0];
private String node;
private String serverVersion;
/**
* if this has been set (or gotten) then it will be applied to new
* connections
*/
private volatile byte[] clientId;
private final RiakConnectionPool pool;
public RiakClient(String host) throws IOException {
this(host, RiakConnection.DEFAULT_RIAK_PB_PORT);
}
public RiakClient(String host, int port) throws IOException {
this(InetAddress.getByName(host), port);
}
public RiakClient(RiakConnectionPool pool) {
this.pool = pool;
}
public RiakClient(InetAddress addr, int port) throws IOException {
this.pool = new RiakConnectionPool(0, RiakConnectionPool.LIMITLESS, addr, port, 1000, BUFFER_SIZE_KB, 1000,0);
this.pool.start();
}
public RiakClient(String host, int port, int bufferSizeKb) throws IOException {
this.pool = new RiakConnectionPool(0, RiakConnectionPool.LIMITLESS, InetAddress.getByName(host), port, 1000, bufferSizeKb, 1000,0);
this.pool.start();
}
RiakConnection getConnection() throws IOException {
RiakConnection c = pool.getConnection(clientId);
return c;
}
void release(RiakConnection c) {
pool.releaseConnection(c);
}
/**
* helper method to use a reasonable default client id
* beware, it caches the client id. If you call it multiple times on the same client
* you get the *same* id (not good for reusing a client with different ids)
*
* @throws IOException
*/
public void prepareClientID() throws IOException {
Preferences prefs = Preferences.userNodeForPackage(RiakClient.class);
String clid = prefs.get("client_id", null);
if (clid == null) {
SecureRandom sr;
try {
sr = SecureRandom.getInstance("SHA1PRNG");
// Not totally secure, but doesn't need to be
// and 100% less prone to 30 second hangs on linux jdk5
sr.setSeed(UUID.randomUUID().getLeastSignificantBits() + new Date().getTime());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] data = new byte[6];
sr.nextBytes(data);
clid = CharsetUtils.asString(Base64.encodeBase64Chunked(data), CharsetUtils.ISO_8859_1);
prefs.put("client_id", clid);
try {
prefs.flush();
} catch (BackingStoreException e) {
throw new IOException(e.toString());
}
}
setClientID(clid);
}
public void ping() throws IOException {
RiakConnection c = getConnection();
try {
c.send(MSG_PingReq);
c.receive_code(MSG_PingResp);
} finally {
release(c);
}
}
/**
* Warning: the riak client id is 4 bytes. This method silently truncates anymore bytes than that.
* Be aware that if you have two client Ids, "boris1" and "boris2" this method will leave you with 1 client id, "bori".
* Use {@link RiakClient#prepareClientID()} to generate a reasonably unique Id.
* @see RiakClient#prepareClientID()
* @param id
* @throws IOException
*/
public void setClientID(String id) throws IOException {
if(id == null || id.length() < Constants.RIAK_CLIENT_ID_LENGTH) {
throw new IllegalArgumentException("Client ID must be at least " + Constants.RIAK_CLIENT_ID_LENGTH + " bytes long");
}
setClientID(ByteString.copyFrom(CharsetUtils.utf8StringToBytes(id), 0, Constants.RIAK_CLIENT_ID_LENGTH));
}
// /////////////////////
public void setClientID(ByteString id) throws IOException {
if(id.size() > Constants.RIAK_CLIENT_ID_LENGTH) {
id = ByteString.copyFrom(id.toByteArray(), 0, Constants.RIAK_CLIENT_ID_LENGTH);
}
this.clientId = id.toByteArray();
}
public String getClientID() throws IOException {
RiakConnection c = getConnection();
try {
c.send(MSG_GetClientIdReq);
byte[] data = c.receive(MSG_GetClientIdResp);
if (data == null)
return null;
RpbGetClientIdResp res = RiakKvPB.RpbGetClientIdResp.parseFrom(data);
clientId = res.getClientId().toByteArray();
return CharsetUtils.asUTF8String(clientId);
} finally {
release(c);
}
}
public Map getServerInfo() throws IOException {
RiakConnection c = getConnection();
try {
c.send(MSG_GetServerInfoReq);
byte[] data = c.receive(MSG_GetServerInfoResp);
if (data == null)
return Collections.emptyMap();
RpbGetServerInfoResp res = RiakPB.RpbGetServerInfoResp.parseFrom(data);
if (res.hasNode()) {
this.node = res.getNode().toStringUtf8();
}
if (res.hasServerVersion()) {
this.serverVersion = res.getServerVersion().toStringUtf8();
}
Map result = new HashMap();
result.put("node", node);
result.put("server_version", serverVersion);
return result;
} finally {
release(c);
}
}
// /////////////////////
public RiakObject[] fetch(String bucket, String key, int readQuorum)
throws IOException {
return fetch(ByteString.copyFromUtf8(bucket), ByteString
.copyFromUtf8(key), readQuorum);
}
public RiakObject[] fetch(ByteString bucket, ByteString key, int readQuorum)
throws IOException {
RpbGetReq req = RiakKvPB.RpbGetReq.newBuilder().setBucket(bucket)
.setKey(key).setR(readQuorum).build();
RiakConnection c = getConnection();
try {
c.send(MSG_GetReq, req);
return processFetchReply(c, bucket, key).getObjects();
} finally {
release(c);
}
}
// All the fetch parameters
public FetchResponse fetch(String bucket, String key, FetchMeta fetchMeta) throws IOException {
return fetch(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(key), fetchMeta);
}
// All the fetch parameters
public FetchResponse fetch(ByteString bucket, ByteString key, FetchMeta fetchMeta) throws IOException {
RpbGetReq.Builder b = RiakKvPB.RpbGetReq.newBuilder().setBucket(bucket).setKey(key);
fetchMeta.write(b);
RiakConnection c = getConnection();
try {
c.send(MSG_GetReq, b.build());
return processFetchReply(c, bucket, key);
} finally {
release(c);
}
}
public RiakObject[] fetch(String bucket, String key) throws IOException {
return fetch(ByteString.copyFromUtf8(bucket), ByteString
.copyFromUtf8(key));
}
public RiakObject[] fetch(ByteString bucket, ByteString key)
throws IOException {
RpbGetReq req = RiakKvPB.RpbGetReq.newBuilder().setBucket(bucket)
.setKey(key).build();
RiakConnection c = getConnection();
try {
c.send(MSG_GetReq, req);
return processFetchReply(c, bucket, key).getObjects();
} finally {
release(c);
}
}
private FetchResponse processFetchReply(RiakConnection c, ByteString bucket, ByteString key) throws IOException {
byte[] rep = c.receive(MSG_GetResp);
if (rep == null) {
return new FetchResponse(NO_RIAK_OBJECTS, false, null);
}
RpbGetResp resp = RiakKvPB.RpbGetResp.parseFrom(rep);
int count = resp.getContentCount();
// To unify the behavior of having just a tombstone vs. siblings
// that include a tombstone, we create an empty object and mark
// it deleted
if (count == 0) {
RiakKvPB.RpbGetResp.Builder responseBuilder = resp.toBuilder();
RiakKvPB.RpbContent.Builder contentBuilder = RiakKvPB.RpbContent.getDefaultInstance().toBuilder();
contentBuilder.setDeleted(true).setValue(ByteString.EMPTY);
resp = responseBuilder.addContent(contentBuilder.build()).build();
count = 1;
}
RiakObject[] out = new RiakObject[count];
ByteString vclock = resp.getVclock();
for (int i = 0; i < count; i++) {
out[i] = new RiakObject(vclock, bucket, key, resp.getContent(i));
}
boolean unchanged = resp.getUnchanged();
return new FetchResponse(out, unchanged, vclock.toByteArray());
}
// /////////////////////
/**
*
* @param bucket
* the bucket name
* @param indexName
* the name of the index (e.g. 'user_bin')
* @param value
* the index value
* @return List
* list of keys for the index
* @throws IOException
*/
public List index(String bucket, String indexName, String value)
throws IOException {
RiakKvPB.RpbIndexReq req = RiakKvPB.RpbIndexReq.newBuilder()
.setBucket(ByteString.copyFromUtf8(bucket))
.setIndex(ByteString.copyFromUtf8(indexName))
.setKey(ByteString.copyFromUtf8(value))
.setQtype(RiakKvPB.RpbIndexReq.IndexQueryType.eq)
.build();
RiakConnection c = getConnection();
try {
c.send(MSG_IndexReq, req);
return processIndexReply(c);
} finally {
release(c);
}
}
/**
*
* @param bucket
* the bucket name
* @param indexName
* the name of the index (e.g. 'user_bin')
* @param start
* the start value in a range (e.g 'a')
* @param end
* the end value in a range (e.g. 'z')
* @return
* a List of keys for this index range
* @throws IOException
*/
public List index(String bucket, String indexName, String start, String end)
throws IOException {
RiakKvPB.RpbIndexReq req = RiakKvPB.RpbIndexReq.newBuilder()
.setBucket(ByteString.copyFromUtf8(bucket))
.setIndex(ByteString.copyFromUtf8(indexName))
.setRangeMin(ByteString.copyFromUtf8(start))
.setRangeMax(ByteString.copyFromUtf8(end))
.setQtype(RiakKvPB.RpbIndexReq.IndexQueryType.range)
.build();
RiakConnection c = getConnection();
try {
c.send(MSG_IndexReq, req);
return processIndexReply(c);
} finally {
release(c);
}
}
/**
*
* @param bucket
* the bucket name
* @param indexName
* the index (e.g. 'age_int')
* @param value
* a long for the index value
* @return
* a List of keys for this index value
* @throws IOException
*/
public List index(String bucket, String indexName, long value)
throws IOException {
RiakKvPB.RpbIndexReq req = RiakKvPB.RpbIndexReq.newBuilder()
.setBucket(ByteString.copyFromUtf8(bucket))
.setIndex(ByteString.copyFromUtf8(indexName))
.setKey(ByteString.copyFromUtf8(String.valueOf(value)))
.setQtype(RiakKvPB.RpbIndexReq.IndexQueryType.eq)
.build();
RiakConnection c = getConnection();
try {
c.send(MSG_IndexReq, req);
return processIndexReply(c);
} finally {
release(c);
}
}
/**
*
* @param bucket
* the bucket name
* @param indexName
* the index (e.g. 'age_int')
* @param start
* the start value in a range (e.g 16)
* @param end
* the end value in a range (e.g. 32)
* @return
* List of the keys for this index range
* @throws IOException
*/
public List index(String bucket, String indexName, long start, long end)
throws IOException {
RiakKvPB.RpbIndexReq req = RiakKvPB.RpbIndexReq.newBuilder()
.setBucket(ByteString.copyFromUtf8(bucket))
.setIndex(ByteString.copyFromUtf8(indexName))
.setRangeMin(ByteString.copyFromUtf8(String.valueOf(start)))
.setRangeMax(ByteString.copyFromUtf8(String.valueOf(end)))
.setQtype(RiakKvPB.RpbIndexReq.IndexQueryType.range)
.build();
RiakConnection c = getConnection();
try {
c.send(MSG_IndexReq, req);
return processIndexReply(c);
} finally {
release(c);
}
}
private List processIndexReply(RiakConnection c) throws IOException {
byte[] rep = c.receive(MSG_IndexResp);
if (null == rep) {
return Collections.EMPTY_LIST;
}
RiakKvPB.RpbIndexResp resp = RiakKvPB.RpbIndexResp.parseFrom(rep);
List keys = new ArrayList(resp.getKeysCount());
for (ByteString bs : resp.getKeysList()) {
keys.add(bs.toStringUtf8());
}
return keys;
}
/**
* Streaming secondary index query.
*
* Requires Riak 1.4
*
* Note that you *must* call {@link IndexSource#close() } if you do not
* iterate through the entire result set.
*
* @param request a {@link IndexRequest} containing the query parameters
* @return A streaming {@link IndexSource}
* @throws IOException
*/
public IndexSource index(IndexRequest request) throws IOException {
RiakKvPB.RpbIndexReq req = request.buildProtocolBufferReq();
RiakConnection c = getConnection();
c.send(MSG_IndexReq, req);
return new IndexSource(this, c, request);
}
// /////////////////////
public Long incrementCounter(String bucket, String counter, long increment, RequestMeta meta)
throws IOException {
return incrementCounter(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(counter), increment, meta);
}
public Long incrementCounter(ByteString bucket, ByteString counter, long increment, RequestMeta meta)
throws IOException {
RiakConnection c = getConnection();
RiakKvPB.RpbCounterUpdateReq.Builder builder =
RiakKvPB.RpbCounterUpdateReq.newBuilder()
.setBucket(bucket)
.setKey(counter)
.setAmount(increment);
if (meta != null) {
meta.prepareCounter(builder);
}
try {
c.send(MSG_CounterUpdateReq, builder.build());
byte[] r = c.receive(MSG_CounterUpdateResp);
if (r == null) {
return null;
} else {
RpbCounterUpdateResp resp = RpbCounterUpdateResp.parseFrom(r);
return resp.getValue();
}
} finally {
release(c);
}
}
public Long fetchCounter(String bucket, String counter, FetchMeta meta) throws IOException {
return fetchCounter(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(counter), meta);
}
public Long fetchCounter(ByteString bucket, ByteString counter, FetchMeta meta) throws IOException {
RiakConnection c = getConnection();
RiakKvPB.RpbCounterGetReq.Builder builder =
RiakKvPB.RpbCounterGetReq.newBuilder()
.setBucket(bucket)
.setKey(counter);
if (meta != null) {
meta.writeCounter(builder);
}
try {
c.send(MSG_CounterGetReq, builder.build());
byte[] r = c.receive(MSG_CounterGetResp);
if (r == null) {
return null;
} else {
RpbCounterGetResp resp = RpbCounterGetResp.parseFrom(r);
return resp.getValue();
}
} finally {
release(c);
}
}
// /////////////////////
public ByteString[] store(RiakObject[] values, RequestMeta meta)
throws IOException {
RiakConnection c = getConnection();
try {
BulkReader reader = new BulkReader(c, values.length);
Thread worker = new Thread(reader);
worker.start();
DataOutputStream dout = c.getOutputStream();
for (int i = 0; i < values.length; i++) {
RiakObject value = values[i];
RiakKvPB.RpbPutReq.Builder builder = RiakKvPB.RpbPutReq.newBuilder()
.setBucket(value.getBucketBS())
.setKey(value.getKeyBS()).setContent(
value.buildContent());
if (value.getVclock() != null) {
builder.setVclock(value.getVclock());
}
builder.setReturnBody(true);
if (meta != null) {
if (meta.writeQuorum != null) {
builder.setW(meta.writeQuorum.intValue());
}
if (meta.durableWriteQuorum != null) {
builder.setDw(meta.durableWriteQuorum.intValue());
}
if (meta.asis != null) {
builder.setAsis(meta.asis.booleanValue());
}
}
RpbPutReq req = builder.build();
int len = req.getSerializedSize();
dout.writeInt(len + 1);
dout.write(MSG_PutReq);
req.writeTo(dout);
}
dout.flush();
try {
worker.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return reader.vclocks;
} finally {
release(c);
}
}
class BulkReader implements Runnable {
private ByteString[] vclocks;
private final RiakConnection c;
public BulkReader(RiakConnection c, int count) {
this.c = c;
this.vclocks = new ByteString[count];
}
public void run() {
try {
for (int i = 0; i < vclocks.length; i++) {
byte[] data = c.receive(MSG_PutResp);
if (data != null) {
RpbPutResp resp = RiakKvPB.RpbPutResp.parseFrom(data);
if (resp.hasVclock()) {
vclocks[i] = resp.getVclock();
}
}
}
} catch (IOException e) {
// TODO at least log it
e.printStackTrace();
}
}
}
public void store(RiakObject value) throws IOException {
store(value, null);
}
public RiakObject[] store(RiakObject value, IRequestMeta meta)
throws IOException {
RiakKvPB.RpbPutReq.Builder builder = RiakKvPB.RpbPutReq.newBuilder().setBucket(
value.getBucketBS()).setContent(
value.buildContent());
if (value.getKeyBS() != null) {
builder.setKey(value.getKeyBS());
}
if (value.getVclock() != null) {
builder.setVclock(value.getVclock());
}
if (meta != null) {
meta.preparePut(builder);
}
RiakConnection c = getConnection();
try {
c.send(MSG_PutReq, builder.build());
byte[] r = c.receive(MSG_PutResp);
if (r == null) {
return NO_RIAK_OBJECTS;
}
RpbPutResp resp = RiakKvPB.RpbPutResp.parseFrom(r);
RiakObject[] res = new RiakObject[resp.getContentCount()];
ByteString vclock = resp.getVclock();
// The key parameter will be set only if the server generated a
// key for the object so we check and set it accordingly
for (int i = 0; i < res.length; i++) {
res[i] = new RiakObject(vclock, value.getBucketBS(),
(resp.hasKey()) ? resp.getKey() : value.getKeyBS(),
resp.getContent(i));
}
return res;
} finally {
release(c);
}
}
// /////////////////////
public void delete(String bucket, String key, DeleteMeta deleteMeta) throws IOException {
delete(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(key), deleteMeta);
}
public void delete(ByteString bucket, ByteString key, DeleteMeta deleteMeta) throws IOException {
RpbDelReq.Builder builder = RiakKvPB.RpbDelReq.newBuilder().setBucket(bucket)
.setKey(key);
deleteMeta.write(builder);
RiakConnection c = getConnection();
try {
c.send(MSG_DelReq, builder.build());
c.receive_code(MSG_DelResp);
} finally {
release(c);
}
}
public void delete(String bucket, String key, int rw) throws IOException {
delete(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(key),
rw);
}
public void delete(ByteString bucket, ByteString key, int rw)
throws IOException {
RpbDelReq req = RiakKvPB.RpbDelReq.newBuilder().setBucket(bucket)
.setKey(key).setRw(rw).build();
RiakConnection c = getConnection();
try {
c.send(MSG_DelReq, req);
c.receive_code(MSG_DelResp);
} finally {
release(c);
}
}
public void delete(String bucket, String key) throws IOException {
delete(ByteString.copyFromUtf8(bucket), ByteString.copyFromUtf8(key));
}
public void delete(ByteString bucket, ByteString key) throws IOException {
RpbDelReq req = RiakKvPB.RpbDelReq.newBuilder().setBucket(bucket)
.setKey(key).build();
RiakConnection c = getConnection();
try {
c.send(MSG_DelReq, req);
c.receive_code(MSG_DelResp);
} finally {
release(c);
}
}
public ByteString[] listBuckets() throws IOException {
byte[] data;
RiakConnection c = getConnection();
try {
c.send(MSG_ListBucketsReq);
data = c.receive(MSG_ListBucketsResp);
if (data == null) {
return NO_BYTE_STRINGS;
}
} finally {
release(c);
}
RpbListBucketsResp resp = RiakKvPB.RpbListBucketsResp.parseFrom(data);
ByteString[] out = new ByteString[resp.getBucketsCount()];
for (int i = 0; i < out.length; i++) {
out[i] = resp.getBuckets(i);
}
return out;
}
public BucketSource listBucketsStreaming() throws IOException {
RiakConnection c = getConnection();
c.send(MSG_ListBucketsReq, RiakKvPB.RpbListBucketsReq.newBuilder().setStream(true).build());
return new BucketSource(this, c);
}
public BucketProperties getBucketProperties(ByteString bucket)
throws IOException {
RiakConnection c = getConnection();
try {
c.send(MSG_GetBucketReq, RiakPB.RpbGetBucketReq.newBuilder()
.setBucket(bucket).build());
byte[] data = c.receive(MSG_GetBucketResp);
BucketProperties bp = new BucketProperties();
if (data == null) {
return bp;
}
bp.init(RiakPB.RpbGetBucketResp.parseFrom(data));
return bp;
} finally {
release(c);
}
}
public void setBucketProperties(ByteString bucket, BucketProperties props)
throws IOException {
RiakPB.RpbSetBucketReq req = RiakPB.RpbSetBucketReq.newBuilder().setBucket(
bucket).setProps(props.build()).build();
RiakConnection c = getConnection();
try {
c.send(MSG_SetBucketReq, req);
c.receive_code(MSG_SetBucketResp);
} finally {
release(c);
}
}
public void resetBucketProperties(ByteString bucket) throws IOException {
RiakPB.RpbResetBucketReq req = RiakPB.RpbResetBucketReq.newBuilder().setBucket(
bucket).build();
RiakConnection c = getConnection();
try {
c.send(MSG_ResetBucketReq, req);
c.receive_code(MSG_ResetBucketResp);
} finally {
release(c);
}
}
// /////////////////////
public KeySource listKeys(ByteString bucket) throws IOException {
RiakConnection c = getConnection();
c.send(MSG_ListKeysReq, RiakKvPB.RpbListKeysReq.newBuilder().setBucket(
bucket).build());
return new KeySource(this, c);
}
// /////////////////////
public MapReduceResponseSource mapReduce(JSONObject obj) throws IOException {
return mapReduce(ByteString.copyFromUtf8(obj.toString()),
new RequestMeta().contentType("application/json"));
}
public MapReduceResponseSource mapReduce(String request,
IRequestMeta meta) throws IOException {
return mapReduce(ByteString.copyFromUtf8(request), meta);
}
public MapReduceResponseSource mapReduce(ByteString request,
IRequestMeta meta) throws IOException {
RiakConnection c = getConnection();
ByteString contentType = meta.getContentType();
if (contentType == null) {
throw new IllegalArgumentException("no content type");
}
RpbMapRedReq req = RiakKvPB.RpbMapRedReq.newBuilder().setRequest(request)
.setContentType(meta.getContentType()).build();
c.send(MSG_MapRedReq, req);
return new MapReduceResponseSource(this, c, contentType);
}
public void shutdown()
{
pool.shutdown();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy