Fixed TunnelToServer bean registration for replaced services
Change-Id: I9f1044b3f2d01c0bd68544ab1b84da9d7f58e21d
Reviewed-on: https://git.eclipse.org/r/58438
Tested-by: Hudson CI
Reviewed-by: Andi Bur <andi.bur@gmail.com>
diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListenerTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListenerTest.java
new file mode 100644
index 0000000..ae0a033
--- /dev/null
+++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListenerTest.java
@@ -0,0 +1,177 @@
+package org.eclipse.scout.rt.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.scout.commons.annotations.Replace;
+import org.eclipse.scout.commons.exception.ProcessingException;
+import org.eclipse.scout.rt.platform.IBean;
+import org.eclipse.scout.rt.platform.interceptor.IBeanInterceptor;
+import org.eclipse.scout.rt.platform.interceptor.IBeanInvocationContext;
+import org.eclipse.scout.rt.platform.internal.BeanManagerImplementor;
+import org.eclipse.scout.rt.platform.inventory.IClassInfo;
+import org.eclipse.scout.rt.platform.inventory.IClassInventory;
+import org.eclipse.scout.rt.platform.inventory.internal.JandexClassInventory;
+import org.eclipse.scout.rt.platform.service.IService;
+import org.eclipse.scout.rt.shared.TunnelToServer;
+import org.jboss.jandex.Indexer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test {@link TunnelToServer} with and without {@link Replace} annotations
+ */
+public class RegisterTunnelToServerPlatformListenerTest {
+
+ private static JandexClassInventory s_classInventory;
+
+ private BeanManagerImplementor m_beanManager;
+ private RegisterTunnelToServerPlatformListener m_registrator;
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ Indexer indexer = new Indexer();
+ indexClass(indexer, IFixtureTunnelToServer.class);
+ indexClass(indexer, IFixtureTunnelToServerEx1.class);
+ indexClass(indexer, IFixtureTunnelToServerEx2.class);
+ s_classInventory = new JandexClassInventory(indexer.complete());
+ }
+
+ protected static void indexClass(Indexer indexer, Class<?> clazz) throws IOException {
+ indexer.index(clazz.getResourceAsStream(RegisterTunnelToServerPlatformListenerTest.class.getSimpleName() + "$" + clazz.getSimpleName() + ".class"));
+ }
+
+ @Before
+ public void before() {
+ m_beanManager = new BeanManagerImplementor(new FixtureClientBeanDecorationFactory());
+ m_registrator = new RegisterTunnelToServerPlatformListener();
+ }
+
+ @After
+ public void after() {
+ m_beanManager = null;
+ }
+
+ @Test
+ public void testBase() {
+ registerTunnelToServerBeans(IFixtureTunnelToServer.class);
+ assertPings("return IFixtureTunnelToServer#ping", IFixtureTunnelToServer.class);
+ }
+
+ @Test
+ public void testReplaceEx1() {
+ registerTunnelToServerBeans(IFixtureTunnelToServer.class, IFixtureTunnelToServerEx1.class);
+ assertPings("return IFixtureTunnelToServerEx1#ping", IFixtureTunnelToServer.class, IFixtureTunnelToServerEx1.class);
+ }
+
+ @Test
+ public void testReplaceEx1ReverseOrderRegistration() {
+ registerTunnelToServerBeans(IFixtureTunnelToServerEx1.class, IFixtureTunnelToServer.class);
+ assertPings("return IFixtureTunnelToServerEx1#ping", IFixtureTunnelToServer.class, IFixtureTunnelToServerEx1.class);
+ }
+
+ @Test
+ public void testReplaceEx2_1() {
+ registerTunnelToServerBeans(IFixtureTunnelToServer.class, IFixtureTunnelToServerEx1.class, IFixtureTunnelToServerEx2.class);
+ assertReplaceEx2();
+ }
+
+ @Test
+ public void testReplaceEx2_2() {
+ registerTunnelToServerBeans(IFixtureTunnelToServer.class, IFixtureTunnelToServerEx2.class, IFixtureTunnelToServerEx1.class);
+ assertReplaceEx2();
+ }
+
+ @Test
+ public void testReplaceEx2_3() {
+ registerTunnelToServerBeans(IFixtureTunnelToServerEx1.class, IFixtureTunnelToServer.class, IFixtureTunnelToServerEx2.class);
+ assertReplaceEx2();
+ }
+
+ @Test
+ public void testReplaceEx2_4() {
+ registerTunnelToServerBeans(IFixtureTunnelToServerEx1.class, IFixtureTunnelToServerEx2.class, IFixtureTunnelToServer.class);
+ assertReplaceEx2();
+ }
+
+ @Test
+ public void testReplaceEx2_5() {
+ registerTunnelToServerBeans(IFixtureTunnelToServerEx2.class, IFixtureTunnelToServer.class, IFixtureTunnelToServerEx1.class);
+ assertReplaceEx2();
+ }
+
+ @Test
+ public void testReplaceEx2_6() {
+ registerTunnelToServerBeans(IFixtureTunnelToServerEx2.class, IFixtureTunnelToServerEx1.class, IFixtureTunnelToServer.class);
+ assertReplaceEx2();
+ }
+
+ private void assertReplaceEx2() {
+ assertPings("return IFixtureTunnelToServerEx2#ping", IFixtureTunnelToServer.class, IFixtureTunnelToServerEx1.class, IFixtureTunnelToServerEx2.class);
+ }
+
+ private void registerTunnelToServerBeans(Class<?>... classes) {
+ Set<IClassInfo> classInfos = new LinkedHashSet<>();
+ for (Class<?> c : classes) {
+ classInfos.add(s_classInventory.getClassInfo(c));
+ }
+ IClassInventory classInventory = mock(IClassInventory.class);
+ when(classInventory.getKnownAnnotatedTypes(TunnelToServer.class)).thenReturn(classInfos);
+ m_registrator.registerTunnelToServerProxies(m_beanManager, classInventory);
+ }
+
+ private void assertPings(String expectedPingResult, Class<?>... queryClasses) {
+ for (Class<?> queryClass : queryClasses) {
+ @SuppressWarnings("unchecked")
+ Class<? extends IFixtureTunnelToServer> clazz = (Class<? extends IFixtureTunnelToServer>) queryClass;
+ assertOnePing(expectedPingResult, clazz);
+ }
+ }
+
+ private <T extends IFixtureTunnelToServer> void assertOnePing(String expectedPingResult, Class<T> queryClass) {
+ assertTrue(IFixtureTunnelToServer.class.isAssignableFrom(queryClass));
+ IBean<T> bean = m_beanManager.getBean(queryClass);
+ assertNotNull(bean);
+ T obj = bean.getInstance(queryClass);
+ assertNotNull(obj);
+ assertEquals(expectedPingResult, obj.ping());
+ }
+
+ private static final class FixtureClientBeanDecorationFactory extends ClientBeanDecorationFactory {
+ @Override
+ protected <T> IBeanInterceptor<T> decorateWithTunnelToServer(final IBean<T> bean, final Class<T> queryType) {
+ return new IBeanInterceptor<T>() {
+ @Override
+ public Object invoke(IBeanInvocationContext<T> context) throws ProcessingException {
+ Method method = context.getTargetMethod();
+ return "return " + bean.getBeanClazz().getSimpleName() + "#" + method.getName();
+ }
+ };
+ }
+ }
+
+ @TunnelToServer
+ public static interface IFixtureTunnelToServer extends IService {
+ String ping();
+ }
+
+ @Replace
+ @TunnelToServer
+ public static interface IFixtureTunnelToServerEx1 extends IFixtureTunnelToServer {
+ }
+
+ @Replace
+ @TunnelToServer
+ public static interface IFixtureTunnelToServerEx2 extends IFixtureTunnelToServerEx1 {
+ }
+}
diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListener.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListener.java
index 9c708fa..28495f9 100644
--- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListener.java
+++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/RegisterTunnelToServerPlatformListener.java
@@ -1,9 +1,13 @@
package org.eclipse.scout.rt.client;
+import java.util.List;
+
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.platform.BeanMetaData;
+import org.eclipse.scout.rt.platform.IBean;
import org.eclipse.scout.rt.platform.IBeanDecorationFactory;
+import org.eclipse.scout.rt.platform.IBeanManager;
import org.eclipse.scout.rt.platform.IPlatform;
import org.eclipse.scout.rt.platform.IPlatformListener;
import org.eclipse.scout.rt.platform.PlatformEvent;
@@ -11,6 +15,7 @@
import org.eclipse.scout.rt.platform.exception.PlatformException;
import org.eclipse.scout.rt.platform.inventory.ClassInventory;
import org.eclipse.scout.rt.platform.inventory.IClassInfo;
+import org.eclipse.scout.rt.platform.inventory.IClassInventory;
import org.eclipse.scout.rt.shared.SharedConfigProperties.CreateTunnelToServerBeansProperty;
import org.eclipse.scout.rt.shared.TunnelToServer;
@@ -27,24 +32,53 @@
return;
}
//register all tunnels to server
- for (IClassInfo ci : ClassInventory.get().getKnownAnnotatedTypes(TunnelToServer.class)) {
- if (!ci.isInterface() || !ci.isPublic()) {
- LOG.error("The annotation @" + TunnelToServer.class.getSimpleName() + " can only be used on public interfaces, not on " + ci.name());
- continue;
- }
- Class<?> c;
- try {
- c = ci.resolveClass();
- }
- catch (Exception e) {
- LOG.warn("loading class", e);
- continue;
- }
- if (!event.getSource().getBeanManager().getBeans(c).isEmpty()) {
- continue;
- }
- event.getSource().getBeanManager().registerBean(new BeanMetaData(c).withApplicationScoped(false));
+ final IBeanManager beanManager = event.getSource().getBeanManager();
+ final IClassInventory classInventory = ClassInventory.get();
+ registerTunnelToServerProxies(beanManager, classInventory);
+ }
+ }
+
+ protected void registerTunnelToServerProxies(final IBeanManager beanManager, final IClassInventory classInventory) {
+ for (IClassInfo ci : classInventory.getKnownAnnotatedTypes(TunnelToServer.class)) {
+ if (!ci.isInterface() || !ci.isPublic()) {
+ LOG.error("The annotation @" + TunnelToServer.class.getSimpleName() + " can only be used on public interfaces, not on " + ci.name());
+ continue;
+ }
+ Class<?> c;
+ try {
+ c = ci.resolveClass();
+ }
+ catch (Exception e) {
+ LOG.warn("loading class", e);
+ continue;
+ }
+
+ if (!acceptClass(beanManager, c)) {
+ LOG.debug("ignoring class [{}]", c);
+ continue;
+ }
+
+ beanManager.registerBean(createBeanMetaData(c));
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the given class (a public interface) should be registered as bean.
+ */
+ protected boolean acceptClass(IBeanManager beanManager, Class<?> beanClass) {
+ List<? extends IBean<?>> beans = beanManager.getBeans(beanClass);
+ for (IBean<?> bean : beans) {
+ if (!bean.getBeanClazz().isInterface()) {
+ return false;
}
}
+ return true;
+ }
+
+ /**
+ * Creates a new {@link BeanMetaData} for the given class.
+ */
+ protected BeanMetaData createBeanMetaData(Class<?> c) {
+ return new BeanMetaData(c).withApplicationScoped(false);
}
}