org.jgroups.blocks.MemcachedConnector Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.blocks;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.util.Util;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ClosedSelectorException;
import java.util.*;
import java.util.concurrent.*;
/** Class which listens on a server socket for memcached clients, reads the requests, forwards them to an instance of
* PartitionedHashMap and sends the response. A memcached client should be able to work without changes once the
* memcached protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt) has been implemented
* completely.
* @author Bela Ban
*/
public class MemcachedConnector implements Runnable {
@ManagedAttribute(writable=false)
private int port=11211;
@ManagedAttribute(writable=false)
private InetAddress bind_addr=null;
private PartitionedHashMap cache=null;
private Thread thread=null;
private ServerSocket srv_sock;
@ManagedAttribute(writable=true)
private int core_threads=1;
@ManagedAttribute(writable=true)
private int max_threads=500;
@ManagedAttribute(writable=true)
private long idle_time=5000L;
private Executor thread_pool;
private long start_time;
private final byte[] STORED="STORED\r\n".getBytes();
private final byte[] DELETED="DELETED\r\n".getBytes();
private final byte[] END="END\r\n".getBytes();
private final byte[] RN="\r\n".getBytes();
public MemcachedConnector(InetAddress bind_addr, int port, PartitionedHashMap cache) {
this.bind_addr=bind_addr;
this.cache=cache;
this.port=port;
}
public InetAddress getBindAddress() {
return bind_addr;
}
public void setBindAddress(InetAddress bind_addr) {
this.bind_addr=bind_addr;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port=port;
}
public PartitionedHashMap getCache() {
return cache;
}
public void setCache(PartitionedHashMap cache) {
this.cache=cache;
}
public int getThreadPoolCoreThreads() {
return core_threads;
}
public void setThreadPoolCoreThreads(int core_threads) {
this.core_threads=core_threads;
}
public int getThreadPoolMaxThreads() {
return max_threads;
}
public void setThreadPoolMaxThreads(int max_threads) {
this.max_threads=max_threads;
}
public long getThreadPoolIdleTime() {
return idle_time;
}
public void setThreadPoolIdleTime(long idle_time) {
this.idle_time=idle_time;
}
public Executor getThreadPool() {
return thread_pool;
}
public void setThreadPool(Executor thread_pool) {
if(this.thread_pool instanceof ExecutorService) {
((ExecutorService)thread_pool).shutdown();
}
this.thread_pool=thread_pool;
}
public Map getStats() {
Map stats=new HashMap<>();
stats.put("time", System.currentTimeMillis());
stats.put("uptime", (System.currentTimeMillis() - start_time) / 1000L);
return stats;
}
@ManagedOperation
public void start() throws IOException, MalformedObjectNameException, MBeanRegistrationException {
srv_sock=new ServerSocket(port, 50, bind_addr);
if(thread_pool == null) {
thread_pool=new ThreadPoolExecutor(core_threads, max_threads, idle_time, TimeUnit.MILLISECONDS,
new SynchronousQueue(), new ThreadPoolExecutor.CallerRunsPolicy());
// thread_pool=new DirectExecutor();
}
if(thread == null || !thread.isAlive()) {
thread=new Thread(this, "Acceptor");
thread.start();
}
start_time=System.currentTimeMillis();
}
@ManagedOperation
public void stop() throws IOException {
Util.close(srv_sock);
thread=null;
if(thread_pool instanceof ExecutorService)
((ExecutorService)thread_pool).shutdown();
}
public void run() {
System.out.println("MemcachedConnector listening on " + srv_sock.getLocalSocketAddress());
while(thread != null && Thread.currentThread().equals(thread)) {
Socket client_sock=null;
try {
client_sock=srv_sock.accept();
// System.out.println("ACCEPT: " + client_sock.getRemoteSocketAddress());
final RequestHandler handler=new RequestHandler(client_sock);
/*new Thread() {
public void run() {
handler.run();
}
}.start();
*/
thread_pool.execute(handler);
}
catch(ClosedSelectorException closed) {
Util.close(client_sock);
break;
}
catch(Throwable e) {
}
}
}
private class RequestHandler implements Runnable {
private final Socket client_sock;
private final InputStream input;
private final OutputStream output;
public RequestHandler(Socket client_sock) throws IOException {
this.client_sock=client_sock;
this.input=new BufferedInputStream(client_sock.getInputStream());
this.output=new BufferedOutputStream(client_sock.getOutputStream());
}
public void run() {
byte[] val;
String line;
while(client_sock.isConnected()) {
try {
line=Util.readLine(input);
if(line == null)
break;
// System.out.println("line = " + line);
Request req=parseRequest(line);
if(req == null) {
break;
}
// System.out.println("req = " + req);
switch(req.type) {
case SET:
byte[] data=new byte[req.number_of_bytes];
int num=input.read(data, 0, data.length);
if(num == -1)
throw new EOFException();
cache.put(req.key, data, req.caching_time);
output.write(STORED);
output.flush();
Util.discardUntilNewLine(input);
break;
case GET:
case GETS:
if(req.keys != null && !req.keys.isEmpty()) {
for(String key: req.keys) {
val=cache.get(key);
if(val != null) {
int length=val.length;
output.write(("VALUE " + key + " 0 " + length + "\r\n").getBytes());
output.write(val, 0, length);
output.write(RN);
}
}
}
output.write(END);
output.flush();
break;
case DELETE:
cache.remove(req.key);
output.write(DELETED);
output.flush();
break;
case STATS:
Map stats=getStats();
StringBuilder sb=new StringBuilder();
for(Map.Entry entry: stats.entrySet()) {
sb.append("STAT ").append(entry.getKey()).append(" ").append(entry.getValue()).append("\r\n");
}
sb.append("END\r\n");
output.write(sb.toString().getBytes());
output.flush();
break;
}
}
catch(StreamCorruptedException corrupted_ex) {
try {
output.write(("CLIENT_ERROR failed to parse request: " + corrupted_ex + ":\r\n").getBytes());
output.flush();
}
catch(IOException e) {}
}
catch(EOFException end_of_file_ex) {
break;
}
catch(Throwable e) {
}
}
Util.close(client_sock);
}
private Request parseRequest(String line) throws IOException {
Request req=new Request();
String[] args=line.trim().split(" +");
String tmp=args[0];
if(tmp == null)
throw new EOFException();
if(tmp.equals("set"))
req.type=Request.Type.SET;
else if(tmp.equals("add"))
req.type=Request.Type.ADD;
else if(tmp.equals("replace"))
req.type=Request.Type.REPLACE;
else if(tmp.equals("prepend"))
req.type=Request.Type.PREPEND;
else if(tmp.equals("append"))
req.type=Request.Type.APPEND;
else if(tmp.equals("cas"))
req.type=Request.Type.CAS;
else if(tmp.equals("incr"))
req.type=Request.Type.INCR;
else if(tmp.equals("decr"))
req.type=Request.Type.DECR;
else if(tmp.equals("get"))
req.type=Request.Type.GET;
else if(tmp.equals("gets"))
req.type=Request.Type.GETS;
else if(tmp.equals("delete"))
req.type=Request.Type.DELETE;
else if(tmp.equals("stat"))
req.type=Request.Type.STAT;
else if(tmp.equals("stats"))
req.type=Request.Type.STATS;
else {
throw new StreamCorruptedException("request \"" + line + "\" not known");
}
switch(req.type) {
case SET:
case ADD:
case REPLACE:
case PREPEND:
case APPEND:
// key
tmp=args[1];
if(tmp == null)
throw new EOFException();
req.key=tmp;
// read flags and discard: flags are not supported
tmp=args[2];
if(tmp == null)
throw new EOFException();
// expiry time
tmp=args[3];
if(tmp == null)
throw new EOFException();
req.caching_time=Long.parseLong(tmp) * 1000L; // convert from secs to ms
// number of bytes
tmp=args[4];
if(tmp == null)
throw new EOFException();
req.number_of_bytes=Integer.parseInt(tmp);
break;
case GET:
case GETS:
req.keys=new ArrayList<>(5);
req.keys.addAll(Arrays.asList(args).subList(1, args.length));
break;
case DELETE:
// key
tmp=args[1];
if(tmp == null)
throw new EOFException();
req.key=tmp;
break;
case STATS:
break;
}
return req;
}
}
public static class Request {
public static enum Type {SET, ADD, REPLACE, PREPEND, APPEND, CAS, INCR, DECR, GET, GETS, DELETE, STAT, STATS};
Type type;
String key;
List keys=null;
long caching_time;
int number_of_bytes=0;
public Request() {
}
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(type + ": ");
if(key != null)
sb.append("key=" + key);
else if(keys != null && !keys.isEmpty())
sb.append("keys=" + keys);
sb.append(", caching_time=" + caching_time + ", number_of_bytes=" + number_of_bytes);
return sb.toString();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy