diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/ButtonTag.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/ButtonTag.java
new file mode 100644
index 00000000000..e96b1eecba7
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/form/ButtonTag.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2002-2011 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.web.servlet.tags.form;
+
+import javax.servlet.jsp.JspException;
+
+import org.springframework.web.servlet.support.RequestDataValueProcessor;
+
+/**
+ * An HTML button tag. This tag is provided for completeness if the application
+ * relies on a {@link RequestDataValueProcessor}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class ButtonTag extends AbstractHtmlElementTag {
+
+ /**
+ * The name of the 'disabled' attribute.
+ */
+ public static final String DISABLED_ATTRIBUTE = "disabled";
+
+ private TagWriter tagWriter;
+
+ private String name;
+
+ private String value;
+
+ private String disabled;
+
+ /**
+ * Set the value of the 'name' attribute.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the value of the 'name' attribute.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the value of the 'value' attribute.
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * Set the value of the 'value' attribute.
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value of the 'disabled' attribute.
+ */
+ public String getDisabled() {
+ return this.disabled;
+ }
+
+ /**
+ * Set the value of the 'disabled' attribute.
+ * May be a runtime expression.
+ */
+ public void setDisabled(String disabled) {
+ this.disabled = disabled;
+ }
+
+ @Override
+ protected int writeTagContent(TagWriter tagWriter) throws JspException {
+ tagWriter.startTag("button");
+ writeDefaultAttributes(tagWriter);
+ tagWriter.writeAttribute("type", getType());
+ writeValue(tagWriter);
+ if (isDisabled()) {
+ tagWriter.writeAttribute(DISABLED_ATTRIBUTE, "disabled");
+ }
+ tagWriter.forceBlock();
+ this.tagWriter = tagWriter;
+ return EVAL_BODY_INCLUDE;
+ }
+
+ /**
+ * Is the current HTML tag disabled?
+ */
+ protected boolean isDisabled() throws JspException {
+ return evaluateBoolean(DISABLED_ATTRIBUTE, getDisabled());
+ }
+
+ /**
+ * Writes the 'value' attribute to the supplied {@link TagWriter}.
+ * Subclasses may choose to override this implementation to control exactly
+ * when the value is written.
+ */
+ protected void writeValue(TagWriter tagWriter) throws JspException {
+ String valueToUse = (getValue() != null) ? getValue() : getDefaultValue();
+ tagWriter.writeAttribute("value", processFieldValue(getName(), valueToUse, getType()));
+ }
+
+ /**
+ * Return the default value.
+ *
+ * @return The default value if none supplied.
+ */
+ protected String getDefaultValue() {
+ return "Submit";
+ }
+
+ /**
+ * Get the value of the 'type' attribute. Subclasses
+ * can override this to change the type of 'input' element
+ * rendered. Default value is 'submit'.
+ */
+ protected String getType() {
+ return "submit";
+ }
+
+ /**
+ * Closes the 'button' block tag.
+ */
+ @Override
+ public int doEndTag() throws JspException {
+ this.tagWriter.endTag();
+ return EVAL_PAGE;
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/main/resources/META-INF/spring-form.tld b/org.springframework.web.servlet/src/main/resources/META-INF/spring-form.tld
index a2806a77e7d..ca61de04fd9 100644
--- a/org.springframework.web.servlet/src/main/resources/META-INF/spring-form.tld
+++ b/org.springframework.web.servlet/src/main/resources/META-INF/spring-form.tld
@@ -2370,4 +2370,36 @@
true
+
+ Renders an HTML 'button' tag.
+ button
+ org.springframework.web.servlet.tags.form.ButtonTag
+ JSP
+
+ HTML Standard Attribute
+ id
+ false
+ true
+
+
+ The name attribute for the HTML button tag
+ name
+ false
+ true
+
+
+ The value attribute for the HTML button tag
+ value
+ false
+ true
+
+
+ HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element.
+ disabled
+ false
+ true
+
+ true
+
+
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/ButtonTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/ButtonTagTests.java
new file mode 100644
index 00000000000..3208aa74f63
--- /dev/null
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/form/ButtonTagTests.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2011 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.web.servlet.tags.form;
+
+import java.io.Writer;
+
+import javax.servlet.jsp.tagext.Tag;
+
+import org.springframework.beans.TestBean;
+
+/**
+ * @author Rossen Stoyanchev
+ */
+public class ButtonTagTests extends AbstractFormTagTests {
+
+ private ButtonTag tag;
+
+ protected void onSetUp() {
+ this.tag = createTag(getWriter());
+ this.tag.setParent(getFormTag());
+ this.tag.setPageContext(getPageContext());
+ this.tag.setId("My Id");
+ this.tag.setName("My Name");
+ this.tag.setValue("My Button");
+ }
+
+ public void testButtonTag() throws Exception {
+ assertEquals(Tag.EVAL_BODY_INCLUDE, this.tag.doStartTag());
+ assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag());
+
+ String output = getOutput();
+ assertTagOpened(output);
+ assertTagClosed(output);
+
+ assertContainsAttribute(output, "id", "My Id");
+ assertContainsAttribute(output, "name", "My Name");
+ assertContainsAttribute(output, "type", "submit");
+ assertContainsAttribute(output, "value", "My Button");
+ assertAttributeNotPresent(output, "disabled");
+ }
+
+ public void testDisabled() throws Exception {
+ this.tag.setDisabled("true");
+
+ this.tag.doStartTag();
+ this.tag.doEndTag();
+
+ String output = getOutput();
+ assertTagOpened(output);
+ assertTagClosed(output);
+
+ assertContainsAttribute(output, "disabled", "disabled");
+ }
+
+ @Override
+ protected TestBean createTestBean() {
+ return new TestBean();
+ }
+
+ protected final void assertTagClosed(String output) {
+ assertTrue("Tag not closed properly", output.endsWith(""));
+ }
+
+ protected final void assertTagOpened(String output) {
+ assertTrue("Tag not opened properly", output.startsWith("