mirror of
https://github.com/apache/struts.git
synced 2026-08-06 07:06:58 +00:00
Moving rest plugin into trunk
WW-2321 git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@594765 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
@@ -0,0 +1,6 @@
|
||||
Apache Struts
|
||||
|
||||
Copyright 2006 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>2.1.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-rest-plugin</artifactId>
|
||||
<version>2.1.1-SNAPSHOT</version>
|
||||
<name>Struts 2 Plugin</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-codebehind-plugin</artifactId>
|
||||
<version>${pom.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.json-lib</groupId>
|
||||
<artifactId>json-lib</artifactId>
|
||||
<classifier>jdk15</classifier>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mockobjects</groupId>
|
||||
<artifactId>mockobjects-core</artifactId>
|
||||
<version>0.09</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-mock</artifactId>
|
||||
<version>1.2.8</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>1.2.8</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-apps</artifactId>
|
||||
<version>2.1.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-rest-showcase</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>2.1.1-SNAPSHOT</version>
|
||||
<name>Struts 2 Rest Showcase Example</name>
|
||||
<description>Struts 2 Rest Showcase Example</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-rest-plugin</artifactId>
|
||||
<version>2.1.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>struts2-rest-showcase</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>maven-jetty-plugin</artifactId>
|
||||
<version>6.0.1</version>
|
||||
<configuration>
|
||||
<scanIntervalSeconds>10</scanIntervalSeconds>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package org.apache.struts2.rest.example;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.struts2.config.Result;
|
||||
import org.apache.struts2.config.Results;
|
||||
import org.apache.struts2.dispatcher.ServletActionRedirectResult;
|
||||
import org.apache.struts2.rest.DefaultHttpHeaders;
|
||||
import org.apache.struts2.rest.HttpHeaders;
|
||||
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.Validateable;
|
||||
import com.opensymphony.xwork2.ValidationAwareSupport;
|
||||
|
||||
@Results({
|
||||
@Result(name="success", type=ServletActionRedirectResult.class, value="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);
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<web-app id="starter" version="2.4"
|
||||
xmlns="http://java.sun.com/xml/ns/j2ee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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>
|
||||
|
||||
|
||||
<!-- Filters -->
|
||||
<!-- START SNIPPET: filter -->
|
||||
<filter>
|
||||
<filter-name>action2</filter-name>
|
||||
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
|
||||
<init-param>
|
||||
<param-name>actionPackages</param-name>
|
||||
<param-value>org.apache.struts2.rest.example</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>devMode</param-name>
|
||||
<param-value>true</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
<!-- END SNIPPET: filter -->
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>action2</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- Welcome file lists -->
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
</web-app>
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
<% response.sendRedirect("orders"); %>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html PUBLIC
|
||||
"-//W3C//DTD XHTML 1.1 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<!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>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<!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>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>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!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>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>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html PUBLIC
|
||||
"-//W3C//DTD XHTML 1.1 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<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>
|
||||
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.rest.handler.ContentTypeHandler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Manages {@link ContentTypeHandler} instances and uses them to
|
||||
* process results
|
||||
*/
|
||||
public class ContentTypeHandlerManager {
|
||||
|
||||
Map<String,ContentTypeHandler> handlers = new HashMap<String,ContentTypeHandler>();
|
||||
String defaultExtension;
|
||||
public static final String STRUTS_REST_HANDLER_OVERRIDE_PREFIX = "struts.rest.handlerOverride.";
|
||||
|
||||
@Inject("struts.rest.defaultExtension")
|
||||
public void setDefaultExtension(String name) {
|
||||
this.defaultExtension = name;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setContainer(Container container) {
|
||||
Set<String> names = container.getInstanceNames(ContentTypeHandler.class);
|
||||
for (String name : names) {
|
||||
ContentTypeHandler handler = container.getInstance(ContentTypeHandler.class, name);
|
||||
|
||||
// Check for overriding handlers for the current extension
|
||||
String overrideName = container.getInstance(String.class, STRUTS_REST_HANDLER_OVERRIDE_PREFIX +handler.getExtension());
|
||||
if (overrideName != null) {
|
||||
if (!handlers.containsKey(handler.getExtension())) {
|
||||
handler = container.getInstance(ContentTypeHandler.class, overrideName);
|
||||
} else {
|
||||
// overriding handler has already been registered
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this.handlers.put(handler.getExtension(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the handler for the request by looking at the extension
|
||||
* @param req The request
|
||||
* @return The appropriate handler
|
||||
*/
|
||||
public ContentTypeHandler getHandlerForRequest(HttpServletRequest req) {
|
||||
String extension = findExtension(req.getRequestURI());
|
||||
if (extension == null) {
|
||||
extension = defaultExtension;
|
||||
}
|
||||
return handlers.get(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the result using handlers to generate content type-specific content
|
||||
*
|
||||
* @param actionConfig The action config for the current request
|
||||
* @param methodResult The object returned from the action method
|
||||
* @param target The object to return, usually the action object
|
||||
* @return The new result code to process
|
||||
* @throws IOException If unable to write to the response
|
||||
*/
|
||||
public String handleResult(ActionConfig actionConfig, Object methodResult, Object target)
|
||||
throws IOException {
|
||||
String resultCode = null;
|
||||
HttpServletRequest req = ServletActionContext.getRequest();
|
||||
HttpServletResponse res = ServletActionContext.getResponse();
|
||||
if (target instanceof ModelDriven) {
|
||||
target = ((ModelDriven)target).getModel();
|
||||
}
|
||||
|
||||
boolean statusNotOk = false;
|
||||
if (methodResult instanceof HttpHeaders) {
|
||||
HttpHeaders info = (HttpHeaders) methodResult;
|
||||
resultCode = info.apply(req, res, target);
|
||||
if (info.getStatus() != SC_OK) {
|
||||
|
||||
// Don't return content on a not modified
|
||||
if (info.getStatus() == SC_NOT_MODIFIED) {
|
||||
target = null;
|
||||
} else {
|
||||
statusNotOk = true;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
resultCode = (String) methodResult;
|
||||
}
|
||||
|
||||
// Don't return any content for PUT, DELETE, and POST where there are no errors
|
||||
if (!statusNotOk && !"get".equalsIgnoreCase(req.getMethod())) {
|
||||
target = null;
|
||||
}
|
||||
|
||||
ContentTypeHandler handler = getHandlerForRequest(req);
|
||||
if (handler != null) {
|
||||
String extCode = resultCode+"-"+handler.getExtension();
|
||||
if (actionConfig.getResults().get(extCode) != null) {
|
||||
resultCode = extCode;
|
||||
} else {
|
||||
StringWriter writer = new StringWriter();
|
||||
resultCode = handler.fromObject(target, resultCode, writer);
|
||||
String text = writer.toString();
|
||||
if (text.length() > 0) {
|
||||
byte[] data = text.getBytes("UTF-8");
|
||||
res.setContentLength(data.length);
|
||||
res.setContentType(handler.getContentType());
|
||||
res.getOutputStream().write(data);
|
||||
res.getOutputStream().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultCode;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the extension in the url
|
||||
*
|
||||
* @param url The url
|
||||
* @return The extension
|
||||
*/
|
||||
protected String findExtension(String url) {
|
||||
int dotPos = url.lastIndexOf('.');
|
||||
int slashPos = url.lastIndexOf('/');
|
||||
if (dotPos > slashPos && dotPos > -1) {
|
||||
return url.substring(dotPos+1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.rest.handler.ContentTypeHandler;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
|
||||
/**
|
||||
* Uses the content handler to apply the request body to the action
|
||||
*/
|
||||
public class ContentTypeInterceptor implements Interceptor {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
ContentTypeHandlerManager selector;
|
||||
|
||||
@Inject
|
||||
public void setContentTypeHandlerSelector(ContentTypeHandlerManager sel) {
|
||||
this.selector = sel;
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
|
||||
public void init() {}
|
||||
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
HttpServletRequest request = ServletActionContext.getRequest();
|
||||
ContentTypeHandler handler = selector.getHandlerForRequest(request);
|
||||
|
||||
Object target = invocation.getAction();
|
||||
if (target instanceof ModelDriven) {
|
||||
target = ((ModelDriven)target).getModel();
|
||||
}
|
||||
|
||||
if (request.getContentLength() > 0) {
|
||||
handler.toObject(request.getReader(), target);
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import org.apache.struts2.config.ClasspathPackageProvider;
|
||||
|
||||
import com.opensymphony.xwork2.util.ResolverUtil.ClassTest;
|
||||
|
||||
/**
|
||||
* Checks for actions ending in Controller indicating a Rest controller
|
||||
*/
|
||||
public class ControllerClasspathPackageProvider extends ClasspathPackageProvider {
|
||||
|
||||
@Override
|
||||
protected ClassTest createActionClassTest() {
|
||||
return new ClassTest() {
|
||||
// Match Action implementations and classes ending with "Controller"
|
||||
public boolean matches(Class type) {
|
||||
return (type.getSimpleName().endsWith("Controller"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getClassSuffix() {
|
||||
return "Controller";
|
||||
}
|
||||
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Default implementation of rest info that uses fluent-style construction
|
||||
*/
|
||||
public class DefaultHttpHeaders implements HttpHeaders {
|
||||
String resultCode;
|
||||
int status = SC_OK;
|
||||
Object etag;
|
||||
Object locationId;
|
||||
String location;
|
||||
boolean disableCaching;
|
||||
boolean noETag = false;
|
||||
Date lastModified;
|
||||
|
||||
public DefaultHttpHeaders() {}
|
||||
|
||||
public DefaultHttpHeaders(String result) {
|
||||
resultCode = result;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders renderResult(String code) {
|
||||
this.resultCode = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders withStatus(int code) {
|
||||
this.status = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders withETag(Object etag) {
|
||||
this.etag = etag;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders withNoETag() {
|
||||
this.noETag = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders setLocationId(Object id) {
|
||||
this.locationId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders setLocation(String loc) {
|
||||
this.location = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders lastModified(Date date) {
|
||||
this.lastModified = date;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DefaultHttpHeaders disableCaching() {
|
||||
this.disableCaching = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.apache.struts2.rest.HttpHeaders#apply(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
|
||||
*/
|
||||
public String apply(HttpServletRequest request, HttpServletResponse response, Object target) {
|
||||
|
||||
if (disableCaching) {
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
}
|
||||
if (lastModified != null) {
|
||||
response.setDateHeader("Last-Modified", lastModified.getTime());
|
||||
}
|
||||
if (etag == null && !noETag) {
|
||||
etag = String.valueOf(target.hashCode());
|
||||
}
|
||||
if (etag != null) {
|
||||
response.setHeader("ETag", etag.toString());
|
||||
}
|
||||
|
||||
if (locationId != null) {
|
||||
String url = request.getRequestURL().toString();
|
||||
int lastSlash = url.lastIndexOf("/");
|
||||
int lastDot = url.lastIndexOf(".");
|
||||
if (lastDot > lastSlash && lastDot > -1) {
|
||||
url = url.substring(0, lastDot)+"/"+locationId+url.substring(lastDot);
|
||||
} else {
|
||||
url += "/"+locationId;
|
||||
}
|
||||
response.setHeader("Location", url);
|
||||
} else if (location != null) {
|
||||
response.setHeader("Location", location);
|
||||
}
|
||||
|
||||
if (status == SC_OK && !disableCaching) {
|
||||
boolean etagNotChanged = false;
|
||||
boolean lastModifiedNotChanged = false;
|
||||
String reqETag = request.getHeader("If-None-Match");
|
||||
if (etag != null) {
|
||||
if (etag.equals(reqETag)) {
|
||||
etagNotChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
String reqLastModified = request.getHeader("If-Modified-Since");
|
||||
if (lastModified != null) {
|
||||
if (String.valueOf(lastModified.getTime()).equals(reqLastModified)) {
|
||||
lastModifiedNotChanged = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((etagNotChanged && lastModifiedNotChanged) ||
|
||||
(etagNotChanged && reqLastModified == null) ||
|
||||
(lastModifiedNotChanged && reqETag == null)) {
|
||||
status = SC_NOT_MODIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
response.setStatus(status);
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Type-safe rest-related informtion to apply to a response
|
||||
*/
|
||||
public interface HttpHeaders {
|
||||
|
||||
/**
|
||||
* Applies the configured information to the response
|
||||
* @param request The request
|
||||
* @param response The response
|
||||
* @param target The target object, usually the action
|
||||
* @return The result code to process
|
||||
*/
|
||||
String apply(HttpServletRequest request,
|
||||
HttpServletResponse response, Object target);
|
||||
|
||||
/**
|
||||
* The HTTP status code
|
||||
*/
|
||||
int getStatus();
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionEventListener;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.DefaultActionInvocation;
|
||||
import com.opensymphony.xwork2.ModelDriven;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
import com.opensymphony.xwork2.UnknownHandler;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.PreResultListener;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.rest.handler.ContentTypeHandler;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Extends the usual {@link ActionInvocation} to add support for processing the object returned
|
||||
* from the action execution. This allows us to support methods that return {@link HttpHeaders}
|
||||
* as well as apply content type-specific operations to the result.
|
||||
*/
|
||||
public class RestActionInvocation extends DefaultActionInvocation {
|
||||
|
||||
private static final long serialVersionUID = 3485701178946428716L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RestActionInvocation.class);
|
||||
|
||||
private ContentTypeHandlerManager handlerSelector;
|
||||
|
||||
protected RestActionInvocation(Map extraContext, boolean pushAction) throws Exception {
|
||||
super(extraContext, pushAction);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setMimeTypeHandlerSelector(ContentTypeHandlerManager sel) {
|
||||
this.handlerSelector = sel;
|
||||
}
|
||||
|
||||
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
|
||||
String methodName = proxy.getMethod();
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Executing action method = " + actionConfig.getMethodName());
|
||||
}
|
||||
|
||||
String timerKey = "invokeAction: "+proxy.getActionName();
|
||||
try {
|
||||
UtilTimerStack.push(timerKey);
|
||||
|
||||
boolean methodCalled = false;
|
||||
Object methodResult = null;
|
||||
Method method = null;
|
||||
try {
|
||||
method = getAction().getClass().getMethod(methodName, new Class[0]);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// hmm -- OK, try doXxx instead
|
||||
try {
|
||||
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
|
||||
method = getAction().getClass().getMethod(altMethodName, new Class[0]);
|
||||
} catch (NoSuchMethodException e1) {
|
||||
// well, give the unknown handler a shot
|
||||
if (unknownHandler != null) {
|
||||
try {
|
||||
methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
|
||||
methodCalled = true;
|
||||
} catch (NoSuchMethodException e2) {
|
||||
// throw the original one
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!methodCalled) {
|
||||
methodResult = method.invoke(action, new Object[0]);
|
||||
}
|
||||
|
||||
return processResult(actionConfig, methodResult);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
|
||||
} catch (InvocationTargetException e) {
|
||||
// We try to return the source exception.
|
||||
Throwable t = e.getTargetException();
|
||||
|
||||
if (actionEventListener != null) {
|
||||
String result = actionEventListener.handleException(t, getStack());
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (t instanceof Exception) {
|
||||
throw(Exception) t;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
UtilTimerStack.pop(timerKey);
|
||||
}
|
||||
}
|
||||
|
||||
protected String processResult(ActionConfig actionConfig, Object methodResult) throws IOException {
|
||||
if (methodResult instanceof Result) {
|
||||
this.explicitResult = (Result) methodResult;
|
||||
return null;
|
||||
} else if (methodResult != null) {
|
||||
resultCode = handlerSelector.handleResult(actionConfig, methodResult, action);
|
||||
}
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
}
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.apache.struts2.dispatcher.mapper.DefaultActionMapper;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
*
|
||||
* This Restful action mapper enforces Ruby-On-Rails Rest-style mappings. If the method
|
||||
* is not specified (via '!' or 'method:' prefix), the method is "guessed" at using
|
||||
* ReST-style conventions that examine the URL and the HTTP method. Special care has
|
||||
* been given to ensure this mapper works correctly with the codebehind plugin so that
|
||||
* XML configuration is unnecessary.
|
||||
*
|
||||
* <p>
|
||||
* This mapper supports the following parameters:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li><code>struts.mapper.idParameterName</code> - If set, this value will be the name
|
||||
* of the parameter under which the id is stored. The id will then be removed
|
||||
* from the action name. Whether or not the method is specified, the mapper will
|
||||
* try to truncate the identifier from the url and store it as a parameter.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.indexMethodName</code> - The method name to call for a GET
|
||||
* request with no id parameter. Defaults to 'index'.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.getMethodName</code> - The method name to call for a GET
|
||||
* request with an id parameter. Defaults to 'show'.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.postMethodName</code> - The method name to call for a POST
|
||||
* request with no id parameter. Defaults to 'create'.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.putMethodName</code> - The method name to call for a PUT
|
||||
* request with an id parameter. Defaults to 'update'.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.deleteMethodName</code> - The method name to call for a DELETE
|
||||
* request with an id parameter. Defaults to 'destroy'.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.editMethodName</code> - The method name to call for a GET
|
||||
* request with an id parameter and the 'edit' view specified. Defaults to 'edit'.
|
||||
* </li>
|
||||
* <li><code>struts.mapper.newMethodName</code> - The method name to call for a GET
|
||||
* request with no id parameter and the 'new' view specified. Defaults to 'editNew'.
|
||||
* </li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The following URL's will invoke its methods:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li><code>GET: /movies => method="index"</code></li>
|
||||
* <li><code>GET: /movies/Thrillers => method="show", id="Thrillers"</code></li>
|
||||
* <li><code>GET: /movies/Thrillers;edit => method="edit", id="Thrillers"</code></li>
|
||||
* <li><code>GET: /movies/Thrillers/edit => method="edit", id="Thrillers"</code></li>
|
||||
* <li><code>GET: /movies/new => method="editNew"</code></li>
|
||||
* <li><code>POST: /movies => method="create"</code></li>
|
||||
* <li><code>PUT: /movies/Thrillers => method="update", id="Thrillers"</code></li>
|
||||
* <li><code>DELETE: /movies/Thrillers => method="destroy", id="Thrillers"</code></li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To simulate the HTTP methods PUT and DELETE, since they aren't supported by HTML,
|
||||
* the HTTP parameter "_method" will be used.
|
||||
* </p>
|
||||
* <!-- END SNIPPET: description -->
|
||||
*/
|
||||
public class RestActionMapper extends DefaultActionMapper {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(RestActionMapper.class);
|
||||
public static final String HTTP_METHOD_PARAM = "_method";
|
||||
private String idParameterName = "id";
|
||||
private String indexMethodName = "index";
|
||||
private String getMethodName = "show";
|
||||
private String postMethodName = "create";
|
||||
private String editMethodName = "edit";
|
||||
private String newMethodName = "editNew";
|
||||
private String deleteMethodName = "destroy";
|
||||
private String putMethodName = "update";
|
||||
|
||||
public RestActionMapper() {
|
||||
}
|
||||
|
||||
public String getIdParameterName() {
|
||||
return idParameterName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value=StrutsConstants.STRUTS_ID_PARAMETER_NAME)
|
||||
public void setIdParameterName(String idParameterName) {
|
||||
this.idParameterName = idParameterName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.indexMethodName")
|
||||
public void setIndexMethodName(String indexMethodName) {
|
||||
this.indexMethodName = indexMethodName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.getMethodName")
|
||||
public void setGetMethodName(String getMethodName) {
|
||||
this.getMethodName = getMethodName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.postMethodName")
|
||||
public void setPostMethodName(String postMethodName) {
|
||||
this.postMethodName = postMethodName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.editMethodName")
|
||||
public void setEditMethodName(String editMethodName) {
|
||||
this.editMethodName = editMethodName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.newMethodName")
|
||||
public void setNewMethodName(String newMethodName) {
|
||||
this.newMethodName = newMethodName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.deleteMethodName")
|
||||
public void setDeleteMethodName(String deleteMethodName) {
|
||||
this.deleteMethodName = deleteMethodName;
|
||||
}
|
||||
|
||||
@Inject(required=false,value="struts.mapper.putMethodName")
|
||||
public void setPutMethodName(String putMethodName) {
|
||||
this.putMethodName = putMethodName;
|
||||
}
|
||||
|
||||
public ActionMapping getMapping(HttpServletRequest request,
|
||||
ConfigurationManager configManager) {
|
||||
ActionMapping mapping = new ActionMapping();
|
||||
String uri = getUri(request);
|
||||
|
||||
uri = dropExtension(uri, mapping);
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
parseNameAndNamespace(uri, mapping, configManager);
|
||||
|
||||
handleSpecialParameters(request, mapping);
|
||||
|
||||
if (mapping.getName() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// handle "name!method" convention.
|
||||
String name = mapping.getName();
|
||||
int exclamation = name.lastIndexOf("!");
|
||||
if (exclamation != -1) {
|
||||
mapping.setName(name.substring(0, exclamation));
|
||||
mapping.setMethod(name.substring(exclamation + 1));
|
||||
}
|
||||
|
||||
String fullName = mapping.getName();
|
||||
// Only try something if the action name is specified
|
||||
if (fullName != null && fullName.length() > 0) {
|
||||
int lastSlashPos = fullName.lastIndexOf('/');
|
||||
String id = null;
|
||||
if (lastSlashPos > -1) {
|
||||
|
||||
// fun trickery to parse 'actionName/id/methodName' in the case of 'animals/dog/edit'
|
||||
int prevSlashPos = fullName.lastIndexOf('/', lastSlashPos - 1);
|
||||
if (prevSlashPos > -1) {
|
||||
mapping.setMethod(fullName.substring(lastSlashPos+1));
|
||||
fullName = fullName.substring(0, lastSlashPos);
|
||||
lastSlashPos = prevSlashPos;
|
||||
}
|
||||
id = fullName.substring(lastSlashPos+1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If a method hasn't been explicitly named, try to guess using ReST-style patterns
|
||||
if (mapping.getMethod() == null) {
|
||||
|
||||
// Handle uris with no id, possibly ending in '/'
|
||||
if (lastSlashPos == -1 || lastSlashPos == fullName.length() -1) {
|
||||
|
||||
// Index e.g. foo
|
||||
if (isGet(request)) {
|
||||
mapping.setMethod(indexMethodName);
|
||||
|
||||
// Creating a new entry on POST e.g. foo
|
||||
} else if (isPost(request)) {
|
||||
mapping.setMethod(postMethodName);
|
||||
}
|
||||
|
||||
// Handle uris with an id at the end
|
||||
} else if (id != null) {
|
||||
|
||||
// Viewing the form to edit an item e.g. foo/1;edit
|
||||
if (isGet(request) && id.endsWith(";edit")) {
|
||||
id = id.substring(0, id.length() - ";edit".length());
|
||||
mapping.setMethod(editMethodName);
|
||||
|
||||
// Viewing the form to create a new item e.g. foo/new
|
||||
} else if (isGet(request) && "new".equals(id)) {
|
||||
mapping.setMethod(newMethodName);
|
||||
|
||||
// Removing an item e.g. foo/1
|
||||
} else if (isDelete(request)) {
|
||||
mapping.setMethod(deleteMethodName);
|
||||
|
||||
// Viewing an item e.g. foo/1
|
||||
} else if (isGet(request)) {
|
||||
mapping.setMethod(getMethodName);
|
||||
|
||||
// Updating an item e.g. foo/1
|
||||
} else if (isPut(request)) {
|
||||
mapping.setMethod(putMethodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cut off the id parameter, even if a method is specified
|
||||
if (id != null) {
|
||||
if (!"new".equals(id)) {
|
||||
if (mapping.getParams() == null) {
|
||||
mapping.setParams(new HashMap());
|
||||
}
|
||||
mapping.getParams().put(idParameterName, new String[]{id});
|
||||
}
|
||||
fullName = fullName.substring(0, lastSlashPos);
|
||||
}
|
||||
|
||||
mapping.setName(fullName);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the name and namespace from the uri. Uses the configured package
|
||||
* namespaces to determine the name and id parameter, to be parsed later.
|
||||
*
|
||||
* @param uri
|
||||
* The uri
|
||||
* @param mapping
|
||||
* The action mapping to populate
|
||||
*/
|
||||
protected void parseNameAndNamespace(String uri, ActionMapping mapping,
|
||||
ConfigurationManager configManager) {
|
||||
String namespace, name;
|
||||
int lastSlash = uri.lastIndexOf("/");
|
||||
if (lastSlash == -1) {
|
||||
namespace = "";
|
||||
name = uri;
|
||||
} else if (lastSlash == 0) {
|
||||
// ww-1046, assume it is the root namespace, it will fallback to
|
||||
// default
|
||||
// namespace anyway if not found in root namespace.
|
||||
namespace = "/";
|
||||
name = uri.substring(lastSlash + 1);
|
||||
} else {
|
||||
// Try to find the namespace in those defined, defaulting to ""
|
||||
Configuration config = configManager.getConfiguration();
|
||||
String prefix = uri.substring(0, lastSlash);
|
||||
namespace = "";
|
||||
// Find the longest matching namespace, defaulting to the default
|
||||
for (Iterator i = config.getPackageConfigs().values().iterator(); i
|
||||
.hasNext();) {
|
||||
String ns = ((PackageConfig) i.next()).getNamespace();
|
||||
if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {
|
||||
if (ns.length() > namespace.length()) {
|
||||
namespace = ns;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name = uri.substring(namespace.length() + 1);
|
||||
}
|
||||
|
||||
mapping.setNamespace(namespace);
|
||||
mapping.setName(name);
|
||||
}
|
||||
|
||||
protected boolean isGet(HttpServletRequest request) {
|
||||
return "get".equalsIgnoreCase(request.getMethod());
|
||||
}
|
||||
|
||||
protected boolean isPost(HttpServletRequest request) {
|
||||
return "post".equalsIgnoreCase(request.getMethod());
|
||||
}
|
||||
|
||||
protected boolean isPut(HttpServletRequest request) {
|
||||
if ("put".equalsIgnoreCase(request.getMethod())) {
|
||||
return true;
|
||||
} else {
|
||||
return isPost(request) && "put".equalsIgnoreCase(request.getParameter(HTTP_METHOD_PARAM));
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isDelete(HttpServletRequest request) {
|
||||
if ("delete".equalsIgnoreCase(request.getMethod())) {
|
||||
return true;
|
||||
} else {
|
||||
return "delete".equalsIgnoreCase(request.getParameter(HTTP_METHOD_PARAM));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.DefaultActionInvocation;
|
||||
import com.opensymphony.xwork2.DefaultActionProxyFactory;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
|
||||
|
||||
/**
|
||||
* Factory that creates the {@link RestActionInvocation}
|
||||
*/
|
||||
public class RestActionProxyFactory extends DefaultActionProxyFactory {
|
||||
|
||||
public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception {
|
||||
ActionInvocation inv = new RestActionInvocation(extraContext, true);
|
||||
container.inject(inv);
|
||||
return createActionProxy(inv, namespace, actionName, extraContext, executeResult, cleanupContext);
|
||||
}
|
||||
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.ValidationAware;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.*;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
*
|
||||
* An interceptor that makes sure there are not validation errors before allowing the interceptor chain to continue.
|
||||
* <b>This interceptor does not perform any validation</b>.
|
||||
*
|
||||
* <p>Copied from the {@link DefaultWorkflowInterceptor}, this interceptor adds support for error handling of Restful
|
||||
* operations. For example, if an validation error is discovered, a map of errors is created and processed to be
|
||||
* returned, using the appropriate content handler for rendering the body.</p>
|
||||
*
|
||||
* <p/>This interceptor does nothing if the name of the method being invoked is specified in the <b>excludeMethods</b>
|
||||
* parameter. <b>excludeMethods</b> accepts a comma-delimited list of method names. For example, requests to
|
||||
* <b>foo!input.action</b> and <b>foo!back.action</b> will be skipped by this interceptor if you set the
|
||||
* <b>excludeMethods</b> parameter to "input, back".
|
||||
*
|
||||
* <b>Note:</b> As this method extends off MethodFilterInterceptor, it is capable of
|
||||
* deciding if it is applicable only to selective methods in the action class. This is done by adding param tags
|
||||
* for the interceptor element, naming either a list of excluded method names and/or a list of included method
|
||||
* names, whereby includeMethods overrides excludedMethods. A single * sign is interpreted as wildcard matching
|
||||
* all methods for both parameters.
|
||||
* See {@link MethodFilterInterceptor} for more info.
|
||||
*
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <p/> <u>Interceptor parameters:</u>
|
||||
*
|
||||
* <!-- START SNIPPET: parameters -->
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>inputResultName - Default to "input". Determine the result name to be returned when
|
||||
* an action / field error is found.</li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <!-- END SNIPPET: parameters -->
|
||||
*
|
||||
* <p/> <u>Extending the interceptor:</u>
|
||||
*
|
||||
* <p/>
|
||||
*
|
||||
* <!-- START SNIPPET: extending -->
|
||||
*
|
||||
* There are no known extension points for this interceptor.
|
||||
*
|
||||
* <!-- END SNIPPET: extending -->
|
||||
*
|
||||
* <p/> <u>Example code:</u>
|
||||
*
|
||||
* <pre>
|
||||
* <!-- START SNIPPET: example -->
|
||||
*
|
||||
* <action name="someAction" class="com.examples.SomeAction">
|
||||
* <interceptor-ref name="params"/>
|
||||
* <interceptor-ref name="validation"/>
|
||||
* <interceptor-ref name="workflow"/>
|
||||
* <result name="success">good_result.ftl</result>
|
||||
* </action>
|
||||
*
|
||||
* <-- In this case myMethod as well as mySecondMethod of the action class
|
||||
* will not pass through the workflow process -->
|
||||
* <action name="someAction" class="com.examples.SomeAction">
|
||||
* <interceptor-ref name="params"/>
|
||||
* <interceptor-ref name="validation"/>
|
||||
* <interceptor-ref name="workflow">
|
||||
* <param name="excludeMethods">myMethod,mySecondMethod</param>
|
||||
* </interceptor-ref name="workflow">
|
||||
* <result name="success">good_result.ftl</result>
|
||||
* </action>
|
||||
*
|
||||
* <-- In this case, the result named "error" will be used when
|
||||
* an action / field error is found -->
|
||||
* <-- The Interceptor will only be applied for myWorkflowMethod method of action
|
||||
* classes, since this is the only included method while any others are excluded -->
|
||||
* <action name="someAction" class="com.examples.SomeAction">
|
||||
* <interceptor-ref name="params"/>
|
||||
* <interceptor-ref name="validation"/>
|
||||
* <interceptor-ref name="workflow">
|
||||
* <param name="inputResultName">error</param>
|
||||
* <param name="excludeMethods">*</param>
|
||||
* <param name="includeMethods">myWorkflowMethod</param>
|
||||
* </interceptor-ref>
|
||||
* <result name="success">good_result.ftl</result>
|
||||
* </action>
|
||||
*
|
||||
* <!-- END SNIPPET: example -->
|
||||
* </pre>
|
||||
*
|
||||
* @author Jason Carreira
|
||||
* @author Rainer Hermanns
|
||||
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
|
||||
* @author Philip Luppens
|
||||
* @author tm_jee
|
||||
*/
|
||||
public class RestWorkflowInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
private static final long serialVersionUID = 7563014655616490865L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RestWorkflowInterceptor.class);
|
||||
|
||||
private String inputResultName = Action.INPUT;
|
||||
|
||||
private ContentTypeHandlerManager manager;
|
||||
|
||||
@Inject
|
||||
public void setContentTypeHandlerManager(ContentTypeHandlerManager mgr) {
|
||||
this.manager = mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the <code>inputResultName</code> (result name to be returned when
|
||||
* a action / field error is found registered). Default to {@link Action#INPUT}
|
||||
*
|
||||
* @param inputResultName what result name to use when there was validation error(s).
|
||||
*/
|
||||
public void setInputResultName(String inputResultName) {
|
||||
this.inputResultName = inputResultName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept {@link ActionInvocation} and processes the errors using the {@link org.apache.struts2.rest.handler.ContentTypeHandler}
|
||||
* appropriate for the request.
|
||||
*
|
||||
* @return String result name
|
||||
*/
|
||||
protected String doIntercept(ActionInvocation invocation) throws Exception {
|
||||
Object action = invocation.getAction();
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
ValidationAware validationAwareAction = (ValidationAware) action;
|
||||
|
||||
if (validationAwareAction.hasErrors()) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Errors on action "+validationAwareAction+", returning result name 'input'");
|
||||
}
|
||||
ActionMapping mapping = (ActionMapping) ActionContext.getContext().get(ServletActionContext.ACTION_MAPPING);
|
||||
String method = inputResultName;
|
||||
if ("create".equals(mapping.getMethod())) {
|
||||
method = "editNew";
|
||||
} else if ("update".equals(mapping.getMethod())) {
|
||||
method = "edit";
|
||||
}
|
||||
|
||||
|
||||
HttpHeaders info = new DefaultHttpHeaders()
|
||||
.disableCaching()
|
||||
.renderResult(method)
|
||||
.withStatus(SC_BAD_REQUEST);
|
||||
|
||||
Map errors = new HashMap();
|
||||
|
||||
errors.put("actionErrors", validationAwareAction.getActionErrors());
|
||||
errors.put("fieldErrors", validationAwareAction.getFieldErrors());
|
||||
return manager.handleResult(invocation.getProxy().getConfig(), info, errors);
|
||||
}
|
||||
}
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest.handler;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
|
||||
/**
|
||||
* Handles transferring content to and from objects for a specific content type
|
||||
*/
|
||||
public interface ContentTypeHandler {
|
||||
|
||||
/**
|
||||
* Populates an object using data from the input stream
|
||||
* @param in The input stream, usually the body of the request
|
||||
* @param target The target, usually the action class
|
||||
*/
|
||||
void toObject(Reader in, Object target) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes content to the stream
|
||||
*
|
||||
* @param obj The object to write to the stream, usually the Action class
|
||||
* @param resultCode The original result code
|
||||
* @param stream The output stream, usually the response
|
||||
* @return The new result code
|
||||
* @throws IOException If unable to write to the output stream
|
||||
*/
|
||||
String fromObject(Object obj, String resultCode, Writer stream) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the content type for this handler
|
||||
*
|
||||
* @return The mime type
|
||||
*/
|
||||
String getContentType();
|
||||
|
||||
/**
|
||||
* Gets the extension this handler supports
|
||||
*
|
||||
* @return The extension
|
||||
*/
|
||||
String getExtension();
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Handles HTML content, usually just a simple passthrough to the framework
|
||||
*/
|
||||
public class HtmlHandler implements ContentTypeHandler {
|
||||
|
||||
public String fromObject(Object obj, String resultCode, Writer out) throws IOException {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public void toObject(Reader in, Object target) {
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "xhtml";
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return "application/xhtml+xml";
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest.handler;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sf.json.JSONObject;
|
||||
import net.sf.json.JsonConfig;
|
||||
import net.sf.json.JSONArray;
|
||||
|
||||
/**
|
||||
* Handles JSON content using json-lib
|
||||
*/
|
||||
public class JsonLibHandler implements ContentTypeHandler {
|
||||
|
||||
public void toObject(Reader in, Object target) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char[] buffer = new char[1024];
|
||||
int len = 0;
|
||||
while ((len = in.read(buffer)) > 0) {
|
||||
sb.append(buffer, 0, len);
|
||||
}
|
||||
if (target != null && sb.length() > 0 && sb.charAt(0) == '[') {
|
||||
JSONArray jsonArray = JSONArray.fromObject(sb.toString());
|
||||
if (target.getClass().isArray()) {
|
||||
JSONArray.toArray(jsonArray, target, new JsonConfig());
|
||||
} else {
|
||||
JSONArray.toList(jsonArray, target, new JsonConfig());
|
||||
}
|
||||
|
||||
} else {
|
||||
JSONObject jsonObject = JSONObject.fromObject(sb.toString());
|
||||
JSONObject.toBean(jsonObject, target, new JsonConfig());
|
||||
}
|
||||
}
|
||||
|
||||
public String fromObject(Object obj, String resultCode, Writer stream) throws IOException {
|
||||
if (obj != null) {
|
||||
if (isArray(obj)) {
|
||||
JSONArray jsonArray = JSONArray.fromObject(obj);
|
||||
stream.write(jsonArray.toString());
|
||||
} else {
|
||||
JSONObject jsonObject = JSONObject.fromObject(obj);
|
||||
stream.write(jsonObject.toString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private boolean isArray(Object obj) {
|
||||
return obj instanceof Collection || obj.getClass().isArray();
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return "text/javascript";
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "json";
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
|
||||
/**
|
||||
* Handles XML content
|
||||
*/
|
||||
public class XStreamHandler implements ContentTypeHandler {
|
||||
|
||||
public String fromObject(Object obj, String resultCode, Writer out) throws IOException {
|
||||
if (obj != null) {
|
||||
XStream xstream = createXStream();
|
||||
xstream.toXML(obj, out);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void toObject(Reader in, Object target) {
|
||||
XStream xstream = createXStream();
|
||||
xstream.fromXML(in, target);
|
||||
}
|
||||
|
||||
protected XStream createXStream() {
|
||||
return new XStream();
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return "application/xml";
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "xml";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.0.dtd">
|
||||
|
||||
<struts>
|
||||
|
||||
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="rest" class="org.apache.struts2.rest.RestActionProxyFactory" />
|
||||
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="rest" class="org.apache.struts2.rest.RestActionMapper" />
|
||||
|
||||
<bean type="com.opensymphony.xwork2.config.PackageProvider" name="rest" class="org.apache.struts2.rest.ControllerClasspathPackageProvider" />
|
||||
|
||||
<bean class="org.apache.struts2.rest.ContentTypeHandlerManager" />
|
||||
|
||||
<bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="xml" class="org.apache.struts2.rest.handler.XStreamHandler" />
|
||||
<bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="json" class="org.apache.struts2.rest.handler.JsonLibHandler" />
|
||||
<bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="html" class="org.apache.struts2.rest.handler.HtmlHandler" />
|
||||
|
||||
<constant name="struts.actionProxyFactory" value="rest" />
|
||||
<constant name="struts.rest.defaultExtension" value="xhtml" />
|
||||
<constant name="struts.mapper.class" value="rest" />
|
||||
<constant name="struts.mapper.idParameterName" value="id" />
|
||||
<constant name="struts.action.extension" value="xhtml,,xml,json" />
|
||||
<constant name="struts.configuration.classpath.defaultParentPackage" value="rest-default" />
|
||||
|
||||
<package name="rest-default" extends="struts-default">
|
||||
<result-types>
|
||||
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult">
|
||||
<param name="statusCode">303</param>
|
||||
</result-type>
|
||||
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult">
|
||||
<param name="statusCode">303</param>
|
||||
</result-type>
|
||||
</result-types>
|
||||
|
||||
<interceptors>
|
||||
<interceptor name="rest" class="org.apache.struts2.rest.ContentTypeInterceptor"/>
|
||||
<interceptor name="restWorkflow" class="org.apache.struts2.rest.RestWorkflowInterceptor"/>
|
||||
<interceptor name="messages" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
|
||||
|
||||
<!-- A complete stack with all the common interceptors in place.
|
||||
Generally, this stack should be the one you use, though it
|
||||
may do more than you need. Also, the ordering can be
|
||||
switched around (ex: if you wish to have your servlet-related
|
||||
objects applied before prepare() is called, you'd need to move
|
||||
servlet-config interceptor up.
|
||||
|
||||
This stack also excludes from the normal validation and workflow
|
||||
the method names input, back, and cancel. These typically are
|
||||
associated with requests that should not be validated.
|
||||
-->
|
||||
<interceptor-stack name="restDefaultStack">
|
||||
<interceptor-ref name="exception"/>
|
||||
<interceptor-ref name="alias"/>
|
||||
<interceptor-ref name="servletConfig"/>
|
||||
<interceptor-ref name="messages">
|
||||
<param name="operationMode">AUTOMATIC</param>
|
||||
</interceptor-ref>
|
||||
<interceptor-ref name="prepare"/>
|
||||
<interceptor-ref name="i18n"/>
|
||||
<interceptor-ref name="chain"/>
|
||||
<interceptor-ref name="debugging"/>
|
||||
<interceptor-ref name="profiling"/>
|
||||
<interceptor-ref name="actionMappingParams"/>
|
||||
<interceptor-ref name="scopedModelDriven"/>
|
||||
<interceptor-ref name="modelDriven">
|
||||
<param name="refreshModelBeforeResult">true</param>
|
||||
</interceptor-ref>
|
||||
<interceptor-ref name="fileUpload"/>
|
||||
<interceptor-ref name="checkbox"/>
|
||||
<interceptor-ref name="staticParams"/>
|
||||
<interceptor-ref name="params">
|
||||
<param name="excludeParams">dojo\..*</param>
|
||||
</interceptor-ref>
|
||||
<interceptor-ref name="rest" />
|
||||
<interceptor-ref name="conversionError"/>
|
||||
<interceptor-ref name="validation">
|
||||
<param name="excludeMethods">input,back,cancel,browse,index,show,edit,editNew</param>
|
||||
</interceptor-ref>
|
||||
<interceptor-ref name="restWorkflow">
|
||||
<param name="excludeMethods">input,back,cancel,browse,index,show,edit,editNew</param>
|
||||
</interceptor-ref>
|
||||
</interceptor-stack>
|
||||
|
||||
</interceptors>
|
||||
|
||||
<default-interceptor-ref name="restDefaultStack"/>
|
||||
</package>
|
||||
|
||||
</struts>
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import com.mockobjects.dynamic.C;
|
||||
import com.mockobjects.dynamic.Mock;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.rest.handler.ContentTypeHandler;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
public class ContentTypeHandlerManagerTest extends TestCase {
|
||||
|
||||
private ContentTypeHandlerManager mgr;
|
||||
private MockHttpServletResponse mockResponse;
|
||||
private MockHttpServletRequest mockRequest;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
mgr = new ContentTypeHandlerManager();
|
||||
mockResponse = new MockHttpServletResponse();
|
||||
mockRequest = new MockHttpServletRequest();
|
||||
mockRequest.setMethod("GET");
|
||||
ActionContext.setContext(new ActionContext(new HashMap()));
|
||||
ServletActionContext.setRequest(mockRequest);
|
||||
ServletActionContext.setResponse(mockResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
mockRequest = null;
|
||||
mockRequest = null;
|
||||
mgr = null;
|
||||
}
|
||||
|
||||
public void testHandleResultOK() throws IOException {
|
||||
|
||||
String obj = "mystring";
|
||||
ContentTypeHandler handler = new ContentTypeHandler() {
|
||||
public void toObject(Reader in, Object target) {}
|
||||
public String fromObject(Object obj, String resultCode, Writer stream) throws IOException {
|
||||
stream.write(obj.toString());
|
||||
return resultCode;
|
||||
}
|
||||
public String getContentType() { return "foo"; }
|
||||
public String getExtension() { return "foo"; }
|
||||
};
|
||||
mgr.handlers.put("xml", handler);
|
||||
mgr.defaultExtension = "xml";
|
||||
mgr.handleResult(new ActionConfig(), new DefaultHttpHeaders().withStatus(SC_OK), obj);
|
||||
|
||||
assertEquals(obj.getBytes().length, mockResponse.getContentLength());
|
||||
}
|
||||
|
||||
public void testHandleResultNotModified() throws IOException {
|
||||
|
||||
Mock mockHandlerXml = new Mock(ContentTypeHandler.class);
|
||||
mockHandlerXml.matchAndReturn("getExtension", "xml");
|
||||
mgr.handlers.put("xml", (ContentTypeHandler) mockHandlerXml.proxy());
|
||||
mgr.handleResult(null, new DefaultHttpHeaders().withStatus(SC_NOT_MODIFIED), new Object());
|
||||
|
||||
assertEquals(0, mockResponse.getContentLength());
|
||||
}
|
||||
|
||||
public void testHandlerOverride() {
|
||||
Mock mockHandlerXml = new Mock(ContentTypeHandler.class);
|
||||
mockHandlerXml.matchAndReturn("getExtension", "xml");
|
||||
mockHandlerXml.matchAndReturn("toString", "xml");
|
||||
Mock mockHandlerJson = new Mock(ContentTypeHandler.class);
|
||||
mockHandlerJson.matchAndReturn("getExtension", "json");
|
||||
mockHandlerJson.matchAndReturn("toString", "json");
|
||||
Mock mockHandlerXmlOverride = new Mock(ContentTypeHandler.class);
|
||||
mockHandlerXmlOverride.matchAndReturn("getExtension", "xml");
|
||||
mockHandlerXmlOverride.matchAndReturn("toString", "xmlOverride");
|
||||
|
||||
Mock mockContainer = new Mock(Container.class);
|
||||
mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("xmlOverride")), mockHandlerXmlOverride.proxy());
|
||||
mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("xml")), mockHandlerXml.proxy());
|
||||
mockContainer.matchAndReturn("getInstance", C.args(C.eq(ContentTypeHandler.class), C.eq("json")), mockHandlerJson.proxy());
|
||||
mockContainer.expectAndReturn("getInstanceNames", C.args(C.eq(ContentTypeHandler.class)), new HashSet(Arrays.asList("xml", "xmlOverride", "json")));
|
||||
|
||||
mockContainer.matchAndReturn("getInstance", C.args(C.eq(String.class),
|
||||
C.eq(ContentTypeHandlerManager.STRUTS_REST_HANDLER_OVERRIDE_PREFIX+"xml")), "xmlOverride");
|
||||
mockContainer.expectAndReturn("getInstance", C.args(C.eq(String.class),
|
||||
C.eq(ContentTypeHandlerManager.STRUTS_REST_HANDLER_OVERRIDE_PREFIX+"json")), null);
|
||||
|
||||
ContentTypeHandlerManager mgr = new ContentTypeHandlerManager();
|
||||
mgr.setContainer((Container) mockContainer.proxy());
|
||||
|
||||
Map<String,ContentTypeHandler> handlers = mgr.handlers;
|
||||
assertNotNull(handlers);
|
||||
assertEquals(2, handlers.size());
|
||||
assertEquals(mockHandlerXmlOverride.proxy(), handlers.get("xml"));
|
||||
assertEquals(mockHandlerJson.proxy(), handlers.get("json"));
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.*;
|
||||
import java.util.Date;
|
||||
|
||||
public class DefaultHttpHeadersTest extends TestCase {
|
||||
private MockHttpServletResponse mockResponse;
|
||||
private MockHttpServletRequest mockRequest;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
mockResponse = new MockHttpServletResponse();
|
||||
mockRequest = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
mockRequest = null;
|
||||
mockRequest = null;
|
||||
}
|
||||
|
||||
public void testApply() {
|
||||
Date now = new Date();
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.lastModified(now)
|
||||
.withStatus(SC_OK)
|
||||
.setLocationId("44")
|
||||
.withETag("asdf");
|
||||
mockRequest.setRequestURI("/foo/bar.xhtml");
|
||||
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_OK, mockResponse.getStatus());
|
||||
assertEquals("http://localhost:80/foo/bar/44.xhtml", mockResponse.getHeader("Location"));
|
||||
assertEquals("asdf", mockResponse.getHeader("ETag"));
|
||||
assertEquals(now.getTime(), mockResponse.getHeader("Last-Modified"));
|
||||
|
||||
}
|
||||
|
||||
public void testApplyNoLocationExtension() {
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.setLocationId("44");
|
||||
mockRequest.setRequestURI("/foo/bar");
|
||||
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
assertEquals("http://localhost:80/foo/bar/44", mockResponse.getHeader("Location"));
|
||||
|
||||
}
|
||||
|
||||
public void testApplyFullLocation() {
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.setLocation("http://localhost/bar/44");
|
||||
mockRequest.setRequestURI("/foo/bar");
|
||||
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
assertEquals("http://localhost/bar/44", mockResponse.getHeader("Location"));
|
||||
|
||||
}
|
||||
|
||||
public void testAutoETag() {
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders();
|
||||
headers.apply(mockRequest, mockResponse, new Object() {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 123;
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals("123", mockResponse.getHeader("ETag"));
|
||||
}
|
||||
|
||||
public void testNoCache() {
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.disableCaching();
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals("no-cache", mockResponse.getHeader("Cache-Control"));
|
||||
}
|
||||
|
||||
public void testConditionalGetForJustETag() {
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.withETag("asdf");
|
||||
mockRequest.addHeader("If-None-Match", "asdf");
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_NOT_MODIFIED, mockResponse.getStatus());
|
||||
assertEquals("asdf", mockResponse.getHeader("ETag"));
|
||||
}
|
||||
|
||||
public void testConditionalGetForJustETagNotOK() {
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.withETag("asdf")
|
||||
.withStatus(SC_BAD_REQUEST);
|
||||
mockRequest.addHeader("If-None-Match", "asdf");
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_BAD_REQUEST, mockResponse.getStatus());
|
||||
assertEquals("asdf", mockResponse.getHeader("ETag"));
|
||||
}
|
||||
|
||||
public void testConditionalGetForJustLastModified() {
|
||||
Date now = new Date();
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.lastModified(now);
|
||||
mockRequest.addHeader("If-Modified-Since", String.valueOf(now.getTime()));
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_NOT_MODIFIED, mockResponse.getStatus());
|
||||
}
|
||||
|
||||
public void testConditionalGetForJustLastModifiedDifferent() {
|
||||
Date now = new Date();
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.lastModified(now);
|
||||
mockRequest.addHeader("If-Modified-Since", String.valueOf(new Date(2323L).getTime()));
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_OK, mockResponse.getStatus());
|
||||
}
|
||||
|
||||
public void testConditionalGetForLastModifiedAndETag() {
|
||||
Date now = new Date();
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.lastModified(now)
|
||||
.withETag("asdf");
|
||||
mockRequest.addHeader("If-None-Match", "asdf");
|
||||
mockRequest.addHeader("If-Modified-Since", String.valueOf(now.getTime()));
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_NOT_MODIFIED, mockResponse.getStatus());
|
||||
}
|
||||
|
||||
public void testConditionalGetForLastModifiedAndETagButNoCache() {
|
||||
Date now = new Date();
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.lastModified(now)
|
||||
.withETag("asdf")
|
||||
.disableCaching();
|
||||
mockRequest.addHeader("If-None-Match", "asdf");
|
||||
mockRequest.addHeader("If-Modified-Since", String.valueOf(now.getTime()));
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_OK, mockResponse.getStatus());
|
||||
}
|
||||
|
||||
public void testConditionalGetForLastModifiedAndETagWithBadETag() {
|
||||
Date now = new Date();
|
||||
DefaultHttpHeaders headers = new DefaultHttpHeaders()
|
||||
.lastModified(now)
|
||||
.withETag("fdsa");
|
||||
mockRequest.addHeader("If-None-Match", "asdfds");
|
||||
mockRequest.addHeader("If-Modified-Since", String.valueOf(now.getTime()));
|
||||
headers.apply(mockRequest, mockResponse, new Object());
|
||||
|
||||
assertEquals(SC_OK, mockResponse.getStatus());
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package org.apache.struts2.rest;
|
||||
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationManager;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
public class RestActionMapperTest extends TestCase {
|
||||
|
||||
private RestActionMapper mapper;
|
||||
private ConfigurationManager configManager;
|
||||
private Configuration config;
|
||||
private MockHttpServletRequest req;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
req = new MockHttpServletRequest();
|
||||
req.setContextPath("/myapp");
|
||||
req.setMethod("GET");
|
||||
|
||||
mapper = new RestActionMapper();
|
||||
|
||||
config = new DefaultConfiguration();
|
||||
PackageConfig pkg = new PackageConfig("myns", "/animals", false, null);
|
||||
PackageConfig pkg2 = new PackageConfig("my", "/my", false, null);
|
||||
config.addPackageConfig("mvns", pkg);
|
||||
config.addPackageConfig("my", pkg2);
|
||||
configManager = new ConfigurationManager() {
|
||||
public Configuration getConfiguration() {
|
||||
return config;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void testGetMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog");
|
||||
req.setServletPath("/animals/dog");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("index", mapping.getMethod());
|
||||
}
|
||||
|
||||
public void testPostMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog");
|
||||
req.setServletPath("/animals/dog");
|
||||
req.setMethod("POST");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("create", mapping.getMethod());
|
||||
}
|
||||
|
||||
public void testDeleteMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/fido");
|
||||
req.setServletPath("/animals/dog/fido");
|
||||
req.setMethod("DELETE");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("destroy", mapping.getMethod());
|
||||
assertEquals("fido", ((String[])mapping.getParams().get("id"))[0]);
|
||||
}
|
||||
|
||||
public void testPutMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/fido");
|
||||
req.setServletPath("/animals/dog/fido");
|
||||
req.setMethod("PUT");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("update", mapping.getMethod());
|
||||
assertEquals("fido", ((String[])mapping.getParams().get("id"))[0]);
|
||||
}
|
||||
|
||||
public void testGetIdMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/fido");
|
||||
req.setServletPath("/animals/dog/fido");
|
||||
req.setMethod("GET");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("show", mapping.getMethod());
|
||||
assertEquals("fido", ((String[])mapping.getParams().get("id"))[0]);
|
||||
}
|
||||
|
||||
public void testNewMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/new");
|
||||
req.setServletPath("/animals/dog/new");
|
||||
req.setMethod("GET");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("editNew", mapping.getMethod());
|
||||
}
|
||||
|
||||
public void testEditMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/fido/edit");
|
||||
req.setServletPath("/animals/dog/fido/edit");
|
||||
req.setMethod("GET");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("fido", ((String[])mapping.getParams().get("id"))[0]);
|
||||
assertEquals("edit", mapping.getMethod());
|
||||
}
|
||||
|
||||
public void testEditSemicolonMapping() throws Exception {
|
||||
req.setRequestURI("/myapp/animals/dog/fido;edit");
|
||||
req.setServletPath("/animals/dog/fido;edit");
|
||||
req.setMethod("GET");
|
||||
|
||||
ActionMapping mapping = mapper.getMapping(req, configManager);
|
||||
|
||||
assertEquals("/animals", mapping.getNamespace());
|
||||
assertEquals("dog", mapping.getName());
|
||||
assertEquals("fido", ((String[])mapping.getParams().get("id"))[0]);
|
||||
assertEquals("edit", mapping.getMethod());
|
||||
}
|
||||
|
||||
public void testParseNameAndNamespace() {
|
||||
tryUri("/foo/23", "", "foo/23");
|
||||
tryUri("/foo/", "", "foo/");
|
||||
tryUri("foo", "", "foo");
|
||||
tryUri("/", "/", "");
|
||||
}
|
||||
|
||||
public void testParseNameAndNamespaceWithNamespaces() {
|
||||
tryUri("/my/foo/23", "/my", "foo/23");
|
||||
tryUri("/my/foo/", "/my", "foo/");
|
||||
}
|
||||
|
||||
public void testParseNameAndNamespaceWithEdit() {
|
||||
tryUri("/my/foo/23;edit", "/my", "foo/23;edit");
|
||||
}
|
||||
|
||||
private void tryUri(String uri, String expectedNamespace, String expectedName) {
|
||||
ActionMapping mapping = new ActionMapping();
|
||||
mapper.parseNameAndNamespace(uri, mapping, configManager);
|
||||
assertEquals(expectedName, mapping.getName());
|
||||
assertEquals(expectedNamespace, mapping.getNamespace());
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest.handler;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Contact {
|
||||
private String name;
|
||||
private boolean important;
|
||||
private int age;
|
||||
|
||||
public Contact() {}
|
||||
|
||||
public Contact(String name, boolean important, int age) {
|
||||
this.name = name;
|
||||
this.important = important;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isImportant() {
|
||||
return important;
|
||||
}
|
||||
|
||||
public void setImportant(boolean important) {
|
||||
this.important = important;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Contact contact = (Contact) o;
|
||||
|
||||
if (age != contact.age) return false;
|
||||
if (important != contact.important) return false;
|
||||
if (name != null ? !name.equals(contact.name) : contact.name != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
HashMap map = new HashMap();
|
||||
map.put("age", age);
|
||||
map.put("important", important);
|
||||
map.put("name", name);
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = (name != null ? name.hashCode() : 0);
|
||||
result = 31 * result + (important ? 1 : 0);
|
||||
result = 31 * result + age;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.apache.struts2.rest.handler;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class JsonLibHandlerTest extends TestCase {
|
||||
|
||||
public void testFromObject() throws IOException {
|
||||
Contact contact = new Contact("bob", true, 44);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
JsonLibHandler handler = new JsonLibHandler();
|
||||
handler.fromObject(contact, "success", writer);
|
||||
|
||||
assertEquals("{\"age\":44,\"important\":true,\"name\":\"bob\"}", writer.toString());
|
||||
}
|
||||
|
||||
public void testFromObjectArray() throws IOException {
|
||||
Contact contact = new Contact("bob", true, 44);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
JsonLibHandler handler = new JsonLibHandler();
|
||||
handler.fromObject(Arrays.asList(contact), "success", writer);
|
||||
|
||||
assertEquals("[{\"age\":44,\"important\":true,\"name\":\"bob\"}]", writer.toString());
|
||||
}
|
||||
|
||||
public void testToObject() throws IOException {
|
||||
Contact contact = new Contact("bob", true, 44);
|
||||
|
||||
Contact target = new Contact();
|
||||
StringReader reader = new StringReader("{\"age\":44,\"important\":true,\"name\":\"bob\"}");
|
||||
JsonLibHandler handler = new JsonLibHandler();
|
||||
handler.toObject(reader, target);
|
||||
|
||||
assertEquals(contact, target);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user