Author: mrdon Date: Mon Sep 4 23:40:29 2006 New Revision: 440278 URL: http://svn.apache.org/viewvc?view=rev&rev=440278 Log: Enhancing jsf variable resolver to look in stack, added ability to configure jsf from struts.xml, added support for more struts-like navigation for jsf result, updated to new jetty plugin, fixed bad unit test in blank app
WW-1415 WW-1416 WW-1414 WW-1349 --This line, and those below, will be ignored-- M apps/starter/pom.xml M apps/blank/src/test/java/example/WelcomeTest.java M apps/showcase/pom.xml M core/src/main/java/org/apache/struts2/jsf/FacesResult.java M core/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java AM core/src/main/java/org/apache/struts2/jsf/FacesRender.java M core/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java M core/src/main/resources/struts-default.xml Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesRender.java (with props) Modified: struts/struts2/trunk/apps/blank/src/test/java/example/WelcomeTest.java struts/struts2/trunk/apps/showcase/pom.xml struts/struts2/trunk/apps/starter/pom.xml struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesResult.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java struts/struts2/trunk/core/src/main/resources/struts-default.xml Modified: struts/struts2/trunk/apps/blank/src/test/java/example/WelcomeTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/blank/src/test/java/example/WelcomeTest.java?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/apps/blank/src/test/java/example/WelcomeTest.java (original) +++ struts/struts2/trunk/apps/blank/src/test/java/example/WelcomeTest.java Mon Sep 4 23:40:29 2006 @@ -28,9 +28,9 @@ public class WelcomeTest extends TestCase { public void testWelcome() throws Exception { - Action welcome = new Welcome(); + Welcome welcome = new Welcome(); String result = welcome.execute(); - assertTrue(ActionSupport.SUCCESS.equals(result)); - assertTrue(welcome.equals(Welcome.MESSAGE)); + assertEquals(ActionSupport.SUCCESS, result); + assertEquals(welcome.getMessage(), Welcome.MESSAGE); } } Modified: struts/struts2/trunk/apps/showcase/pom.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/pom.xml?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/apps/showcase/pom.xml (original) +++ struts/struts2/trunk/apps/showcase/pom.xml Mon Sep 4 23:40:29 2006 @@ -68,7 +68,8 @@ <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> - <artifactId>maven-jetty6-plugin</artifactId> + <artifactId>maven-jetty-plugin</artifactId> + <version>6.0.0rc3</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> </configuration> Modified: struts/struts2/trunk/apps/starter/pom.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/starter/pom.xml?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/apps/starter/pom.xml (original) +++ struts/struts2/trunk/apps/starter/pom.xml Mon Sep 4 23:40:29 2006 @@ -26,7 +26,8 @@ <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> - <artifactId>maven-jetty6-plugin</artifactId> + <artifactId>maven-jetty-plugin</artifactId> + <version>6.0.0rc3</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> </configuration> Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesRender.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesRender.java?view=auto&rev=440278 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesRender.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesRender.java Mon Sep 4 23:40:29 2006 @@ -0,0 +1,76 @@ +/* + * $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.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"); + } +} Propchange: struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesRender.java ------------------------------------------------------------------------------ svn:executable = * Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesResult.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesResult.java?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesResult.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesResult.java Mon Sep 4 23:40:29 2006 @@ -17,13 +17,11 @@ */ 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.component.UIViewRoot; import javax.faces.context.FacesContext; -import javax.faces.event.PhaseId; + +import org.apache.struts2.dispatcher.StrutsResultSupport; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.Result; @@ -31,58 +29,46 @@ /** * Executes the JSF render phase */ -public class FacesResult extends FacesSupport implements Result { +public class FacesResult extends StrutsResultSupport implements Result { private static final long serialVersionUID = -3548970638740937804L; /** - * Executes the result + * 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(java.lang.String, + * com.opensymphony. */ - public void execute(ActionInvocation invocation) throws Exception { - render(FacesContext.getCurrentInstance()); + protected void doExecute(String finalLocation, ActionInvocation invocation) + throws Exception { + performNavigation(finalLocation, FacesContext.getCurrentInstance()); + new FacesRender().render(FacesContext.getCurrentInstance()); } /** - * Executes the render phase, borrowed from MyFaces + * 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 faces context - * @throws FacesException - * If anything goes wrong + * The FacesContext */ - 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); + 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(); } - } 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"); } } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/FacesSetupInterceptor.java Mon Sep 4 23:40:29 2006 @@ -17,15 +17,30 @@ */ package org.apache.struts2.jsf; +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; + 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 org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsException; +import org.apache.struts2.util.ClassLoaderUtils; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; @@ -34,102 +49,205 @@ import com.opensymphony.xwork2.interceptor.Interceptor; /** - * Initializes the JSF context for this request + * * Initializes the JSF context for this request. + * <p> + * </P> + * The JSF Application can additionaly be configured from the Struts.xml by + * adding <param> tags to the jsfSetup <interceptor-ref>. + * <p> + * </p> + * <b>Example struts.xml configuration:</b> + * + * <pre> + * <interceptor-ref name="jsfSetup"> + * <param name="actionListener"></param> + * <param name="defaultRenderKitId"></param> + * <param name="supportedLocale"></param> + * <param name="defaultLocale"></param> + * <param name="messageBundle"></param> + * <param name="navigationHandler">org.apache.struts2.jsf.StrutsNavigationHandler</param> + * <param name="propertyResolver"></param> + * <param name="stateManager"></param> + * <param name="variableResolver"> + * org.apache.myfaces.el.VariableResolverImpl + * ,org.apache.struts2.jsf.StrutsVariableResolver + * </param> + * <param name="viewHandler;">org.apache.shale.tiles.TilesViewHandler</param> + * </interceptor-ref> + * </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 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); + } - 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() { - try { - facesContextFactory = (FacesContextFactory) FactoryFinder - .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY); - } catch (Exception ex) { - log.debug("Unable to initialize faces", ex); - } - - if (facesContextFactory == null) { - log.info("Unable to initialize jsf interceptors probably due missing JSF framework initialization"); - 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 (!(application.getNavigationHandler() instanceof StrutsNavigationHandler)) { - application.setNavigationHandler(new StrutsNavigationHandler( - application.getNavigationHandler())); - } - 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 { + if (facesContextFactory == null) { + log + .info("Unable to initialize jsf interceptors probably due missing JSF framework initialization"); + 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 && 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(); - } + + FacesContext facesContext = facesContextFactory.getFacesContext( + ServletActionContext.getServletContext(), + ServletActionContext.getRequest(), ServletActionContext + .getResponse(), lifecycle); + + setLifecycle(lifecycle); + + try { + return invocation.invoke(); + } finally { + facesContext.release(); + } } else { return invocation.invoke(); } - } - - /** - * Cleans up the lifecycle and factories - */ - public void destroy() { - facesContextFactory = null; - lifecycle = null; - } - + } + + /** + * 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 + * @param inv + * The action invocation * @return True if the JSF interceptors should fire */ protected boolean isFacesAction(ActionInvocation inv) { @@ -140,7 +258,9 @@ try { resClass = Class.forName(resultConfig.getClassName()); } catch (ClassNotFoundException ex) { - log.warn("Can't find result class, ignoring as a faces request", ex); + log.warn( + "Can't find result class, ignoring as a faces request", + ex); } if (resClass != null) { if (resClass.isAssignableFrom(FacesResult.class)) { @@ -149,5 +269,317 @@ } } 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 = ClassLoaderUtils.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); } } Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/jsf/StrutsVariableResolver.java Mon Sep 4 23:40:29 2006 @@ -21,50 +21,59 @@ import javax.faces.el.EvaluationException; import javax.faces.el.VariableResolver; +import com.opensymphony.xwork2.util.OgnlValueStack; import com.opensymphony.xwork2.ActionContext; /** - * Adds the current Action instance to the variable lookups. All other requests - * delegate to underlying resolver. + * 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 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"; + /** The variable name of our Struts action */ + private static final String STRUTS_VARIABLE_NAME = "action"; - /** - * Constructor - * - * @param original - * Original resolver to delegate to. - */ - public StrutsVariableResolver(VariableResolver original) { - - this.original = original; - - } - - /** - * <p> - * Resolve variable names known to this resolver; otherwise, delegate to the - * original resolver passed to our constructor. - * </p> - * - * @param name - * Variable name to be resolved - */ - public Object resolveVariable(FacesContext context, String name) - throws EvaluationException { - - if (STRUTS_VARIABLE_NAME.equals(name)) { - return ActionContext.getContext().getActionInvocation().getAction(); - } else { - return original.resolveVariable(context, name); - } + /** + * 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); + } - } + } } Modified: struts/struts2/trunk/core/src/main/resources/struts-default.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/struts-default.xml?view=diff&rev=440278&r1=440277&r2=440278 ============================================================================== --- struts/struts2/trunk/core/src/main/resources/struts-default.xml (original) +++ struts/struts2/trunk/core/src/main/resources/struts-default.xml Mon Sep 4 23:40:29 2006 @@ -187,7 +187,10 @@ <!-- Sample JSF stack, can be combined with other stacks easily --> <interceptor-stack name="jsfStack"> - <interceptor-ref name="jsfSetup" /> + <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" />