package org.handwerkszeug.riak.transport.rest; import static org.handwerkszeug.riak.util.Validation.notNull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.node.ObjectNode; import org.handwerkszeug.riak.Markers; import org.handwerkszeug.riak.RiakException; import org.handwerkszeug.riak._; import org.handwerkszeug.riak.mapreduce.MapReduceQueryBuilder; import org.handwerkszeug.riak.mapreduce.MapReduceResponse; import org.handwerkszeug.riak.mapreduce.internal.DefaultMapReduceQueryBuilder; import org.handwerkszeug.riak.mapreduce.internal.MapReduceQueryContext; import org.handwerkszeug.riak.model.AbstractRiakObject; import org.handwerkszeug.riak.model.Bucket; import org.handwerkszeug.riak.model.DefaultGetOptions; import org.handwerkszeug.riak.model.DefaultRiakObject; import org.handwerkszeug.riak.model.GetOptions; import org.handwerkszeug.riak.model.KeyResponse; import org.handwerkszeug.riak.model.Location; import org.handwerkszeug.riak.model.PostOptions; import org.handwerkszeug.riak.model.PutOptions; import org.handwerkszeug.riak.model.Quorum; import org.handwerkszeug.riak.model.Range; import org.handwerkszeug.riak.model.RiakFuture; import org.handwerkszeug.riak.model.RiakObject; import org.handwerkszeug.riak.op.RiakResponseHandler; import org.handwerkszeug.riak.op.SiblingHandler; import org.handwerkszeug.riak.transport.internal.AbstractCompletionChannelHandler; import org.handwerkszeug.riak.transport.internal.ChunkedMessageAggregator; import org.handwerkszeug.riak.transport.internal.Completion; import org.handwerkszeug.riak.transport.internal.CompletionSupport; import org.handwerkszeug.riak.transport.internal.CountDownRiakFuture; import org.handwerkszeug.riak.transport.internal.MessageHandler; import org.handwerkszeug.riak.transport.rest.internal.BucketHolder; import org.handwerkszeug.riak.transport.rest.internal.ContinuousMessageHandler; import org.handwerkszeug.riak.transport.rest.internal.RequestFactory; import org.handwerkszeug.riak.transport.rest.internal.RestErrorResponse; import org.handwerkszeug.riak.transport.rest.internal.RestMapReduceResponse; import org.handwerkszeug.riak.transport.rest.internal.SimpleMessageHandler; import org.handwerkszeug.riak.util.JsonUtil; import org.handwerkszeug.riak.util.NettyUtil; import org.handwerkszeug.riak.util.StringUtil; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBufferInputStream; import org.jboss.netty.buffer.ChannelBufferOutputStream; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandler; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.handler.codec.http.HttpChunk; import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpMessage; import org.jboss.netty.handler.codec.http.HttpMethod; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.MultipartResponseDecoder; import org.jboss.netty.handler.codec.http.PartMessage; import org.jboss.netty.handler.stream.ChunkedStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author taichi */ public class RestRiakOperations implements HttpRiakOperations, Completion { static final Logger LOG = LoggerFactory.getLogger(RestRiakOperations.class); RestRiakConfig config; // for luwak support. Channel channel; CompletionSupport support; RequestFactory factory; public RestRiakOperations(String host, RestRiakConfig config, Channel channel) { this(host, config, channel, new RequestFactory(host, config)); } public RestRiakOperations(String host, RestRiakConfig config, Channel channel, RequestFactory factory) { notNull(host, "host"); notNull(channel, "channel"); this.channel = channel; this.support = new CompletionSupport(channel); this.config = config; this.factory = factory; } @Override public RiakFuture ping(final RiakResponseHandler<String> handler) { notNull(handler, "handler"); HttpRequest request = this.factory.newPingRequest(); final String procedure = "ping"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isSuccessful(response.getStatus())) { handler.handle(RestRiakOperations.this.support .newResponse("pong")); future.setSuccess(); return true; } } return false; } }); } @Override public void setClientId(String clientId) { this.factory.setClientId(clientId); } @Override public String getClientId() { return this.factory.getClientId(); } @Override public RiakFuture listBuckets( final RiakResponseHandler<List<String>> handler) { notNull(handler, "handler"); HttpRequest request = this.factory.newListBucketsRequest(); final String procedure = "listBuckets"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; ChannelBuffer buffer = response.getContent(); ObjectNode node = to(buffer); if (node != null) { List<String> list = JsonUtil.to(node.get("buckets")); handler.handle(RestRiakOperations.this.support .newResponse(list)); future.setSuccess(); return true; } } return false; } }); } @SuppressWarnings("unchecked") <T extends JsonNode> T to(ChannelBuffer buffer, T... t) { try { if (buffer != null && buffer.readable()) { ObjectMapper objectMapper = new ObjectMapper(); JsonNode node = objectMapper .readTree(new ChannelBufferInputStream(buffer)); Class<?> clazz = t.getClass().getComponentType(); if (clazz.isAssignableFrom(node.getClass())) { return (T) node; } } } catch (IOException e) { LOG.error(Markers.BOUNDARY, e.getMessage(), e); throw new RiakException(e); } return null; } @Override public RiakFuture listKeys(String bucket, final RiakResponseHandler<KeyResponse> handler) { notNull(bucket, "bucket"); notNull(handler, "handler"); HttpRequest request = this.factory.newListKeysRequest(bucket); final String procedure = "listKeys"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isSuccessful(response.getStatus())) { boolean done = response.isChunked() == false; if (done) { _listKeys(response.getContent(), handler); future.setSuccess(); } return done; } } else if (receive instanceof HttpChunk) { HttpChunk chunk = (HttpChunk) receive; boolean done = chunk.isLast(); if (done) { future.setSuccess(); } else { _listKeys(chunk.getContent(), handler); } return done; } return false; } }); } protected void _listKeys(ChannelBuffer buffer, final RiakResponseHandler<KeyResponse> handler) throws Exception { ObjectNode on = to(buffer); if (on != null) { JsonNode node = on.get("keys"); if (node != null) { List<String> list = JsonUtil.to(node); KeyResponse kr = new KeyResponse(list, list.isEmpty()); handler.handle(this.support.newResponse(kr)); } } } @Override public RiakFuture getBucket(String bucket, final RiakResponseHandler<Bucket> handler) { notNull(bucket, "bucket"); notNull(handler, "handler"); HttpRequest request = this.factory.newGetBucketRequest(bucket); final String procedure = "getBucket"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isSuccessful(response.getStatus())) { ObjectMapper objectMapper = new ObjectMapper(); BucketHolder holder = objectMapper.readValue( new ChannelBufferInputStream(response .getContent()), BucketHolder.class); handler.handle(RestRiakOperations.this.support .newResponse(holder.props)); future.setSuccess(); return true; } } return false; } }); } @Override public RiakFuture setBucket(Bucket bucket, final RiakResponseHandler<_> handler) { notNull(bucket, "bucket"); notNull(handler, "handler"); HttpRequest request = this.factory.newSetBucketRequest(bucket); final String procedure = "setBucket"; return handle(procedure, request, handler); } @Override public RiakFuture get(Location location, RiakResponseHandler<RiakObject<byte[]>> handler) { notNull(location, "location"); notNull(handler, "handler"); return getSingle(this.factory.newGetRequst(location), location, handler); } @Override public RiakFuture get(Location location, GetOptions options, RiakResponseHandler<RiakObject<byte[]>> handler) { notNull(location, "location"); notNull(options, "options"); notNull(handler, "handler"); return getSingle(this.factory.newGetRequst(location, options), location, handler); } protected RiakFuture getSingle(HttpRequest request, final Location location, final RiakResponseHandler<RiakObject<byte[]>> handler) { String procedure = "get/single"; return handle(procedure, request, handler, new ChunkedMessageAggregator(procedure, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { HttpResponse response = (HttpResponse) receive; RiakObject<byte[]> ro = RestRiakOperations.this.factory .convert(response, response.getContent(), location); handler.handle(RestRiakOperations.this.support .newResponse(ro)); future.setSuccess(); return true; } })); } @Override public RiakFuture get(final Location location, GetOptions options, final SiblingHandler handler) { notNull(location, "location"); notNull(options, "options"); notNull(handler, "handler"); HttpRequest request = this.factory.newGetRequst(location, options); request.setHeader(HttpHeaders.Names.ACCEPT, RiakHttpHeaders.MULTI_PART); final String procedure = "get/sibling"; return handle(procedure, request, handler, new MessageHandler() { String vclock; @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; this.vclock = response .getHeader(RiakHttpHeaders.VECTOR_CLOCK); handler.begin(); return false; } else if (receive instanceof PartMessage) { PartMessage part = (PartMessage) receive; boolean done = part.isLast(); part.setHeader(RiakHttpHeaders.VECTOR_CLOCK, this.vclock); if (done) { handler.end(); future.setSuccess(); } else { RiakObject<byte[]> ro = RestRiakOperations.this.factory .convert(part, part.getContent(), location); handler.handle(RestRiakOperations.this.support .newResponse(ro)); } return done; } return false; } }); } @Override public RiakFuture put(RiakObject<byte[]> content, final RiakResponseHandler<_> handler) { notNull(content, "content"); notNull(handler, "handler"); HttpRequest request = this.factory.newPutRequest(content); final String procedure = "put"; return handle(procedure, request, handler); } /** * if returning body has sibling then call get with sibling call * automatically. */ @Override public RiakFuture put(final RiakObject<byte[]> content, final PutOptions options, final SiblingHandler handler) { notNull(content, "content"); notNull(options, "options"); notNull(handler, "handler"); HttpRequest request = this.factory.newPutRequest(content, options); final String procedure = "put/sibling"; final CountDownRiakFuture future = this.support .newRiakFuture(procedure); ChannelHandler internal = new AbstractCompletionChannelHandler<RiakObject<byte[]>>( this.support, procedure, handler, future) { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Object receive = e.getMessage(); if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isError(response.getStatus())) { try { handler.onError(new RestErrorResponse(response)); } finally { future.finished(); RestRiakOperations.this.support.responseComplete(); } } else if (NettyUtil.isSuccessful(response.getStatus())) { try { try { handler.begin(); RiakObject<byte[]> ro = content; if (options.getReturnBody()) { ro = RestRiakOperations.this.factory .convert(response, response.getContent(), content.getLocation()); } handler.handle(RestRiakOperations.this.support .newResponse(ro)); } finally { handler.end(); } future.setSuccess(); } finally { RestRiakOperations.this.support.responseComplete(); } } else if (response.getStatus().getCode() == 300) { RestRiakOperations.this.support .decrementProgress(procedure); dispatchToGetSibling(content.getLocation(), options, handler, future); } } } }; return this.support.handle(procedure, request, handler, internal, future); } protected void dispatchToGetSibling(final Location location, final PutOptions options, final SiblingHandler handler, final CountDownRiakFuture future) { LOG.debug(Markers.DETAIL, "dispatchToGetSibling"); GetOptions go = new DefaultGetOptions() { @Override public String getIfNoneMatch() { return options.getIfNoneMatch(); } @Override public String getIfMatch() { return options.getIfMatch(); } @Override public Date getIfModifiedSince() { return options.getIfModifiedSince(); } }; HttpRequest request = this.factory.newGetRequst(location, go); request.setHeader(HttpHeaders.Names.ACCEPT, RiakHttpHeaders.MULTI_PART); final String procedure = "put/sibling>get"; future.setName(procedure); ChannelHandler internal = new AbstractCompletionChannelHandler<RiakObject<byte[]>>( this.support, procedure, handler, future) { String vclock; Exception failed; @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Object receive = e.getMessage(); if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isError(response.getStatus())) { try { handler.onError(new RestErrorResponse(response)); } finally { future.finished(); RestRiakOperations.this.support.responseComplete(); } } else { this.vclock = response .getHeader(RiakHttpHeaders.VECTOR_CLOCK); handler.begin(); } } else if (receive instanceof PartMessage) { PartMessage part = (PartMessage) receive; boolean done = part.isLast(); part.setHeader(RiakHttpHeaders.VECTOR_CLOCK, this.vclock); if (done) { try { handler.end(); if (this.failed == null) { future.setSuccess(); } else { future.setFailure(this.failed); } } finally { RestRiakOperations.this.support.responseComplete(); } } else { RiakObject<byte[]> ro = RestRiakOperations.this.factory .convert(part, part.getContent(), location); try { handler.handle(RestRiakOperations.this.support .newResponse(ro)); } catch (Exception ex) { this.failed = ex; LOG.error(Markers.DESIGN, ex.getMessage(), ex); } } } } }; this.support.handle(procedure, request, handler, internal, future); } @Override public RiakFuture post(RiakObject<byte[]> content, final RiakResponseHandler<RiakObject<byte[]>> handler) { notNull(content, "content"); notNull(handler, "handler"); HttpRequest request = this.factory.newPostRequest(content); final String procedure = "post"; return _post(procedure, content, handler, request); } private RiakFuture _post(final String procedure, RiakObject<byte[]> content, final RiakResponseHandler<RiakObject<byte[]>> handler, HttpRequest request) { final RiakObject<byte[]> copied = new DefaultRiakObject(content); return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isSuccessful(response.getStatus())) { Location location = to(response); if (location != null) { copied.setLocation(location); handler.handle(RestRiakOperations.this.support .newResponse(copied)); future.setSuccess(); return true; } } } return false; } }); } protected Location to(HttpMessage response) { String loc = response.getHeader(HttpHeaders.Names.LOCATION); if (loc != null && loc.isEmpty() == false) { String[] slashed = loc.split("/"); if (slashed != null && 3 < slashed.length) { Location location = new Location(slashed[2], slashed[3]); return location; } } return null; } @Override public RiakFuture post(RiakObject<byte[]> content, PostOptions options, final RiakResponseHandler<RiakObject<byte[]>> handler) { notNull(content, "content"); notNull(options, "options"); notNull(handler, "handler"); HttpRequest request = this.factory.newPostRequest(content, options); if (options.getReturnBody() == false) { return _post("post/opt", content, handler, request); } final String procedure = "post/returnbody"; return handle(procedure, request, handler, new ChunkedMessageAggregator(procedure, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { HttpResponse response = (HttpResponse) receive; Location location = to(response); RiakObject<byte[]> ro = RestRiakOperations.this.factory .convert(response, response.getContent(), location); handler.handle(RestRiakOperations.this.support .newResponse(ro)); future.setSuccess(); return true; } })); } @Override public RiakFuture delete(Location location, final RiakResponseHandler<_> handler) { notNull(location, "location"); notNull(handler, "handler"); HttpRequest request = this.factory.newDeleteRequest(location); return _delete("delete", handler, request); } protected RiakFuture _delete(String name, final RiakResponseHandler<_> handler, HttpRequest request) { final String procedure = name; return handle(procedure, request, handler); } @Override public RiakFuture delete(Location location, Quorum readWrite, RiakResponseHandler<_> handler) { notNull(location, "location"); notNull(readWrite, "readWrite"); notNull(handler, "handler"); HttpRequest request = this.factory .newDeleteRequest(location, readWrite); return _delete("delete/quorum", handler, request); } protected RiakFuture mapReduce(HttpRequest request, final RiakResponseHandler<MapReduceResponse> handler) { final String procedure = "mapReduce"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { return false; } else if (receive instanceof HttpChunk) { HttpChunk chunk = (HttpChunk) receive; boolean done = chunk.isLast(); ObjectNode node = to(chunk.getContent()); MapReduceResponse response = new RestMapReduceResponse( node, done); handler.handle(RestRiakOperations.this.support .newResponse(response)); if (done) { future.setSuccess(); } return done; } return false; } }); } @Override public MapReduceQueryBuilder<RiakFuture> mapReduce( final RiakResponseHandler<MapReduceResponse> handler) { notNull(handler, "handler"); MapReduceQueryContext<RiakFuture> context = new MapReduceQueryContext<RiakFuture>() { @Override public RiakFuture execute() { HttpRequest request = RestRiakOperations.this.factory .newMapReduceRequest(); ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(1024); prepare(new ChannelBufferOutputStream(buffer)); HttpHeaders.setContentLength(request, buffer.readableBytes()); request.setContent(buffer); return mapReduce(request, handler); } }; DefaultMapReduceQueryBuilder<RiakFuture> builder = new DefaultMapReduceQueryBuilder<RiakFuture>( context); builder.initialize(); return builder; } @Override public RiakFuture mapReduce(String rawJson, RiakResponseHandler<MapReduceResponse> handler) { notNull(rawJson, "rawJson"); notNull(handler, "handler"); HttpRequest request = this.factory.newMapReduceRequest(); HttpHeaders.setContentLength(request, rawJson.length()); request.setContent(ChannelBuffers.wrappedBuffer(rawJson.getBytes())); return mapReduce(request, handler); } @Override public RiakFuture walk(Location walkbegin, List<LinkCondition> conditions, final RiakResponseHandler<LinkWalkingResponse> handler) { notNull(walkbegin, "walkbegin"); notNull(conditions, "conditions"); notNull(handler, "handler"); HttpRequest request = this.factory.newWalkRequst(walkbegin, conditions); final String procedure = "walk"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { // do nothing return false; } else if (receive instanceof PartMessage) { PartMessage part = (PartMessage) receive; boolean done = part.isLast(); if (done) { handler.handle(RestRiakOperations.this.support .<LinkWalkingResponse> newResponse(new LinkWalkingResponse() { @Override public List<RiakObject<byte[]>> getResponse() { return Collections.emptyList(); } @Override public boolean getDone() { return true; } })); future.setSuccess(); } else { notifyStep(part, handler); } return done; } return false; } }); } protected void notifyStep(PartMessage message, RiakResponseHandler<LinkWalkingResponse> handler) throws Exception { MultipartResponseDecoder decoder = new MultipartResponseDecoder(); if (decoder.setUpBoundary(message)) { final List<RiakObject<byte[]>> list = new ArrayList<RiakObject<byte[]>>(); ChannelBuffer buffer = message.getContent(); while (buffer.readable()) { PartMessage msg = decoder.parse(buffer); if (msg.isLast()) { break; } else { Location location = to(msg); if (location != null) { RiakObject<byte[]> ro = this.factory.convert(msg, msg.getContent(), location); list.add(ro); } } } handler.handle(this.support .<LinkWalkingResponse> newResponse(new LinkWalkingResponse() { @Override public boolean getDone() { return false; } @Override public List<RiakObject<byte[]>> getResponse() { return list; } })); } } @Override public RiakFuture getStats(final RiakResponseHandler<ObjectNode> handler) { notNull(handler, "handler"); HttpRequest request = this.factory.newGetStatsRequest(); final String procedure = "getStats"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; if (NettyUtil.isSuccessful(response.getStatus())) { ObjectNode node = to(response.getContent()); handler.handle(RestRiakOperations.this.support .newResponse(node)); future.setSuccess(); return true; } } return false; } }); } protected RiakFuture handle(final String name, Object send, final RiakResponseHandler<_> users) { return this.support.handle(name, send, users, new SimpleMessageHandler( name, users, this.support)); } protected <T> RiakFuture handle(final String name, Object send, final RiakResponseHandler<T> users, final MessageHandler internal) { return this.support.handle(name, send, users, new ContinuousMessageHandler<T>(users, internal, this.support)); } @Override public RiakFuture getStream(String key, final StreamResponseHandler handler) { notNull(key, "key"); notNull(handler, "handler"); HttpRequest request = this.factory.newGetStreamRequest(key); final String procedure = "getStream"; return _getStream(procedure, request, handler); } protected RiakFuture _getStream(final String procedure, HttpRequest request, final StreamResponseHandler handler) { return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; boolean done = response.isChunked() == false; RiakObject<_> ro = new AbstractRiakObject<_>() { @Override public _ getContent() { return _._; } }; RestRiakOperations.this.factory .convertHeaders(response, ro); handler.begin(ro); if (done) { try { handler.handle(RestRiakOperations.this.support .newResponse(response.getContent())); } finally { handler.end(); } future.setSuccess(); } return done; } else if (receive instanceof HttpChunk) { HttpChunk chunk = (HttpChunk) receive; boolean done = chunk.isLast(); if (done) { handler.end(); future.setSuccess(); } else { handler.handle(RestRiakOperations.this.support .newResponse(chunk.getContent())); } return done; } return false; } }); } @Override public RiakFuture getStream(String key, Range range, StreamResponseHandler handler) { notNull(key, "key"); notNull(range, "range"); notNull(handler, "handler"); HttpRequest request = this.factory.newGetStreamRequest(key); request.setHeader(HttpHeaders.Names.RANGE, range.toRangeSpec()); LOG.debug(Markers.BOUNDARY, request.toString()); final String procedure = "getStream/range"; return _getStream(procedure, request, handler); } @Override public RiakFuture postStream(final RiakObject<InputStreamHandler> content, final RiakResponseHandler<String> handler) { notNull(content, "content"); notNull(handler, "handler"); HttpRequest request = this.factory.newStreamRequest(content, "", HttpMethod.POST); final String procedure = "postStream"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; HttpResponseStatus status = response.getStatus(); if (HttpResponseStatus.CONTINUE.equals(status)) { InputStreamHandler ish = content.getContent(); RestRiakOperations.this.channel .write(new ChunkedStream(ish.open())); return false; } else if (HttpResponseStatus.CREATED.equals(status)) { String loc = response .getHeader(HttpHeaders.Names.LOCATION); if (StringUtil.isEmpty(loc) == false && loc.startsWith("/" + RestRiakOperations.this.config .getLuwakName() + "/")) { final String newKey = loc.substring(7); handler.handle(RestRiakOperations.this.support .newResponse(newKey)); future.setSuccess(); return true; } } } return false; } }); } @Override public RiakFuture putStream(final RiakObject<InputStreamHandler> content, final RiakResponseHandler<_> handler) { notNull(content, "content"); notNull(handler, "handler"); HttpRequest request = this.factory.newStreamRequest(content, content .getLocation().getKey(), HttpMethod.PUT); final String procedure = "putStream"; return handle(procedure, request, handler, new MessageHandler() { @Override public boolean handle(Object receive, CountDownRiakFuture future) throws Exception { if (receive instanceof HttpResponse) { HttpResponse response = (HttpResponse) receive; HttpResponseStatus status = response.getStatus(); if (HttpResponseStatus.CONTINUE.equals(status)) { InputStreamHandler ish = content.getContent(); RestRiakOperations.this.channel .write(new ChunkedStream(ish.open())); return false; } else if (NettyUtil.isSuccessful(status)) { handler.handle(RestRiakOperations.this.support .newResponse()); future.setSuccess(); return true; } } return false; } }); } @Override public RiakFuture delete(String key, RiakResponseHandler<_> handler) { notNull(key, "key"); notNull(handler, "handler"); HttpRequest request = this.factory.newDeleteRequest(key); return handle("delete/luwak", request, handler); } @Override public void complete() { this.support.operationComplete(); } }