Devs
Devs

Reputation: 286

Java Servlet session shows null value

Here is the code of my servlet.

protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // TODO Auto-generated method stub 

    Map m=request.getParameterMap();
    Set s = m.entrySet();
    Iterator it = s.iterator();
    int index=0;

    while (it.hasNext()) {
        Map.Entry<String,String[]> entry = (Map.Entry<String,String[]>) it.next();
        String key             = entry.getKey();
        String[] value         = entry.getValue();

        System.out.println("Value is " + value[0].toString());
        switch(key) { 
            case "RegId":  
                RegId = value[0].toString();
                break; 
            case "isTrackingRequested":  
                isTrackingRequested = Boolean.valueOf(value[0]);
                break;      
        } 
    } 

    // Create a session object if it is already not  created.
    HttpSession session = request.getSession(true);

    if (session.isNew()) {        
        session.setAttribute("id",isTrackingRequested);
    }
    ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

    ses.scheduleAtFixedRate(new Runnable() {
        @Override  
        public void run() {  
            boolean isTrackingRequestednew = (boolean) session.getAttribute("id");

            // code to run  
            if (isTrackingRequestednew) {
                try { 
                    System.out.println("===========================================================================");
                    System.out.println("new track status is " + isTrackingRequestednew);
                    System.out.println("===========================================================================");
                } catch (Exception e) {    

                }  
            } else { 
                ses.shutdown();
            } 
        }  
    }, 0, 1, TimeUnit.SECONDS);
} 

I am trying to track a vehicle by using a ScheduledExcecutorService. I am using a flag isTrackingRequested to check if user has requested the tracking. So I am saving the values in the session, but whenever I request for the tracking to stop, the previously set session attribute shows null value.

In short, I am trying to access a previously set session variable, but I end up getting null. No solution I have tried seems to work.

Upvotes: 0

Views: 4034

Answers (2)

John Bollinger
John Bollinger

Reputation: 180083

whenever I request for the tracking to stop, the previously set session attribute shows null value.

A reasonably likely explanation is that the requests are not in the same session. Maintaining a session requires cooperation from the client, which is not guaranteed to be given. The most common mechanisms for associating requests with sessions are cookies and URL rewriting. If the client refuses cookies and is making its requests to a static URL then every request will likely be in its own session.

That's one of the lesser of your problems, however. You also have these:

On every POST request, you create a new ScheduledExecutorService and a new task for it to manage. Surely that's not what you intended.

Added: You do not update existing sessions with the tracking state carried by requests belonging to those sessions. Only if the session is newly created for the request being serviced do you set the session attribute.

Moreover, when last I studied the JavaEE specs (a version ago) JavaEE components such as servlets were not permitted to start their own threads, but yours does -- many of them -- within all the ScheduledExecutorServices. That doesn't mean starting a new thread (or creating a ScheduledExecutorService) will necessarily fail, but your violation of the specs does mean that you cannot rely on the JavaEE APIs to behave as documented.

Moreover, your code is not properly synchronized. You access shared state (the Session) without proper synchronization.

Furthermore, you appear to have no mechanism to shut down tracking when a session expires or is manually terminated.

To do this properly, the tracking should be performed in a separate service running outside the servlet container. Alternatively, you could hack it together with only the scheduler itself running outside the container, and all the tracked state living inside. The scheduler would then need only to serve as a clock by sending a periodic request to a different servlet in the same container.

You would do well to decouple your task from the session. Instead of having it get the tracking state from the session, give it a member variable for that, and store a reference to the task itself in the session. Modify the task's state directly in response to requests, instead of passing that information indirectly via the session. And make sure all accesses to that object's shared state are properly synchronized!

Added: Furthermore, I suggest that you make the task implement HttpSessionBindingListener, so that when it is unbound from the session -- either manually or as a result of the session reaching the end of its life -- it can cancel itself.

Added: Additionally, note that modern JavaEE requires the container to make a ScheduledExecutorService available to enterprise components. You should be able to obtain a reference to it via the JNDI name java:comp/DefaultManagedScheduledExecutorService (see section EE.5.21 in the Java EE 7 platform specification). It would be wise to use this container-provided service instead of attempting to set up your own.

Upvotes: 1

11thdimension
11thdimension

Reputation: 10633

There are a few bugs in the code you provided. Value of isTrackingRequested should be checked every time as requester may send a value false to stop tracking. Also null value of isTrackingRequested should be taken into account. If it is null then it probably means user wants to carry on as with the previous decision.

These have been corrected in the code below, this should be working now.

protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

    String RegId = String.valueOf(request.getParameter("RegId"));

    // Create a session object if it is already not created.
    final HttpSession session = request.getSession(true);

    String trackingRequestParam = request.getParameter("isTrackingRequested");
    boolean isTrackingRequested = false;
    if(trackingRequestParam != null) {
         isTrackingRequested = Boolean.valueOf(trackingRequestParam);
        session.setAttribute("id", isTrackingRequested);
    }


    if(trackingRequestParam != null && isTrackingRequested) {
        final ScheduledExecutorService ses = Executors
                .newSingleThreadScheduledExecutor();
        session.setAttribute("isRunning", true);

        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                boolean isTrackingRequestednew = Boolean.valueOf(String.valueOf(session.getAttribute("id") ));

                System.out.println("inside repeater : " + session.getAttribute("id") + " : " + isTrackingRequestednew);
                // code to run
                if (isTrackingRequestednew) {
                    try {
                        System.out.println("===========================================================================");
                        System.out.println("new track status is " + isTrackingRequestednew);
                        System.out.println("===========================================================================");
                    } catch (Exception e) {

                    }
                } else {
                    ses.shutdown();
                }
            }
        }, 0, 1, TimeUnit.SECONDS);
    }
}

Edits *****

I'm adding entire TestServlet code that I used here. I have converted POST method to GET method for ease of testing.

TestServlet

package test;

import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        Boolean isLogout = Boolean.valueOf(String.valueOf(request.getParameter("logout")));
        String RegId = String.valueOf(request.getParameter("RegId"));

        // Create a session object if it is already not created.
        final HttpSession session = request.getSession(true);

        String trackingRequestParam = request.getParameter("isTrackingRequested");
        boolean isTrackingRequested = false;
        if(trackingRequestParam != null) {
             isTrackingRequested = Boolean.valueOf(trackingRequestParam);
            session.setAttribute("id", isTrackingRequested);
        }

        if(isLogout) {
            session.invalidate();
        }


        if(trackingRequestParam != null && isTrackingRequested) {
            final ScheduledExecutorService ses = Executors
                    .newSingleThreadScheduledExecutor();
            session.setAttribute("isRunning", true);

            ses.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        boolean isTrackingRequestednew = Boolean.valueOf(String.valueOf(session.getAttribute("id") ));

                        System.out.println("inside repeater : " + session.getAttribute("id") + " : " + isTrackingRequestednew);
                        // code to run
                        if (isTrackingRequestednew) {
                            try {
                                System.out.println("===========================================================================");
                                System.out.println("new track status is " + isTrackingRequestednew);
                                System.out.println("===========================================================================");
                            } catch (Exception e) {

                            }
                        } else {
                            ses.shutdown();
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        ses.shutdown();
                    }
                }
            }, 0, 1, TimeUnit.SECONDS);
        }
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
    }
}

Entire TestServlet

Testing

Hit below URLs (TestWeb was context name in my case, replace it with yours)

http://localhost:8080/TestWeb/TestServlet?isTrackingRequested=true<br>
//then after a few seconds<br>
http://localhost:8080/TestWeb/TestServlet?isTrackingRequested=false

Output (Test was done on Tomcat 7.0.59)

inside repeater : true : true =========================================================================== new track status is true =========================================================================== inside repeater : true : true =========================================================================== new track status is true =========================================================================== inside repeater : true : true =========================================================================== new track status is true =========================================================================== inside repeater : true : true =========================================================================== new track status is true =========================================================================== inside repeater : true : true =========================================================================== new track status is true =========================================================================== inside repeater : true : true =========================================================================== new track status is true =========================================================================== inside repeater : false : false

After tracking was set to false, it stops printing as executor was shutdown.

Note: Clear your cookies to start a new session.

Edit2 *** Added code to support manual logout call URL below to logout.

http://localhost:8080/TestWeb/TestServlet?logout=true

Upvotes: 0

Related Questions