Creating custom component in Struts 2
Posted December 27, 2008
on:- In: Java
- 19 Comments
On my previous Tapestry 5 article I mentioned how difficult it is to create a custom component with Struts 2 compared to Tapestry 5. Since in our legacy apps we used Struts 2 and I got a task that made me to create a custom component I might as well just document it here since it is not documented in Struts 2 documentation nor the Struts 2 book.
I will show you a very simple example on how to create custom component which is the renown “Hello World”. This component would have one property: name.
First of all you would create a Component object which is extended from Component
class. This is a reusable UI Component in Struts 2 which can be reused for JSTL, Freemarker and Velocity. For this article I will show you how to reuse it in JSTL, but it shouldn’t be hard to reuse it in Freemarker and Velocity once you get the gist of how custom components works in Struts 2.
package lab.struts2.components; import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.components.Component; import java.io.IOException; import java.io.Writer; import java.util.Iterator; import java.util.List; public class Hello extends Component { protected String name; public Hello(ValueStack stack) { super(stack); } public void setName(String name) { this.name = name; } public boolean start(Writer writer) { try { writer.write("Hello " + name); } catch (IOException e) { e.printStackTrace(); } return true; } public boolean end(Writer writer) { return true; } @Override public boolean usesBody() { return false; } }
As you can see from the code above, the Writer
is located in this Component class. If you have written a JSTL tag before then you would know that the Writer is in the Taglib class. That is because this component will be reused by other view like Freemarker or Velocity. Since the Writer that is responsible to send buffer to the view is located in this class, you have to write the start
and end
method in this class. These two methods will be responsible to process anything on the tag opener and tag closer. The last method is the usesBody which is responsible to indicated whether body should be used or not. Besides those methods you must also create the property that can be accepted by this Component including its setters. The last thing you must write in this class is a constructor which receives ValueStack
as its parameter.
Now onward to the next step. As I have told you that this article intend to show how to reuse Struts 2 component in JSTL. So the next step would be creating a JSTL tag. In this article we will extend the Tag class from ComponentTagSupport
which is a Struts 2 base class for JSTL view that supports Struts 2 component. Two methods that must exists in this class is the populateParams
and getBean
method which is responsible to propagate the values that is set to the Component object. Now in this class you must also again write the property that can be accepted along with its setters.
package lab.struts2.taglib; import org.apache.struts2.views.jsp.ComponentTagSupport; import org.apache.struts2.components.Component; import com.opensymphony.xwork2.util.ValueStack; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lab.struts2.components.Hello; public class HelloTag extends ComponentTagSupport { protected String name; public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) { return new Hello(stack); } protected void populateParams() { super.populateParams(); Hello hello = (Hello)component; hello.setName(name); } public void setName(String name) { this.name = name; } }
Since we are reusing the component in JSTL, the last step would be registering this component in a .tld file which defines what properties that can be called from the JSP page. As for me I would save this .tld in META-INF/
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>2.2.3</tlib-version> <jsp-version>1.2</jsp-version> <short-name>lab</short-name> <uri>/lab</uri> <display-name>"Lab Tags"</display-name> <description><![CDATA["lab"]]></description> <tag> <name>hello</name> <tag-class>lab.struts2.taglib.HelloTag</tag-class> <body-content>JSP</body-content> <description><![CDATA[Render a hello]]></description> <attribute> <name>id</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> <![CDATA[id for referencing element]]></description> </attribute> <attribute> <name>name</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <description><![CDATA[ Name]]></description> </attribute> </tag> </taglib>
Now that we have created our component, we would call it from a JSP page as such:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@taglib prefix="s" uri="/struts-tags" %> <%@taglib prefix="lab" uri="/lab" %> <html> <head> <title>Hello World</title> <s:head /> </head> <body> <lab:hello name="joshua"/> </body> </html>
If you open up your browser it would display: Hello joshua.
Well that shows you some effort you need to do for creating custom component in Struts 2. As you can see that this is only a very trivial example, but it shouldn’t be too difficult to get more advanced from here. Good luck.
19 Responses to "Creating custom component in Struts 2"
Hi Joshua,
Thank you very much for this post, I really appreciate it. I am in the process of doing a conversion from Struts 1 on IBM Portal to Struts 2 on Tomcat and I think that this article will prove to be very helpful.
Take Care,
Cory Wheeler
Thanks for article. Very useful!
@pen ma
what do you mean about :
stop using struts and tapestry it is all dead. User more advanced ultra modern framework like Wicket.
is it the struts2 & tapestry project have been discountinued?..
i see many people is still learn and use Struts2 and tapestry for their solution..
CMIIW..
thx..
@Aji Dont Listen to the lame pen ma is an idiot and he does not know what he is talking about.
Tapestry, Wicket and Struts are alive and well projects that continue to improve.
Nice article to get started with.
[…] creating-custom-components-with-struts-2 : https://joshuajava.wordpress.com/2008/12/27/creating-custom-components-with-struts-2/ […]
[…] creating-custom-components-with-struts-2 : https://joshuajava.wordpress.com/2008/12/27/creating-custom-components-with-struts-2/ […]
Nice post. Could you put an example on how to manage, instead of a String for the “name” variable, a List for exemple ?
I want to have a inside Writer.write . However, it is not being rendered in the webbrowser…
This was very useful thanks. In the old struts 1 world only one class was needed for a custom tag. Is there a way to do this with one class or do you always need to subclass ComponentTagSupport and Component in two classes for a JSTL tag in struts 2?
closing tag is not working. You need to use the following method
end(java.io.Writer writer, java.lang.String body)
Hi !
Thanks! for the post. Quite usefull.
I followed your custom tag tutorial and as long as I hard code the property values into the tag it works. If I try to inject a session or request object into it it will not read the actual value of the tag it reads it as a string value. Example:
name = “#request.name”
it will
read it as the string “#request.name”. I even tried to set the true and it still will not read it correctly. What do I need to do to fix this. Thank You
Also I know Struts 1 has an InitPlugin which operates on the servletContext layer to iitialize variables when the appl first starts up in the server. I use it to initialize a few variables. I can arc a spring service into it and load the results into the getAttribute. Does Struts have anything like this? If so it needs to do the same thing.
This is in reference to my query to create a struts2 custom tag that has access to request or session objects. I went ahead and researched this solution. By the time anyone would come up with a viable solution we might be moving on to Struts3. or be dead and buried. I felt this solution might help a few motivated soles:
Here is the TableTag java file that does all the work
(list is just a simple string value that is coming from a request object)
Pay particular attention to the
ValueStack stack = TagUtils.getStack((PageContext)getJspContext());
stack.findValue(list)
this is where all the processing is occuring
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
package pojo;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import org.apache.struts2.views.jsp.TagUtils;
import com.opensymphony.xwork2.util.ValueStack;
public class TableTag extends SimpleTagSupport{
private String list;
public void doTag() throws JspException
{
ValueStack stack = TagUtils.getStack((PageContext)getJspContext());
JspWriter out = getJspContext().getOut();
try {
StringBuffer st = new StringBuffer();
st.append(“This is from the custom tag via request object: ” + stack.findValue(list));
out.println(st.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setList(String list)
{
this.list = list;
}
public String getList() {
return list;
}
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Here is the tbl.tld file (just place this file in your web-inf folder
2.2.3
1.2
lab
/lab
“Lab Tags”
tbl
pojo.TableTag
scriptless
list
true
true
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Here is the jsp that has an instance of my tbl.tld file
within the tbl tag I am injecting an instance of the request obj factor which has been set
at the string “yes” within my action class
Struts 2 Login Application!
asdasd
Test:
Test:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
When a user clicks the submit button the factor request object will populate the tag and then via
ValueStack stack = TagUtils.getStack((PageContext)getJspContext());
the stack.findValue(list)); will evaluate and extract the value
for the jspwriter to display
Test:This is from the custom tag via request object: Yes
Hi,
I want to make reusable component in/for Struts 2 framework. For example, a login form having validations, authentication on form submission, error display, Forgot password link etc. I want to create this form in such a way so that it can be placed anywhere within the site (horizontal at one place and vertical at another place) and in any site without any changes.
Please suggest what should I use or better if you can provide and reference for the example of such type of components.
Thanks
Krishan Babbar
1 | pen ma
January 1, 2009 at 5:11 pm
stop using struts and tapestry it is all dead. User more advanced ultra modern framework like Wicket.
mead
October 9, 2010 at 1:06 pm
Well, the Wicket is not such so great, the design idea is similar as ASP.NET, but it is hard for users.