Note that in the file appengine-web.xml there is the required:
<sessions-enabled>true</sessions-enabled>
I have been digging for quite a long time on how to make jsf and session beans work on google app engine. The problem is that the bean gets lost at every request if I use in the web.xml file:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
This makes on the local development a hidden field be created in the page of the client which encode the view state in base64.
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState"
value="H4sIAAAAAAAAAE1QO0sDQRAe73LxLTGClelsLDwQLEQLDWjwMD4QFMFCN3drcmHv9txHcmcRSKOFjYUWFqKFZf6EWNgJWlqJvbWtuyEmDuwwuzPzPbb1DVbEGYxXUQ3ZUvjEXke8sokiq//j6Xny+M0EowBDhCKvgFxBmQODosIwr1DixdHyCugYqQ+onFHHFDDl0sDmMrRPkIu5vZqEKPDdvCt8GnLFNdHjyjOGkqLPRdx8z92+oDsT+hxIcf8Mx5EGrqd0jgWMVo98T8wvKmZJhFQoh8U2DkFh2d4uVbErlq5eD+4zfIYYakWvpyIVAiy9OydPoQFp9Wp0K0v3uzezwWBWY8Yd5cpHREMcCnvP2fdxfZdSMb3DaISZSDZwwqETWcXHYKynZy2Uwf+mEpEmiAvH6/51e84JBS5jlv16ePxpXiwY2r1VQ0RihZfpzW3JoITZeesmN3z9eflnT8mPfwGGpwEXwQEAAA==" autocomplete="off" />
And supposedly in the google server would be the same. But the result seems wierd.
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState"
value="-7485706817535542099:-1131777842892951150" autocomplete="off" />
and if I use:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
The google app engine yields a string out of bound error not sure why. Here it is:
/Trainer.jsf
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.String.substring(String.java:1949)
at com.sun.faces.renderkit.ServerSideStateHelper.getState(ServerSideStateHelper.java:277)
at com.sun.faces.renderkit.ResponseStateManagerImpl.getState(ResponseStateManagerImpl.java:100)
at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.restoreView(FaceletPartialStateManagementStrategy.java:352)
at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:138)
at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:518)
at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:142)
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:192)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:266)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:146)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:447)
at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:454)
at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:461)
at com.google.tracing.TraceContext.runInContext(TraceContext.java:703)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:338)
at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:330)
at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:458)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
at java.lang.Thread.run(Thread.java:679)
The thing is that the app works perfectly in the local development server.
I'm using GAE-1.7.4.
Here are some of the stuff I have read so far and did not get a conclusive answer:
- http://consultingblogs.emc.com/jaddy/archive/2009/11/20/jsf2-in-google-app-engine.aspx
- JSF2 with GAE and ViewScoped ManagedBean
- Session Bean being lost?
Hope it is not some silly situation. Thanks in advance.
UPDATE 1: I was able to concluded, using:
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Warning building CardController", null));
That the session bean was not being created at every single page request but for some unknown reason, the values are being setted to their defaults. I am starting to think that is better to use cookies and reload them in to the bean, as a fix at every single page request. I don't see any other way out of this. May be I will use AES to encrypt some of the cookies for extra security.
UPDATE 2: Again tried the following:
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import com.google.gson.Gson;
@SuppressWarnings("serial")
public class Counter implements Serializable{
public int counter;
public Counter() {
counter = -1;
}
public String getCounter(){
counter++;
return counter+"";
}
public String getSetCounter(){
Gson gson = new Gson();
HttpServletResponse res = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
Cookie cookie = new Cookie(this.hashCode()+"",gson.toJson(this));
cookie.setMaxAge(60*60); //1 hour
res.addCookie(cookie);
return "Write Counter";
}
public String getReadCounter(){
Gson gson = new Gson();
Cookie cookieJSON = getCookie();
if(cookieJSON!=null){
Counter counter = gson.fromJson(cookieJSON.getValue(), Counter.class);
this.counter = counter.counter;
}
return "Read Counter";
}
private Cookie getCookie(){
HttpServletRequest req = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
Cookie[] cookies = req.getCookies();
Cookie cookieJSON = null;
if(cookies!=null){
List<Cookie> cookiesList = Arrays.asList();
cookieJSON = (Cookie) CollectionUtils.find(cookiesList, new Predicate() {
@Override
public boolean evaluate(Object cookie) {
return ((Cookie)cookie).getName().equals(Counter.this.hashCode()+"");
}
});
}
return cookieJSON;
}
}
And in the .xhtml of the facelet
<h:outputText value="#{counter.readCounter}"/>
<br />
<h:outputText value="#{counter.counter}"/>
<br />
<h:outputText value="#{counter.setCounter}"/>
With no result the data just won't hold via session or cookies.
UPDATE 3:
"Not supported There are various APIs and technologies that are not supported by App Engine at present for various reasons. These include:
Enterprise Java Beans (EJB)"
http://code.google.com/p/googleappengine/wiki/WillItPlayInJava
Tested session variables with out java beans and it works fine.
UPDATE 4:
You can't in GAE just write a object to session because its attributes values will get lost. I suspect that EJB does this. Maybe they could just serialize the object using JSON I don't know. I am sure that is a work around. I added a thread asking for this feature in:
https://community.jboss.org/thread/221125?tstart=0
Don't sure if this is the group responsible for developing the EJB just for jboss, though... Any way if you want this feature may be you should make some noise in that thread.