我有一个JSF应用程序,它使用几个java类来动态编译java代码。
代码写在文本区域中,并通过Ajax按h:commandButton
进行编译。
当我按下2到3次以编译不同的代码时会出现问题。
这是堆栈跟踪:
javax.faces.application.ViewExpiredException: viewId:/home.xhtml - View /home.xhtml could not be restored.
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:210)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:121)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Unknown Source)
如果我将属性transient=true
设置为f:view
,则问题会因为状态未保存而停止,但这限制了我使用其他功能,例如将我的源文件保存在数据库中以供用户检索以后的时间。
home.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Home Page</title>
</h:head>
<h:body>
<f:view transient="true">
<h:form prependId="false">
<h:panelGrid columns="1">
<h:inputTextarea id="codeArea" rows="25" cols="70" value="#{user.userInputCode}" />
<h:outputText id="messages" value="#{user.compilationMessages}"/>
</h:panelGrid>
<h:commandButton value="Compile">
<f:ajax execute="codeArea" render="messages" listener="#{user.compileCode()}"/>
</h:commandButton>
</h:form>
</f:view>
</h:body>
</html>
的UserBean
@Named(value = "user")
@SessionScoped
public class UserBean implements Serializable {
private String userInputCode;
private String compilationMessages;
private CompilerBean compiler;
public UserBean() {
compiler = new CompilerBean();
userInputCode = compiler.getDefaultCodeModel();
}
public String getUserInputCode() {
return userInputCode;
}
public void setUserInputCode(String userInputCode) {
this.userInputCode = userInputCode;
}
public String getCompilationMessages() {
return compilationMessages;
}
public void compileCode() throws Exception {
if (!compiler.isValidClass(userInputCode)) {
compilationMessages = "Please provide a correct class format";
} else {
if (compiler.compile(userInputCode)) {
compilationMessages = "Compilation Success!";
} else {
compilationMessages = compiler.getDiagnosticMessages();
}
}
}
编译器
public class CompilerBean implements CompilationInterface {
private JavaCompiler compiler;
private DiagnosticCollector diagCollector;
private StandardJavaFileManager fileManager;
private String sourceFile;
public CompilerBean() {
sourceFile = DEFAULT_SOURCEFILE;
}
public boolean compile(String inputCode) throws Exception {
compiler = ToolProvider.getSystemJavaCompiler();
diagCollector = new DiagnosticCollector();
fileManager = compiler.getStandardFileManager(diagCollector, null, null);
File outputFile = new File(CLASS_FILES_PATH);
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(outputFile));
String className = extractClassName(inputCode);
sourceFile = className + JAVA_POSTFIX;
JavaFileObject sourceObject = new CompilerJavaObject(sourceFile, inputCode);
Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(sourceObject);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagCollector, null, null, fileObjects);
deleteCompiledFiles();
return task.call();
}
public String getDiagnosticMessages() {
String message = "";
List<Diagnostic> diagErrors = diagCollector.getDiagnostics();
for (Diagnostic d : diagErrors) {
message = ("Error: " + d.getLineNumber() + " Cause: " + d.getMessage(null));
}
return message;
}
private void deleteCompiledFiles() {
File f = new File(CLASS_FILES_PATH);
for (File classFile : f.listFiles()) {
classFile.delete();
}
}
public String getDefaultCodeModel() {
return DEFAULT_CLASS_MODEL;
}
public String getSourceFile() {
return sourceFile;
}
/*
* Extracts the class name from the input code
*/
private String extractClassName(String input) {
String className = input.replaceAll(COMMENTS_REGEX, "");
className = className.replaceAll(IMPORTS_REGEX, "");
className = className.replaceAll(CLASS_BODY, "");
className = className.replaceAll(CLASS_REGEX, "").trim();
return className;
}
/*
* Checks if the input code is in a valid class format
*/
public boolean isValidClass(String input) {
Pattern pat1 = Pattern.compile(COMMENTS_REGEX);
Pattern pat2 = Pattern.compile(IMPORTS_REGEX);
Pattern pat3 = Pattern.compile(CLASS_REGEX);
Matcher m1 = pat1.matcher(input);
Matcher m2 = pat2.matcher(input);
Matcher m3 = pat3.matcher(input);
return m3.lookingAt() || m1.lookingAt() || m2.lookingAt();
}
}
编译器使用另外2个类,一个带有一些String常量的接口和一个扩展SimpleJavaFileObject的类
答案 0 :(得分:0)
您可以采取一些方法:
<context-param> <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name> <param-value>true</param-value> </context-param>
<context-param> <param-name>com.sun.faces.NUMBER_OF_LOGICAL_VIEWS_IN_SESSION</param-name> <param-value>50</param-value> </context-param>
ViewExpiredException
编写处理程序并以编程方式还原视图。这将刷新您在客户端的视图(可能不是最好的用户体验)import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.faces.FacesException; import javax.faces.application.ViewHandler; import javax.faces.application.ViewHandlerWrapper; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; public class MyViewExpiredHandler extends ViewHandlerWrapper { private ViewHandler wrapped; private static Map<String, Boolean> viewsToProcess = new HashMap<String, Boolean>(); //assuming these xhtmls throw ViewExpiredException static { viewsToProcess.put("/view/xxxx.xhtml", true); viewsToProcess.put("/view/aaa.xhtml", true); viewsToProcess.put("/view/yyy.xhtml", true); } public MyViewExpiredHandler(ViewHandler parent) { this.wrapped = parent; } @Override public ViewHandler getWrapped() { return wrapped; } @Override public UIViewRoot restoreView(FacesContext context, String viewId) { UIViewRoot viewRoot = super.restoreView(context, viewId); if(viewsToProcess.containsKey(viewId) && viewRoot == null) { viewRoot = super.createView(context, viewId); super.initView(context); try { super.renderView(context, viewRoot); } catch (FacesException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return viewRoot; } }