fauxjsp is a Java Server Pages (JSP) implementation for use during development: JSPs are interpreted lazily (instead of compiled) which reduces application and page startup times, speeds up page reloads when JSPs change and doesn't require server restarts. Best used when developing, can be used in production also if specification compliance isn't critical.
web.xml:
<servlet>
<servlet-name>FauxJsp</servlet-name>
<servlet-class>fauxjsp.servlet.JspServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FauxJsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>Versions below (and including) 1.2.3 are in maven central:
pom.xml:
<dependency>
<groupId>com.github.ggeorgovassilis</groupId>
<artifactId>fauxjsp</artifactId>
<version>1.2.3</version>
</dependency>Versions after 1.2.3 require a repository:
<repositories>
<repository>
<id>fauxjsp-repo</id>
<url>https://github.com/ggeorgovassilis/fauxjsp/raw/gh-pages/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>1.2.7-SNAPSHOT (not released on maven central)
1.2.3
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
trimDirectiveWhiteSpaces, c:set body content implemented0.0.9
0.0.5-SNAPSHOT
0.0.4-SNAPSHOT
Yes. In short:
For most of you cool dudes JSP is horribly out of fashion, but I like using it for prototyping and tagfiles. Sadly and surprisingly, not many people know about tagfiles despite them being a well-supported and mature technique for creating reusable web UI components.
Starting a JSP-heavy application is slow because other JSP implementations will first compile JSP and tagfiles to java source code, then to byte code and then to machine code. Also, when making changes to JSP files, the entire process has to be repeated which slows down development. At some point you'll even get unexplainable compile errors, class loading errors, run out of memory and the likes, you'll know you've had enough and you'll restart the servlet container.
fauxjsp implements a JSP interpreter and a JSP servlet which reads JSP files and interprets them on the fly, skipping compilation altogether. This brings the benefit of instant page reloads, fast application start times and robustness (no classloader fiddling!) but, obviously, the generated JSP pages are slower under load than the standard implementation which may be an issue in production.
Currently implemented features:
Constraints and missing features:
c:out will work but
third party taglibs such as displaytag won't, unless you re-implement them for fauxjsp.classpath: prefix for resolving tagfiles and JSPs in the classpath. Does not support the standard way of resolving classpath resources.Because:
Bonus:
<dependency>
<groupId>com.github.ggeorgovassilis</groupId>
<artifactId>fauxjsp</artifactId>
<version>1.1.0</version>
</dependency>OR
download sources and compile
git clone https://github.com/ggeorgovassilis/fauxjsp.git
cd fauxjsp
mvn install<servlet>
<servlet-name>FauxJsp</servlet-name>
<servlet-class>fauxjsp.servlet.JspServlet</servlet-class>
<!-- optionally specify a root for resource lookup, defaults to "/"
<init-param>
<param-name>base-path</param-name>
<param-value>/WEB-INF/jsp/</param-value>
</init-param>
-->
</servlet>
<servlet-mapping>
<servlet-name>FauxJsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>Servlet API, JSTL, EL are scoped as provided because the servlet container or the web application contribute these dependencies
log4j, Beanshell are scoped as provided because they are optional
commons-lang is scoped as compile because it's mandatory
For up-to-date details, please see the pom.
Please submit a bug report when you find a bug or require a feature implemented. Now, in real (project) life, you are probably on a tight schedule and don't want to wait for an official fix. fauxjsp is modular and easily extensible. So have a look at the next chapter about fauxjsp's architecture which will help you understand the various, rather simple components, how to modify them and implement new functionality.
JspServlet accepts an HTTP request and renders the requested JSP. The entire sequence looks like this:
ServletRequest
jspbase (see javadocs for JspServlet) for the JSP fileJspParserFactory for a new JspParser
JspParser who parses it, recursively resolves dependencies and returns a JspPage
RenderSession
JspRendererFactory for a new JspRenderer
JspPage and RenderSession to the JspRenderer who renders itJspServlet has a bunch of protected methods which construct the various factories mentioned earlier. Should you need special setups
then overriding these constructor methods allows you to specify your own factories which can return modified or completely new implementations
of parsers and renderers.
The JspParser and more specifically its only implementation JspParserImpl is given a JSP file location, renders it and returns
the parsed results. In detail:
JspParser.parse(path) is given the path of the JSP file to renderResourceResolver instance (the JspParserFactory sets that one up) which returns
the JSP file as a string.JspNodes.TagLibDefinitionCache which makes sure that
taglibs are read only once during each request.JspParserFactory for a new parser who recursively parses the tagfile.Unfortunately, currently it is not possible to load taglibs other than tagfiles. However it is possible to use missing taglibs by providing a special implementation for fauxjsp yourself. Depending on the taglib you are trying to simulate this may or may not be
a labour-intensive task. For some examples, have a look at the JstlCoreTaglib* classes. The DefaultJspParserFactoryImpl factory sets those
up under a special namespace, one for each taglib method.
<%@ taglib prefix uri tagdir%>
<@ include file %>
<%@ attribute name required rtexprvalue type %>
<jsp:attribute name="...">...</jsp:attribute>
<jsp:body>...</jsp:body>
<jsp:doBody/><c:out value="..."/>
<c:choose test="...">...</c:choose>
<c:when test="...">...</c:when>
<c:otherwise>...</c:otherwise>
<c:forEach var="..." items="..." varStatus="..." begin="..." end="...">...</c:forEach>
<c:if test="...">...</c:if>
<c:set var="..." value="..."/>
<fmt:message key="..."/>
<fmt:setBundle basename="..."/>
<fmt:formatNumber value="..." .../>
<fmt:formatDate value="..." .../>
<fmt:setLocale value="..."/>
fauxjsp delegates to the standard JSTL function implementation used by the application server, so everything should work out of the box.
If you need more function taglibs, don't forget to declare them in web.xml:
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/jstl/core-rt</taglib-uri>
<taglib-location>META-INF/c-1_0-rt.tld</taglib-location>
</taglib>
</jsp-config>taglib-location can be a server- or classpath resource path.
As written before, fauxjsp can't use taglibs and has to emulate them instead, which means that someone has to program that emulation.
Step 1: create the taglib implementation. Just find one of the already simulated taglibs like JstlCoreTaglibOut and copy & paste it
public class TaglibAdd extends TaglibDefinition{
protected void runAdd(RenderSession session, JspTaglibInvocation invocation) {
String xExpression = invocation.getArguments().get("x");
if (xExpression == null)
throw new RuntimeException("Missing x argument");
Object x = session.elEvaluation.evaluate(xExpression, session);
String yExpression = invocation.getArguments().get("y");
if (yExpression == null)
throw new RuntimeException("Missing y argument");
Object y = session.elEvaluation.evaluate(yExpression, session);
try {
int ix = (Number)x;
int iy = (Number)y;
String s = ""+(ix+iy);
session.response.getOutputStream().write((s).getBytes(session.response.getCharacterEncoding()));
} catch (Exception e) {
throw new JspRenderException(invocation, e);
}
}
@Override
public void render(RenderSession session, JspTaglibInvocation invocation) {
runAdd(session, invocation);
}
public TaglibAdd() {
this.name="add";
this.attributes.put("x", new AttributeDefinition("x", Integer.class.getName(), true, true));
this.attributes.put("y", new AttributeDefinition("y", Integer.class.getName(), true, true));
}
}
Step 2: extend DefaultJspParserFactoryImpl and override the setup method. Here you can register the new taglib you wrote
public class MyJspParserFactory extends DefaultJspParserFactoryImpl{
protected void setup(JspParserImpl parser) {
super.setup(parser);
parser.registerTaglibDefinition(
"http://mytaglibs/add",
new TaglibAdd());
}
}Step 3: extend JspServlet and override getJspParserFactory so that it returns your new factory
public class MyJspServlet extends JspServlet{
protected JspParserFactory getJspParserFactory(ServletConfig config){
ResourceResolver location = new ServletResourceResolver(jspBase, getServletContext());
DefaultJspParserFactoryImpl factory = new MyJspParserFactory(location);
return factory;
}
}Step 4: use your new JspServlet implementation in web.xml instead of the old JspServlet
<servlet>
<servlet-name>FauxJsp</servlet-name>
<servlet-class>MyJspServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FauxJsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>JspServlet logs through log4j, if found on the classpath, otherwise through the standard JDK logging mechanism.
A possible logging setup in log4j.properties could look like this:
log4j.rootLogger=INFO, stdout
log4j.logger.fauxjsp=INFO, stdout
log4j.additivity.fauxjsp.impl.parser.JspParserImpl=false
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
Scriptlets are implemented with the use of beanshell, thus some deviations from the standard scriptlet behavior are to be expected. Scriptlets won't be enabled unless the Beanshell interpreter is found on the classpath. Current limitations are:
Ideally you'd find a way that works both in the production JSP environment as well as with fauxjsp. An example would be the case of varargs used in scriptlets we talked about earlier. The fauxjsp parser is likely to be confused by strings that appear in EL expressions and scriptlets which are used elsewhere as delimiters like <, >, {, } etc. In that case try to use escapes like u007B for {.
The use case is that your tagfiles or JSPs reference other tagfiles or JSPs which reside in the classpath (eg. a JAR file).
Don't use tagdir but use uri instead when including the resource, eg:
<%@ taglib prefix="cp" uri="classpath:/tagfiles" %>You probably noticed that the JstlView and InternalViewResolver don't work and Spring doesn't find your JSPs.
Under the hood, Spring should populate the model (the M in MVC) into the HttpServletRequest attributes and forward
the request to fauxjsp, but it doesn't. We can implement a simple forwarding mechanism like so:
import java.util.Locale;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
@Component
public class ForwardingViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
ForwardingView view = new ForwardingView();
view.setUrl("classpath:/jsp/"+viewName+".jsp");
return view;
}
}import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.view.InternalResourceView;
public class ForwardingView extends InternalResourceView {
@SuppressWarnings("unchecked")
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
@Override
public String getServletPath() {
return getUrl();
}
};
exposeModelAsRequestAttributes((Map<String, Object>)model, wrapper);
exposeHelpers(wrapper);
RequestDispatcher rd = request.getServletContext().getNamedDispatcher("FauxJsp");
rd.forward(wrapper, response);
}
}The simple presence of those two components in the web application context should be enough for them to be picked up and activated.
JspServlet can be configured to cache parsed JSPs. This is a minor performance improvement as normally rendering is
slower than parsing.
<servlet>
<servlet-name>FauxJsp</servlet-name>
<servlet-class>fauxjsp.servlet.JspServlet</servlet-class>
<init-param>
<param-name>cachePages</param-name>
<param-value>true</param-value>
</init-param>
</servlet>Specify a servlet init parameter trimDirectiveWhiteSpaces
<servlet>
<servlet-name>FauxJsp</servlet-name>
<servlet-class>fauxjsp.servlet.JspServlet</servlet-class>
<init-param>
<param-name>trimDirectiveWhiteSpaces</param-name>
<param-value>true</param-value>
</init-param>
</servlet>Features to come in the near sometime in the future:
Science fiction (things I have a rough idea how to implement but need to work out yet and may never come):
Ross Studtman for JSP Tutorials which inspired many great unit tests.
fauxjsp is available under the GPL. Since fauxjsp is a development tool, you normally wouldn't deploy it with your application binaries into production, so the "non-commercial" aspect of the GPL doesn't affect your application.