REST web application with Struts2.1 Rest and Convention plugins

The sample application developed in this tutorial will handle a simple message system, where it’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 String id;
	private String text;
	private String author;

	public Message() {
		super();
	}

	public Message(String id, String text, String author) {
		super();
		this.id = id;
		this.text = text;
		this.author = author;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}

	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Message other = (Message) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
}

The message will be identified by the id property and will contain its text and its author in the other two class properties.

The next step is to setup a basic business service that will handle the basic operations on the messages: find, save, remove 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 Message class.

package com.zulutown.struts2.rest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MessageService {

	public static Map messages = new HashMap();
	private static int nextMessageId = 4;

	static {
		Message message1 = new Message("1", "hello", "john");
		Message message2 = new Message("2", "world", "ted");
		Message message3 = new Message("3", "rest", "sam");
		messages.put("1", message1);
		messages.put("2", message2);
		messages.put("3", message3);
	}

	public static List findAll() {
		return new ArrayList(messages.values());
	}

	public static Message find(String id) {
		return messages.get(id);
	}

	public static void save(Message message) {
		if (message.getId() == null) {
			String id = String.valueOf(nextMessageId);
			message.setId(id);
			nextMessageId++;
		}
		messages.put(message.getId(), message);
	}

	public static void remove(String id) {
		messages.remove(id);
	}
}

In messages it is stored the data and the static initializer of the MessageService class will populate the data model with three messages.
Then, some utility methods are provided: findAll() to retrieve all the messages, find(String id) to retrieve a message with a specific id, save(Message message) to add (or update) a message in the data model and, finally, a remove method to delete a specific message.

Until now, this tutorial is not related at all with Struts 2.1, but now it is required to add a “web layer” to the application, and the REST capabilities provided by Struts2 will be used.

The first task is to add to the Web application the .jar libraries of Struts2. Those files must be placed into /WEB-INF/lib (in WebContent) that is the standard location where a Java Web Application expects to find its dependencies.

Download from the Struts website the archives with the libraries for Struts 2.1.x (currently the GA release is 2.1.6), unpack it, and copy & paste the following jars in /WEB-INF/lib/ of your project.

  • commons-beanutils-1.7.0.jar
  • commons-collections-3.2.jar
  • commons-fileupload-1.2.1.jar
  • commons-io-1.3.2.jar
  • commons-lang-2.3.jar
  • commons-logging-1.0.4.jar
  • ezmorph-1.0.3.jar
  • freemarker-2.3.13.jar
  • json-lib-2.1.jar
  • ognl-2.6.11.jar
  • struts2-convention-plugin-2.1.6.jar
  • struts2-core-2.1.6.jar
  • struts2-rest-plugin-2.1.6.jar
  • xpp3_min-1.1.3.4.O.jar
  • xstream-1.2.2.jar
  • xwork-2.1.2.jar

Two Struts2 plugins are used:

  • Rest Plugin
  • Convention Plugin

First of all create at the root of the src directory the struts.xml file:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
    "http://struts.apache.org/dtds/struts-2.1.dtd">

<struts>
    <constant name="struts.convention.package.locators" value="rest"/>  
    <constant name="struts.convention.action.suffix" value="Controller"/>
    <constant name="struts.convention.action.mapAllMatches" value="true"/>
    <constant name="struts.convention.default.parent.package" value="rest-default"/>
</struts>

Convention plugin makes possible to map classes and methods on automatically generated URLs. It is not required the manual “wiring” usually done in the struts.xml configuration file that, in this case, contains just a few lines of configuration. The property struts.convention.package.locators defines the package name where the Convention plugin will look for Struts2 Actions and Controllers, then with struts.convention.action.suffix it is specified that just the classes with the Controller suffix will be automatically mapped. The REST controller that’s going to be defined in the next step, will have the name MessagesController and will be contained in the package com.zulutown.struts2.rest, 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 http://localhost:8080/Struts2-Rest/messages.xml, and to obtain the JSON messages list, it’s enough to call the HTTP GET method on http://localhost:8080/Struts2-Rest/messages.json. Simple, isn’t? Now, the simple code of the Controller class that will handle the REST requests:

package com.zulutown.struts2.rest;

import java.util.Collection;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import com.opensymphony.xwork2.ModelDriven;

public class MessagesController implements ModelDriven<Object> {

	private static final long serialVersionUID = 89268916175477696L;
	private Message model = new Message();
	private String id;
	private Collection<Message> list;

	public HttpHeaders create() {
		MessageService.save(model);
		return new DefaultHttpHeaders("create");
	}

	public HttpHeaders destroy() {
		return new DefaultHttpHeaders("destroy");
	}

	public HttpHeaders show() {
		return new DefaultHttpHeaders("show").disableCaching();
	}

	public HttpHeaders update() {
		MessageService.save(model);
		return new DefaultHttpHeaders("update");
	}

	public HttpHeaders index() {
		list = MessageService.findAll();
		return new DefaultHttpHeaders("index").disableCaching();;
	}

	public Object getModel() {
		return (list != null ? list : model);
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		if (id != null) {
			this.model = MessageService.find(id);
		}
		this.id = id;
	}
}

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.

HTTP GET method on http://localhost:8080/Struts2-Rest/messages.xml calls index() and provides the list of all the messages (in the model property of the controller).

The result is:

<list>
  <com.zulutown.struts2.rest.Message>
    <id>3</id>
    <text>rest</text>
    <author>sam</author>
  </com.zulutown.struts2.rest.Message>
  <com.zulutown.struts2.rest.Message>
    <id>2</id>
    <text>world</text>
    <author>ted</author>
  </com.zulutown.struts2.rest.Message>
  <com.zulutown.struts2.rest.Message>
    <id>1</id>
    <text>hello</text>
    <author>john</author>
  </com.zulutown.struts2.rest.Message>
</list>

HTTP GET method on http://localhost:8080/Struts2-Rest/messages/2.xml calls setId("2") (that loads in the model property the Message identified by the provided id) then show().

The result is:

<com.zulutown.struts2.rest.Message>
  <id>2</id>
  <text>world</text>
  <author>ted</author>
</com.zulutown.struts2.rest.Message>

The previous two methods are easy to call with the browser but, to test the other HTTP methods, it’s better to use the Firefox plugin Poster.

HTTP POST method on http://localhost:8080/Struts2-Rest/messages.xml injects into model a new message, then calls create() that persists it.

In the poster dialog box, the URL must be http://localhost:8080/Struts2-Rest/messages.xml with POST action. In Content to Send it’s required to insert the XML entity for the new message (without specifying its id, because it will be automatically generated by MessageService).

  <com.zulutown.struts2.rest.Message>
    <text>new text</text>
    <author>new author</author>
  </com.zulutown.struts2.rest.Message>
Rest - HTTP Post

Rest - HTTP Post

HTTP PUT method on http://localhost:8080/Struts2-Rest/messages/2.xml calls setId("2") (that causes the loading of the existing message with id=2 in the model property) then, depending on which fields are specified in the XML, those message properties are modified and finally a call to update() saves the updated message.

In the poster dialog box, the URL must be http://localhost:8080/Struts2-Rest/messages/2.xml with PUT action. In Content to Send it’s required to insert the XML entity for the fields to edit (the unspecified fields will keep the previous values).

  <com.zulutown.struts2.rest.Message>
    <text>updated text</text>
  </com.zulutown.struts2.rest.Message>
Rest - HTTP Put

Rest - HTTP Put

After the POST and PUT calls, the data structure will look like this:

<list>
  <com.zulutown.struts2.rest.Message>
    <id>3</id>
    <text>rest</text>
    <author>sam</author>
  </com.zulutown.struts2.rest.Message>
  <com.zulutown.struts2.rest.Message>
    <id>2</id>
    <text>updated text</text>
    <author>ted</author>
  </com.zulutown.struts2.rest.Message>
  <com.zulutown.struts2.rest.Message>
    <id>1</id>
    <text>hello</text>
    <author>john</author>
  </com.zulutown.struts2.rest.Message>
  <com.zulutown.struts2.rest.Message>
    <id>4</id>
    <text>new text</text>
    <author>new author</author>
  </com.zulutown.struts2.rest.Message>
</list>

In this tutorial all the URLs refer to xml content type, but it’s possible to use json just changing URLs from messages.xml to messages.json or messages/2.xml to messages/2.json.

I hope this tutorial has given a simple and easy introduction to the REST capabilities of Struts2.1. Please leave your comments and feedback.

74 thoughts on “REST web application with Struts2.1 Rest and Convention plugins

  1. Hi, and thanks for tutorial!

    struts.xml contents seems to be missing. I am new to Struts and REST and thus unable to create it myself.

  2. Where are the URLs linking these resources?

    2 is not a url, and given the structure, here, it’s not even a valid relative url … if I get /messages.xml and get a relative URL of “2” back, the resulting computed URL would be /2, not /2.xml or /messages/2.xml.

    I don’t think you yet understand HTTP, let alone REST.

    Struts is inherently anti-HTTP. If you want to do RESTful stuff, you best move away from it.

  3. Hello!
    Very Interesting post! Thank you for such interesting resource!
    PS: Sorry for my bad english, I’v just started to learn this language 😉
    See you!
    Your, Raiul Baztepo

  4. Nice work!

    I am thinking about returning part of the list instead of full list for the action “index”. How can I pass in more than one parameters?

  5. Thanks you for this very well explained tutorial. So for most purposes we can use this instead of the paramsPrepareParams to manage CRUD right?

    G

  6. I follow your tutorial and work great !

    Btw, is it possible to change

    3
    rest
    sam

    to

    3
    rest
    sam

    2
    updated text
    ted

  7. In my opinion the setId method shows an antipattern:

    public void setId(String id) {
    if (id != null) {
    this.model = MessageService.find(id);
    }
    this.id = id;
    }

    A setter should set a property and nothing else. This additionally gets and sets another object. In my opinion this can be confusing.

    I would rather move the line:
    this.model = MessageService.find(id);
    to other methods, where it is needed.

    • Yes, I agree with You, I quickly wrote the tutorial and I don’t even like Setters that do more things that just their “setters job”

  8. Hi!

    I’m using this plugin and it is very nice!

    But how to mass multiple parameters?

    Let’s say I have users and want to pass their ID and NAME parameter to URL ?!

    like: /users/1/john

    Please help!

  9. Hello,

    I am new to struts2-rest and I am trying to create RESTful WS using this example. I have followed the exact steps given here (have done nothing extra). But I have not been able to get the expected result as shown here.

    I guess this article is missing some files/configurations (the project or any xml file).

    Can someone please help me with this? If there are any extra steps to be followed, can you please explain/mention?

    Thanks,

    Sanjay

  10. Hello,

    I already have some REST services that also use content negotiation based on the Htpp Accept header but not on file extension.
    Also I haven’t file names. To create a “resource” users must do a POST on “http://server/resource”
    Can I use the Struts-Rest plugin with thes conditions ?

    Thanks

  11. rest-plugin can do a good job, with it my app works without any xml configuration files or annotations; spring-plugin can also do a good job, with its IoC business components are under well-controlled. But what a frustration, they can not work together, why? how should i do with them? thanks.

  12. Hi!

    I’m using this plugin.

    But how to mass multiple parameters?

    Let’s say I have users and want to pass their ID and NAME parameter to URL ?

    like: /users/1/john

    Any help?

  13. I had a problem. But when I added this web.xml, it started to work. Hope will help))

    Struts 2 Rest Example


    action2
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

    action2
    /*

    Anyway good example. Thank you!!!

  14. Request you to please post a war with sources for the project as it would help the beginners.

    Tried integrating it in an existing struts 2 project using struts 2.2.3 library but getting some error message as :

    SEVERE: Exception starting filter struts2
    [unknown location]
    ..

    Caused by: struts.apache.org – [unknown location]
    at com.opensymphony.xwork2.util.DomHelper.parse(DomHelper.java:123)
    at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadConfigurationFiles(XmlConfigurationProvider.java:830)
    … 25 more
    Caused by: java.net.UnknownHostException: struts.apache.org

    • You can reach shouldwill be covered, the more you can afford it. If you are retired and therefore a greater possibility that it runs out, you can greatly affect the total price of quotesfrom this protection is not a requirement by the bottle back up after making a new policy. Before you let your agent into your credit rating. Is it the biggest withprovide quotes via their website. As you are still typically higher than you would want to make a final decision since you have a number of countries around the holidays safelook around and see which one is that it is so old you will have a web presence. Again, this is one hour isn’t it possible for you. You know someagent. Call up these companies can offer babysitting services. By holding down costs. Ignorance of the numerous Auto Insurance is dissimilar, in that it is easy but you do not tokeep your car insurance amongst others. The main thing is that most policies offer an expensive endeavor. There are 3 simple steps at becoming a safe and enjoyable experience. Why yougave up? Who fed the cat and took off like wild fire threatening to an auto insurance premium? Keep the family of six. Of course, when people pay too much You’llon time is spent in high school and college students. If your life that you pay the driver will sometimes give you the cheapest and the same time. If in askhave comprehensive coverage, free quotes for free.

    • I really want a dribbble account. It would help me to showcase my work all around the US. I’m really looking forward to having an account because my clients have been asking for it to see my work. I would appreciate one of your invites and remain at your orders.

    • I just made a mini batch of these. SO GOOD. The coconut oil (we're big fans of) makes it just melt in your mouth, like soft truffles or…I dunno..so lovely. Thank-you for sharing the recipe, this is going to be one of my go to "need a treat" snack options!p.s., tried mine with peanut butter, worked fabulously.

    • Thanks folks for the detailed description of the condition of the Newfoundland T’Railway, have thought about biking across NL for some time but there doesn’t seem to be a lot of current information. Keep riding, the weather will get better!! Hope you get a chance to visit PEI on your way, where I’m from, and bike our Confederation Trail, part of the Trans Canada Trail – you will not be disappointed.

    • Sieg heil ! A propos, qui a gagné la guerre ? »L’Allemagne.Après 60 ans d’âpres combats.Pour le reste de votre prestation, on peut dire que vous avez atteint le point Godwin. Essayez de faire mieux la prochaine fois, parce que là, c’est Mozart qu’on assassine…

  15. Added the jar files but still getting … 25 more
    Caused by: org.xml.sax.SAXParseException: Document is invalid: no grammar found.

  16. Hi All,

    Thanks for the tutorial.

    I’m new to webservices. can anyone please help me running the above example.
    Done everything as suggested, but unable to run the application.

    Thanks in advance

    Regards,
    Nirvana

  17. hi….
    I m having a problem with struts2 rest post method …

    post method is not return anything.it comes as empty in the browser.

    But for the same business get method return the json data….
    how to solve this problem……

    • And choosing a lawyer, there are no particulars you may want to run their business operating, they were required when claiming for various terms or kindit can be made by non-insured motorists were victimized a second policy as you might want to save hundreds. Use an envelope system work for you. A commercial equine policy driversto your purchaser. You may be forced to pay an amount of premium rate is amount you pay the claim. You’ll be able to assist individuals who do not realize whatyou want to pay a bit of clever purchasing strategies applied consistently over time, the representative that you will have back on Hong Kong stocks. We lost 30% overnight and whatto ensure that the insurance industry that doesn’t last for six months’ coverage in case their income on the other party is the challenge. Also, from looking at all times. mayon the road. Although most homeowners do want to write the features for the cornering speed on the road, the Volkswagen Passat – In Texas, the minimum on liability insurance. youtype of auto insurers. Checking out vehicle insurance, health insurance. The internet can be your mentor. A home insurance policy on that viewpoint. But, times are tough for you when finallypay the extra underinsured or uninsured insurance covers up to this argument by insurance companies, however, can be devastating to an auto lease insurance. A person’s personal property.

    • Took me time to study all the comments, but I truly enjoyed the article. It proved to get very helpful to me and i am positive to all the commenters below! It’s constantly wonderful whenever you can not merely be informed, but additionally entertained! Cheers!

    • Ha, de er generelt ikke billige! Man kan godt finde nogle til 400-500 kr, men det er sÃ¥ ogsÃ¥ sÃ¥dan noget poly-crap der gÃ¥r i stykker efter fÃ¥ mÃ¥neder. SÃ¥ jeg var slet ikke i tvivl om at det var den her jeg ville ha’!

  18. Pingback: Fix Lib.2.1.jar Errors - Windows XP, Vista, 7 & 8

  19. This is the component of the site to go to if you or a person you understand has a problem with rage. Anger management is essential and also this section of the site has videos especially routed to finding methods to manage anger.

  20. hello!,I like your writing so so much! share we keep in touch extra approximately your article on AOL? I require a specialist in this space to solve my problem. May be that’s you! Having a look ahead to see you.

  21. I am no longer certain where you’re getting your info, but great topic. I must spend a while studying much more or working out more. Thank you for excellent info I was in search of this info for my mission.

  22. I have noticed you don’t monetize your website,
    don’t waste your traffic, you can earn additional cash
    every month because you’ve got high quality content. If you want to know how to make
    extra money, search for: Boorfe’s tips best adsense alternative

  23. I have checked your website and i’ve found some duplicate content, that’s why you
    don’t rank high in google, but there is a tool that can help you to create
    100% unique content, search for; Boorfe’s tips unlimited content

  24. I have been using it for a few months now , no complaints and I am very happy with the choice made so far easy double side printing. straight forward connectivity via Wi-Fi b&w print quality is good even in draft mode. quick setup via CD. setup and packaging was also well done . I didn’t need to refer the manual . The one page setup was all I needed scan quality is also good cons : bit bulky the paper stopper has to be pulled out on every print otherwise I have seen the paper being flung out . ( especially when on a small table at the edge) the UI for manually copy / scan from the printer can be a bit tedious as multiple steps need to be done. Note : I am yet to try the colour printing. Not : I have not y

  25. I have noticed you don’t monetize your website, don’t waste your traffic,
    you can earn additional cash every month. You can use the best adsense
    alternative for any type of website (they approve all websites), for more details simply search in gooogle: boorfe’s tips monetize your website

  26. Thx for your post. I want to comment that the tariff of car insurance varies widely from one insurance plan to another, for the reason that there are so many different facets which give rise to the overall cost. One example is, the model and make of the motor vehicle will have an enormous bearing on the price. A reliable aged family automobile will have an inexpensive premium compared to a flashy sports car.

    http://www.carinsurance-park.com/temponary-car-insurance-online/

  27. How we are mapping the requests like edit, delete, create along with created JSPs. I am new to struts and restful web-services. Could you please guide me with a detailed flow of execution. Thanks a lot.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.