Injecting ThreadLocal Objects
Jakob Jenkov |
It is possible to inject ThreadLocal
objects from within
Butterfly Container. This text describes how, and a few common use cases
for injecting ThreadLocal
objects.
ThreadLocal Revisited
First let's refresh what a ThreadLocal
object is, and what
it looks like. A ThreadLocal
looks like this:
ThreadLocal threadLocal = new ThreadLocal();
When a thread wants to associate an object to itself, it can set that object into the
ThreadLocal
like this:
threadLocal.set(theObject);
The object can be obtained again like this:
Object theObject = threadLocal.get();
Different threads may set different objects on the same ThreadLocal
instance,
but they will not get mixed up. A thread calling get()
will only get the object
it had set itself by calling set()
.
Here is an example of a ThreadLocal
object referenced from witin a container script:
//the ThreadLocal inside a class: public class MyClass{ public static final ThreadLocal threadLocal = new ThreadLocal(); }
/* the script referencing the ThreadLocal myThreadLocal = * com.myapp.MyClass.threadLocal.get();
Now, whenever the factory myThreadLocal
is referenced it will return whatever value is
associated to the calling thread, inside the ThreadLocal
inside MyClass
.
For instance, if a thread does this:
MyClass.threadLocal.set("A ThreadLocal String"); String string = (String) container.instance("myThreadLocal");
Then the value obtained from the container.instance("myThreadLocal")
call
will be the value the calling thread associated with itself, by calling
MyClass.threadLocal.set()
. In this case it will be the value
"A ThreadLocal String"
.
You can also reference the myThreadLocal
factory within the container script,
like this:
myThreadLocal = * com.myapp.MyClass.threadLocal.get(); bean = * com.myapp.MyBean(myThreadLocal);
Injecting ThreadLocal Locale's
ThreadLocal
's can be used to associate a java.util.Locale
with the calling
thread. That is useful if you need to localize the dependencies for a component at instantiation time.
You can read more about localization in the text
Using BCS for Internationalization
Basically what you will do is call a ThreadLocal.set()
method with the Locale
to associate with the calling thread, before calling container.instance()
.
Then, by referencing that ThreadLocal
from inside a container script you can inject
that Locale
into objects or use it as parameter to method calls.
Here is how it looks:
public class MyClass{ public static final ThreadLocal threadLocale = new ThreadLocal(); public static Locale getLocale() { return (Locale) threadLocale.get(); } }
threadLocale = * com.myapp.MyClass.getLocale(); mybean = * com.myapp.MyBean(threadLocale);
Locale locale = ... //obtain Locale for user. MyClass.threadLocal.set(locale); MyBean localizedBean = (MyBean) container.instance("mybean");
Notice how the Locale
is not obtained by the ThreadLocal.get()
method, but via the static method getLocale()
. The reason for this is,
that the ThreadLocal.get()
returns Object
. If you need to
call Locale
methods on the Locale
you cannot do so unless
Butterfly Container can determine that the returned object is a Locale
.
The getLocale()
method functions as a cast from Object
to its return value Locale
, enabling the container to determine the
type of the returned object (Locale
).
Injecting ThreadLocal Request, Session etc. in Web Apps
It is also possible to associate an HTTP request object with the executing thread, thus making it possible for the container to inject the request object into instantiated beans. It is also possible to inject objects obtained from the request, like request parameters, the session object or the servlet context. Butterfly Web UI uses this method to make these objects available for injection.
Here is how that looks:
public class ControlServlet{ public static final ThreadLocal threadRequest = new ThreadLocal(); public static final ThreadLocal threadResponse = new ThreadLocal(); public static HttpServletRequest getRequest() { return (HttpServletRequest) threadRequest.get(); } public static HttpServletResponse getResponse() { return (HttpServletResponse) threadResponse.get(); } }
request = * com.jenkov.webui.ControlServlet.getRequest(); response = * com.jenkov.webui.ControlServlet.getResponse(); session = * request.getSession(); servletContext = session.getServletContext(); mybean = com.myapp.MyBean(session.getAttribute("someAttribute"));
Notice how the HttpServletRequest
and HttpServletResponse
are not obtained by the ThreadLocal.get()
method, but via the static methods getRequest()
and getResponse()
.
The reason for this is,
that the ThreadLocal.get()
returns Object
. If you need to
call HttpServletRequest
methods on the request
you cannot do so unless
Butterfly Container can determine that the returned object is a HttpServletRequest
.
The getRequest()
method functions as a cast from Object
to its return value HttpServletRequest
, enabling the container to determine the
type of the returned object (HttpServletRequest
). The same is true
for the method getResponse()
.
Tweet | |
Jakob Jenkov |