Merges JSF into Action 2, allowing JSF components and backing beans to

be able to be used along side regular saf2 actions.  Furthermore, the
familiar feel of an action-based framework is brought to JSF pages,
giving each page its own action and making that action available in the
component's expressions.  This commit also includes a new section in the
showcase, demonstrating the JSF support.

WW-1320


git-svn-id: https://svn.apache.org/repos/asf/struts/action2/trunk@408129 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Donald J. Brown
2006-05-21 04:13:53 +00:00
parent a6da35052f
commit a7d7706ec1
25 changed files with 1468 additions and 2 deletions
+11
View File
@@ -83,6 +83,17 @@
<version>1.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>myfaces</groupId>
<artifactId>myfaces-impl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>myfaces</groupId>
<artifactId>myfaces-api</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
@@ -0,0 +1,37 @@
package org.apache.struts.action2.showcase.jsf;
import java.util.Arrays;
import java.util.List;
public class Employee {
private String name;
private int id;
private List skills;
public Employee() {}
public Employee(int id, String name, String[] skills) {
this.id = id;
this.name = name;
this.skills = Arrays.asList(skills);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getSkills() {
return skills;
}
public void setSkills(List skills) {
this.skills = skills;
}
}
@@ -0,0 +1,58 @@
/*
* $Id: QuizAction.java 394498 2006-04-16 15:28:06Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.showcase.jsf;
import java.util.List;
import com.opensymphony.xwork.ActionSupport;
public class EmployeeAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private Employee employee;
private int id;
public EmployeeAction() {
employee = new Employee();
}
public Employee getEmployee() {
return employee;
}
public String[] getAllSkills() {
return EmployeeDao.getSkills();
}
public void setId(int id) {
this.id = id;
}
public String execute() {
this.employee = EmployeeDao.getEmployee(id);
return SUCCESS;
}
public String save() {
EmployeeDao.save(employee);
return "index";
}
}
@@ -0,0 +1,37 @@
package org.apache.struts.action2.showcase.jsf;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class EmployeeDao {
private static String[] skills;
private static List<Employee> employees;
static {
employees = new ArrayList<Employee>();
employees.add(new Employee(0, "Tom Jones", new String[]{"Java", "PHP"}));
employees.add(new Employee(1, "Bart Simpson", new String[]{"PHP"}));
employees.add(new Employee(2, "Sofia Jones", new String[]{"Java"}));
skills = new String[]{"Java", "PHP", "C#"};
}
public static List<Employee> getEmployees() {
return employees;
}
public static Employee getEmployee(int id) {
return employees.get(id);
}
public static void save(Employee e) {
employees.set(e.getId(), e);
}
public static String[] getSkills() {
return skills;
}
}
@@ -0,0 +1,43 @@
/*
* $Id: QuizAction.java 394498 2006-04-16 15:28:06Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.showcase.jsf;
import java.util.List;
import com.opensymphony.xwork.ActionSupport;
public class IndexAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private List loadedEmployees;
public List getEmployees() {
System.out.println("getting employees:"+loadedEmployees);
return loadedEmployees;
}
public String execute() {
System.out.println("executing");
loadedEmployees = EmployeeDao.getEmployees();
return SUCCESS;
}
}
@@ -0,0 +1,21 @@
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.1.1//EN" "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd">
<xwork>
<include file="struts-default.xml"/>
<package name="jsf" extends="struts-default" namespace="/jsf">
<action name="index" class="org.apache.struts.action2.showcase.jsf.IndexAction">
<interceptor-ref name="jsfStack"/>
<result name="success" type="jsf" />
</action>
<action name="employee" class="org.apache.struts.action2.showcase.jsf.EmployeeAction">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="jsfStack"/>
<result name="success" type="jsf" />
<result name="index" type="redirect-action">index</result>
</action>
</package>
</xwork>
@@ -23,6 +23,8 @@
<include file="xwork-person.xml" />
<include file="xwork-wait.xml" />
<include file="xwork-jsf.xml" />
<include file="xwork-token.xml" />
@@ -75,6 +75,7 @@
<li><a href="<saf:url value="/wait/index.jsp"/>">Execute & Wait</a></li>
<li><a href="<saf:url value="/token/index.jsp"/>">Token</a></li>
<li><a href="<saf:url value="/filedownload/index.jsp"/>">File Download</a></li>
<li><a href="<saf:url action="index" namespace="/jsf"/>">JSF</a></li>
<li class="last"><a href="<saf:url value="/help.jsp"/>">Help</a></li>
</ul>
</div>
+23 -2
View File
@@ -4,6 +4,7 @@
<display-name>WebWork Showcase Application</display-name>
<filter>
<filter-name>action2-cleanup</filter-name>
<filter-class>org.apache.struts.action2.dispatcher.ActionContextCleanUp</filter-class>
@@ -18,6 +19,8 @@
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>action2-cleanup</filter-name>
@@ -38,8 +41,13 @@
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>
org.apache.myfaces.webapp.StartupServletContextListener
</listener-class>
</listener>
<!-- SNIPPET START: dwr -->
<servlet>
@@ -50,6 +58,19 @@
<param-value>true</param-value>
</init-param>
</servlet>
<!-- JavaServer Faces Servlet Configuration, not used directly -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- JavaServer Faces Servlet Mapping, not called directly -->
<servlet-mapping>
<servlet-name>faces</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dwr</servlet-name>
@@ -0,0 +1,61 @@
<%--
Copyright 2006 The Apache Software Foundation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
$Id: welcome.jsp 371852 2006-01-24 07:25:10Z craigmcc $
--%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<f:view>
<html>
<head>
<title>JSF Integration Examples</title>
</head>
<body>
<h2>Modify Employee</h2>
<h:form>
<h:inputHidden value="#{action.employee.id}" />
<h:panelGrid columns="2">
<h:outputText value="ID:" />
<h:outputText value="#{action.employee.id}" />
<h:outputText value="Name:" />
<h:inputText size="30" value="#{action.employee.name}" />
<h:outputText value="Skills:" />
<h:selectManyCheckbox value="#{action.employee.skills}">
<f:selectItem itemValue="Java" itemLabel="Java" />
<f:selectItem itemValue="PHP" itemLabel="PHP" />
<f:selectItem itemValue="Ruby" itemLabel="Ruby" />
</h:selectManyCheckbox>
</h:panelGrid>
<h:commandButton value="Submit" action="#{action.save}" />
<br /><br />
<h:outputLink value="index.action">
<h:outputText value="Back" />
</h:outputLink>
</h:form>
</body>
</html>
</f:view>
@@ -0,0 +1,68 @@
<%--
Copyright 2006 The Apache Software Foundation.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
$Id: welcome.jsp 371852 2006-01-24 07:25:10Z craigmcc $
--%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<f:view>
<html>
<head>
<title>JSF Integration Examples</title>
</head>
<body>
<h2>JSF Integration Examples</h2>
<p>
The following pages show how Struts Action 2 and JSF components can work together,
each doing what they do best.
</p>
<h3>Employee List</h3>
<h:dataTable value="#{action.employees}" var="e">
<h:column>
<f:facet name="header">
<h:outputText value="ID" />
</f:facet>
<h:outputLink value="employee.action">
<f:param name="id" value="#{e.id}" />
<h:outputText value="#{e.id}" />
</h:outputLink>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Name" />
</f:facet>
<h:outputText value="#{e.name}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Skills" />
</f:facet>
<h:outputText value="#{e.skills}" />
</h:column>
</h:dataTable>
</body>
</html>
</f:view>
+7
View File
@@ -258,6 +258,13 @@
<version>1.2.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>myfaces</groupId>
<artifactId>myfaces-jsf-api</artifactId>
<version>1.0.9</version>
<scope>provided</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
@@ -0,0 +1,70 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
/**
* Applies the request values to the component tree
*/
public class ApplyRequestValuesInterceptor extends FacesInterceptor {
private static final long serialVersionUID = -1471180154211835323L;
/**
* Apply Request Values (JSF.2.2.2)
*
* @param viewId The view id
* @param facesContext The faces context
* @return true, if response is complete
*/
protected boolean executePhase(String viewId, FacesContext facesContext)
throws FacesException {
boolean skipFurtherProcessing = false;
if (log.isTraceEnabled())
log.trace("entering applyRequestValues");
informPhaseListenersBefore(facesContext, PhaseId.APPLY_REQUEST_VALUES);
if (isResponseComplete(facesContext, "applyRequestValues", true)) {
// have to return right away
return true;
}
if (shouldRenderResponse(facesContext, "applyRequestValues", true)) {
skipFurtherProcessing = true;
}
facesContext.getViewRoot().processDecodes(facesContext);
informPhaseListenersAfter(facesContext, PhaseId.APPLY_REQUEST_VALUES);
if (isResponseComplete(facesContext, "applyRequestValues", false)
|| shouldRenderResponse(facesContext, "applyRequestValues",
false)) {
// since this phase is completed we don't need to return right away
// even if the response is completed
skipFurtherProcessing = true;
}
if (!skipFurtherProcessing && log.isTraceEnabled())
log.trace("exiting applyRequestValues");
return skipFurtherProcessing;
}
}
@@ -0,0 +1,90 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.context.FacesContext;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.Interceptor;
/**
* Translates JSF phases into individual interceptors, and adapts their expected
* workflow to Action 2
*/
public class FacesInterceptor extends FacesSupport implements Interceptor {
private static final long serialVersionUID = -5418255964277566516L;
/**
* Not used
*/
public void init() {
}
/**
* Adapts the phase workflow to Action 2
*
* @param invocation
* The action invocation
* @return The string result code
*/
public String intercept(ActionInvocation invocation) throws Exception {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getRenderResponse()) {
return invocation.invoke();
} else {
String viewId = invocation.getProxy().getNamespace() + '/'
+ invocation.getProxy().getActionName();
executePhase(viewId, context);
if (context.getResponseComplete()) {
// Abort the chain as the result is done
return null;
} else {
if (invocation.getResultCode() != null) {
return invocation.getResultCode();
} else {
return invocation.invoke();
}
}
}
}
/**
* Executes the specific phase. The phase id is constructed as a composite
* of the namespace and action name.
*
* @param viewId
* The view id
* @param facesContext
* The current faces context
* @return True if the next phases should be skipped
*/
protected boolean executePhase(String viewId, FacesContext facesContext) {
return false;
}
/**
* Not used
*/
public void destroy() {
}
}
@@ -0,0 +1,85 @@
/*
* $Id: PlainTextResult.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.ViewHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.Result;
/**
* Executes the JSF render phase
*/
public class FacesResult extends FacesSupport implements Result {
private static final long serialVersionUID = -3548970638740937804L;
/**
* Executes the result
*/
public void execute(ActionInvocation invocation) throws Exception {
render(FacesContext.getCurrentInstance());
}
/**
* Executes the render phase, borrowed from MyFaces
*
* @param facesContext
* The faces context
* @throws FacesException
* If anything goes wrong
*/
public void render(FacesContext facesContext) throws FacesException {
// if the response is complete we should not be invoking the phase
// listeners
if (isResponseComplete(facesContext, "render", true)) {
return;
}
if (log.isTraceEnabled())
log.trace("entering renderResponse");
informPhaseListenersBefore(facesContext, PhaseId.RENDER_RESPONSE);
// also possible that one of the listeners completed the response
if (isResponseComplete(facesContext, "render", true)) {
return;
}
Application application = facesContext.getApplication();
ViewHandler viewHandler = application.getViewHandler();
try {
viewHandler.renderView(facesContext, facesContext.getViewRoot());
} catch (IOException e) {
throw new FacesException(e.getMessage(), e);
}
informPhaseListenersAfter(facesContext, PhaseId.RENDER_RESPONSE);
if (log.isTraceEnabled()) {
// Note: DebugUtils Logger must also be in trace level
// DebugUtils.traceView("View after rendering");
}
if (log.isTraceEnabled())
log.trace("exiting renderResponse");
}
}
@@ -0,0 +1,110 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import org.apache.struts.action2.ServletActionContext;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.Interceptor;
/**
* Initializes the JSF context for this request
*/
public class FacesSetupInterceptor extends FacesSupport implements Interceptor {
private static final long serialVersionUID = -621512342655103941L;
private String lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
private FacesContextFactory facesContextFactory;
private Lifecycle lifecycle;
/**
* Sets the lifecycle id
*
* @param id
* The id
*/
public void setLifecycleId(String id) {
this.lifecycleId = id;
}
/**
* Initializes the lifecycle and factories
*/
public void init() {
facesContextFactory = (FacesContextFactory) FactoryFinder
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
// Javadoc says: Lifecycle instance is shared across multiple
// simultaneous requests, it must be implemented in a thread-safe
// manner.
// So we can acquire it here once:
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
Application application = ((ApplicationFactory) FactoryFinder
.getFactory(FactoryFinder.APPLICATION_FACTORY))
.getApplication();
if (!(application.getNavigationHandler() instanceof StrutsNavigationHandler)) {
application.setNavigationHandler(new StrutsNavigationHandler());
}
if (!(application.getVariableResolver() instanceof StrutsVariableResolver)) {
application.setVariableResolver(new StrutsVariableResolver(
application.getVariableResolver()));
}
}
/**
* Creates the faces context for other phases.
*
* @param invocation
* The action invocation
*/
public String intercept(ActionInvocation invocation) throws Exception {
FacesContext facesContext = facesContextFactory.getFacesContext(
ServletActionContext.getServletContext(), ServletActionContext
.getRequest(), ServletActionContext.getResponse(),
lifecycle);
setLifecycle(lifecycle);
try {
return invocation.invoke();
} finally {
facesContext.release();
}
}
/**
* Cleans up the lifecycle and factories
*/
public void destroy() {
facesContextFactory = null;
lifecycle = null;
}
}
@@ -0,0 +1,164 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.lifecycle.Lifecycle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.opensymphony.xwork.ActionContext;
/**
* Provides common lifecycle phase methods needed by interceptors and results.
*/
public class FacesSupport {
private static final String LIFECYCLE_KEY = "lifecycle";
/** Log instance for each class */
protected Log log;
/** Initializes log instance for the instance object */
protected FacesSupport() {
log = LogFactory.getLog(getClass());
}
/**
* Gets the shared lifecycle for this request
*
* @return The lifecycle
*/
private Lifecycle getLifecycle() {
return (Lifecycle) ActionContext.getContext().get(LIFECYCLE_KEY);
}
/**
* Sets the lifecycle for this request
*
* @param lifecycle
* The lifecycle
*/
protected void setLifecycle(Lifecycle lifecycle) {
ActionContext.getContext().put(LIFECYCLE_KEY, lifecycle);
}
/**
* Informs phase listeners before a phase is executed
*
* @param facesContext
* The current faces context
* @param phaseId
* The phase id about to be executed
*/
protected void informPhaseListenersBefore(FacesContext facesContext,
PhaseId phaseId) {
Lifecycle lifecycle = getLifecycle();
PhaseListener[] phaseListeners = lifecycle.getPhaseListeners();
for (int i = 0; i < phaseListeners.length; i++) {
PhaseListener phaseListener = phaseListeners[i];
int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
if (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal()
|| listenerPhaseId == phaseId.getOrdinal()) {
phaseListener.beforePhase(new PhaseEvent(FacesContext
.getCurrentInstance(), phaseId, lifecycle));
}
}
}
/**
* Informs phase listeners after a phase is executed
*
* @param facesContext
* The current faces context
* @param phaseId
* The phase id that was executed
*/
protected void informPhaseListenersAfter(FacesContext facesContext,
PhaseId phaseId) {
Lifecycle lifecycle = getLifecycle();
PhaseListener[] phaseListeners = lifecycle.getPhaseListeners();
for (int i = 0; i < phaseListeners.length; i++) {
PhaseListener phaseListener = phaseListeners[i];
int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
if (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal()
|| listenerPhaseId == phaseId.getOrdinal()) {
phaseListener.afterPhase(new PhaseEvent(FacesContext
.getCurrentInstance(), phaseId, lifecycle));
}
}
}
/**
* Checks to see if the response has been completed. Mainly used for better
* debugging messages.
*
* @param facesContext
* The current faces context
* @param phase
* The phase id in execution
* @param before
* Whether the phase has been executed or not
* @return True if the response is complete
*/
protected boolean isResponseComplete(FacesContext facesContext,
String phase, boolean before) {
boolean flag = false;
if (facesContext.getResponseComplete()) {
if (log.isDebugEnabled())
log
.debug("exiting from lifecycle.execute in "
+ phase
+ " because getResponseComplete is true from one of the "
+ (before ? "before" : "after") + " listeners");
flag = true;
}
return flag;
}
/**
* Checks to see the render phase should be executed next. Mainly used for
* better debugging messages.
*
* @param facesContext
* The current faces context
* @param phase
* The phase id in execution
* @param before
* Whether the phase has been executed or not
* @return True if the response is complete
*/
protected boolean shouldRenderResponse(FacesContext facesContext,
String phase, boolean before) {
boolean flag = false;
if (facesContext.getRenderResponse()) {
if (log.isDebugEnabled())
log.debug("exiting from lifecycle.execute in " + phase
+ " because getRenderResponse is true from one of the "
+ (before ? "before" : "after") + " listeners");
flag = true;
}
return flag;
}
}
@@ -0,0 +1,73 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
/**
* Invokes the application
*/
public class InvokeApplicationInterceptor extends FacesInterceptor {
private static final long serialVersionUID = -7388153356410171208L;
/**
* Invoke Application (JSF.2.2.5)
*
* @param viewId
* The view id
* @param facesContext
* The faces context
* @return true, if response is complete
*/
protected boolean executePhase(String viewId, FacesContext facesContext)
throws FacesException {
boolean skipFurtherProcessing = false;
if (log.isTraceEnabled())
log.trace("entering invokeApplication");
informPhaseListenersBefore(facesContext, PhaseId.INVOKE_APPLICATION);
if (isResponseComplete(facesContext, "invokeApplication", true)) {
// have to return right away
return true;
}
if (shouldRenderResponse(facesContext, "invokeApplication", true)) {
skipFurtherProcessing = true;
}
facesContext.getViewRoot().processApplication(facesContext);
informPhaseListenersAfter(facesContext, PhaseId.INVOKE_APPLICATION);
if (isResponseComplete(facesContext, "invokeApplication", false)
|| shouldRenderResponse(facesContext, "invokeApplication",
false)) {
// since this phase is completed we don't need to return right away
// even if the response is completed
skipFurtherProcessing = true;
}
if (!skipFurtherProcessing && log.isTraceEnabled())
log.trace("exiting invokeApplication ");
return skipFurtherProcessing;
}
}
@@ -0,0 +1,72 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
/**
* Processes validations on the component tree
*/
public class ProcessValidationsInterceptor extends FacesInterceptor {
private static final long serialVersionUID = 8785236570688278147L;
/**
* Process Validations (JSF.2.2.3)
*
* @param viewId
* The view id
* @param facesContext
* The faces context
* @return true, if response is complete
*/
protected boolean executePhase(String viewId, FacesContext facesContext)
throws FacesException {
boolean skipFurtherProcessing = false;
if (log.isTraceEnabled())
log.trace("entering processValidations");
informPhaseListenersBefore(facesContext, PhaseId.PROCESS_VALIDATIONS);
if (isResponseComplete(facesContext, "processValidations", true)) {
// have to return right away
return true;
}
if (shouldRenderResponse(facesContext, "processValidations", true)) {
skipFurtherProcessing = true;
}
facesContext.getViewRoot().processValidators(facesContext);
informPhaseListenersAfter(facesContext, PhaseId.PROCESS_VALIDATIONS);
if (isResponseComplete(facesContext, "processValidations", false)
|| shouldRenderResponse(facesContext, "processValidations",
false)) {
// since this phase is completed we don't need to return right away
// even if the response is completed
skipFurtherProcessing = true;
}
if (!skipFurtherProcessing && log.isTraceEnabled())
log.trace("exiting processValidations");
return skipFurtherProcessing;
}
}
@@ -0,0 +1,221 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Iterator;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.PhaseId;
/**
* Restores the view or component tree
*/
public class RestoreViewInterceptor extends FacesInterceptor {
private static final long serialVersionUID = -1500785113037140668L;
/**
* Restore View (JSF.2.2.1)
*
* @param viewId
* The view id
* @param facesContext
* The faces context
* @return true, if immediate rendering should occur
*/
protected boolean executePhase(String viewId, FacesContext facesContext) {
boolean skipFurtherProcessing = false;
if (log.isTraceEnabled())
log.trace("entering restoreView");
informPhaseListenersBefore(facesContext, PhaseId.RESTORE_VIEW);
if (isResponseComplete(facesContext, "restoreView", true)) {
// have to skips this phase
return true;
}
if (shouldRenderResponse(facesContext, "restoreView", true)) {
skipFurtherProcessing = true;
}
ExternalContext externalContext = facesContext.getExternalContext();
String defaultSuffix = externalContext
.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
String suffix = defaultSuffix != null ? defaultSuffix
: ViewHandler.DEFAULT_SUFFIX;
if (viewId != null) {
viewId += suffix;
}
if (viewId == null) {
if (!externalContext.getRequestServletPath().endsWith("/")) {
try {
externalContext.redirect(externalContext
.getRequestServletPath()
+ "/");
facesContext.responseComplete();
return true;
} catch (IOException e) {
throw new FacesException("redirect failed", e);
}
}
}
Application application = facesContext.getApplication();
ViewHandler viewHandler = application.getViewHandler();
// boolean viewCreated = false;
UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
if (viewRoot == null) {
viewRoot = viewHandler.createView(facesContext, viewId);
viewRoot.setViewId(viewId);
facesContext.renderResponse();
// viewCreated = true;
}
facesContext.setViewRoot(viewRoot);
/*
* This section has been disabled because it causes some bug. Be careful
* if you need to re-enable it. Furthermore, for an unknown reason, it
* seems that by default it is executed (i.e. log.isTraceEnabled() is
* true). Bug example : This traceView causes DebugUtil.printComponent
* to print all the attributes of the view components. And if you have a
* data table within an aliasBean, this causes the data table to
* initialize it's value attribute while the alias isn't set. So, the
* value initializes with an UIData.EMPTY_DATA_MODEL, and not with the
* aliased one. But as it's initialized, it will not try to get the
* value from the ValueBinding next time it needs to. I expect this to
* cause more similar bugs. TODO : Completely remove or be SURE by
* default it's not executed, and it has no more side-effects.
*
* if (log.isTraceEnabled()) { //Note: DebugUtils Logger must also be in
* trace level DebugUtils.traceView(viewCreated ? "Newly created view" :
* "Restored view"); }
*/
if (facesContext.getExternalContext().getRequestParameterMap()
.isEmpty()) {
// no POST or query parameters --> set render response flag
facesContext.renderResponse();
}
recursivelyHandleComponentReferencesAndSetValid(facesContext, viewRoot);
informPhaseListenersAfter(facesContext, PhaseId.RESTORE_VIEW);
if (isResponseComplete(facesContext, "restoreView", false)
|| shouldRenderResponse(facesContext, "restoreView", false)) {
// since this phase is completed we don't need to return right away
// even if the response is completed
skipFurtherProcessing = true;
}
if (!skipFurtherProcessing && log.isTraceEnabled())
log.trace("exiting restoreView ");
return skipFurtherProcessing;
}
/**
* Walk the component tree, executing any component-bindings to reattach
* components to their backing beans. Also, any UIInput component is marked
* as Valid.
* <p>
* Note that this method effectively breaks encapsulation; instead of asking
* each component to update itself and its children, this method just
* reaches into each component. That makes it impossible for any component
* to customise its behaviour at this point.
* <p>
* This has been filed as an issue against the spec. Until this issue is
* resolved, we'll add a new marker-interface for components to allow them
* to define their interest in handling children bindings themselves.
*/
protected void recursivelyHandleComponentReferencesAndSetValid(
FacesContext facesContext, UIComponent parent) {
recursivelyHandleComponentReferencesAndSetValid(facesContext, parent,
false);
}
protected void recursivelyHandleComponentReferencesAndSetValid(
FacesContext facesContext, UIComponent parent, boolean forceHandle) {
Method handleBindingsMethod = getBindingMethod(parent);
if (handleBindingsMethod != null && !forceHandle) {
try {
handleBindingsMethod.invoke(parent, new Object[] {});
} catch (Throwable th) {
log.error(
"Exception while invoking handleBindings on component with client-id:"
+ parent.getClientId(facesContext), th);
}
} else {
for (Iterator it = parent.getFacetsAndChildren(); it.hasNext();) {
UIComponent component = (UIComponent) it.next();
ValueBinding binding = component.getValueBinding("binding"); // TODO:
// constant
if (binding != null && !binding.isReadOnly(facesContext)) {
binding.setValue(facesContext, component);
}
if (component instanceof UIInput) {
((UIInput) component).setValid(true);
}
recursivelyHandleComponentReferencesAndSetValid(facesContext,
component);
}
}
}
/**
* This is all a hack to work around a spec-bug which will be fixed in
* JSF2.0
*
* @param parent
* @return true if this component is bindingAware (e.g. aliasBean)
*/
private static Method getBindingMethod(UIComponent parent) {
Class[] clazzes = parent.getClass().getInterfaces();
for (int i = 0; i < clazzes.length; i++) {
Class clazz = clazzes[i];
if (clazz.getName().indexOf("BindingAware") != -1) {
try {
return parent.getClass().getMethod("handleBindings",
new Class[] {});
} catch (NoSuchMethodException e) {
// return
}
}
}
return null;
}
}
@@ -0,0 +1,46 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import com.opensymphony.xwork.ActionContext;
/**
* Overrides the JFS navigation by delegating the result to handling by the core
* result code lookup and execution.
*/
public class StrutsNavigationHandler extends NavigationHandler {
/**
* Stores any outcomes as the result code
*
* @param facesContext The faces context
* @param fromAction The action we are coming from
* @param outcome The String return code
*/
@Override
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome) {
ActionContext ctx = ActionContext.getContext();
if (outcome != null) {
ctx.getActionInvocation().setResultCode(outcome);
}
}
}
@@ -0,0 +1,70 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.VariableResolver;
import com.opensymphony.xwork.ActionContext;
/**
* Adds the current Action instance to the variable lookups. All other requests
* delegate to underlying resolver.
*/
public class StrutsVariableResolver extends VariableResolver {
/** The original <code>VariableResolver</code> passed to our constructor. */
private VariableResolver original = null;
/** The variable name of our Struts action */
private static final String STRUTS_VARIABLE_NAME = "action";
/**
* Constructor
*
* @param original
* Original resolver to delegate to.
*/
public StrutsVariableResolver(VariableResolver original) {
this.original = original;
}
/**
* <p>
* Resolve variable names known to this resolver; otherwise, delegate to the
* original resolver passed to our constructor.
* </p>
*
* @param name
* Variable name to be resolved
*/
public Object resolveVariable(FacesContext context, String name)
throws EvaluationException {
if (STRUTS_VARIABLE_NAME.equals(name)) {
return ActionContext.getContext().getActionInvocation().getAction();
} else {
return original.resolveVariable(context, name);
}
}
}
@@ -0,0 +1,70 @@
/*
* $Id: TokenInterceptor.java 394468 2006-04-16 12:16:03Z tmjee $
*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.struts.action2.jsf;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
/**
* Updates the model values from the component tree
*/
public class UpdateModelValuesInterceptor extends FacesInterceptor {
private static final long serialVersionUID = 4011504235094251077L;
/**
* Update Model Values (JSF.2.2.4)
*
* @param viewId The view id
* @param facesContext The faces context
* @return true, if response is complete
*/
protected boolean executePhase(String viewId, FacesContext facesContext) throws FacesException
{
boolean skipFurtherProcessing = false;
if (log.isTraceEnabled()) log.trace("entering updateModelValues");
informPhaseListenersBefore(facesContext, PhaseId.UPDATE_MODEL_VALUES);
if(isResponseComplete(facesContext, "updateModelValues", true))
{
// have to return right away
return true;
}
if(shouldRenderResponse(facesContext, "updateModelValues", true))
{
skipFurtherProcessing = true;
}
facesContext.getViewRoot().processUpdates(facesContext);
informPhaseListenersAfter(facesContext, PhaseId.UPDATE_MODEL_VALUES);
if (isResponseComplete(facesContext, "updateModelValues", false)
|| shouldRenderResponse(facesContext, "updateModelValues", false))
{
// since this phase is completed we don't need to return right away even if the response is completed
skipFurtherProcessing = true;
}
if (!skipFurtherProcessing && log.isTraceEnabled()) log.trace("exiting updateModelValues");
return skipFurtherProcessing;
}
}
@@ -0,0 +1,9 @@
<body>
Integrates the JSF library into the Struts Action Framework. The overall approach
splits the JSF lifecycle into an Interceptor stack and Result, delegating
navigation and general controller functions to Struts.
<p>
Most of this code was borrowed from MyFaces, and as such, will need to be synchronized
occassionally with the original code.
</p>
</body>
@@ -13,6 +13,7 @@
<result-type name="velocity" class="org.apache.struts.action2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts.action2.views.xslt.XSLTResult"/>
<result-type name="plaintext" class="org.apache.struts.action2.dispatcher.PlainTextResult" />
<result-type name="jsf" class="org.apache.struts.action2.jsf.FacesResult" />
<!-- third party integration -->
<result-type name="jasper" class="org.apache.struts.action2.views.jasperreports.JasperReportsResult"/>
@@ -44,6 +45,14 @@
<interceptor name="token-session" class="org.apache.struts.action2.interceptor.TokenSessionStoreInterceptor"/>
<interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
<interceptor name="workflow" class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor"/>
<!-- JSF interceptors, one per lifecycle phase -->
<interceptor class="org.apache.struts.action2.jsf.FacesSetupInterceptor" name="jsfSetup" />
<interceptor class="org.apache.struts.action2.jsf.RestoreViewInterceptor" name="jsfRestore" />
<interceptor class="org.apache.struts.action2.jsf.ApplyRequestValuesInterceptor" name="jsfApply" />
<interceptor class="org.apache.struts.action2.jsf.ProcessValidationsInterceptor" name="jsfValidate" />
<interceptor class="org.apache.struts.action2.jsf.UpdateModelValuesInterceptor" name="jsfUpdate" />
<interceptor class="org.apache.struts.action2.jsf.InvokeApplicationInterceptor" name="jsfInvoke" />
<!-- Basic stack -->
<interceptor-stack name="basicStack">
@@ -176,6 +185,16 @@
<interceptor-ref name="execAndWait">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
</interceptor-stack>
<!-- Sample JSF stack, can be combined with other stacks easily -->
<interceptor-stack name="jsfStack">
<interceptor-ref name="jsfSetup" />
<interceptor-ref name="jsfRestore" />
<interceptor-ref name="jsfApply" />
<interceptor-ref name="jsfValidate" />
<interceptor-ref name="jsfUpdate" />
<interceptor-ref name="jsfInvoke" />
</interceptor-stack>
</interceptors>