diff --git a/spring-js/.classpath b/spring-js/.classpath index 0bf7037c..589976b6 100644 --- a/spring-js/.classpath +++ b/spring-js/.classpath @@ -16,8 +16,12 @@ - + + + + + diff --git a/spring-js/ivy.xml b/spring-js/ivy.xml index e25fe7f9..ad95be3c 100644 --- a/spring-js/ivy.xml +++ b/spring-js/ivy.xml @@ -36,6 +36,8 @@ + + diff --git a/spring-js/src/main/java/org/springframework/js/ajax/tiles2/AjaxTilesView.java b/spring-js/src/main/java/org/springframework/js/ajax/tiles2/AjaxTilesView.java index 4ec1ae50..06210138 100644 --- a/spring-js/src/main/java/org/springframework/js/ajax/tiles2/AjaxTilesView.java +++ b/spring-js/src/main/java/org/springframework/js/ajax/tiles2/AjaxTilesView.java @@ -30,6 +30,7 @@ import org.apache.tiles.Definition; import org.apache.tiles.Attribute.AttributeType; import org.apache.tiles.access.TilesAccess; import org.apache.tiles.context.TilesRequestContext; +import org.apache.tiles.definition.DefinitionsFactoryException; import org.apache.tiles.impl.BasicTilesContainer; import org.springframework.js.ajax.AjaxHandler; import org.springframework.js.ajax.SpringJavascriptAjaxHandler; @@ -73,8 +74,10 @@ public class AjaxTilesView extends TilesView { String[] attrNames = getRenderFragments(model, request, response); if (attrNames.length == 0) { logger.warn("An Ajax request was detected, but no fragments were specified to be re-rendered. " - + "Falling back to full page render."); + + "Falling back to full page render. This can cause unpredictable results when processing " + + "the ajax response on the client."); super.renderMergedOutputModel(model, request, response); + return; } BasicTilesContainer container = (BasicTilesContainer) TilesAccess.getContainer(servletContext); @@ -127,13 +130,17 @@ public class AjaxTilesView extends TilesView { while (i.hasNext()) { Object key = i.next(); Attribute attr = (Attribute) compositeDefinition.getAttributes().get(key); - if (AttributeType.DEFINITION.equals(attr.getType()) || AttributeType.TEMPLATE.equals(attr.getType()) - || attr.getType() == null) { - Definition nestedDefinition = container.getDefinitionsFactory().getDefinition( - attr.getValue().toString(), requestContext); + AttributeType attrType = attr.getType() != null ? attr.getType() : detectType(container, + requestContext, attr); + if (AttributeType.DEFINITION.equals(attrType) || AttributeType.TEMPLATE.equals(attrType)) { resultMap.put(key, attr); - if (nestedDefinition != null && nestedDefinition != compositeDefinition) { - flattenAttributeMap(container, requestContext, resultMap, nestedDefinition, request, response); + if (AttributeType.DEFINITION.equals(attrType)) { + Definition nestedDefinition = container.getDefinitionsFactory().getDefinition( + attr.getValue().toString(), requestContext); + if (nestedDefinition != null && nestedDefinition != compositeDefinition) { + flattenAttributeMap(container, requestContext, resultMap, nestedDefinition, request, + response); + } } } } @@ -148,4 +155,18 @@ public class AjaxTilesView extends TilesView { resultMap.put(key, attr); } } + + private AttributeType detectType(BasicTilesContainer container, TilesRequestContext requestContext, Attribute attr) + throws DefinitionsFactoryException { + if (attr.getValue() instanceof String) { + if (container.getDefinitionsFactory().getDefinition(attr.getValue().toString(), requestContext) != null) { + return AttributeType.DEFINITION; + } else if (attr.getValue().toString().startsWith("/")) { + return AttributeType.TEMPLATE; + } else { + return AttributeType.STRING; + } + } + return AttributeType.OBJECT; + } } diff --git a/spring-js/src/test/java/org/springframework/js/ajax/tiles2/AjaxTilesViewTests.java b/spring-js/src/test/java/org/springframework/js/ajax/tiles2/AjaxTilesViewTests.java new file mode 100644 index 00000000..1b53f7a4 --- /dev/null +++ b/spring-js/src/test/java/org/springframework/js/ajax/tiles2/AjaxTilesViewTests.java @@ -0,0 +1,107 @@ +package org.springframework.js.ajax.tiles2; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.tiles.Attribute; +import org.apache.tiles.Definition; +import org.apache.tiles.access.TilesAccess; +import org.apache.tiles.context.TilesRequestContext; +import org.apache.tiles.impl.BasicTilesContainer; +import org.springframework.js.ajax.SpringJavascriptAjaxHandler; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.support.StaticWebApplicationContext; +import org.springframework.web.servlet.support.RequestContext; +import org.springframework.web.servlet.view.tiles2.TilesConfigurer; + +public class AjaxTilesViewTests extends TestCase { + + private AjaxTilesView ajaxTilesView; + + private MockHttpServletRequest request; + private MockHttpServletResponse response; + private MockServletContext servletContext; + + protected void setUp() throws Exception { + + servletContext = new MockServletContext("/org/springframework/js/ajax/tiles2/"); + request = new MockHttpServletRequest(servletContext); + response = new MockHttpServletResponse(); + + TilesConfigurer tc = new TilesConfigurer(); + tc.setDefinitions(new String[] { "tiles-definitions.xml" }); + tc.setValidateDefinitions(true); + tc.setServletContext(servletContext); + tc.afterPropertiesSet(); + + ajaxTilesView = new AjaxTilesView(); + } + + private void setupStaticWebApplicationContext() { + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.setServletContext(servletContext); + wac.refresh(); + request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); + ajaxTilesView.setApplicationContext(wac); + } + + public void testFullPageRendering() throws Exception { + setupStaticWebApplicationContext(); + ajaxTilesView.setUrl("search"); + ajaxTilesView.renderMergedOutputModel(new HashMap(), request, response); + assertEquals("/WEB-INF/layout.jsp", response.getForwardedUrl()); + } + + public void testAjaxRequestNoFragments() throws Exception { + setupStaticWebApplicationContext(); + request.addHeader("Accept", SpringJavascriptAjaxHandler.AJAX_ACCEPT_CONTENT_TYPE); + ajaxTilesView.setUrl("search"); + ajaxTilesView.renderMergedOutputModel(new HashMap(), request, response); + assertEquals("/WEB-INF/layout.jsp", response.getForwardedUrl()); + } + + public void testRenderFragment_Template() throws Exception { + setupStaticWebApplicationContext(); + request.addHeader("Accept", SpringJavascriptAjaxHandler.AJAX_ACCEPT_CONTENT_TYPE); + request.addParameter("fragments", "searchResults"); + ajaxTilesView.setUrl("search"); + ajaxTilesView.renderMergedOutputModel(new HashMap(), request, response); + assertEquals("/WEB-INF/searchResults.jsp", response.getIncludedUrl()); + } + + public void testRenderFragment_Definition() throws Exception { + setupStaticWebApplicationContext(); + request.addHeader("Accept", SpringJavascriptAjaxHandler.AJAX_ACCEPT_CONTENT_TYPE); + request.addParameter("fragments", "body"); + ajaxTilesView.setUrl("search"); + ajaxTilesView.renderMergedOutputModel(new HashMap(), request, response); + assertEquals("/WEB-INF/search.jsp", response.getIncludedUrl()); + } + + public void testFlattenAttributeMap() throws Exception { + BasicTilesContainer container = (BasicTilesContainer) TilesAccess.getContainer(servletContext); + TilesRequestContext tilesRequestContext = container.getContextFactory().createRequestContext( + container.getApplicationContext(), new Object[] { request, response }); + Definition compositeDefinition = container.getDefinitionsFactory().getDefinition("search", tilesRequestContext); + Map resultMap = new HashMap(); + ajaxTilesView.flattenAttributeMap(container, tilesRequestContext, resultMap, compositeDefinition, request, + response); + assertNotNull(resultMap.get("body")); + assertNotNull(resultMap.get("searchForm")); + assertEquals("/WEB-INF/searchForm.jsp", ((Attribute) resultMap.get("searchForm")).getValue()); + assertNotNull(resultMap.get("searchResults")); + } + + public void testGetRenderFragments() throws Exception { + Map model = new HashMap(); + request.setParameter("fragments", "f1,f2, f3"); + String[] fragments = ajaxTilesView.getRenderFragments(model, request, response); + assertEquals("f1", fragments[0]); + assertEquals("f2", fragments[1]); + assertEquals("f3", fragments[2]); + } +} diff --git a/spring-js/src/test/java/org/springframework/js/ajax/tiles2/tiles-definitions.xml b/spring-js/src/test/java/org/springframework/js/ajax/tiles2/tiles-definitions.xml new file mode 100644 index 00000000..a61c4b17 --- /dev/null +++ b/spring-js/src/test/java/org/springframework/js/ajax/tiles2/tiles-definitions.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file