commit 7e6ed0af4193c90473bbd25623e4067acea4115d Author: Jonathan Leibiusky Date: Fri Jun 11 02:57:11 2010 -0300 Protocol implementation diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2cc762e --- /dev/null +++ b/pom.xml @@ -0,0 +1,15 @@ + + 4.0.0 + redis.clients + jedis + 0.0.1-SNAPSHOT + + + junit + junit + 4.8.1 + jar + compile + + + \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java new file mode 100644 index 0000000..80491b3 --- /dev/null +++ b/src/main/java/redis/clients/jedis/Client.java @@ -0,0 +1,106 @@ +package redis.clients.jedis; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; + +import redis.clients.jedis.Protocol; + +public class Client { + private String host; + private int port = Protocol.DEFAULT_PORT; + private Socket socket; + private boolean connected = false; + private Protocol protocol = new Protocol(); + private OutputStream outputStream; + private InputStream inputStream; + + public Client(String host) { + super(); + this.host = host; + } + + public Client(String host, int port) { + super(); + this.host = host; + this.port = port; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public Client() { + } + + public String ping() { + String command = protocol.buildCommand("PING"); + try { + outputStream.write(command.getBytes()); + return protocol.getSingleLineReply(inputStream); + } catch (IOException e) { + // TODO Not sure what to do here + return null; + } + } + + public void connect() throws UnknownHostException, IOException { + if (!connected) { + socket = new Socket(host, port); + connected = socket.isConnected(); + outputStream = socket.getOutputStream(); + inputStream = socket.getInputStream(); + } + } + + public void disconnect() throws IOException { + if (connected) { + inputStream.close(); + outputStream.close(); + if (!socket.isClosed()) { + socket.close(); + } + connected = false; + } + } + + public boolean isConnected() { + return connected; + } + + public String set(String key, String value) { + String command = protocol.buildCommand("SET", key, value); + try { + outputStream.write(command.getBytes()); + return protocol.getSingleLineReply(inputStream); + } catch (IOException e) { + // TODO Not sure what to do here + return null; + } + } + + public String get(String key) { + String command = protocol.buildCommand("GET", key); + try { + outputStream.write(command.getBytes()); + return protocol.getBulkReply(inputStream); + } catch (IOException e) { + // TODO Not sure what to do here + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java new file mode 100644 index 0000000..32c3e09 --- /dev/null +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -0,0 +1,116 @@ +package redis.clients.jedis; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class Protocol { + public static final String DOLLAR = "$"; + public static final String ASTERISK = "*"; + public static final String PLUS = "+"; + public static final String COLON = ":"; + public static final String COMMAND_DELIMITER = "\r\n"; + public static final int DEFAULT_PORT = 6379; + + public static final byte DOLLAR_BYTE = DOLLAR.getBytes()[0]; + public static final byte ASTERISK_BYTE = ASTERISK.getBytes()[0]; + public static final byte PLUS_BYTE = PLUS.getBytes()[0]; + public static final byte COLON_BYTE = COLON.getBytes()[0]; + + public String buildCommand(String name, String... args) { + StringBuilder builder = new StringBuilder(); + builder.append(ASTERISK).append(args.length + 1).append( + COMMAND_DELIMITER); + builder.append(DOLLAR).append(name.length()).append(COMMAND_DELIMITER); + builder.append(name).append(COMMAND_DELIMITER); + for (String arg : args) { + builder.append(DOLLAR).append(arg.length()).append( + COMMAND_DELIMITER).append(arg).append(COMMAND_DELIMITER); + } + return builder.toString(); + } + + public String getBulkReply(InputStream is) { + String ret = null; + try { + if ((byte) is.read() == DOLLAR_BYTE) { + int len = Integer.parseInt(readLine(is)); + if (len == -1) { + return null; + } + byte[] read = new byte[len]; + is.read(read); + // read 2 more bytes for the command delimiter + is.read(); + is.read(); + + ret = new String(read); + + } + } catch (IOException e) { + // TODO Not sure that I should return null + return null; + } + return ret; + } + + private String readLine(InputStream is) throws IOException { + byte b; + StringBuilder sb = new StringBuilder(); + + while ((b = (byte) is.read()) != -1) { + if (b == '\r') { + b = (byte) is.read(); + if (b == '\n') { + break; + } + } + sb.append((char) b); + } + return sb.toString(); + } + + public String getSingleLineReply(InputStream is) { + String ret = null; + try { + if ((byte) is.read() == PLUS_BYTE) { + ret = readLine(is); + } + } catch (IOException e) { + // TODO Not sure that I should return null + return null; + } + return ret; + } + + public int getIntegerReply(InputStream is) { + int ret = 0; + try { + if ((byte) is.read() == COLON_BYTE) { + String num = readLine(is); + ret = Integer.parseInt(num); + } + } catch (IOException e) { + // TODO Not sure that I should return 0 + return 0; + } + return ret; + } + + public List getMultiBulkReply(InputStream is) { + List ret = new ArrayList(); + try { + if ((byte) is.read() == ASTERISK_BYTE) { + int num = Integer.parseInt(readLine(is)); + for (int i = 0; i < num; i++) { + ret.add(getBulkReply(is)); + } + } + } catch (IOException e) { + // TODO Not sure that I should return null + return null; + } + return ret; + } +} \ No newline at end of file diff --git a/src/test/java/redis/clients/jedis/tests/ClientTest.java b/src/test/java/redis/clients/jedis/tests/ClientTest.java new file mode 100644 index 0000000..cd0d6bc --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/ClientTest.java @@ -0,0 +1,61 @@ +package redis.clients.jedis.tests; + +import java.io.IOException; +import java.net.UnknownHostException; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import redis.clients.jedis.Client; + +public class ClientTest extends Assert { + private Client client; + + @Before + public void setUp() throws Exception { + client = new Client(); + } + + @After + public void tearDown() throws Exception { + client.disconnect(); + } + + @Test + public void ping() throws UnknownHostException, IOException { + client.setHost("localhost"); + client.connect(); + + assertTrue(client.isConnected()); + + String status = client.ping(); + assertEquals("PONG", status); + } + + @Test + public void setAndGet() throws UnknownHostException, IOException { + client.setHost("localhost"); + client.connect(); + + String status = client.set("foo", "bar"); + assertEquals("OK", status); + + String value = client.get("foo"); + assertEquals("bar", value); + } + + @Test(expected = UnknownHostException.class) + public void checkUnkownHost() throws UnknownHostException, IOException { + client.setHost("someunknownhost"); + client.connect(); + } + + @Test(expected = IOException.class) + public void checkWrongPort() throws UnknownHostException, IOException { + client.setHost("localhost"); + client.setPort(55665); + client.connect(); + } +} \ No newline at end of file diff --git a/src/test/java/redis/clients/jedis/tests/ProtocolTest.java b/src/test/java/redis/clients/jedis/tests/ProtocolTest.java new file mode 100644 index 0000000..33c4ed5 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/ProtocolTest.java @@ -0,0 +1,72 @@ +package redis.clients.jedis.tests; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Assert; + +import org.junit.Test; + +import redis.clients.jedis.Protocol; + +public class ProtocolTest extends Assert { + @Test + public void buildACommand() { + Protocol protocol = new Protocol(); + String command = protocol.buildCommand("GET", "SOMEKEY"); + + String expectedCommand = "*2\r\n$3\r\nGET\r\n$7\r\nSOMEKEY\r\n"; + + assertEquals(expectedCommand, command); + } + + @Test + public void bulkReply() { + InputStream is = new ByteArrayInputStream("$6\r\nfoobar\r\n".getBytes()); + Protocol protocol = new Protocol(); + String response = protocol.getBulkReply(is); + assertEquals("foobar", response); + } + + @Test + public void nullBulkReply() { + InputStream is = new ByteArrayInputStream("$-1\r\n".getBytes()); + Protocol protocol = new Protocol(); + String response = protocol.getBulkReply(is); + assertEquals(null, response); + } + + @Test + public void singleLineReply() { + InputStream is = new ByteArrayInputStream("+OK\r\n".getBytes()); + Protocol protocol = new Protocol(); + String response = protocol.getSingleLineReply(is); + assertEquals("OK", response); + } + + @Test + public void integerReply() { + InputStream is = new ByteArrayInputStream(":123\r\n".getBytes()); + Protocol protocol = new Protocol(); + int response = protocol.getIntegerReply(is); + assertEquals(123, response); + } + + @Test + public void multiBulkReply() { + InputStream is = new ByteArrayInputStream( + "*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n" + .getBytes()); + Protocol protocol = new Protocol(); + List response = protocol.getMultiBulkReply(is); + List expected = new ArrayList(); + expected.add("foo"); + expected.add("bar"); + expected.add("Hello"); + expected.add("World"); + + assertEquals(expected, response); + } +} \ No newline at end of file