Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse Scout » [neon] How to pass per-request information from client to server?
[neon] How to pass per-request information from client to server? [message #1753536] Tue, 07 February 2017 15:05 Go to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 573
Registered: October 2012
Location: Bern, Switzerland
Senior Member
In the Mars version of our application we created a "request-id" for each server call that we passed to the server. This request id was then used in all log statements, so that we can reconcile logs in the client and server logs.

The way we did this was that we extended the ServiceTunnelRequest, the ClientHttpServiceTunnel and the ServiceTunnelServlet with implementations of our own.

We then overrode ClientHttpServiceTunnel.decorateBackgroundJob() in which we set the requestId property of our own ServiceTunnelRequest implementation. In ServiceTunnelServlet.runServerJobTransaction() and ServiceTunnelServlet.runServerJobTransactionWithDelegate() we then extracted this information and stored it on a per-thread context.

However, as the whole service tunnel mechanism was greatly refactored in Neon we had to throw all this out at the very start. Unfortunately we didn't realise this would break our log-request-id mechanism.

What is the recommended way to pass per-service-call information from client to server implicitely in Neon?

[Updated on: Tue, 07 February 2017 15:11]

Report message to a moderator

Re: [neon] How to pass per-request information from client to server? [message #1753537 is a reply to message #1753536] Tue, 07 February 2017 15:10 Go to previous messageGo to next message
Arthur van Dorp is currently offline Arthur van DorpFriend
Messages: 48
Registered: October 2015
Member
Scout Neon already comes with a correlation id (see class CorrelationId). It shows up in your log files as scout_correlation_id and in your HTTP requests as X-Scout-Correlation-Id.
Re: [neon] How to pass per-request information from client to server? [message #1753539 is a reply to message #1753537] Tue, 07 February 2017 15:15 Go to previous messageGo to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 573
Registered: October 2012
Location: Bern, Switzerland
Senior Member
Thanks Arthur

The problem is that we are already using our own id in many places, so I am not sure using the scout correlation would work. Is there a way of accessing or modifying this id in the client? And on the server side we would need to extract this correlation id to set our log context utility class. Where in the server can we "hook" into the exit of the service tunnel to do this?
Re: [neon] How to pass per-request information from client to server? [message #1753543 is a reply to message #1753539] Tue, 07 February 2017 15:26 Go to previous messageGo to next message
Arthur van Dorp is currently offline Arthur van DorpFriend
Messages: 48
Registered: October 2015
Member
If possible I would simply try to use the new id where possible as it seems to do exactly what you need. Otherwise I would look at where the CorrelationId is used in the Scout code and similarly add your own (you could e.g. overwrite HttpServiceTunnel.addCustomHeader() and add your own property as well on the connection, as it is done for the correlation id).
Re: [neon] How to pass per-request information from client to server? [message #1753608 is a reply to message #1753543] Wed, 08 February 2017 07:54 Go to previous messageGo to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 573
Registered: October 2012
Location: Bern, Switzerland
Senior Member
Arthur

It seems that addCustomHeader is what I need. However, I have trouble getting this to work. I've tried the following:
@Replace
public class MyHttpServiceTunnel extends HttpServiceTunnel {
  public final static String HEADER_REQUEST_ID = "MyRequestId";

  @Override
  protected void addCustomHeaders(URLConnection urlConn, String method, byte[] callData) throws IOException {
    super.addCustomHeaders(urlConn, method, callData);
    addRequestId(urlConn);
  }

  protected void addRequestId(final URLConnection urlConn) throws IOException {
    String reqid = RequestIdUtil.getNewRequestId();
    urlConn.setRequestProperty(HEADER_REQUEST_ID, reqid);
  }
}


I thought adding the @Replace annotation would be sufficient to ensure that my service tunnel implementation would be used but when starting the ui-server I get the exception listed at the end of my post.

Also, I've tried finding the code location where the correlation id is retrieved on server side but have not been succesful so far. Can you give me a hint?


[2017-02-08 08:52:30,622] [Thread-3] ERROR org.eclipse.scout.rt.platform.internal.PlatformImplementor start -   Error during platform startup
org.eclipse.scout.rt.platform.exception.BeanCreationException: Could not create bean [org.eclipse.scout.rt.client.clientnotification.ClientNotificationPoller]
	at org.eclipse.scout.rt.platform.internal.BeanInstanceUtil.translateException(BeanInstanceUtil.java:89)
	at org.eclipse.scout.rt.platform.internal.BeanInstanceUtil.createAndInitializeBean(BeanInstanceUtil.java:52)
	at org.eclipse.scout.rt.platform.internal.DefaultBeanInstanceProducer.createInstance(DefaultBeanInstanceProducer.java:101)
	at org.eclipse.scout.rt.platform.internal.DefaultBeanInstanceProducer.safeCreateInstance(DefaultBeanInstanceProducer.java:83)
	at org.eclipse.scout.rt.platform.internal.DefaultBeanInstanceProducer.access$0(DefaultBeanInstanceProducer.java:71)
	at org.eclipse.scout.rt.platform.internal.DefaultBeanInstanceProducer$1.call(DefaultBeanInstanceProducer.java:60)
	at org.eclipse.scout.rt.platform.util.FinalValue.setIfAbsent(FinalValue.java:90)
	at org.eclipse.scout.rt.platform.internal.DefaultBeanInstanceProducer.getApplicationScopedInstance(DefaultBeanInstanceProducer.java:57)
	at org.eclipse.scout.rt.platform.internal.DefaultBeanInstanceProducer.produce(DefaultBeanInstanceProducer.java:34)
	at org.eclipse.scout.rt.platform.internal.BeanImplementor.getInstance(BeanImplementor.java:72)
	at org.eclipse.scout.rt.platform.internal.BeanManagerImplementor.startCreateImmediatelyBeans(BeanManagerImplementor.java:245)
	at org.eclipse.scout.rt.platform.internal.PlatformImplementor.startCreateImmediatelyBeans(PlatformImplementor.java:236)
	at org.eclipse.scout.rt.platform.internal.PlatformImplementor.start(PlatformImplementor.java:137)
	at org.eclipse.scout.rt.platform.internal.PlatformStarter.run(PlatformStarter.java:32)
Caused by: org.eclipse.scout.rt.platform.exception.BeanCreationException: Exception while invoking @PostConstruct method
	at org.eclipse.scout.rt.platform.internal.BeanInstanceUtil.translateException(BeanInstanceUtil.java:89)
	at org.eclipse.scout.rt.platform.internal.BeanInstanceUtil.initializeBeanInstance(BeanInstanceUtil.java:69)
	at org.eclipse.scout.rt.platform.internal.BeanInstanceUtil.createAndInitializeBean(BeanInstanceUtil.java:49)
	... 12 more
Caused by: org.eclipse.scout.rt.platform.util.Assertions$AssertionException: Assertion error: multiple instances found for query: interface org.eclipse.scout.rt.shared.servicetunnel.IServiceTunnel [IBean[@Replace @Bean core.scout.shared.servicetunnel.MyHttpServiceTunnel], IBean[@Replace @Bean org.eclipse.scout.rt.client.servicetunnel.http.ClientHttpServiceTunnel]]
	at org.eclipse.scout.rt.platform.util.Assertions.fail(Assertions.java:580)
	at org.eclipse.scout.rt.platform.internal.BeanManagerImplementor.optBean(BeanManagerImplementor.java:222)
	at org.eclipse.scout.rt.platform.BEANS.opt(BEANS.java:55)
	at org.eclipse.scout.rt.platform.BEANS.get(BEANS.java:41)
	at org.eclipse.scout.rt.client.clientnotification.ClientNotificationPoller.start(ClientNotificationPoller.java:49)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.eclipse.scout.rt.platform.internal.BeanInstanceUtil.initializeBeanInstance(BeanInstanceUtil.java:66)
	... 13 more

[Updated on: Wed, 08 February 2017 07:56]

Report message to a moderator

Re: [neon] How to pass per-request information from client to server? [message #1753611 is a reply to message #1753608] Wed, 08 February 2017 08:13 Go to previous messageGo to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 573
Registered: October 2012
Location: Bern, Switzerland
Senior Member
Update:

I have found ServiceTunnelServlet.createServletRunContext() where the correlationId is extracted. I assume extending that class and then registering my extended class in the web.xml would be sufficient?
Re: [neon] How to pass per-request information from client to server? [message #1753612 is a reply to message #1753608] Wed, 08 February 2017 08:18 Go to previous messageGo to next message
Arthur van Dorp is currently offline Arthur van DorpFriend
Messages: 48
Registered: October 2015
Member
For services you have to use the @Order annotation (lower number wins).

On the server side the ServiceTunnelServlet.createServletRunContext retrieves the correlation id from the http headers if present or creates a new one and adds it to the ServletRunContext.

Edit: Just seen your last update: Yes, that should do it. I still believe that you are copying and extending a lot of code for dubious profit. If you need the correlation id to be something else than the current UUID you could try to overwrite simply the CorrelationId bean with a new implementation of newCorrelationId(). (I have never tried that).

[Updated on: Wed, 08 February 2017 08:24]

Report message to a moderator

Re: [neon] How to pass per-request information from client to server? [message #1753615 is a reply to message #1753612] Wed, 08 February 2017 08:33 Go to previous messageGo to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 573
Registered: October 2012
Location: Bern, Switzerland
Senior Member
Arthur

Thanks, with a low @Order everything works nicely now. The additional code is actually a lot simpler than the corresponding Scout-Mars code:

On client side:
@Replace
public class MyClientHttpServiceTunnel extends ClientHttpServiceTunnel {
  public final static String HEADER_REQUEST_ID = "MyRequestId";

  @Override
  protected void addCustomHeaders(URLConnection urlConn, String method, byte[] callData) throws IOException {
    super.addCustomHeaders(urlConn, method, callData);
    addRequestId(urlConn);
  }

  protected void addRequestId(final URLConnection urlConn) throws IOException {
    String reqid = RequestIdUtil.getNewRequestId();
    urlConn.setRequestProperty(HEADER_REQUEST_ID, reqid);
  }
}


And on server side:
public class CisServiceTunnelServlet extends ServiceTunnelServlet {
  private static final long serialVersionUID = 1L;

  @Override
  protected ServletRunContext createServletRunContext(final HttpServletRequest req, final HttpServletResponse resp) {
    final String reqid = req.getHeader(CisHttpServiceTunnel.HEADER_REQUEST_ID);
    LogContextUtil.putRequestContext(reqid != null ? reqid.toString() : null);
    return super.createServletRunContext(req, resp);
  }
}


And then in the web.xml:
        <!-- <servlet-class>org.eclipse.scout.rt.server.ServiceTunnelServlet</servlet-class> -->
        <servlet-class>ch.sbb.cisi.core.scout.server.CisServiceTunnelServlet</servlet-class>


Overwriting the CorrelationId might work (we need a numeric value instead of a UUID) but would only save me the code on the client side as I still would need to extract the correlation-id and pass it to our LogContextUtil as we are using a spring backend outside the scout code scope.

Thanks for your help!

[Updated on: Wed, 08 February 2017 10:39]

Report message to a moderator

Re: [neon] How to pass per-request information from client to server? [message #1753625 is a reply to message #1753608] Wed, 08 February 2017 09:36 Go to previous messageGo to next message
Jeremie Bresson is currently offline Jeremie BressonFriend
Messages: 1252
Registered: October 2011
Senior Member
The error is clear:
Quote:
Assertion error: multiple instances found for query:
interface org.eclipse.scout.rt.shared.servicetunnel.IServiceTunnel


You can have a look at the Type Hierarchy of IServiceTunnel:

index.php/fa/28375/0/

You have a conlict because the Bean manager can not decide between:
* MyHttpServiceTunnel
* ClientHttpServiceTunnel

Using @Order as described by Arthur is one solution.

My guess is you want to @Replace ClientHttpServiceTunnel instead of HttpServiceTunnel
@Replace
public class MyHttpServiceTunnel extends ClientHttpServiceTunnel {

If you do it like this, the query for IServiceTunnel will provide an unambiguous result (your class).
Re: [neon] How to pass per-request information from client to server? [message #1753632 is a reply to message #1753625] Wed, 08 February 2017 10:35 Go to previous message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 573
Registered: October 2012
Location: Bern, Switzerland
Senior Member
Hi Jeremy

You are right, that was exactly the problem. While @Order helped to only instantiate my class, it actually broke scout because it now replaced ClientHttpServiceTunnel which destroyed the syncrhonisation of the server and client sessions. When I changed my class to extend ClientHttpServiceTunnel that worked again. I guess replacing the @Order annotation with @Replace will be sufficient again.

I've adjusted the code above to represent the correct solution.

Thanks for your help.
Previous Topic:Custom Fields FormData
Next Topic:[oxygen] Search Form Behavior
Goto Forum:
  


Current Time: Sat Apr 20 02:00:17 GMT 2024

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

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

Back to the top