From 5d158abce7ca264e890c93ef85e4151a9062c7e2 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 14 Sep 2010 12:26:24 +0000 Subject: [PATCH] SWF-1375 Add element, JsfFlowHandlerAdapter, and provide useful error message when FacesContext is null. --- .../faces/config/FacesNamespaceHandler.java | 1 + .../config/ResourcesBeanDefinitionParser.java | 81 +++++++++++++++++++ .../faces/config/spring-faces-2.0.xsd | 21 +++++ .../faces/webflow/JsfFlowHandlerAdapter.java | 42 ++++++++++ ...er.java => JsfResourceRequestHandler.java} | 2 +- .../faces/webflow/JsfViewFactory.java | 6 ++ .../ResourcesBeanDefinitionParserTests.java | 39 +++++++++ .../faces/config/resources.xml | 11 +++ .../webflow/JsfFlowHandlerAdapterTests.java | 47 +++++++++++ .../webapp/WEB-INF/config/webflow-config.xml | 13 ++- .../webapp/WEB-INF/config/webmvc-config.xml | 22 ++--- 11 files changed, 260 insertions(+), 25 deletions(-) create mode 100644 spring-faces/src/main/java/org/springframework/faces/config/ResourcesBeanDefinitionParser.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java rename spring-faces/src/main/java/org/springframework/faces/webflow/{FacesJsfResourceRequestHandler.java => JsfResourceRequestHandler.java} (94%) create mode 100644 spring-faces/src/test/java/org/springframework/faces/config/ResourcesBeanDefinitionParserTests.java create mode 100644 spring-faces/src/test/java/org/springframework/faces/config/resources.xml create mode 100644 spring-faces/src/test/java/org/springframework/faces/webflow/JsfFlowHandlerAdapterTests.java diff --git a/spring-faces/src/main/java/org/springframework/faces/config/FacesNamespaceHandler.java b/spring-faces/src/main/java/org/springframework/faces/config/FacesNamespaceHandler.java index 08d9ae58..007cc904 100644 --- a/spring-faces/src/main/java/org/springframework/faces/config/FacesNamespaceHandler.java +++ b/spring-faces/src/main/java/org/springframework/faces/config/FacesNamespaceHandler.java @@ -25,5 +25,6 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class FacesNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("flow-builder-services", new FacesFlowBuilderServicesBeanDefinitionParser()); + registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); } } diff --git a/spring-faces/src/main/java/org/springframework/faces/config/ResourcesBeanDefinitionParser.java b/spring-faces/src/main/java/org/springframework/faces/config/ResourcesBeanDefinitionParser.java new file mode 100644 index 00000000..f7e43915 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/config/ResourcesBeanDefinitionParser.java @@ -0,0 +1,81 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * 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.springframework.faces.config; + +import java.util.Map; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; +import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; +import org.w3c.dom.Element; + +/** + * Parser for the resources tag. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + */ +public class ResourcesBeanDefinitionParser implements BeanDefinitionParser { + + static final String RESOURCE_HANDLER_BEAN_NAME = "jsfResourceRequestHandler"; + static final String RESOURCE_HANDLER_CLASS_NAME = "org.springframework.faces.webflow.JsfResourceRequestHandler"; + + public BeanDefinition parse(Element element, ParserContext parserContext) { + Object source = parserContext.extractSource(element); + registerHandlerAdapterIfNecessary(parserContext, source); + registerResourceHandler(parserContext, source); + registerHandlerMappings(element, parserContext, source); + return null; + } + + private void registerHandlerMappings(Element element, ParserContext parserContext, Object source) { + Map urlMap = new ManagedMap(); + urlMap.put("/javax.faces.resource/**", RESOURCE_HANDLER_BEAN_NAME); + + RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleUrlHandlerMapping.class); + beanDefinition.setSource(source); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + beanDefinition.getPropertyValues().add("urlMap", urlMap); + + String order = element.getAttribute("order"); + beanDefinition.getPropertyValues().add("order", StringUtils.hasText(order) ? order : 0); + parserContext.getReaderContext().registerWithGeneratedName(beanDefinition); + } + + private void registerResourceHandler(ParserContext parserContext, Object source) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(RESOURCE_HANDLER_CLASS_NAME); + beanDefinition.setSource(source); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + parserContext.getRegistry().registerBeanDefinition(RESOURCE_HANDLER_BEAN_NAME, beanDefinition); + } + + private void registerHandlerAdapterIfNecessary(ParserContext parserContext, Object source) { + if (parserContext.getRegistry().containsBeanDefinition( + "org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter")) { + return; + } + RootBeanDefinition beanDefinition = new RootBeanDefinition(HttpRequestHandlerAdapter.class); + beanDefinition.setSource(source); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + parserContext.getReaderContext().registerWithGeneratedName(beanDefinition); + } + +} \ No newline at end of file diff --git a/spring-faces/src/main/java/org/springframework/faces/config/spring-faces-2.0.xsd b/spring-faces/src/main/java/org/springframework/faces/config/spring-faces-2.0.xsd index 445d1b06..02d1f3dc 100644 --- a/spring-faces/src/main/java/org/springframework/faces/config/spring-faces-2.0.xsd +++ b/spring-faces/src/main/java/org/springframework/faces/config/spring-faces-2.0.xsd @@ -73,4 +73,25 @@ When set to true, changes to a flow definition will be auto-detected and will re + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java new file mode 100644 index 00000000..658e60c5 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright 2004-2010 the original author or authors. + * + * 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.springframework.faces.webflow; + +import org.springframework.js.ajax.AjaxHandler; +import org.springframework.webflow.mvc.servlet.FlowHandlerAdapter; + +/** + * An extension of {@link FlowHandlerAdapter} that replaces the default {@link AjaxHandler} instance with a + * {@link JsfAjaxHandler} assuming JSF 2 is the runtime environment. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + */ +public class JsfFlowHandlerAdapter extends FlowHandlerAdapter { + + public void afterPropertiesSet() throws Exception { + boolean initializeAjaxHandler = getAjaxHandler() == null; + super.afterPropertiesSet(); + if (initializeAjaxHandler) { + if (JsfRuntimeInformation.isAtLeastJsf20()) { + JsfAjaxHandler ajaxHandler = new JsfAjaxHandler(); + ajaxHandler.setApplicationContext(getApplicationContext()); + setAjaxHandler(ajaxHandler); + } + } + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FacesJsfResourceRequestHandler.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfResourceRequestHandler.java similarity index 94% rename from spring-faces/src/main/java/org/springframework/faces/webflow/FacesJsfResourceRequestHandler.java rename to spring-faces/src/main/java/org/springframework/faces/webflow/JsfResourceRequestHandler.java index f7c78bed..62ced4ec 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/FacesJsfResourceRequestHandler.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfResourceRequestHandler.java @@ -34,7 +34,7 @@ import org.springframework.web.context.support.WebApplicationObjectSupport; * @author Rossen Stoyanchev * @see ResourceHandler */ -public class FacesJsfResourceRequestHandler extends WebApplicationObjectSupport implements HttpRequestHandler { +public class JsfResourceRequestHandler extends WebApplicationObjectSupport implements HttpRequestHandler { public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java index 1d9a4f45..a5186356 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java @@ -72,6 +72,12 @@ public class JsfViewFactory implements ViewFactory { */ public View getView(RequestContext context) { FacesContext facesContext = FlowFacesContext.getCurrentInstance(); + if (facesContext == null) { + throw new IllegalStateException( + "FacesContext has not been initialized within the current Web Flow request." + + " Check the configuration for your ." + + " For JSF you will need FlowFacesContextLifecycleListener configured as one of its flow execution listeners."); + } if (isAtLeastJsf20()) { facesContext.setCurrentPhaseId(PhaseId.RESTORE_VIEW); } diff --git a/spring-faces/src/test/java/org/springframework/faces/config/ResourcesBeanDefinitionParserTests.java b/spring-faces/src/test/java/org/springframework/faces/config/ResourcesBeanDefinitionParserTests.java new file mode 100644 index 00000000..041018c4 --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/config/ResourcesBeanDefinitionParserTests.java @@ -0,0 +1,39 @@ +package org.springframework.faces.config; + +import java.util.Map; + +import junit.framework.TestCase; + +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.faces.webflow.JsfResourceRequestHandler; +import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; +import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; + +public class ResourcesBeanDefinitionParserTests extends TestCase { + + private ClassPathXmlApplicationContext context; + + public void setUp() throws Exception { + context = new ClassPathXmlApplicationContext("org/springframework/faces/config/resources.xml"); + } + + protected void tearDown() throws Exception { + } + + public void testConfigureDefaults() { + Map map = context.getBeansOfType(HttpRequestHandlerAdapter.class); + assertEquals(1, map.values().size()); + + Object resourceHandler = context.getBean(ResourcesBeanDefinitionParser.RESOURCE_HANDLER_BEAN_NAME); + assertNotNull(resourceHandler); + assertTrue(resourceHandler instanceof JsfResourceRequestHandler); + + map = context.getBeansOfType(SimpleUrlHandlerMapping.class); + assertEquals(1, map.values().size()); + SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) map.values().iterator().next(); + assertEquals(ResourcesBeanDefinitionParser.RESOURCE_HANDLER_BEAN_NAME, handlerMapping.getUrlMap().get( + "/javax.faces.resource/**")); + assertEquals(0, handlerMapping.getOrder()); + } + +} diff --git a/spring-faces/src/test/java/org/springframework/faces/config/resources.xml b/spring-faces/src/test/java/org/springframework/faces/config/resources.xml new file mode 100644 index 00000000..6a76e24b --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/config/resources.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFlowHandlerAdapterTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFlowHandlerAdapterTests.java new file mode 100644 index 00000000..0047bb1b --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFlowHandlerAdapterTests.java @@ -0,0 +1,47 @@ +package org.springframework.faces.webflow; + +import junit.framework.TestCase; + +import org.springframework.js.ajax.AjaxHandler; +import org.springframework.js.ajax.SpringJavascriptAjaxHandler; +import org.springframework.webflow.context.ExternalContext; +import org.springframework.webflow.core.FlowException; +import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.executor.FlowExecutionResult; +import org.springframework.webflow.executor.FlowExecutor; + +public class JsfFlowHandlerAdapterTests extends TestCase { + + private JsfFlowHandlerAdapter handlerAdapter; + + protected void setUp() throws Exception { + handlerAdapter = new JsfFlowHandlerAdapter(); + handlerAdapter.setFlowExecutor(new StubFlowExecutor()); + } + + public void testAjaxHandlerNotProvided() throws Exception { + handlerAdapter.afterPropertiesSet(); + assertNotNull(handlerAdapter.getAjaxHandler()); + assertTrue(handlerAdapter.getAjaxHandler() instanceof JsfAjaxHandler); + } + + public void testAjaxHandlerProvided() throws Exception { + AjaxHandler myAjaxHandler = new SpringJavascriptAjaxHandler(); + handlerAdapter.setAjaxHandler(myAjaxHandler); + handlerAdapter.afterPropertiesSet(); + assertTrue(myAjaxHandler == handlerAdapter.getAjaxHandler()); + } + + private final class StubFlowExecutor implements FlowExecutor { + public FlowExecutionResult resumeExecution(String flowExecutionKey, ExternalContext context) + throws FlowException { + throw new UnsupportedOperationException("Not expected"); + } + + public FlowExecutionResult launchExecution(String flowId, MutableAttributeMap input, ExternalContext context) + throws FlowException { + throw new UnsupportedOperationException("Not expected"); + } + } + +} diff --git a/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webflow-config.xml b/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webflow-config.xml index 876d4388..91474860 100644 --- a/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webflow-config.xml +++ b/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webflow-config.xml @@ -4,17 +4,14 @@ xmlns:webflow="http://www.springframework.org/schema/webflow-config" xmlns:faces="http://www.springframework.org/schema/faces" xsi:schemaLocation=" - http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-2.5.xsd - http://www.springframework.org/schema/webflow-config - http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd - http://www.springframework.org/schema/faces - http://www.springframework.org/schema/faces/spring-faces-2.0.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd + http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd + http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.0.xsd"> - + @@ -29,7 +26,7 @@ - + diff --git a/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webmvc-config.xml b/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webmvc-config.xml index c8c1e6aa..9e0de203 100644 --- a/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webmvc-config.xml +++ b/spring-webflow-samples/booking-faces/src/main/webapp/WEB-INF/config/webmvc-config.xml @@ -1,13 +1,12 @@ + xmlns:faces="http://www.springframework.org/schema/faces" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd + http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.0.xsd"> - - - - - + @@ -30,17 +29,8 @@ - + - - - - - - - - - \ No newline at end of file