I recently enabled CSRF protection in my web Application. There are around 100+ JSP pages containing FORM submission. What is the best way adding CSRF token :
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
such that all the form submission will have this form data. I don't want to add this parameter to every individual FORM submit.
So I've finally found a working solution. Basically I create a custom FormRenderer like this :
import com.sun.faces.renderkit.html_basic.FormRenderer;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import java.io.IOException;
public class FormWithCSRFRenderer extends FormRenderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
log.debug("FormWithCSRFRenderer - Adding CSRF Token to form element");
ELContext elContext = context.getELContext();
ExpressionFactory expFactory = context.getApplication().getExpressionFactory();
ResponseWriter writer = context.getResponseWriter();
writer.startElement("input", component);
writer.writeAttribute("type", "hidden", null);
writer.writeAttribute("name", expFactory.createValueExpression(elContext, "${_csrf.parameterName}", String.class).getValue(elContext), null);
writer.writeAttribute("value", expFactory.createValueExpression(elContext, "${_csrf.token}", String.class).getValue(elContext), null);
writer.endElement("input");
writer.write("\n");
super.encodeEnd(context, component);
}
}
Then register it to override the FormRenderer by setting it in faces-config.xml :
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
<render-kit>
<renderer>
<component-family>javax.faces.Form</component-family>
<renderer-type>javax.faces.Form</renderer-type>
<renderer-class>com.acme.FormWithCSRFRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
I've tried to create a Component then add it as children but it wouldn't let me set the name of the input correctly so I directly write it.
Related
I am using Spring and Thymeleaf. Thanks to xerx593, I was able to get it working so I updated this question to show the working code.
Here is my application class
package com.propfinancing.www;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
#Controller
#SpringBootApplication
public class PfWebApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(PfWebApplication.class, args);
}
#Bean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
#GetMapping("/page1.html")
public String page1() {
return "page1";
}
}
Next, I create a layout.html file in src/main/resources/templates/layout.html
<!DOCTYPE html>
<html lang="en" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body>
This is the layout template
<div layout:fragment="content">
<p>This is were the content will go</p>
</div>
</body>
</html>
And fin
ally, I created /ser/main/resources/templates/page1.html to use the template:
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout}">
<body>
<div layout:fragment="content">
This is the content of page 1.
</div>
</body>
</html>
When I go to http://dev.propfinancing.com/www/page1.html, it gives me the template driven output I was expecting.
Thanks!
Neil
The most obvious mistake:
I created a simple html page called page1.html in my src/main/resources/static directory
(This is super for (spring-web) static content, but...)
And finally, I updated my page1.html to use the template...
Updating is not enough, you have to also move it to a configured template location! So moving the file to src/main/resources/templates/ (default location, issuing same browser request,) will hopefully/probably produce the desired result(, or at least throw an exception).
In short: src/main/resources/static directory is not intended for templates! (It can still be configured, but this would be very strange/hacky/bunch full of "side effects"!?).
Ok, the 404, can be fixed (simply) with:
#Controller // !
#SpringBootApplication
public class ThymeleafTestApplication {
public static void main(String[] args) {
SpringApplication.run(ThymeleafTestApplication.class, args);
}
#GetMapping("/page1.html") // !!
public String page1() {
return "page1"; // returning the view name
}
// ...
}
i.e. by providing a "controller" for this "view".
Or by configuring:
#SpringBootApplication
public class PfWebApplication // extends ...
implements WebMvcConfigurer {
#Override
public void addViewControllers (ViewControllerRegistry registry) {
ViewControllerRegistration r = registry.addViewController("/page1.html");
r.setViewName("page1");
// r.set...
}
...
One important thing is, that:
#Bean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
is the "auto-configuration" approach, which equips us with "all the spring (boot) magic".
Whereas:
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addDialect(new LayoutDialect());
return templateEngine;
}
..is the "DIY" approach, and we'd have to tune (like e.g. spring-boot does).
Links:
https://ultraq.github.io/thymeleaf-layout-dialect/
https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web
https://www.thymeleaf.org/doc/articles/layouts.html
In my case the problem was in the use of decorator I solved this issue by simply changing decorator to decorate
Here I had the error when I used decorator to configure the created template using Thymeleaf
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="template1">
Then I changed the decorator keyword to decorate and it just worked fine :
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="template1">
As soon as I define a composite component in a .taglib.xml to have a <handler-class>, the .xhtml file is ignored.
What I need is a xhtml/facelet based component with a component-type (UIComponent-derived class) and(!) also with a taghandler class. My aim is to catch all working on its child tags in order to be able to activate a custom cdi scope during this.
I found a way by looking at the richfaces impl.jar.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<factory>
<tag-handler-delegate-factory>de.sundn.regionscoped.jsf.BehaviorsTagHandlerDelegateFactoryImpl</tag-handler-delegate-factory>
</factory>
</faces-config>
Implement the factory
package de.sundn.regionscoped.jsf;
import javax.faces.FacesWrapper;
import javax.faces.view.facelets.BehaviorHandler;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.ConverterHandler;
import javax.faces.view.facelets.TagHandlerDelegate;
import javax.faces.view.facelets.TagHandlerDelegateFactory;
import javax.faces.view.facelets.ValidatorHandler;
/**
* #author Nick Belaevski
*/
public class BehaviorsTagHandlerDelegateFactoryImpl extends TagHandlerDelegateFactory implements
FacesWrapper<TagHandlerDelegateFactory> {
private TagHandlerDelegateFactory factory;
public BehaviorsTagHandlerDelegateFactoryImpl(TagHandlerDelegateFactory factory) {
this.factory = factory;
}
#Override
public TagHandlerDelegate createBehaviorHandlerDelegate(BehaviorHandler owner) {
return factory.createBehaviorHandlerDelegate(owner);
}
#Override
public TagHandlerDelegate createComponentHandlerDelegate(ComponentHandler owner) {
// TagHandlers structure is created when view is compiled
// so there's no need to check for BehaviorsStack
if (owner instanceof BehaviorsAddingComponentHandlerWrapper) {
// this is to avoid StackOverflowError because of ComponentHandler constructor call
return null;
}
ComponentHandler wrappedHandler = new BehaviorsAddingComponentHandlerWrapper(owner);
return factory.createComponentHandlerDelegate(wrappedHandler);
}
#Override
public TagHandlerDelegate createConverterHandlerDelegate(ConverterHandler owner) {
return factory.createConverterHandlerDelegate(owner);
}
#Override
public TagHandlerDelegate createValidatorHandlerDelegate(ValidatorHandler owner) {
return factory.createValidatorHandlerDelegate(owner);
}
#Override
public TagHandlerDelegateFactory getWrapped() {
return factory;
}
}
I'm trying to use spring security's "url" attribute of <authorize> tag. I got into a weird situation where the tag seems doesn't take effect.
My business need is to use two <http> elements, one for web service access and another for normal user access: the web service access is stateless but the user access is session based, that is why we need two http elements.
To illustrate this problem, I will use the spring security 3.1.4 tutorial instead. I can reproduce this problem by only adding one extra <http> element to the spring config file.
The original applicationContext-secutiry.xml from tutorial is defined like this:
<http pattern="/static/**" security="none"/>
<http pattern="/loggedout.jsp" security="none"/>
<http use-expressions="true">
<intercept-url pattern="/secure/extreme/**" access="hasRole('supervisor')"/>
<intercept-url pattern="/secure/**" access="isAuthenticated()" />
....
</http>
It's landing page, index.jsp, uses "authorize" tag like this:
<sec:authorize url='/secure/index.jsp'>
<p>
You can currently access "/secure" URLs.
</p>
</sec:authorize>
When a user first time tries to access this page, the tag will check the permission to url '/secure/index.jsp', which requires authentication and thus, the tag will NOT displace its content and UI display message like.
Your principal object is....: null
Now, change applicationContext-security.xml by adding a new http element before the last "http" element
<http pattern="/static/**" security="none"/>
<http pattern="/loggedout.jsp" security="none"/>
<!--This is the new element added-->
<http pattern="/user/**"
use-expressions="true">
<intercept-url pattern="/user/**" access="hasRole('user')"/>
<http-basic />
</http>
<http use-expressions="true">
<intercept-url pattern="/secure/extreme/**" access="hasRole('supervisor')"/>
<intercept-url pattern="/secure/**" access="isAuthenticated()" />
....
</http>
Now, I access the index.jsp (no login yet) and the page actually prints out a message saying:
Your principal object is....: null
You can currently access "/secure" URLs.
You can currently access "/secure/extreme" URLs.
The "authorize" tag evaluates to true in this case, even if I haven't logged in!
I tried to debug through source code, DefaultFilterInvocationSecurityMetadataSource, and found that when the first http request, "/index.jsp" comes in, it uses the default element (the last one) in applicationContext-security.xml but when the "" tag tries to check access to "/secure/index.jsp", DefaultFilterInvocationSecurityMetadataSource is using the new element and its getAttributes() return null to DefaultWebInvocationPrivilegeEvaluator which eventually return true.
This looks like a spring security bug to me: the authorize tag url, "/secure/index.jsp" should match the default "http", not the other one.
One workaround I used is to copy the "intercept-url" definitions for "/secure/index.jsp" and "/secure/extreme/index.jsp" from the default "http" to the new "http" element and then the UI works as expected. But I don't want to copy codes!
Any idea is appreciated.
I have resolved this issue as following.
Java:
package com.github.kazuki43zoo.web.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.stereotype.Component;
#Component
public class CustomWebInvocationPrivilegeEvaluatorProvideFilter implements Filter,
WebInvocationPrivilegeEvaluator, BeanPostProcessor {
private List<WebInvocationPrivilegeEvaluator> webInvocationPrivilegeEvaluators = new ArrayList<>();
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE, this);
chain.doFilter(request, response);
}
#Override
public void destroy() {
webInvocationPrivilegeEvaluators.clear();
}
#Override
public boolean isAllowed(String uri, Authentication authentication) {
for (WebInvocationPrivilegeEvaluator privilegeEvaluator : webInvocationPrivilegeEvaluators) {
if (!privilegeEvaluator.isAllowed(uri, authentication)) {
return false;
}
}
return true;
}
#Override
public boolean isAllowed(String contextPath, String uri, String method,
Authentication authentication) {
for (WebInvocationPrivilegeEvaluator privilegeEvaluator : webInvocationPrivilegeEvaluators) {
if (!privilegeEvaluator.isAllowed(contextPath, uri, method, authentication)) {
return false;
}
}
return true;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof WebInvocationPrivilegeEvaluator
&& !bean.getClass().isAssignableFrom(getClass())) {
webInvocationPrivilegeEvaluators.add((WebInvocationPrivilegeEvaluator) bean);
}
return bean;
}
}
web.xml:
<filter>
<filter-name>CustomWebInvocationPrivilegeEvaluatorProvideFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>customWebInvocationPrivilegeEvaluatorProvideFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CustomWebInvocationPrivilegeEvaluatorProvideFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Here is what worked for me:
org.springframework.context.ApplicationContext ctx = org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
java.util.Map<String, org.springframework.security.web.access.WebInvocationPrivilegeEvaluator>
wipes = ctx.getBeansOfType(org.springframework.security.web.access.WebInvocationPrivilegeEvaluator.class);
if(wipes.size() > 0){
//I need last one
org.springframework.security.web.access.WebInvocationPrivilegeEvaluator appEvaluator =
(WebInvocationPrivilegeEvaluator)wipes.values().toArray()[wipes.size() - 1];
//set request attribute so that JSP tag can use it request.setAttribute(org.springframework.security.web.WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE
, appEvaluator);
}
I'm integrating struts2, tiles3 and Freemarker (templates from a DB). The integration is mostly done, but the freemarker renderer is rendering to the result immediately instead of inserting its content into the template at the expected location.
Any advice on how to correct this?
Expected Output:
<!DOCTYPE html>
<html>
<body>
<h1>Template</h1>
<strong>A bold statement!</strong><div>a message from the Action...</div><div> I came from: the_location</div><div>a message from the Action...</div>
</body>
</html>
Actual Output (Not formatted for readability because this is a template issue and not an html issue so accuracy of output counts):
<strong>A bold statement!</strong><div>a message from the Action...</div><div> I came from: the_location</div>
<!DOCTYPE html>
<html>
<body>
<h1>Template</h1>
</body>
</html>
tiles-defs.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="test_def2" template="/WEB-INF/template/template.jsp">
<put-attribute name="body" value="the_location" type="db_freemarker"/>
</definition>
</tiles-definitions>
/WEB-INF/template/template.jsp
<%#taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%#taglib prefix="s" uri="/struts-tags"%>
<%#page contentType="text/html" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<body>
<h1>Template</h1>
<tiles:insertAttribute name="body"/>
</body>
</html>
Created and registered a DbFreemarkerTilesRenderer with key "db_freemarker", this is the implementation of DbFreemarkerTilesRenderer:
package com.quaternion.struts2.result.freemarker;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.freemarker.render.FreemarkerRenderer;
import org.apache.tiles.request.render.CannotRenderException;
import org.apache.tiles.request.servlet.ServletRequest;
public class DbFreemarkerTilesRenderer extends FreemarkerRenderer{
public DbFreemarkerTilesRenderer(){
super(null); //Expects a AttributeValueFreemarkerServlet
}
/** {#inheritDoc} */
#Override
public void render(String location, Request request) throws IOException {
if (location == null) {
throw new CannotRenderException("Cannot dispatch a null path");
}
ServletRequest servletRequest = org.apache.tiles.request.servlet.ServletUtil.getServletRequest(request);
HttpServletRequest httpRequest = servletRequest.getRequest();
HttpServletResponse httpResponse = servletRequest.getResponse();
DataBaseTemplateLoader dataBaseTemplateLoader = new DataBaseTemplateLoader();
try {
dataBaseTemplateLoader.render(location, httpRequest, httpResponse);
} catch (Exception ex) {
Logger.getLogger(DbFreemarkerTilesRenderer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
The "DataBaseTemplateLoader" actually loads a freemarker template from a hard coded string, but it will load from a DB once this issue is resolved (see the render method the rest of the class is included for context, Struts2 developers will recognize it as a Result type, the idea being the Freemarker DB functionality can be provided directly to Struts2 as a Result):
package com.quaternion.struts2.result.freemarker;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.util.ContextUtil;
public class DataBaseTemplateLoader extends ServletDispatcherResult {
private static final Logger log = Logger.getLogger(DataBaseTemplateLoader.class.getName());
private String defaultEncoding = "ISO-8859-1"; //TODO: Hardcoded?
private FreemarkerManager freemarkerManager;
private Configuration configuration;
//Inject works if this is a Struts2 component (Result) however
//when using this from Tiles, the following is not injected...
#Inject
public void setFreemarkerManager(FreemarkerManager mgr) {
this.freemarkerManager = mgr;
}
public DataBaseTemplateLoader() {
super();
}
public DataBaseTemplateLoader(String location) {
super(location); //TODO: look up template based on "location" string
}
protected Configuration getConfiguration() throws TemplateException {
if (freemarkerManager == null){
//Force injection
freemarkerManager = ActionContext.getContext().getContainer().getInstance(FreemarkerManager.class);
}
return freemarkerManager.getConfiguration(ServletActionContext.getServletContext());
}
#Override
public void doExecute(String location, ActionInvocation invocation) throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
render(location, request, response);
}
public void render(String location, HttpServletRequest request, HttpServletResponse response) throws Exception {
//location is String passed from Struts2 action which will formated
//in such a way to apply conventions though to view
//Format: namespace#action#method
//setLocation(location);
StringTemplateLoader stringLoader = new StringTemplateLoader();
//BOTH "name" and "source" will be retrieved from the DB
String name = "firstTemplate";
String source = "<strong>A bold statement!</strong><div>${action.message}</div><div> I came from: " + location + "</div>";
stringLoader.putTemplate(name, source);
// It's possible to add more than one template (they might include each other)
// String secondTemplate = "<#include \"greetTemplate\"><#greet/> World!";
// stringLoader.putTemplate("greetTemplate", secondTemplate);
freemarker.template.Configuration cfg = getConfiguration();
cfg.setTemplateLoader(stringLoader);
Template template = cfg.getTemplate(name);
Map map = ContextUtil.getStandardContext(ActionContext.getContext().getValueStack(), request, response);
response.setContentType("text/html;charset=" + defaultEncoding);
template.process(map, response.getWriter());
}
}
The Struts2 Action (not needed, just shows where "a message from the Action..." comes from:
package com.quaternion.tilesdb.action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
/**
* Tests Tiles with Freemarker integration, where the freemarker template
* is parsed from a String, this is to allow later integration with freemarker
* database templates
*/
#ParentPackage("quaternion-results")
#Result(type="tiles", location="test_def2")
public class TilesFtlTest2 extends ActionSupport{
private String message = "a message from the Action...";
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
I just want to close this question off, the answer is I was missing tiles configuration from web.xml (the Freemarker servlet configuration).
Here is the web.xml which allows the above to work:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<listener>
<listener-class>com.quaternion.struts2.result.freemarker.MoreCompleteAutoloadTilesListener</listener-class>
<!--<listener-class>org.apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>-->
</listener>
<!-- Following is required to enable freemarker(ftl) support-->
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>org.apache.tiles.request.freemarker.servlet.SharedVariableLoaderFreemarkerServlet</servlet-class>
<!-- FreemarkerServlet settings: -->
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/WEB-INF/template/</param-value>
</init-param>
<init-param>
<param-name>NoCache</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>ContentType</param-name>
<param-value>text/html</param-value>
</init-param>
<!-- FreeMarker settings: -->
<init-param>
<param-name>template_update_delay</param-name>
<param-value>0</param-value> <!-- 0 is for development only! Use higher value otherwise. -->
</init-param>
<init-param>
<param-name>default_encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
<init-param>
<param-name>number_format</param-name>
<param-value>0.##########</param-value>
</init-param>
<init-param>
<param-name>org.apache.tiles.request.freemarker.CUSTOM_SHARED_VARIABLE_FACTORIES</param-name>
<param-value>tiles,org.apache.tiles.freemarker.TilesSharedVariableFactory</param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>/index.action</welcome-file>
</welcome-file-list>
</web-app>
I have read the following post which was very helpful
Migrating from JSF 1.2 to JSF 2.0
but I am having a problem with the migration as I have a custom view handler which extends from FaceletViewHandler - this is not part of faclets 2.
I am migrating on JBoss 4.2.2 the following:
- JSF 1.2 to JSF 2.0
I also want to migrate the faclets - which i have a problem described above.
In my application, I am also using Tomahawk - is there any problem with this migration?
Thanks in advance.
Elico.
Right, you need to replace FaceletViewHandler by ViewHandlerWrapper.
So the following basic FaceletViewHandler implementation:
import javax.faces.application.ViewHandler;
import com.sun.facelets.FaceletViewHandler;
public class MyViewHandler extends FaceletViewHandler {
public MyViewHandler(ViewHandler parent) {
super(parent);
}
// ...
}
needs to be updated as follows:
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
public class MyViewHandler extends ViewHandlerWrapper {
private ViewHandler wrapped;
public MyViewHandler(ViewHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ViewHandler getWrapped() {
return wrapped;
}
// ...
}
I've updated my answer on the migration question accordingly.
To activate MyViewHandler e.g. for JEE7, WEB-INF/faces-config.xml should be defined like:
<?xml version="1.0"?>
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<application>
<view-handler>pkg.MyViewHandler</view-handler>
</application>
</faces-config>