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