diff --git a/.gitignore b/.gitignore
index 6b6a4200bec..f856d1d983c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ bin
build.sh
integration-repo
ivy-cache
+jxl.log
jmx.log
org.springframework.jdbc/derby.log
org.springframework.spring-parent/.classpath
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContext.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContext.java
index ed5ab9e8ecf..4c14efe315d 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContext.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/support/RequestContext.java
@@ -41,6 +41,7 @@ import org.springframework.web.bind.EscapedErrors;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.util.HtmlUtils;
+import org.springframework.web.util.UriTemplate;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.WebUtils;
@@ -408,6 +409,23 @@ public class RequestContext {
return url;
}
+ /**
+ * Return a context-aware URl for the given relative URL with placeholders (named keys with braces {}).
+ * @param relativeUrl the relative URL part
+ * @param a map of parameters to insert as placeholders in the url
+ * @return a URL that points back to the server with an absolute path
+ * (also URL-encoded accordingly)
+ */
+ public String getContextUrl(String relativeUrl, Map params) {
+ String url = getContextPath() + relativeUrl;
+ UriTemplate template = new UriTemplate(url);
+ url = template.expand(params).toASCIIString();
+ if (this.response != null) {
+ url = this.response.encodeURL(url);
+ }
+ return url;
+ }
+
/**
* Return the request URI of the original request, that is, the invoked URL
* without parameters. This is particularly useful as HTML form action target,
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/freemarker/spring.ftl b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/freemarker/spring.ftl
index 4bd0de23cb6..d72dbc39b03 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/freemarker/spring.ftl
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/freemarker/spring.ftl
@@ -86,7 +86,7 @@
* Takes a relative URL and makes it absolute from the server root by
* adding the context root for the web application.
-->
-<#macro url relativeUrl>${springMacroRequestContext.getContextUrl(relativeUrl)}#macro>
+<#macro url relativeUrl extra...><#if extra?? && extra?size!=0>${springMacroRequestContext.getContextUrl(relativeUrl,extra)}<#else>${springMacroRequestContext.getContextUrl(relativeUrl)}#if>#macro>
<#--
* bind
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/DummyMacroRequestContext.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/DummyMacroRequestContext.java
index 6aa6a0671af..2e5f3f4cb60 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/DummyMacroRequestContext.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/DummyMacroRequestContext.java
@@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.support.BindStatus;
import org.springframework.web.servlet.support.RequestContext;
+import org.springframework.web.util.UriTemplate;
/**
* Dummy request context used for VTL and FTL macro tests.
@@ -134,6 +135,14 @@ public class DummyMacroRequestContext {
return getContextPath() + relativeUrl;
}
+ /**
+ * @see org.springframework.web.servlet.support.RequestContext#getContextUrl(String, Map)
+ */
+ public String getContextUrl(String relativeUrl, Map params) {
+ UriTemplate template = new UriTemplate(relativeUrl);
+ return getContextPath() + template.expand(params).toASCIIString();
+ }
+
/**
* @see org.springframework.web.servlet.support.RequestContext#getBindStatus(String)
*/
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java
index 7b449d6bf4f..84a7854a1ae 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java
@@ -16,23 +16,28 @@
package org.springframework.web.servlet.view.freemarker;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.FileWriter;
+import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.TemplateModel;
-import freemarker.template.SimpleHash;
-import freemarker.template.TemplateException;
-import junit.framework.TestCase;
-
+import org.junit.Before;
+import org.junit.Test;
import org.springframework.beans.TestBean;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
+import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
@@ -42,16 +47,20 @@ import org.springframework.web.servlet.support.RequestContext;
import org.springframework.web.servlet.theme.FixedThemeResolver;
import org.springframework.web.servlet.view.DummyMacroRequestContext;
+import freemarker.template.Configuration;
+import freemarker.template.SimpleHash;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
/**
* @author Darren Davison
* @author Juergen Hoeller
* @since 25.01.2005
*/
-public class FreeMarkerMacroTests extends TestCase {
+public class FreeMarkerMacroTests {
private static final String TEMPLATE_FILE = "test.ftl";
-
private StaticWebApplicationContext wac;
private MockHttpServletRequest request;
@@ -60,14 +69,14 @@ public class FreeMarkerMacroTests extends TestCase {
private FreeMarkerConfigurer fc;
-
+ @Before
public void setUp() throws Exception {
wac = new StaticWebApplicationContext();
wac.setServletContext(new MockServletContext());
- //final Template expectedTemplate = new Template();
+ // final Template expectedTemplate = new Template();
fc = new FreeMarkerConfigurer();
- fc.setPreferFileSystemAccess(false);
+ fc.setTemplateLoaderPaths(new String[] { "classpath:/", "file://" + System.getProperty("java.io.tmpdir") });
fc.afterPropertiesSet();
wac.getDefaultListableBeanFactory().registerSingleton("freeMarkerConfigurer", fc);
@@ -80,10 +89,12 @@ public class FreeMarkerMacroTests extends TestCase {
response = new MockHttpServletResponse();
}
+ @Test
public void testExposeSpringMacroHelpers() throws Exception {
FreeMarkerView fv = new FreeMarkerView() {
@Override
- protected void processTemplate(Template template, SimpleHash fmModel, HttpServletResponse response) throws TemplateException {
+ protected void processTemplate(Template template, SimpleHash fmModel, HttpServletResponse response)
+ throws TemplateException {
Map model = fmModel.toMap();
assertTrue(model.get(FreeMarkerView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE) instanceof RequestContext);
RequestContext rc = (RequestContext) model.get(FreeMarkerView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
@@ -101,6 +112,7 @@ public class FreeMarkerMacroTests extends TestCase {
fv.render(model, request, response);
}
+ @Test
public void testSpringMacroRequestContextAttributeUsed() {
final String helperTool = "wrongType";
@@ -119,14 +131,143 @@ public class FreeMarkerMacroTests extends TestCase {
try {
fv.render(model, request, response);
- }
- catch (Exception ex) {
+ } catch (Exception ex) {
assertTrue(ex instanceof ServletException);
assertTrue(ex.getMessage().contains(FreeMarkerView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE));
}
}
- public void testAllMacros() throws Exception {
+ @Test
+ public void testName() throws Exception {
+ assertEquals("Darren", getMacroOutput("NAME"));
+ }
+
+ @Test
+ public void testAge() throws Exception {
+ assertEquals("99", getMacroOutput("AGE"));
+ }
+
+ @Test
+ public void testMessage() throws Exception {
+ assertEquals("Howdy Mundo", getMacroOutput("MESSAGE"));
+ }
+
+ @Test
+ public void testDefaultMessage() throws Exception {
+ assertEquals("hi planet", getMacroOutput("DEFAULTMESSAGE"));
+ }
+
+ @Test
+ public void testMessageArgs() throws Exception {
+ assertEquals("Howdy[World]", getMacroOutput("MESSAGEARGS"));
+ }
+
+ @Test
+ public void testMessageArgsWithDefaultMessage() throws Exception {
+ assertEquals("Hi", getMacroOutput("MESSAGEARGSWITHDEFAULTMESSAGE"));
+ }
+
+ @Test
+ public void testTheme() throws Exception {
+ assertEquals("Howdy! Mundo!", getMacroOutput("THEME"));
+ }
+
+ @Test
+ public void testDefaultTheme() throws Exception {
+ assertEquals("hi! planet!", getMacroOutput("DEFAULTTHEME"));
+ }
+
+ @Test
+ public void testThemeArgs() throws Exception {
+ assertEquals("Howdy![World]", getMacroOutput("THEMEARGS"));
+ }
+
+ @Test
+ public void testThemeArgsWithDefaultMessage() throws Exception {
+ assertEquals("Hi!", getMacroOutput("THEMEARGSWITHDEFAULTMESSAGE"));
+ }
+
+ @Test
+ public void testUrl() throws Exception {
+ assertEquals("/springtest/aftercontext.html", getMacroOutput("URL"));
+ }
+
+ @Test
+ public void testUrlParams() throws Exception {
+ assertEquals("/springtest/aftercontext/bar?spam=bucket", getMacroOutput("URLPARAMS"));
+ }
+
+ @Test
+ public void testForm1() throws Exception {
+ assertEquals("", getMacroOutput("FORM1"));
+ }
+
+ @Test
+ public void testForm2() throws Exception {
+ assertEquals("",
+ getMacroOutput("FORM2"));
+ }
+
+ @Test
+ public void testForm3() throws Exception {
+ assertEquals("", getMacroOutput("FORM3"));
+ }
+
+ @Test
+ public void testForm4() throws Exception {
+ assertEquals("", getMacroOutput("FORM4"));
+ }
+
+ // TODO verify remaining output (fix whitespace)
+ @Test
+ public void testForm9() throws Exception {
+ assertEquals("", getMacroOutput("FORM9"));
+ }
+
+ @Test
+ public void testForm10() throws Exception {
+ assertEquals("",
+ getMacroOutput("FORM10"));
+ }
+
+ @Test
+ public void testForm11() throws Exception {
+ assertEquals("", getMacroOutput("FORM11"));
+ }
+
+ @Test
+ public void testForm12() throws Exception {
+ assertEquals("",
+ getMacroOutput("FORM12"));
+ }
+
+ @Test
+ public void testForm13() throws Exception {
+ assertEquals("", getMacroOutput("FORM13"));
+ }
+
+ @Test
+ public void testForm15() throws Exception {
+ String output = getMacroOutput("FORM15");
+ assertTrue("Wrong output: " + output, output.startsWith(""));
+ assertTrue("Wrong output: " + output, output.contains(""));
+ }
+
+ @Test
+ public void testForm16() throws Exception {
+ String output = getMacroOutput("FORM16");
+ assertTrue("Wrong output: " + output, output.startsWith(""));
+ assertTrue("Wrong output: " + output, output.contains(""));
+ }
+
+ private String getMacroOutput(String name) throws Exception {
+
+ String macro = fetchMacro(name);
+ assertNotNull(macro);
+
+ FileSystemResource resource = new FileSystemResource(System.getProperty("java.io.tmpdir") + "/tmp.ftl");
+ FileCopyUtils.copy("<#import \"spring.ftl\" as spring />\n" + macro, new FileWriter(resource.getPath()));
+
DummyMacroRequestContext rc = new DummyMacroRequestContext(request);
Map msgMap = new HashMap();
msgMap.put("hello", "Howdy");
@@ -153,13 +294,13 @@ public class FreeMarkerMacroTests extends TestCase {
Map model = new HashMap();
model.put("command", tb);
model.put("springMacroRequestContext", rc);
- model.put("msgArgs", new Object[] {"World"});
+ model.put("msgArgs", new Object[] { "World" });
model.put("nameOptionMap", names);
model.put("options", names.values());
FreeMarkerView view = new FreeMarkerView();
view.setBeanName("myView");
- view.setUrl("test.ftl");
+ view.setUrl("tmp.ftl");
view.setExposeSpringMacroHelpers(false);
view.setConfiguration(config);
view.setServletContext(new MockServletContext());
@@ -168,36 +309,20 @@ public class FreeMarkerMacroTests extends TestCase {
// tokenize output and ignore whitespace
String output = response.getContentAsString();
- System.out.println(output);
- String[] tokens = StringUtils.tokenizeToStringArray(output, "\t\n");
+ return output.trim();
+ }
- for (int i = 0; i < tokens.length; i++) {
- if (tokens[i].equals("NAME")) assertEquals("Darren", tokens[i + 1]);
- if (tokens[i].equals("AGE")) assertEquals("99", tokens[i + 1]);
- if (tokens[i].equals("MESSAGE")) assertEquals("Howdy Mundo", tokens[i + 1]);
- if (tokens[i].equals("DEFAULTMESSAGE")) assertEquals("hi planet", tokens[i + 1]);
- if (tokens[i].equals("MESSAGEARGS")) assertEquals("Howdy[World]", tokens[i + 1]);
- if (tokens[i].equals("MESSAGEARGSWITHDEFAULTMESSAGE")) assertEquals("Hi", tokens[i + 1]);
- if (tokens[i].equals("THEME")) assertEquals("Howdy! Mundo!", tokens[i + 1]);
- if (tokens[i].equals("DEFAULTTHEME")) assertEquals("hi! planet!", tokens[i + 1]);
- if (tokens[i].equals("THEMEARGS")) assertEquals("Howdy![World]", tokens[i + 1]);
- if (tokens[i].equals("THEMEARGSWITHDEFAULTMESSAGE")) assertEquals("Hi!", tokens[i + 1]);
- if (tokens[i].equals("URL")) assertEquals("/springtest/aftercontext.html", tokens[i + 1]);
- if (tokens[i].equals("FORM1")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM2")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM3")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM4")) assertEquals("", tokens[i + 1]);
- //TODO verify remaining output (fix whitespace)
- if (tokens[i].equals("FORM9")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM10")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM11")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM12")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM13")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM15")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM15")) assertEquals("", tokens[i + 2]);
- if (tokens[i].equals("FORM16")) assertEquals("", tokens[i + 1]);
- if (tokens[i].equals("FORM16")) assertEquals("", tokens[i + 2]);
+ private String fetchMacro(String name) throws Exception {
+ ClassPathResource resource = new ClassPathResource("test.ftl", getClass());
+ assertTrue(resource.exists());
+ String all = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
+ String[] macros = StringUtils.delimitedListToStringArray(all, "\n\n");
+ for (String macro : macros) {
+ if (macro.startsWith(name)) {
+ return macro.substring(macro.indexOf("\n")).trim();
+ }
}
+ return null;
}
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl
index 7394b65423a..41550687750 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl
@@ -36,6 +36,9 @@ THEMEARGSWITHDEFAULTMESSAGE
URL
<@spring.url "/aftercontext.html"/>
+URLPARAMS
+<@spring.url relativeUrl="/aftercontext/{foo}?spam={spam}" foo="bar" spam="bucket"/>
+
FORM1
<@spring.formInput "command.name", ""/>