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