1

I have spent some time reading BalusC's answer on this question but I can't seem to apply that knowledge to my situation. I am also getting the ViewExpiredException but only under certain circumstances.

My page is backed by a @ViewScoped bean and uses two p:layoutUnits to display a p:tree (west) with the contents of the folder in the center pane. Selecting a folder in the tree triggers an AJAX event to update the center pane with the contents of the selected folder. The skeleton of the JSF is as follows:

            <p:layout id="workLayout">

                <p:layoutUnit id="paneTree"
                              position="west"
                              size="200">

                    <h:form id="treeForm">
                        <p:tree value="#{workspace.listTree}"
                                var="node"
                                dynamic="true"
                                selectionMode="single"
                                >
                            <p:ajax event="select"
                                    listener="#{workspace.onNodeSelect}"
                                    update="panelData"
                                    />
                            <p:treeNode>
                                <h:outputText value="#{node.name}" />
                            </p:treeNode>
                        </p:tree>
                    </h:form>

                </p:layoutUnit>

                <p:layoutUnit id="paneData"
                              position="center"
                              resizable="true">

                    <h:panelGroup id="panelData" >
                        <h:form id="formData" rendered="#{workspace.selected ne null}">
                            <h2>#{workspace.selected.name}: #{workspace.selected.title}</h2>
                            <p>

                                <div id="divImages"
                                     style="width: 49%; height: 99%; float: right">

                                    <p:panel rendered="#{empty workspace.listImages}">
                                        <p>No Images In This List.</p>
                                    </p:panel>

                                    <ui:repeat value="#{workspace.listImages}"
                                               var="img"
                                               >
                                        <p>
                                            #{img.name}: #{img.title}
                                        </p>
                                        <img src="#{workspace.getUrl(img)}"
                                            style="max-width: 250px"
                                             />
                                        <hr/>
                                    </ui:repeat>

                                </div>
                                <div id="divDocs"
                                     style="width: 49%; height: 99%; float: left">


                                    <p:dataTable id="tableFiles"
                                                 value="#{workspace.listDocuments}"
                                        >
                                        <p:column  ...
                                        <p:column  ...
                                        <p:column  ...

                                    </p:dataTable>

                                </div>

                            </p>
                        </h:form>
                    </h:panelGroup>

                </p:layoutUnit>

The trouble starts when you select a folder that has a few images in it. As little as 10 or fewer images is enough to create the problem. This causes the ui:repeat element to render the number of img tags, each with a URL that loads an image.

The URL triggers a Servlet in the application that the same CDI domain so it can inject the same resources as the managed beans do. The Servlet responds with just a response header with the MIME type and the data. However each img access creates a new @SessionScoped bean -- the same one this JSF page uses. It does not get the same instance as the JSF page and the various img accesses do not share a session bean between them either. Each access generates a new one.

The Servlet looks like this:

@WebServlet(name = "Obj", urlPatterns = {"/obj/*"})
public class Obj extends HttpServlet {

    @Inject ProtoObjectControl poc;
    @Inject UserSession us;

The UserSession is the @SessionScoped object that contains login information (which is not logged in since it always generates a new one) and various utilities like the log functions. The ProtoObjectControl is a DAO object to fetch the image to display.

The end result is that the page that did the display ends up crashing with a ViewExpiredException. I think this is because the explosion of sessions is consuming resources that are getting exhuasted. If I take out the img tag and run the page with that the ViewExpiredException never comes up and the page works as reliably as Mac Finder or Windows File Explorer.

So is there any way to explain why this happens and to engineer around it? I want to not have a limit on the number of images that can come up in the display. Ideally I would like to have the Servlet access the same UserSession bean as the JSF page -- it is after all being accessed from the same client -- but perhaps that is not possible. Is there any other approach?

Community
  • 1
  • 1
AlanObject
  • 9,613
  • 19
  • 86
  • 142
  • Are you saying that each image request creates a new HTTP session? Did you confirm that based on HTTP request and response headers on those image requests? – BalusC Jul 06 '15 at 05:26
  • @BalusC The way I confirm that is in my session bean I have a PostConstruct method that provides a printout. When I display the page with all the images on it I get a string of those outputs. You're up early aren't you? Isn't it 6AM where you are? – AlanObject Jul 06 '15 at 05:35
  • Better confirm it based on raw HTTP data (particularly the `Cookie` and `Set-Cookie` headers). This way we can exclude one and other. It's 7:38AM right now. I usually wake up between 5:30 and 6:30. – BalusC Jul 06 '15 at 05:38
  • You mean print out the Cookie data from the Servlet? That doesn't sound too hard I'll give it a try. – AlanObject Jul 06 '15 at 05:45
  • No, just press F12 in your webbrowser to open the webdeveloper's toolset and look in the "Network"/"Net" tab panel. All HTTP requests are logged there. – BalusC Jul 06 '15 at 05:48
  • OK. I use Firefox so I opened Firebug. Here is what I think I am seeing. When the AJAX request causes the images to load, each GET request results in the servlet setting a new cookie with the parameter JSESSIONID assigned to a new value. I am not sure if this is a problem or just a symptom of it. I am running out of energy tonight so I'll have to investigate this more tomorrow. – AlanObject Jul 06 '15 at 06:49
  • This is a symptom. The servlet couldn't associate the request with the intented session and thus creates a new one. Did the ajax request contain a `Cookie` header? – BalusC Jul 06 '15 at 06:50
  • So the answer is yes, according to Firebug. The XHR request is a POST action of course and cookies sent with it by the browser are the same as what the original page was requested with. However the GET requests that are intended to pull the images get cookies with a different JSESSIONID cookie for each one. Where are they coming from? – AlanObject Jul 06 '15 at 18:12
  • 1
    So those image requests didn't contain a `Cookie` header and got a new `Set-Cookie` on the response? Perhaps the image URL simply isn't on same domain/path as the session cookie? – BalusC Jul 06 '15 at 18:13
  • When the main page is loaded with a GET, the browser sends the cookies JSESSIONID and User. (The User cookie has my auto-login info). The initial get request sets the csfcfc Cookie. On all the other GETs to load css and js and stuff the same two cookies are sent. Then the POST sends again those same two cookies. Then all the GETs following send a new JSESSIONID each. I never see another cookie coming from the server. However your last comment I think is on to something I am going to update the main question. – AlanObject Jul 06 '15 at 18:26
  • 1
    You thus say that all those GET requests on the individual images have each a different `Cookie` header on the request which was never specified beforehand via `Set-Cookie` on a previous response? I can't imagine how that's possible, so there must be a misobservation or misunderstanding somewhere. – BalusC Jul 06 '15 at 18:27

1 Answers1

0

Based on BalusC's input above I think this problem has been solved. The error was in my code as follows:

public String getBaseUrl() {
    if (baseURL != null) return baseURL;
    FacesContext fc = FacesContext.getCurrentInstance();
    if (fc == null) return "";
    ExternalContext ec = fc.getExternalContext();
    baseURL = String.format("http://%s:%d/%s/",
            ec.getRequestServerName(),
            ec.getRequestServerPort(),
            ec.getRequestContextPath());
    return baseURL;
}

Using this method generates URLs of the form:

http://localhost:8080//MyApp/obj?id=nnn

The problem of course is the double slash before the application context path. Correcting this results in no new sessions being created because the container correctly correlates the GET requests to the servlet as part of the same session.

Although the program works now there are two unresolved questions:

  1. In all the extraneous sessions, each got a JSESSIONID cookie with a different value. But with Firebug I could not find any corresponding set-cookie coming from the server. So where were those values coming from?

  2. It would seem that JSF applications of this nature -- where you use a Servlet to serve objects that fill in a page -- require that the Servlet execute in the same session context as the main application. The unexplained (to me at least) question is why did the Faclets page crash just because the URL in the src tag was malformed and extraneous sessions were being generated? I wouldn't expect it to if the URL was addressing a different server (a image storing service for example). I would also expect Glassfish to handle thousands of sessions with no problem.

Hopefully this article will save somebody from this particular pitfall someday.

AlanObject
  • 9,613
  • 19
  • 86
  • 142