package org.handwerkszeug.riak.transport.rest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import net.iharder.Base64;
import org.codehaus.jackson.node.ObjectNode;
import org.handwerkszeug.riak.Hosts;
import org.handwerkszeug.riak._;
import org.handwerkszeug.riak.model.AbstractRiakObject;
import org.handwerkszeug.riak.model.DefaultRiakObject;
import org.handwerkszeug.riak.model.KeyResponse;
import org.handwerkszeug.riak.model.Link;
import org.handwerkszeug.riak.model.Location;
import org.handwerkszeug.riak.model.Range;
import org.handwerkszeug.riak.model.RiakContentsResponse;
import org.handwerkszeug.riak.model.RiakFuture;
import org.handwerkszeug.riak.model.RiakObject;
import org.handwerkszeug.riak.model.RiakResponse;
import org.handwerkszeug.riak.op.RiakOperationsTest;
import org.handwerkszeug.riak.op.TestingHandler;
import org.handwerkszeug.riak.transport.internal.DefaultCompletionChannelHandler;
import org.handwerkszeug.riak.transport.rest.internal.RestPipelineFactory;
import org.handwerkszeug.riak.util.LogbackUtil;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.junit.Test;
/**
* @author taichi
*/
public class RestRiakOperationsTest extends
RiakOperationsTest<RestRiakOperations> {
static final RestRiakConfig config = RestRiakConfig.newConfig(
Hosts.RIAK_HOST, Hosts.RIAK_HTTP_PORT);
@Override
protected ChannelPipelineFactory newChannelPipelineFactory() {
return new RestPipelineFactory();
}
@Override
protected RestRiakOperations newTarget(Channel channel) {
return new RestRiakOperations(RestRiakClient.toRiakURI(config), config,
channel);
}
@Override
protected SocketAddress connectTo() {
return config.getRiakAddress();
}
@Override
protected void testSetClientId(String id) throws Exception {
this.target.setClientId(id);
assertEquals(id, this.target.getClientId());
}
@Test
public void testWalk() throws Exception {
// A -> B -> D
// A -> C -> D
String bucket = "testWalk";
RiakObject<byte[]> D = createData(bucket, "D", new ArrayList<Link>());
put(D);
List<Link> c2d = new ArrayList<Link>();
c2d.add(new Link(D.getLocation(), "2d"));
RiakObject<byte[]> C = createData(bucket, "C", c2d);
put(C);
List<Link> b2d = new ArrayList<Link>();
b2d.add(new Link(D.getLocation(), "2d"));
RiakObject<byte[]> B = createData(bucket, "B", b2d);
put(B);
List<Link> a = new ArrayList<Link>();
a.add(new Link(C.getLocation(), "a2c"));
a.add(new Link(B.getLocation(), "a2b"));
RiakObject<byte[]> A = createData(bucket, "A", a);
put(A);
// walk
List<List<byte[]>> list = new ArrayList<List<byte[]>>();
List<byte[]> phase1 = new ArrayList<byte[]>();
phase1.add(B.getContent());
phase1.add(C.getContent());
list.add(phase1);
List<byte[]> phase2 = new ArrayList<byte[]>();
phase2.add(D.getContent());
list.add(phase2);
try {
testWalk(A.getLocation(), list);
} finally {
// delete
testDelete(D.getLocation());
testDelete(C.getLocation());
testDelete(B.getLocation());
testDelete(A.getLocation());
}
}
RiakObject<byte[]> createData(String bucket, String key, List<Link> links) {
Location location = new Location(bucket, key);
DefaultRiakObject ro = new DefaultRiakObject(location);
ro.setLinks(links);
Random r = new Random();
byte[] bytes = new byte[20];
r.nextBytes(bytes);
String data = Base64.encodeBytes(bytes);
ro.setContent(data.getBytes());
return ro;
}
public void put(RiakObject<byte[]> ro) throws Exception {
final boolean[] is = { false };
RiakFuture waiter = this.target.put(ro, new TestingHandler<_>() {
@Override
public void handle(RiakContentsResponse<_> response)
throws Exception {
is[0] = true;
}
});
waitFor(waiter);
assertTrue(is[0]);
}
public void testWalk(Location init, List<List<byte[]>> expected)
throws Exception {
List<LinkCondition> conds = new ArrayList<LinkCondition>();
conds.add(LinkCondition.KEEP_ANY);
conds.add(LinkCondition.ANY);
final List<List<byte[]>> actual = new ArrayList<List<byte[]>>();
RiakFuture waiter = this.target.walk(init, conds,
new TestingHandler<LinkWalkingResponse>() {
@Override
public void handle(
RiakContentsResponse<LinkWalkingResponse> response)
throws Exception {
LinkWalkingResponse resp = response.getContents();
if (resp.getDone() == false) {
List<byte[]> list = new ArrayList<byte[]>();
for (RiakObject<byte[]> ro : resp.getResponse()) {
list.add(ro.getContent());
}
actual.add(list);
}
}
});
waitFor(waiter);
assertEquals(2, actual.size());
List<String> e1 = new ArrayList<String>();
for (byte[] b : expected.get(0)) {
e1.add(new String(b));
}
List<String> a1 = new ArrayList<String>();
for (byte[] b : actual.get(0)) {
a1.add(new String(b));
}
System.out.println(e1);
System.out.println(a1);
assertTrue(e1.contains(a1.get(0)));
assertTrue(e1.contains(a1.get(1)));
List<byte[]> e2 = expected.get(1);
List<byte[]> a2 = actual.get(1);
assertEquals(new String(e2.get(0)), new String(a2.get(0)));
}
@Test
public void testGetStats() throws Exception {
final ObjectNode[] actual = new ObjectNode[1];
RiakFuture waiter = this.target
.getStats(new TestingHandler<ObjectNode>() {
@Override
public void handle(RiakContentsResponse<ObjectNode> response)
throws Exception {
assertNotNull(response.getContents());
actual[0] = response.getContents();
}
});
waitFor(waiter);
ObjectNode node = actual[0];
assertNotNull(node);
assertNotNull(node.get("riak_kv_version"));
System.out.println(node);
}
static final String LARGEFILE = "org/handwerkszeug/riak/transport/rest/large_data.jpg";
@Test
public void testDeleteAllFromLuwak() throws Exception {
final List<String> list = new ArrayList<String>();
RiakFuture waiter = this.target.listKeys("luwak_tld",
new TestingHandler<KeyResponse>() {
@Override
public void handle(
RiakContentsResponse<KeyResponse> response)
throws Exception {
KeyResponse keys = response.getContents();
for (String s : keys.getKeys()) {
list.add(s);
}
}
});
waitFor(waiter);
for (String s : list) {
testDeleteFromLuwak(s);
}
}
@Test
public void testLuwak() throws Exception {
// for slow test problem.
// sibling message body is huge.
LogbackUtil.suppressLogging(new LogbackUtil.Action() {
@Override
public void execute() throws Exception {
for (int i = 0; i < 5; i++) {
String key = testPostToLuwak();
try {
System.out.println("luwak storaging wait.");
Thread.sleep(300);
testGetStream(key);
testGetRangeStream(key);
} finally {
testDeleteFromLuwak(key);
System.err.println(i);
}
}
}
}, DefaultCompletionChannelHandler.class);
}
public String testPostToLuwak() throws Exception {
URL url = getClass().getClassLoader().getResource(LARGEFILE);
final File file = new File(url.getFile());
final String[] key = new String[1];
RiakObject<InputStreamHandler> ro = new AbstractRiakObject<InputStreamHandler>() {
@Override
public InputStreamHandler getContent() {
return new InputStreamHandler() {
@Override
public InputStream open() throws IOException {
return new BufferedInputStream(
new FileInputStream(file));
}
@Override
public long getContentLength() {
return file.length();
}
};
}
};
ro.setContentType("image/jpeg");
Map<String, String> map = new HashMap<String, String>();
map.put("Mmm", "ZZZZZ");
ro.setUserMetadata(map);
// link is erased by luwak.
List<Link> links = new ArrayList<Link>();
links.add(new Link(new Location("bbb", "kkk"), "ttt"));
ro.setLinks(links);
RiakFuture waiter = this.target.postStream(ro,
new TestingHandler<String>() {
@Override
public void handle(RiakContentsResponse<String> response)
throws Exception {
key[0] = response.getContents();
}
});
waitFor(waiter);
assertNotNull(key[0]);
return key[0];
}
public void testGetStream(String key) throws Exception {
final boolean[] is = { false };
final File download = new File("target/test-classes/download.jpg");
if (download.exists()) {
download.delete();
}
RiakFuture waiter = this.target.getStream(key,
new StreamResponseHandler() {
FileOutputStream out;
@Override
public void onError(RiakResponse response) throws Exception {
fail(response.getMessage());
}
@Override
public void begin(RiakObject<_> header) throws Exception {
Map<String, String> map = header.getUserMetadata();
assertEquals("ZZZZZ", map.get("Mmm"));
this.out = new FileOutputStream(download);
}
@Override
public void handle(
RiakContentsResponse<ChannelBuffer> response)
throws Exception {
System.out.println("receive chunk...");
ChannelBuffer buffer = response.getContents();
this.out.write(buffer.array());
this.out.flush();
}
@Override
public void end() throws Exception {
System.out.println("END***********");
this.out.close();
is[0] = true;
}
});
assertTrue("timeout", waiter.await(5, TimeUnit.SECONDS));
assertTrue(is[0]);
URL url = getClass().getClassLoader().getResource(LARGEFILE);
final File file = new File(url.getFile());
assertEquals("length", file.length(), download.length());
}
public void testGetRangeStream(String key) throws Exception {
final boolean[] is = { false };
final File download = new File("target/test-classes/download.jpg.part");
if (download.exists()) {
download.delete();
}
Range range = Range.ranges(Range.range(101, 10000),
Range.range(10101, 10200));
RiakFuture waiter = this.target.getStream(key, range,
new StreamResponseHandler() {
FileOutputStream out;
@Override
public void onError(RiakResponse response) throws Exception {
fail(response.getMessage());
}
@Override
public void begin(RiakObject<_> header) throws Exception {
Map<String, String> map = header.getUserMetadata();
assertEquals("ZZZZZ", map.get("Mmm"));
this.out = new FileOutputStream(download);
}
@Override
public void handle(
RiakContentsResponse<ChannelBuffer> response)
throws Exception {
ChannelBuffer buffer = response.getContents();
this.out.write(buffer.array());
this.out.flush();
}
@Override
public void end() throws Exception {
System.out.println("end");
this.out.close();
is[0] = true;
}
});
waitFor(waiter);
assertTrue(is[0]);
assertEquals("length", 10000, download.length());
}
public void testDeleteFromLuwak(String key) throws Exception {
final boolean[] is = { false };
RiakFuture waiter = this.target.delete(key, new TestingHandler<_>() {
@Override
public void handle(RiakContentsResponse<_> response)
throws Exception {
is[0] = true;
}
});
waitFor(waiter);
assertTrue(is[0]);
}
@Test
public void testPutToLuwak() throws Exception {
String key = testPostToLuwak();
Thread.sleep(150);
testPutToLuwak(key);
Thread.sleep(150);
testDeleteFromLuwak(key);
}
public void testPutToLuwak(String key) throws Exception {
final boolean[] is = { false };
URL url = getClass().getClassLoader().getResource(LARGEFILE);
final File file = new File(url.getFile());
Location location = new Location("", key);
RiakObject<InputStreamHandler> ro = new AbstractRiakObject<InputStreamHandler>(
location) {
@Override
public InputStreamHandler getContent() {
return new InputStreamHandler() {
@Override
public InputStream open() throws IOException {
return new BufferedInputStream(
new FileInputStream(file));
}
@Override
public long getContentLength() {
return file.length();
}
};
}
};
ro.setContentType("image/jpeg");
Map<String, String> map = new HashMap<String, String>();
map.put("Mmm", "XXXXX");
ro.setUserMetadata(map);
RiakFuture waiter = this.target.putStream(ro, new TestingHandler<_>() {
@Override
public void handle(RiakContentsResponse<_> response)
throws Exception {
is[0] = true;
}
});
waitFor(waiter);
assertTrue(is[0]);
}
}