JSF 2.2应用程序ViewExpiredException

时间:2013-11-20 10:32:37

标签: jsf

我有一个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的类

1 个答案:

答案 0 :(得分:0)

您可以采取一些方法:

  • 在web.xml中将部分状态保存设为true
<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>true</param-value>
</context-param>
  • 增加会话中的逻辑视图数。请注意,JSF会缓存您的视图,并且该缓存存在限制。
<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;
    }
}