The @ViewScoped
annotation ties the bean to a specific JSF view which in turn relies on the presence of FacesContext#getViewRoot()
and subsequently UIViewRoot#getViewMap()
. None of both are available in a "plain vanilla" servlet. There's no means of a JSF view inside a plain servlet request, let alone a JSF context. So, unfortunately, this behavior is "by design".
You've basically 2 options:
Store the desired shared information in the session scope which is keyed by an unique key which is passed around as HTTP request parameter so that both the JSF managed bean and the servlet can grab it from the session scope.
E.g. in JSF backing bean:
dataId = UUID.randomUUID().toString(); externalContext.getSessionMap().put(dataId, data);
In JSF view:
<h:outputScript>var dataId = "#{bean.dataId}";</h:outputScript>
In JavaScript:
function loadData() { $.get("servletURL", { dataId: dataId }, function(response) { // ... }); }
In servlet:
String dataId = request.getParameter("dataId"); Data data = (Data) session.getAttribute(dataId);
Use a true JSF backing bean instead of a plain vanilla servlet. You can definitely use
<p:remoteCommand>
for that. You can useRequestContext#addCallbackParam()
in action(listener) method to "pass" ("print" is technically more correct) a JSON object from Java to JS and finally useoncomplete
attribute to process it. Given that you're using OmniFaces, the<o:commandScript>
andAjax#data()
offers the same functionality. TheAjax#data()
has the additional advantage that it automatically converts from Java to JSON so that you don't need to do it yourself.E.g. in JSF view:
<o:commandScript name="loadData" action="#{bean.loadData}" oncomplete="processData()" />
In JSF backing bean:
public void loadData() { Ajax.data(data); }
In JavaScript:
function processData() { var data = OmniFaces.Ajax.data; // ... }