package com.peterhi;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.SocketAddress;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Set;
import com.peterhi.ModelInputStream;
public final class RemoteRegistry {
public static final int BODY_TYPE_DATA = 0;
public static final int BODY_TYPE_OBJECT = 1;
private static RudpLocalEndpoint localEndpoint;
private static Set<RemoteRegistryItem> registryItems;
private static Set<EventListener> listeners;
private static RudpDataListener dataListener;
public static RudpLocalEndpoint getLocalEndpoint() {
return localEndpoint;
}
public static void setLocalEndpoint(RudpLocalEndpoint localEndpoint) {
if (RemoteRegistry.localEndpoint != null) {
RemoteRegistry.localEndpoint.removeListener(getDataListener());
}
RemoteRegistry.localEndpoint = localEndpoint;
if (RemoteRegistry.localEndpoint != null) {
RemoteRegistry.localEndpoint.addListener(getDataListener());
}
}
public static boolean publish(String name, Class<?> type, RemoteObject object) {
synchronized (getRegistryItems()) {
RemoteRegistryItem registryItem = new RemoteRegistryItem(name, type, object);
boolean result = getRegistryItems().add(registryItem);
return result;
}
}
public static <T extends RemoteObject> T subscribe(SocketAddress address, String name, final Class<T> type, int timeout) throws IOException {
final Reference<Object> reference = new Reference<Object>();
final RemoteRequest request = ModelUtilities.newInstance(RemoteRequest.class);
request.setName(name);
request.setOperationId(request.hashCode());
RudpDataListener callbackDataListener = new RudpDataListener() {
@Override
public void received(RudpDataEvent event) {
Object object = processObject(event);
if (!(object instanceof RemoteResponse)) {
return;
}
RemoteResponse response = (RemoteResponse )object;
if (response.getOperationId() != request.getOperationId()) {
return;
}
if (response.getResult() == RemoteResponse.SUCCESS_OK) {
reference.setArgument(response.getObjectId());
} else if (response.getResult() == RemoteResponse.FAIL_CLASS_NOT_FOUND) {
reference.setArgument(new ClassNotFoundException(type.toString()));
}
synchronized (request) {
request.notifyAll();
}
}
};
localEndpoint.addListener(callbackDataListener);
postObject(address, request);
synchronized (request) {
try {
request.wait(timeout);
} catch (Exception ex) {
ex.printStackTrace();
}
}
localEndpoint.removeListener(callbackDataListener);
if (reference.getArgument() instanceof Exception) {
throw new IOException((Exception )reference.getArgument());
}
if (reference.getArgument() instanceof Integer) {
Integer objectId = (Integer )reference.getArgument();
ClassLoader loader = type.getClassLoader();
Class<?>[] types = new Class<?>[] { type };
InvocationHandler invocationHandler = new RemoteInvocationHandler(type, address, objectId, timeout);
Object proxy = Proxy.newProxyInstance(loader, types, invocationHandler);
return type.cast(proxy);
}
throw new IllegalStateException();
}
protected static Object processObject(RudpDataEvent event) {
if (!event.isReliable()) {
return null;
}
try {
Object object = null;
InputStream is = event.getInputStream();
is.mark(0);
int bodyType = is.read();
if (bodyType == BODY_TYPE_OBJECT) {
ModelInputStream mis = new ModelInputStream(is);
object = mis.readModel();
}
is.reset();
return object;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
protected static void postObject(SocketAddress address, Model model) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(BODY_TYPE_OBJECT);
ModelOutputStream mos = new ModelOutputStream(baos);
mos.writeModel(model);
byte[] data = baos.toByteArray();
localEndpoint.post(address, data, 0, data.length, new Callback<RudpResult>() {
@Override
public void callback(RudpResult argument) {
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static Set<RemoteRegistryItem> getRegistryItems() {
if (registryItems == null) {
registryItems = new HashSet<RemoteRegistryItem>();
}
return registryItems;
}
private static Set<EventListener> getListeners() {
if (listeners == null) {
listeners = new HashSet<EventListener>();
}
return listeners;
}
public static <T extends EventListener> T registerListener(T listener) {
synchronized (getListeners()) {
getListeners().add(listener);
}
return listener;
}
public static <T extends EventListener> T unregisterListener(T listener) {
synchronized (getListeners()) {
getListeners().remove(listener);
}
return listener;
}
public static <T extends EventListener> T getListener(int hashCode) {
synchronized (getListeners()) {
for (EventListener listener : getListeners()) {
if (listener.hashCode() == hashCode) {
return (T )listener;
}
}
}
return null;
}
public static boolean isListenerRegistered(EventListener listener) {
synchronized (getListeners()) {
for (EventListener element : getListeners()) {
if (element.equals(listener)) {
return true;
}
}
}
return false;
}
private static RudpDataListener getDataListener() {
if (dataListener == null) {
dataListener = new RudpDataListener() {
@Override
public void received(RudpDataEvent event) {
onReceived(event);
}
};
}
return dataListener;
}
private static RemoteRegistryItem getRegistryItem(String name) {
synchronized (getRegistryItems()) {
for (RemoteRegistryItem registryItem : getRegistryItems()) {
if (registryItem.getName().equals(name)) {
return registryItem;
}
}
}
return null;
}
private static RemoteRegistryItem getRegistryItem(int objectId) {
synchronized (getRegistryItems()) {
for (RemoteRegistryItem registryItem : getRegistryItems()) {
if (registryItem.getObject().hashCode() == objectId) {
return registryItem;
}
}
}
return null;
}
private static void onReceived(RudpDataEvent event) {
Object object = processObject(event);
if (object instanceof RemoteRequest) {
RemoteRequest request = (RemoteRequest )object;
RemoteRegistryItem item = getRegistryItem(request.getName());
RemoteResponse response = ModelUtilities.newInstance(RemoteResponse.class);
response.setOperationId(request.getOperationId());
if (item != null) {
response.setResult(RemoteResponse.SUCCESS_OK);
response.setObjectId(item.getObject().hashCode());
} else {
response.setResult(RemoteResponse.FAIL_CLASS_NOT_FOUND);
}
postObject(event.getSocketAddress(), response);
} else if (object instanceof RemoteCall) {
RemoteCall call = (RemoteCall )object;
RemoteRegistryItem item = getRegistryItem(call.getObjectId());
if (item != null) {
RemoteReturn ret = ModelUtilities.newInstance(RemoteReturn.class);
ret.setOperationId(call.getOperationId());
Method method = getMethod(item.getObject().getClass(), call.getName(), call.getParameters());
try {
ret.setReturnValue(method.invoke(item.getObject(), transformListeners(event.getSocketAddress(), method, call.getParameters())));
ret.setResult(RemoteReturn.SUCCESS_OK);
} catch (Exception ex) {
ex.printStackTrace();
ret.setErrorMessage(ex.toString());
ret.setResult(RemoteReturn.FAIL_ERROR);
}
postObject(event.getSocketAddress(), ret);
} else {
EventListener listener = getListener(call.getObjectId());
if (listener == null) {
throw new IllegalArgumentException("Listener not registered.");
}
RemoteReturn ret = ModelUtilities.newInstance(RemoteReturn.class);
ret.setOperationId(call.getOperationId());
Method method = getMethod(listener.getClass(), call.getName(), call.getParameters());
try {
ret.setReturnValue(method.invoke(listener, transformListeners(event.getSocketAddress(), method, call.getParameters())));
ret.setResult(RemoteReturn.SUCCESS_OK);
} catch (Exception ex) {
ex.printStackTrace();
ret.setErrorMessage(ex.toString());
ret.setResult(RemoteReturn.FAIL_ERROR);
}
postObject(event.getSocketAddress(), ret);
}
}
}
private static Object[] transformListeners(SocketAddress address, Method method, Object[] parameters) {
Class<?>[] types = method.getParameterTypes();
for (int i = 0; i < types.length; i++) {
Class<?> type = types[i];
if (EventListener.class.isAssignableFrom(type) && parameters[i] instanceof Integer) {
int objectId = (Integer )parameters[i];
EventListener listener = getListener(objectId);
if (listener == null) {
ClassLoader loader = type.getClassLoader();
Class<?>[] interfaces = new Class<?>[] { type };
InvocationHandler invocationHandler = new RemoteInvocationHandler(type, address, objectId, 0);
listener = (EventListener )Proxy.newProxyInstance(loader, interfaces, invocationHandler);
}
parameters[i] = listener;
}
}
return parameters;
}
private static Method getMethod(Class<?> type, String name, Object[] args) {
if (args == null) {
args = new Object[0];
}
Method[] methods = type.getMethods();
Method method = null;
for (Method element : methods) {
if (!element.getName().equals(name)) {
continue;
}
if (element.getParameterTypes().length != args.length) {
continue;
}
method = element;
break;
}
return method;
}
}