diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java index 7796fef..082d3c9 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java @@ -233,4 +233,10 @@ public void verify_jvm_agent_with_options() throws Exception { public void verify_jvm_agent_multiple_agents() throws Exception { super.verify_jvm_agent_multiple_agents(); } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java index 8e8264f..7ea96b1 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java @@ -226,4 +226,10 @@ public void verify_exit_code_is_available() throws Exception { public void verify_exit_code_is_reported() { super.verify_exit_code_is_reported(); } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java index 0a40e63..a34e368 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java @@ -227,4 +227,10 @@ public void verify_lifecycle_listener_with_invalid_arg() throws Exception { public void verify_exit_code_is_reported() { super.verify_exit_code_is_reported(); } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateNodeFeatureTest.java index 5b1cd50..7cc21fe 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateNodeFeatureTest.java @@ -126,4 +126,10 @@ public void test_classpath_limiting() throws MalformedURLException, URISyntaxExc public void test_annonimous_primitive_in_args() { super.test_annonimous_primitive_in_args(); } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateShallowNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateShallowNodeFeatureTest.java index d639f8a..5cd33fe 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateShallowNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/IsolateShallowNodeFeatureTest.java @@ -126,4 +126,10 @@ public void test_classpath_limiting() throws MalformedURLException, URISyntaxExc public void test_annonimous_primitive_in_args() { super.test_annonimous_primitive_in_args(); } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java index 27660ed..16dd41e 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java @@ -192,6 +192,12 @@ public void verify_lifecycle_listener_with_invalid_arg() throws Exception { super.verify_lifecycle_listener_with_invalid_arg(); } + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } + @Test @Override public void verify_jvm_agent_multiple_agents() throws Exception { diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/LocalShallowNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/LocalShallowNodeFeatureTest.java index 0dca227..cdd3801 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/LocalShallowNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/LocalShallowNodeFeatureTest.java @@ -174,13 +174,19 @@ public void verify_jvm_agent_with_options() throws Exception { @Test @Override - public void verify_jvm_agent_multiple_agents() throws Exception { - super.verify_jvm_agent_with_options(); - } - - @Test - @Override - public void verify_exit_code_is_reported() { - super.verify_exit_code_is_reported(); - } -} + public void verify_jvm_agent_multiple_agents() throws Exception { + super.verify_jvm_agent_with_options(); + } + + @Test + @Override + public void verify_exit_code_is_reported() { + super.verify_exit_code_is_reported(); + } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } +} diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java index c6bc4cc..d266365 100755 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java @@ -855,6 +855,44 @@ public void verify_lifecycle_listener_with_invalid_arg() throws Exception { pll.waitNodeExitCode(node.toString(), 1); } + public void verify_transparent_proxy_over_classes() throws Exception { + // marked with Remote interfaces + class TestForProxyRemote implements Remote { + public String greet() { + return "Hello"; + } + } + + final TestForProxyRemote testForProxyRemote = new TestForProxyRemote(); + + ViNode node = cloud.node(testName.getMethodName()); + Assert.assertEquals("Hello", + node.exec(new Callable() { + @Override + public String call() { + return testForProxyRemote.greet(); + } + }) + ); + + // marked with cloud.createRmiProxy + class TestForProxy { + public String greet() { + return "Hello"; + } + } + final TestForProxy testForProxy = cloud.createRmiProxy(new TestForProxy()); + + Assert.assertEquals("Hello", + node.exec(new Callable() { + @Override + public String call() { + return testForProxy.greet(); + } + }) + ); + } + private static String readMarkerFromResources() throws IOException { URL url = IsolateNodeFeatureTest.class.getResource("/marker.txt"); Assert.assertNotNull(url); diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/viengine/Engine2_LocalNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/viengine/Engine2_LocalNodeFeatureTest.java index 3e99d78..54e9bfb 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/viengine/Engine2_LocalNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/viengine/Engine2_LocalNodeFeatureTest.java @@ -163,4 +163,10 @@ public void verify_jvm_agent_with_options() throws Exception { public void verify_jvm_agent_multiple_agents() throws Exception { super.verify_jvm_agent_multiple_agents(); } + + @Test + @Override + public void verify_transparent_proxy_over_classes() throws Exception { + super.verify_transparent_proxy_over_classes(); + } } diff --git a/nanotest/src/main/java/org/gridkit/nanocloud/test/junit/DisposableCloud.java b/nanotest/src/main/java/org/gridkit/nanocloud/test/junit/DisposableCloud.java index 91ba7e4..7cad307 100644 --- a/nanotest/src/main/java/org/gridkit/nanocloud/test/junit/DisposableCloud.java +++ b/nanotest/src/main/java/org/gridkit/nanocloud/test/junit/DisposableCloud.java @@ -5,6 +5,7 @@ import org.gridkit.nanocloud.Cloud; import org.gridkit.nanocloud.CloudFactory; import org.gridkit.vicluster.ViNode; +import org.gridkit.zerormi.SmartRmiMarshaler; import org.junit.rules.ExternalResource; public class DisposableCloud extends ExternalResource implements CloudRule { @@ -35,4 +36,9 @@ public Collection listNodes(String nameOrSelector) { public void shutdown() { cloud.shutdown(); } + + @Override + public T createRmiProxy(T object){ + return SmartRmiMarshaler.wrapForRmiProxy(object); + } } diff --git a/vicluster-core/src/main/java/org/gridkit/nanocloud/Cloud.java b/vicluster-core/src/main/java/org/gridkit/nanocloud/Cloud.java index cd49e24..e443623 100644 --- a/vicluster-core/src/main/java/org/gridkit/nanocloud/Cloud.java +++ b/vicluster-core/src/main/java/org/gridkit/nanocloud/Cloud.java @@ -24,4 +24,11 @@ public interface Cloud { public void shutdown(); + /** + * Replaced class with instances that eligible to transparent-rmi. + * If result of this call will be passed to remote side, proxy will be created at remote side, and + * all method invocations will be passed to original object. + */ + public T createRmiProxy(T object); + } diff --git a/vicluster-core/src/main/java/org/gridkit/vicluster/ViManager.java b/vicluster-core/src/main/java/org/gridkit/vicluster/ViManager.java index e12b92b..e0f2282 100644 --- a/vicluster-core/src/main/java/org/gridkit/vicluster/ViManager.java +++ b/vicluster-core/src/main/java/org/gridkit/vicluster/ViManager.java @@ -46,6 +46,7 @@ import org.gridkit.zerormi.RemoteExecutorAsynAdapter; import org.gridkit.zerormi.RemoteExecutor; import org.gridkit.zerormi.RemoteStub; +import org.gridkit.zerormi.SmartRmiMarshaler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -240,7 +241,12 @@ public void run() { } provider.shutdown(); } - + + @Override + public T createRmiProxy(T object){ + return SmartRmiMarshaler.wrapForRmiProxy(object); + } + public synchronized void resetDeadNode() { ensureAlive(); deadNodes.clear(); diff --git a/zerormi/pom.xml b/zerormi/pom.xml index c4285e9..da16ea0 100644 --- a/zerormi/pom.xml +++ b/zerormi/pom.xml @@ -63,6 +63,17 @@ provided + + net.bytebuddy + byte-buddy + 1.14.5 + + + org.objenesis + objenesis + 3.3 + + junit junit @@ -85,7 +96,38 @@ always - + + + org.apache.maven.plugins + maven-shade-plugin + 1.6 + + + package + + shade + + + + + net.bytebuddy + org.gridkit.internal.net.bytebuddy + + + org.objenesis + org.gridkit.internal.org.objenesis + + + + + net.bytebuddy:* + org.objenesis:* + + + + + + \ No newline at end of file diff --git a/zerormi/src/main/java/org/gridkit/zerormi/CreateRemoteProxy.java b/zerormi/src/main/java/org/gridkit/zerormi/CreateRemoteProxy.java new file mode 100644 index 0000000..0c3b604 --- /dev/null +++ b/zerormi/src/main/java/org/gridkit/zerormi/CreateRemoteProxy.java @@ -0,0 +1,8 @@ +package org.gridkit.zerormi; + +/** + * Marker interface, classes wrapped with Cloud::createRmiProxy will be marked with it. + * Not intended for direct use in client code. + */ +public interface CreateRemoteProxy { +} diff --git a/zerormi/src/main/java/org/gridkit/zerormi/Exported.java b/zerormi/src/main/java/org/gridkit/zerormi/Exported.java index 5afc7e0..0b7f0bd 100644 --- a/zerormi/src/main/java/org/gridkit/zerormi/Exported.java +++ b/zerormi/src/main/java/org/gridkit/zerormi/Exported.java @@ -22,19 +22,19 @@ */ public class Exported { - private Class[] interfaces; private Object object; - - public Exported(Object object, Class... interfaces) { - this.object = object; - this.interfaces = interfaces; - } + private final Class originalClass; - public Class[] getInterfaces() { - return interfaces; + public Exported(Object object, Class originalClass) { + this.object = object; + this.originalClass = originalClass; } public Object getObject() { return object; } + + public Class getOriginalClass() { + return originalClass; + } } diff --git a/zerormi/src/main/java/org/gridkit/zerormi/RemoteInstance.java b/zerormi/src/main/java/org/gridkit/zerormi/RemoteInstance.java index cc734de..c931aca 100644 --- a/zerormi/src/main/java/org/gridkit/zerormi/RemoteInstance.java +++ b/zerormi/src/main/java/org/gridkit/zerormi/RemoteInstance.java @@ -27,32 +27,32 @@ public final class RemoteInstance implements Serializable { private static final long serialVersionUID = 20090415L; String instanceId; - String[] interfaces; + String className; - public String[] getInterfaces() { - return interfaces; + public String getClassName() { + return className; } - + public String getInstanceId() { return instanceId; } - public RemoteInstance(String instanceId, String[] interfaces) { + public RemoteInstance(String instanceId, String className) { if (instanceId == null) { throw new NullPointerException("instanceId cannot be null"); } - if (interfaces == null) { - throw new NullPointerException("interfaces cannot be null"); + if (className == null) { + throw new NullPointerException("className cannot be null"); } this.instanceId = instanceId; - this.interfaces = interfaces; + this.className = className; } @Override public boolean equals(Object obj) { if (obj instanceof RemoteInstance) { RemoteInstance ri = (RemoteInstance) obj; - return instanceId.equals(ri.instanceId) && Arrays.equals(interfaces, ri.interfaces); + return instanceId.equals(ri.instanceId) && className.equals(ri.className); } return false; } @@ -64,6 +64,6 @@ public int hashCode() { @Override public String toString() { - return "${" + instanceId +"}" + Arrays.toString(interfaces); + return "${" + instanceId +"}" + className; } } diff --git a/zerormi/src/main/java/org/gridkit/zerormi/RemoteStub.java b/zerormi/src/main/java/org/gridkit/zerormi/RemoteStub.java index 6741383..a2906ac 100644 --- a/zerormi/src/main/java/org/gridkit/zerormi/RemoteStub.java +++ b/zerormi/src/main/java/org/gridkit/zerormi/RemoteStub.java @@ -18,10 +18,19 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import org.gridkit.util.concurrent.FutureBox; import org.gridkit.util.concurrent.FutureEx; +import org.objenesis.ObjenesisStd; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.InvocationHandlerAdapter; + +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; +import static net.bytebuddy.matcher.ElementMatchers.not; /** * @@ -29,6 +38,15 @@ */ public class RemoteStub implements InvocationHandler { + /** + * Interface to simplify interaction with Proxy instances. + * Methods are started with ___ to not interfere with methods of original class. + */ + public interface ProxyHandlerSetterGetter { + InvocationHandler ___getHandler(); + void ___setHandler(InvocationHandler handler); + } + private RemoteInstance identity; private RmiChannel channel; @@ -77,30 +95,54 @@ public FutureEx asyncInvoke(Object proxy, Method method, Object[] args) return channel.asyncRemoteInvocation(this, proxy, method, args); } } - + @SuppressWarnings("rawtypes") public static Object buildProxy(RemoteInstance remoteInstance, RmiChannel channel) throws ClassNotFoundException { - String[] classNames = remoteInstance.interfaces; - Class[] classes = new Class[classNames.length]; - for(int i = 0; i != classNames.length; ++i) { - classes[i] = channel.classForName(classNames[i]); + Class clazz = Class.forName(remoteInstance.className); + Class proxyClass = new ByteBuddy() + .subclass(clazz) + + .defineField("handler", InvocationHandler.class, Visibility.PUBLIC) + + .method(not(isDeclaredBy(ProxyHandlerSetterGetter.class))) + .intercept(InvocationHandlerAdapter.toField("handler")) + + .implement(ProxyHandlerSetterGetter.class) + .intercept(FieldAccessor.ofField("handler")) + + .make() + .load(RemoteStub.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded(); + Object result = new ObjenesisStd().newInstance(proxyClass); + ((ProxyHandlerSetterGetter)result).___setHandler(new RemoteStub(remoteInstance, channel)); + return result; + } + + private static InvocationHandler getInvocationHandler(Object proxy) { + if (proxy instanceof ProxyHandlerSetterGetter) { + return ((ProxyHandlerSetterGetter) proxy).___getHandler(); + } + else { + throw new IllegalArgumentException("Not a proxy instance"); } - - return Proxy.newProxyInstance(channel.getClassLoader(), classes, new RemoteStub(remoteInstance, channel)); } - public static boolean isRemoteStub(Object proxy) { - if (Proxy.isProxyClass(proxy.getClass()) && Proxy.getInvocationHandler(proxy) instanceof RemoteStub) { - return true; - } - else { - return false; - } - } + public static boolean isRemoteStub(Object proxy) { + return proxy instanceof ProxyHandlerSetterGetter; + } + + public static Class getOriginalClass(Object proxy){ + if (proxy instanceof ProxyHandlerSetterGetter) { + return proxy.getClass().getSuperclass(); + } + else { + throw new IllegalArgumentException("Not a proxy instance"); + } + } @SuppressWarnings("unchecked") public static FutureEx remoteSubmit(Object proxy, Method method, Object... arguments) { - Object handler = Proxy.getInvocationHandler(proxy); + Object handler = getInvocationHandler(proxy); if (handler instanceof RemoteStub) { RemoteStub stub = (RemoteStub) handler; return (FutureEx) stub.asyncInvoke(proxy, method, arguments); diff --git a/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel.java b/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel.java index 068e1bf..2e22f5a 100644 --- a/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel.java +++ b/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel.java @@ -27,13 +27,13 @@ interface RmiChannel { public Object remoteInvocation(RemoteStub remoteStub, Object proxy, Method method, Object[] args) throws Throwable; - - public FutureEx asyncRemoteInvocation(RemoteStub remoteStub, Object proxy, Method method, Object[] args); - - public void close(Throwable cause); - - public void handleMessage(RemoteMessage message); - + + public FutureEx asyncRemoteInvocation(RemoteStub remoteStub, Object proxy, Method method, Object[] args); + + public void close(Throwable cause); + + public void handleMessage(RemoteMessage message); + public void exportObject(Class facade, C impl); public Object streamResolveObject(Object obj) throws IOException; diff --git a/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel1.java b/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel1.java index 017fc3b..77cc68b 100644 --- a/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel1.java +++ b/zerormi/src/main/java/org/gridkit/zerormi/RmiChannel1.java @@ -465,7 +465,7 @@ private Object getProxyFromRemoteInstance(RemoteInstance remoteInstance) { if (proxy == null) { try { proxy = RemoteStub.buildProxy(remoteInstance, this); - } catch (ClassNotFoundException e) { + } catch (Exception e) { e.printStackTrace(); } remoteInstanceProxys.put(remoteInstance, proxy); @@ -474,20 +474,15 @@ private Object getProxyFromRemoteInstance(RemoteInstance remoteInstance) { return proxy; } - public void exportObject(Class iface, T implementation) { - exportObject(new Class[]{iface}, implementation); + public void exportObject(Class facade, T obj) { + exportObjectImpl(facade, obj); } - @SuppressWarnings({ "rawtypes" }) - private synchronized RemoteInstance exportObject(Class[] interfaces, Object obj) { + private synchronized RemoteInstance exportObjectImpl(Class facade, Object obj) { RemoteInstance remote = object2remote.get(obj); if (remote == null) { String uuid = UUID.randomUUID().toString(); - String[] ifNames = new String[interfaces.length]; - for (int i = 0; i != ifNames.length; ++i) { - ifNames[i] = interfaces[i].getName(); - } - remote = new RemoteInstance(uuid, ifNames); + remote = new RemoteInstance(uuid, facade.getName()); object2remote.put(obj, remote); remote2object.put(remote, obj); } @@ -540,7 +535,7 @@ public synchronized Object streamReplaceObject(Object obj) throws IOException { Object mr = marshaler.writeReplace(obj); if (mr instanceof Exported) { Exported exp = (Exported) mr; - return new RemoteRef(exportObject(exp.getInterfaces(), exp.getObject())); + return new RemoteRef(exportObjectImpl(exp.getOriginalClass(), exp.getObject())); } return mr; diff --git a/zerormi/src/main/java/org/gridkit/zerormi/SmartRmiMarshaler.java b/zerormi/src/main/java/org/gridkit/zerormi/SmartRmiMarshaler.java index 8402c1f..7f6fd43 100644 --- a/zerormi/src/main/java/org/gridkit/zerormi/SmartRmiMarshaler.java +++ b/zerormi/src/main/java/org/gridkit/zerormi/SmartRmiMarshaler.java @@ -19,12 +19,17 @@ import java.io.Serializable; import java.lang.reflect.Proxy; import java.rmi.Remote; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.objenesis.ObjenesisStd; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.matcher.ElementMatchers; + /** * * @author Alexey Ragozin (alexey.ragozin@gmail.com) @@ -41,127 +46,45 @@ public SmartRmiMarshaler() { remoteInterfaceMarkers = new Class[]{Remote.class}; } - public SmartRmiMarshaler(Class... types) { - remoteInterfaceMarkers = types; + /** + * Return supclass of object's class implementing merker interface CreateRemoteProxy. + * All methods will be proxied to original class. + */ + public static T wrapForRmiProxy(T object) { + Class proxyClass = new ByteBuddy() + .subclass(object.getClass()) + .method(ElementMatchers.any()) + .intercept(MethodDelegation.to(object)) + + .implement(CreateRemoteProxy.class)// mark as eligible to proxy-creation + + .make() + .load(RemoteStub.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded(); + //noinspection unchecked + return (T) new ObjenesisStd().newInstance(proxyClass); } - + @Override @SuppressWarnings("rawtypes") public Object writeReplace(Object obj) throws IOException { if (obj instanceof Serializable && !Proxy.isProxyClass(obj.getClass())) { return obj; // no marshaling } - else if (isEligbleForExport(obj)){ - Class[] ifs = getRemoteInterfaces(obj); - return new Exported(obj, ifs); - } - else { - return SmartAnonMarshaler.marshal(obj); - } - } - - @SuppressWarnings("rawtypes") - protected boolean isEligbleForExport(Object obj) { - for(Class marker: remoteInterfaceMarkers) { - if (marker.isInstance(obj)) { - return true; - } - } - - return false; - } - - protected Class[] getRemoteInterfaces(Object obj) throws IOException { - Class objClass = obj.getClass(); - Class[] result = remoteAutodetectCache.get(objClass); - if (result != null) { - return result; - } else { - result = detectRemoteInterfaces(objClass); - remoteAutodetectCache.put(objClass, result); - return result; - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - Class[] detectRemoteInterfaces(Class objClass) throws IOException { - Class[] result; - List> iflist = new ArrayList>(); - collect(iflist, objClass); - - Iterator> it = iflist.iterator(); - while (it.hasNext()) { - Class intf = it.next(); - - if (!isRemoteInterface(intf)) { - it.remove(); - continue; - } - - for (Class other : new ArrayList>(iflist)) { - if (intf != other && intf.isAssignableFrom(other)) { - it.remove(); - } - } - } - - if (iflist.isEmpty()) { - // no interfaces are explicitly marker as remote - // this is a special case, assume all interfaces except Remote markers are exported - for(Class intf: objClass.getInterfaces()) { - if (!isRemoteInterface(intf)) { - iflist.add(intf); - } - } - - reduceSuperTypes(iflist); + else if (obj instanceof CreateRemoteProxy){ + return new Exported(obj, obj.getClass().getSuperclass()); } - - if (iflist.isEmpty()) { - throw new IOException("Cannot calculate remote interface for class " + objClass.getName()); + else if (RemoteStub.isRemoteStub(obj)){ + return new Exported(obj, RemoteStub.getOriginalClass(obj)); } - - result = iflist.toArray(new Class[iflist.size()]); - return result; - } - - private void collect(List> iflist, Class objClass) { - if (objClass == Object.class) { - return; + else if (obj instanceof Remote){ + return new Exported(obj, obj.getClass()); } - for(Class c: objClass.getInterfaces()) { - if (!iflist.contains(c)) { - iflist.add(c); - } + else { + return SmartAnonMarshaler.marshal(obj); } - collect(iflist, objClass.getSuperclass()); - } - - private void reduceSuperTypes(List> iflist) { - Iterator> it = iflist.iterator(); - while (it.hasNext()) { - Class intf = it.next(); - for (Class other : iflist) { - if (intf != other && intf.isAssignableFrom(other)) { - it.remove(); - } - } - } } - @SuppressWarnings("rawtypes") - private boolean isRemoteInterface(Class intf) { - boolean remote = false; - for (Class marker : remoteInterfaceMarkers) { - if (marker.isAssignableFrom(intf)) { - remote = true; - break; - } - } - return remote; - } - - @Override public Object readResolve(Object obj) throws IOException { if (obj instanceof SmartAnonMarshaler.AnonEnvelop) { diff --git a/zerormi/src/test/java/org/gridkit/zerormi/SmartRmiMarshalerTest.java b/zerormi/src/test/java/org/gridkit/zerormi/SmartRmiMarshalerTest.java index 1cebda5..873b1d9 100644 --- a/zerormi/src/test/java/org/gridkit/zerormi/SmartRmiMarshalerTest.java +++ b/zerormi/src/test/java/org/gridkit/zerormi/SmartRmiMarshalerTest.java @@ -57,98 +57,5 @@ public String toString() { Object md = ois.readObject(); Assert.assertEquals("task.10.100", SmartAnonMarshaler.unmarshal(md).toString()); - } - - @Test - public void check_PingServerA() throws IOException { - assertRemotes(PingServerA.class, PingA.class); - } - - @Test - public void check_PingServerB() throws IOException { - assertRemotes(PingServerB.class, PingA.class); - } - - @Test - public void check_PingServerC() throws IOException { - assertRemotes(PingServerC.class, PingC.class, PingA.class); - } - - @Test - public void check_PingServerX() throws IOException { - assertRemotes(PingServerX.class, PingX.class); - } - - @Test - public void check_PingServerXX() throws IOException { - assertRemotes(PingServerXX.class, PingX.class, PingC.class); - } - - void assertRemotes(Class target, Class... expected) throws IOException { - SmartRmiMarshaler marshaler = new SmartRmiMarshaler(); - Assert.assertEquals(Arrays.toString(expected), Arrays.toString(marshaler.detectRemoteInterfaces(target))); - } - - - @Test(expected = IOException.class) - public void check_PingUnserver() throws IOException { - SmartRmiMarshaler marshaler = new SmartRmiMarshaler(); - - marshaler.detectRemoteInterfaces(PingUnserver.class); - } - - public interface PingA extends Remote { - - public void ping(); - - } - - public interface PingC extends Remote { - - public void ping(); - - } - - public interface PingX extends PingA { - - } - - public static class PingUnserver { - - } - - public static class PingServerA implements PingA { - - @Override - public void ping() { - } - } - - public static class PingServerB extends PingServerA { - - @Override - public void ping() { - } - } - - public static class PingServerC extends PingServerB implements PingC { - - @Override - public void ping() { - } - } - - public static class PingServerX implements PingX { - - @Override - public void ping() { - } - } - - public static class PingServerXX extends PingServerC implements PingX { - - @Override - public void ping() { - } } }