2

I am trying to pass an Object from Servlet's doPost() to JSF's Managed bean's action method. But I am unable to do that.

I have tried to set the value from Servlet as:

request.getSession().setAttribute(key, "JYM");

And tried to retrieve it form Managed bean as:

FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key)

It is returning null.

Also this is also returning null from Managed bean:

((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().getAttribute(key);

Also from Managed bean this is returning null:

((HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(false)).getAttribute(key)

I am passing the key as:

'${pageContext.request.contextPath}/uploadservlet;jsessionid=${pageContext.session.id}?key=<h:outputText value="#{uploadBean.key}" />'

uploadBean is the name of the Managed bean and the key is generated as:

key = UUID.randomUUID().toString();

The key remains unchanged in both of the Servlet and in managed bean. I have printed is to check.

How can I pass the Object from Servlet to Action? Any pointer would be very helpful.

Update

The Managed bean is in session scope.

Update

By using ServletContext I am able to pass the value:

Here is what I did: In Servlet:

String key = request.getParameter("key");

if (getServletContext().getAttribute(key) == null) {
    List<FileItem> fileFields = new ArrayList<FileItem>();
    fileFields.add(fileField);
    getServletContext().setAttribute(key, fileFields);
} else {
    List<FileItem> fileFields = (List<FileItem>)getServletContext().getAttribute(key);
    fileFields.add(fileField);
}

And from session scoped bean:

ServletContext servletContext = ((ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext());
List<FileItem> fileFields = (List<FileItem>)servletContext.getAttribute(key);
servletContext.setAttribute(key, null);

Now the fileFields is not null anymore. What I understand is the ServletContext behave like Application Scoped variable.

Update

HttpSessionListener's implementation:

This is the class I have written:

public class UploadListener implements HttpSessionListener {
    private HttpSession session = null;

    public void sessionCreated(HttpSessionEvent event) {
        session  = event.getSession();
        session.setMaxInactiveInterval(10);
    }

    public void sessionDestroyed(HttpSessionEvent event) {
        session  = event.getSession();
        Set<String> keys = (Set<String>) session.getAttribute("key");
        Map<String, Object> data = (Map<String, Object>) session.getServletContext().getAttribute("key");
        data.keySet().removeAll(keys);
    }
}

I am setting the value in the ServletContext as:

String key = request.getParameter("key");

List<FileItem> fileFields = (List<FileItem>)getServletContext().getAttribute(key);

if (fileFields == null) {
    fileFields = new ArrayList<FileItem>();
    getServletContext().setAttribute(key, fileFields);
}

fileFields.add(fileField);

And this is the way I am calling the Servlet: '${pageContext.request.contextPath}/uploadservlet?key=<h:outputText value="#{uploadBean.key}" />'.

Tapas Bose
  • 28,796
  • 74
  • 215
  • 331
  • Have a look at [this post](http://stackoverflow.com/questions/11578940/jsf-1-2-not-able-to-retreive-bean-values-in-xhtml-page/11579028#comment15320495_11579028). You need to put the bean in the scope, exactly there where JSF expects it – Vikas V Jan 11 '13 at 10:23
  • @VikasV did you mean from servlet I need to do `request.getSession().setAttribute("key", "value");`? – Tapas Bose Jan 11 '13 at 10:27
  • Ya I meant to say so. Your bean is in session scope. So the code you have typed above should be fine. – Vikas V Jan 11 '13 at 10:30
  • @VikasV I have mentioned in the question, in the first code snippet, that I did the exactly same thing in the servlet. – Tapas Bose Jan 11 '13 at 10:34
  • You are setting the _key_ and not _managed bean_. If you re look at the post I have mentioned, you could see that **managed bean** is put in the required scope – Vikas V Jan 11 '13 at 10:36
  • The symptoms suggests that the bean and the servlet don't run in the same session. Debug `HttpSession#getId()`. – BalusC Jan 11 '13 at 12:15
  • @BalusC I have printed the HttpSession object and they are not same. They differs in the servlet and bean. When I logged the HttpSession object I got weblogic.servlet.internal.session.MemorySessionData. – Tapas Bose Jan 11 '13 at 12:24
  • @BalusC this problem is regarding the issue that I have mentioned in this [thread](http://stackoverflow.com/questions/14260583/integrating-flash-file-upload-with-jsf/14260875) – Tapas Bose Jan 11 '13 at 12:25
  • Apparently Weblogic is configured to not accept `jsessionid` URL path fragment and it will just create a new session for the servlet. I don't do Weblogic, so I have no idea how to configure that. At least, the default configurations of Tomcat/Glassfish/JBoss doesn't do that (they are however also configureable to not accept `jsessionid` URL fragment). All I can suggest is to store it in application scope instead. This is a little more risky, but the randomness of `UUID` is strong enough to not cause clashes. You should only perform manual cleanup when session expires. – BalusC Jan 11 '13 at 12:27
  • @BalusC I have created a Singleton class and defined Map within that, and in that Map I storing the key and the value from Servlet. The key is generated by UUID in the managed bean and passed as request parameter as you have instructed me before. After taking the value out from the Map of the Singleton class, in the very next line I am removing the key from the Map. is this approach good or safe? I will post it in the aforesaid thread, I need to do more test. – Tapas Bose Jan 11 '13 at 12:57

1 Answers1

2

This construct will fail if the servletcontainer doesn't support identifying the HTTP session by jsessionid URL fragment. This is by default supported, but it's possible to turn off this by servletcontainer specific configuration. So far it unfortunately looks like that your Weblogic server is configured as such.

Your best bet is then to exchange the data in the application scope. The randomness of UUID is strong enough to not cause clashes. You should only need to make sure that the session-associated data is cleaned up when the session is destroyed. Otherwise the memory will leak away. For this, you can use a HttpSessionListener. Provided that you store the key in both the application scope (referencing the shared data) and in the session scope (referencing a set of all keys used so far), then the sessionDestroyed() implementation can look like this:

public void sessionDestroyed(HttpSessionEvent event) {
    Set<String> keys = (Set<String>) event.getSession().getAttribute("keys");
    Map<String, Object> data = (Map<String, Object>) event.getSession().getServletContext().getAttribute("data");
    data.keySet().removeAll(keys);
}

Update as per your update, a bit more elegant way to get/set them is:

String key = request.getParameter("key");
List<FileItem> fileFields = (List<FileItem>) getServletContext().getAttribute(key);

if (fileFields == null) {
    fileFields = new ArrayList<FileItem>();
    getServletContext().setAttribute(key, fileFields);
}

fileFields.add(fileField);

and

List<FileItem> fileFields = (List<FileItem>) FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().remove(key);
// ...
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I want to express cordial thanks to you. I don't understand one point, do you want to me to have a managed bean in Application Scope? Or you are saying to store the key in ApplicationMap? Could you please explain how can I get the Application Scope in Servlet? I have got success by the use of ServletContext for the value passing. I have updated my question to show you what I did. Just to know and for my knowledge, is that the approach of use of Singleton class is faulty or risky? – Tapas Bose Jan 11 '13 at 18:11
  • Just store the key in application scope. It's in JSF indeed represented by `ExternalContext#getApplicationMap()`. It can even be just a property of an application scoped managed bean. The application scope is represented by attributes of `ServletContext`. JSF also stores application scoped managed beans in there, keyed by the managed bean name. See also e.g. http://stackoverflow.com/questions/2633112/jsf-get-managed-bean-by-name/2633733#2633733 This approach is not faulty, but risky for the case the key clashes. I'd however take a second look to get Weblogic to accept `jsessionid` URL fragment – BalusC Jan 11 '13 at 18:13
  • Just for confirmation, I can use `ServletContext` for this case as I have shown in the second update of my question? – Tapas Bose Jan 11 '13 at 18:16
  • In JSF side, you mean? It's doable, but just unnecessary. The `ExternalContext#getApplicationMap()` delegates to `ServletContext` attributes already. You know, JSF is an abstraction over the "raw" Servlet API. See also [javadoc](http://docs.oracle.com/javaee/5/api/javax/faces/context/ExternalContext.html#getApplicationMap()). – BalusC Jan 11 '13 at 18:18
  • Thank you, it's working. So I don't need the `HttpSessionListener` anymore, as I am explicitly removing the key, and so no chance of memory leak? – Tapas Bose Jan 11 '13 at 19:02
  • You're welcome. There is still a memory leak risk if the enduser never invokes the bean action method and let the view/session expire (thus, when the enduser aborted "halfway" the process). You end up with unused references in the application scope. – BalusC Jan 11 '13 at 19:15
  • I have posted the solution (here)[http://stackoverflow.com/questions/14260583/integrating-flash-file-upload-with-jsf/14286079#14286079]. Please have look and if let me know. – Tapas Bose Jan 11 '13 at 20:27
  • I have posted the solution [here](http://stackoverflow.com/questions/14260583/integrating-flash[file-upload-with-jsf/14286079#14286079). Please have look and let me know. – Tapas Bose Jan 11 '13 at 20:28
  • It looks okay. It does the job you want, right? :) I'd however still bring in the proposed session listener to eliminate the memory leak. – BalusC Jan 11 '13 at 20:47
  • Thank you again. Yes, it does the functionality I want. I have a plan to implement it with ADF Faces tomorrow. I have few questions about the `HttpSessionListener` that you have mentioned. Did you mean `keys` by `key`(`key` is what I have stored in `SessionContext`)? As the code I have posted in that thread, didn't store anything in the `HttpSession` so the line `event.getSession().getAttribute("keys");` would return null? I didn't have implemented the `HttpSessionListener` yet. Also what is `data`. I didn't understand this one. Could you please tell me? – Tapas Bose Jan 12 '13 at 16:13
  • A single user can potentially open the same page multiple times in the same session. I'd expect that each page (view) has its own key (which you retain by `` or ``). Hence the `Set keys` instead of `key`. The `data` is just a mapping of all those file uploads keyed by those generated keys. It just allows easy access and manipulation. – BalusC Jan 12 '13 at 16:16
  • I mean by `key`(or `keys`) is `Set keys = (Set) session.getAttribute("key");`. I have tried to implement the `HttpServletListenet`, Is the data is the key? I am getting NPE in the line `Map data = (Map) session.getServletContext().getAttribute("key");`. :-(. I have updated my code. I really didn't understand the functionality you want me to implement. I really appreciate your help. – Tapas Bose Jan 12 '13 at 17:22