diff --git a/example/pom.xml b/example/pom.xml index c5c9fdce87..dde14f8226 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -52,6 +52,11 @@ netty-codec-http ${project.version} + + ${project.groupId} + netty-codec-memcache + ${project.version} + ${project.groupId} netty-codec-http2 diff --git a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java new file mode 100644 index 0000000000..2a15e0e736 --- /dev/null +++ b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java @@ -0,0 +1,108 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you 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 io.netty.example.memcache.binary; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec; +import io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * Simple memcache client that demonstrates get and set commands against a memcache server. + */ +public class MemcacheClient { + + private final String host; + private final int port; + + public MemcacheClient(String host, int port) { + this.host = host; + this.port = port; + } + + public void run() throws Exception { + EventLoopGroup group = new NioEventLoopGroup(); + try { + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new BinaryMemcacheClientCodec()); + p.addLast(new BinaryMemcacheObjectAggregator(Integer.MAX_VALUE)); + p.addLast(new MemcacheClientHandler()); + } + }); + + // Start the connection attempt. + Channel ch = b.connect(host, port).sync().channel(); + + // Read commands from the stdin. + System.out.println("Enter commands (quit to end)"); + System.out.println("get "); + System.out.println("set "); + ChannelFuture lastWriteFuture = null; + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + for (;;) { + String line = in.readLine(); + if (line == null) { + break; + } + if ("quit".equals(line.toLowerCase())) { + ch.close().sync(); + break; + } + // Sends the received line to the server. + lastWriteFuture = ch.writeAndFlush(line); + } + + // Wait until all messages are flushed before closing the channel. + if (lastWriteFuture != null) { + lastWriteFuture.sync(); + } + } finally { + group.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length != 2) { + System.err.println( + "Usage: " + MemcacheClient.class.getSimpleName() + + " "); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + + new MemcacheClient(host, port).run(); + } +} diff --git a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java new file mode 100644 index 0000000000..95d617f441 --- /dev/null +++ b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you 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 io.netty.example.memcache.binary; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerAdapter; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.memcache.binary.BinaryMemcacheOpcodes; +import io.netty.handler.codec.memcache.binary.BinaryMemcacheRequest; +import io.netty.handler.codec.memcache.binary.DefaultBinaryMemcacheRequest; +import io.netty.handler.codec.memcache.binary.DefaultFullBinaryMemcacheRequest; +import io.netty.handler.codec.memcache.binary.FullBinaryMemcacheResponse; +import io.netty.util.CharsetUtil; + +public class MemcacheClientHandler extends ChannelHandlerAdapter { + + /** + * Transforms basic string requests to binary memcache requests + */ + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + String command = (String) msg; + if (command.startsWith("get ")) { + String key = command.substring("get ".length()); + + BinaryMemcacheRequest req = new DefaultBinaryMemcacheRequest(key); + req.setOpcode(BinaryMemcacheOpcodes.GET); + req.setKeyLength((short) key.length()); + req.setTotalBodyLength(key.length()); + + ctx.write(req, promise); + } else if (command.startsWith("set ")) { + String[] parts = command.split(" ", 3); + if (parts.length < 3) { + throw new IllegalArgumentException("Malformed Command: " + command); + } + String key = parts[1]; + String value = parts[2]; + + ByteBuf content = Unpooled.wrappedBuffer(value.getBytes(CharsetUtil.UTF_8)); + ByteBuf extras = ctx.alloc().buffer(8); + + BinaryMemcacheRequest req = new DefaultFullBinaryMemcacheRequest(key, extras, content); + req.setOpcode(BinaryMemcacheOpcodes.SET); + req.setKeyLength((short) key.length()); + req.setExtrasLength((byte) 8); + req.setTotalBodyLength(key.length() + 8 + value.length()); + + ctx.write(req, promise); + } else { + throw new IllegalStateException("Unknown Message: " + msg); + } + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + FullBinaryMemcacheResponse res = (FullBinaryMemcacheResponse) msg; + System.out.println(res.content().toString(CharsetUtil.UTF_8)); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + ctx.close(); + } +}