Serialization
Object to byte stream and byte stream to Object
package com.talend.example.rpc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
interface Writeable {
void readFields(DataInput in) throws IOException;
void write(DataOutput out) throws IOException;
}
Serialization action
//object to byte stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
Writeable obj = new IntWriteable(1);
obj.write(out);
//byte stream to object
DataInputStream in = new DataInputStream(socket.getInputStream());
Writeable obj = new StringWriteable();
obj.readFields(in );
A IntWriteable
package com.talend.example.rpc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class IntWriteable implements Writeable {
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public IntWriteable() {
}
public IntWriteable(int value) {
this.value = value;
}
@Override
public void readFields(DataInput in) throws IOException {
value = in.readInt();
}
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(value);
}
}
A StringWriteable
package com.talend.example.rpc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class StringWriteable implements Writeable {
String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public StringWriteable() {
}
public StringWriteable(String value) {
this.value = value;
}
@Override
public void readFields(DataInput in) throws IOException {
int length = in.readInt();
byte[] b = new byte[length];
in.readFully(b);
value = new String(b, "UTF-8");
}
@Override
public void write(DataOutput out) throws IOException {
byte[] b = value.getBytes("UTF-8");
out.writeInt(b.length);
out.write(b);
}
public static void writeString(DataOutput out, String content)
throws IOException {
byte[] b = content.getBytes("UTF-8");
out.writeInt(b.length);
out.write(b);
}
public static String readString(DataInput in) throws IOException {
int length = in.readInt();
byte[] b = new byte[length];
in.readFully(b);
return new String(b, "UTF-8");
}
}
A special Writeable : ObjectWriteable
package com.talend.example.rpc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
public class ObjectWriteable implements Writeable {
private static final Map<String, Class<?>> PRIMITIVE_NAMES = new HashMap<String, Class<?>>();
static {
PRIMITIVE_NAMES.put("boolean", Boolean.TYPE);
PRIMITIVE_NAMES.put("byte", Byte.TYPE);
PRIMITIVE_NAMES.put("char", Character.TYPE);
PRIMITIVE_NAMES.put("short", Short.TYPE);
PRIMITIVE_NAMES.put("int", Integer.TYPE);
PRIMITIVE_NAMES.put("long", Long.TYPE);
PRIMITIVE_NAMES.put("float", Float.TYPE);
PRIMITIVE_NAMES.put("double", Double.TYPE);
PRIMITIVE_NAMES.put("void", Void.TYPE);
}
Object instance;
Class declaredClass;
public ObjectWriteable() {
}
public ObjectWriteable(Object instance, Class declaredClass) {
this.instance = instance;
this.declaredClass = declaredClass;
}
public Object getInstance() {
return instance;
}
public void setInstance(Object instance) {
this.instance = instance;
}
public Class getDeclaredClass() {
return declaredClass;
}
public void setDeclaredClass(Class declaredClass) {
this.declaredClass = declaredClass;
}
@Override
public void readFields(DataInput in) throws IOException {
readObject(in, this);
}
@Override
public void write(DataOutput out) throws IOException {
writeObject(out, instance, declaredClass);
}
public static Object readObject(DataInput in, ObjectWriteable objectWritable)
throws IOException {
String className = StringWriteable.readString(in);
Class<?> declaredClass = PRIMITIVE_NAMES.get(className);
if (declaredClass == null) {
try {
declaredClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("readObject can't find class "
+ className, e);
}
}
Object instance;
if (declaredClass.isPrimitive()) {
if (declaredClass == Boolean.TYPE) {
instance = Boolean.valueOf(in.readBoolean());
} else if (declaredClass == Character.TYPE) {
instance = Character.valueOf(in.readChar());
} else if (declaredClass == Byte.TYPE) {
instance = Byte.valueOf(in.readByte());
} else if (declaredClass == Short.TYPE) {
instance = Short.valueOf(in.readShort());
} else if (declaredClass == Integer.TYPE) {
instance = Integer.valueOf(in.readInt());
} else if (declaredClass == Long.TYPE) {
instance = Long.valueOf(in.readLong());
} else if (declaredClass == Float.TYPE) {
instance = Float.valueOf(in.readFloat());
} else if (declaredClass == Double.TYPE) {
instance = Double.valueOf(in.readDouble());
} else if (declaredClass == Void.TYPE) {
instance = null;
} else {
throw new IllegalArgumentException("Not a primitive: "
+ declaredClass);
}
} else if (declaredClass.isArray()) {
int length = in.readInt();
instance = Array.newInstance(declaredClass.getComponentType(),
length);
for (int i = 0; i < length; i++) {
Array.set(instance, i, readObject(in, null));
}
} else if (declaredClass == String.class) {
instance = StringWriteable.readString(in);
} else {
Class instanceClass = null;
String str = "";
try {
str = StringWriteable.readString(in);
instanceClass = Class.forName(str);
} catch (ClassNotFoundException e) {
throw new RuntimeException(
"readObject can't find class " + str, e);
}
Writeable writable = null;
try {
writable = (Writeable) instanceClass.newInstance();
} catch (Exception e) {
}
writable.readFields(in);
instance = writable;
}
if (objectWritable != null) {
objectWritable.declaredClass = declaredClass;
objectWritable.instance = instance;
}
return instance;
}
public static void writeObject(DataOutput out, Object instance,
Class declaredClass) throws IOException {
StringWriteable.writeString(out, declaredClass.getName());
if (declaredClass.isArray()) {
int length = Array.getLength(instance);
out.writeInt(length);
for (int i = 0; i < length; i++) {
writeObject(out, Array.get(instance, i),
declaredClass.getComponentType());
}
} else if (declaredClass == String.class) {
StringWriteable.writeString(out, (String) instance);
} else if (declaredClass.isPrimitive()) {
if (declaredClass == Boolean.TYPE) {
out.writeBoolean(((Boolean) instance).booleanValue());
} else if (declaredClass == Character.TYPE) {
out.writeChar(((Character) instance).charValue());
} else if (declaredClass == Byte.TYPE) {
out.writeByte(((Byte) instance).byteValue());
} else if (declaredClass == Short.TYPE) {
out.writeShort(((Short) instance).shortValue());
} else if (declaredClass == Integer.TYPE) {
out.writeInt(((Integer) instance).intValue());
} else if (declaredClass == Long.TYPE) {
out.writeLong(((Long) instance).longValue());
} else if (declaredClass == Float.TYPE) {
out.writeFloat(((Float) instance).floatValue());
} else if (declaredClass == Double.TYPE) {
out.writeDouble(((Double) instance).doubleValue());
} else if (declaredClass == Void.TYPE) {
} else {
throw new IllegalArgumentException("Not a primitive: "
+ declaredClass);
}
} else if (Writeable.class.isAssignableFrom(declaredClass)) {
StringWriteable.writeString(out, instance.getClass().getName());
((Writeable) instance).write(out);
} else {
throw new IOException("Can't write: " + instance + " as "
+ declaredClass);
}
}
}
Send and receive procedure information by the net
Method information Writeable
package com.talend.example.rpc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Method;
public class MethodInformation implements Writeable {
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
public Class[] getParameterClazz() {
return parameterClazz;
}
public void setParameterClazz(Class[] parameterClazz) {
this.parameterClazz = parameterClazz;
}
String method;
Class[] parameterClazz;
Object[] parameters;
public MethodInformation() {
}
public MethodInformation(Method method, Object[] parameters) {
this.method = method.getName();
this.parameters = parameters;
this.parameterClazz = method.getParameterTypes();
}
@Override
public void readFields(DataInput in) throws IOException {
method = StringWriteable.readString(in);
parameters = new Object[in.readInt()];
parameterClazz = new Class[parameters.length];
ObjectWriteable objectWritable = new ObjectWriteable();
for (int i = 0; i < parameters.length; i++) {
parameters[i] = ObjectWriteable.readObject(in, objectWritable);
parameterClazz[i] = objectWritable.getDeclaredClass();
}
}
@Override
public void write(DataOutput out) throws IOException {
StringWriteable.writeString(out, method);
out.writeInt(parameterClazz.length);
for (int i = 0; i < parameterClazz.length; i++) {
ObjectWriteable.writeObject(out, parameters[i], parameterClazz[i]);
}
}
}
A protocol
package com.talend.example.rpc;
public interface Protocol {
}
A sum protocol
package com.talend.example.rpc;
interface SumProtocol extends Protocol {
int sum(int x,int y);
IntWriteable sum(IntWriteable x, IntWriteable y);
}
A sum protocol implement
package com.talend.example.rpc;
public class SumProtocolImple implements SumProtocol {
@Override
public int sum(int x, int y) {
return x + y;
}
@Override
public IntWriteable sum(IntWriteable x, IntWriteable y) {
int value = x.getValue() + y.getValue();
IntWriteable result = new IntWriteable(value);
return result;
}
}
RPC client that use java dynamic proxy
package com.talend.example.rpc;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
public class RPC {
static ByteBuffer HEADER = ByteBuffer.wrap("wrpc".getBytes());
public static Protocol getProxy(Class<? extends Protocol> protocol,InetSocketAddress addr) throws IOException {
final Invoker invoker = new Invoker(protocol, addr);
Protocol proxy = (Protocol) Proxy.newProxyInstance(protocol.getClassLoader(), new Class[] { protocol }, invoker);
return proxy;
}
}
invoker for dynamic proxy and send the request,receive the response
package com.talend.example.rpc;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
public class Invoker implements InvocationHandler {
Class<? extends Protocol> protocol;
InetSocketAddress address;
Invoker(Class<? extends Protocol> protocol, InetSocketAddress address)
throws IOException {
this.protocol = protocol;
this.address = address;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
MethodInformation mi = new MethodInformation(method, args);
// send it from client to server
Client client = new Client(address);
ObjectWriteable result = (ObjectWriteable) client.call(protocol,mi);
return result.getInstance();
}
}
RPC internal client
package com.talend.example.rpc;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import javax.net.SocketFactory;
public class Client {
Socket socket;
public Client(InetSocketAddress address) throws IOException {
socket = SocketFactory.getDefault().createSocket();
socket.connect(address);
}
ObjectWriteable call(Class<? extends Protocol> protocol, Writeable method)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
StringWriteable.writeString(dos, protocol.getName());
method.write(dos);
byte[] data = baos.toByteArray();
int length = data.length;
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(RPC.HEADER.array());
out.writeInt(length);
out.write(data);
out.flush();
DataInputStream in = new DataInputStream(socket.getInputStream());
ObjectWriteable obj = new ObjectWriteable();
ObjectWriteable.readObject(in, obj);
socket.close();
return obj;
}
}
RPC server
package com.talend.example.rpc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import com.sun.corba.se.impl.oa.poa.ActiveObjectMap.Key;
public class Server {
ServerSocketChannel ssc;
Selector selector = null;
int port;
Object protocolInstance = null;
Server(int port, Object protocolInstance) {
this.port = port;
this.protocolInstance = protocolInstance;
}
void init() throws IOException {
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
void listen() throws IOException {
while (true) {
selector.select();
Iterator<SelectionKey> itera = selector.selectedKeys().iterator();
while (itera.hasNext()) {
SelectionKey key = null;
try {
key = itera.next();
itera.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
doAccept(key);
} else if (key.isReadable()) {
doRead(key);
} else if (key.isWritable()) {
doWrite(key);
}
} catch (Exception e) {
//TODO
} finally {
}
}
}
}
private void doAccept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel;
while ((channel = server.accept()) != null) {
channel.configureBlocking(false);
SelectionKey skey = channel.register(selector, SelectionKey.OP_READ
| SelectionKey.OP_WRITE);
Connection conn = new Connection(channel);
skey.attach(conn);
}
}
private void doWrite(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
Connection conn = (Connection) key.attachment();
if (!conn.result.hasRemaining()) {
}
if (conn.resultIsReady) {
channel.write(conn.result);
}
}
private void doRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
Connection conn = (Connection) key.attachment();
// read rpc header or data length
int count = -1;
if (conn.rpcheaderbuffer.hasRemaining()) {
count = channel.read(conn.rpcheaderbuffer);
if (count < 0 || conn.rpcheaderbuffer.hasRemaining()) {
return;
}
}
if (!conn.hasReadRPCHeader) {
// now header buffer is full
conn.rpcheaderbuffer.flip();
if (!conn.rpcheaderbuffer.equals(RPC.HEADER)) {
System.out.println("is not a rpc request");
key.cancel();
channel.close();
}
conn.hasReadRPCHeader = true;
conn.rpcheaderbuffer.clear();
return;
}
if (conn.databuffer == null) {
conn.rpcheaderbuffer.flip();
int dataLength = conn.rpcheaderbuffer.getInt();
conn.databuffer = ByteBuffer.allocate(dataLength);
}
channel.read(conn.databuffer);
if (!conn.databuffer.hasRemaining()) {
conn.databuffer.flip();
conn.process(conn.databuffer.array());
}
}
class Connection {
MethodInformation mi = new MethodInformation();
ByteBuffer rpcheaderbuffer = ByteBuffer.allocate(4);
ByteBuffer databuffer;
ByteBuffer result;
boolean hasReadRPCHeader = false;
boolean resultIsReady = false;
SocketChannel channel;
public Connection(SocketChannel channel) {
this.channel = channel;
}
void process(byte[] data) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream in = new DataInputStream(bais);
String protocol = StringWriteable.readString(in);
try {
Class protocolClass = Class.forName(protocol);
mi.readFields(in);
String method = mi.getMethod();
Class[] paraClasses = mi.getParameterClazz();
Object[] parameters = mi.getParameters();
Method met = protocolClass.getMethod(method, paraClasses);
Object result = met.invoke(protocolInstance, parameters);
doResponse(result, met.getReturnType());
} catch (Exception e) {
e.printStackTrace();
}
}
private void doResponse(Object result, Class resulttype)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
ObjectWriteable.writeObject(dos, result, resulttype);
this.result = ByteBuffer.wrap(baos.toByteArray());
resultIsReady = true;
}
}
public static void main(String[] args) throws IOException {
Server server = new Server(9000, new SumProtocolImple());
server.init();
server.listen();
}
}
start RPC server
Server server = new Server(9000, new SumProtocolImple());
server.init();
server.listen();
A RPC client example :
package com.talend.example.rpc;
import java.io.IOException;
import java.net.InetSocketAddress;
public class AppClient {
public static void main(String[] args) throws IOException {
SumProtocol sumprotocol = (SumProtocol) (RPC.getProxy(
SumProtocol.class, new InetSocketAddress("127.0.0.1", 9000)));
int result = sumprotocol.sum(2, 4);
System.out.println(result);
IntWriteable result2 = sumprotocol.sum(new IntWriteable(4), new IntWriteable(4));
System.out.println(result2.getValue());
}
}