From a7d7706ec116ee9cd7de16e3be2496469da427c0 Mon Sep 17 00:00:00 2001 From: "Donald J. Brown" Date: Sun, 21 May 2006 04:13:53 +0000 Subject: [PATCH] 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 --- apps/showcase/pom.xml | 11 + .../struts/action2/showcase/jsf/Employee.java | 37 +++ .../action2/showcase/jsf/EmployeeAction.java | 58 +++++ .../action2/showcase/jsf/EmployeeDao.java | 37 +++ .../action2/showcase/jsf/IndexAction.java | 43 ++++ .../main/webapp/WEB-INF/classes/xwork-jsf.xml | 21 ++ .../src/main/webapp/WEB-INF/classes/xwork.xml | 2 + .../main/webapp/WEB-INF/decorators/main.jsp | 1 + apps/showcase/src/main/webapp/WEB-INF/web.xml | 25 +- .../showcase/src/main/webapp/jsf/employee.jsp | 61 +++++ apps/showcase/src/main/webapp/jsf/index.jsp | 68 ++++++ core/pom.xml | 7 + .../jsf/ApplyRequestValuesInterceptor.java | 70 ++++++ .../struts/action2/jsf/FacesInterceptor.java | 90 +++++++ .../struts/action2/jsf/FacesResult.java | 85 +++++++ .../action2/jsf/FacesSetupInterceptor.java | 110 +++++++++ .../struts/action2/jsf/FacesSupport.java | 164 +++++++++++++ .../jsf/InvokeApplicationInterceptor.java | 73 ++++++ .../jsf/ProcessValidationsInterceptor.java | 72 ++++++ .../action2/jsf/RestoreViewInterceptor.java | 221 ++++++++++++++++++ .../action2/jsf/StrutsNavigationHandler.java | 46 ++++ .../action2/jsf/StrutsVariableResolver.java | 70 ++++++ .../jsf/UpdateModelValuesInterceptor.java | 70 ++++++ .../apache/struts/action2/jsf/package.html | 9 + core/src/main/resources/struts-default.xml | 19 ++ 25 files changed, 1468 insertions(+), 2 deletions(-) create mode 100644 apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/Employee.java create mode 100644 apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeAction.java create mode 100644 apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeDao.java create mode 100644 apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/IndexAction.java create mode 100644 apps/showcase/src/main/webapp/WEB-INF/classes/xwork-jsf.xml create mode 100644 apps/showcase/src/main/webapp/jsf/employee.jsp create mode 100644 apps/showcase/src/main/webapp/jsf/index.jsp create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/ApplyRequestValuesInterceptor.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/FacesInterceptor.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/FacesResult.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/FacesSetupInterceptor.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/FacesSupport.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/InvokeApplicationInterceptor.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/ProcessValidationsInterceptor.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/RestoreViewInterceptor.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/StrutsNavigationHandler.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/StrutsVariableResolver.java create mode 100644 core/src/main/java/org/apache/struts/action2/jsf/UpdateModelValuesInterceptor.java create mode 100644 core/src/main/resources/org/apache/struts/action2/jsf/package.html diff --git a/apps/showcase/pom.xml b/apps/showcase/pom.xml index c40f6b6e5..c6704b94c 100644 --- a/apps/showcase/pom.xml +++ b/apps/showcase/pom.xml @@ -83,6 +83,17 @@ 1.2.1 provided + + + myfaces + myfaces-impl + 1.1.2 + + + myfaces + myfaces-api + 1.1.2 + diff --git a/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/Employee.java b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/Employee.java new file mode 100644 index 000000000..147c925bf --- /dev/null +++ b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/Employee.java @@ -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; + } +} diff --git a/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeAction.java b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeAction.java new file mode 100644 index 000000000..52da41b64 --- /dev/null +++ b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeAction.java @@ -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"; + } + +} + diff --git a/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeDao.java b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeDao.java new file mode 100644 index 000000000..a8010fcb0 --- /dev/null +++ b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/EmployeeDao.java @@ -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 employees; + + static { + employees = new ArrayList(); + 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 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; + } +} diff --git a/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/IndexAction.java b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/IndexAction.java new file mode 100644 index 000000000..a30296478 --- /dev/null +++ b/apps/showcase/src/main/java/org/apache/struts/action2/showcase/jsf/IndexAction.java @@ -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; + } + +} + diff --git a/apps/showcase/src/main/webapp/WEB-INF/classes/xwork-jsf.xml b/apps/showcase/src/main/webapp/WEB-INF/classes/xwork-jsf.xml new file mode 100644 index 000000000..ea520e441 --- /dev/null +++ b/apps/showcase/src/main/webapp/WEB-INF/classes/xwork-jsf.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + index + + + + + diff --git a/apps/showcase/src/main/webapp/WEB-INF/classes/xwork.xml b/apps/showcase/src/main/webapp/WEB-INF/classes/xwork.xml index 77788cfe8..5539901fb 100644 --- a/apps/showcase/src/main/webapp/WEB-INF/classes/xwork.xml +++ b/apps/showcase/src/main/webapp/WEB-INF/classes/xwork.xml @@ -23,6 +23,8 @@ + + diff --git a/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp b/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp index 14e0d5ba6..5dfd70262 100644 --- a/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp +++ b/apps/showcase/src/main/webapp/WEB-INF/decorators/main.jsp @@ -75,6 +75,7 @@
  • ">Execute & Wait
  • ">Token
  • ">File Download
  • +
  • ">JSF
  • ">Help
  • diff --git a/apps/showcase/src/main/webapp/WEB-INF/web.xml b/apps/showcase/src/main/webapp/WEB-INF/web.xml index f60362634..2f46b0c1c 100644 --- a/apps/showcase/src/main/webapp/WEB-INF/web.xml +++ b/apps/showcase/src/main/webapp/WEB-INF/web.xml @@ -4,6 +4,7 @@ WebWork Showcase Application + action2-cleanup org.apache.struts.action2.dispatcher.ActionContextCleanUp @@ -18,6 +19,8 @@ sitemesh com.opensymphony.module.sitemesh.filter.PageFilter + + action2-cleanup @@ -38,8 +41,13 @@ org.springframework.web.context.ContextLoaderListener - - + + + + org.apache.myfaces.webapp.StartupServletContextListener + + + @@ -50,6 +58,19 @@ true + + + + faces + javax.faces.webapp.FacesServlet + 1 + + + + + faces + *.action + dwr diff --git a/apps/showcase/src/main/webapp/jsf/employee.jsp b/apps/showcase/src/main/webapp/jsf/employee.jsp new file mode 100644 index 000000000..91574eb5e --- /dev/null +++ b/apps/showcase/src/main/webapp/jsf/employee.jsp @@ -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" %> + + + + + + + JSF Integration Examples + + + + +

    Modify Employee

    + + + + + + + + + + + + + + + + + +

    + + + +
    + + + + +
    diff --git a/apps/showcase/src/main/webapp/jsf/index.jsp b/apps/showcase/src/main/webapp/jsf/index.jsp new file mode 100644 index 000000000..c4c7f70ac --- /dev/null +++ b/apps/showcase/src/main/webapp/jsf/index.jsp @@ -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" %> + + + + + + + JSF Integration Examples + + + +

    JSF Integration Examples

    +

    + The following pages show how Struts Action 2 and JSF components can work together, + each doing what they do best. +

    + +

    Employee List

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/core/pom.xml b/core/pom.xml index 1787af2c3..46331a534 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -258,6 +258,13 @@ 1.2.6 test + + + myfaces + myfaces-jsf-api + 1.0.9 + provided + diff --git a/core/src/main/java/org/apache/struts/action2/jsf/ApplyRequestValuesInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/ApplyRequestValuesInterceptor.java new file mode 100644 index 000000000..13a936e05 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/ApplyRequestValuesInterceptor.java @@ -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; + } +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/FacesInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/FacesInterceptor.java new file mode 100644 index 000000000..c9f1863c6 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/FacesInterceptor.java @@ -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() { + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/apache/struts/action2/jsf/FacesResult.java b/core/src/main/java/org/apache/struts/action2/jsf/FacesResult.java new file mode 100644 index 000000000..41ff99a35 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/FacesResult.java @@ -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"); + } + +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/FacesSetupInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/FacesSetupInterceptor.java new file mode 100644 index 000000000..f5771191b --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/FacesSetupInterceptor.java @@ -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; + } +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/FacesSupport.java b/core/src/main/java/org/apache/struts/action2/jsf/FacesSupport.java new file mode 100644 index 000000000..842b88617 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/FacesSupport.java @@ -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; + } +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/InvokeApplicationInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/InvokeApplicationInterceptor.java new file mode 100644 index 000000000..732742bf0 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/InvokeApplicationInterceptor.java @@ -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; + } +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/ProcessValidationsInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/ProcessValidationsInterceptor.java new file mode 100644 index 000000000..fb5066242 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/ProcessValidationsInterceptor.java @@ -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; + } +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/RestoreViewInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/RestoreViewInterceptor.java new file mode 100644 index 000000000..e965438d5 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/RestoreViewInterceptor.java @@ -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. + *

    + * 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. + *

    + * 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; + } +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/StrutsNavigationHandler.java b/core/src/main/java/org/apache/struts/action2/jsf/StrutsNavigationHandler.java new file mode 100644 index 000000000..6a8196619 --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/StrutsNavigationHandler.java @@ -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); + } + } + +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/StrutsVariableResolver.java b/core/src/main/java/org/apache/struts/action2/jsf/StrutsVariableResolver.java new file mode 100644 index 000000000..7b9dcf7bd --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/StrutsVariableResolver.java @@ -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 VariableResolver 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; + + } + + /** + *

    + * Resolve variable names known to this resolver; otherwise, delegate to the + * original resolver passed to our constructor. + *

    + * + * @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); + } + + } + +} diff --git a/core/src/main/java/org/apache/struts/action2/jsf/UpdateModelValuesInterceptor.java b/core/src/main/java/org/apache/struts/action2/jsf/UpdateModelValuesInterceptor.java new file mode 100644 index 000000000..537cb580f --- /dev/null +++ b/core/src/main/java/org/apache/struts/action2/jsf/UpdateModelValuesInterceptor.java @@ -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; + } +} diff --git a/core/src/main/resources/org/apache/struts/action2/jsf/package.html b/core/src/main/resources/org/apache/struts/action2/jsf/package.html new file mode 100644 index 000000000..7576b8635 --- /dev/null +++ b/core/src/main/resources/org/apache/struts/action2/jsf/package.html @@ -0,0 +1,9 @@ + +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. +

    + Most of this code was borrowed from MyFaces, and as such, will need to be synchronized + occassionally with the original code. +

    + diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 609a9b388..02385ccbf 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -13,6 +13,7 @@ + @@ -44,6 +45,14 @@ + + + + + + + + @@ -176,6 +185,16 @@ input,back,cancel + + + + + + + + + +