请注意,在文件 appengine-web.xml 中有必需的:
<sessions-enabled>true</sessions-enabled>
我一直在研究如何使 jsf 和会话 bean 在谷歌应用引擎上工作。问题是,如果我在 web.xml 文件中使用,bean 在每个请求中都会丢失:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
这使得在本地开发中会在客户端页面中创建一个隐藏字段,该隐藏字段将视图状态编码为 base64。
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState"
value="H4sIAAAAAAAAAE1QO0sDQRAe73LxLTGClelsLDwQLEQLDWjwMD4QFMFCN3drcmHv9txHcmcRSKOFjYUWFqKFZf6EWNgJWlqJvbWtuyEmDuwwuzPzPbb1DVbEGYxXUQ3ZUvjEXke8sokiq//j6Xny+M0EowBDhCKvgFxBmQODosIwr1DixdHyCugYqQ+onFHHFDDl0sDmMrRPkIu5vZqEKPDdvCt8GnLFNdHjyjOGkqLPRdx8z92+oDsT+hxIcf8Mx5EGrqd0jgWMVo98T8wvKmZJhFQoh8U2DkFh2d4uVbErlq5eD+4zfIYYakWvpyIVAiy9OydPoQFp9Wp0K0v3uzezwWBWY8Yd5cpHREMcCnvP2fdxfZdSMb3DaISZSDZwwqETWcXHYKynZy2Uwf+mEpEmiAvH6/51e84JBS5jlv16ePxpXiwY2r1VQ0RihZfpzW3JoITZeesmN3z9eflnT8mPfwGGpwEXwQEAAA==" autocomplete="off" />
并且据说在谷歌服务器中会是一样的。但结果似乎很奇怪。
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState"
value="-7485706817535542099:-1131777842892951150" autocomplete="off" />
如果我使用:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
谷歌应用引擎产生一个字符串越界错误,不知道为什么。这里是:
/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)
问题是该应用程序在本地开发服务器中完美运行。
我正在使用 GAE-1.7.4。
以下是我迄今为止阅读的一些内容,但没有得到确凿的答案:
- http://consultingblogs.emc.com/jaddy/archive/2009/11/20/jsf2-in-google-app-engine.aspx
- JSF2 与 GAE 和 ViewScoped ManagedBean
- 会话 Bean 丢失?
希望这不是一些愚蠢的情况。提前致谢。
更新1:我能够得出结论,使用:
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Warning building CardController", null));
会话 bean 不是在每个单独的页面请求中创建的,但是由于某种未知的原因,这些值被设置为默认值。我开始认为最好使用 cookie 并将它们重新加载到 bean 中,作为对每个页面请求的修复。我看不出有任何其他的出路。可能我会使用 AES 加密一些 cookie 以提高安全性。
更新2:再次尝试以下:
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;
}
}
在 facelet 的 .xhtml 中
<h:outputText value="#{counter.readCounter}"/>
<br />
<h:outputText value="#{counter.counter}"/>
<br />
<h:outputText value="#{counter.setCounter}"/>
如果没有结果,数据将不会通过会话或 cookie 保存。
更新 3:
“不支持 目前 App Engine 不支持各种 API 和技术,原因有很多。其中包括:
企业 Java Bean (EJB)"
http://code.google.com/p/googleappengine/wiki/WillItPlayInJava
在没有 java bean 的情况下测试了会话变量,它工作正常。
更新 4:
您不能在 GAE 中只将对象写入会话,因为它的属性值会丢失。我怀疑 EJB 会这样做。也许他们可以使用我不知道的 JSON 序列化对象。我确信这是一种解决方法。我在以下位置添加了一个要求此功能的线程:
https://community.jboss.org/thread/221125?tstart=0
不确定这是否是专门为 jboss 开发 EJB 的小组,不过……如果你想要这个特性,你应该在那个线程中制造一些噪音。