Add 'plugins/struts2-jsf-plugin/' from commit '8fb3c8e63e8bb2e7cc46cdefd2a51b807500dc50'

git-subtree-dir: plugins/struts2-jsf-plugin
git-subtree-mainline: 26af44ad43d22929ce693e1ec400fbc914c43003
git-subtree-split: 8fb3c8e63e8bb2e7cc46cdefd2a51b807500dc50
diff --git a/plugins/struts2-jsf-plugin/pom.xml b/plugins/struts2-jsf-plugin/pom.xml
new file mode 100644
index 0000000..85d6f4f
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.struts</groupId>
+        <artifactId>struts2-plugins</artifactId>
+        <version>2.5-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>struts2-jsf-plugin</artifactId>
+    <packaging>jar</packaging>
+    <name>Struts 2 JSF Plugin</name>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>myfaces</groupId>
+            <artifactId>myfaces-jsf-api</artifactId>
+            <version>1.0.9</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>mockobjects</groupId>
+            <artifactId>mockobjects-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <properties>
+    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+</project>
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/ApplyRequestValuesInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/ApplyRequestValuesInterceptor.java
new file mode 100644
index 0000000..0b6d3ab
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/ApplyRequestValuesInterceptor.java
@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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);
+
+        try {
+            if (isResponseComplete(facesContext, "applyRequestValues", true)) {
+                // have to return right away
+                return true;
+            }
+            if (shouldRenderResponse(facesContext, "applyRequestValues", true)) {
+                skipFurtherProcessing = true;
+            }
+
+            facesContext.getViewRoot().processDecodes(facesContext);
+        } finally {
+            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/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesInterceptor.java
new file mode 100644
index 0000000..b3ab48d
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesInterceptor.java
@@ -0,0 +1,110 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.jsf;
+
+import javax.faces.context.FacesContext;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.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 {
+
+        if (isFacesEnabled(invocation.getInvocationContext())) {
+            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();
+                    }
+                }
+            }
+        } 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() {
+    }
+
+    /**
+     * Determines whether to process this request with the JSF phases
+     *
+     * @param ctx The current action context
+     * @return True if it is a faces-enabled request
+     */
+    protected boolean isFacesEnabled(ActionContext ctx) {
+        return ctx.get(FACES_ENABLED) != null;
+    }
+
+}
\ No newline at end of file
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesRender.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesRender.java
new file mode 100755
index 0000000..c9ce35f
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesRender.java
@@ -0,0 +1,80 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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;
+
+/**
+ * Performs the JSF render lifecycle phase.
+ *
+ */
+public class FacesRender extends FacesSupport {
+
+    /**
+     * 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);
+        try {
+            // 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);
+            }
+        } finally {
+            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/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesResult.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesResult.java
new file mode 100644
index 0000000..1f3ec6a
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesResult.java
@@ -0,0 +1,84 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.jsf;
+
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+import org.apache.struts2.dispatcher.StrutsResultSupport;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Result;
+
+/**
+ * Executes the JSF render phase
+ */
+public class FacesResult extends StrutsResultSupport implements Result {
+
+    private static final long serialVersionUID = -3548970638740937804L;
+
+    public FacesResult() {
+        super();
+    }
+
+    public FacesResult(String location) {
+        super(location);
+    }
+    /**
+     * Checks to see if we need to build a new JSF ViewId from the Struts Result
+     * config and then renders the result by delegating to the
+     * FacesRender.render().
+     *
+     * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(String, ActionInvocation)
+     */
+    protected void doExecute(String finalLocation, ActionInvocation invocation)
+            throws Exception {
+        performNavigation(finalLocation, FacesContext.getCurrentInstance());
+        new FacesRender().render(FacesContext.getCurrentInstance());
+    }
+
+    /**
+     * Compares the Struts Result uri to the faces viewId. If they are different
+     * use the Struts uri to build a new faces viewId.
+     *
+     * @param finalLocation
+     *            The result uri
+     * @param facesContext
+     *            The FacesContext
+     */
+    private void performNavigation(String finalLocation,
+            FacesContext facesContext) {
+        String facesViewId = facesContext.getViewRoot().getViewId();
+        if (finalLocation != null) {
+            if (finalLocation.equals(facesViewId) == false) {
+                ViewHandler viewHandler = facesContext.getApplication()
+                        .getViewHandler();
+                UIViewRoot viewRoot = viewHandler.createView(facesContext,
+                        finalLocation);
+                facesContext.setViewRoot(viewRoot);
+                facesContext.renderResponse();
+            }
+        }
+    }
+
+}
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java
new file mode 100644
index 0000000..1f67e0e
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java
@@ -0,0 +1,589 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.jsf;
+
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.config.entities.ResultConfig;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+import com.opensymphony.xwork2.util.ClassLoaderUtil;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.StrutsException;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.StateManager;
+import javax.faces.application.ViewHandler;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.el.PropertyResolver;
+import javax.faces.el.VariableResolver;
+import javax.faces.event.ActionListener;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * * Initializes the JSF context for this request.
+ * <p>
+ * </P>
+ * The JSF Application can additionaly be configured from the Struts.xml by
+ * adding &lt;param&gt; tags to the jsfSetup &lt;interceptor-ref&gt;.
+ * <p>
+ * </p>
+ * <b>Example struts.xml configuration:</b>
+ *
+ * <pre>
+ *   &lt;interceptor-ref name=&quot;jsfSetup&quot;&gt;
+ *       &lt;param name=&quot;actionListener&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;defaultRenderKitId&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;supportedLocale&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;defaultLocale&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;messageBundle&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;navigationHandler&quot;&gt;org.apache.struts2.jsf.StrutsNavigationHandler&lt;/param&gt;
+ *       &lt;param name=&quot;propertyResolver&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;stateManager&quot;&gt;&lt;/param&gt;
+ *       &lt;param name=&quot;variableResolver&quot;&gt;
+ *           org.apache.myfaces.el.VariableResolverImpl
+ *          ,org.apache.struts2.jsf.StrutsVariableResolver
+ *       &lt;/param&gt;
+ *       &lt;param name=&quot;viewHandler;&quot;&gt;org.apache.shale.tiles.TilesViewHandler&lt;/param&gt;
+ *   &lt;/interceptor-ref&gt;
+ * </pre>
+ *
+ * <p>
+ * </p>
+ * <b>Note: None of the parameters are required but all are shown in the example
+ * for completeness.</b>
+ */
+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;
+
+    // jsf Application configuration
+    private List<String> actionListener;
+
+    private String defaultRenderKitId;
+
+    private List<String> supportedLocale;
+
+    private String defaultLocale;
+
+    private String messageBundle;
+
+    private List<String> navigationHandler;
+
+    private List<String> propertyResolver;
+
+    private List<String> stateManager;
+
+    private List<String> variableResolver;
+
+    private List<String> viewHandler;
+
+    /**
+     * Sets the lifecycle id
+     *
+     * @param id
+     *            The id
+     */
+    public void setLifecycleId(String id) {
+        this.lifecycleId = id;
+    }
+
+    /**
+     * Initializes the lifecycle and factories
+     */
+    public void init() {
+        try {
+            facesContextFactory = (FacesContextFactory) FactoryFinder
+                    .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+        } catch (Exception ex) {
+            log.debug("Unable to initialize faces", ex);
+            return;
+        }
+
+        // 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 (actionListener != null) {
+            Iterator i = actionListener.iterator();
+            application
+                    .setActionListener((ActionListener) getApplicationObject(
+                            ActionListener.class, i, application
+                                    .getActionListener()));
+        }
+        if (defaultRenderKitId != null && defaultRenderKitId.length() > 0) {
+            application.setDefaultRenderKitId(defaultRenderKitId);
+        }
+        if (messageBundle != null && messageBundle.length() > 0) {
+            application.setMessageBundle(messageBundle);
+        }
+        if (supportedLocale != null) {
+            List<Locale> locales = new ArrayList<Locale>();
+            for (Iterator i = supportedLocale.iterator(); i.hasNext();) {
+                locales.add(toLocale((String) i.next()));
+            }
+            application.setSupportedLocales(locales);
+        }
+        if (defaultLocale != null && defaultLocale.length() > 0) {
+            application.setDefaultLocale(toLocale(defaultLocale));
+        }
+        if (navigationHandler != null) {
+            Iterator i = navigationHandler.iterator();
+            application
+                    .setNavigationHandler((NavigationHandler) getApplicationObject(
+                            NavigationHandler.class, i, application
+                                    .getNavigationHandler()));
+        }
+        if (propertyResolver != null) {
+            Iterator i = propertyResolver.iterator();
+            application
+                    .setPropertyResolver((PropertyResolver) getApplicationObject(
+                            PropertyResolver.class, i, application
+                                    .getPropertyResolver()));
+        }
+        if (stateManager != null) {
+            Iterator i = stateManager.iterator();
+            application.setStateManager((StateManager) getApplicationObject(
+                    StateManager.class, i, application.getStateManager()));
+        }
+        if (variableResolver != null) {
+            Iterator i = variableResolver.iterator();
+            application
+                    .setVariableResolver((VariableResolver) getApplicationObject(
+                            VariableResolver.class, i, application
+                                    .getVariableResolver()));
+        }
+        if (viewHandler != null) {
+            Iterator i = viewHandler.iterator();
+            application.setViewHandler((ViewHandler) getApplicationObject(
+                    ViewHandler.class, i, application.getViewHandler()));
+        }
+    }
+
+    /**
+     * Creates the faces context for other phases.
+     *
+     * @param invocation
+     *            The action invocation
+     */
+    public String intercept(ActionInvocation invocation) throws Exception {
+        if (facesContextFactory != null) {
+            if (isFacesAction(invocation)) {
+
+                invocation.getInvocationContext().put(
+                        FacesInterceptor.FACES_ENABLED, Boolean.TRUE);
+
+                FacesContext facesContext = facesContextFactory
+                        .getFacesContext(ServletActionContext
+                                .getServletContext(), ServletActionContext
+                                .getRequest(), ServletActionContext
+                                .getResponse(), lifecycle);
+
+                setLifecycle(lifecycle);
+
+                try {
+                    return invocation.invoke();
+                } finally {
+                    facesContext.release();
+                }
+            }
+        } else {
+            throw new StrutsException(
+                    "Unable to initialize jsf interceptors probably due missing JSF implementation libraries",
+                    invocation.getProxy().getConfig());
+        }
+        return invocation.invoke();
+    }
+
+    /**
+     * Cleans up the lifecycle and factories
+     */
+    public void destroy() {
+        facesContextFactory = null;
+        lifecycle = null;
+    }
+
+    /**
+     * Determines if this action mapping will be have a JSF view
+     *
+     * @param inv
+     *            The action invocation
+     * @return True if the JSF interceptors should fire
+     */
+    protected boolean isFacesAction(ActionInvocation inv) {
+        ActionConfig config = inv.getProxy().getConfig();
+        if (config != null) {
+            ResultConfig resultConfig = config.getResults().get(Action.SUCCESS);
+            Class resClass = null;
+            try {
+                resClass = Class.forName(resultConfig.getClassName());
+            } catch (ClassNotFoundException ex) {
+                log.warn(
+                        "Can't find result class, ignoring as a faces request",
+                        ex);
+            }
+            if (resClass != null) {
+                if (resClass.isAssignableFrom(FacesResult.class)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Constructs an object from a list of class names. This method supports
+     * creating the objects using constructor delegation, if the requested class
+     * supports it. Classes will be imbedded from top to bottom in the list with
+     * the last class listed being the one that will be returned.
+     *
+     * @param interfaceClass
+     *            The Class type that is expected to be returned
+     * @param classNamesIterator
+     *            An Iterator for a list of Strings that represent the class
+     *            names
+     * @param defaultObject
+     *            The current Object that the jsf Application has set
+     * @return
+     */
+    private Object getApplicationObject(Class interfaceClass,
+            Iterator classNamesIterator, Object defaultObject) {
+        Object current = defaultObject;
+
+        while (classNamesIterator.hasNext()) {
+            String implClassName = (String) classNamesIterator.next();
+            Class implClass = null;
+
+            try {
+                implClass = ClassLoaderUtil.loadClass(implClassName, this
+                        .getClass());
+            } catch (ClassNotFoundException e1) {
+                throw new IllegalArgumentException("Class " + implClassName
+                        + " was not found.");
+            }
+
+            // check, if class is of expected interface type
+            if (!interfaceClass.isAssignableFrom(implClass)) {
+                throw new IllegalArgumentException("Class " + implClassName
+                        + " is no " + interfaceClass.getName());
+            }
+
+            if (current == null) {
+                // nothing to decorate
+                try {
+                    current = implClass.newInstance();
+                } catch (InstantiationException e) {
+                    log.error(e.getMessage(), e);
+                    throw new StrutsException(e);
+                } catch (IllegalAccessException e) {
+                    log.error(e.getMessage(), e);
+                    throw new StrutsException(e);
+                }
+            } else {
+                // let's check if class supports the decorator pattern
+                try {
+                    Constructor delegationConstructor = implClass
+                            .getConstructor(new Class[] { interfaceClass });
+                    // impl class supports decorator pattern,
+                    try {
+                        // create new decorator wrapping current
+                        current = delegationConstructor
+                                .newInstance(new Object[] { current });
+                    } catch (InstantiationException e) {
+                        log.error(e.getMessage(), e);
+                        throw new StrutsException(e);
+                    } catch (IllegalAccessException e) {
+                        log.error(e.getMessage(), e);
+                        throw new StrutsException(e);
+                    } catch (InvocationTargetException e) {
+                        log.error(e.getMessage(), e);
+                        throw new StrutsException(e);
+                    }
+                } catch (NoSuchMethodException e) {
+                    // no decorator pattern support
+                    try {
+                        current = implClass.newInstance();
+                    } catch (InstantiationException e1) {
+                        log.error(e.getMessage(), e);
+                        throw new StrutsException(e);
+                    } catch (IllegalAccessException e1) {
+                        log.error(e.getMessage(), e);
+                        throw new StrutsException(e);
+                    }
+                }
+            }
+        }
+
+        return current;
+    }
+
+    /**
+     * Takes a comma delimited string of class names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param actionListener
+     *            A comma delimited string of class names
+     */
+    public void setActionListener(String actionListener) {
+        if (this.actionListener == null) {
+            this.actionListener = new ArrayList<String>();
+        }
+        String clean = actionListener.replaceAll("[ \t\r\n]", "");
+        String[] actionListenerNames = clean.split(",");
+
+        for (int i = 0; i < actionListenerNames.length; i++) {
+            this.actionListener.add(actionListenerNames[i]);
+        }
+    }
+
+    /**
+     * A <code>String</code> to be used as the defaultRenderKitId for the jsf
+     * application. The incoming <code>String</code> will be cleaned of
+     * whitespace characters.
+     *
+     * @param defaultRenderKitId
+     *            The defaultRenderKitId
+     */
+    public void setDefaultRenderKitId(String defaultRenderKitId) {
+        String clean = defaultRenderKitId.replaceAll("[ \t\r\n]", "");
+        this.defaultRenderKitId = clean;
+    }
+
+    /**
+     * Takes a comma delimited string of local names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param supportedLocale
+     *            A comma delimited string of local names
+     */
+    public void setSupportedLocale(String supportedLocale) {
+        if (this.supportedLocale == null) {
+            this.supportedLocale = new ArrayList<String>();
+        }
+        String clean = supportedLocale.replaceAll("[ \t\r\n]", "");
+        String[] supportedLocaleNames = clean.split(",");
+
+        for (int i = 0; i < supportedLocaleNames.length; i++) {
+            this.supportedLocale.add(supportedLocaleNames[i]);
+        }
+    }
+
+    /**
+     * Stores a String representation of the defaultLocale. The incoming
+     * <code>String</code> will be cleaned of any whitespace characters before
+     * the class names are stored.
+     *
+     * @param defaultLocale
+     *            The default local
+     */
+    public void setDefaultLocale(String defaultLocale) {
+        String clean = defaultLocale.replaceAll("[ \t\r\n]", "");
+        this.defaultLocale = clean;
+    }
+
+    /**
+     * Stores the messageBundle to be used to configure the jsf Application.
+     *
+     * @param messageBundle
+     *            The messageBundle
+     */
+    public void setMessageBundle(String messageBundle) {
+        String clean = messageBundle.replaceAll("[ \t\r\n]", "");
+        this.messageBundle = clean;
+    }
+
+    /**
+     * Takes a comma delimited string of class names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param navigationHandlerName
+     *            A comma delimited string of class names
+     */
+    public void setNavigationHandler(String navigationHandlerName) {
+        if (navigationHandler == null) {
+            navigationHandler = new ArrayList<String>();
+        }
+        String clean = navigationHandlerName.replaceAll("[ \t\r\n]", "");
+        String[] navigationHandlerNames = clean.split(",");
+
+        for (int i = 0; i < navigationHandlerNames.length; i++) {
+            navigationHandler.add(navigationHandlerNames[i]);
+        }
+    }
+
+    /**
+     * Takes a comma delimited string of class names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param propertyResolverName
+     *            A comma delimited string of class names
+     */
+    public void setPropertyResolver(String propertyResolverName) {
+        if (propertyResolver == null) {
+            propertyResolver = new ArrayList<String>();
+        }
+        String clean = propertyResolverName.replaceAll("[ \t\r\n]", "");
+        String[] propertyResolverNames = clean.split(",");
+
+        for (int i = 0; i < propertyResolverNames.length; i++) {
+            propertyResolver.add(propertyResolverNames[i]);
+        }
+    }
+
+    /**
+     * Takes a comma delimited string of class names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param stateManagerName
+     *            A comma delimited string of class names
+     */
+    public void setStateManager(String stateManagerName) {
+        if (stateManager == null) {
+            stateManager = new ArrayList<String>();
+        }
+        String clean = stateManagerName.replaceAll("[ \t\r\n]", "");
+        String[] stateManagerNames = clean.split(",");
+
+        for (int i = 0; i < stateManagerNames.length; i++) {
+            stateManager.add(stateManagerNames[i]);
+        }
+    }
+
+    /**
+     * Takes a comma delimited string of class names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param variableResolverName
+     *            A comma delimited string of class names
+     */
+    public void setVariableResolver(String variableResolverName) {
+        if (variableResolver == null) {
+            variableResolver = new ArrayList<String>();
+        }
+        String clean = variableResolverName.replaceAll("[ \t\r\n]", "");
+        String[] variableResolverNames = clean.split(",");
+
+        for (int i = 0; i < variableResolverNames.length; i++) {
+            variableResolver.add(variableResolverNames[i]);
+        }
+    }
+
+    /**
+     * Takes a comma delimited string of class names and stores the names in an
+     * <code>ArrayList</code>. The incoming <code>String</code> will be
+     * cleaned of any whitespace characters before the class names are stored.
+     *
+     * @param viewHandlerName
+     *            A comma delimited string of class names
+     */
+    public void setViewHandler(String viewHandlerName) {
+        if (viewHandler == null) {
+            viewHandler = new ArrayList<String>();
+        }
+        String[] viewHandlerNames = viewHandlerName
+                .split(",^[ \t\r\n]+|[ \t\r\n]+$");
+
+        for (int i = 0; i < viewHandlerNames.length; i++) {
+            viewHandler.add(viewHandlerNames[i]);
+        }
+    }
+
+    /**
+     * Converts a locale string to <code>Locale</code> class. Accepts both '_'
+     * and '-' as separators for locale components.
+     *
+     * @param localeString
+     *            string representation of a locale
+     * @return Locale instance, compatible with the string representation
+     */
+    private Locale toLocale(String localeString) {
+        if ((localeString == null) || (localeString.length() == 0)) {
+            Locale locale = Locale.getDefault();
+            if (log.isWarnEnabled())
+                log
+                        .warn("Locale name in faces-config.xml null or empty, setting locale to default locale : "
+                                + locale.toString());
+            return locale;
+        }
+
+        int separatorCountry = localeString.indexOf('_');
+        char separator;
+        if (separatorCountry >= 0) {
+            separator = '_';
+        } else {
+            separatorCountry = localeString.indexOf('-');
+            separator = '-';
+        }
+
+        String language, country, variant;
+        if (separatorCountry < 0) {
+            language = localeString;
+            country = variant = "";
+        } else {
+            language = localeString.substring(0, separatorCountry);
+
+            int separatorVariant = localeString.indexOf(separator,
+                    separatorCountry + 1);
+            if (separatorVariant < 0) {
+                country = localeString.substring(separatorCountry + 1);
+                variant = "";
+            } else {
+                country = localeString.substring(separatorCountry + 1,
+                        separatorVariant);
+                variant = localeString.substring(separatorVariant + 1);
+            }
+        }
+
+        return new Locale(language, country, variant);
+    }
+}
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesSupport.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesSupport.java
new file mode 100644
index 0000000..aecfe92
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/FacesSupport.java
@@ -0,0 +1,173 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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 com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * 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 Logger log;
+
+    /**
+     * Marker key for the ActionContext to dictate whether to treat the request
+     * as a JSF faces request and therefore process the Faces phases
+     */
+    protected static final String FACES_ENABLED = "facesEnabled";
+
+    /** Initializes log instance for the instance object */
+    protected FacesSupport() {
+        log = LoggerFactory.getLogger(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/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/InvokeApplicationInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/InvokeApplicationInterceptor.java
new file mode 100644
index 0000000..857a03f
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/InvokeApplicationInterceptor.java
@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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);
+
+        try {
+            if (isResponseComplete(facesContext, "invokeApplication", true)) {
+                // have to return right away
+                return true;
+            }
+            if (shouldRenderResponse(facesContext, "invokeApplication", true)) {
+                skipFurtherProcessing = true;
+            }
+
+            facesContext.getViewRoot().processApplication(facesContext);
+        } finally {
+            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/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/ProcessValidationsInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/ProcessValidationsInterceptor.java
new file mode 100644
index 0000000..3d99b98
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/ProcessValidationsInterceptor.java
@@ -0,0 +1,78 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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);
+
+        try {
+                if (isResponseComplete(facesContext, "processValidations", true)) {
+                    // have to return right away
+                    return true;
+                }
+                if (shouldRenderResponse(facesContext, "processValidations", true)) {
+                    skipFurtherProcessing = true;
+                }
+
+                facesContext.getViewRoot().processValidators(facesContext);
+        } finally {
+            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/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/RestoreViewInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/RestoreViewInterceptor.java
new file mode 100644
index 0000000..7d86e5e
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/RestoreViewInterceptor.java
@@ -0,0 +1,229 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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);
+
+        try {
+            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);
+        } finally {
+            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;
+    }
+}
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/StrutsNavigationHandler.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/StrutsNavigationHandler.java
new file mode 100644
index 0000000..b0e4f03
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/StrutsNavigationHandler.java
@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.jsf;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.config.entities.ResultConfig;
+
+import javax.faces.application.NavigationHandler;
+import javax.faces.context.FacesContext;
+import java.util.Map;
+
+/**
+ * Overrides the JFS navigation by delegating the result to handling by the core
+ * result code lookup and execution.  If a result cannot be found, the previous
+ * NavigationHandler is called.
+ */
+public class StrutsNavigationHandler extends NavigationHandler {
+
+    private NavigationHandler parent;
+
+    /**
+     * Creates the handler
+     *
+     * @param handler The old NavigationHandler to possibly delegate to
+     */
+    public StrutsNavigationHandler(NavigationHandler handler) {
+        this.parent = handler;
+    }
+
+    /**
+     * Stores any outcomes as the result code, failing over to the old
+     * NavigationHandler
+     *
+     * @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) {
+            if (ctx == null || ctx.getActionInvocation() == null) {
+                delegateToParentNavigation(facesContext, fromAction, outcome);
+            } else {
+                ActionConfig config = ctx.getActionInvocation().getProxy().getConfig();
+                Map results = config.getResults();
+    
+                ResultConfig resultConfig = null;
+    
+                synchronized (config) {
+                    try {
+                        resultConfig = (ResultConfig) results.get(outcome);
+                    } catch (NullPointerException e) {
+                    }
+                    if (resultConfig == null) {
+                        // If no result is found for the given resultCode, try to get a wildcard '*' match.
+                        resultConfig = (ResultConfig) results.get("*");
+                    }
+                }
+                if (resultConfig != null) {
+                    ctx.getActionInvocation().setResultCode(outcome);
+                } else {
+                    delegateToParentNavigation(facesContext, fromAction, outcome);
+                }
+            }
+        }
+    }
+
+    private void delegateToParentNavigation(FacesContext facesContext, String fromAction, String outcome) {
+        // Failing over to parent handler
+        parent.handleNavigation(facesContext, fromAction, outcome);
+    }
+
+}
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java
new file mode 100644
index 0000000..9487005
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java
@@ -0,0 +1,83 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.jsf;
+
+import javax.faces.context.FacesContext;
+import javax.faces.el.EvaluationException;
+import javax.faces.el.VariableResolver;
+
+import com.opensymphony.xwork2.ognl.OgnlValueStack;
+import com.opensymphony.xwork2.ActionContext;
+
+/**
+ * Will return a reference to the current action if the action name matches the
+ * requested variable name. Otherwise it will attempt to resolve the name from
+ * the value stack. Otherwise it will delegate to the original jsf 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>
+     * Will return a reference to the current action if the action name matches
+     * the requested variable name. Otherwise it will attempt to resolve the
+     * name from the value stack. Otherwise it will delegate to the original jsf
+     * resolver.
+     * </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();
+        }
+
+        Object obj = ActionContext.getContext().getValueStack().findValue(name);
+        if (obj != null) {
+            return obj;
+        } else {
+            return original.resolveVariable(context, name);
+        }
+
+    }
+
+}
diff --git a/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/UpdateModelValuesInterceptor.java b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/UpdateModelValuesInterceptor.java
new file mode 100644
index 0000000..655a6b7
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/java/org/apache/struts2/jsf/UpdateModelValuesInterceptor.java
@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.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);
+
+        try {
+            if (isResponseComplete(facesContext, "updateModelValues", true)) {
+                // have to return right away
+                return true;
+            }
+            if (shouldRenderResponse(facesContext, "updateModelValues", true)) {
+                skipFurtherProcessing = true;
+            }
+
+            facesContext.getViewRoot().processUpdates(facesContext);
+        } finally {
+            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/plugins/struts2-jsf-plugin/src/main/resources/LICENSE.txt b/plugins/struts2-jsf-plugin/src/main/resources/LICENSE.txt
new file mode 100644
index 0000000..dd5b3a5
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/resources/LICENSE.txt
@@ -0,0 +1,174 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/plugins/struts2-jsf-plugin/src/main/resources/NOTICE.txt b/plugins/struts2-jsf-plugin/src/main/resources/NOTICE.txt
new file mode 100644
index 0000000..bfba90c
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/resources/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Struts
+Copyright 2000-2011 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
\ No newline at end of file
diff --git a/plugins/struts2-jsf-plugin/src/main/resources/org/apache/struts2/jsf/package.html b/plugins/struts2-jsf-plugin/src/main/resources/org/apache/struts2/jsf/package.html
new file mode 100644
index 0000000..771f43d
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/resources/org/apache/struts2/jsf/package.html
@@ -0,0 +1,31 @@
+<!--
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+-->
+<body>
+Integrates the JSF library into Struts.  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>
diff --git a/plugins/struts2-jsf-plugin/src/main/resources/struts-plugin.xml b/plugins/struts2-jsf-plugin/src/main/resources/struts-plugin.xml
new file mode 100644
index 0000000..e4c7e64
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/main/resources/struts-plugin.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+-->
+<!DOCTYPE struts PUBLIC
+	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
+	"http://struts.apache.org/dtds/struts-2.3.dtd">
+    
+<struts>
+    <package name="jsf-default" extends="struts-default">
+
+        <result-types>
+            <result-type name="jsf" class="org.apache.struts2.jsf.FacesResult" />
+        </result-types>
+        <interceptors>
+            <interceptor class="org.apache.struts2.jsf.FacesSetupInterceptor" name="jsfSetup" />
+            <interceptor class="org.apache.struts2.jsf.RestoreViewInterceptor" name="jsfRestore" />
+            <interceptor class="org.apache.struts2.jsf.ApplyRequestValuesInterceptor" name="jsfApply" />
+            <interceptor class="org.apache.struts2.jsf.ProcessValidationsInterceptor" name="jsfValidate" />
+            <interceptor class="org.apache.struts2.jsf.UpdateModelValuesInterceptor" name="jsfUpdate" />
+            <interceptor class="org.apache.struts2.jsf.InvokeApplicationInterceptor" name="jsfInvoke" />
+
+            <interceptor-stack name="jsfStack">
+                <interceptor-ref name="jsfSetup">
+                    <param name="variableResolver">org.apache.struts2.jsf.StrutsVariableResolver</param>
+                    <param name="navigationHandler">org.apache.struts2.jsf.StrutsNavigationHandler</param>
+                </interceptor-ref>
+                <interceptor-ref name="jsfRestore" />
+                <interceptor-ref name="jsfApply" />
+                <interceptor-ref name="jsfValidate" />
+                <interceptor-ref name="jsfUpdate" />
+                <interceptor-ref name="jsfInvoke" />
+            </interceptor-stack>
+        </interceptors>
+        
+        <default-interceptor-ref name="jsfStack"/>
+        
+    </package>
+
+</struts>
diff --git a/plugins/struts2-jsf-plugin/src/site/site.xml b/plugins/struts2-jsf-plugin/src/site/site.xml
new file mode 100644
index 0000000..07a667e
--- /dev/null
+++ b/plugins/struts2-jsf-plugin/src/site/site.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+-->
+<project name="Struts 2">
+    <skin>
+        <groupId>org.apache.maven.skins</groupId>
+        <artifactId>maven-fluido-skin</artifactId>
+        <version>1.3.1</version>
+    </skin>
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org/</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Struts</name>
+        <src>http://struts.apache.org/img/struts-logo.svg</src>
+        <href>http://struts.apache.org/</href>
+    </bannerRight>
+    <publishDate position="none"/>
+    <version position="none"/>
+    <body>
+        <links>
+            <item name="Apache" href="http://www.apache.org/"/>
+            <item name="Struts" href="http://struts.apache.org/"/>
+        </links>
+
+        <menu ref="parent"/>
+        <menu ref="reports"/>
+
+        <footer>
+            <div class="row span12">
+                Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts
+                project logos are trademarks of The Apache Software Foundation.
+            </div>
+        </footer>
+
+    </body>
+</project>