resthttpjava-ee-8

How to share value between two different classes in Java EE environment


I have Java EE REST API with Rabitt MQ.

The system will receive the message, create a unique ID, and publish it for processing.

I want to use the ContainerRequestFilter to intercept the HTTP request for logging purposes. In which I will publish to another queue for logging.

@Provider
@PreMatching
@LogHttp
public class LogInterceptor implements ContainerRequestFilter {

private static ThreadLocal<String> userContext=new ThreadLocal<>();
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
    // create UID
    // gather logging information and publish to queue

} }

I need to share the UID created to be used when data is received so that I can link between the message process and logging information.

I appreciate your help.


Solution

  • To share a value, such as a unique ID (UID), between two different classes in a Java EE environment, you can use a combination of ThreadLocal storage and a shared service or bean.

    +------------------+          +---------------------+        +------------------+
    | ContainerRequest |          | Shared Service/Bean |        | Other Components |
    |      Filter      |   UID    |  (e.g., UIDService) | Access | (e.g., Message   |
    |                  | -------->|                     |<-------|   Processing)    |
    |  (LogInterceptor)|          |                     |        |                  |
    +------------------+          +---------------------+        +------------------+
    

    Since you are using a ContainerRequestFilter to intercept HTTP requests and generate a UID, you can store this UID in a ThreadLocal variable and then access it from other parts of your application where required.

    You are already using ThreadLocal in LogInterceptor. That is a good approach to store the UID per request.

    Create a service or bean (e.g., UIDService) to manage UIDs. That service can provide methods to set and get the current thread's UID.
    Other components of your application can access the UIDService to get the current UID.

    For instance:

    LogInterceptor class:

    @Provider
    @PreMatching
    @LogHttp
    public class LogInterceptor implements ContainerRequestFilter {
        private static ThreadLocal<String> userContext = new ThreadLocal<>();
    
        @Inject
        private UIDService uidService;
    
        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            // create UID
            String uid = // generate UID
            userContext.set(uid);
    
            // Set UID to UIDService
            uidService.setCurrentUID(uid);
    
            // gather logging information and publish to queue
        }
    }
    

    UIDService class:

    @ApplicationScoped
    public class UIDService {
        private static ThreadLocal<String> currentUID = new ThreadLocal<>();
    
        public void setCurrentUID(String uid) {
            currentUID.set(uid);
        }
    
        public String getCurrentUID() {
            return currentUID.get();
        }
    }
    

    Any other component:

    @Inject
    private UIDService uidService;
    
    public void processMessage() {
        String uid = uidService.getCurrentUID();
        // use uid for linking with logs
    }
    

    That way, the UID created during the HTTP request filtering is accessible throughout the processing of that request in a thread-safe manner.
    Do clear the ThreadLocal after the request is processed to prevent memory leaks.

    +-------------------+          +---------------------+        +------------------+
    | ContainerRequest  |          |   UIDService (Bean) |        | Other Components |
    |      Filter       |   UID    | - Set/Get UID       | Access | (e.g., Message   |
    |  (LogInterceptor) |--------->| - ThreadLocal       |<-------|   Processing)    |
    | - Set UID in      |          |  Storage            |        | - Use UID for    |
    |   ThreadLocal     |          +---------------------+        |   Linking Logs   |
    +-------------------+                                         +------------------+
    | Recommendations:                                            | Recommendations: |
    | - Make sure ThreadLocal is cleared                          | - Inject UID     |
    |   after request processing to prevent                       |   Service for    |
    |   memory leaks.                                             |   UID access.    |
    +-------------------------------------------------------------+------------------+
    

    I tried the code, and it works, but I have concerns

    1. The UIDService is a singleton; I have to test it with multiple endpoints being invoked.
    2. I was trying to use ThreadLocal set by the httpInterceptor class and Use in the resource class, It did not work, is there a way to make it work without UIDService class.
    1. Singleton UIDService and concurrent access:

      Being a singleton, UIDService is shared across all requests, but the use of ThreadLocal makes sure the UID is unique and isolated per thread (i.e., per request in a typical Java EE server).
      That means that each request will have its own UID, and there will not be any cross-talk between requests regarding UIDs. It is still a good idea to test with multiple endpoints and concurrent requests to make sure everything works as expected.

    2. Using ThreadLocal directly in Resource class:

      It is possible to use ThreadLocal directly in your resource class without a separate UIDService.

      • The ThreadLocal variable must be accessible by both the LogInterceptor and your resource class. That can be achieved by defining it in a common class or interface.
      • The lifecycle of the request thread must be understood clearly. The ThreadLocal value set by the LogInterceptor should be available to the resource class later in the same request processing thread.

    So, create a common class to hold the ThreadLocal variable:

    public class RequestContext {
        public static final ThreadLocal<String> currentUID = new ThreadLocal<>();
    }
    

    And set the UID in the LogInterceptor:

    @Provider
    @PreMatching
    @LogHttp
    public class LogInterceptor implements ContainerRequestFilter {
        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            // create UID
            String uid = // generate UID
            RequestContext.currentUID.set(uid);
    
            // gather logging information and publish to queue
        }
    }
    

    Access the UID in your resource class:

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @LogHttp
    public Response createEntity(Pojo pojo) throws Exception {
        String uid = RequestContext.currentUID.get();
        System.out.println(uid);
        // Further processing
    }
    

    Make sure the ThreadLocal variable is cleared at the end of the request processing to avoid memory leaks. That is typically done in a filter or interceptor that runs at the end of the request lifecycle.
    Note that in asynchronous processing scenarios, the thread that starts processing a request may not be the same thread that finishes it. That can lead to issues with ThreadLocal usage. If your application uses asynchronous processing, you might need additional mechanisms to pass the UID across threads.