ThreadLocal in DispatcherServlet - java

I have a Spring MVC (v4.1.3) web application with javascript UI. I have implemented a custom DispatcherServlet and configured the same in web.xml
There is a unique screen code which is sent in the HTTP Header of each request made by the UI to server.
In the doService method of my custom dispatcher servlet, I capture the HTTP Header and put the value in a ThreadLocal dto variable. I access this ThreadLocal variable in the service layer for performing some audit logic which is common for all requests.
Code from CustomDispatcherServlet:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
String uiCode = request.getHeader("uiCode");
if ((uiCode != null && !uiCode.trim().isEmpty())) {
UiCodeDto uiCodeDto = new UiCodeDto(uiCode);
final ThreadLocal<UiCodeDto> threadLocalUser = new ThreadLocal<UiCodeDto>();
threadLocalUser.set(uiCodeDto);
}
...
super.doService(request, response);
}
Code from service layer:
UiCodeDto temp = ThreadLocalUtil.getUiCodeDto(Thread.currentThread());
Code of ThreadLocalUtil to retrieve the value from ThreadLocal:
public final class ThreadLocalUtil {
public static UiCodeDto getUiCodeDto(Thread currThread) {
UiCodeDto UiCodeDto = null;
try {
Field threadLocals = Thread.class.getDeclaredField("threadLocals");
threadLocals.setAccessible(true);
Object currentThread = threadLocals.get(currThread);
Field threadLocalsMap = currentThread.getClass().getDeclaredField("table");
threadLocalsMap.setAccessible(true);
threadLocalsMap.setAccessible(true);
Object[] objectKeys = (Object[]) threadLocalsMap.get(currentThread);
for (Object objectKey : objectKeys) {
if (objectKey != null) {
Field objectMap = objectKey.getClass().getDeclaredField("value");
objectMap.setAccessible(true);
Object object = objectMap.get(objectKey);
if (object instanceof UiCodeDto) {
UiCodeDto = (UiCodeDto) object;
break;
}
}
}
} catch (Exception e) {
...
}
return UiCodeDto;
}
}
The problem is as follows -
1. I am getting random values of screen code - which means the value of some http request N is coming in http request N+1.
2. There are null DTOs in ThreadLocal variable with same name - hence, sometimes when I access the ThreadLocal in service layer, I get a null
I need help in understanding the behavior of ThreadLocal in DispatcherServlet - why would it get values of another request in doService method?
Thanks in advance.

Your code is error prone and hard to understand also why would you need a custom DispatcherServlet. A filter seems more suited for this task.
public class UiCodeFilter extends OncePerRequestFilter {
protected void doFilterInternally(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
try {
String uiCode = req.getHeader("uiCode");
if ((uiCode != null && !uiCode.trim().isEmpty())) {
UiCodeDto uiCodeDto = new UiCodeDto(uiCode);
UiCodeHolder.set(uiCodeDta);
}
chain.doFilter(req, res);
} finally {
UiCodeHolder.clear(); // Always clear!
}
}
}
The UiCodeHolder has a static ThreadLocal to keep the value.
public abstract class UiCodeHolder {
static ThreadLocal<UiCodeDto> current = new ThreadLocal<>()
public void set(UiCodeDto uiCode) {
current.set(uiCode);
}
public UiCodeDta get() {
return current.get();
}
public void clear() {
current.remove(); // for older versions use current.set(null);
}
}
In your service you can now simply do UiContextHolder.get() to obtain the correct value. The UiCodeFilter takes care of setting the value and at the end of the request clears the value again to prevent leaking.
This approach doesn't require ugly reflection hooks, is quite easy to understand is is used by Spring, Hibernate and frameworks alike.

A more Spring way of doing this is to use a request-scoped bean to extract and hold the header:
#Component
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UiCodeDto {
private String uiCode;
#Inject
public void setCode(HttpServletRequest req) {
uiCode = req.getHeader("uiCode");
}
public String getUiCode() {
return uiCode;
}
}
And you can use it like a normal bean:
#Service
public class RandomService {
#Inject
UiCodeDto uiCodeDto;
public void handle() {
System.out.println(uiCodeDto.getUiCode());
}
}

Related

how to initialize a boolean variable for every api call? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
i have an api
public interface UserService{
#GetMapping("/get-user-data")
public Response getUserData();
}
i have my impl as below
public class UserServiceImpl implements UserService{
private boolean isAuthorized = false;
public Response getUserData(User user){
//check if user is authorized
isAuthorized = isUserAuthorized()?true:false;
//perform other things based on above boolean
return someResponse;
}
}
i want this boolean "isAuthorized" must be set to false for every call to this api. whatever i have above isn't working, how to achieve this?
Instead of keeping track of the state of the boolean variable, usually we perform some action based on the positive condition:
public Response getUserData(User user){
if (isUserAuthorized()) {
// actions that should happen only when authorized
} else {
// actions that should happen only when NOT authorized
}
}
But if you really want to keep track of that state, you can always set the variable to false at the end of the method call:
//check if user is authorized
isAuthorized = isUserAuthorized()?true:false;
//perform other things based on above boolean
isAuthorized = false;
return someResponse;
Note: that may cause issues related to concurrency, but if that doesn't concern you, no harm.
if you want to call a business logic method for all api methods, you need to use a web HandlerInterceptor, by example:
#Component
public class AppInterceptor implements HandlerInterceptor {
private static Logger logger = LoggerFactory.getLogger(AppInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
var authorized = UserIsAuthorizedDummy(); // <- you business logic here
request.setAttribute("is_authorized", authorized);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {}
private boolean UserIsAuthorizedDummy() {
return true;
}
}
And configure this interceptor with a WebMvcConfigurer implementation, by example:
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Autowired
AppInterceptor appInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appInterceptor);
}
}
Then you can use it in you controller methods, and you will see and access the is_authorized atrribute from request object. example:
#GetMapping("/user")
public LocalResponse getUser(HttpServletRequest request) {
var response = new LocalResponse();
// check AppInterceptor.preHandle() to see when "is_authorized" attribute is set
response.setAuthorized((boolean) request.getAttribute("is_authorized"));
if (response.isAuthorized()) {
response.setUserId("1111");
}
return response;
}
#GetMapping("/profile")
public LocalResponse getProfile(HttpServletRequest request) {
var response = new LocalResponse();
// check AppInterceptor.preHandle() to see when "is_authorized" attribute is set
response.setAuthorized((boolean) request.getAttribute("is_authorized"));
if (response.isAuthorized()) {
response.setUserId("1111-22222");
}
return response;
}
You can see a working example in the following repository: https://github.com/eriknyk/springboot-web-interceptor-demo/

Spring custom request context

One of the scopes that Spring provides is request, beans in it are only valid in the context of a request.
That request is normally delegated to a controller after an HTTP request with Spring already setting up everything necessary.
But what if the request comes from a different source, like for example a Java Message Service.
Would it possible to set up a request scope for each processing of a message?
Can I annotate a method with something to mark it as the boundary of a request scope?
Under the cover , Spring just calls RequestContextHolder to set RequestAttributes to ThreadLocal at the beginning when processing the HTTP request , and remove it from ThreadLocal just before that thread finishes processing the request. The request and session scope beans are actually stored in and get from this RequestAttributes.
In the normal web situation ,this RequestAttributes implementation is backed with the HttpServletRequest. However , in the non-web context , there is no HttpServletRequest and so you cannot use the existing implementation. One of the way is to implement a RequestAttributes that is backed by an internal map :
public class InMemoryRequestAttributes extends AbstractRequestAttributes {
protected Map<String, Object> attributes = new HashMap<>();
#Override
public Object getAttribute(String name, int scope) {
return attributes.get(name);
}
#Override
public void setAttribute(String name, Object value, int scope) {
attributes.put(name, value);
}
#Override
public void removeAttribute(String name, int scope) {
attributes.remove(name);
}
#Override
public String[] getAttributeNames(int scope) {
String[] result = new String[attributes.keySet().size()];
attributes.keySet().toArray(result);
return result;
}
#Override
public void registerDestructionCallback(String name, Runnable callback, int scope) {
synchronized (this.requestDestructionCallbacks) {
this.requestDestructionCallbacks.put(name, callback);
}
}
#Override
public Object resolveReference(String key) {
return attributes;
}
#Override
public String getSessionId() {
return null;
}
#Override
public Object getSessionMutex() {
return null;
}
#Override
protected void updateAccessedSessionAttributes() {
}
}
Note: It only work with request scope bean. Modify it if you want to support session scope bean too ....
Then set it and remove it from the ThreadLocal just before and finish processing the JMS messages, something likes :
public void receive(String message){
RequestContextHolder.setRequestAttributes(new InMemoryRequestAttributes());
fooBean.processMessage(message);
RequestContextHolder.resetRequestAttributes();
}

Spring simultaneous calls

I think my problems is architecture-based, but not so sure so I'll try to explain the reality of it.
The software I'm working on is monolithic oriented but fully exposed with SOAP. The architecture is as followed :
The fondamental concept is what we called "Transaction", which is defined by its request and response (used for SOAP). It structures configurable business logic as well (editing documents, workflows, etc)
We have 1 bean configured in application-context.xml for each Transaction (PersonViewTransaction, PersonSelectTransaction, PersonEditTransaction for example)
All of them inherit
from the same abstract class AbstractTransaction which implements controls on the request provided
that AbstractTransaction implements an interface ITransaction
We build a TransactionFactory which, on any call with beanId and request, calls static methods calling every ITransaction's methods for the Transaction identified by beanId.
When a call is made through Webservices, a Servlet will call this factory, and execute the same logic, as a call from the web project (maven) would do calling directly the factory.
In other words :
Definition of ITransaction
/**
* The main interface defining what a transaction is.
*/
public interface ITransaction <Q extends GenericRequest, P extends GenericResponse> {
P businessLogic(Q req);
void executeControls(String transactionId, Q params, PerimetreAppel environnement)
default void controleLicence() throws NassurCoreException
{
//TODO : contrôles de la licence à effectuer.
}
/**
* Log before transaction
*/
default void logEntree() throws NassurCoreException
{
//TODO to be implemented
}
/**
* Log after transaction
*/
default void logSortie() throws NassurCoreException
{
//TODO to be implemented
}
/**
* Checks that the user can call transaction
*/
default void controleDroits() throws NassurCoreException
{
//TODO to be implemented
}
//...
//Some other method not necessarily implemented in each transaction
}
/**
* The abstract defining the default behaviour of a transaction
*/
public abstract class AbstractTransaction<Q extends GenericRequest, P extends GenericResponse> implements ITransaction<Q, P>{
#Autowired
private ControlService controlService;
//.... some other autowired fields
#Autowired
protected UserDetailsService userDetailsService;
protected String idTransaction;
//Every transaction must implement its own logic
P businessLogic(Q params);
#Override
public void initialisation(String trId)
{
this.idTransaction = trId;
}
#Override
#Transactional
public void executeControls(String transactionId, Q params, PerimetreAppel environnement)
{
//Some controls found from database, and executed
// A control consists of a combination between java checks and some Jruby executed from the Service
List<SomeEntity> list = someEntityRepository.findByTransactionId(transactionId);
for(SomeEntity e : list){
controlService.executeUnitControl(e, params);
}
}
}
Transactions
//Example Transaction
public class PersonViewTransaction extends AbstractTransaction<PersonViewRequest, PersonViewResponse>{
#Override
#Transactional
public PersonViewResponse businessLogic(PersonViewRequest req){
//...
}
}
//Example Transaction
public class PersonEditTransaction extends AbstractTransaction<PersonEditRequest, PersonEditResponse> {
#Override
#Transactional
public PersonEditResponse businessLogic(PersonEditRequest req){
//...
}
}
//Example Transaction
public class PersonSearchTransaction extends AbstractTransaction<PersonSearchRequest, PersonSearchResponse> {
#Override
#Transactional
public PersonSearchResponse businessLogic(PersonSearchRequest req){
//...
}
}
Those Beans are declared in application-context.xml.
Factory
public class TransactionFactory
{
public static GenericResponse executeTransaction(String id, GenericRequest req, HttpServletRequest request)
throws SomeExceptions
{
ServletContext servletCtx = request.getSession().getServletContext();
ApplicationContext actualCtx = WebApplicationContextUtils.getWebApplicationContext(servletCtx);
ITransaction tr = (ITransaction<GenericRequest, GenericResponse>) actualCtx.getBean(id.name());
if (tr == null)
{
throw new Exception("internal Error", null);
}
return executeStack(id, tr, req, request);
}
private static GenericResponse executeStack(String id, ITransaction tr, GenericRequest req, HttpServletRequest request)
throws SomeExceptions
{
try
{
tr.initialisation(id);
tr.controleLicence();
tr.logEntree();
tr.controleDroits();
//...Several operations calling ITransaction
tr.executeControls(id, req, provenance);
//Calling transaction logic
GenericResponse resp = tr.businessLogic(req);
//...Many "after-business" other calls
return resp;
}
catch (SomeExceptions e)
{
logger.error(e);
throw e;
}
}
}
By some mystery, when two calls are made simultaneously on the same transactionId, the executeControls throws some weird returns, as if both executions where mixed in the same bean (to me it appears thread-safe but not that sure...) :
List<SomeEntity> list = someEntityRepository.findByTransactionId(transactionId);
for(SomeEntity e : list){
controlService.executeUnitControl(e, params);
}
executeUnitControl checks if the control must apply, and throws an error if needed. To be more into detail, this is based on a JRuby script that takes params as parameter to build configurable and non-Java based business logic.
Thanks a lots for your help, and if there are french people, I'd love to chat with them any time to explain more details if needed !

why does interceptor not get fired on an EJB method called from servlet filter?

I want to implement high level resource filtering on URLs with a servlet filter and lower level action filtering on methods with an interceptor but my interceptor does not get fired on the EJB method called from the servlet filter.
Interceptor annotation Interface:
#Inherited
#InterceptorBinding
#Retention (RUNTIME)
#Target({TYPE, METHOD})
public #interface Permit {
#Nonbinding
String[] actions() default "N/A";
}
The Interceptor:
#Permit
#Interceptor
public class PermitInterceptor {
#AroundInvoke
public Object verifyPermission(InvocationContext context) throws Exception {
Method method = context.getMethod();
if(method.isAnnotationPresent(Permit.class)) {
Permit permitAnnotation = method.getAnnotation(Permit.class);
List<String> permittedActions = Arrays.asList(permitAnnotation.actions());
List<String> userActions = SecurityContextHandler.getActiveUser().getActions();
if(!Collections.disjoint(permittedActions, userActions)){
return context.proceed();
}
}
throw new PermissionException("You do NOT have the required permissions to perform this action");
}
}
Servlet Filter:
#WebFilter(urlPatterns = {"/*"})
public class AccessFilter implements Filter {
#EJB
private RulesBean rules;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURI();
if(rules.isAllowed(url)){
chain.doFilter(request, response);
}else{
//handle as necessary
}
}catch(Exception ex){
//handle as necessary
}
}
}
And finally here's what the EJB RulesBean that I want to use to manage routing/interception for all my servlets looks like;
Rules:
#Stateless
#LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return canViewAllCustomers();
default: return true;
}
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
#Permit(actions={"ViewCustomers"})
private boolean canViewAllCustomers(){
return true;
}
...
//Other tests carried out here ...
}
Unfortunately PermitInterceptor doesn't get called before entering canViewAllCustomers() method.
Amazingly however, PermitInterceptor gets triggered when canViewAllCustomers() is made public and called directly as rules.canViewAllCustomers() instead of through the helper method rules.isAllowed(String url). But this isn't helpful in my case, as it gives me no single entry point for my URL checks which essentially means I have to do all the checks in the Servlet Filter.
QUESTION: Please can anybody shed more light on the reason why things are occurring in this manner?... and suggestions about the best way to implement this scenario is highly welcome. Thanks.
NOTE: (To give more perspective)
You may be wondering why I want to do this OR more specifically why the RuleBean even exists at all... The reason is simply because a good number of my Servlets aren't doing much except route response to a view that triggers a server-side DataTables ajax call which populates the tables, hence I really need to ensure that the request for the view doesn't even get through to the if...else condition that fetches the view unless the permission checks in the interceptor is satisfied.
See sample servlet below;
#WebServlet ("/customers/*")
public class CustomerServlet extends VelocityViewServlet {
private static final String CUSTOMERS = "/customers"
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
if(uri.equals(CUSTOMERS)){
String view = Path.getTemplate("/customers/index.vm");
request.setAttribute("headerTitle", "Customers");
request.getRequestDispatcher(view).forward(request, response);
}else if(...){
...
}
}
}
You invoke canViewAllCustomers() within isAllowed() directly, which gives the Application Server no chance to intercept the call.
Interception works with proxy classes. When you inject the EJB into your servlet, like you did with:
#EJB
private RulesBean rules;
what actually gets injected is not an EJB instance, but a proxy class, that the application server created at runtime (you can see this with the debugger). Invocations on that proxy class will be intercepted for transactions, custom interceptors, etc. and then delegated to the actual class instance.
So what you need to do is either put canViewAllCustomers() into a new EJB, that you can let the application server inject into your RulesBean class,
or you can retrieve a reference of your RulesBean class from inside isAllowed() like so:
#Stateless
#LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
#Resource
SessionContext ctx;
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return self().canViewAllCustomers();
default: return true;
}
}
private RulesBean self() {
return ctx.getBusinessObject(RulesBean.class);
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
#Permit(actions={"ViewCustomers"})
public boolean canViewAllCustomers(){
return true;
}
}

Should utilities in Spring context be static or injected?

I'm writing some utlility methods that helps constructing complex object structures for webservice requests.
When using Spring: is it better to provide these classes as static utilities or inject/autowire them?
Example:
//the service used to build different parts for the webservice request
class RequestService {
public addCurrency(Req req) {
//create currency xml object
req.addCurrency(cur);
}
public addPassengers(Req req, List<String> names) {
for (String name : names) {
req.getPassengers().add(passenger);
//create passengers
}
}
}
class WebserviceClient {
void runWithStatic() {
Req req = new Req();
RequestService.addCurrency(req, ..);
RequestService.addPassengers(req, ..);
send(req);
}
//or
#Autowired
private RequestService reqService;
void runWithInjected() {
Req req = new Req();
reqService.addCurrency(req, ..);
reqService.addPassengers(req, ..);
send(req);
}
}
Is there one approach one would prefer from the overal spring design point of view?

Categories

Resources