Tuesday, July 17, 2012

Struts2 with Rest Application


* In Struts2 Rest application must contains some naming conventions

struts.xml:
----------
<struts>
<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"/>
<constant name="struts.convention.package.locators" value="example"/>
</struts>


* Ur class name  must start with action  name with suffix as Controller (e.g suppose ur action name is orders ur class name must be OrdersController).

*Another role is ur controller class must be contains in the "example" package.(it means which package are mentioned in the constant (e.g <constant name="struts.convention.package.locators" value="example">).

web.xml:
-------
<web-app id="starter" version="2.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/j2ee" xsi:schemalocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Struts 2 Rest Example</display-name>
<filter>
<filter-name>action2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>action2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

</web-app>


index.jsp:
---------
<%
response.sendRedirect("usersviews");
%>


below java class contains in the  org.apache.struts2.rest.example packages


Order.java:
----------
package org.apache.struts2.rest.example;

public class Order {
    String id;
    String clientName;
    int amount;
 
    public Order() {}
 
    public Order(String id, String clientName, int amount) {
        super();
        this.id = id;
        this.clientName = clientName;
        this.amount = amount;
    }
    public int getAmount() {
        return amount;
    }
    public void setAmount(int amount) {
        this.amount = amount;
    }
    public String getClientName() {
        return clientName;
    }
    public void setClientName(String clientName) {
        this.clientName = clientName;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

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

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final Order other = (Order) obj;
        if (amount != other.amount)
            return false;
        if (clientName == null) {
            if (other.clientName != null)
                return false;
        } else if (!clientName.equals(other.clientName))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }
   }


OrdersController.java:
----------------------

package org.apache.struts2.rest.example;

import java.util.Collection;

import org.apache.struts2.dispatcher.ServletActionRedirectResult;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Namespaces;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.InterceptorRef;

import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Validateable;
import com.opensymphony.xwork2.ValidationAwareSupport;

@Results({ @Result(name = "success", type = "redirectAction", params = {
"actionName", "orders" }) })
public class OrdersController extends ValidationAwareSupport implements
ModelDriven<Object>, Validateable {

private Order model = new Order();
private String id;
private Collection<Order> list;
private OrdersService ordersService = new OrdersService();

// GET /orders/1
public HttpHeaders show() {
return new DefaultHttpHeaders("show");
}

// GET /orders
public HttpHeaders index() {
list = ordersService.getAll();
return new DefaultHttpHeaders("index").disableCaching();
}

// GET /orders/1/edit
public String edit() {
return "edit";
}

// GET /orders/new
public String editNew() {
model = new Order();
return "editNew";
}

// GET /orders/1/deleteConfirm
public String deleteConfirm() {
return "deleteConfirm";
}

// DELETE /orders/1
public String destroy() {
ordersService.remove(id);
addActionMessage("Order removed successfully");
return "success";
}

// POST /orders
public HttpHeaders create() {
ordersService.save(model);
addActionMessage("New order created successfully");
return new DefaultHttpHeaders("success").setLocationId(model.getId());
}

// PUT /orders/1
public String update() {
ordersService.save(model);
addActionMessage("Order updated successfully");
return "success";
}

public void validate() {
if (model.getClientName() == null
|| model.getClientName().length() == 0) {
addFieldError("clientName", "The client name is empty");
}
}

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

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

}
 OrdersService.java: 
 -------------------    

package org.apache.struts2.rest.example;

import java.util.*;

public class OrdersService {

    private static Map<String,Order> orders = new HashMap<String,Order>();
    private static int nextId = 6;
    static {
        orders.put("3", new Order("3", "Bob", 33));
        orders.put("4", new Order("4", "Sarah", 44));
        orders.put("5", new Order("5", "Jim", 66));
    }

    public Order get(String id) {
        return orders.get(id);
    }

    public List<Order> getAll() {
        return new ArrayList<Order>(orders.values());
    }

    public void save(Order order) {
        if (order.getId() == null) {
            order.setId(String.valueOf(nextId++));
        }

        orders.put(order.getId(), order);
    }

    public void remove(String id) {
        orders.remove(id);
    }

}






 jsps:(these jsps contains in the content folder in the webcontent folder) 


 orders-edit.jsp: 
----------------   
<!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" %> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
<head> 
 <title>Order <s:property value="id" /></title> </head> 
<body> <s:form method="post" action="%{#request.contextPath}/orders/%{id}"> 
 <s:hidden name="_method" value="put" /> 
 <table> <s:textfield name="id" label="ID" disabled="true"/> 
 <s:textfield name="clientName" label="Client"/> 
 <s:textfield name="amount" label="Amount" /> 
 <tr> <td colspan="2">
 <s:submit /> </td> </table> </s:form>
 <a href="<%=request.getContextPath() %>/orders">Back to Orders</a> </body> </html>


 orders-editNew.jsp:
 -------------------  
<%@taglib prefix="s" uri="/struts-tags" %>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>New Order</title>
</head>
<body>
    <s:form method="post" action="%{#request.contextPath}/orders">
    <table>
        <s:textfield name="clientName" label="Client"/>
        <s:textfield name="amount" label="Amount" />
        <tr>
            <td colspan="2">
                <s:submit />
            </td>
    </table>
    </s:form>    
    <a href="<%=request.getContextPath() %>/orders">Back to Orders</a>
</body>
</html>


  orders-index.jsp: 
-----------------  
 <%@taglib prefix="s" uri="/struts-tags" %>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Orders</title>
</head>
<body>
    <s:actionmessage />
    <table>
        <tr>
            <th>ID</th>
            <th>Client</th>
            <th>Amount</th>
            <th>Actions</th>
        </tr>
        <s:iterator value="model">
        <tr>
            <td>${id}</td>
            <td>${clientName}</td>
            <td>${amount}</td>
            <td><a href="orders/${id}">View</a> |
                <a href="orders/${id}/edit">Edit</a> |
                <a href="orders/${id}/deleteConfirm">Delete</a></td>
        </tr>
        </s:iterator>
    </table>    
    <a href="orders/new">Create a new order</a>
</body>
</html>
 orders-show.jsp:
 ----------------     
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head> <title>Order ${id}</title> </head> 
<body> 
 <table> <tr> <th>ID</th>
 <td>${id}</td> 
 </tr> 
 <tr> <th>Client</th>
 <td>${clientName}</td> 
 </tr> 
 <tr> <th>Amount</th> <td>${amount}</td> </tr>
 </table> 
 <a href="../orders">Back to Orders</a> </body> </html>

 orders-deleteConfirm.jsp 
------------------------    
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Order ${id}</title>
</head>
<body>
    <form action="../${id}?_method=DELETE" method="post">
        <p>
            Are you sure you want to delete order ${id}?
        </p>
        <div>
            <input type="submit" value="Delete" />
            <input type="button" value="Cancel" onclick="window.location.href = '../../orders'" />
        </div>
    </form>
    <br />
    <a href="../../orders">Back to Orders</a>
</body>
</html>



  Note: Here jsp names must be action name-return type      <p>


jar files:
-------

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.1.jar


ezmorph-1.0.3.jar
freemarker-2.3.13.jar
json-lib-2.1-jdk15.jar
log4j-1.2.16.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
struts2-rest-showcase-2.3.1.1-javadoc.jar
xpp3_min-1.1.3.4.O.jar
xstream-1.2.2.jar
xwork-2.1.2.jar



10 comments:

  1. Hi, Nice post thanks. It would be great if you can explaing how different request uri's are getting mapped to different methods eventhough you have not configured them anywhere. For instance /orders/new maps to editNew(), if i keep a new method how it will be mapped with which uri. I am new to struts and rest, thanks in advance.

    ReplyDelete
  2. Awesome, but how to call my web services REST using jQuery JSONP ??? support The REST Plugin call JSONP???

    ReplyDelete
  3. Do i need Maven to run this project?

    ReplyDelete
    Replies
    1. you can download above jars from maven or download manually add to ur build path

      Delete
    2. Yeah! but do i need maven integration separately coz i have older version of eclipse(Indigo)

      Delete
  4. HTTP Status 404 - There is no Action mapped for namespace / and action name usersviews is showing please help

    ReplyDelete
  5. How Will I get XML response ?? what extra code we need to add for that

    ReplyDelete
  6. I am getting HTTP Status 404 - There is no Action mapped for namespace / and action name orders. this error ?? How i can sol

    ReplyDelete
  7. hi is there any way to convert show() action to POST Http method?

    ReplyDelete