0

I have a JSF application that uses several java classes to do dynamic compilation of java code.

Code is written in a text area and compiled by pressing an h:commandButton via Ajax.

Problem occurs when i press more than 2 or 3 times to compile different codes.

Here is the stack trace :

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)

If i set the attribute transient=true to f:view, the problem stops since the state is not saved, but that restricts me from using other functionality like saving my source file in database for user to retrieve in later time.

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();
            }
        }
    }

Compiler

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();
    }
}

Compiler uses 2 more class an Interface with some String constants and a class that extends SimpleJavaFileObject

user3001365
  • 1
  • 1
  • 2
  • Could you post the contents of home.xhtml and any backing beans which it uses? – 8bitjunkie Nov 20 '13 at 12:07
  • 2
    possible duplicate of [javax.faces.application.ViewExpiredException: View could not be restored](http://stackoverflow.com/questions/3642919/javax-faces-application-viewexpiredexception-view-could-not-be-restored) – Aritz Nov 20 '13 at 12:14
  • what do you mean 'compilation of java code' ? – erencan Nov 20 '13 at 14:19
  • Sadly i have seen the post nothing helped me.I added the home.xhtml page and the bean with the compiler class. – user3001365 Nov 21 '13 at 08:14

1 Answers1

0

There are a few approaches you can take:

  • Turn partial-state saving true in web.xml
<context-param>
    <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
    <param-value>true</param-value>
</context-param>
  • Increase the number of logical views in session. Note that JSF caches your views and there is a limit to that cache.
<context-param>
    <param-name>com.sun.faces.NUMBER_OF_LOGICAL_VIEWS_IN_SESSION</param-name>
    <param-value>50</param-value>
</context-param>
  • In cases where the above doesn't solve your problem, write a handler for ViewExpiredException and restore the view programatically. This will refresh your view on the client side(might not be the best user experience, though)
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;
    }
}
Ashesh Nishant
  • 103
  • 1
  • 10