{"id":50,"date":"2009-01-28T14:22:41","date_gmt":"2009-01-28T21:22:41","guid":{"rendered":"http:\/\/www.zulutown.com\/blog\/?p=50"},"modified":"2009-03-24T02:57:15","modified_gmt":"2009-03-24T09:57:15","slug":"rest-web-application-with-struts21-rest-and-convention-plugins","status":"publish","type":"post","link":"http:\/\/www.zulutown.com\/blog\/2009\/01\/28\/rest-web-application-with-struts21-rest-and-convention-plugins\/","title":{"rendered":"REST web application with Struts2.1 Rest and Convention plugins"},"content":{"rendered":"<p>The sample application developed in this tutorial will handle a simple message system, where it&#8217;s possible to read, add and remove messages.<br \/>\nSo, the basic bean will be defined in the <code>Message<\/code> class:<\/p>\n<pre>package com.zulutown.struts2.rest;\r\n\r\npublic class Message {\r\n\tprivate String id;\r\n\tprivate String text;\r\n\tprivate String author;\r\n\r\n\tpublic Message() {\r\n\t\tsuper();\r\n\t}\r\n\r\n\tpublic Message(String id, String text, String author) {\r\n\t\tsuper();\r\n\t\tthis.id = id;\r\n\t\tthis.text = text;\r\n\t\tthis.author = author;\r\n\t}\r\n\r\n\tpublic String getId() {\r\n\t\treturn id;\r\n\t}\r\n\r\n\tpublic void setId(String id) {\r\n\t\tthis.id = id;\r\n\t}\r\n\r\n\tpublic String getText() {\r\n\t\treturn text;\r\n\t}\r\n\r\n\tpublic void setText(String text) {\r\n\t\tthis.text = text;\r\n\t}\r\n\r\n\tpublic String getAuthor() {\r\n\t\treturn author;\r\n\t}\r\n\tpublic void setAuthor(String author) {\r\n\t\tthis.author = author;\r\n\t}\r\n\r\n\tpublic int hashCode() {\r\n\t\tfinal int prime = 31;\r\n\t\tint result = 1;\r\n\t\tresult = prime * result + ((id == null) ? 0 : id.hashCode());\r\n\t\treturn result;\r\n\t}\r\n\r\n\tpublic boolean equals(Object obj) {\r\n\t\tif (this == obj)\r\n\t\t\treturn true;\r\n\t\tif (obj == null)\r\n\t\t\treturn false;\r\n\t\tif (getClass() != obj.getClass())\r\n\t\t\treturn false;\r\n\t\tMessage other = (Message) obj;\r\n\t\tif (id == null) {\r\n\t\t\tif (other.id != null)\r\n\t\t\t\treturn false;\r\n\t\t} else if (!id.equals(other.id))\r\n\t\t\treturn false;\r\n\t\treturn true;\r\n\t}\r\n}<\/pre>\n<p>The message will be identified by the <code>id<\/code> property and will contain its <code>text<\/code> and its <code>author<\/code> in the other two class properties.<\/p>\n<p>The next step is to setup a basic <em>business service<\/em> that will handle the basic operations on the messages: <em>find<\/em>, <em>save<\/em>, <em>remove<\/em> and so on. In a real world application this service would be a singleton that interacts with a database (through JDBC or JPA) but in this little demo the data model is just a very basic (and ugly) Map kept in a static property of the <code>Message<\/code> class.<\/p>\n<pre>package com.zulutown.struts2.rest;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\npublic class MessageService {\r\n\r\n\tpublic static Map messages = new HashMap();\r\n\tprivate static int nextMessageId = 4;\r\n\r\n\tstatic {\r\n\t\tMessage message1 = new Message(\"1\", \"hello\", \"john\");\r\n\t\tMessage message2 = new Message(\"2\", \"world\", \"ted\");\r\n\t\tMessage message3 = new Message(\"3\", \"rest\", \"sam\");\r\n\t\tmessages.put(\"1\", message1);\r\n\t\tmessages.put(\"2\", message2);\r\n\t\tmessages.put(\"3\", message3);\r\n\t}\r\n\r\n\tpublic static List findAll() {\r\n\t\treturn new ArrayList(messages.values());\r\n\t}\r\n\r\n\tpublic static Message find(String id) {\r\n\t\treturn messages.get(id);\r\n\t}\r\n\r\n\tpublic static void save(Message message) {\r\n\t\tif (message.getId() == null) {\r\n\t\t\tString id = String.valueOf(nextMessageId);\r\n\t\t\tmessage.setId(id);\r\n\t\t\tnextMessageId++;\r\n\t\t}\r\n\t\tmessages.put(message.getId(), message);\r\n\t}\r\n\r\n\tpublic static void remove(String id) {\r\n\t\tmessages.remove(id);\r\n\t}\r\n}<\/pre>\n<p>In <code>messages<\/code> it is stored the data and the static initializer of the <code>MessageService<\/code> class will populate the data model with three messages.<br \/>\nThen, some utility methods are provided: <code>findAll()<\/code> to retrieve all the messages, <code>find(String id)<\/code> to retrieve a message with a specific <code>id<\/code>, <code>save(Message message)<\/code> to add (or update) a message in the data model and, finally, a <code>remove<\/code> method to delete a specific message.<\/p>\n<p>Until now, this tutorial is not related at all with Struts 2.1, but now it is required to add a &#8220;web layer&#8221; to the application, and the REST capabilities provided by Struts2 will be used.<\/p>\n<p>The first task is to add to the Web application the <code>.jar<\/code> libraries of Struts2. Those files must be placed into <code>\/WEB-INF\/lib<\/code> (in WebContent) that is the standard location where a Java Web Application expects to find its dependencies.<\/p>\n<p>Download from the Struts <a href=\"http:\/\/struts.apache.org\/download.cgi\">website<\/a> the archives with the libraries for Struts 2.1.x (currently the GA release is 2.1.6), unpack it, and copy &amp; paste the following jars in <code>\/WEB-INF\/lib\/<\/code> of your project.<\/p>\n<ul>\n<li>commons-beanutils-1.7.0.jar<\/li>\n<li>commons-collections-3.2.jar<\/li>\n<li>commons-fileupload-1.2.1.jar<\/li>\n<li>commons-io-1.3.2.jar<\/li>\n<li>commons-lang-2.3.jar<\/li>\n<li>commons-logging-1.0.4.jar<\/li>\n<li>ezmorph-1.0.3.jar<\/li>\n<li>freemarker-2.3.13.jar<\/li>\n<li>json-lib-2.1.jar<\/li>\n<li>ognl-2.6.11.jar<\/li>\n<li>struts2-convention-plugin-2.1.6.jar<\/li>\n<li>struts2-core-2.1.6.jar<\/li>\n<li>struts2-rest-plugin-2.1.6.jar<\/li>\n<li>xpp3_min-1.1.3.4.O.jar<\/li>\n<li>xstream-1.2.2.jar<\/li>\n<li>xwork-2.1.2.jar<\/li>\n<\/ul>\n<p>Two Struts2 plugins are used:<\/p>\n<ul>\n<li> Rest Plugin<\/li>\n<li>Convention Plugin<\/li>\n<\/ul>\n<p>First of all create at the root of the <code>src<\/code> directory the <code>struts.xml<\/code> file:<\/p>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt;\r\n&lt;!DOCTYPE struts PUBLIC\r\n    \"-\/\/Apache Software Foundation\/\/DTD Struts Configuration 2.1\/\/EN\"\r\n    \"http:\/\/struts.apache.org\/dtds\/struts-2.1.dtd\"&gt;\r\n\r\n&lt;struts&gt;\r\n    &lt;constant name=\"struts.convention.package.locators\" value=\"rest\"\/&gt;  \r\n    &lt;constant name=\"struts.convention.action.suffix\" value=\"Controller\"\/&gt;\r\n    &lt;constant name=\"struts.convention.action.mapAllMatches\" value=\"true\"\/&gt;\r\n    &lt;constant name=\"struts.convention.default.parent.package\" value=\"rest-default\"\/&gt;\r\n&lt;\/struts&gt;<\/pre>\n<p>Convention plugin makes possible to map classes and methods on automatically generated URLs. It is not required the manual &#8220;wiring&#8221; usually done in the <code>struts.xml<\/code> configuration file that, in this case, contains just a few lines of configuration.  The property <code>struts.convention.package.locators<\/code> defines the package name where the Convention plugin will look for Struts2 Actions and Controllers, then with <code>struts.convention.action.suffix<\/code> it is specified that just the classes with the <em>Controller<\/em> suffix will be automatically mapped.  The REST controller that&#8217;s going to be defined in the next step, will have the name <code>MessagesController<\/code> and will be contained in the package <code>com.zulutown.struts2.rest<\/code>, then it will match both the configurations described above.  Given this configuration, the list of the messages in XML format will be accessible through an HTTP GET method on <code>http:\/\/localhost:8080\/Struts2-Rest\/messages.xml<\/code>, and to obtain the JSON messages list, it&#8217;s enough to call the HTTP GET method on <code>http:\/\/localhost:8080\/Struts2-Rest\/messages.json<\/code>. Simple, isn&#8217;t?  Now, the simple code of the Controller class that will handle the REST requests:<\/p>\n<pre>package com.zulutown.struts2.rest;\r\n\r\nimport java.util.Collection;\r\nimport org.apache.struts2.rest.DefaultHttpHeaders;\r\nimport org.apache.struts2.rest.HttpHeaders;\r\nimport com.opensymphony.xwork2.ModelDriven;\r\n\r\npublic class MessagesController implements ModelDriven&lt;Object&gt; {\r\n\r\n\tprivate static final long serialVersionUID = 89268916175477696L;\r\n\tprivate Message model = new Message();\r\n\tprivate String id;\r\n\tprivate Collection&lt;Message&gt; list;\r\n\r\n\tpublic HttpHeaders create() {\r\n\t\tMessageService.save(model);\r\n\t\treturn new DefaultHttpHeaders(\"create\");\r\n\t}\r\n\r\n\tpublic HttpHeaders destroy() {\r\n\t\treturn new DefaultHttpHeaders(\"destroy\");\r\n\t}\r\n\r\n\tpublic HttpHeaders show() {\r\n\t\treturn new DefaultHttpHeaders(\"show\").disableCaching();\r\n\t}\r\n\r\n\tpublic HttpHeaders update() {\r\n\t\tMessageService.save(model);\r\n\t\treturn new DefaultHttpHeaders(\"update\");\r\n\t}\r\n\r\n\tpublic HttpHeaders index() {\r\n\t\tlist = MessageService.findAll();\r\n\t\treturn new DefaultHttpHeaders(\"index\").disableCaching();;\r\n\t}\r\n\r\n\tpublic Object getModel() {\r\n\t\treturn (list != null ? list : model);\r\n\t}\r\n\r\n\tpublic String getId() {\r\n\t\treturn id;\r\n\t}\r\n\r\n\tpublic void setId(String id) {\r\n\t\tif (id != null) {\r\n\t\t\tthis.model = MessageService.find(id);\r\n\t\t}\r\n\t\tthis.id = id;\r\n\t}\r\n}<\/pre>\n<p>In the Struts2 REST plugin, the method name (in the the Controller class) identifies which kind of operation should be executed on specific HTTP requests.<\/p>\n<p>HTTP GET method on <code>http:\/\/localhost:8080\/Struts2-Rest\/messages.xml<\/code> calls <code>index()<\/code> and provides the list of all the messages (in the <code>model<\/code> property of the controller).<\/p>\n<p>The result is:<\/p>\n<pre>&lt;list&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;3&lt;\/id&gt;\r\n    &lt;text&gt;rest&lt;\/text&gt;\r\n    &lt;author&gt;sam&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;2&lt;\/id&gt;\r\n    &lt;text&gt;world&lt;\/text&gt;\r\n    &lt;author&gt;ted&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;1&lt;\/id&gt;\r\n    &lt;text&gt;hello&lt;\/text&gt;\r\n    &lt;author&gt;john&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n&lt;\/list&gt;<\/pre>\n<p>HTTP GET method on <code>http:\/\/localhost:8080\/Struts2-Rest\/messages\/2.xml<\/code> calls <code>setId(\"2\")<\/code> (that loads in the <code>model<\/code> property the Message identified by the provided <code>id<\/code>) then <code>show()<\/code>.<\/p>\n<p>The result is:<\/p>\n<pre>&lt;com.zulutown.struts2.rest.Message&gt;\r\n  &lt;id&gt;2&lt;\/id&gt;\r\n  &lt;text&gt;world&lt;\/text&gt;\r\n  &lt;author&gt;ted&lt;\/author&gt;\r\n&lt;\/com.zulutown.struts2.rest.Message&gt;<\/pre>\n<p>The previous two methods are easy to call with the browser but, to test the other HTTP methods, it&#8217;s better to use the Firefox plugin <a href=\"http:\/\/code.google.com\/p\/poster-extension\/\">Poster<\/a>.<\/p>\n<p>HTTP POST method on <code>http:\/\/localhost:8080\/Struts2-Rest\/messages.xml<\/code> injects into <code>model<\/code> a new message, then calls <code>create()<\/code> that persists it.<\/p>\n<p>In the <em>poster<\/em> dialog box, the URL must be <code>http:\/\/localhost:8080\/Struts2-Rest\/messages.xml<\/code> with <code>POST<\/code> action. In <em>Content to Send<\/em> it&#8217;s required to insert the XML entity for the new message (without specifying its id, because it will be automatically generated by <code>MessageService<\/code>).<\/p>\n<pre>  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;text&gt;new text&lt;\/text&gt;\r\n    &lt;author&gt;new author&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;<\/pre>\n<div id=\"attachment_62\" style=\"width: 490px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-62\" class=\"size-large wp-image-62\" title=\"Rest - HTTP Post\" src=\"http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-post-480x592.png\" alt=\"Rest - HTTP Post\" width=\"480\" height=\"592\" srcset=\"http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-post-480x592.png 480w, http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-post-243x300.png 243w, http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-post.png 509w\" sizes=\"(max-width: 480px) 100vw, 480px\" \/><p id=\"caption-attachment-62\" class=\"wp-caption-text\">Rest - HTTP Post<\/p><\/div>\n<p>HTTP PUT method on <code>http:\/\/localhost:8080\/Struts2-Rest\/messages\/2.xml<\/code> calls <code>setId(\"2\")<\/code> (that causes the loading of the existing message with <code>id=2<\/code> in the <code>model<\/code> property) then, depending on which fields are specified in the XML, those message properties are modified and finally  a call to <code>update()<\/code> saves the updated message.<\/p>\n<p>In the <em>poster<\/em> dialog box, the URL must be <code>http:\/\/localhost:8080\/Struts2-Rest\/messages\/2.xml<\/code> with <code>PUT<\/code> action. In <em>Content to Send<\/em> it&#8217;s required to insert the XML entity for the fields to edit (the unspecified fields will keep the previous values).<\/p>\n<pre>  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;text&gt;updated text&lt;\/text&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;<\/pre>\n<div id=\"attachment_63\" style=\"width: 490px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-63\" class=\"size-large wp-image-63\" title=\"Rest - HTTP Put\" src=\"http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-put-480x592.png\" alt=\"Rest - HTTP Put\" width=\"480\" height=\"592\" srcset=\"http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-put-480x592.png 480w, http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-put-243x300.png 243w, http:\/\/www.zulutown.com\/blog\/wp-content\/uploads\/2009\/01\/poster-put.png 509w\" sizes=\"(max-width: 480px) 100vw, 480px\" \/><p id=\"caption-attachment-63\" class=\"wp-caption-text\">Rest - HTTP Put<\/p><\/div>\n<p>After the POST and PUT calls, the data structure will look like this:<\/p>\n<pre>&lt;list&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;3&lt;\/id&gt;\r\n    &lt;text&gt;rest&lt;\/text&gt;\r\n    &lt;author&gt;sam&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;2&lt;\/id&gt;\r\n    &lt;text&gt;updated text&lt;\/text&gt;\r\n    &lt;author&gt;ted&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;1&lt;\/id&gt;\r\n    &lt;text&gt;hello&lt;\/text&gt;\r\n    &lt;author&gt;john&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n  &lt;com.zulutown.struts2.rest.Message&gt;\r\n    &lt;id&gt;4&lt;\/id&gt;\r\n    &lt;text&gt;new text&lt;\/text&gt;\r\n    &lt;author&gt;new author&lt;\/author&gt;\r\n  &lt;\/com.zulutown.struts2.rest.Message&gt;\r\n&lt;\/list&gt;<\/pre>\n<p>In this tutorial all the URLs refer to <code>xml<\/code> content type, but it&#8217;s possible to use <code>json<\/code> just changing URLs from <code>messages.xml<\/code> to <code>messages.json<\/code> or <code>messages\/2.xml<\/code> to <code>messages\/2.json<\/code>.<\/p>\n<p>I hope this tutorial has given a simple and easy introduction to the REST capabilities of Struts2.1. Please leave your comments and feedback.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The sample application developed in this tutorial will handle a simple message system, where it&#8217;s possible to read, add and remove messages. So, the basic bean will be defined in the Message class: package com.zulutown.struts2.rest; public class Message { private &hellip; <a href=\"http:\/\/www.zulutown.com\/blog\/2009\/01\/28\/rest-web-application-with-struts21-rest-and-convention-plugins\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,9],"tags":[21,16,17,19,18,169,166,167,15,20],"_links":{"self":[{"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/posts\/50"}],"collection":[{"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/comments?post=50"}],"version-history":[{"count":14,"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/posts\/50\/revisions"}],"predecessor-version":[{"id":103,"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/posts\/50\/revisions\/103"}],"wp:attachment":[{"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/media?parent=50"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/categories?post=50"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.zulutown.com\/blog\/wp-json\/wp\/v2\/tags?post=50"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}