diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..4a3ff26
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..b3d72bc
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ snrpc
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f9fe345
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..6249222
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a8ffd5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+SNRPC
+
+--------------------
+ -- a simple netty RPC framework
+ use protostuff-1.07 for serializer,use netty-3.2.1 for nio.
+
+ ##How to use
+
+ e.g.
+
+ 1, interface and implementor
+ // define an interface:
+ public interface SnRpcInterface {
+ public String getMessage(String param);
+ }
+
+ // implement interface
+ public class SnRpcImpl implements SnRpcInterface {
+ public String getMessage(String param) {
+ return "hi,it is message from server...param+" + param;
+ }
+ }
+
+2, start server
+
+ SnRpcInterface inter = new SnRpcImpl();
+ SnRpcServer server = new SnNettyRpcServer(new Object[] { inter });
+ try {
+ server.start();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+3, client invoker
+
+ SnRpcConnectionFactory factory = new SnNettyRpcConnectionFactory(
+ "localhost", 8080);
+ factory = new PoolableRpcConnectionFactory(factory);
+ SnRpcClient client = new CommonSnRpcClient(factory);
+ try {
+ SnRpcInterface clazz = client.proxy(SnRpcInterface.class);
+ String message = clazz.getMessage("come on");
+ System.out.println("client receive message .... : " + message);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+
+## Pre-requirement
+
+* JDK6+
+* Maven 2
+
+
+
+# Dependency
+
+* reflectasm-1.07.jar
+* asm-4.0.jar
+* log4j-1.2.16.jar
+* dom4j-1.6.1.jar
+* xml-apis-1.0.b2.jar
+* slf4j-api-1.6.6.jar
+* netty-3.2.1.Final.jar
+* jaxen-1.1.6.jar
+* protostuff-core-1.0.7.jar
+* protostuff-api-1.0.7.jar
+* protostuff-runtime-1.0.7.jar
+* protostuff-collectionschema-1.0.7.jar
+* commons-pool-1.6.jar
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..550f793
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,91 @@
+
+ 4.0.0
+
+ org.stefan
+ snrpc
+ 0.0.1-SNAPSHOT
+ jar
+
+ faraway
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+ com.esotericsoftware.reflectasm
+ reflectasm
+ 1.07
+
+
+ log4j
+ log4j
+ 1.2.16
+ runtime
+
+
+ dom4j
+ dom4j
+ 1.6.1
+
+
+ org.slf4j
+ slf4j-api
+ 1.6.6
+ true
+ provided
+
+
+ org.jboss.netty
+ netty
+ 3.2.1.Final
+
+
+ javax.servlet
+ servlet-api
+
+
+ commons-logging
+ commons-logging
+
+
+ true
+ provided
+
+
+ jaxen
+ jaxen
+ 1.1.6
+
+
+ com.dyuproject.protostuff
+ protostuff-core
+ 1.0.7
+ true
+ provided
+
+
+ com.dyuproject.protostuff
+ protostuff-runtime
+ 1.0.7
+ true
+ provided
+
+
+ commons-pool
+ commons-pool
+ 1.6
+ true
+ provided
+
+
+
diff --git a/src/main/java/org/stefan/snrpc/SnRpcClient.java b/src/main/java/org/stefan/snrpc/SnRpcClient.java
new file mode 100644
index 0000000..03b4537
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/SnRpcClient.java
@@ -0,0 +1,9 @@
+package org.stefan.snrpc;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public interface SnRpcClient {
+
+ public T proxy(Class interfaceClass) throws Throwable;
+}
diff --git a/src/main/java/org/stefan/snrpc/SnRpcConnection.java b/src/main/java/org/stefan/snrpc/SnRpcConnection.java
new file mode 100644
index 0000000..5157005
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/SnRpcConnection.java
@@ -0,0 +1,20 @@
+package org.stefan.snrpc;
+
+import org.stefan.snrpc.serializer.SnRpcRequest;
+import org.stefan.snrpc.serializer.SnRpcResponse;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public interface SnRpcConnection {
+
+ SnRpcResponse sendRequest(SnRpcRequest request) throws Throwable;
+
+ void connection() throws Throwable;
+
+ void close() throws Throwable;
+
+ boolean isConnected();
+
+ boolean isClosed();
+}
diff --git a/src/main/java/org/stefan/snrpc/SnRpcConnectionFactory.java b/src/main/java/org/stefan/snrpc/SnRpcConnectionFactory.java
new file mode 100644
index 0000000..26432d6
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/SnRpcConnectionFactory.java
@@ -0,0 +1,11 @@
+package org.stefan.snrpc;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public interface SnRpcConnectionFactory {
+
+ SnRpcConnection getConnection() throws Throwable;
+
+ void recycle(SnRpcConnection connection) throws Throwable;
+}
diff --git a/src/main/java/org/stefan/snrpc/SnRpcServer.java b/src/main/java/org/stefan/snrpc/SnRpcServer.java
new file mode 100644
index 0000000..191f4eb
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/SnRpcServer.java
@@ -0,0 +1,11 @@
+package org.stefan.snrpc;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public interface SnRpcServer {
+
+ void start() throws Throwable;
+
+ void stop() throws Throwable;
+}
diff --git a/src/main/java/org/stefan/snrpc/client/CommonSnRpcClient.java b/src/main/java/org/stefan/snrpc/client/CommonSnRpcClient.java
new file mode 100644
index 0000000..844ddb7
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/client/CommonSnRpcClient.java
@@ -0,0 +1,150 @@
+package org.stefan.snrpc.client;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.stefan.snrpc.SnRpcClient;
+import org.stefan.snrpc.SnRpcConnection;
+import org.stefan.snrpc.SnRpcConnectionFactory;
+import org.stefan.snrpc.log.Logger;
+import org.stefan.snrpc.log.LoggerFactory;
+import org.stefan.snrpc.serializer.SnRpcRequest;
+import org.stefan.snrpc.serializer.SnRpcResponse;
+import org.stefan.snrpc.util.Sequence;
+
+/**
+ * rpc client
+ * @author zhaoliangang 2014-11-13
+ */
+public class CommonSnRpcClient implements SnRpcClient {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(CommonSnRpcClient.class);
+
+ private SnRpcConnectionFactory connectionFactory;
+
+ private SnRpcConnection connection;
+
+ private SnRpcInvoker invoker = new SnRpcInvoker();
+
+ /**
+ * @param connectionFactory
+ */
+ public CommonSnRpcClient(SnRpcConnectionFactory connectionFactory) {
+ if (null == connectionFactory)
+ throw new NullPointerException("connectionFactory is null...");
+ this.connectionFactory = connectionFactory;
+ }
+
+ /**
+ * @param connection
+ */
+ public CommonSnRpcClient(SnRpcConnection connection) {
+ if (null == connection)
+ throw new NullPointerException("connection is null...");
+ this.connection = connection;
+ }
+
+ /**
+ * destroy
+ * @throws Throwable
+ */
+ public void destroy() throws Throwable {
+ if (null != connection) {
+ connection.close();
+ }
+ }
+
+
+ /**
+ * generate requestID
+ * @return
+ */
+ protected String generateRequestID() {
+ return Sequence.next()+"";
+ }
+
+ /**
+ * recycle
+ * @param connection
+ */
+ private void recycle(SnRpcConnection connection) {
+ if (null != connection && null != connectionFactory) {
+ try {
+ connectionFactory.recycle(connection);
+ } catch (Throwable t) {
+ logger.warn("recycle rpc connection fail!", t);
+ }
+ }
+ }
+
+ /**
+ * get connection
+ * @return
+ * @throws Throwable
+ */
+ private SnRpcConnection getConnection() throws Throwable {
+ if (null != connection) {
+ if (!connection.isConnected()) {
+ connection.connection();
+ }
+ return connection;
+ } else {
+ return connectionFactory.getConnection();
+ }
+ }
+
+ /*
+ * proxy
+ */
+ @SuppressWarnings("unchecked")
+ public T proxy(Class interfaceClass) throws Throwable {
+ if (!interfaceClass.isInterface()) {
+ throw new IllegalArgumentException(interfaceClass.getName()
+ + " is not an interface");
+ }
+ return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
+ new Class>[] { interfaceClass }, invoker);
+ }
+
+
+ /**
+ * invoker
+ */
+ private class SnRpcInvoker implements InvocationHandler {
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ String className = method.getDeclaringClass().getName();
+ List parameterTypes = new LinkedList();
+ for (Class> parameterType : method.getParameterTypes()) {
+ parameterTypes.add(parameterType.getName());
+ }
+
+ String requestID = generateRequestID();
+ SnRpcRequest request = new SnRpcRequest(requestID, className,
+ method.getName(), parameterTypes.toArray(new String[0]),
+ args);
+ SnRpcConnection connection = null;
+ SnRpcResponse response = null;
+ try {
+ connection = getConnection();
+ response = connection.sendRequest(request);
+ } catch (Throwable t) {
+ logger.warn("send rpc request fail! request: <{}>",
+ new Object[] { request }, t);
+ throw new RuntimeException(t);
+ } finally {
+ recycle(connection);
+ }
+
+ if (response.getException() != null) {
+ throw response.getException();
+ } else {
+ return response.getResult();
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/client/PoolableRpcConnectionFactory.java b/src/main/java/org/stefan/snrpc/client/PoolableRpcConnectionFactory.java
new file mode 100644
index 0000000..d6712fe
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/client/PoolableRpcConnectionFactory.java
@@ -0,0 +1,152 @@
+package org.stefan.snrpc.client;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.stefan.snrpc.SnRpcConnection;
+import org.stefan.snrpc.SnRpcConnectionFactory;
+
+/**
+ * connection poolable
+ *
+ * @author zhaoliangang 2014-11-14
+ */
+public class PoolableRpcConnectionFactory implements SnRpcConnectionFactory,
+ PoolableObjectFactory {
+ //connectionFactory
+ private SnRpcConnectionFactory connectionFactory;
+
+ //org.apache.commons.pool
+ private GenericObjectPool pool = new GenericObjectPool(
+ this);
+
+ public PoolableRpcConnectionFactory(SnRpcConnectionFactory factory) {
+ if (null == factory) {
+ throw new NullPointerException("factory");
+ }
+ this.connectionFactory = factory;
+ }
+
+ /* get Connection
+ * @see org.stefan.snrpc.SnRpcConnectionFactory#getConnection()
+ */
+ public SnRpcConnection getConnection() throws Throwable {
+ return pool.borrowObject();
+ }
+
+ /* recycle connection pool
+ * @see org.stefan.snrpc.SnRpcConnectionFactory#recycle(org.stefan.snrpc.SnRpcConnection)
+ */
+ public void recycle(SnRpcConnection connection) throws Throwable {
+ if (null != connection) {
+ pool.returnObject(connection);
+ }
+ }
+
+ /**
+ * destroy connection pool
+ * @throws Throwable
+ */
+ public void destroy() throws Throwable {
+ pool.close();
+ }
+
+ /* activate connection
+ * @see org.apache.commons.pool.PoolableObjectFactory#activateObject(java.lang.Object)
+ */
+ public void activateObject(SnRpcConnection connection) throws Exception {
+ try {
+ connection.connection();
+ } catch (Throwable e) {
+ throw new Exception(e);
+ }
+ }
+
+ /*destroy connection
+ * @see org.apache.commons.pool.PoolableObjectFactory#destroyObject(java.lang.Object)
+ */
+ public void destroyObject(SnRpcConnection connection) throws Exception {
+ try {
+ connection.close();
+ } catch (Throwable e) {
+ throw new Exception(e);
+ }
+ }
+
+ /* make connection
+ * @see org.apache.commons.pool.PoolableObjectFactory#makeObject()
+ */
+ public SnRpcConnection makeObject() throws Exception {
+ try {
+ return connectionFactory.getConnection();
+ } catch (Throwable e) {
+ throw new Exception(e);
+ }
+ }
+
+ /* passivateconnection
+ * @see org.apache.commons.pool.PoolableObjectFactory#passivateObject(java.lang.Object)
+ */
+ public void passivateObject(SnRpcConnection connection) throws Exception {
+ }
+
+ /* validate connection
+ * @see org.apache.commons.pool.PoolableObjectFactory#validateObject(java.lang.Object)
+ */
+ public boolean validateObject(SnRpcConnection connection) {
+ return connection.isConnected() && !connection.isClosed();
+ }
+
+ public void setLifo(boolean lifo) {
+ pool.setLifo(lifo);
+ }
+
+ public void setMaxActive(int maxActive) {
+ pool.setMaxActive(maxActive);
+ }
+
+ public void setMaxIdle(int maxIdle) {
+ pool.setMaxIdle(maxIdle);
+ }
+
+ public void setMaxWait(long maxWait) {
+ pool.setMaxWait(maxWait);
+ }
+
+ public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+ pool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+ }
+
+ public void setMinIdle(int minIdle) {
+ pool.setMinIdle(minIdle);
+ }
+
+ public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+ pool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
+ }
+
+ public void setSoftMinEvictableIdleTimeMillis(
+ long softMinEvictableIdleTimeMillis) {
+ pool.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
+ }
+
+ public void setTestOnBorrow(boolean testOnBorrow) {
+ pool.setTestOnBorrow(testOnBorrow);
+ }
+
+ public void setTestOnReturn(boolean testOnReturn) {
+ pool.setTestOnReturn(testOnReturn);
+ }
+
+ public void setTestWhileIdle(boolean testWhileIdle) {
+ pool.setTestWhileIdle(testWhileIdle);
+ }
+
+ public void setTimeBetweenEvictionRunsMillis(
+ long timeBetweenEvictionRunsMillis) {
+ pool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+ }
+
+ public void setWhenExhaustedAction(byte whenExhaustedAction) {
+ pool.setWhenExhaustedAction(whenExhaustedAction);
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/client/SnNettyRpcConnection.java b/src/main/java/org/stefan/snrpc/client/SnNettyRpcConnection.java
new file mode 100644
index 0000000..b6c38ab
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/client/SnNettyRpcConnection.java
@@ -0,0 +1,172 @@
+package org.stefan.snrpc.client;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.jboss.netty.bootstrap.ClientBootstrap;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
+import org.jboss.netty.handler.codec.http.HttpContentCompressor;
+import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timer;
+import org.stefan.snrpc.SnRpcConnection;
+import org.stefan.snrpc.conf.SnRpcConfig;
+import org.stefan.snrpc.serializer.ProtobufSerializer;
+import org.stefan.snrpc.serializer.SnRpcRequest;
+import org.stefan.snrpc.serializer.SnRpcRequestEncoder;
+import org.stefan.snrpc.serializer.SnRpcResponse;
+import org.stefan.snrpc.serializer.SnRpcResponseDecoder;
+
+/**
+ * Sn netty rpc connection
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnNettyRpcConnection extends SimpleChannelHandler implements
+ SnRpcConnection {
+
+ //inetsocket address
+ private InetSocketAddress inetAddr;
+
+ //org.jboss.netty.channel.Channel
+ private volatile Channel channel;
+
+ //response
+ private volatile SnRpcResponse response;
+
+ //exception
+ private volatile Throwable exception;
+
+ //HashedWheelTimer
+ private volatile Timer timer;
+
+ private boolean connected;
+
+ private SnRpcConfig snRpcConfig = SnRpcConfig.getInstance();
+
+ public SnNettyRpcConnection(String host, int port) {
+ snRpcConfig.loadProperties("snrpcserver.properties");
+ this.inetAddr = new InetSocketAddress(host, port);
+ this.timer = new HashedWheelTimer();
+ }
+
+ public SnRpcResponse sendRequest(SnRpcRequest request) throws Throwable {
+ if (!isConnected()) {
+ throw new IllegalStateException("not connected");
+ }
+ ChannelFuture writeFuture = channel.write(request);
+ if (!writeFuture.awaitUninterruptibly().isSuccess()) {
+ close();
+ throw writeFuture.getCause();
+ }
+ waitForResponse();
+
+ Throwable ex = exception;
+ SnRpcResponse resp = this.response;
+ this.response = null;
+ this.exception = null;
+
+ if (null != ex) {
+ close();
+ throw ex;
+ }
+ return resp;
+ }
+
+ public void connection() throws Throwable {
+ if (connected) {
+ return;
+ }
+ ChannelFactory factory = new NioClientSocketChannelFactory(
+ Executors.newCachedThreadPool(),
+ Executors.newCachedThreadPool());
+ ClientBootstrap bootstrap = new ClientBootstrap(factory);
+
+ bootstrap.setOption("tcpNoDelay", Boolean.parseBoolean(snRpcConfig
+ .getProperty("snrpc.tcp.nodelay", "true")));
+ bootstrap.setOption("reuseAddress", Boolean.parseBoolean(snRpcConfig
+ .getProperty("snrpc.tcp.reuseAddress", "true")));
+ bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
+ public ChannelPipeline getPipeline() {
+ ChannelPipeline pipeline = Channels.pipeline();
+ int readTimeout = snRpcConfig.getReadTimeout();
+ if (readTimeout > 0) {
+ pipeline.addLast("timeout", new ReadTimeoutHandler(timer,
+ readTimeout, TimeUnit.MILLISECONDS));
+ }
+ pipeline.addLast("decoder", new SnRpcRequestEncoder(
+ ProtobufSerializer.getInstance()));
+ pipeline.addLast("aggregator", new HttpChunkAggregator(1024*1024));
+ pipeline.addLast("encoder", new SnRpcResponseDecoder(
+ ProtobufSerializer.getInstance()));
+ pipeline.addLast("deflater", new HttpContentCompressor());
+ pipeline.addLast("handler", SnNettyRpcConnection.this);
+ return pipeline;
+ }
+ });
+
+ ChannelFuture channelFuture = bootstrap.connect(inetAddr);
+ if (!channelFuture.awaitUninterruptibly().isSuccess()) {
+ bootstrap.releaseExternalResources();
+ throw channelFuture.getCause();
+ }
+ channel = channelFuture.getChannel();
+ connected = true;
+ }
+
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
+ throws Exception {
+ response = (SnRpcResponse) e.getMessage();
+ synchronized (channel) {
+ channel.notifyAll();
+ }
+ }
+
+ public void close() throws Throwable {
+ connected = false;
+ if (null != timer) {
+ timer.stop();
+ timer = null;
+ }
+ if (null != channel) {
+ channel.close().awaitUninterruptibly();
+ channel.getFactory().releaseExternalResources();
+
+ this.exception = new IOException("connection closed");
+ synchronized (channel) {
+ channel.notifyAll();
+ }
+ channel = null;
+ }
+ }
+
+ public void waitForResponse() {
+ synchronized (channel) {
+ try {
+ channel.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public boolean isClosed() {
+ return (null == channel) || !channel.isConnected()
+ || !channel.isReadable() || !channel.isWritable();
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/client/SnNettyRpcConnectionFactory.java b/src/main/java/org/stefan/snrpc/client/SnNettyRpcConnectionFactory.java
new file mode 100644
index 0000000..7a2b7ef
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/client/SnNettyRpcConnectionFactory.java
@@ -0,0 +1,30 @@
+package org.stefan.snrpc.client;
+
+import java.net.InetSocketAddress;
+
+import org.stefan.snrpc.SnRpcConnection;
+import org.stefan.snrpc.SnRpcConnectionFactory;
+
+/**
+ * SnNettyRpcConnectionFactory
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnNettyRpcConnectionFactory implements SnRpcConnectionFactory {
+ private InetSocketAddress serverAddr;
+
+ public SnNettyRpcConnectionFactory(String host, int port) {
+ this.serverAddr = new InetSocketAddress(host, port);
+ }
+
+ public SnRpcConnection getConnection() throws Throwable {
+ return new SnNettyRpcConnection(this.serverAddr.getHostName(),
+ this.serverAddr.getPort());
+ }
+
+ public void recycle(SnRpcConnection connection) throws Throwable {
+ if (null != connection) {
+ connection.close();
+ }
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/conf/ConfigureParse.java b/src/main/java/org/stefan/snrpc/conf/ConfigureParse.java
new file mode 100644
index 0000000..dde5a0d
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/conf/ConfigureParse.java
@@ -0,0 +1,12 @@
+package org.stefan.snrpc.conf;
+
+import java.util.List;
+
+/**
+ * ConfigureParse
+ * @author zhaoliangang 2014-11-12
+ */
+public interface ConfigureParse {
+
+ List parseService();
+}
diff --git a/src/main/java/org/stefan/snrpc/conf/RpcImplementor.java b/src/main/java/org/stefan/snrpc/conf/RpcImplementor.java
new file mode 100644
index 0000000..57bd93e
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/conf/RpcImplementor.java
@@ -0,0 +1,62 @@
+package org.stefan.snrpc.conf;
+
+import java.io.Serializable;
+
+import com.esotericsoftware.reflectasm.MethodAccess;
+
+/**
+ * RpcImplementor
+ * @author zhaoliangang 2014-11-12
+ */
+public class RpcImplementor implements Serializable {
+
+ private static final long serialVersionUID = 4679847970989376057L;
+
+ private Class> processorClass;
+
+ private MethodAccess methodAccess;
+
+ public RpcImplementor() {
+ }
+
+ /**
+ * @return the processorClass
+ */
+ public Class> getProcessorClass() {
+ return processorClass;
+ }
+
+ /**
+ * @param processorClass
+ * @param methodAccess
+ */
+ public RpcImplementor(Class> processorClass) {
+ super();
+ this.processorClass = processorClass;
+ this.methodAccess = MethodAccess.get(processorClass);
+ }
+
+ /**
+ * @param processorClass
+ * the processorClass to set
+ */
+ public void setProcessorClass(Class> processorClass) {
+ this.processorClass = processorClass;
+ }
+
+ /**
+ * @return the methodAccess
+ */
+ public MethodAccess getMethodAccess() {
+ return methodAccess;
+ }
+
+ /**
+ * @param methodAccess
+ * the methodAccess to set
+ */
+ public void setMethodAccess(MethodAccess methodAccess) {
+ this.methodAccess = methodAccess;
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/conf/RpcService.java b/src/main/java/org/stefan/snrpc/conf/RpcService.java
new file mode 100644
index 0000000..851f66c
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/conf/RpcService.java
@@ -0,0 +1,107 @@
+package org.stefan.snrpc.conf;
+
+/**
+ * RpcService
+ *
+ *
+ *
+ *
+ *
+ *
+ * @author zhaoliangang 2014-11-12
+ */
+public class RpcService {
+
+ protected Class> typeClass;
+ protected String id;
+ protected String name;
+ private boolean overload = false;
+ private RpcImplementor rpcImplementor;
+
+ /**
+ * @param id
+ * @param name
+ */
+ public RpcService(String id, String name) {
+ super();
+ this.id = id;
+ this.name = name;
+ }
+
+ /**
+ * @return the typeClass
+ */
+ public Class> getTypeClass() {
+ return typeClass;
+ }
+
+ /**
+ * @param typeClass
+ * the typeClass to set
+ */
+ public void setTypeClass(Class> typeClass) {
+ this.typeClass = typeClass;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ * the id to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name
+ * the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the overload
+ */
+ public boolean isOverload() {
+ return overload;
+ }
+
+ /**
+ * @param overload
+ * the overload to set
+ */
+ public void setOverload(boolean overload) {
+ this.overload = overload;
+ }
+
+ /**
+ * @return the rpcImplementor
+ */
+ public RpcImplementor getRpcImplementor() {
+ return rpcImplementor;
+ }
+
+ /**
+ * @param rpcImplementor
+ * the rpcImplementor to set
+ */
+ public void setRpcImplementor(RpcImplementor rpcImplementor) {
+ this.rpcImplementor = rpcImplementor;
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/conf/SnRpcConfig.java b/src/main/java/org/stefan/snrpc/conf/SnRpcConfig.java
new file mode 100644
index 0000000..98a19cf
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/conf/SnRpcConfig.java
@@ -0,0 +1,105 @@
+package org.stefan.snrpc.conf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.stefan.snrpc.exception.SnRpcException;
+import org.stefan.snrpc.log.Logger;
+import org.stefan.snrpc.log.LoggerFactory;
+import org.stefan.snrpc.util.StringUtil;
+
+/**
+ * SnRpcConfig
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcConfig {
+
+ private static Logger logger = LoggerFactory.getLogger(SnRpcConfig.class);
+
+ private static SnRpcConfig snRpcConfig;
+
+ private static Properties properties = new Properties();
+
+ private SnRpcConfig() {
+ }
+
+ public static SnRpcConfig getInstance() {
+ if (snRpcConfig == null)
+ snRpcConfig = new SnRpcConfig();
+ return snRpcConfig;
+ }
+
+ public void loadProperties(String fileName) {
+ if (StringUtil.isEmpty(fileName))
+ throw new SnRpcException("snRpcConfig name is null...");
+ InputStream inputStream = null;
+ try {
+ inputStream = Thread.currentThread().getContextClassLoader()
+ .getResourceAsStream(fileName);
+ properties.load(inputStream);
+ } catch (IOException e) {
+ throw new SnRpcException(" snRpcConfig file load failed... "
+ + fileName);
+ } finally {
+ try {
+ if (inputStream != null)
+ inputStream.close();
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ if (properties == null)
+ throw new RuntimeException("Properties file loading failed: "
+ + fileName);
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public String getProperty(String key) {
+ return properties.getProperty(key).trim();
+ }
+
+ public String getProperty(String key, String defaultValue) {
+ return properties.getProperty(key, defaultValue.trim());
+ }
+
+ /**
+ * get the service properties file,default is config.xml
+ *
+ * @return
+ */
+ public String getpropertiesFile() {
+ String f = properties.getProperty("properties.file", "config.xml");
+ return f.trim();
+ }
+
+ public boolean getDevMod() {
+ String dev = properties.getProperty("snrpc.dev", "false");
+ return Boolean.parseBoolean(dev);
+ }
+
+ /**
+ * get the method's invoke timeout,default is 3s
+ *
+ * @return
+ */
+ public int getReadTimeout() {
+ String timeOutStr = properties
+ .getProperty("snrpc.read.timeout", "3000");
+ return Integer.parseInt(timeOutStr);
+ }
+
+ /**
+ * get the server's HTTP port,default is -1
+ *
+ * @return
+ */
+ public int getHttpPort() {
+ String port = properties.getProperty("snrpc.http.port", "-1");
+ return Integer.parseInt(port);
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/conf/XmlConfigureParse.java b/src/main/java/org/stefan/snrpc/conf/XmlConfigureParse.java
new file mode 100644
index 0000000..36a4ecd
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/conf/XmlConfigureParse.java
@@ -0,0 +1,150 @@
+package org.stefan.snrpc.conf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.Node;
+import org.dom4j.io.SAXReader;
+import org.stefan.snrpc.log.Logger;
+import org.stefan.snrpc.log.LoggerFactory;
+import org.stefan.snrpc.util.StringUtil;
+
+/**
+ *
+ * XmlConfigureParse
+ *
+
+
+
+
+
+
+ * @author zhaoliangang 2014-11-12
+ */
+public class XmlConfigureParse implements ConfigureParse {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(XmlConfigureParse.class);
+
+ private String configFile = null;
+ private Document document = null;
+ private Element root = null;
+
+ /**
+ * @param configFile
+ * @param root
+ */
+ public XmlConfigureParse(String configFile) {
+ super();
+ this.configFile = configFile;
+ this.root = getRoot();
+ }
+
+ @SuppressWarnings("unchecked")
+ private Element getRoot() {
+ Document doc = getDocument();
+ List list = doc.selectNodes("//application");
+ if (list.size() > 0) {
+ Element aroot = list.get(0);
+ return aroot;
+ }
+ return null;
+ }
+
+ private Document getDocument() {
+ InputStream is = getFileStream();
+ try {
+ if (document == null) {
+ SAXReader sr = new SAXReader();
+ sr.setValidation(false);
+ if (is == null) {
+ throw new RuntimeException("can not find config file..."
+ + configFile);
+ }
+ document = sr.read(is);
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ throw new RuntimeException("get xml file failed");
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return document;
+ }
+
+ private InputStream getFileStream() {
+ return getFileStream(configFile);
+ }
+
+ private InputStream getFileStream(String fileName) {
+ InputStream is = Thread.currentThread().getContextClassLoader()
+ .getResourceAsStream(fileName);
+ return is;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List parseService() {
+
+ List slist = new ArrayList();
+ Node serviceRoot = root.selectSingleNode("//rpcServices");
+ List serviceList = serviceRoot.selectNodes("//rpcService");
+
+ int i = 0;
+ for (Element serviceNode : serviceList) {
+ String name = serviceNode.attributeValue("name");// service name
+ String interfaceStr = serviceNode.attributeValue("interface");
+ String overloadStr = serviceNode.attributeValue("overload");
+
+ if (StringUtil.isEmpty(name)) {
+ logger.warn(configFile + ":a rpcservice's name is empty.");
+ continue;
+ }
+ if (StringUtil.isEmpty(interfaceStr)) {
+ logger.warn(configFile + ":rpcservice[" + name
+ + "] has an empty interface configure.");
+ continue;
+ }
+ Class> type = null;
+ try {
+ type = Class.forName(interfaceStr);
+ } catch (ClassNotFoundException e) {
+ logger.error(e.getMessage());
+ throw new RuntimeException("can't find rpc Interface:"
+ + interfaceStr);
+ }
+ RpcService service = new RpcService("" + i, name);
+ service.setTypeClass(type);
+
+ if (StringUtil.isNotEmpty(overloadStr)
+ && "true".equals(overloadStr.trim())) {
+ service.setOverload(true);
+ }
+
+ Element rpcImplementor = serviceNode.element("rpcImplementor");
+ String processor = rpcImplementor.attributeValue("class");
+ Class> providerClass = null;
+ try {
+ providerClass = Class.forName(processor);
+ } catch (ClassNotFoundException e) {
+ logger.error(e.getMessage());
+ throw new RuntimeException("can't find rpcImplementor Class:"
+ + processor);
+ }
+ RpcImplementor sv = new RpcImplementor(providerClass);
+ service.setRpcImplementor(sv);
+ slist.add(service);
+ i++;
+ }
+ return slist;
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/exception/SerializerException.java b/src/main/java/org/stefan/snrpc/exception/SerializerException.java
new file mode 100644
index 0000000..5dbf466
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/exception/SerializerException.java
@@ -0,0 +1,28 @@
+package org.stefan.snrpc.exception;
+
+/**
+ * SerializerException
+ *
+ * @author zhaoliangang
+ *
+ */
+public class SerializerException extends RuntimeException {
+
+ private static final long serialVersionUID = -6831220895401658422L;
+
+ public SerializerException() {
+ super();
+ }
+
+ public SerializerException(String msg) {
+ super(msg);
+ }
+
+ public SerializerException(Throwable t) {
+ super(t);
+ }
+
+ public SerializerException(String msg, Throwable t) {
+ super(msg, t);
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/exception/SnRpcException.java b/src/main/java/org/stefan/snrpc/exception/SnRpcException.java
new file mode 100644
index 0000000..ab464fe
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/exception/SnRpcException.java
@@ -0,0 +1,28 @@
+package org.stefan.snrpc.exception;
+
+/**
+ * SnRpcException
+ *
+ * @author zhaoliangang
+ *
+ */
+public class SnRpcException extends RuntimeException {
+
+ private static final long serialVersionUID = 6443147893553933129L;
+
+ public SnRpcException() {
+ super();
+ }
+
+ public SnRpcException(String msg) {
+ super(msg);
+ }
+
+ public SnRpcException(Throwable t) {
+ super(t);
+ }
+
+ public SnRpcException(String msg, Throwable t) {
+ super(msg, t);
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/log/AbstractLogger.java b/src/main/java/org/stefan/snrpc/log/AbstractLogger.java
new file mode 100644
index 0000000..42386c8
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/AbstractLogger.java
@@ -0,0 +1,256 @@
+package org.stefan.snrpc.log;
+
+/**
+ * @author zhaoliangang 2014-11-12
+ */
+public abstract class AbstractLogger implements Logger {
+
+ private final String name;
+
+ /**
+ * Instantiate the abstract logger.
+ */
+ protected AbstractLogger(String nm) {
+ super();
+ if (nm == null) {
+ throw new NullPointerException("Logger name may not be null.");
+ }
+ name = nm;
+ }
+
+ /**
+ * Get the name of this logger.
+ */
+ public String getName() {
+ return (name);
+ }
+
+ /**
+ * Get the throwable from the last element of this array if it is Throwable,
+ * else null.
+ */
+ public Throwable getThrowable(Object args[]) {
+ Throwable rv = null;
+ if (args.length > 0) {
+ if (args[args.length - 1] instanceof Throwable) {
+ rv = (Throwable) args[args.length - 1];
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * True if debug is enabled for this logger. Default implementation always
+ * returns false
+ *
+ * @return true if debug messages would be displayed
+ */
+ public abstract boolean isDebugEnabled();
+
+ /**
+ * True if debug is enabled for this logger. Default implementation always
+ * returns false
+ *
+ * @return true if info messages would be displayed
+ */
+ public abstract boolean isInfoEnabled();
+
+ /**
+ * Log a message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ public void debug(Object message, Throwable exception) {
+ log(Level.DEBUG, message, exception);
+ }
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ public void debug(String message, Object... args) {
+ if (isDebugEnabled()) {
+ debug(String.format(message, args), getThrowable(args));
+ }
+ }
+
+ /**
+ * Log a message at debug level.
+ *
+ * @param message
+ * the message to log
+ */
+ public void debug(Object message) {
+ debug(message, null);
+ }
+
+ /**
+ * Log a message at info level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ public void info(Object message, Throwable exception) {
+ log(Level.INFO, message, exception);
+ }
+
+ /**
+ * Log a formatted message at info level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ public void info(String message, Object... args) {
+ if (isInfoEnabled()) {
+ info(String.format(message, args), getThrowable(args));
+ }
+ }
+
+ /**
+ * Log a message at info level.
+ *
+ * @param message
+ * the message to log
+ */
+ public void info(Object message) {
+ info(message, null);
+ }
+
+ /**
+ * Log a message at warning level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ public void warn(Object message, Throwable exception) {
+ log(Level.WARN, message, exception);
+ }
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ public void warn(String message, Object... args) {
+ warn(String.format(message, args), getThrowable(args));
+ }
+
+ /**
+ * Log a message at warning level.
+ *
+ * @param message
+ * the message to log
+ */
+ public void warn(Object message) {
+ warn(message, null);
+ }
+
+ /**
+ * Log a message at error level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ public void error(Object message, Throwable exception) {
+ log(Level.ERROR, message, exception);
+ }
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ public void error(String message, Object... args) {
+ error(String.format(message, args), getThrowable(args));
+ }
+
+ /**
+ * Log a message at error level.
+ *
+ * @param message
+ * the message to log
+ */
+ public void error(Object message) {
+ error(message, null);
+ }
+
+ /**
+ * Log a message at fatal level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ public void fatal(Object message, Throwable exception) {
+ log(Level.FATAL, message, exception);
+ }
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ public void fatal(String message, Object... args) {
+ fatal(String.format(message, args), getThrowable(args));
+ }
+
+ /**
+ * Log a message at fatal level.
+ *
+ * @param message
+ * the message to log
+ */
+ public void fatal(Object message) {
+ fatal(message, null);
+ }
+
+ /**
+ * Log a message at the given level.
+ *
+ * @param level
+ * the level
+ * @param message
+ * the message
+ */
+ public void log(Level level, Object message) {
+ log(level, message, null);
+ }
+
+ /**
+ * Subclasses should implement this method to determine what to do when a
+ * client wants to log at a particular level.
+ *
+ * @param level
+ * the level to log at (see the fields of this class)
+ * @param message
+ * the message to log
+ * @param e
+ * the exception that caused the message (or null)
+ */
+ public abstract void log(Level level, Object message, Throwable e);
+}
diff --git a/src/main/java/org/stefan/snrpc/log/DefaultLogger.java b/src/main/java/org/stefan/snrpc/log/DefaultLogger.java
new file mode 100644
index 0000000..85a0dc5
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/DefaultLogger.java
@@ -0,0 +1,52 @@
+package org.stefan.snrpc.log;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author zhaoliangang 2014-11-12
+ */
+public class DefaultLogger extends AbstractLogger {
+
+ private final SimpleDateFormat df;
+
+ /**
+ * Get an instance of DefaultLogger.
+ */
+ public DefaultLogger(String name) {
+ super(name);
+ df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ }
+
+ /**
+ * False.
+ */
+ @Override
+ public boolean isDebugEnabled() {
+ return (false);
+ }
+
+ /**
+ * True.
+ */
+ @Override
+ public boolean isInfoEnabled() {
+ return (true);
+ }
+
+ /**
+ * @see AbstractLogger
+ */
+ @Override
+ public synchronized void log(Level level, Object message, Throwable e) {
+ if (level == Level.INFO || level == Level.WARN || level == Level.ERROR
+ || level == Level.FATAL) {
+ System.err.printf("%s %s %s: %s\n", df.format(new Date()),
+ level.name(), getName(), message);
+ if (e != null) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/stefan/snrpc/log/Level.java b/src/main/java/org/stefan/snrpc/log/Level.java
new file mode 100644
index 0000000..e689b70
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/Level.java
@@ -0,0 +1,18 @@
+package org.stefan.snrpc.log;
+
+/**
+ * log level
+ *
+ * @author zhaoliangang
+ *
+ */
+public enum Level {
+
+ DEBUG, INFO, WARN, ERROR, FATAL;
+
+ @Override
+ public String toString() {
+ return "{LogLevel:" + name() + "}";
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/log/Log4JLogger.java b/src/main/java/org/stefan/snrpc/log/Log4JLogger.java
new file mode 100644
index 0000000..522a1b3
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/Log4JLogger.java
@@ -0,0 +1,80 @@
+package org.stefan.snrpc.log;
+
+/**
+ * @author zhaoliangang 2014-11-12
+ */
+public class Log4JLogger extends AbstractLogger {
+
+ // Can't really import this without confusion as there's another thing
+ // by this name in here.
+ private final org.apache.log4j.Logger l4jLogger;
+
+ /**
+ * Get an instance of Log4JLogger.
+ */
+ public Log4JLogger(String name) {
+ super(name);
+
+ // Get the log4j logger instance.
+ l4jLogger = org.apache.log4j.Logger.getLogger(name);
+ }
+
+ /**
+ * True if the underlying logger would allow debug messages through.
+ */
+ @Override
+ public boolean isDebugEnabled() {
+ return (l4jLogger.isDebugEnabled());
+ }
+
+ /**
+ * True if the underlying logger would allow info messages through.
+ */
+ @Override
+ public boolean isInfoEnabled() {
+ return (l4jLogger.isInfoEnabled());
+ }
+
+ /**
+ * Wrapper around log4j.
+ *
+ * @param level
+ * net.spy.compat.log.AbstractLogger level.
+ * @param message
+ * object message
+ * @param e
+ * optional throwable
+ */
+ @Override
+ public void log(Level level, Object message, Throwable e) {
+ org.apache.log4j.Level pLevel = org.apache.log4j.Level.DEBUG;
+
+ switch (level == null ? Level.FATAL : level) {
+ case DEBUG:
+ pLevel = org.apache.log4j.Level.DEBUG;
+ break;
+ case INFO:
+ pLevel = org.apache.log4j.Level.INFO;
+ break;
+ case WARN:
+ pLevel = org.apache.log4j.Level.WARN;
+ break;
+ case ERROR:
+ pLevel = org.apache.log4j.Level.ERROR;
+ break;
+ case FATAL:
+ pLevel = org.apache.log4j.Level.FATAL;
+ break;
+ default:
+ // I don't know what this is, so consider it fatal
+ pLevel = org.apache.log4j.Level.FATAL;
+ l4jLogger.log("org.stefan.snrpc.log.AbstractLogger", pLevel,
+ "Unhandled log level: " + level
+ + " for the following message", null);
+ }
+
+ l4jLogger
+ .log("org.stefan.snrpc.log.AbstractLogger", pLevel, message, e);
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/log/Logger.java b/src/main/java/org/stefan/snrpc/log/Logger.java
new file mode 100644
index 0000000..a09bf49
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/Logger.java
@@ -0,0 +1,189 @@
+package org.stefan.snrpc.log;
+
+/**
+ * @author zhaoliangang
+ *
+ */
+public interface Logger {
+
+ /**
+ * Get the name of this logger.
+ */
+ String getName();
+
+ /**
+ * True if debug is enabled for this logger.
+ *
+ * @return true if debug messages would be displayed
+ */
+ boolean isDebugEnabled();
+
+ /**
+ * True if info is enabled for this logger.
+ *
+ * @return true if info messages would be displayed
+ */
+ boolean isInfoEnabled();
+
+ /**
+ * Log a message at the specified level.
+ *
+ * @param level
+ * the level at which to log
+ * @param message
+ * the message to log
+ * @param exception
+ * an exception that caused the message
+ */
+ void log(Level level, Object message, Throwable exception);
+
+ /**
+ * Log a message at the specified level.
+ *
+ * @param level
+ * the level at which to log
+ * @param message
+ * the message to log
+ */
+ void log(Level level, Object message);
+
+ /**
+ * Log a message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ void debug(Object message, Throwable exception);
+
+ /**
+ * Log a message at debug level.
+ *
+ * @param message
+ * the message to log
+ */
+ void debug(Object message);
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ void debug(String message, Object... args);
+
+ /**
+ * Log a message at info level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ void info(Object message, Throwable exception);
+
+ /**
+ * Log a message at info level.
+ *
+ * @param message
+ * the message to log
+ */
+ void info(Object message);
+
+ /**
+ * Log a formatted message at info level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ void info(String message, Object... args);
+
+ /**
+ * Log a message at warning level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ void warn(Object message, Throwable exception);
+
+ /**
+ * Log a message at warning level.
+ *
+ * @param message
+ * the message to log
+ */
+ void warn(Object message);
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ void warn(String message, Object... args);
+
+ /**
+ * Log a message at error level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ void error(Object message, Throwable exception);
+
+ /**
+ * Log a message at error level.
+ *
+ * @param message
+ * the message to log
+ */
+ void error(Object message);
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ void error(String message, Object... args);
+
+ /**
+ * Log a message at fatal level.
+ *
+ * @param message
+ * the message to log
+ * @param exception
+ * the exception that caused the message to be generated
+ */
+ void fatal(Object message, Throwable exception);
+
+ /**
+ * Log a message at fatal level.
+ *
+ * @param message
+ * the message to log
+ */
+ void fatal(Object message);
+
+ /**
+ * Log a formatted message at debug level.
+ *
+ * @param message
+ * the message to log
+ * @param args
+ * the arguments for that message
+ */
+ void fatal(String message, Object... args);
+}
diff --git a/src/main/java/org/stefan/snrpc/log/LoggerFactory.java b/src/main/java/org/stefan/snrpc/log/LoggerFactory.java
new file mode 100644
index 0000000..5a017e4
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/LoggerFactory.java
@@ -0,0 +1,125 @@
+package org.stefan.snrpc.log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author zhaoliangang 2014-11-12
+ */
+public class LoggerFactory {
+
+ private static LoggerFactory instance = null;
+ private final ConcurrentMap instances;
+ private Constructor extends Logger> instanceConstructor;
+
+ private LoggerFactory() {
+ super();
+ instances = new ConcurrentHashMap();
+ }
+
+ private static void init() {
+ if (instance == null)
+ instance = new LoggerFactory();
+ }
+
+ public static Logger getLogger(Class> clazz) {
+ return getLogger(clazz.getName());
+ }
+
+ public static Logger getLogger(String name) {
+ if (name == null)
+ throw new NullPointerException("Logger name can not be null...");
+
+ init();
+ return instance.internalGetLogger(name);
+ }
+
+ private Logger internalGetLogger(String name) {
+ assert name != null : "logger name is null";
+ Logger logger = instances.get(name);
+ if (logger == null) {
+ Logger newLogger = null;
+ try {
+ newLogger = getNewInstance(name);
+ } catch (Exception e) {
+ throw new RuntimeException("Problem getting logger", e);
+ }
+ Logger tmp = instances.putIfAbsent(name, newLogger);
+ logger = tmp == null ? newLogger : tmp;
+ }
+
+ return logger;
+ }
+
+ /**
+ * @param name
+ * @return
+ */
+ private Logger getNewInstance(String name) throws InstantiationException,
+ IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException {
+ if (instanceConstructor == null) {
+ getConstructor();
+ }
+ Object[] args = { name };
+ Logger logger = instanceConstructor.newInstance(args);
+ return logger;
+ }
+
+ // Find the appropriate constructor
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private void getConstructor() {
+ Class extends Logger> c = DefaultLogger.class;
+ String className = System.getProperty("org.stefan.log.LoggerImpl");
+
+ if (className != null) {
+ try {
+ c = (Class extends Logger>) Class.forName(className);
+ } catch (NoClassDefFoundError e) {
+ System.err.println("Warning: " + className
+ + " not found while initializing"
+ + " org.stefan.snrpc.log.LoggerFactory");
+ e.printStackTrace();
+ c = DefaultLogger.class;
+ } catch (ClassNotFoundException e) {
+ System.err.println("Warning: " + className
+ + " not found while initializing"
+ + " org.stefan.snrpc.log.LoggerFactory");
+ e.printStackTrace();
+ c = DefaultLogger.class;
+ }
+ }
+
+ // Find the best constructor
+ try {
+ // Try to find a constructor that takes a single string
+ Class[] args = { String.class };
+ instanceConstructor = c.getConstructor(args);
+ } catch (NoSuchMethodException e) {
+ try {
+ // Try to find an empty constructor
+ Class[] args = {};
+ instanceConstructor = c.getConstructor(args);
+ } catch (NoSuchMethodException e2) {
+ System.err.println("Warning: " + className
+ + " has no appropriate constructor, using defaults.");
+
+ // Try to find a constructor that takes a single string
+ try {
+ Class[] args = { String.class };
+ instanceConstructor = DefaultLogger.class
+ .getConstructor(args);
+ } catch (NoSuchMethodException e3) {
+ // This shouldn't happen.
+ throw new NoSuchMethodError(
+ "There used to be a constructor that takes a single "
+ + "String on " + DefaultLogger.class
+ + ", but I can't " + "find one now.");
+ } // SOL
+ } // No empty constructor
+ } // No constructor that takes a string
+ } // getConstructor
+
+}
diff --git a/src/main/java/org/stefan/snrpc/log/SunLogger.java b/src/main/java/org/stefan/snrpc/log/SunLogger.java
new file mode 100644
index 0000000..c25e1cf
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/log/SunLogger.java
@@ -0,0 +1,111 @@
+package org.stefan.snrpc.log;
+
+/**
+ * @author zhaoliangang 2014-11-14
+ */
+public class SunLogger extends AbstractLogger {
+
+ // Can't really import this without confusion as there's another thing
+ // by this name in here.
+ private final java.util.logging.Logger sunLogger;
+
+ /**
+ * Get an instance of SunLogger.
+ */
+ public SunLogger(String name) {
+ super(name);
+
+ // Get the sun logger instance.
+ sunLogger = java.util.logging.Logger.getLogger(name);
+ }
+
+ /**
+ * True if the underlying logger would allow Level.FINE through.
+ */
+ @Override
+ public boolean isDebugEnabled() {
+ return (sunLogger.isLoggable(java.util.logging.Level.FINE));
+ }
+
+ /**
+ * True if the underlying logger would allow Level.INFO through.
+ */
+ @Override
+ public boolean isInfoEnabled() {
+ return (sunLogger.isLoggable(java.util.logging.Level.INFO));
+ }
+
+ /**
+ * Wrapper around sun logger.
+ *
+ * @param level
+ * net.spy.compat.log.AbstractLogger level.
+ * @param message
+ * object message
+ * @param e
+ * optional throwable
+ */
+ @Override
+ public void log(Level level, Object message, Throwable e) {
+ java.util.logging.Level sLevel = java.util.logging.Level.SEVERE;
+
+ switch (level == null ? Level.FATAL : level) {
+ case DEBUG:
+ sLevel = java.util.logging.Level.FINE;
+ break;
+ case INFO:
+ sLevel = java.util.logging.Level.INFO;
+ break;
+ case WARN:
+ sLevel = java.util.logging.Level.WARNING;
+ break;
+ case ERROR:
+ sLevel = java.util.logging.Level.SEVERE;
+ break;
+ case FATAL:
+ sLevel = java.util.logging.Level.SEVERE;
+ break;
+ default:
+ // I don't know what this is, so consider it fatal
+ sLevel = java.util.logging.Level.SEVERE;
+ sunLogger.log(sLevel, "Unhandled log level: " + level
+ + " for the following message");
+ }
+
+ // Figure out who was logging.
+ Throwable t = new Throwable();
+ StackTraceElement[] ste = t.getStackTrace();
+ StackTraceElement logRequestor = null;
+ String alclass = AbstractLogger.class.getName();
+ for (int i = 0; i < ste.length && logRequestor == null; i++) {
+ if (ste[i].getClassName().equals(alclass)) {
+ // Make sure there's another stack frame.
+ if (i + 1 < ste.length) {
+ logRequestor = ste[i + 1];
+ if (logRequestor.getClassName().equals(alclass)) {
+ logRequestor = null;
+ } // Also AbstractLogger
+ } // Found something that wasn't abstract logger
+ } // check for abstract logger
+ }
+
+ // See if we could figure out who was doing the original logging,
+ // if we could, we want to include a useful class and method name
+ if (logRequestor != null) {
+ if (e != null) {
+ sunLogger.logp(sLevel, logRequestor.getClassName(),
+ logRequestor.getMethodName(), message.toString(), e);
+ } else {
+ sunLogger.logp(sLevel, logRequestor.getClassName(),
+ logRequestor.getMethodName(), message.toString());
+ }
+ } else {
+ if (e != null) {
+ sunLogger.log(sLevel, message.toString(), e);
+ } else {
+ sunLogger.log(sLevel, message.toString());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/AbstractProtostuffSerializer.java b/src/main/java/org/stefan/snrpc/serializer/AbstractProtostuffSerializer.java
new file mode 100644
index 0000000..dab0d4f
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/AbstractProtostuffSerializer.java
@@ -0,0 +1,90 @@
+package org.stefan.snrpc.serializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.stefan.snrpc.exception.SerializerException;
+import org.stefan.snrpc.util.BufferCache;
+import org.stefan.snrpc.util.IOUtils;
+import org.stefan.snrpc.util.SchemaCache;
+
+import com.dyuproject.protostuff.LinkedBuffer;
+import com.dyuproject.protostuff.Schema;
+
+/**
+ * AbstractProtostuffSerializer
+ * @author zhaoliangang 2014-11-14
+ */
+public abstract class AbstractProtostuffSerializer implements ClientSerializer,
+ ServerSerializer {
+ /**
+ * @param buffer
+ * buffer writen to
+ * @param object
+ * @param schema
+ * @return length
+ */
+ protected abstract int writeObject(LinkedBuffer buffer, T object,
+ Schema schema);
+
+ /**
+ * @param bytes
+ * @param template
+ * @param schema
+ * @return
+ */
+ protected abstract void parseObject(byte[] bytes, T template,
+ Schema schema);
+
+ public SnRpcRequest decodeRequest(InputStream inputStream)
+ throws SerializerException, IOException {
+ return decode(inputStream, new SnRpcRequest());
+ }
+
+ public void encodeResponse(OutputStream outputStream, SnRpcResponse result)
+ throws SerializerException, IOException {
+ encode(outputStream, result);
+ }
+
+ public SnRpcResponse decodeResponse(InputStream inputStream)
+ throws SerializerException, IOException {
+ return decode(inputStream, new SnRpcResponse());
+ }
+
+ public void encodeRequest(OutputStream outputStream, SnRpcRequest request)
+ throws SerializerException, IOException {
+ encode(outputStream, request);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private void encode(OutputStream out, T object) throws IOException {
+ LinkedBuffer buffer = BufferCache.getBuffer();
+ Schema schema = null;
+ if (null == object) {
+ schema = SchemaCache.getSchema(Object.class);
+ } else {
+ schema = SchemaCache.getSchema(object.getClass());
+ }
+
+ // write the length header
+ int length = writeObject(buffer, object, schema);
+ IOUtils.writeInt(out, length);
+ // write content
+ LinkedBuffer.writeTo(out, buffer);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private T decode(InputStream in, T template) throws IOException {
+ Schema schema = SchemaCache.getSchema(template.getClass());
+
+ // read the length header
+ int length = IOUtils.readInt(in);
+ // read exactly $length bytes
+ byte[] bytes = new byte[length];
+ IOUtils.readFully(in, bytes, 0, length);
+ // parse object
+ parseObject(bytes, template, schema);
+ return template;
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/ClientSerializer.java b/src/main/java/org/stefan/snrpc/serializer/ClientSerializer.java
new file mode 100644
index 0000000..4f58532
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/ClientSerializer.java
@@ -0,0 +1,37 @@
+package org.stefan.snrpc.serializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.stefan.snrpc.exception.SerializerException;
+
+/**
+ * ClientSerializer
+ * @author zhaoliangang 2014-11-13
+ */
+public interface ClientSerializer {
+ /**
+ * deserialize the inputStream
+ *
+ * @param inputStream
+ * @return
+ * @throws SerializeException
+ * @throws IOException
+ */
+ SnRpcResponse decodeResponse(InputStream inputStream)
+ throws SerializerException, IOException;
+
+ /**
+ * serialize the request object into the outputStream
+ *
+ * @param outputStream
+ * @param object
+ * @param method
+ * @param arguments
+ * @throws SerializeException
+ * @throws IOException
+ */
+ void encodeRequest(OutputStream outputStream, SnRpcRequest request)
+ throws SerializerException, IOException;
+}
\ No newline at end of file
diff --git a/src/main/java/org/stefan/snrpc/serializer/ProtobufSerializer.java b/src/main/java/org/stefan/snrpc/serializer/ProtobufSerializer.java
new file mode 100644
index 0000000..66f8f43
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/ProtobufSerializer.java
@@ -0,0 +1,32 @@
+package org.stefan.snrpc.serializer;
+
+import com.dyuproject.protostuff.LinkedBuffer;
+import com.dyuproject.protostuff.ProtobufIOUtil;
+import com.dyuproject.protostuff.Schema;
+
+/**
+ * ProtobufSerializer
+ * @author zhaoliangang 2014-11-14
+ */
+public class ProtobufSerializer extends AbstractProtostuffSerializer {
+
+ private static final ProtobufSerializer INSTANCE = new ProtobufSerializer();
+
+ private ProtobufSerializer() {
+ }
+
+ public static ProtobufSerializer getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ protected int writeObject(LinkedBuffer buffer, T object,
+ Schema schema) {
+ return ProtobufIOUtil.writeTo(buffer, object, schema);
+ }
+
+ @Override
+ protected void parseObject(byte[] bytes, T template, Schema schema) {
+ ProtobufIOUtil.mergeFrom(bytes, template, schema);
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/ServerSerializer.java b/src/main/java/org/stefan/snrpc/serializer/ServerSerializer.java
new file mode 100644
index 0000000..c5eae09
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/ServerSerializer.java
@@ -0,0 +1,35 @@
+package org.stefan.snrpc.serializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.stefan.snrpc.exception.SerializerException;
+
+/**
+ * ServerSerializer
+ * @author zhaoliangang 2014-11-13
+ */
+public interface ServerSerializer {
+ /**
+ * deserialize the inputStream
+ *
+ * @param inputStream
+ * @return
+ * @throws SerializeException
+ * @throws IOException
+ */
+ SnRpcRequest decodeRequest(InputStream inputStream)
+ throws SerializerException, IOException;
+
+ /**
+ * serialize the result object into the outputStream
+ *
+ * @param outputStream
+ * @param result
+ * @throws SerializeException
+ * @throws IOException
+ */
+ void encodeResponse(OutputStream outputStream, SnRpcResponse result)
+ throws SerializerException, IOException;
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/SnRpcRequest.java b/src/main/java/org/stefan/snrpc/serializer/SnRpcRequest.java
new file mode 100644
index 0000000..ef589ab
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/SnRpcRequest.java
@@ -0,0 +1,88 @@
+package org.stefan.snrpc.serializer;
+
+import org.stefan.snrpc.util.MessageFormatter;
+
+/**
+ * SnRpcRequest
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcRequest {
+
+ private String requestID;
+
+ private String className;
+
+ private String methodName;
+
+ private String[] parameterTypes;
+
+ private Object[] parameters;
+
+ public SnRpcRequest() {
+ }
+
+ public SnRpcRequest(String className, String methodName,
+ String[] parameterTypes, Object[] parameters) {
+ this.className = className;
+ this.methodName = methodName;
+ this.parameterTypes = parameterTypes;
+ this.parameters = parameters;
+ }
+
+ public SnRpcRequest(String requestID, String className, String methodName,
+ String[] parameterTypes, Object[] parameters) {
+ this.requestID = requestID;
+ this.className = className;
+ this.methodName = methodName;
+ this.parameterTypes = parameterTypes;
+ this.parameters = parameters;
+ }
+
+ public String getRequestID() {
+ return requestID;
+ }
+
+ public void setRequestID(String requestID) {
+ this.requestID = requestID;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public String[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public void setParameterTypes(String[] parameterTypes) {
+ this.parameterTypes = parameterTypes;
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Object[] parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormatter
+ .format("requestID: {}, className: {}, methodName: {}, parameterTypes: {}, parameters: {}",
+ new Object[] { requestID, className, methodName,
+ parameterTypes, parameters });
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/SnRpcRequestDecoder.java b/src/main/java/org/stefan/snrpc/serializer/SnRpcRequestDecoder.java
new file mode 100644
index 0000000..17253b9
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/SnRpcRequestDecoder.java
@@ -0,0 +1,37 @@
+package org.stefan.snrpc.serializer;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBufferInputStream;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+/**
+ * SnRpcRequestDecoder
+ *
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcRequestDecoder extends FrameDecoder {
+
+ private final ServerSerializer serializer;
+
+ public SnRpcRequestDecoder(ServerSerializer serializer) {
+ this.serializer = serializer;
+ }
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, Channel channel,
+ ChannelBuffer buffer) throws Exception {
+ if (buffer.readableBytes() < 4) {
+ return null;
+ }
+ int length = buffer.getInt(buffer.readerIndex());
+ if (buffer.readableBytes() < length + 4) {
+ return null;
+ }
+ ChannelBufferInputStream in = new ChannelBufferInputStream(buffer);
+ SnRpcRequest request = serializer.decodeRequest(in);
+ return request;
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/SnRpcRequestEncoder.java b/src/main/java/org/stefan/snrpc/serializer/SnRpcRequestEncoder.java
new file mode 100644
index 0000000..a9f69d6
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/SnRpcRequestEncoder.java
@@ -0,0 +1,33 @@
+package org.stefan.snrpc.serializer;
+
+import java.io.ByteArrayOutputStream;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcRequestEncoder extends SimpleChannelHandler {
+
+ private final ClientSerializer serializer;
+
+ public SnRpcRequestEncoder(ClientSerializer serializer) {
+ this.serializer = serializer;
+ }
+
+ @Override
+ public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
+ throws Exception {
+ SnRpcRequest request = (SnRpcRequest) e.getMessage();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(16384);
+ serializer.encodeRequest(baos, request);
+ ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(baos.toByteArray());
+ Channels.write(ctx, e.getFuture(), buffer);
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/SnRpcResponse.java b/src/main/java/org/stefan/snrpc/serializer/SnRpcResponse.java
new file mode 100644
index 0000000..f3d646a
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/SnRpcResponse.java
@@ -0,0 +1,55 @@
+package org.stefan.snrpc.serializer;
+
+import org.stefan.snrpc.util.MessageFormatter;
+
+/**
+ * SnRpcResponse
+ *
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcResponse {
+
+ private String requestID;
+
+ private Throwable exception;
+
+ private Object result;
+
+ public SnRpcResponse() {
+ }
+
+ public SnRpcResponse(String requestID) {
+ this.requestID = requestID;
+ }
+
+ public String getRequestID() {
+ return requestID;
+ }
+
+ public void setRequestID(String requestID) {
+ this.requestID = requestID;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public void setException(Throwable exception) {
+ this.exception = exception;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormatter.format(
+ "requestID: {}, result: {}, exception: {}", new Object[] {
+ requestID, result, exception });
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/SnRpcResponseDecoder.java b/src/main/java/org/stefan/snrpc/serializer/SnRpcResponseDecoder.java
new file mode 100644
index 0000000..7ebaae4
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/SnRpcResponseDecoder.java
@@ -0,0 +1,35 @@
+package org.stefan.snrpc.serializer;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBufferInputStream;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+/**
+ * SnRpcResponseDecoder
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcResponseDecoder extends FrameDecoder {
+
+ private final ClientSerializer serializer;
+
+ public SnRpcResponseDecoder(ClientSerializer serializer) {
+ this.serializer = serializer;
+ }
+
+ @Override
+ protected Object decode(ChannelHandlerContext context, Channel channel,
+ ChannelBuffer buffer) throws Exception {
+ if (buffer.readableBytes() < 4) {
+ return null;
+ }
+ int length = buffer.getInt(buffer.readerIndex());
+ if (buffer.readableBytes() < length + 4) {
+ return null;
+ }
+ ChannelBufferInputStream in = new ChannelBufferInputStream(buffer);
+ SnRpcResponse response = serializer.decodeResponse(in);
+ return response;
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/serializer/SnRpcResponseEncoder.java b/src/main/java/org/stefan/snrpc/serializer/SnRpcResponseEncoder.java
new file mode 100644
index 0000000..7a110ce
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/serializer/SnRpcResponseEncoder.java
@@ -0,0 +1,32 @@
+package org.stefan.snrpc.serializer;
+
+import java.io.ByteArrayOutputStream;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+
+/**
+ * SnRpcResponseEncoder
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnRpcResponseEncoder extends SimpleChannelHandler {
+ private final ServerSerializer serializer;
+
+ public SnRpcResponseEncoder(ServerSerializer serializer) {
+ this.serializer = serializer;
+ }
+
+ @Override
+ public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
+ throws Exception {
+ SnRpcResponse response = (SnRpcResponse) e.getMessage();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(16384);
+ serializer.encodeResponse(baos, response);
+ ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(baos.toByteArray());
+ Channels.write(ctx, e.getFuture(), buffer);
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/server/ParseXmlToService.java b/src/main/java/org/stefan/snrpc/server/ParseXmlToService.java
new file mode 100644
index 0000000..00de01b
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/server/ParseXmlToService.java
@@ -0,0 +1,25 @@
+package org.stefan.snrpc.server;
+
+import java.util.List;
+
+import org.stefan.snrpc.conf.ConfigureParse;
+import org.stefan.snrpc.conf.RpcService;
+import org.stefan.snrpc.conf.SnRpcConfig;
+import org.stefan.snrpc.conf.XmlConfigureParse;
+
+/**
+ * ParseXmlToService
+ *
+ * @author zhaoliangang 2014-11-14
+ */
+public class ParseXmlToService {
+
+ public void parse() {
+ String configFile = SnRpcConfig.getInstance().getpropertiesFile();
+ ConfigureParse parse = new XmlConfigureParse(configFile);
+ List serviceList = parse.parseService();
+ for (RpcService service : serviceList) {
+ SnNettyRpcServerHandler.putService(service);
+ }
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/server/SnNettyRpcServer.java b/src/main/java/org/stefan/snrpc/server/SnNettyRpcServer.java
new file mode 100644
index 0000000..f6be170
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/server/SnNettyRpcServer.java
@@ -0,0 +1,179 @@
+package org.stefan.snrpc.server;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.channel.group.ChannelGroupFuture;
+import org.jboss.netty.channel.group.DefaultChannelGroup;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
+import org.jboss.netty.handler.codec.http.HttpContentCompressor;
+import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timer;
+import org.stefan.snrpc.SnRpcServer;
+import org.stefan.snrpc.conf.SnRpcConfig;
+import org.stefan.snrpc.log.Logger;
+import org.stefan.snrpc.log.LoggerFactory;
+import org.stefan.snrpc.serializer.ProtobufSerializer;
+import org.stefan.snrpc.serializer.SnRpcRequestDecoder;
+import org.stefan.snrpc.serializer.SnRpcResponseEncoder;
+import org.stefan.snrpc.util.HandlerMapper;
+
+/**
+ *
+ * SnNettyRpcServer
+ *
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnNettyRpcServer implements SnRpcServer {
+
+ private static Logger logger = LoggerFactory
+ .getLogger(SnNettyRpcServer.class);
+
+ protected Map handlersMap;
+
+ private ServerBootstrap bootstrap = null;
+ private AtomicBoolean stopped = new AtomicBoolean(false);
+ private SnRpcConfig snRpcConfig = SnRpcConfig.getInstance();
+ private Timer timer;
+ private int httpListenPort;
+
+ public SnNettyRpcServer(Object... handlers) {
+ snRpcConfig.loadProperties("snrpcserver.properties");
+ this.handlersMap = HandlerMapper.getHandlerMap(handlers);
+ }
+
+ public SnNettyRpcServer(String fileName, Object... handlers) {
+ snRpcConfig.loadProperties(fileName);
+ this.handlersMap = HandlerMapper.getHandlerMap(handlers);
+ }
+
+ private void initServerInfo() {
+ httpListenPort = snRpcConfig.getHttpPort();
+ new ParseXmlToService().parse();
+ }
+
+ private void initHttpBootstrap() {
+ if (SnRpcConfig.getInstance().getDevMod()) {
+ StatisticsService.reportPerformance();
+ }
+ logger.info("init HTTP Bootstrap...........");
+ final ChannelGroup channelGroup = new DefaultChannelGroup(getClass()
+ .getName());
+ bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
+ Executors.newCachedThreadPool(),
+ Executors.newCachedThreadPool()));
+
+ bootstrap.setOption("tcpNoDelay", Boolean.parseBoolean(snRpcConfig
+ .getProperty("snrpc.tcp.nodelay", "true")));
+ bootstrap.setOption("reuseAddress", Boolean.parseBoolean(snRpcConfig
+ .getProperty("snrpc.tcp.reuseaddress", "true")));
+
+ timer = new HashedWheelTimer();
+ bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
+ public ChannelPipeline getPipeline() throws Exception {
+ ChannelPipeline pipeline = Channels.pipeline();
+
+ int readTimeout = snRpcConfig.getReadTimeout();
+ if (readTimeout > 0) {
+ pipeline.addLast("timeout", new ReadTimeoutHandler(timer,
+ readTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ pipeline.addLast("decoder", new SnRpcRequestDecoder(
+ ProtobufSerializer.getInstance()));
+ pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
+ pipeline.addLast("encoder", new SnRpcResponseEncoder(
+ ProtobufSerializer.getInstance()));
+ pipeline.addLast("deflater", new HttpContentCompressor());
+ pipeline.addLast("handler", new SnNettyRpcServerHandler(
+ handlersMap, channelGroup));
+ return pipeline;
+ }
+ });
+
+ if (!checkPortConfig(httpListenPort)) {
+ throw new IllegalStateException("port: " + httpListenPort
+ + " already in use!");
+ }
+
+ Channel channel = bootstrap.bind(new InetSocketAddress(httpListenPort));
+ channelGroup.add(channel);
+ logger.info("snrpc server started");
+
+ waitForShutdownCommand();
+ ChannelGroupFuture future = channelGroup.close();
+ future.awaitUninterruptibly();
+ bootstrap.releaseExternalResources();
+ timer.stop();
+ timer = null;
+
+ logger.info("snrpc server stoped");
+
+ }
+
+ public void start() {
+ initServerInfo();
+ initHttpBootstrap();
+ }
+
+ public void stop() throws Throwable {
+ stopped.set(true);
+ synchronized (stopped) {
+ stopped.notifyAll();
+ }
+ }
+
+ private void waitForShutdownCommand() {
+ synchronized (stopped) {
+ while (!stopped.get()) {
+ try {
+ stopped.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ private boolean checkPortConfig(int listenPort) {
+ if (listenPort < 0 || listenPort > 65536) {
+ throw new IllegalArgumentException("Invalid start port: "
+ + listenPort);
+ }
+ ServerSocket ss = null;
+ DatagramSocket ds = null;
+ try {
+ ss = new ServerSocket(listenPort);
+ ss.setReuseAddress(true);
+ ds = new DatagramSocket(listenPort);
+ ds.setReuseAddress(true);
+ return true;
+ } catch (IOException e) {
+ } finally {
+ if (ds != null) {
+ ds.close();
+ }
+ if (ss != null) {
+ try {
+ ss.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/server/SnNettyRpcServerHandler.java b/src/main/java/org/stefan/snrpc/server/SnNettyRpcServerHandler.java
new file mode 100644
index 0000000..93ae0c3
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/server/SnNettyRpcServerHandler.java
@@ -0,0 +1,123 @@
+package org.stefan.snrpc.server;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.stefan.snrpc.conf.RpcService;
+import org.stefan.snrpc.conf.SnRpcConfig;
+import org.stefan.snrpc.log.Logger;
+import org.stefan.snrpc.log.LoggerFactory;
+import org.stefan.snrpc.serializer.SnRpcRequest;
+import org.stefan.snrpc.serializer.SnRpcResponse;
+import org.stefan.snrpc.util.ReflectionCache;
+
+/**
+ * SnNettyRpcServerHandler
+ *
+ * @author zhaoliangang 2014-11-13
+ */
+public class SnNettyRpcServerHandler extends SimpleChannelUpstreamHandler {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(SnNettyRpcServerHandler.class);
+
+ private final Map handlersMap;
+
+ private final static Map serviceMap = new HashMap();
+
+ private final ChannelGroup channelGroups;
+
+ public SnNettyRpcServerHandler(Map handlersMap) {
+ this(handlersMap, null);
+ }
+
+ public SnNettyRpcServerHandler(Map handlersMap,
+ ChannelGroup channelGroups) {
+ this.handlersMap = handlersMap;
+ this.channelGroups = channelGroups;
+ }
+
+ public static void putService(RpcService service) {
+ if (null != service) {
+ serviceMap.put(service.getName(), service);
+ }
+ }
+
+ public static Map getServiceMap() {
+ return Collections.unmodifiableMap(serviceMap);
+ }
+
+ public static RpcService getServiceByName(String serviceName) {
+ return serviceMap.get(serviceName);
+ }
+
+ @Override
+ public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
+ throws Exception {
+ if (null != channelGroups) {
+ channelGroups.add(e.getChannel());
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
+ throws Exception {
+ SnRpcRequest request = (SnRpcRequest) ctx.getAttachment();
+ logger.warn("handle rpc request fail! request: <{}>",
+ new Object[] { request }, e.getCause());
+ e.getChannel().close().awaitUninterruptibly();
+ }
+
+ @Override
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
+ throws Exception {
+ Object msg = e.getMessage();
+ if (!(msg instanceof SnRpcRequest)) {
+ return;
+ }
+ SnRpcRequest request = (SnRpcRequest) msg;
+ ctx.setAttachment(request);
+
+ SnRpcResponse response = new SnRpcResponse(request.getRequestID());
+ try {
+ Object result = handle(request);
+ response.setResult(result);
+ } catch (Throwable t) {
+ logger.warn("handle rpc request fail! request: <{}>",
+ new Object[] { request }, t);
+ response.setException(t);
+ }
+ e.getChannel().write(response);
+ }
+
+ private Object handle(SnRpcRequest request) throws Throwable {
+ if (SnRpcConfig.getInstance().getDevMod()) {
+ StatisticsService.reportBeforeInvoke(request);
+ }
+ String className = request.getClassName();
+ String[] classNameSplits = className.split("\\.");
+ String serviceName = classNameSplits[classNameSplits.length - 1];
+ RpcService rpcService = getServiceByName(serviceName);
+ if (null == rpcService)
+ throw new NullPointerException("server interface config is null");
+
+ Class> clazz = rpcService.getRpcImplementor().getProcessorClass();
+ Method method = ReflectionCache.getMethod(clazz.getName(),
+ request.getMethodName(), request.getParameterTypes());
+ Object[] parameters = request.getParameters();
+ // get handler
+ Object handler = handlersMap.get(request.getClassName());
+ // invoke
+ Object result = method.invoke(handler, parameters);
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/server/StatisticsService.java b/src/main/java/org/stefan/snrpc/server/StatisticsService.java
new file mode 100644
index 0000000..9286b6a
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/server/StatisticsService.java
@@ -0,0 +1,73 @@
+package org.stefan.snrpc.server;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.stefan.snrpc.log.Logger;
+import org.stefan.snrpc.log.LoggerFactory;
+import org.stefan.snrpc.serializer.SnRpcRequest;
+
+/**
+ * StatisticsService
+ *
+ * @author zhaoliangang 2014-11-13
+ */
+public class StatisticsService {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(StatisticsService.class);
+
+ private static final AtomicLong requestTimes = new AtomicLong();
+ private static final SimpleDateFormat df = new SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss");
+
+ static final boolean reportBeforeInvoke(SnRpcRequest request) {
+ doReport(request);
+ return true;
+ }
+
+ private static final void doReport(SnRpcRequest request) {
+ requestTimes.getAndIncrement();
+ StringBuilder tip = new StringBuilder(
+ "\nsnRpc request report -------- ").append(
+ df.format(new Date())).append(
+ " ------------------------------\n");
+ String className = request.getClassName();
+ String methodName = request.getMethodName();
+ String requestId = request.getRequestID();
+ Object[] param = request.getParameters();
+ tip.append("requestId : ").append(requestId);
+ tip.append("className : ").append(className);
+ tip.append("method : ").append(methodName);
+ tip.append("param[0] : ").append(param[0]).append("\n");
+ tip.append("--------------------------------------------------------------------------------\n");
+ System.out.print(tip.toString());
+ }
+
+ static final void reportPerformance() {
+ new Thread(new Runnable() {
+ public void run() {
+ final long begin = System.currentTimeMillis();
+ while (true) {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ }
+ long pass = System.currentTimeMillis() - begin;
+ long totalMemory = Runtime.getRuntime().totalMemory();
+ long freeMemory = Runtime.getRuntime().freeMemory();
+ long usedMemory = totalMemory - freeMemory;
+ java.text.NumberFormat format = new java.text.DecimalFormat(
+ "###,###");
+ String memoryInfo = format.format(usedMemory) + "/"
+ + format.format(totalMemory);
+ logger.warn("\r\nMemory:" + memoryInfo + ",Time:"
+ + df.format(new Date()) + ",Time passed:" + pass
+ + ",Total:" + requestTimes.get() + "(Average TPS:"
+ + (requestTimes.get() * 1000 / pass) + ")");
+ }
+ }
+ }).start();
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/BufferCache.java b/src/main/java/org/stefan/snrpc/util/BufferCache.java
new file mode 100644
index 0000000..7db7c56
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/BufferCache.java
@@ -0,0 +1,22 @@
+package org.stefan.snrpc.util;
+
+import com.dyuproject.protostuff.LinkedBuffer;
+
+/**
+ * BufferCache
+ * @author zhaoliangang 2014-11-13
+ */
+public class BufferCache {
+
+ private static ThreadLocal BUFFERS = new ThreadLocal() {
+ protected LinkedBuffer initialValue() {
+ return LinkedBuffer.allocate(4096);
+ };
+ };
+
+ public static LinkedBuffer getBuffer() {
+ LinkedBuffer buffer = BUFFERS.get();
+ buffer.clear();
+ return buffer;
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/FileUtil.java b/src/main/java/org/stefan/snrpc/util/FileUtil.java
new file mode 100644
index 0000000..dd63dae
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/FileUtil.java
@@ -0,0 +1,12 @@
+package org.stefan.snrpc.util;
+
+import java.io.File;
+
+public class FileUtil {
+
+ public static File getFile(String path) {
+ String applicationPath = FileUtil.class.getClassLoader()
+ .getResource("").getPath();
+ return new File(applicationPath, path);
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/HandlerMapper.java b/src/main/java/org/stefan/snrpc/util/HandlerMapper.java
new file mode 100644
index 0000000..1de028c
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/HandlerMapper.java
@@ -0,0 +1,48 @@
+package org.stefan.snrpc.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author zhaoliangang 2014-11-14
+ */
+public class HandlerMapper {
+
+ public static Map getHandlerMap(Object... handlers) {
+ if (null == handlers || handlers.length == 0) {
+ throw new IllegalArgumentException("handlers not provided");
+ }
+ Map handlerMap = new HashMap();
+ for (Object handler : handlers) {
+ Class>[] interfaces = handler.getClass().getInterfaces();
+ for (Class> iface : interfaces) {
+ String interfaceName = iface.getName();
+ if (ignore(interfaceName)) {
+ continue;
+ }
+ if (null != handlerMap.put(interfaceName, handler)) {
+ throw new IllegalArgumentException(
+ "more than one handler for the interface ["
+ + interfaceName + "]");
+ }
+ }
+ }
+ return handlerMap;
+ }
+
+ private static boolean ignore(String interfaceName) {
+ if (interfaceName.startsWith("java.")) {
+ return true;
+ }
+ if (interfaceName.startsWith("javax.")) {
+ return true;
+ }
+ if (interfaceName.startsWith("sun.")) {
+ return true;
+ }
+ if (interfaceName.startsWith("com.sun.")) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/IOUtils.java b/src/main/java/org/stefan/snrpc/util/IOUtils.java
new file mode 100644
index 0000000..1250e66
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/IOUtils.java
@@ -0,0 +1,103 @@
+package org.stefan.snrpc.util;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public class IOUtils {
+ /**
+ * block until exactly length
bytes read into
+ * bytes
+ *
+ * @param in
+ * @param bytes
+ * @param offset
+ * @param length
+ * @throws IOException
+ */
+ public static void readFully(InputStream in, byte[] bytes, int offset,
+ int length) throws IOException {
+ if (length < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ int n = 0;
+ while (n < length) {
+ int count = in.read(bytes, offset + n, length - n);
+ if (count < 0) {
+ throw new EOFException();
+ }
+ n += count;
+ }
+ }
+
+ /**
+ * write an integer to the output stream
+ *
+ * @param out
+ * @param value
+ * @throws IOException
+ */
+ public static void writeInt(OutputStream out, int value) throws IOException {
+ out.write((value >>> 24) & 0xFF);
+ out.write((value >>> 16) & 0xFF);
+ out.write((value >>> 8) & 0xFF);
+ out.write((value >>> 0) & 0xFF);
+ }
+
+ /**
+ * read an integer from the input stream
+ *
+ * @param in
+ * @return
+ * @throws IOException
+ */
+ public static int readInt(InputStream in) throws IOException {
+ int ch1 = in.read();
+ int ch2 = in.read();
+ int ch3 = in.read();
+ int ch4 = in.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0) {
+ throw new EOFException();
+ }
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
+ }
+
+ public static void closeQuietly(Closeable closeable) {
+ if (null == closeable) {
+ return;
+ }
+ try {
+ closeable.close();
+ } catch (Throwable t) {
+ }
+ }
+
+ public static void closeQuietly(Socket socket) {
+ if (null == socket) {
+ return;
+ }
+ if (!socket.isInputShutdown()) {
+ try {
+ socket.shutdownInput();
+ } catch (IOException e) {
+ }
+ }
+ if (!socket.isOutputShutdown()) {
+ try {
+ socket.shutdownOutput();
+ } catch (IOException e) {
+ }
+ }
+ try {
+ socket.close();
+ } catch (Throwable t) {
+ }
+ }
+
+}
diff --git a/src/main/java/org/stefan/snrpc/util/LRUMap.java b/src/main/java/org/stefan/snrpc/util/LRUMap.java
new file mode 100644
index 0000000..c5943c8
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/LRUMap.java
@@ -0,0 +1,86 @@
+package org.stefan.snrpc.util;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public class LRUMap extends LinkedHashMap implements Map {
+ private static final long serialVersionUID = -188971896404993320L;
+ private int maxSize;
+
+ public LRUMap(int maxSize) {
+ super((int) Math.ceil((1f * maxSize) / 0.75f) + 16, 0.75f, true);
+ this.maxSize = maxSize;
+ }
+
+ @Override
+ protected synchronized boolean removeEldestEntry(
+ java.util.Map.Entry eldest) {
+ boolean delete = size() > maxSize;
+ return delete;
+ }
+
+ @Override
+ public synchronized int size() {
+ return super.size();
+ }
+
+ @Override
+ public synchronized V get(Object key) {
+ return super.get(key);
+ }
+
+ @Override
+ public synchronized V remove(Object key) {
+ return super.remove(key);
+ }
+
+ @Override
+ public synchronized void clear() {
+ super.clear();
+ }
+
+ @Override
+ public synchronized boolean isEmpty() {
+ return super.isEmpty();
+ }
+
+ @Override
+ public synchronized boolean containsKey(Object key) {
+ return super.containsKey(key);
+ }
+
+ @Override
+ public synchronized boolean containsValue(Object value) {
+ return super.containsValue(value);
+ }
+
+ @Override
+ public synchronized V put(K key, V value) {
+ return super.put(key, value);
+ }
+
+ @Override
+ public synchronized void putAll(Map extends K, ? extends V> m) {
+ super.putAll(m);
+ }
+
+ @Override
+ public synchronized Set keySet() {
+ return super.keySet();
+ }
+
+ @Override
+ public synchronized Collection values() {
+ return super.values();
+ }
+
+ @Override
+ public synchronized Set> entrySet() {
+ return super.entrySet();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/stefan/snrpc/util/MessageFormatter.java b/src/main/java/org/stefan/snrpc/util/MessageFormatter.java
new file mode 100644
index 0000000..1b59918
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/MessageFormatter.java
@@ -0,0 +1,293 @@
+package org.stefan.snrpc.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @author zhaoliangang
+ * 2014-11-20
+ */
+public class MessageFormatter {
+ public static final String DEFAULT_PLACE_HOLDER = "{}";
+
+ public static final char DEFAULT_ESCAPE_CHAR = '\\';
+
+ /**
+ * equivalent to
+ * LogFormatter.format(msgPattern, "{}", '\\', args)
+ *
+ * @param msgPattern
+ * @param args
+ * @return
+ */
+ public static String format(String msgPattern, Object[] args) {
+ return format(msgPattern, DEFAULT_PLACE_HOLDER, DEFAULT_ESCAPE_CHAR,
+ args);
+ }
+
+ /**
+ * equivalent to
+ * LogFormatter.format(msgPattern, placeholder, '\\', args)
+ *
+ * @param msgPattern
+ * @param placeholder
+ * @param args
+ * @return
+ */
+ public static String format(String msgPattern, String placeholder,
+ Object[] args) {
+ return format(msgPattern, placeholder, DEFAULT_ESCAPE_CHAR, args);
+ }
+
+ /**
+ * @param msgPattern
+ * @param placeholder
+ * @param escapeChar
+ * @param args
+ * @return
+ */
+ @SuppressWarnings("rawtypes")
+ public static String format(String msgPattern, String placeholder,
+ char escapeChar, Object[] args) {
+ if (null == msgPattern || msgPattern.length() == 0) {
+ return null;
+ }
+ if (null == args || args.length == 0) {
+ return msgPattern;
+ }
+ if (null == placeholder || "".equals(placeholder)) {
+ return msgPattern;
+ }
+
+ int lastMatchedIndex = 0, currentMatchedIndex = 0;
+ StringBuilder strbuf = new StringBuilder(msgPattern.length() + 64);
+
+ for (int argIndex = 0; argIndex < args.length; argIndex++) {
+ currentMatchedIndex = msgPattern.indexOf(placeholder,
+ lastMatchedIndex);
+
+ if (-1 == currentMatchedIndex) {
+ // no more variables
+ if (0 == lastMatchedIndex) {
+ // this is a simple string
+ return msgPattern;
+ } else {
+ // add the tail string which contains no variables
+ strbuf.append(msgPattern.substring(lastMatchedIndex,
+ msgPattern.length()));
+ return strbuf.toString();
+ }
+ } else {
+ // successive escape chars before the placeholder
+ int cnt = countSuccessiveEscapeChar(msgPattern, escapeChar,
+ currentMatchedIndex, lastMatchedIndex);
+ if (0 == cnt) { // all escaped itself
+ strbuf.append(msgPattern.substring(lastMatchedIndex,
+ currentMatchedIndex));
+ deeplyAppendParameter(strbuf, args[argIndex], new HashMap());
+ lastMatchedIndex = currentMatchedIndex
+ + placeholder.length();
+ } else {
+ int escapeItselfCnt = cnt / 2;
+ strbuf.append(msgPattern.substring(lastMatchedIndex,
+ (currentMatchedIndex - cnt + escapeItselfCnt)));
+ if (cnt % 2 != 0) {
+ argIndex--;// placeholder was escaped, thus should not
+ // be incremented
+ strbuf.append(placeholder.charAt(0));
+ lastMatchedIndex = currentMatchedIndex + 1;
+ } else {
+ deeplyAppendParameter(strbuf, args[argIndex],
+ new HashMap());
+ lastMatchedIndex = currentMatchedIndex
+ + placeholder.length();
+ }
+ }
+ }
+ }
+ // append the characters following the last {} pair.
+ strbuf.append(msgPattern.substring(lastMatchedIndex,
+ msgPattern.length()));
+ return strbuf.toString();
+ }
+
+ private static int countSuccessiveEscapeChar(String msgPattern,
+ char escapeChar, int delimeterStartIndex, int delimeterStopIndex) {
+ if (0 == delimeterStartIndex) {
+ return 0;
+ }
+ int cnt = 0;
+ for (int i = delimeterStartIndex - 1; i >= delimeterStopIndex; i--) {
+ if (msgPattern.charAt(i) == escapeChar) {
+ ++cnt;
+ } else {
+ break;
+ }
+ }
+ return cnt;
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static void deeplyAppendParameter(StringBuilder sbuf, Object o,
+ Map seenMap) {
+ if (o == null) {
+ sbuf.append("null");
+ return;
+ }
+ if (!o.getClass().isArray()) {
+ safeObjectAppend(sbuf, o);
+ } else {
+ // check for primitive array types because they
+ // unfortunately cannot be cast to Object[]
+ if (o instanceof boolean[]) {
+ booleanArrayAppend(sbuf, (boolean[]) o);
+ } else if (o instanceof byte[]) {
+ byteArrayAppend(sbuf, (byte[]) o);
+ } else if (o instanceof char[]) {
+ charArrayAppend(sbuf, (char[]) o);
+ } else if (o instanceof short[]) {
+ shortArrayAppend(sbuf, (short[]) o);
+ } else if (o instanceof int[]) {
+ intArrayAppend(sbuf, (int[]) o);
+ } else if (o instanceof long[]) {
+ longArrayAppend(sbuf, (long[]) o);
+ } else if (o instanceof float[]) {
+ floatArrayAppend(sbuf, (float[]) o);
+ } else if (o instanceof double[]) {
+ doubleArrayAppend(sbuf, (double[]) o);
+ } else {
+ objectArrayAppend(sbuf, (Object[]) o, seenMap);
+ }
+ }
+ }
+
+ private static void safeObjectAppend(StringBuilder sbuf, Object o) {
+ try {
+ String oAsString = o.toString();
+ sbuf.append(oAsString);
+ } catch (Throwable t) {
+ System.err
+ .println("Failed toString() invocation on an object of type ["
+ + o.getClass().getName() + "]");
+ t.printStackTrace();
+ sbuf.append("[FAILED toString()]");
+ }
+
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static void objectArrayAppend(StringBuilder sbuf, Object[] a,
+ Map seenMap) {
+ sbuf.append('[');
+ if (!seenMap.containsKey(a)) {
+ seenMap.put(a, null);
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ deeplyAppendParameter(sbuf, a[i], seenMap);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ // allow repeats in siblings
+ seenMap.remove(a);
+ } else {
+ sbuf.append("...");
+ }
+ sbuf.append(']');
+ }
+
+ private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ sbuf.append(']');
+ }
+
+ private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1)
+ sbuf.append(", ");
+ }
+ sbuf.append(']');
+ }
+
+ private static void charArrayAppend(StringBuilder sbuf, char[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1)
+ sbuf.append(", ");
+ }
+ sbuf.append(']');
+ }
+
+ private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ sbuf.append(']');
+ }
+
+ private static void intArrayAppend(StringBuilder sbuf, int[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ sbuf.append(']');
+ }
+
+ private static void longArrayAppend(StringBuilder sbuf, long[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ sbuf.append(']');
+ }
+
+ private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ sbuf.append(']');
+ }
+
+ private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
+ sbuf.append('[');
+ final int len = a.length;
+ for (int i = 0; i < len; i++) {
+ sbuf.append(a[i]);
+ if (i != len - 1) {
+ sbuf.append(", ");
+ }
+ }
+ sbuf.append(']');
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/ReflectionCache.java b/src/main/java/org/stefan/snrpc/util/ReflectionCache.java
new file mode 100644
index 0000000..b934638
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/ReflectionCache.java
@@ -0,0 +1,89 @@
+package org.stefan.snrpc.util;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author zhaoliangang 2014-11-13
+ */
+public class ReflectionCache {
+ private static final Map> PRIMITIVE_CLASS = new HashMap>();
+
+ private static final LRUMap> CLASS_CACHE = new LRUMap>(
+ 128);
+
+ private static final LRUMap METHOD_CACHE = new LRUMap(
+ 1024);
+ static {
+ PRIMITIVE_CLASS.put("boolean", boolean.class);
+ PRIMITIVE_CLASS.put("byte", byte.class);
+ PRIMITIVE_CLASS.put("short", short.class);
+ PRIMITIVE_CLASS.put("int", int.class);
+ PRIMITIVE_CLASS.put("long", long.class);
+ PRIMITIVE_CLASS.put("long", long.class);
+ PRIMITIVE_CLASS.put("float", float.class);
+ PRIMITIVE_CLASS.put("double", double.class);
+ PRIMITIVE_CLASS.put("void", void.class);
+
+ CLASS_CACHE.putAll(PRIMITIVE_CLASS);
+ }
+
+ public static Class> getClass(String className)
+ throws ClassNotFoundException {
+ Class> clazz = CLASS_CACHE.get(className);
+ if (null != clazz) {
+ return clazz;
+ }
+ synchronized (CLASS_CACHE) {
+ if (null == CLASS_CACHE.get(className)) {
+ clazz = PRIMITIVE_CLASS.get(className);
+ if (null == clazz) {
+ clazz = Class.forName(className);
+ }
+ CLASS_CACHE.put(className, clazz);
+ return clazz;
+ } else {
+ return CLASS_CACHE.get(className);
+ }
+ }
+ }
+
+ public static Method getMethod(String className, String methodName,
+ String[] parameterTypes) throws ClassNotFoundException,
+ SecurityException, NoSuchMethodException {
+ String key = className + "-" + methodName + "-"
+ + join(parameterTypes, ";");
+ Method method = METHOD_CACHE.get(key);
+ if (null != method) {
+ return method;
+ }
+ synchronized (METHOD_CACHE) {
+ if (null == METHOD_CACHE.get(key)) {
+ Class> clazz = getClass(className);
+ Class>[] parameterClasses = new Class>[parameterTypes.length];
+ for (int i = 0; i < parameterClasses.length; i++) {
+ parameterClasses[i] = getClass(parameterTypes[i]);
+ }
+
+ method = clazz.getMethod(methodName, parameterClasses);
+ METHOD_CACHE.put(key, method);
+ return method;
+ } else {
+ return METHOD_CACHE.get(key);
+ }
+ }
+ }
+
+ private static String join(String[] strs, String seperator) {
+ if (null == strs || 0 == strs.length) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder(1024);
+ sb.append(strs[0]);
+ for (int i = 1; i < strs.length; i++) {
+ sb.append(seperator).append(strs[i]);
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/SchemaCache.java b/src/main/java/org/stefan/snrpc/util/SchemaCache.java
new file mode 100644
index 0000000..a9c84cb
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/SchemaCache.java
@@ -0,0 +1,60 @@
+package org.stefan.snrpc.util;
+
+import org.stefan.snrpc.serializer.SnRpcRequest;
+import org.stefan.snrpc.serializer.SnRpcResponse;
+
+import com.dyuproject.protostuff.Schema;
+import com.dyuproject.protostuff.runtime.RuntimeSchema;
+
+/**
+ * SchemaCache
+ * @author zhaoliangang
+ * 2014-11-20
+ */
+public class SchemaCache {
+
+ private static final LRUMap> SCHEMA_CACHE = new LRUMap>(
+ 4096);
+
+ @SuppressWarnings("unchecked")
+ public static Schema getSchema(Class clazz) {
+ String className = clazz.getName();
+ Schema schema = (Schema) SCHEMA_CACHE.get(className);
+ if (null != schema) {
+ return schema;
+ }
+ synchronized (SCHEMA_CACHE) {
+ if (null == SCHEMA_CACHE.get(className)) {
+ schema = RuntimeSchema.getSchema(clazz);
+ SCHEMA_CACHE.put(className, schema);
+ return schema;
+ } else {
+ return (Schema) SCHEMA_CACHE.get(className);
+ }
+ }
+ }
+
+ public static Schema getSchema(SnRpcRequest request) {
+ Schema schema = getSchema(SnRpcRequest.class);
+ Object[] parameters = request.getParameters();
+ if (null != parameters && parameters.length > 0) {
+ for (Object param : parameters) {
+ if (null != param) {
+ getSchema(param.getClass());
+ }
+ }
+ }
+ return schema;
+ }
+
+ public static Schema getSchema(SnRpcResponse response) {
+ Schema schema = getSchema(SnRpcResponse.class);
+ if (response.getException() != null) {
+ getSchema(response.getException().getClass());
+ }
+ if (response.getResult() != null) {
+ getSchema(response.getResult().getClass());
+ }
+ return schema;
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/Sequence.java b/src/main/java/org/stefan/snrpc/util/Sequence.java
new file mode 100644
index 0000000..cf3c277
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/Sequence.java
@@ -0,0 +1,21 @@
+package org.stefan.snrpc.util;
+
+/**
+ * @author zhaoliangang
+ * 2014-11-14
+ */
+public class Sequence{
+ private static final Object locker = new Object();
+ private static int sequence = 1000;
+
+ public static int next()
+ {
+ synchronized (locker)
+ {
+ sequence++;
+ if (sequence < 0)
+ sequence = 1;
+ return sequence;
+ }
+ }
+}
diff --git a/src/main/java/org/stefan/snrpc/util/StringUtil.java b/src/main/java/org/stefan/snrpc/util/StringUtil.java
new file mode 100644
index 0000000..0165559
--- /dev/null
+++ b/src/main/java/org/stefan/snrpc/util/StringUtil.java
@@ -0,0 +1,16 @@
+package org.stefan.snrpc.util;
+
+/**
+ * @author zhaoliangang 2014-11-12
+ */
+public class StringUtil {
+
+ public static boolean isEmpty(String str) {
+ return str == null || "".equals(str.trim()) ? true : false;
+ }
+
+ public static boolean isNotEmpty(String str) {
+ return str == null || "".equals(str.trim()) ? false : true;
+ }
+
+}
diff --git a/src/test/java/org/stefan/snrpc/Client.java b/src/test/java/org/stefan/snrpc/Client.java
new file mode 100644
index 0000000..1540671
--- /dev/null
+++ b/src/test/java/org/stefan/snrpc/Client.java
@@ -0,0 +1,28 @@
+package org.stefan.snrpc;
+
+import org.stefan.snrpc.client.CommonSnRpcClient;
+import org.stefan.snrpc.client.PoolableRpcConnectionFactory;
+import org.stefan.snrpc.client.SnNettyRpcConnectionFactory;
+import org.stefan.snrpc.server.SnRpcInterface;
+
+/**
+ * @author zhaoliangang 2014-11-14
+ */
+public class Client {
+
+ public static void main(String[] args) {
+ SnRpcConnectionFactory factory = new SnNettyRpcConnectionFactory(
+ "localhost", 8080);
+ factory = new PoolableRpcConnectionFactory(factory);
+ SnRpcClient client = new CommonSnRpcClient(factory);
+ try {
+ SnRpcInterface clazz = client.proxy(SnRpcInterface.class);
+ String message = clazz.getMessage("come on");
+ System.out.println("client receive message .... : " + message);
+ } catch (Throwable e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/src/test/java/org/stefan/snrpc/Server.java b/src/test/java/org/stefan/snrpc/Server.java
new file mode 100644
index 0000000..7b07468
--- /dev/null
+++ b/src/test/java/org/stefan/snrpc/Server.java
@@ -0,0 +1,22 @@
+package org.stefan.snrpc;
+
+import org.stefan.snrpc.server.SnNettyRpcServer;
+import org.stefan.snrpc.server.SnRpcImpl;
+import org.stefan.snrpc.server.SnRpcInterface;
+
+/**
+ * @author zhaoliangang 2014-11-14
+ */
+public class Server {
+
+ public static void main(String[] args) {
+
+ SnRpcInterface inter = new SnRpcImpl();
+ SnRpcServer server = new SnNettyRpcServer(new Object[] { inter });
+ try {
+ server.start();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/org/stefan/snrpc/server/SnRpcImpl.java b/src/test/java/org/stefan/snrpc/server/SnRpcImpl.java
new file mode 100644
index 0000000..df54e81
--- /dev/null
+++ b/src/test/java/org/stefan/snrpc/server/SnRpcImpl.java
@@ -0,0 +1,12 @@
+package org.stefan.snrpc.server;
+
+/**
+ * @author zhaoliangang 2014-11-14
+ */
+public class SnRpcImpl implements SnRpcInterface {
+
+ public String getMessage(String param) {
+ return "hi,it is message from server...param+" + param;
+ }
+
+}
diff --git a/src/test/java/org/stefan/snrpc/server/SnRpcInterface.java b/src/test/java/org/stefan/snrpc/server/SnRpcInterface.java
new file mode 100644
index 0000000..74bd36e
--- /dev/null
+++ b/src/test/java/org/stefan/snrpc/server/SnRpcInterface.java
@@ -0,0 +1,9 @@
+package org.stefan.snrpc.server;
+
+/**
+ * @author zhaoliangang 2014-11-14
+ */
+public interface SnRpcInterface {
+
+ public String getMessage(String param);
+}
diff --git a/src/test/resources/config.xml b/src/test/resources/config.xml
new file mode 100644
index 0000000..a4f9341
--- /dev/null
+++ b/src/test/resources/config.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/snrpcserver.properties b/src/test/resources/snrpcserver.properties
new file mode 100644
index 0000000..e154b8f
--- /dev/null
+++ b/src/test/resources/snrpcserver.properties
@@ -0,0 +1,10 @@
+#tcpNoDelay
+snrpc.tcp.nodelay=true
+#call the bind method as many times as you want
+snrpc.tcp.reuseAddress=true
+#ISDEBUG
+snrpc.dev=true
+#TCP timeout
+snrpc.read.timeout=25000
+#server port
+snrpc.http.port=8080
diff --git a/target/classes/config.xml b/target/classes/config.xml
new file mode 100644
index 0000000..a4f9341
--- /dev/null
+++ b/target/classes/config.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/target/classes/org/stefan/snrpc/SnRpcClient.class b/target/classes/org/stefan/snrpc/SnRpcClient.class
new file mode 100644
index 0000000..ac9b009
Binary files /dev/null and b/target/classes/org/stefan/snrpc/SnRpcClient.class differ
diff --git a/target/classes/org/stefan/snrpc/SnRpcConnection.class b/target/classes/org/stefan/snrpc/SnRpcConnection.class
new file mode 100644
index 0000000..819c3f4
Binary files /dev/null and b/target/classes/org/stefan/snrpc/SnRpcConnection.class differ
diff --git a/target/classes/org/stefan/snrpc/SnRpcConnectionFactory.class b/target/classes/org/stefan/snrpc/SnRpcConnectionFactory.class
new file mode 100644
index 0000000..02d846f
Binary files /dev/null and b/target/classes/org/stefan/snrpc/SnRpcConnectionFactory.class differ
diff --git a/target/classes/org/stefan/snrpc/SnRpcServer.class b/target/classes/org/stefan/snrpc/SnRpcServer.class
new file mode 100644
index 0000000..18b4e80
Binary files /dev/null and b/target/classes/org/stefan/snrpc/SnRpcServer.class differ
diff --git a/target/classes/org/stefan/snrpc/client/CommonSnRpcClient$SnRpcInvoker.class b/target/classes/org/stefan/snrpc/client/CommonSnRpcClient$SnRpcInvoker.class
new file mode 100644
index 0000000..053e9aa
Binary files /dev/null and b/target/classes/org/stefan/snrpc/client/CommonSnRpcClient$SnRpcInvoker.class differ
diff --git a/target/classes/org/stefan/snrpc/client/CommonSnRpcClient.class b/target/classes/org/stefan/snrpc/client/CommonSnRpcClient.class
new file mode 100644
index 0000000..c0a255a
Binary files /dev/null and b/target/classes/org/stefan/snrpc/client/CommonSnRpcClient.class differ
diff --git a/target/classes/org/stefan/snrpc/client/PoolableRpcConnectionFactory.class b/target/classes/org/stefan/snrpc/client/PoolableRpcConnectionFactory.class
new file mode 100644
index 0000000..b262956
Binary files /dev/null and b/target/classes/org/stefan/snrpc/client/PoolableRpcConnectionFactory.class differ
diff --git a/target/classes/org/stefan/snrpc/client/SnNettyRpcConnection$1.class b/target/classes/org/stefan/snrpc/client/SnNettyRpcConnection$1.class
new file mode 100644
index 0000000..4bba6b4
Binary files /dev/null and b/target/classes/org/stefan/snrpc/client/SnNettyRpcConnection$1.class differ
diff --git a/target/classes/org/stefan/snrpc/client/SnNettyRpcConnection.class b/target/classes/org/stefan/snrpc/client/SnNettyRpcConnection.class
new file mode 100644
index 0000000..3bf6a8d
Binary files /dev/null and b/target/classes/org/stefan/snrpc/client/SnNettyRpcConnection.class differ
diff --git a/target/classes/org/stefan/snrpc/client/SnNettyRpcConnectionFactory.class b/target/classes/org/stefan/snrpc/client/SnNettyRpcConnectionFactory.class
new file mode 100644
index 0000000..9120307
Binary files /dev/null and b/target/classes/org/stefan/snrpc/client/SnNettyRpcConnectionFactory.class differ
diff --git a/target/classes/org/stefan/snrpc/conf/ConfigureParse.class b/target/classes/org/stefan/snrpc/conf/ConfigureParse.class
new file mode 100644
index 0000000..168d9fa
Binary files /dev/null and b/target/classes/org/stefan/snrpc/conf/ConfigureParse.class differ
diff --git a/target/classes/org/stefan/snrpc/conf/RpcImplementor.class b/target/classes/org/stefan/snrpc/conf/RpcImplementor.class
new file mode 100644
index 0000000..6f35047
Binary files /dev/null and b/target/classes/org/stefan/snrpc/conf/RpcImplementor.class differ
diff --git a/target/classes/org/stefan/snrpc/conf/RpcService.class b/target/classes/org/stefan/snrpc/conf/RpcService.class
new file mode 100644
index 0000000..fb89923
Binary files /dev/null and b/target/classes/org/stefan/snrpc/conf/RpcService.class differ
diff --git a/target/classes/org/stefan/snrpc/conf/SnRpcConfig.class b/target/classes/org/stefan/snrpc/conf/SnRpcConfig.class
new file mode 100644
index 0000000..a5c1e55
Binary files /dev/null and b/target/classes/org/stefan/snrpc/conf/SnRpcConfig.class differ
diff --git a/target/classes/org/stefan/snrpc/conf/XmlConfigureParse.class b/target/classes/org/stefan/snrpc/conf/XmlConfigureParse.class
new file mode 100644
index 0000000..36446ed
Binary files /dev/null and b/target/classes/org/stefan/snrpc/conf/XmlConfigureParse.class differ
diff --git a/target/classes/org/stefan/snrpc/exception/SerializerException.class b/target/classes/org/stefan/snrpc/exception/SerializerException.class
new file mode 100644
index 0000000..0e06689
Binary files /dev/null and b/target/classes/org/stefan/snrpc/exception/SerializerException.class differ
diff --git a/target/classes/org/stefan/snrpc/exception/SnRpcException.class b/target/classes/org/stefan/snrpc/exception/SnRpcException.class
new file mode 100644
index 0000000..93fe189
Binary files /dev/null and b/target/classes/org/stefan/snrpc/exception/SnRpcException.class differ
diff --git a/target/classes/org/stefan/snrpc/log/AbstractLogger.class b/target/classes/org/stefan/snrpc/log/AbstractLogger.class
new file mode 100644
index 0000000..deab049
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/AbstractLogger.class differ
diff --git a/target/classes/org/stefan/snrpc/log/DefaultLogger.class b/target/classes/org/stefan/snrpc/log/DefaultLogger.class
new file mode 100644
index 0000000..6106023
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/DefaultLogger.class differ
diff --git a/target/classes/org/stefan/snrpc/log/Level.class b/target/classes/org/stefan/snrpc/log/Level.class
new file mode 100644
index 0000000..ae97459
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/Level.class differ
diff --git a/target/classes/org/stefan/snrpc/log/Log4JLogger.class b/target/classes/org/stefan/snrpc/log/Log4JLogger.class
new file mode 100644
index 0000000..2605f02
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/Log4JLogger.class differ
diff --git a/target/classes/org/stefan/snrpc/log/Logger.class b/target/classes/org/stefan/snrpc/log/Logger.class
new file mode 100644
index 0000000..b5e2ad0
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/Logger.class differ
diff --git a/target/classes/org/stefan/snrpc/log/LoggerFactory.class b/target/classes/org/stefan/snrpc/log/LoggerFactory.class
new file mode 100644
index 0000000..1310bd5
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/LoggerFactory.class differ
diff --git a/target/classes/org/stefan/snrpc/log/SunLogger.class b/target/classes/org/stefan/snrpc/log/SunLogger.class
new file mode 100644
index 0000000..3890ca6
Binary files /dev/null and b/target/classes/org/stefan/snrpc/log/SunLogger.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/AbstractProtostuffSerializer.class b/target/classes/org/stefan/snrpc/serializer/AbstractProtostuffSerializer.class
new file mode 100644
index 0000000..bec8b38
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/AbstractProtostuffSerializer.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/ClientSerializer.class b/target/classes/org/stefan/snrpc/serializer/ClientSerializer.class
new file mode 100644
index 0000000..c8dec47
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/ClientSerializer.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/ProtobufSerializer.class b/target/classes/org/stefan/snrpc/serializer/ProtobufSerializer.class
new file mode 100644
index 0000000..7a5bcba
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/ProtobufSerializer.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/ServerSerializer.class b/target/classes/org/stefan/snrpc/serializer/ServerSerializer.class
new file mode 100644
index 0000000..a359752
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/ServerSerializer.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/SnRpcRequest.class b/target/classes/org/stefan/snrpc/serializer/SnRpcRequest.class
new file mode 100644
index 0000000..40a1cf6
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/SnRpcRequest.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/SnRpcRequestDecoder.class b/target/classes/org/stefan/snrpc/serializer/SnRpcRequestDecoder.class
new file mode 100644
index 0000000..5687ae7
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/SnRpcRequestDecoder.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/SnRpcRequestEncoder.class b/target/classes/org/stefan/snrpc/serializer/SnRpcRequestEncoder.class
new file mode 100644
index 0000000..6abaced
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/SnRpcRequestEncoder.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/SnRpcResponse.class b/target/classes/org/stefan/snrpc/serializer/SnRpcResponse.class
new file mode 100644
index 0000000..b288b07
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/SnRpcResponse.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/SnRpcResponseDecoder.class b/target/classes/org/stefan/snrpc/serializer/SnRpcResponseDecoder.class
new file mode 100644
index 0000000..ad89e1a
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/SnRpcResponseDecoder.class differ
diff --git a/target/classes/org/stefan/snrpc/serializer/SnRpcResponseEncoder.class b/target/classes/org/stefan/snrpc/serializer/SnRpcResponseEncoder.class
new file mode 100644
index 0000000..945c079
Binary files /dev/null and b/target/classes/org/stefan/snrpc/serializer/SnRpcResponseEncoder.class differ
diff --git a/target/classes/org/stefan/snrpc/server/ParseXmlToService.class b/target/classes/org/stefan/snrpc/server/ParseXmlToService.class
new file mode 100644
index 0000000..121590e
Binary files /dev/null and b/target/classes/org/stefan/snrpc/server/ParseXmlToService.class differ
diff --git a/target/classes/org/stefan/snrpc/server/SnNettyRpcServer$1.class b/target/classes/org/stefan/snrpc/server/SnNettyRpcServer$1.class
new file mode 100644
index 0000000..8bf2335
Binary files /dev/null and b/target/classes/org/stefan/snrpc/server/SnNettyRpcServer$1.class differ
diff --git a/target/classes/org/stefan/snrpc/server/SnNettyRpcServer.class b/target/classes/org/stefan/snrpc/server/SnNettyRpcServer.class
new file mode 100644
index 0000000..4a85a75
Binary files /dev/null and b/target/classes/org/stefan/snrpc/server/SnNettyRpcServer.class differ
diff --git a/target/classes/org/stefan/snrpc/server/SnNettyRpcServerHandler.class b/target/classes/org/stefan/snrpc/server/SnNettyRpcServerHandler.class
new file mode 100644
index 0000000..8e2ed2e
Binary files /dev/null and b/target/classes/org/stefan/snrpc/server/SnNettyRpcServerHandler.class differ
diff --git a/target/classes/org/stefan/snrpc/server/StatisticsService$1.class b/target/classes/org/stefan/snrpc/server/StatisticsService$1.class
new file mode 100644
index 0000000..b4ea1ab
Binary files /dev/null and b/target/classes/org/stefan/snrpc/server/StatisticsService$1.class differ
diff --git a/target/classes/org/stefan/snrpc/server/StatisticsService.class b/target/classes/org/stefan/snrpc/server/StatisticsService.class
new file mode 100644
index 0000000..c023a25
Binary files /dev/null and b/target/classes/org/stefan/snrpc/server/StatisticsService.class differ
diff --git a/target/classes/org/stefan/snrpc/util/BufferCache$1.class b/target/classes/org/stefan/snrpc/util/BufferCache$1.class
new file mode 100644
index 0000000..940c804
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/BufferCache$1.class differ
diff --git a/target/classes/org/stefan/snrpc/util/BufferCache.class b/target/classes/org/stefan/snrpc/util/BufferCache.class
new file mode 100644
index 0000000..c3cf2e7
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/BufferCache.class differ
diff --git a/target/classes/org/stefan/snrpc/util/FileUtil.class b/target/classes/org/stefan/snrpc/util/FileUtil.class
new file mode 100644
index 0000000..a68ff20
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/FileUtil.class differ
diff --git a/target/classes/org/stefan/snrpc/util/HandlerMapper.class b/target/classes/org/stefan/snrpc/util/HandlerMapper.class
new file mode 100644
index 0000000..beb2ae6
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/HandlerMapper.class differ
diff --git a/target/classes/org/stefan/snrpc/util/IOUtils.class b/target/classes/org/stefan/snrpc/util/IOUtils.class
new file mode 100644
index 0000000..0f82be3
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/IOUtils.class differ
diff --git a/target/classes/org/stefan/snrpc/util/LRUMap.class b/target/classes/org/stefan/snrpc/util/LRUMap.class
new file mode 100644
index 0000000..efc7b23
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/LRUMap.class differ
diff --git a/target/classes/org/stefan/snrpc/util/MessageFormatter.class b/target/classes/org/stefan/snrpc/util/MessageFormatter.class
new file mode 100644
index 0000000..695e111
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/MessageFormatter.class differ
diff --git a/target/classes/org/stefan/snrpc/util/ReflectionCache.class b/target/classes/org/stefan/snrpc/util/ReflectionCache.class
new file mode 100644
index 0000000..55e2c83
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/ReflectionCache.class differ
diff --git a/target/classes/org/stefan/snrpc/util/SchemaCache.class b/target/classes/org/stefan/snrpc/util/SchemaCache.class
new file mode 100644
index 0000000..c05db8b
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/SchemaCache.class differ
diff --git a/target/classes/org/stefan/snrpc/util/Sequence.class b/target/classes/org/stefan/snrpc/util/Sequence.class
new file mode 100644
index 0000000..338e65e
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/Sequence.class differ
diff --git a/target/classes/org/stefan/snrpc/util/StringUtil.class b/target/classes/org/stefan/snrpc/util/StringUtil.class
new file mode 100644
index 0000000..709fd4b
Binary files /dev/null and b/target/classes/org/stefan/snrpc/util/StringUtil.class differ
diff --git a/target/classes/snrpcserver.properties b/target/classes/snrpcserver.properties
new file mode 100644
index 0000000..e154b8f
--- /dev/null
+++ b/target/classes/snrpcserver.properties
@@ -0,0 +1,10 @@
+#tcpNoDelay
+snrpc.tcp.nodelay=true
+#call the bind method as many times as you want
+snrpc.tcp.reuseAddress=true
+#ISDEBUG
+snrpc.dev=true
+#TCP timeout
+snrpc.read.timeout=25000
+#server port
+snrpc.http.port=8080
diff --git a/target/test-classes/config.xml b/target/test-classes/config.xml
new file mode 100644
index 0000000..a4f9341
--- /dev/null
+++ b/target/test-classes/config.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/target/test-classes/org/stefan/snrpc/Client.class b/target/test-classes/org/stefan/snrpc/Client.class
new file mode 100644
index 0000000..b20dbfa
Binary files /dev/null and b/target/test-classes/org/stefan/snrpc/Client.class differ
diff --git a/target/test-classes/org/stefan/snrpc/Server.class b/target/test-classes/org/stefan/snrpc/Server.class
new file mode 100644
index 0000000..517ee1f
Binary files /dev/null and b/target/test-classes/org/stefan/snrpc/Server.class differ
diff --git a/target/test-classes/org/stefan/snrpc/server/SnRpcImpl.class b/target/test-classes/org/stefan/snrpc/server/SnRpcImpl.class
new file mode 100644
index 0000000..d0c4b87
Binary files /dev/null and b/target/test-classes/org/stefan/snrpc/server/SnRpcImpl.class differ
diff --git a/target/test-classes/org/stefan/snrpc/server/SnRpcInterface.class b/target/test-classes/org/stefan/snrpc/server/SnRpcInterface.class
new file mode 100644
index 0000000..076d26a
Binary files /dev/null and b/target/test-classes/org/stefan/snrpc/server/SnRpcInterface.class differ
diff --git a/target/test-classes/snrpcserver.properties b/target/test-classes/snrpcserver.properties
new file mode 100644
index 0000000..e154b8f
--- /dev/null
+++ b/target/test-classes/snrpcserver.properties
@@ -0,0 +1,10 @@
+#tcpNoDelay
+snrpc.tcp.nodelay=true
+#call the bind method as many times as you want
+snrpc.tcp.reuseAddress=true
+#ISDEBUG
+snrpc.dev=true
+#TCP timeout
+snrpc.read.timeout=25000
+#server port
+snrpc.http.port=8080