Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » scout » [neon] Replacing proxied beans and service tunnel in Stubbed Client
[neon] Replacing proxied beans and service tunnel in Stubbed Client [message #1752062] Wed, 18 January 2017 12:32 Go to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 567
Registered: October 2012
Location: Bern, Switzerland
Senior Member
In one of the applications that I am migrating from Mars to Neon the developers added the possibility of running the client in "stubbed mode". This mode is triggered by passing a specific system property when starting the client.

In addition to the Service-Interfaces (defined in shared) and their normal implementations (in server), this project also has stubbed implementations of all these services, that are not registered in the plugin.xml file but are manually registered to replace the serivce-proxies created by scout when the client is started. At the same time, the normal service tunnel is replaced with an empty implementation of the service tunnel interface. The stubbed service implementations then contain mocked functionality. They used this to be able to start the client without needing to start a server for fast and simple client development and testing.

I am now trying to migrate this "on the fly replacement" of the service proxies with the stubbed service implementations and am not sure how to do this correctly.

Here is a rough code skeletton of my setup, the service-classes already in their migrated (neon) form, the registration code still in the unmigrated (mars) form:
shared:
@ApplicationScoped
@TunnelToServer
public interface IMyService {
  public MyDto someAction();
}

server:
public class MyService implements IMyService {
  public MyDto someAction() {
    return getDtoFromBackendOrEvenDatabase();
  }
}

client:
public class MyServiceStub implements IMyService {
  public MyDto someAction() {
    // mock implementation without going to server
    return new MyDtoBuilder().withValueA(a).withValueB(b).build();
  }
}

public class MyClientSession extends AbstractClientSession {
  @Override
  public void execLoadSession() throws ProcessingException {
    if (isClientOnlyMode()) {
      registerStubbedServices();
    } else {
      super.execLoadSession();
    }
  }
  
  //
  // all code below has not yet been (fully) migrated to Neon
  //
  private void registerStubbedServices() throws ProcessingException {
    Object[] stubedServices = StubbedServiceRegistry.getStubbedServices();
    registerServices(getBundle(), 500, stubedServices); // I guess I can get rid of getBundle() here
    setServiceTunnel(new StubbedServiceTunnel()); // how do I best replace the service tunnel bean?
    setDesktop(new Desktop());
  }

  public static List<ServiceRegistration<?>> registerServices(Bundle bundle, int ranking, Object... services) {
    ArrayList<ServiceRegistration<?>> result = new ArrayList<>();
    Hashtable<String, Object> initParams = new Hashtable<>();
    initParams.put(Constants.SERVICE_RANKING, ranking); // how do I dynamically set the ranking on my services when I register them?
    for (Object service : services) {
      ServiceRegistration<?> reg = bundle.getBundleContext().registerService(computeServiceNames(service), service, initParams); // I guess I do this using BEANS.getBeanManager().registerClass(), but how do I make sure no proxy is created or if it was already created, how do I replace it?
      result.add(reg);
      if (Proxy.isProxyClass(service.getClass())) {
        // nop
      } else if (service instanceof IService) {
        ((IService) service).initializeService(reg); // I guess I can just skip this, as the services no longer have this method by default
      }
    }
    SERVICES.clearCache(); // is something similar needed on BEANS?
    return result;
  }
}

  /**
   * NOP service tunnel
   */
  public final class StubbedServiceTunnel implements IClientServiceTunnel {
    @Override
    public void setServerURL(URL url) {
    }

    @Override
    public Object invokeService(Class<?> serviceInterfaceClass, Method operation, Object[] args) throws ProcessingException {
      return null;
    }

    @Override
    public URL getServerURL() {
      return null;
    }

    @Override
    public void setClientNotificationPollInterval(long intervallMillis) {
    }

    @Override
    public void setAnalyzeNetworkLatency(boolean b) {
    }

    @Override
    public boolean isAnalyzeNetworkLatency() {
      return false;
    }

    @Override
    public long getClientNotificationPollInterval() {
      return 0;
    }
  }


I've read up on Bean-Management but some questions still remain. In Mars the service proxies were created using the ClientProxyServiceFactory registered for each interface in the plugin.xml. How is this done in Neon? Or rather, how can I stop this from happening in stubbed mode? If I cannot stop it from happening, I see two possibilities:

  • either I can register the stubbed service in addition to the service proxy, but then I must make sure that the stubbed service has a higher ranking/lower order than the proxy, how do I do that?
  • or I can replace the proxy with the stubbed service. Using the @Replace annotation is probably not an option because in "standard mode" the proxy must not be replaced. There is BEANS.getBeanManager().unregisterClass/unregisterBean, but will this work for the proxies? If so, which argument do I need to use? The service interface? Or do I need to use the proxy class? If so, how do I get this class?
  • or is there a more elegant way that I am missing?


Once I have solved this I should be able to also replace the normal service tunnel with my NOP-service tunnel implementation.
Re: [neon] Replacing proxied beans and service tunnel in Stubbed Client [message #1752067 is a reply to message #1752062] Wed, 18 January 2017 14:15 Go to previous messageGo to next message
Jeremie Bresson is currently offline Jeremie BressonFriend
Messages: 1250
Registered: October 2011
Senior Member
I do not understand why you need to replace the tunnel proxy implementation...

You just need to register "MyServiceStub" with an higher priority, and when you will call BEANS.get(IMyService.class) you will get the "MyServiceStub" instance instead of the dynamically generated tunnel-proxy implementation.

Here is how I would do it:

* add "org.eclipse.scout.rt.platform.IgnoreBean" annotation on your MyServiceStub
* in registerStubbedServices() register manually your bean as described here: http://eclipsescout.github.io/6.0/technical-guide.html#bean-registration (you can specify an appropriate order with withOrder(..) on the BeanMetadata)

See also http://stackoverflow.com/a/36147550/91497 [a similar pattern is used in client unit tests]

---

Why are your stubbed implementations not in a separate project (maven artifact) that you include or not on the classpath when you start your application?

[Updated on: Wed, 18 January 2017 14:17]

Report message to a moderator

Re: [neon] Replacing proxied beans and service tunnel in Stubbed Client [message #1752130 is a reply to message #1752067] Thu, 19 January 2017 07:14 Go to previous messageGo to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 567
Registered: October 2012
Location: Bern, Switzerland
Senior Member
Jérémie

Thank you for your answers, I'll give this a try. As to your questions:
Quote:
I do not understand why you need to replace the tunnel proxy implementation...
Neither do I and the person who wrote the code is no longer with us. Maybe it was needed for the client to work without a server in Mars despite the ServiceStubs being registered...
Quote:
Why are your stubbed implementations not in a separate project (maven artifact) that you include or not on the classpath when you start your application?
For historical reasons, because that's the code base used in Mars Smile I could probably change that if needed.

What priority do the scout generated service proxies have? Somewhere in the range of 4001 to 5999 that is mentioned in the technical guide in the bean manager chapter? So if I used withOrder(2000) my stubs should get higher priority, right?
Re: [neon] Replacing proxied beans and service tunnel in Stubbed Client [message #1752133 is a reply to message #1752130] Thu, 19 January 2017 07:38 Go to previous messageGo to next message
Jeremie Bresson is currently offline Jeremie BressonFriend
Messages: 1250
Registered: October 2011
Senior Member
Urs Beeli wrote on Thu, 19 January 2017 08:14
What priority do the scout generated service proxies have? Somewhere in the range of 4001 to 5999 that is mentioned in the technical guide in the bean manager chapter? So if I used withOrder(2000) my stubs should get higher priority, right?


I had a look at: org.eclipse.scout.rt.shared.servicetunnel.RegisterTunnelToServerPlatformListener.registerTunnelToServerProxy(IBeanManager, Class<?>)

In the method RegisterTunnelToServerPlatformListener.createBeanMetaData(Class<?>) no order is defined, so I guess that the proxy are registered with the default value 5000.

You can give 2000 a try. For debugging purpose you can use BEANS.all(IMyService.class) and verify that you have the 2 implementations you except (your stub first and the proxy in the second position).
Re: [neon] Replacing proxied beans and service tunnel in Stubbed Client [message #1752155 is a reply to message #1752130] Thu, 19 January 2017 09:50 Go to previous message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 567
Registered: October 2012
Location: Bern, Switzerland
Senior Member
OK, I've done some experimentation and there still are some issues.

ServiceTunnel
I *do* need to replace the service tunnel with a dummy implementation that has isActive() return false. If I don't do this, the ClientNotification mechanism tries to connect to the server which is not running and when I try to log in, the same happens and the login fails.

I am doing this during startup in the PlatformListener during the BeanManagerPrepared phase. I noticed that my dummy service tunnel *cannot* be an inner class, otherwise the bean manager cannot instantiate it.

public class MyPlattformListener implements IPlattformListener {
  @Override
  public void stateChanged(PlatformEvent event) {
    if (event.getState() == State.BeanManagerPrepared) {
      System.out.println("Registering stubbed service tunnel");
      if (isClientOnlyMode()) {
        BeanMetaData beanData = new BeanMetaData(StubbedServiceTunnel.class).withApplicationScoped(true).withOrder(500);
        BEANS.getBeanManager().registerBean(beanData);
      }
      System.out.println("Stubbed service tunnel registered");
    }
  }
}


This let's me start the ui server without any error messages and the login mechanism works. However, for some strange reason this only works when launching the ui server using Tomcat (in that case I see the two sysouts telling me about registration of the stubbed tunnel *and* I also see the sysout I have placed in the constructor of the stubbed service tunnel). When launching my ui server using Jetyy I see the two sysouts telling me that the stubbed service tunnel was registered but I never see the constructor-sysout of the stubbed tunnel and then the application throws exceptions about "connection refused" both in the client notification mechanism as well as when trying to log in. [see quote at the very bottom]

--> is there any reason why the jetty based approach would not instantiate the stubbed tunnel that I am missing?

Replacing the services
As with the replacement of the service tunnel, I had to move the stubbing point from MyClientSession.execLoadSession() to the PlattformListener (if I didn't do that, BEANS.all() returned a list containing both the stubbed service *and* the proxy, BEANS.get() returned the proxy).
  private void registerStubbedServices() throws ProcessingException {
    Object[] services = StubbedServiceRegistry.getStubbedServices();
    for (Object service : services) {
      BeanMetaData beanData = new BeanMetaData(service.getClass()).withApplicationScoped(true).withOrder(500).withReplace(true);
      BEANS.getBeanManager().registerBean(beanData);
      }
  }


So, for the time being it looks like the replacement works using tomcat but not jetty....



[quote name="Output when run in Jetty"][2017-01-19 10:37:03,758] [Thread-9] INFO org.eclipse.scout.rt.platform.inventory.ClassInventory <clinit> - Finished preparation of jandex class inventory in 1632.409027 ms
Registering stubbed service tunnel
Stubbed service tunnel registered
[2017-01-19 10:37:04,177] [Thread-9] INFO org.eclipse.scout.rt.platform.job.internal.DevelopmentThreadNameDecorator stateChanged - +++ Development thread name decoration
[2017-01-19 10:37:04,183] [Thread-9] INFO org.eclipse.scout.rt.platform.logger.LoggerPlatformListener registerLoggerSupportBean - registered logger support [org.eclipse.scout.rt.platform.logger.Log4jLoggerSupport]
[2017-01-19 10:37:04,206] [Thread-9] INFO org.eclipse.scout.rt.shared.services.common.code.CodeTypeRegistrator stateChanged - 29 code type classes registered.
[2017-01-19 10:37:04,243] [Thread-9] INFO org.eclipse.scout.rt.shared.servicetunnel.RegisterTunnelToServerPlatformListener registerTunnelToServerProxies - Tunnel to server proxies registered.
[2017-01-19 10:37:04,248] [Thread-9] INFO org.eclipse.scout.rt.shared.servicetunnel.http.MultiSessionCookieStoreInstaller install - Successfully installed java.net.CookieManager@43badb7c (Cookie store: org.eclipse.scout.rt.shared.servicetunnel.http.MultiSessionCookieStore@38a138e3)
[2017-01-19 10:37:04,348] [main] INFO org.eclipse.jetty.server.handler.ContextHandler doStart - Started o.e.s.d.j.P_WebAppContext@6500df86{/fos,file:/D:/dev/workspaces/cisi-neon/fos.scout.ui.html.dev/src/main/webapp/,AVAILABLE}
[2017-01-19 10:37:04,417] [main] INFO org.eclipse.jetty.server.ServerConnector doStart - Started ServerConnector@4abdb505{HTTP/1.1}{0.0.0.0:8083}
[2017-01-19 10:37:04,417] [main] INFO org.eclipse.jetty.server.Server doStart - Started @3179ms
[2017-01-19 10:37:04,423] [main] INFO org.eclipse.scout.dev.jetty.JettyServer start - Server ready. To run the application, open one of the following addresses in a web browser:
---------------------------------------------------------------------
http://localhost:8083/fos
http://k18270:8083/fos
http://192.168.56.1:8083/fos
---------------------------------------------------------------------
To shut the server down, type "shutdown" in the console.

[2017-01-19 10:37:05,341] [scout-thread-2 ClientNotificationPoller] ERROR org.eclipse.scout.rt.client.clientnotification.ClientNotificationPoller run - Error receiving client notifications
org.eclipse.scout.rt.platform.exception.PlatformException: Connection refused: connect [translator=org.eclipse.scout.rt.platform.exception.DefaultRuntimeExceptionTranslator, user=notification-authenticator, calling-thread=scout-thread-2 ClientNotificationPoller, job=Tunneling service request [seq=1, submitter=ClientNotificationPoller]]
at org.eclipse.scout.rt.platform.exception.DefaultRuntimeExceptionTranslator.translateInternal(DefaultRuntimeExceptionTranslator.java:54)
at org.eclipse.scout.rt.platform.exception.DefaultRuntimeExceptionTranslator.translate(DefaultRuntimeExceptionTranslator.java:37)
at org.eclipse.scout.rt.platform.exception.DefaultRuntimeExceptionTranslator.translate(DefaultRuntimeExceptionTranslator.java:1)
at org.eclipse.scout.rt.platform.job.internal.JobExceptionTranslator.translateExecutionException(JobExceptionTranslator.java:63)
at org.eclipse.scout.rt.platform.job.internal.JobFutureTask.awaitDoneAndGet(JobFutureTask.java:392)
at org.eclipse.scout.rt.platform.job.internal.JobFutureTask.awaitDoneAndGet(JobFutureTask.java:381)
at org.eclipse.scout.rt.shared.servicetunnel.http.HttpServiceTunnel.tunnel(HttpServiceTunnel.java:230)
at org.eclipse.scout.rt.shared.servicetunnel.AbstractServiceTunnel.invokeService(AbstractServiceTunnel.java:55)
at org.eclipse.scout.rt.shared.servicetunnel.AbstractServiceTunnel.invokeService(AbstractServiceTunnel.java:44)
at org.eclipse.scout.rt.shared.servicetunnel.http.HttpServiceTunnel.invokeService(HttpServiceTunnel.java:191)
at org.eclipse.scout.rt.shared.servicetunnel.ServiceTunnelProxyProducer.invoke(ServiceTunnelProxyProducer.java:43)
at org.eclipse.scout.rt.platform.interceptor.DecoratingProxy.invokeImpl(DecoratingProxy.java:134)
at org.eclipse.scout.rt.platform.interceptor.DecoratingProxy$P_InvocationHandler.invoke(DecoratingProxy.java:172)
at com.sun.proxy.$Proxy18.getNotifications(Unknown Source)
at org.eclipse.scout.rt.client.clientnotification.ClientNotificationPoller$P_NotificationPoller.run(ClientNotificationPoller.java:84)
at org.eclipse.scout.rt.platform.util.concurrent.Callables$1.call(Callables.java:37)
at org.eclipse.scout.rt.platform.util.concurrent.Callables$1.call(Callables.java:1)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:185)
at org.eclipse.scout.rt.platform.job.internal.ExceptionProcessor.intercept(ExceptionProcessor.java:41)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:180)
at org.eclipse.scout.rt.platform.context.RunContextRunner$1.call(RunContextRunner.java:42)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:185)
at org.eclipse.scout.rt.platform.security.SubjectProcessor$1.run(SubjectProcessor.java:47)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:422)
at org.eclipse.scout.rt.platform.security.SubjectProcessor.intercept(SubjectProcessor.java:43)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:180)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain.call(CallableChain.java:135)
at org.eclipse.scout.rt.platform.context.RunContext.call(RunContext.java:121)
at org.eclipse.scout.rt.platform.context.RunContextRunner.intercept(RunContextRunner.java:38)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:180)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain.call(CallableChain.java:135)
at org.eclipse.scout.rt.platform.job.internal.JobFutureTask$1.call(JobFutureTask.java:100)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.eclipse.scout.rt.platform.job.internal.JobFutureTask.run(JobFutureTask.java:160)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
at org.eclipse.scout.rt.platform.job.internal.NamedThreadFactory$1.run(NamedThreadFactory.java:54)
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:211)
at sun.net.www.http.HttpClient.New(HttpClient.java:308)
at sun.net.www.http.HttpClient.New(HttpClient.java:326)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1168)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1104)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:998)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:932)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1282)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1257)
at org.eclipse.scout.rt.shared.servicetunnel.http.HttpServiceTunnel.createURLConnection(HttpServiceTunnel.java:114)
at org.eclipse.scout.rt.shared.servicetunnel.http.RemoteServiceInvocationCallable.call(RemoteServiceInvocationCallable.java:73)
at org.eclipse.scout.rt.shared.servicetunnel.http.RemoteServiceInvocationCallable.call(RemoteServiceInvocationCallable.java:1)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:185)
at org.eclipse.scout.rt.platform.context.RunContextRunner$1.call(RunContextRunner.java:42)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:185)
... 12 more
[/quote]

[Updated on: Thu, 19 January 2017 09:53]

Report message to a moderator

Previous Topic:[neon] How to specify different config.properties file when starting client
Next Topic:[neon] Compilation fails with "cannot find symbol" using javac
Goto Forum:
  


Current Time: Tue Nov 21 04:42:47 GMT 2017

Powered by FUDForum. Page generated in 0.02208 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software