将接口转换为 RMI 接口
Convert interface to RMI interface
我有接口,例如:
interface MyService {
String sayHello(Object who) throws ServiceException;
}
现在我需要将此接口与 RMI 一起使用。当然我可以定义另一个接口,如:
interface MyService extends Remote{
String sayHello(Object who) throws RemoteException;
}
然后调整我的实现 class 以适应 RMI 接口。
但是我有数百个接口可以转换为 RMI。用相同的逻辑编写数百个 classes 是如此无聊和丑陋。
那么有没有简单的方法可以在RMI中使用这些接口呢?
这是我作品中的真实场景。我使用动态字节码一次完成了数百个工作。
首先,这是我的旧代码演示(没有 RMI)。
class ServiceException extends Exception {
public ServiceException(String msg) {
super(msg);
}
}
interface MyService {
String sayHello(Object who) throws ServiceException;
void throwIt() throws ServiceException;
}
class MyServiceImpl implements MyService {
@Override
public String sayHello(Object who) throws ServiceException {
String hello = who.toString();
System.out.println("Server said: " + hello);
return "Hello! " + hello;
}
@Override
public void throwIt() throws ServiceException {
throw new ServiceException("throw in server");
}
}
并且我使用Javassist将接口转换为RMI接口:
public static Class<? extends Remote> toRemoteInterface(Class<?> inter) throws Exception {
return cache("toRemote", inter, () -> uncheck(() -> {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.getAndRename(inter.getName(), inter.getName() + "$RemoteVersion");
cc.setModifiers(Modifier.PUBLIC | cc.getModifiers());
cc.addInterface(pool.get(Remote.class.getName()));
for (CtMethod cm : cc.getMethods()) {
cm.setExceptionTypes(new CtClass[] { pool.getCtClass(RemoteException.class.getName()) });
}
cc.writeFile();
return cc.toClass();
}));
}
然后,使用Cglib在远程对象和本地对象之间进行转换:
public static <T> T fromRemote(Remote remote, Class<T> inter) throws Exception {
Enhancer e = new Enhancer();
e.setInterfaces(new Class[] { inter });
e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
Method remoteMethod = remote.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
return remoteMethod.invoke(remote, args);
} catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
while (targetException instanceof RemoteException) {
targetException = targetException.getCause();
}
throw targetException;
}
});
return (T) e.create();
}
public static <T> Remote toRemote(T local, Class<T> inter) throws Exception {
Enhancer e = new Enhancer();
e.setSuperclass(UnicastRemoteObject.class);
e.setInterfaces(new Class[] { toRemoteInterface(inter) });
e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
Method targetMethod = local.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
return targetMethod.invoke(local, args);
} catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
throw new RemoteException(targetException.getMessage(), targetException);
}
});
return (Remote) e.create();
}
现在我们可以像使用 RMI 接口一样使用非 rmi 接口及其实现了:
public static void startClient() throws Exception {
String stringURL = "rmi://127.0.0.1/" + MyService.class.getName();
toRemoteInterface(MyService.class);// define the Remote interface in client classloader
MyService service = fromRemote(Naming.lookup(stringURL), MyService.class);
String said = service.sayHello("Dean");
System.out.println("Client heard: " + said);
service.throwIt();
}
public static void startServer() throws Exception {
LocateRegistry.createRegistry(1099);
Remote remote = toRemote(new MyServiceImpl(), MyService.class);
Naming.rebind(MyService.class.getName(), remote);
System.out.println(remote);
System.out.println(remote.getClass());
System.out.println("Server started!");
}
完整代码,see this on github
我有接口,例如:
interface MyService {
String sayHello(Object who) throws ServiceException;
}
现在我需要将此接口与 RMI 一起使用。当然我可以定义另一个接口,如:
interface MyService extends Remote{
String sayHello(Object who) throws RemoteException;
}
然后调整我的实现 class 以适应 RMI 接口。
但是我有数百个接口可以转换为 RMI。用相同的逻辑编写数百个 classes 是如此无聊和丑陋。
那么有没有简单的方法可以在RMI中使用这些接口呢?
这是我作品中的真实场景。我使用动态字节码一次完成了数百个工作。
首先,这是我的旧代码演示(没有 RMI)。
class ServiceException extends Exception {
public ServiceException(String msg) {
super(msg);
}
}
interface MyService {
String sayHello(Object who) throws ServiceException;
void throwIt() throws ServiceException;
}
class MyServiceImpl implements MyService {
@Override
public String sayHello(Object who) throws ServiceException {
String hello = who.toString();
System.out.println("Server said: " + hello);
return "Hello! " + hello;
}
@Override
public void throwIt() throws ServiceException {
throw new ServiceException("throw in server");
}
}
并且我使用Javassist将接口转换为RMI接口:
public static Class<? extends Remote> toRemoteInterface(Class<?> inter) throws Exception {
return cache("toRemote", inter, () -> uncheck(() -> {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.getAndRename(inter.getName(), inter.getName() + "$RemoteVersion");
cc.setModifiers(Modifier.PUBLIC | cc.getModifiers());
cc.addInterface(pool.get(Remote.class.getName()));
for (CtMethod cm : cc.getMethods()) {
cm.setExceptionTypes(new CtClass[] { pool.getCtClass(RemoteException.class.getName()) });
}
cc.writeFile();
return cc.toClass();
}));
}
然后,使用Cglib在远程对象和本地对象之间进行转换:
public static <T> T fromRemote(Remote remote, Class<T> inter) throws Exception {
Enhancer e = new Enhancer();
e.setInterfaces(new Class[] { inter });
e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
Method remoteMethod = remote.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
return remoteMethod.invoke(remote, args);
} catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
while (targetException instanceof RemoteException) {
targetException = targetException.getCause();
}
throw targetException;
}
});
return (T) e.create();
}
public static <T> Remote toRemote(T local, Class<T> inter) throws Exception {
Enhancer e = new Enhancer();
e.setSuperclass(UnicastRemoteObject.class);
e.setInterfaces(new Class[] { toRemoteInterface(inter) });
e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
Method targetMethod = local.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
return targetMethod.invoke(local, args);
} catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
throw new RemoteException(targetException.getMessage(), targetException);
}
});
return (Remote) e.create();
}
现在我们可以像使用 RMI 接口一样使用非 rmi 接口及其实现了:
public static void startClient() throws Exception {
String stringURL = "rmi://127.0.0.1/" + MyService.class.getName();
toRemoteInterface(MyService.class);// define the Remote interface in client classloader
MyService service = fromRemote(Naming.lookup(stringURL), MyService.class);
String said = service.sayHello("Dean");
System.out.println("Client heard: " + said);
service.throwIt();
}
public static void startServer() throws Exception {
LocateRegistry.createRegistry(1099);
Remote remote = toRemote(new MyServiceImpl(), MyService.class);
Naming.rebind(MyService.class.getName(), remote);
System.out.println(remote);
System.out.println(remote.getClass());
System.out.println("Server started!");
}
完整代码,see this on github