package com.guokr.hebo.server;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.guokr.hebo.HeboEngine;
import com.guokr.hebo.HeboCallback;
import com.guokr.hebo.HeboCommand;
import com.guokr.hebo.HeboRegistry;
import com.guokr.hebo.HeboRequest;
import com.guokr.hebo.HeboUtils;
import com.guokr.hebo.util.PrefixThreadFactory;
public class ServerHandler implements IHandler {
private final ExecutorService execs;
private final HeboRegistry registry;
private final HeboEngine engine;
public ServerHandler(int threadPoolSize, String prefix, int queueSize,
HeboRegistry registry, HeboEngine engine) {
this.registry = registry;
this.engine = engine;
PrefixThreadFactory factory = new PrefixThreadFactory(prefix);
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(
queueSize);
this.execs = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0,
TimeUnit.MILLISECONDS, queue, factory);
}
public void handle(final RedisRequests reqs, final RespCallback cb) {
execs.submit(new Runnable() {
@Override
public void run() {
ServerCallback rootCallback = new ServerCallback(reqs.length(), cb);
for (HeboRequest request : reqs) {
HeboCallback callback = new PipedCallback(rootCallback, request.order);
handle(callback, request);
}
}
public void handle(HeboCallback callback, HeboRequest request) {
try {
HeboCommand command = registry.get(request.name()
.toLowerCase());
String sig = command.signature();
switch (sig.length()) {
case 0:
command.invoke(engine, callback);
break;
case 1:
command.invoke(engine, request.args(1), callback);
break;
case 2:
sig = sig.substring(1);
if (sig.equals("s")) {
command.invoke(engine, request.args(1),
request.args(2), callback);
} else if (sig.equals("i")) {
command.invoke(engine, request.args(1),
request.argi(2), callback);
} else if (sig.equals("f")) {
command.invoke(engine, request.args(1),
request.argf(2), callback);
} else if (sig.equals("S")) {
command.invoke(engine, request.args(1),
request.argS(2), callback);
} else if (sig.equals("I")) {
command.invoke(engine, request.args(1),
request.argI(2), callback);
} else if (sig.equals("F")) {
command.invoke(engine, request.args(1),
request.argF(2), callback);
}
break;
case 3:
sig = sig.substring(1);
if (sig.equals("ss")) {
command.invoke(engine, request.args(1),
request.args(2), request.args(3), callback);
} else if (sig.equals("si")) {
command.invoke(engine, request.args(1),
request.args(2), request.argi(3), callback);
} else if (sig.equals("sf")) {
command.invoke(engine, request.args(1),
request.args(2), request.argf(3), callback);
} else if (sig.equals("sS")) {
command.invoke(engine, request.args(1),
request.args(2), request.argS(3), callback);
} else if (sig.equals("sI")) {
command.invoke(engine, request.args(1),
request.args(2), request.argI(3), callback);
} else if (sig.equals("sF")) {
command.invoke(engine, request.args(1),
request.args(2), request.argF(3), callback);
} else if (sig.equals("is")) {
command.invoke(engine, request.args(1),
request.argi(2), request.args(3), callback);
} else if (sig.equals("ii")) {
command.invoke(engine, request.args(1),
request.argi(2), request.argi(3), callback);
} else if (sig.equals("if")) {
command.invoke(engine, request.args(1),
request.argi(2), request.argf(3), callback);
} else if (sig.equals("iS")) {
command.invoke(engine, request.args(1),
request.argi(2), request.argS(3), callback);
} else if (sig.equals("iI")) {
command.invoke(engine, request.args(1),
request.argi(2), request.argI(3), callback);
} else if (sig.equals("iF")) {
command.invoke(engine, request.args(1),
request.argi(2), request.argF(3), callback);
} else if (sig.equals("fs")) {
command.invoke(engine, request.args(1),
request.argf(2), request.args(3), callback);
} else if (sig.equals("fi")) {
command.invoke(engine, request.args(1),
request.argf(2), request.argi(3), callback);
} else if (sig.equals("ff")) {
command.invoke(engine, request.args(1),
request.argf(2), request.argf(3), callback);
} else if (sig.equals("fS")) {
command.invoke(engine, request.args(1),
request.argf(2), request.argS(3), callback);
} else if (sig.equals("fI")) {
command.invoke(engine, request.args(1),
request.argf(2), request.argI(3), callback);
}
break;
}
} catch (Exception e) {
if (e instanceof IndexOutOfBoundsException) {
callback.error(String.format(
"Invalid arguments for command '%s'",
request.name()));
} else {
callback.error(e.getMessage());
}
}
}
});
}
public void close(int timeoutTs) {
if (timeoutTs > 0) {
execs.shutdown();
try {
if (!execs.awaitTermination(timeoutTs, TimeUnit.MILLISECONDS)) {
execs.shutdownNow();
}
} catch (InterruptedException ie) {
execs.shutdownNow();
Thread.currentThread().interrupt();
}
} else {
execs.shutdownNow();
}
}
public void clientClose(final AsyncChannel channel, final int status) {
if (channel.closedRan == 0) { // server did not close it first
// has close handler, execute it in another thread
if (channel.closeHandler != null) {
try {
// no need to maintain order
execs.submit(new Runnable() {
public void run() {
try {
channel.onClose(status);
} catch (Exception e) {
HeboUtils.printError("on close handler", e);
}
}
});
} catch (RejectedExecutionException e) {
HeboUtils.printError(
"increase :queue-size if this happens often", e);
}
} else {
// no close handler, mark the connection as closed
// channel.closedRan = 1;
// lazySet
AsyncChannel.unsafe.putOrderedInt(channel,
AsyncChannel.closedRanOffset, 1);
}
}
}
}