Skip to content

Instantly share code, notes, and snippets.

@PiotrNowicki
Created November 23, 2011 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PiotrNowicki/1389318 to your computer and use it in GitHub Desktop.
Save PiotrNowicki/1389318 to your computer and use it in GitHub Desktop.
Example of context data usage in EJB
package com.piotrnowicki;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptors;
import javax.interceptor.InvocationContext;
@Stateless
public class MyService {
/**
* EJB session context - allows to get to the context data.
*/
@Resource
SessionContext ctx;
/**
* Method invoked by the client.
*
* Explicit interceptor; it could be also configured in ejb-jar.xml
* or it could be annotated at the class level (which means that
* every class method will be intercepted.)
*
* @param id
* just an exemplary parameter
*/
@Interceptors(MyInterceptor.class)
public void myMethod(String id) {
String ctxParamKey = ContextData.MY_PARAM.getKey();
// Simple as that - access context's data (map) value.
Object ctxParam = ctx.getContextData().get(ctxParamKey);
print("[myMethod] Method parameter: " + id);
print("[myMethod] Context parameter: " + ctxParam);
}
/**
* Just a minor helper method to print message on the console.
*
* @param msg
* message to be printed.
*/
private void print(String msg) {
System.out.println(msg);
}
/**
* Simple enumeration to be less error-prone when using String based
* identifiers as map key.
*
*/
public static enum ContextData {
MY_PARAM("myParamKey");
private String key;
private ContextData(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}
/**
* Interceptor which will set our context data before calling the
* real EJB method.
*/
public static class MyInterceptor {
/**
* Interceptor's SessionContext is the same as EJB's.
*/
@Resource
SessionContext ejbCtx;
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
String ctxParamKey = ContextData.MY_PARAM.getKey();
// Just put some data into context map.
ctx.getContextData().put(ctxParamKey, "Context hello!");
// You can use EJB's context - i.e. check user's principal
// ejbCtx.getCallerPrincipal();
return ctx.proceed();
}
}
}
@mr-rycho
Copy link

Hello, Piotr :) I've already tested that this context does not propagate when calling another bean from myMethod. Also it does not propagate when using ContextService.createContextualProxy. So the bean itself and other interceptors are the only places where the value can be read? In your opinion is this by design? Do you know another means to "pass" values or objects or something like "context" without passing it as method's parameter? Thread locals are one option (set and reset by interceptors). And I didn't read about context propagation in cdi yet. jndi is shared among all threads (is it?). Any other? btw my other goal is to share some "context" information or pass it between threads or rather tasks that form a graph of CompletableFutures :)

@PiotrNowicki
Copy link
Author

Hi mr-rycho.

It's been a while since I wrote this and worked in Java EE, but:

  • Regarding context-propagation - do you have the interceptor configured for the another bean you're invoking?
  • Today perhaps you could create a CDI bean for the request scope (or custom scope if you're not working in web application) and pass it to the beans (from the top of my head - not verified in any way),
  • Since writing this document few years passed by. Now, seeing prod-issues or debugging complications I'd say that the more obvious data you have and can inspect it the better for maintainability, so I'd say: the less you hide the better :-)

@mr-rycho
Copy link

I'm trying to develop a kind of monitoring tool for asynchronous jobs where jobs can identify themselves belonging to some chain (so the progress of the chain can be logged and/or monitored). I could have used a simpler solution i.e. just pass some context object as param but I found that this problem is an opportunity to dig more into these mysterious ejb context objects and cdi scopes ;) . I wanted this thing to work a bit like @transactional annotation i.e. first encountered annotation "starts" some "monitoring context" and nested annotations follow but they not only are nested but can also be async.
Also: afair I've already read about passing request scoped beans to asyncs and afair this was ... discouraged? forbidden? because these beans would be "destroyed" right after the "source" request ends.
Also: I've kinda found what I was looking for in weld, here: https://docs.jboss.org/weld/reference/latest/en-US/html/contexts.html , take a look at "23.2.2. Example of context propagation". On one hand it looks really bad but on the other it can simply be closed in some utility class.
Also: the CDI community is already discussing it e.g. here : https://issues.jboss.org/browse/CDI-452?_sscc=t or here : https://issues.jboss.org/browse/CDI-587?_sscc=t . Btw this feature is already present in ejb spec and it is the ContextService which came as part of the Concurrency Utilities suite in java ee 7 but it's documented rather poorly i.e. not saying what else is propagated besides security context... This guy ( https://youtu.be/JeE_L9VJ4E8?t=723 ) claims that also the JNDI namespace is propagated but I couldn't make it work and found jndi to be rather global than contextuable but maybe I just don't know how to use jndi :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment