WW-3217 Bring JSON plugin into trunk

git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@802878 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Musachy Barroso
2009-08-10 18:19:48 +00:00
parent 2f7177ce8f
commit 60fdb065c5
78 changed files with 6781 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-plugins</artifactId>
<version>2.1.8-SNAPSHOT</version>
</parent>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<packaging>jar</packaging>
<name>Struts 2 JSON Plugin</name>
<scm>
<connection>scm:svn:http://svn.apache.org/repos/asf/struts/struts2/trunk/struts2-json-plugin</connection>
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/sandbox/struts2/struts2-json-plugin</developerConnection>
<url>http://svn.apache.org/viewcvs.cgi/struts/sandbox/struts2/struts2-json-plugin</url>
</scm>
<build>
<plugins>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Activator>org.apache.struts2.osgi.StrutsActivator</Bundle-Activator>
<manifestLocation>META-INF</manifestLocation>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${pom.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<version>${pom.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${struts2.springPlatformVersion}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,64 @@
/*
* $Id$
*
* 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.json;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Isolate the process of cleaning JSON data from the Interceptor class itself.
*/
public abstract class JSONCleaner {
public Object clean(String ognlPrefix, Object data) throws JSONException {
if (data == null)
return null;
else if (data instanceof List)
return cleanList(ognlPrefix, data);
else if (data instanceof Map)
return cleanMap(ognlPrefix, data);
else
return cleanValue(ognlPrefix, data);
}
protected Object cleanList(String ognlPrefix, Object data) throws JSONException {
List list = (List) data;
int count = list.size();
for (int i = 0; i < count; i++) {
list.set(i, clean(ognlPrefix + "[" + i + "]", list.get(i)));
}
return list;
}
protected Object cleanMap(String ognlPrefix, Object data) throws JSONException {
Map map = (Map) data;
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry e = (Map.Entry) iter.next();
e.setValue(clean((ognlPrefix.length() > 0 ? ognlPrefix + "." : "") + e.getKey(), e.getValue()));
}
return map;
}
protected abstract Object cleanValue(String ognlName, Object data) throws JSONException;
}
@@ -0,0 +1,38 @@
/*
* $Id$
*
* 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.json;
/**
* Wrap exceptions throwed by the JSON serializer
*/
public class JSONException extends Exception {
public JSONException(String message) {
super(message);
}
public JSONException(Throwable cause) {
super(cause);
}
public JSONException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,480 @@
/*
* $Id$
*
* 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.json;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.json.annotations.SMDMethod;
import org.apache.struts2.json.rpc.RPCError;
import org.apache.struts2.json.rpc.RPCErrorCode;
import org.apache.struts2.json.rpc.RPCResponse;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* Populates an action from a JSON string
*/
public class JSONInterceptor implements Interceptor {
private static final long serialVersionUID = 4950170304212158803L;
private static final Logger LOG = LoggerFactory.getLogger(JSONInterceptor.class);
private boolean enableSMD = false;
private boolean enableGZIP = false;
private boolean wrapWithComments;
private boolean prefix;
private String defaultEncoding = "ISO-8859-1";
private boolean ignoreHierarchy = true;
private String root;
private List<Pattern> excludeProperties;
private List<Pattern> includeProperties;
private boolean ignoreSMDMethodInterfaces = true;
private JSONPopulator populator = new JSONPopulator();
private JSONCleaner dataCleaner = null;
private boolean debug = false;
private boolean noCache = false;
private boolean excludeNullProperties;
private String callbackParameter;
private String contentType;
public void destroy() {
}
public void init() {
}
@SuppressWarnings("unchecked")
public String intercept(ActionInvocation invocation) throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
String contentType = request.getHeader("content-type");
if (contentType != null) {
int iSemicolonIdx;
if ((iSemicolonIdx = contentType.indexOf(";")) != -1)
contentType = contentType.substring(0, iSemicolonIdx);
}
Object rootObject;
if (this.root != null) {
ValueStack stack = invocation.getStack();
rootObject = stack.findValue(this.root);
if (rootObject == null) {
throw new RuntimeException("Invalid root expression: '" + this.root + "'.");
}
} else {
rootObject = invocation.getAction();
}
if ((contentType != null) && contentType.equalsIgnoreCase("application/json")) {
// load JSON object
Object obj = JSONUtil.deserialize(request.getReader());
if (obj instanceof Map) {
Map json = (Map) obj;
// clean up the values
if (dataCleaner != null)
dataCleaner.clean("", json);
// populate fields
populator.populateObject(rootObject, json);
} else {
LOG.error("Unable to deserialize JSON object from request");
throw new JSONException("Unable to deserialize JSON object from request");
}
} else if ((contentType != null) && contentType.equalsIgnoreCase("application/json-rpc")) {
Object result;
if (this.enableSMD) {
// load JSON object
Object obj = JSONUtil.deserialize(request.getReader());
if (obj instanceof Map) {
Map smd = (Map) obj;
// invoke method
try {
result = this.invoke(rootObject, smd);
} catch (Exception e) {
RPCResponse rpcResponse = new RPCResponse();
rpcResponse.setId(smd.get("id").toString());
rpcResponse.setError(new RPCError(e, RPCErrorCode.EXCEPTION, debug));
result = rpcResponse;
}
} else {
String message = "SMD request was not in the right format. See http://json-rpc.org";
RPCResponse rpcResponse = new RPCResponse();
rpcResponse.setError(new RPCError(message, RPCErrorCode.INVALID_PROCEDURE_CALL));
result = rpcResponse;
}
String json = JSONUtil.serialize(result, excludeProperties, includeProperties,
ignoreHierarchy, excludeNullProperties);
json = addCallbackIfApplicable(request, json);
JSONUtil.writeJSONToResponse(new SerializationParams(response, this.defaultEncoding,
this.wrapWithComments, json, true, false, noCache, -1, -1, prefix, contentType));
return Action.NONE;
} else {
String message = "Request with content type of 'application/json-rpc' was received but SMD is "
+ "not enabled for this interceptor. Set 'enableSMD' to true to enable it";
RPCResponse rpcResponse = new RPCResponse();
rpcResponse.setError(new RPCError(message, RPCErrorCode.SMD_DISABLED));
result = rpcResponse;
}
String json = JSONUtil.serialize(result, excludeProperties, includeProperties, ignoreHierarchy,
excludeNullProperties);
json = addCallbackIfApplicable(request, json);
boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
JSONUtil.writeJSONToResponse(new SerializationParams(response, this.defaultEncoding,
this.wrapWithComments, json, true, writeGzip, noCache, -1, -1, prefix, contentType));
return Action.NONE;
} else {
if (LOG.isDebugEnabled()) {
LOG
.debug("Content type must be 'application/json' or 'application/json-rpc'. Ignoring request with content type "
+ contentType);
}
}
return invocation.invoke();
}
@SuppressWarnings("unchecked")
public RPCResponse invoke(Object object, Map data) throws IllegalArgumentException,
IllegalAccessException, InvocationTargetException, JSONException, InstantiationException,
NoSuchMethodException, IntrospectionException {
RPCResponse response = new RPCResponse();
// validate id
Object id = data.get("id");
if (id == null) {
String message = "'id' is required for JSON RPC";
response.setError(new RPCError(message, RPCErrorCode.METHOD_NOT_FOUND));
return response;
}
// could be a numeric value
response.setId(id.toString());
// the map is going to have: 'params', 'method' and 'id' (what is the id
// for?)
Class clazz = object.getClass();
// parameters
List parameters = (List) data.get("params");
int parameterCount = parameters != null ? parameters.size() : 0;
// method
String methodName = (String) data.get("method");
if (methodName == null) {
String message = "'method' is required for JSON RPC";
response.setError(new RPCError(message, RPCErrorCode.MISSING_METHOD));
return response;
}
Method method = this.getMethod(clazz, methodName, parameterCount);
if (method == null) {
String message = "Method " + methodName + " could not be found in action class.";
response.setError(new RPCError(message, RPCErrorCode.METHOD_NOT_FOUND));
return response;
}
// parameters
if (parameterCount > 0) {
Class[] parameterTypes = method.getParameterTypes();
Type[] genericTypes = method.getGenericParameterTypes();
List invocationParameters = new ArrayList();
// validate size
if (parameterTypes.length != parameterCount) {
// size mismatch
String message = "Parameter count in request, " + parameterCount
+ " do not match expected parameter count for " + methodName + ", "
+ parameterTypes.length;
response.setError(new RPCError(message, RPCErrorCode.PARAMETERS_MISMATCH));
return response;
}
// convert parameters
for (int i = 0; i < parameters.size(); i++) {
Object parameter = parameters.get(i);
Class paramType = parameterTypes[i];
Type genericType = genericTypes[i];
// clean up the values
if (dataCleaner != null)
parameter = dataCleaner.clean("[" + i + "]", parameter);
Object converted = populator.convert(paramType, genericType, parameter, method);
invocationParameters.add(converted);
}
response.setResult(method.invoke(object, invocationParameters.toArray()));
} else {
response.setResult(method.invoke(object, new Object[0]));
}
return response;
}
@SuppressWarnings("unchecked")
private Method getMethod(Class clazz, String name, int parameterCount) {
Method[] smdMethods = JSONUtil.listSMDMethods(clazz, ignoreSMDMethodInterfaces);
for (Method method : smdMethods) {
if (checkSMDMethodSignature(method, name, parameterCount)) {
return method;
}
}
return null;
}
/**
* Look for a method in clazz carrying the SMDMethod annotation with
* matching name and parametersCount
*
* @return true if matches name and parameterCount
*/
private boolean checkSMDMethodSignature(Method method, String name, int parameterCount) {
SMDMethod smdMethodAnntotation = method.getAnnotation(SMDMethod.class);
if (smdMethodAnntotation != null) {
String alias = smdMethodAnntotation.name();
boolean paramsMatch = method.getParameterTypes().length == parameterCount;
if (((alias.length() == 0) && method.getName().equals(name) && paramsMatch)
|| (alias.equals(name) && paramsMatch)) {
return true;
}
}
return false;
}
protected String addCallbackIfApplicable(HttpServletRequest request, String json) {
if ((callbackParameter != null) && (callbackParameter.length() > 0)) {
String callbackName = request.getParameter(callbackParameter);
if ((callbackName != null) && (callbackName.length() > 0))
json = callbackName + "(" + json + ")";
}
return json;
}
public boolean isEnableSMD() {
return this.enableSMD;
}
public void setEnableSMD(boolean enableSMD) {
this.enableSMD = enableSMD;
}
/**
* Ignore annotations on methods in interfaces You may need to set to this
* true if your action is a proxy/enhanced as annotations are not inherited
*/
public void setIgnoreSMDMethodInterfaces(boolean ignoreSMDMethodInterfaces) {
this.ignoreSMDMethodInterfaces = ignoreSMDMethodInterfaces;
}
/**
* Wrap generated JSON with comments. Only used if SMD is enabled.
*
* @param wrapWithComments
*/
public void setWrapWithComments(boolean wrapWithComments) {
this.wrapWithComments = wrapWithComments;
}
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setDefaultEncoding(String val) {
this.defaultEncoding = val;
}
/**
* Ignore properties defined on base classes of the root object.
*
* @param ignoreHierarchy
*/
public void setIgnoreHierarchy(boolean ignoreHierarchy) {
this.ignoreHierarchy = ignoreHierarchy;
}
/**
* Sets the root object to be deserialized, defaults to the Action
*
* @param root
* OGNL expression of root object to be serialized
*/
public void setRoot(String root) {
this.root = root;
}
/**
* Sets the JSONPopulator to be used
*
* @param populator
* JSONPopulator
*/
public void setJSONPopulator(JSONPopulator populator) {
this.populator = populator;
}
/**
* Sets the JSONCleaner to be used
*
* @param dataCleaner
* JSONCleaner
*/
public void setJSONCleaner(JSONCleaner dataCleaner) {
this.dataCleaner = dataCleaner;
}
/**
* Turns debugging on or off
*
* @param debug
* true or false
*/
public boolean getDebug() {
return this.debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* Sets a comma-delimited list of regular expressions to match properties
* that should be excluded from the JSON output.
*
* @param commaDelim
* A comma-delimited list of regular expressions
*/
public void setExcludeProperties(String commaDelim) {
List<String> excludePatterns = JSONUtil.asList(commaDelim);
if (excludePatterns != null) {
this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
for (String pattern : excludePatterns) {
this.excludeProperties.add(Pattern.compile(pattern));
}
}
}
/**
* Sets a comma-delimited list of regular expressions to match properties
* that should be included from the JSON output.
*
* @param commaDelim
* A comma-delimited list of regular expressions
*/
public void setIncludeProperties(String commaDelim) {
List<String> includePatterns = JSONUtil.asList(commaDelim);
if (includePatterns != null) {
this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
for (String pattern : includePatterns) {
this.includeProperties.add(Pattern.compile(pattern));
}
}
}
public boolean isEnableGZIP() {
return enableGZIP;
}
/**
* Setting this property to "true" will compress the output.
*
* @param enableGZIP
* Enable compressed output
*/
public void setEnableGZIP(boolean enableGZIP) {
this.enableGZIP = enableGZIP;
}
public boolean isNoCache() {
return noCache;
}
/**
* Add headers to response to prevent the browser from caching the response
*
* @param noCache
*/
public void setNoCache(boolean noCache) {
this.noCache = noCache;
}
public boolean isExcludeNullProperties() {
return excludeNullProperties;
}
/**
* Do not serialize properties with a null value
*
* @param excludeNullProperties
*/
public void setExcludeNullProperties(boolean excludeNullProperties) {
this.excludeNullProperties = excludeNullProperties;
}
public void setCallbackParameter(String callbackParameter) {
this.callbackParameter = callbackParameter;
}
public String getCallbackParameter() {
return callbackParameter;
}
/**
* Add "{} && " to generated JSON
*
* @param prefix
*/
public void setPrefix(boolean prefix) {
this.prefix = prefix;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
}
@@ -0,0 +1,444 @@
/*
* $Id$
*
* 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.json;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.struts2.json.annotations.JSON;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* Isolate the process of populating JSON objects from the Interceptor class
* itself.
*/
public class JSONPopulator {
private static final Logger LOG = LoggerFactory.getLogger(JSONPopulator.class);
private String dateFormat = JSONUtil.RFC3339_FORMAT;
public JSONPopulator() {
}
public JSONPopulator(String dateFormat) {
this.dateFormat = dateFormat;
}
public String getDateFormat() {
return dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
@SuppressWarnings("unchecked")
public void populateObject(Object object, final Map elements) throws IllegalAccessException,
InvocationTargetException, NoSuchMethodException, IntrospectionException,
IllegalArgumentException, JSONException, InstantiationException {
Class clazz = object.getClass();
BeanInfo info = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = info.getPropertyDescriptors();
// iterate over class fields
for (int i = 0; i < props.length; ++i) {
PropertyDescriptor prop = props[i];
String name = prop.getName();
if (elements.containsKey(name)) {
Object value = elements.get(name);
Method method = prop.getWriteMethod();
if (method != null) {
JSON json = method.getAnnotation(JSON.class);
if ((json != null) && !json.deserialize()) {
continue;
}
// use only public setters
if (Modifier.isPublic(method.getModifiers())) {
Class[] paramTypes = method.getParameterTypes();
Type[] genericTypes = method.getGenericParameterTypes();
if (paramTypes.length == 1) {
Object convertedValue = this.convert(paramTypes[0], genericTypes[0], value,
method);
method.invoke(object, new Object[] { convertedValue });
}
}
}
}
}
}
@SuppressWarnings("unchecked")
public Object convert(Class clazz, Type type, Object value, Method method)
throws IllegalArgumentException, JSONException, IllegalAccessException,
InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException {
if (value == null) {
// if it is a java primitive then get a default value, otherwise
// leave it as null
return clazz.isPrimitive() ? convertPrimitive(clazz, value, method) : null;
} else if (isJSONPrimitive(clazz))
return convertPrimitive(clazz, value, method);
else if (Collection.class.isAssignableFrom(clazz))
return convertToCollection(clazz, type, value, method);
else if (Map.class.isAssignableFrom(clazz))
return convertToMap(clazz, type, value, method);
else if (clazz.isArray())
return convertToArray(clazz, type, value, method);
else if (value instanceof Map) {
// nested bean
Object convertedValue = clazz.newInstance();
this.populateObject(convertedValue, (Map) value);
return convertedValue;
} else if (BigDecimal.class.equals(clazz)) {
return new BigDecimal(value != null ? value.toString() : "0");
} else if (BigInteger.class.equals(clazz)) {
return new BigInteger(value != null ? value.toString() : "0");
} else
throw new JSONException("Incompatible types for property " + method.getName());
}
private static boolean isJSONPrimitive(Class clazz) {
return clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Date.class)
|| clazz.equals(Boolean.class) || clazz.equals(Byte.class) || clazz.equals(Character.class)
|| clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Integer.class)
|| clazz.equals(Long.class) || clazz.equals(Short.class) || clazz.equals(Locale.class)
|| clazz.isEnum();
}
@SuppressWarnings("unchecked")
private Object convertToArray(Class clazz, Type type, Object value, Method accessor)
throws JSONException, IllegalArgumentException, IllegalAccessException,
InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException {
if (value == null)
return null;
else if (value instanceof List) {
Class arrayType = clazz.getComponentType();
List values = (List) value;
Object newArray = Array.newInstance(arrayType, values.size());
// create an object for each element
for (int j = 0; j < values.size(); j++) {
Object listValue = values.get(j);
if (arrayType.equals(Object.class)) {
// Object[]
Array.set(newArray, j, listValue);
} else if (isJSONPrimitive(arrayType)) {
// primitive array
Array.set(newArray, j, this.convertPrimitive(arrayType, listValue, accessor));
} else if (listValue instanceof Map) {
// array of other class
Object newObject = null;
if (Map.class.isAssignableFrom(arrayType)) {
newObject = convertToMap(arrayType, type, listValue, accessor);
} else if (List.class.isAssignableFrom(arrayType)) {
newObject = convertToCollection(arrayType, type, listValue, accessor);
} else {
newObject = arrayType.newInstance();
this.populateObject(newObject, (Map) listValue);
}
Array.set(newArray, j, newObject);
} else
throw new JSONException("Incompatible types for property " + accessor.getName());
}
return newArray;
} else
throw new JSONException("Incompatible types for property " + accessor.getName());
}
@SuppressWarnings("unchecked")
private Object convertToCollection(Class clazz, Type type, Object value, Method accessor)
throws JSONException, IllegalArgumentException, IllegalAccessException,
InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException {
if (value == null)
return null;
else if (value instanceof List) {
Class itemClass = Object.class;
Type itemType = null;
if ((type != null) && (type instanceof ParameterizedType)) {
ParameterizedType ptype = (ParameterizedType) type;
itemType = ptype.getActualTypeArguments()[0];
if (itemType.getClass().equals(Class.class)) {
itemClass = (Class) itemType;
} else {
itemClass = (Class) ((ParameterizedType) itemType).getRawType();
}
}
List values = (List) value;
Collection newCollection = null;
try {
newCollection = (Collection) clazz.newInstance();
} catch (InstantiationException ex) {
// fallback if clazz represents an interface or abstract class
if (Set.class.isAssignableFrom(clazz)) {
newCollection = new HashSet();
} else {
newCollection = new ArrayList();
}
}
// create an object for each element
for (int j = 0; j < values.size(); j++) {
Object listValue = values.get(j);
if (itemClass.equals(Object.class)) {
// Object[]
newCollection.add(listValue);
} else if (isJSONPrimitive(itemClass)) {
// primitive array
newCollection.add(this.convertPrimitive(itemClass, listValue, accessor));
} else if (Map.class.isAssignableFrom(itemClass)) {
Object newObject = convertToMap(itemClass, itemType, listValue, accessor);
newCollection.add(newObject);
} else if (List.class.isAssignableFrom(itemClass)) {
Object newObject = convertToCollection(itemClass, itemType, listValue, accessor);
newCollection.add(newObject);
} else if (listValue instanceof Map) {
// array of beans
Object newObject = itemClass.newInstance();
this.populateObject(newObject, (Map) listValue);
newCollection.add(newObject);
} else
throw new JSONException("Incompatible types for property " + accessor.getName());
}
return newCollection;
} else
throw new JSONException("Incompatible types for property " + accessor.getName());
}
@SuppressWarnings("unchecked")
private Object convertToMap(Class clazz, Type type, Object value, Method accessor) throws JSONException,
IllegalArgumentException, IllegalAccessException, InvocationTargetException,
InstantiationException, NoSuchMethodException, IntrospectionException {
if (value == null)
return null;
else if (value instanceof Map) {
Class itemClass = Object.class;
Type itemType = null;
if ((type != null) && (type instanceof ParameterizedType)) {
ParameterizedType ptype = (ParameterizedType) type;
itemType = ptype.getActualTypeArguments()[1];
if (itemType.getClass().equals(Class.class)) {
itemClass = (Class) itemType;
} else {
itemClass = (Class) ((ParameterizedType) itemType).getRawType();
}
}
Map values = (Map) value;
Map newMap = null;
try {
newMap = (Map) clazz.newInstance();
} catch (InstantiationException ex) {
// fallback if clazz represents an interface or abstract class
newMap = new HashMap();
}
// create an object for each element
Iterator iter = values.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
Object v = entry.getValue();
if (itemClass.equals(Object.class)) {
// String, Object
newMap.put(key, v);
} else if (isJSONPrimitive(itemClass)) {
// primitive map
newMap.put(key, this.convertPrimitive(itemClass, v, accessor));
} else if (Map.class.isAssignableFrom(itemClass)) {
Object newObject = convertToMap(itemClass, itemType, v, accessor);
newMap.put(key, newObject);
} else if (List.class.isAssignableFrom(itemClass)) {
Object newObject = convertToCollection(itemClass, itemType, v, accessor);
newMap.put(key, newObject);
} else if (v instanceof Map) {
// map of beans
Object newObject = itemClass.newInstance();
this.populateObject(newObject, (Map) v);
newMap.put(key, newObject);
} else
throw new JSONException("Incompatible types for property " + accessor.getName());
}
return newMap;
} else
throw new JSONException("Incompatible types for property " + accessor.getName());
}
/**
* Converts numbers to the desired class, if possible
*
* @throws JSONException
*/
@SuppressWarnings("unchecked")
private Object convertPrimitive(Class clazz, Object value, Method method) throws JSONException {
if (value == null) {
if (Short.TYPE.equals(clazz) || Short.class.equals(clazz))
return (short) 0;
else if (Byte.TYPE.equals(clazz) || Byte.class.equals(clazz))
return (byte) 0;
else if (Integer.TYPE.equals(clazz) || Integer.class.equals(clazz))
return 0;
else if (Long.TYPE.equals(clazz) || Long.class.equals(clazz))
return 0L;
else if (Float.TYPE.equals(clazz) || Float.class.equals(clazz))
return 0f;
else if (Double.TYPE.equals(clazz) || Double.class.equals(clazz))
return 0d;
else if (Boolean.TYPE.equals(clazz) || Boolean.class.equals(clazz))
return Boolean.FALSE;
else
return null;
} else if (value instanceof Number) {
Number number = (Number) value;
if (Short.TYPE.equals(clazz))
return number.shortValue();
else if (Short.class.equals(clazz))
return new Short(number.shortValue());
else if (Byte.TYPE.equals(clazz))
return number.byteValue();
else if (Byte.class.equals(clazz))
return new Byte(number.byteValue());
else if (Integer.TYPE.equals(clazz))
return number.intValue();
else if (Integer.class.equals(clazz))
return new Integer(number.intValue());
else if (Long.TYPE.equals(clazz))
return number.longValue();
else if (Long.class.equals(clazz))
return new Long(number.longValue());
else if (Float.TYPE.equals(clazz))
return number.floatValue();
else if (Float.class.equals(clazz))
return new Float(number.floatValue());
else if (Double.TYPE.equals(clazz))
return number.doubleValue();
else if (Double.class.equals(clazz))
return new Double(number.doubleValue());
else if (String.class.equals(clazz))
return value.toString();
} else if (clazz.equals(Date.class)) {
try {
JSON json = method.getAnnotation(JSON.class);
DateFormat formatter = new SimpleDateFormat(
(json != null) && (json.format().length() > 0) ? json.format() : this.dateFormat);
return formatter.parse((String) value);
} catch (ParseException e) {
LOG.error(e.getMessage(), e);
throw new JSONException("Unable to parse date from: " + value);
}
} else if (clazz.isEnum()) {
String sValue = (String) value;
return Enum.valueOf(clazz, sValue);
} else if (value instanceof String) {
String sValue = (String) value;
if (Boolean.TYPE.equals(clazz))
return Boolean.parseBoolean(sValue);
else if (Boolean.class.equals(clazz))
return Boolean.valueOf(sValue);
else if (Short.TYPE.equals(clazz))
return Short.parseShort(sValue);
else if (Short.class.equals(clazz))
return Short.valueOf(sValue);
else if (Byte.TYPE.equals(clazz))
return Byte.parseByte(sValue);
else if (Byte.class.equals(clazz))
return Byte.valueOf(sValue);
else if (Integer.TYPE.equals(clazz))
return Integer.parseInt(sValue);
else if (Integer.class.equals(clazz))
return Integer.valueOf(sValue);
else if (Long.TYPE.equals(clazz))
return Long.parseLong(sValue);
else if (Long.class.equals(clazz))
return Long.valueOf(sValue);
else if (Float.TYPE.equals(clazz))
return Float.parseFloat(sValue);
else if (Float.class.equals(clazz))
return Float.valueOf(sValue);
else if (Double.TYPE.equals(clazz))
return Double.parseDouble(sValue);
else if (Double.class.equals(clazz))
return Double.valueOf(sValue);
else if (Character.TYPE.equals(clazz) || Character.class.equals(clazz)) {
char charValue = 0;
if (sValue.length() > 0) {
charValue = sValue.charAt(0);
}
if (Character.TYPE.equals(clazz))
return charValue;
else
return new Character(charValue);
} else if (clazz.equals(Locale.class)) {
String[] components = sValue.split("_", 2);
if (components.length == 2) {
return new Locale(components[0], components[1]);
} else {
return new Locale(sValue);
}
} else if (Enum.class.isAssignableFrom(clazz)) {
return Enum.valueOf(clazz, sValue);
}
}
return value;
}
}
@@ -0,0 +1,289 @@
/*
* $Id$
*
* 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.json;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* Deserializes and object from a JSON string
* </p>
*/
class JSONReader {
private static final Object OBJECT_END = new Object();
private static final Object ARRAY_END = new Object();
private static final Object COLON = new Object();
private static final Object COMMA = new Object();
private static Map<Character, Character> escapes = new HashMap<Character, Character>();
static {
escapes.put(new Character('"'), new Character('"'));
escapes.put(new Character('\\'), new Character('\\'));
escapes.put(new Character('/'), new Character('/'));
escapes.put(new Character('b'), new Character('\b'));
escapes.put(new Character('f'), new Character('\f'));
escapes.put(new Character('n'), new Character('\n'));
escapes.put(new Character('r'), new Character('\r'));
escapes.put(new Character('t'), new Character('\t'));
}
private CharacterIterator it;
private char c;
private Object token;
private StringBuilder buf = new StringBuilder();
private char next() {
this.c = this.it.next();
return this.c;
}
private void skipWhiteSpace() {
while (Character.isWhitespace(this.c)) {
this.next();
}
}
public Object read(String string) throws JSONException {
this.it = new StringCharacterIterator(string);
this.c = this.it.first();
return this.read();
}
private Object read() throws JSONException {
Object ret = null;
this.skipWhiteSpace();
if (this.c == '"') {
this.next();
ret = this.string('"');
} else if (this.c == '\'') {
this.next();
ret = this.string('\'');
} else if (this.c == '[') {
this.next();
ret = this.array();
} else if (this.c == ']') {
ret = ARRAY_END;
this.next();
} else if (this.c == ',') {
ret = COMMA;
this.next();
} else if (this.c == '{') {
this.next();
ret = this.object();
} else if (this.c == '}') {
ret = OBJECT_END;
this.next();
} else if (this.c == ':') {
ret = COLON;
this.next();
} else if ((this.c == 't') && (this.next() == 'r') && (this.next() == 'u') && (this.next() == 'e')) {
ret = Boolean.TRUE;
this.next();
} else if ((this.c == 'f') && (this.next() == 'a') && (this.next() == 'l') && (this.next() == 's')
&& (this.next() == 'e')) {
ret = Boolean.FALSE;
this.next();
} else if ((this.c == 'n') && (this.next() == 'u') && (this.next() == 'l') && (this.next() == 'l')) {
ret = null;
this.next();
} else if (Character.isDigit(this.c) || (this.c == '-')) {
ret = this.number();
} else {
throw buildInvalidInputException();
}
this.token = ret;
return ret;
}
@SuppressWarnings("unchecked")
private Map object() throws JSONException {
Map ret = new HashMap();
Object next = this.read();
if (next != OBJECT_END) {
String key = (String) next;
while (this.token != OBJECT_END) {
this.read(); // should be a colon
if (this.token != OBJECT_END) {
ret.put(key, this.read());
if (this.read() == COMMA) {
Object name = this.read();
if (name instanceof String) {
key = (String) name;
} else
throw buildInvalidInputException();
}
}
}
}
return ret;
}
private JSONException buildInvalidInputException() {
return new JSONException("Input string is not well formed JSON (invalid char " + this.c + ")");
}
@SuppressWarnings("unchecked")
private List array() throws JSONException {
List ret = new ArrayList();
Object value = this.read();
while (this.token != ARRAY_END) {
ret.add(value);
Object read = this.read();
if (read == COMMA) {
value = this.read();
} else if (read != ARRAY_END) {
throw buildInvalidInputException();
}
}
return ret;
}
private Object number() {
this.buf.setLength(0);
if (this.c == '-') {
this.add();
}
this.addDigits();
if (this.c == '.') {
this.add();
this.addDigits();
}
if ((this.c == 'e') || (this.c == 'E')) {
this.add();
if ((this.c == '+') || (this.c == '-')) {
this.add();
}
this.addDigits();
}
return (this.buf.indexOf(".") >= 0) ? (Object) Double.parseDouble(this.buf.toString())
: (Object) Long.parseLong(this.buf.toString());
}
private Object string(char quote) {
this.buf.setLength(0);
while ((this.c != quote) && (this.c != CharacterIterator.DONE)) {
if (this.c == '\\') {
this.next();
if (this.c == 'u') {
this.add(this.unicode());
} else {
Object value = escapes.get(new Character(this.c));
if (value != null) {
this.add(((Character) value).charValue());
}
}
} else {
this.add();
}
}
this.next();
return this.buf.toString();
}
private void add(char cc) {
this.buf.append(cc);
this.next();
}
private void add() {
this.add(this.c);
}
private void addDigits() {
while (Character.isDigit(this.c)) {
this.add();
}
}
private char unicode() {
int value = 0;
for (int i = 0; i < 4; ++i) {
switch (this.next()) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + (this.c - '0');
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + (this.c - 'W');
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + (this.c - '7');
break;
}
}
return (char) value;
}
}
@@ -0,0 +1,522 @@
/*
* $Id$
*
* 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.json;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.json.annotations.SMD;
import org.apache.struts2.json.annotations.SMDMethod;
import org.apache.struts2.json.annotations.SMDMethodParameter;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <!-- START SNIPPET: description --> <p/> This result serializes an action
* into JSON. <p/> <!-- END SNIPPET: description --> <p/> <p/> <u>Result
* parameters:</u> <p/> <!-- START SNIPPET: parameters --> <p/>
* <ul>
* <p/>
* <li>excludeProperties - list of regular expressions matching the properties
* to be excluded. The regular expressions are evaluated against the OGNL
* expression representation of the properties. </li>
* <p/>
* </ul>
* <p/> <!-- END SNIPPET: parameters --> <p/> <b>Example:</b> <p/>
*
* <pre>
* &lt;!-- START SNIPPET: example --&gt;
* &lt;result name=&quot;success&quot; type=&quot;json&quot; /&gt;
* &lt;!-- END SNIPPET: example --&gt;
* </pre>
*/
public class JSONResult implements Result {
private static final long serialVersionUID = 8624350183189931165L;
private static final Logger LOG = LoggerFactory.getLogger(JSONResult.class);
private String defaultEncoding = "ISO-8859-1";
private List<Pattern> includeProperties;
private List<Pattern> excludeProperties;
private String root;
private boolean wrapWithComments;
private boolean prefix;
private boolean enableSMD = false;
private boolean enableGZIP = false;
private boolean ignoreHierarchy = true;
private boolean ignoreInterfaces = true;
private boolean enumAsBean = JSONWriter.ENUM_AS_BEAN_DEFAULT;
private boolean noCache = false;
private boolean excludeNullProperties = false;
private int statusCode;
private int errorCode;
private String callbackParameter;
private String contentType;
private String wrapPrefix;
private String wrapSuffix;
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setDefaultEncoding(String val) {
this.defaultEncoding = val;
}
/**
* Gets a list of regular expressions of properties to exclude from the JSON
* output.
*
* @return A list of compiled regular expression patterns
*/
public List<Pattern> getExcludePropertiesList() {
return this.excludeProperties;
}
/**
* Sets a comma-delimited list of regular expressions to match properties
* that should be excluded from the JSON output.
*
* @param commaDelim
* A comma-delimited list of regular expressions
*/
public void setExcludeProperties(String commaDelim) {
List<String> excludePatterns = JSONUtil.asList(commaDelim);
if (excludePatterns != null) {
this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
for (String pattern : excludePatterns) {
this.excludeProperties.add(Pattern.compile(pattern));
}
}
}
/**
* @return the includeProperties
*/
public List<Pattern> getIncludePropertiesList() {
return includeProperties;
}
/**
* @param includedProperties
* the includeProperties to set
*/
public void setIncludeProperties(String commaDelim) {
List<String> includePatterns = JSONUtil.asList(commaDelim);
if (includePatterns != null) {
this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
HashMap existingPatterns = new HashMap();
for (String pattern : includePatterns) {
// Compile a pattern for each *unique* "level" of the object
// hierarchy specified in the regex.
String[] patternPieces = pattern.split("\\\\\\.");
String patternExpr = "";
for (String patternPiece : patternPieces) {
if (patternExpr.length() > 0) {
patternExpr += "\\.";
}
patternExpr += patternPiece;
// Check for duplicate patterns so that there is no overlap.
if (!existingPatterns.containsKey(patternExpr)) {
existingPatterns.put(patternExpr, patternExpr);
// Add a pattern that does not have the indexed property
// matching (ie. list\[\d+\] becomes list).
if (patternPiece.endsWith("\\]")) {
this.includeProperties.add(Pattern.compile(patternExpr.substring(0, patternPiece
.lastIndexOf("\\["))));
if (LOG.isDebugEnabled())
LOG.debug("Adding include property expression: "
+ patternExpr.substring(0, patternPiece.lastIndexOf("\\[")));
}
this.includeProperties.add(Pattern.compile(patternExpr));
if (LOG.isDebugEnabled())
LOG.debug("Adding include property expression: " + patternExpr);
}
}
}
}
}
public void execute(ActionInvocation invocation) throws Exception {
ActionContext actionContext = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
try {
String json;
Object rootObject;
if (this.enableSMD) {
// generate SMD
rootObject = this.writeSMD(invocation);
} else {
// generate JSON
if (this.root != null) {
ValueStack stack = invocation.getStack();
rootObject = stack.findValue(this.root);
} else {
rootObject = invocation.getAction();
}
}
json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
enumAsBean, excludeNullProperties);
json = addCallbackIfApplicable(request, json);
boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
writeToResponse(response, json, writeGzip);
} catch (IOException exception) {
LOG.error(exception.getMessage(), exception);
throw exception;
}
}
protected void writeToResponse(HttpServletResponse response, String json, boolean gzip)
throws IOException {
JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), isWrapWithComments(),
json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,
wrapSuffix));
}
@SuppressWarnings("unchecked")
protected org.apache.struts2.json.smd.SMD writeSMD(ActionInvocation invocation) {
ActionContext actionContext = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
// root is based on OGNL expression (action by default)
Object rootObject = null;
if (this.root != null) {
ValueStack stack = invocation.getStack();
rootObject = stack.findValue(this.root);
} else {
rootObject = invocation.getAction();
}
Class clazz = rootObject.getClass();
org.apache.struts2.json.smd.SMD smd = new org.apache.struts2.json.smd.SMD();
// URL
smd.setServiceUrl(request.getRequestURI());
// customize SMD
SMD smdAnnotation = (SMD) clazz.getAnnotation(SMD.class);
if (smdAnnotation != null) {
smd.setObjectName(smdAnnotation.objectName());
smd.setServiceType(smdAnnotation.serviceType());
smd.setVersion(smdAnnotation.version());
}
// get public methods
Method[] methods = JSONUtil.listSMDMethods(clazz, ignoreInterfaces);
for (Method method : methods) {
SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
// SMDMethod annotation is required
if (((smdMethodAnnotation != null) && !this.shouldExcludeProperty(method.getName()))) {
String methodName = smdMethodAnnotation.name().length() == 0 ? method.getName()
: smdMethodAnnotation.name();
org.apache.struts2.json.smd.SMDMethod smdMethod = new org.apache.struts2.json.smd.SMDMethod(
methodName);
smd.addSMDMethod(smdMethod);
// find params for this method
int parametersCount = method.getParameterTypes().length;
if (parametersCount > 0) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parametersCount; i++) {
// are you ever going to pick shorter names? nope
SMDMethodParameter smdMethodParameterAnnotation = this
.getSMDMethodParameterAnnotation(parameterAnnotations[i]);
String paramName = smdMethodParameterAnnotation != null ? smdMethodParameterAnnotation
.name()
: "p" + i;
// goog thing this is the end of the hierarchy,
// oitherwise I would need that 21'' LCD ;)
smdMethod.addSMDMethodParameter(new org.apache.struts2.json.smd.SMDMethodParameter(
paramName));
}
}
} else {
if (LOG.isDebugEnabled())
LOG.debug("Ignoring property " + method.getName());
}
}
return smd;
}
/**
* Find an SMDethodParameter annotation on this array
*/
private org.apache.struts2.json.annotations.SMDMethodParameter getSMDMethodParameterAnnotation(
Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation instanceof org.apache.struts2.json.annotations.SMDMethodParameter)
return (org.apache.struts2.json.annotations.SMDMethodParameter) annotation;
}
return null;
}
private boolean shouldExcludeProperty(String expr) {
if (this.excludeProperties != null) {
for (Pattern pattern : this.excludeProperties) {
if (pattern.matcher(expr).matches())
return true;
}
}
return false;
}
/**
* Retrieve the encoding <p/>
*
* @return The encoding associated with this template (defaults to the value
* of 'struts.i18n.encoding' property)
*/
protected String getEncoding() {
String encoding = this.defaultEncoding;
if (encoding == null) {
encoding = System.getProperty("file.encoding");
}
if (encoding == null) {
encoding = "UTF-8";
}
return encoding;
}
protected String addCallbackIfApplicable(HttpServletRequest request, String json) {
if ((callbackParameter != null) && (callbackParameter.length() > 0)) {
String callbackName = request.getParameter(callbackParameter);
if ((callbackName != null) && (callbackName.length() > 0))
json = callbackName + "(" + json + ")";
}
return json;
}
/**
* @return OGNL expression of root object to be serialized
*/
public String getRoot() {
return this.root;
}
/**
* Sets the root object to be serialized, defaults to the Action
*
* @param root
* OGNL expression of root object to be serialized
*/
public void setRoot(String root) {
this.root = root;
}
/**
* @return Generated JSON must be enclosed in comments
*/
public boolean isWrapWithComments() {
return this.wrapWithComments;
}
/**
* Wrap generated JSON with comments
*
* @param wrapWithComments
*/
public void setWrapWithComments(boolean wrapWithComments) {
this.wrapWithComments = wrapWithComments;
}
/**
* @return Result has SMD generation enabled
*/
public boolean isEnableSMD() {
return this.enableSMD;
}
/**
* Enable SMD generation for action, which can be used for JSON-RPC
*
* @param enableSMD
*/
public void setEnableSMD(boolean enableSMD) {
this.enableSMD = enableSMD;
}
public void setIgnoreHierarchy(boolean ignoreHierarchy) {
this.ignoreHierarchy = ignoreHierarchy;
}
/**
* Controls whether interfaces should be inspected for method annotations
* You may need to set to this true if your action is a proxy as annotations
* on methods are not inherited
*/
public void setIgnoreInterfaces(boolean ignoreInterfaces) {
this.ignoreInterfaces = ignoreInterfaces;
}
/**
* Controls how Enum's are serialized : If true, an Enum is serialized as a
* name=value pair (name=name()) (default) If false, an Enum is serialized
* as a bean with a special property _name=name()
*
* @param enumAsBean
*/
public void setEnumAsBean(boolean enumAsBean) {
this.enumAsBean = enumAsBean;
}
public boolean isEnumAsBean() {
return enumAsBean;
}
public boolean isEnableGZIP() {
return enableGZIP;
}
public void setEnableGZIP(boolean enableGZIP) {
this.enableGZIP = enableGZIP;
}
public boolean isNoCache() {
return noCache;
}
/**
* Add headers to response to prevent the browser from caching the response
*
* @param noCache
*/
public void setNoCache(boolean noCache) {
this.noCache = noCache;
}
public boolean isIgnoreHierarchy() {
return ignoreHierarchy;
}
public boolean isExcludeNullProperties() {
return excludeNullProperties;
}
/**
* Do not serialize properties with a null value
*
* @param excludeNullProperties
*/
public void setExcludeNullProperties(boolean excludeNullProperties) {
this.excludeNullProperties = excludeNullProperties;
}
/**
* Status code to be set in the response
*
* @param statusCode
*/
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
/**
* Error code to be set in the response
*
* @param errorCode
*/
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public void setCallbackParameter(String callbackParameter) {
this.callbackParameter = callbackParameter;
}
public String getCallbackParameter() {
return callbackParameter;
}
/**
* Prefix JSON with "{} &&"
*
* @param prefix
*/
public void setPrefix(boolean prefix) {
this.prefix = prefix;
}
/**
* Content type to be set in the response
*
* @param contentType
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getWrapPrefix() {
return wrapPrefix;
}
/**
* Text to be inserted at the begining of the response
*/
public void setWrapPrefix(String wrapPrefix) {
this.wrapPrefix = wrapPrefix;
}
public String getWrapSuffix() {
return wrapSuffix;
}
/**
* Text to be inserted at the end of the response
*/
public void setWrapSuffix(String wrapSuffix) {
this.wrapSuffix = wrapSuffix;
}
}
@@ -0,0 +1,395 @@
/*
* $Id$
*
* 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.json;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.xwork.StringUtils;
import org.apache.struts2.json.annotations.SMDMethod;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* Wrapper for JSONWriter with some utility methods.
*/
public class JSONUtil {
final static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private static final Logger LOG = LoggerFactory.getLogger(JSONUtil.class);
/**
* Serializes an object into JSON.
*
* @param object
* to be serialized
* @return JSON string
* @throws JSONException
*/
public static String serialize(Object object) throws JSONException {
JSONWriter writer = new JSONWriter();
return writer.write(object);
}
/**
* Serializes an object into JSON, excluding any properties matching any of
* the regular expressions in the given collection.
*
* @param object
* to be serialized
* @param excludeProperties
* Patterns matching properties to exclude
* @param ignoreHierarchy
* whether to ignore properties defined on base classes of the
* root object
* @return JSON string
* @throws JSONException
*/
public static String serialize(Object object, Collection<Pattern> excludeProperties,
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties)
throws JSONException {
JSONWriter writer = new JSONWriter();
writer.setIgnoreHierarchy(ignoreHierarchy);
return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
}
/**
* Serializes an object into JSON, excluding any properties matching any of
* the regular expressions in the given collection.
*
* @param object
* to be serialized
* @param excludeProperties
* Patterns matching properties to exclude
* @param ignoreHierarchy
* whether to ignore properties defined on base classes of the
* root object
* @param enumAsBean
* whether to serialized enums a Bean or name=value pair
* @return JSON string
* @throws JSONException
*/
public static String serialize(Object object, Collection<Pattern> excludeProperties,
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,
boolean excludeNullProperties) throws JSONException {
JSONWriter writer = new JSONWriter();
writer.setIgnoreHierarchy(ignoreHierarchy);
writer.setEnumAsBean(enumAsBean);
return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
}
/**
* Serializes an object into JSON to the given writer.
*
* @param writer
* Writer to serialize the object to
* @param object
* object to be serialized
* @throws IOException
* @throws JSONException
*/
public static void serialize(Writer writer, Object object) throws IOException, JSONException {
writer.write(serialize(object));
}
/**
* Serializes an object into JSON to the given writer, excluding any
* properties matching any of the regular expressions in the given
* collection.
*
* @param writer
* Writer to serialize the object to
* @param object
* object to be serialized
* @param excludeProperties
* Patterns matching properties to ignore
* @throws IOException
* @throws JSONException
*/
public static void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws IOException,
JSONException {
writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties));
}
/**
* Deserializes a object from JSON
*
* @param json
* string in JSON
* @return desrialized object
* @throws JSONException
*/
public static Object deserialize(String json) throws JSONException {
JSONReader reader = new JSONReader();
return reader.read(json);
}
/**
* Deserializes a object from JSON
*
* @param reader
* Reader to read a JSON string from
* @return deserialized object
* @throws JSONException
* when IOException happens
*/
public static Object deserialize(Reader reader) throws JSONException {
// read content
BufferedReader bufferReader = new BufferedReader(reader);
String line = null;
StringBuilder buffer = new StringBuilder();
try {
while ((line = bufferReader.readLine()) != null) {
buffer.append(line);
}
} catch (IOException e) {
throw new JSONException(e);
}
return deserialize(buffer.toString());
}
public static void writeJSONToResponse(SerializationParams serializationParams) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
if (StringUtils.isNotBlank(serializationParams.getSerializedJSON()))
stringBuilder.append(serializationParams.getSerializedJSON());
if (StringUtils.isNotBlank(serializationParams.getWrapPrefix()))
stringBuilder.insert(0, serializationParams.getWrapPrefix());
else if (serializationParams.isWrapWithComments()) {
stringBuilder.insert(0, "/* ");
stringBuilder.append(" */");
} else if (serializationParams.isPrefix())
stringBuilder.insert(0, "{}&& ");
if (StringUtils.isNotBlank(serializationParams.getWrapSuffix()))
stringBuilder.append(serializationParams.getWrapSuffix());
String json = stringBuilder.toString();
if (LOG.isDebugEnabled()) {
LOG.debug("[JSON]" + json);
}
HttpServletResponse response = serializationParams.getResponse();
// status or error code
if (serializationParams.getStatusCode() > 0)
response.setStatus(serializationParams.getStatusCode());
else if (serializationParams.getErrorCode() > 0)
response.sendError(serializationParams.getErrorCode());
// content type
if (serializationParams.isSmd())
response.setContentType("application/json-rpc;charset=" + serializationParams.getEncoding());
else
response.setContentType(serializationParams.getContentType() + ";charset="
+ serializationParams.getEncoding());
if (serializationParams.isNoCache()) {
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "0");
response.setHeader("Pragma", "No-cache");
}
if (serializationParams.isGzip()) {
response.addHeader("Content-Encoding", "gzip");
GZIPOutputStream out = null;
InputStream in = null;
try {
out = new GZIPOutputStream(response.getOutputStream());
in = new ByteArrayInputStream(json.getBytes());
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
if (in != null)
in.close();
if (out != null) {
out.finish();
out.close();
}
}
} else {
response.setContentLength(json.getBytes(serializationParams.getEncoding()).length);
PrintWriter out = response.getWriter();
out.print(json);
}
}
public static List<String> asList(String commaDelim) {
if ((commaDelim == null) || (commaDelim.trim().length() == 0))
return null;
List<String> list = new ArrayList<String>();
String[] split = commaDelim.split(",");
for (int i = 0; i < split.length; i++) {
String trimmed = split[i].trim();
if (trimmed.length() > 0) {
list.add(trimmed);
}
}
return list;
}
/**
* List visible methods carrying the
*
* @SMDMethod annotation
*
* @param ignoreInterfaces
* if true, only the methods of the class are examined. If false,
* annotations on every interfaces' methods are examined.
*/
@SuppressWarnings("unchecked")
public static Method[] listSMDMethods(Class clazz, boolean ignoreInterfaces) {
final List<Method> methods = new LinkedList<Method>();
if (ignoreInterfaces) {
for (Method method : clazz.getMethods()) {
SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
if (smdMethodAnnotation != null) {
methods.add(method);
}
}
} else {
// recurse the entire superclass/interface hierarchy and add in
// order encountered
JSONUtil.visitInterfaces(clazz, new JSONUtil.ClassVisitor() {
public boolean visit(Class aClass) {
for (Method method : aClass.getMethods()) {
SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
if ((smdMethodAnnotation != null) && !methods.contains(method)) {
methods.add(method);
}
}
return true;
}
});
}
Method[] methodResult = new Method[methods.size()];
return methods.toArray(methodResult);
}
/**
* Realizes the visit(Class) method called by vistInterfaces for all
* encountered classes/interfaces
*/
public static interface ClassVisitor {
/**
* Called when a new interface/class is encountered
*
* @param aClass
* the encountered class/interface
* @return true if the recursion should continue, false to stop
* recursion immediately
*/
@SuppressWarnings("unchecked")
boolean visit(Class aClass);
}
/**
* Visit all the interfaces realized by the specified object, its
* superclasses and its interfaces <p/> Visitation is performed in the
* following order: aClass aClass' interfaces the interface's superclasses
* (interfaces) aClass' superclass superclass' interfaces superclass'
* interface's superclasses (interfaces) super-superclass and so on <p/> The
* Object base class is base excluded. Classes/interfaces are only visited
* once each
*
* @param aClass
* the class to start recursing upwards from
* @param visitor
* this vistor is called for each class/interface encountered
* @return true if all classes/interfaces were visited, false if it was
* exited early as specified by a ClassVisitor result
*/
@SuppressWarnings("unchecked")
public static boolean visitInterfaces(Class aClass, ClassVisitor visitor) {
List<Class> classesVisited = new LinkedList<Class>();
return visitUniqueInterfaces(aClass, visitor, classesVisited);
}
/**
* Recursive method to visit all the interfaces of a class (and its
* superclasses and super-interfaces) if they haven't already been visited.
* <p/> Always visits itself if it hasn't already been visited
*
* @param thisClass
* the current class to visit (if not already done so)
* @param classesVisited
* classes already visited
* @param visitor
* this vistor is called for each class/interface encountered
* @return true if recursion can continue, false if it should be aborted
*/
private static boolean visitUniqueInterfaces(Class thisClass, ClassVisitor visitor,
List<Class> classesVisited) {
boolean okayToContinue = true;
if (!classesVisited.contains(thisClass)) {
classesVisited.add(thisClass);
okayToContinue = visitor.visit(thisClass);
if (okayToContinue) {
Class[] interfaces = thisClass.getInterfaces();
int index = 0;
while ((index < interfaces.length) && (okayToContinue)) {
okayToContinue = visitUniqueInterfaces(interfaces[index++], visitor, classesVisited);
}
if (okayToContinue) {
Class superClass = thisClass.getSuperclass();
if ((superClass != null) && (!Object.class.equals(superClass))) {
okayToContinue = visitUniqueInterfaces(superClass, visitor, classesVisited);
}
}
}
}
return okayToContinue;
}
public static boolean isGzipInRequest(HttpServletRequest request) {
String header = request.getHeader("Accept-Encoding");
return (header != null) && (header.indexOf("gzip") >= 0);
}
}
@@ -0,0 +1,549 @@
/*
* $Id$
*
* 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.json;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.text.CharacterIterator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.StringCharacterIterator;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Pattern;
import org.apache.struts2.json.annotations.JSON;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* <p>
* Serializes an object into JavaScript Object Notation (JSON). If cyclic
* references are detected they will be nulled out.
* </p>
*/
class JSONWriter {
private static final Logger LOG = LoggerFactory.getLogger(JSONWriter.class);
/**
* By default, enums are serialzied as name=value pairs
*/
public static final boolean ENUM_AS_BEAN_DEFAULT = false;
static char[] hex = "0123456789ABCDEF".toCharArray();
private StringBuilder buf = new StringBuilder();
private Stack stack = new Stack();
private boolean ignoreHierarchy = true;
private Object root;
private boolean buildExpr = true;
private String exprStack = "";
private Collection<Pattern> excludeProperties;
private Collection<Pattern> includeProperties;
private DateFormat formatter;
private boolean enumAsBean = ENUM_AS_BEAN_DEFAULT;
private boolean excludeNullProperties;
/**
* @param object
* Object to be serialized into JSON
* @return JSON string for object
* @throws JSONException
*/
public String write(Object object) throws JSONException {
return this.write(object, null, null, false);
}
/**
* @param object
* Object to be serialized into JSON
* @return JSON string for object
* @throws JSONException
*/
public String write(Object object, Collection<Pattern> excludeProperties,
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException {
this.excludeNullProperties = excludeNullProperties;
this.buf.setLength(0);
this.root = object;
this.exprStack = "";
this.buildExpr = ((excludeProperties != null) && !excludeProperties.isEmpty())
|| ((includeProperties != null) && !includeProperties.isEmpty());
this.excludeProperties = excludeProperties;
this.includeProperties = includeProperties;
this.value(object, null);
return this.buf.toString();
}
/**
* Detect cyclic references
*/
private void value(Object object, Method method) throws JSONException {
if (object == null) {
this.add("null");
return;
}
if (this.stack.contains(object)) {
Class clazz = object.getClass();
// cyclic reference
if (clazz.isPrimitive() || clazz.equals(String.class)) {
this.process(object, method);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Cyclic reference detected on " + object);
}
this.add("null");
}
return;
}
this.process(object, method);
}
/**
* Serialize object into json
*/
private void process(Object object, Method method) throws JSONException {
this.stack.push(object);
if (object instanceof Class) {
this.string(object);
} else if (object instanceof Boolean) {
this.bool(((Boolean) object).booleanValue());
} else if (object instanceof Number) {
this.add(object);
} else if (object instanceof String) {
this.string(object);
} else if (object instanceof Character) {
this.string(object);
} else if (object instanceof Map) {
this.map((Map) object, method);
} else if (object.getClass().isArray()) {
this.array(object, method);
} else if (object instanceof Iterable) {
this.array(((Iterable) object).iterator(), method);
} else if (object instanceof Date) {
this.date((Date) object, method);
} else if (object instanceof Calendar) {
this.date(((Calendar) object).getTime(), method);
} else if (object instanceof Locale) {
this.string(object);
} else if (object instanceof Enum) {
this.enumeration((Enum) object);
} else {
this.bean(object);
}
this.stack.pop();
}
/**
* Instrospect bean and serialize its properties
*/
private void bean(Object object) throws JSONException {
this.add("{");
BeanInfo info;
try {
Class clazz = object.getClass();
info = ((object == this.root) && this.ignoreHierarchy) ? Introspector.getBeanInfo(clazz, clazz
.getSuperclass()) : Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = info.getPropertyDescriptors();
boolean hasData = false;
for (int i = 0; i < props.length; ++i) {
PropertyDescriptor prop = props[i];
String name = prop.getName();
Method accessor = prop.getReadMethod();
Method baseAccessor = null;
if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) {
try {
baseAccessor = Class.forName(
clazz.getName().substring(0, clazz.getName().indexOf("$$"))).getMethod(
accessor.getName(), accessor.getParameterTypes());
} catch (Exception ex) {
LOG.debug(ex.getMessage(), ex);
}
} else
baseAccessor = accessor;
if (baseAccessor != null) {
JSON json = baseAccessor.getAnnotation(JSON.class);
if (json != null) {
if (!json.serialize())
continue;
else if (json.name().length() > 0)
name = json.name();
}
// ignore "class" and others
if (this.shouldExcludeProperty(clazz, prop)) {
continue;
}
String expr = null;
if (this.buildExpr) {
expr = this.expandExpr(name);
if (this.shouldExcludeProperty(expr)) {
continue;
}
expr = this.setExprStack(expr);
}
Object value = accessor.invoke(object, new Object[0]);
boolean propertyPrinted = this.add(name, value, accessor, hasData);
hasData = hasData || propertyPrinted;
if (this.buildExpr) {
this.setExprStack(expr);
}
}
}
// special-case handling for an Enumeration - include the name() as
// a property */
if (object instanceof Enum) {
Object value = ((Enum) object).name();
this.add("_name", value, object.getClass().getMethod("name"), hasData);
}
} catch (Exception e) {
throw new JSONException(e);
}
this.add("}");
}
/**
* Instrospect an Enum and serialize it as a name/value pair or as a bean
* including all its own properties
*/
private void enumeration(Enum enumeration) throws JSONException {
if (enumAsBean) {
this.bean(enumeration);
} else {
this.string(enumeration.name());
}
}
/**
* Ignore "class" field
*/
private boolean shouldExcludeProperty(Class clazz, PropertyDescriptor prop) throws SecurityException,
NoSuchFieldException {
String name = prop.getName();
if (name.equals("class") || name.equals("declaringClass") || name.equals("cachedSuperClass")
|| name.equals("metaClass")) {
return true;
}
return false;
}
private String expandExpr(int i) {
return this.exprStack + "[" + i + "]";
}
private String expandExpr(String property) {
if (this.exprStack.length() == 0)
return property;
return this.exprStack + "." + property;
}
private String setExprStack(String expr) {
String s = this.exprStack;
this.exprStack = expr;
return s;
}
private boolean shouldExcludeProperty(String expr) {
if (this.excludeProperties != null) {
for (Pattern pattern : this.excludeProperties) {
if (pattern.matcher(expr).matches()) {
if (LOG.isDebugEnabled())
LOG.debug("Ignoring property because of exclude rule: " + expr);
return true;
}
}
}
if (this.includeProperties != null) {
for (Pattern pattern : this.includeProperties) {
if (pattern.matcher(expr).matches()) {
return false;
}
}
if (LOG.isDebugEnabled())
LOG.debug("Ignoring property because of include rule: " + expr);
return true;
}
return false;
}
/**
* Add name/value pair to buffer
*/
private boolean add(String name, Object value, Method method, boolean hasData) throws JSONException {
if (!excludeNullProperties || (value != null)) {
if (hasData) {
this.add(',');
}
this.add('"');
this.add(name);
this.add("\":");
this.value(value, method);
return true;
}
return false;
}
/**
* Add map to buffer
*/
private void map(Map map, Method method) throws JSONException {
this.add("{");
Iterator it = map.entrySet().iterator();
boolean warnedNonString = false; // one report per map
boolean hasData = false;
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
String expr = null;
if (this.buildExpr) {
if (key == null) {
LOG.error("Cannot build expression for null key in " + this.exprStack);
continue;
} else {
expr = this.expandExpr(key.toString());
if (this.shouldExcludeProperty(expr)) {
continue;
}
expr = this.setExprStack(expr);
}
}
if (hasData) {
this.add(',');
}
hasData = true;
if (!warnedNonString && !(key instanceof String)) {
LOG.warn("JavaScript doesn't support non-String keys, using toString() on "
+ key.getClass().getName());
warnedNonString = true;
}
this.value(key.toString(), method);
this.add(":");
this.value(entry.getValue(), method);
if (this.buildExpr) {
this.setExprStack(expr);
}
}
this.add("}");
}
/**
* Add date to buffer
*/
private void date(Date date, Method method) {
JSON json = null;
if (method != null)
json = method.getAnnotation(JSON.class);
if (this.formatter == null)
this.formatter = new SimpleDateFormat(JSONUtil.RFC3339_FORMAT);
DateFormat formatter = (json != null) && (json.format().length() > 0) ? new SimpleDateFormat(json
.format()) : this.formatter;
this.string(formatter.format(date));
}
/**
* Add array to buffer
*/
private void array(Iterator it, Method method) throws JSONException {
this.add("[");
boolean hasData = false;
for (int i = 0; it.hasNext(); i++) {
String expr = null;
if (this.buildExpr) {
expr = this.expandExpr(i);
if (this.shouldExcludeProperty(expr)) {
it.next();
continue;
}
expr = this.setExprStack(expr);
}
if (hasData) {
this.add(',');
}
hasData = true;
this.value(it.next(), method);
if (this.buildExpr) {
this.setExprStack(expr);
}
}
this.add("]");
}
/**
* Add array to buffer
*/
private void array(Object object, Method method) throws JSONException {
this.add("[");
int length = Array.getLength(object);
boolean hasData = false;
for (int i = 0; i < length; ++i) {
String expr = null;
if (this.buildExpr) {
expr = this.expandExpr(i);
if (this.shouldExcludeProperty(expr)) {
continue;
}
expr = this.setExprStack(expr);
}
if (hasData) {
this.add(',');
}
hasData = true;
this.value(Array.get(object, i), method);
if (this.buildExpr) {
this.setExprStack(expr);
}
}
this.add("]");
}
/**
* Add boolean to buffer
*/
private void bool(boolean b) {
this.add(b ? "true" : "false");
}
/**
* escape characters
*/
private void string(Object obj) {
this.add('"');
CharacterIterator it = new StringCharacterIterator(obj.toString());
for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
if (c == '"') {
this.add("\\\"");
} else if (c == '\\') {
this.add("\\\\");
} else if (c == '/') {
this.add("\\/");
} else if (c == '\b') {
this.add("\\b");
} else if (c == '\f') {
this.add("\\f");
} else if (c == '\n') {
this.add("\\n");
} else if (c == '\r') {
this.add("\\r");
} else if (c == '\t') {
this.add("\\t");
} else if (Character.isISOControl(c)) {
this.unicode(c);
} else {
this.add(c);
}
}
this.add('"');
}
/**
* Add object to buffer
*/
private void add(Object obj) {
this.buf.append(obj);
}
/**
* Add char to buffer
*/
private void add(char c) {
this.buf.append(c);
}
/**
* Represent as unicode
*
* @param c
* character to be encoded
*/
private void unicode(char c) {
this.add("\\u");
int n = c;
for (int i = 0; i < 4; ++i) {
int digit = (n & 0xf000) >> 12;
this.add(hex[digit]);
n <<= 4;
}
}
public void setIgnoreHierarchy(boolean ignoreHierarchy) {
this.ignoreHierarchy = ignoreHierarchy;
}
/**
* If true, an Enum is serialized as a bean with a special property
* _name=name() as all as all other properties defined within the enum.<br/>
* If false, an Enum is serialized as a name=value pair (name=name())
*
* @param enumAsBean
* true to serialize an enum as a bean instead of as a name=value
* pair (default=false)
*/
public void setEnumAsBean(boolean enumAsBean) {
this.enumAsBean = enumAsBean;
}
}
@@ -0,0 +1,120 @@
/*
* $Id$
*
* 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.json;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.xwork.StringUtils;
public class SerializationParams {
private static final String DEFAULT_CONTENT_TYPE = "application/json";
private final HttpServletResponse response;
private final String encoding;
private final boolean wrapWithComments;
private final String serializedJSON;
private final boolean smd;
private final boolean gzip;
private final boolean noCache;
private final int statusCode;
private final int errorCode;
private final boolean prefix;
private String contentType = DEFAULT_CONTENT_TYPE;
private String wrapPrefix;
private String wrapSuffix;
public SerializationParams(HttpServletResponse response, String encoding, boolean wrapWithComments,
String serializedJSON, boolean smd, boolean gzip, boolean noCache, int statusCode, int errorCode,
boolean prefix, String contentType, String wrapPrefix, String wrapSuffix) {
this.response = response;
this.encoding = encoding;
this.wrapWithComments = wrapWithComments;
this.serializedJSON = serializedJSON;
this.smd = smd;
this.gzip = gzip;
this.noCache = noCache;
this.statusCode = statusCode;
this.errorCode = errorCode;
this.prefix = prefix;
this.contentType = StringUtils.defaultString(contentType, DEFAULT_CONTENT_TYPE);
this.wrapPrefix = wrapPrefix;
this.wrapSuffix = wrapSuffix;
}
public SerializationParams(HttpServletResponse response, String defaultEncoding,
boolean wrapWithComments, String json, boolean b, boolean b1, boolean noCache, int i, int i1,
boolean prefix, String contentType) {
this(response, defaultEncoding, wrapWithComments, json, b, b1, noCache, i, i1, prefix, contentType,
null, null);
}
public String getWrapSuffix() {
return wrapSuffix;
}
public String getWrapPrefix() {
return wrapPrefix;
}
public HttpServletResponse getResponse() {
return response;
}
public String getEncoding() {
return encoding;
}
public boolean isWrapWithComments() {
return wrapWithComments;
}
public String getSerializedJSON() {
return serializedJSON;
}
public boolean isSmd() {
return smd;
}
public boolean isGzip() {
return gzip;
}
public boolean isNoCache() {
return noCache;
}
public int getStatusCode() {
return statusCode;
}
public int getErrorCode() {
return errorCode;
}
public boolean isPrefix() {
return prefix;
}
public String getContentType() {
return contentType;
}
}
@@ -0,0 +1,41 @@
/*
* $Id$
*
* 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.json.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* Annotation used to customize serialization
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JSON {
String name() default "";
boolean serialize() default true;
boolean deserialize() default true;
String format() default "";
}
@@ -0,0 +1,36 @@
/*
* $Id$
*
* 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.json.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SMD {
String version() default org.apache.struts2.json.smd.SMD.DEFAULT_VERSION;
String objectName();
String serviceType() default org.apache.struts2.json.smd.SMD.DEFAULT_SERVICE_TYPE;
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* 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.json.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SMDMethod {
String name() default "";
}
@@ -0,0 +1,32 @@
/*
* $Id$
*
* 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.json.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface SMDMethodParameter {
String name();
}
@@ -0,0 +1,109 @@
/*
* $Id$
*
* 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.json.rpc;
import java.io.PrintWriter;
import java.io.StringWriter;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/*
* Used to serialize RPC Errors
*/
public class RPCError {
private static final Logger LOG = LoggerFactory.getLogger(RPCError.class);
private int code;
private String name;
private String message;
private String stack;
public RPCError() {
}
public RPCError(String message, int code) {
this.code = code;
this.message = message;
LOG.error(message);
}
public RPCError(String message, RPCErrorCode code) {
this(message, code.code());
}
public RPCError(Throwable t, int code, boolean debug) {
while (t.getCause() != null) {
t = t.getCause();
}
this.code = code;
this.message = t.getMessage();
this.name = t.getClass().getName();
if (debug) {
StringWriter s = new StringWriter();
PrintWriter w = new PrintWriter(s);
t.printStackTrace(w);
w.flush();
this.stack = s.toString();
}
LOG.error(t.getMessage(), t);
}
public RPCError(Throwable t, RPCErrorCode code, boolean debug) {
this(t, code.code(), debug);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getStack() {
return stack;
}
public void setStack(String stack) {
this.stack = stack;
}
}
@@ -0,0 +1,47 @@
/*
* $Id$
*
* 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.json.rpc;
public enum RPCErrorCode {
MISSING_METHOD(100, "'method' parameter is missing in request"),
MISSING_ID(100, "'id' parameter is missing in request"),
INVALID_PROCEDURE_CALL(0, "Invalid procedure call"),
METHOD_NOT_FOUND(101, "Procedure not found"),
PARAMETERS_MISMATCH(102, "Parameters count in request does not patch parameters count on method"),
EXCEPTION(103, "An exception was thrown"),
SMD_DISABLED(104, "SMD is disabled");
private int code;
private String message;
RPCErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int code() {
return code;
}
public String message() {
return this.message;
}
}
@@ -0,0 +1,54 @@
/*
* $Id$
*
* 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.json.rpc;
/**
* Class that will be serialized as a response to an RPC call
*/
public class RPCResponse {
private String id;
private Object result;
private RPCError error;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
public RPCError getError() {
return error;
}
public void setError(RPCError error) {
this.error = error;
}
}
@@ -0,0 +1,78 @@
/*
* $Id$
*
* 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.json.smd;
import java.util.Set;
import java.util.TreeSet;
/*
* HOlds SMD declarations for a class
*/
public class SMD {
public static final String DEFAULT_VERSION = ".1";
public static final String DEFAULT_SERVICE_TYPE = "JSON-RPC";
private String version = DEFAULT_VERSION;
private String objectName;
private String serviceType = DEFAULT_SERVICE_TYPE;
private String serviceUrl;
private Set<SMDMethod> methods = new TreeSet<SMDMethod>();
public void addSMDMethod(SMDMethod method) {
this.methods.add(method);
}
public String getVersion() {
return this.version;
}
public void setVersion(String version) {
this.version = version;
}
public String getObjectName() {
return this.objectName;
}
public void setObjectName(String objectName) {
this.objectName = objectName;
}
public String getServiceType() {
return this.serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
public String getServiceUrl() {
return this.serviceUrl;
}
public void setServiceUrl(String serviceUrl) {
this.serviceUrl = serviceUrl;
}
public Set<SMDMethod> getMethods() {
return this.methods;
}
}
@@ -0,0 +1,75 @@
/*
* $Id$
*
* 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.json.smd;
import java.util.Set;
import java.util.TreeSet;
public class SMDMethod implements Comparable {
private String name;
private Set<SMDMethodParameter> parameters = new TreeSet<SMDMethodParameter>();
public SMDMethod(String name) {
this.name = name;
}
public void addSMDMethodParameter(SMDMethodParameter parameter) {
this.parameters.add(parameter);
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Set<SMDMethodParameter> getParameters() {
return this.parameters;
}
public int compareTo(Object o) {
if (!(o instanceof SMDMethod))
return 1;
if (o == null)
return 1;
SMDMethod other = (SMDMethod) o;
if ((name == null) && (other.name == null))
return 0;
if (name == null)
return -1;
if (name.equals(other.name))
return parameters.size() - other.parameters.size();
return name.compareTo(other.name);
}
public boolean equals(Object obj) {
if (!(obj instanceof SMDMethod))
return false;
SMDMethod toCompare = (SMDMethod) obj;
if ((name == null) && (toCompare.name == null))
return true;
return (name != null) && name.equals(toCompare.name)
&& (parameters.size() == toCompare.parameters.size());
}
}
@@ -0,0 +1,57 @@
/*
* $Id$
*
* 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.json.smd;
public class SMDMethodParameter implements Comparable {
private String name;
public SMDMethodParameter(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int compareTo(Object o) {
if (!(o instanceof SMDMethodParameter))
return 1;
if (o == null)
return 1;
if ((name == null) && (((SMDMethodParameter) o).name == null))
return 0;
if (name == null)
return -1;
return name.compareTo(((SMDMethodParameter) o).name);
}
public boolean equals(Object o) {
if (!(o instanceof SMDMethodParameter))
return false;
if ((name == null) && (((SMDMethodParameter) o).name == null))
return true;
return (name != null) && name.equals(((SMDMethodParameter) o).name);
}
}
@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<taglib 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-jsptaglibrary_2_0.xsd"
version="2.0">
<description>
This exposes JSON util functions from the Struts JSON plugin
</description>
<tlib-version>1.0</tlib-version>
<short-name>json</short-name>
<uri>/struts-json-tags</uri>
<function>
<name>serialize</name>
<function-class>org.apache.struts2.json.JSONUtil</function-class>
<function-signature>
java.lang.String serialize(java.lang.Object)
</function-signature>
</function>
</taglib>
@@ -0,0 +1,16 @@
<?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>
<package name="json-default" extends="struts-default">
<result-types>
<result-type name="json" class="org.apache.struts2.json.JSONResult"/>
</result-types>
<interceptors>
<interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>
</interceptors>
</package>
</struts>
@@ -0,0 +1,28 @@
/*
* $Id$
*
* 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.json;
/**
* An enumeration for JSON serialization testing
*/
public enum AnEnum {
ValueA, ValueB, ValueC
}
@@ -0,0 +1,45 @@
/*
* $Id$
*
* 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.json;
/**
* A more complicated Enum with additional properties.
*/
public enum AnEnumBean {
One("A", "B"), Two("C", "D"), Three("E", "F");
private String propA;
private String propB;
AnEnumBean(String propA, String propB) {
this.propA = propA;
this.propB = propB;
}
public String getPropA() {
return propA;
}
public String getPropB() {
return propB;
}
}
@@ -0,0 +1,151 @@
/*
* $Id$
*
* 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.json;
import java.math.BigDecimal;
import java.math.BigInteger;
public class Bean {
private String stringField;
private int intField;
private boolean booleanField;
private char charField;
private long longField;
private float floatField;
private double doubleField;
private Object objectField;
private byte byteField;
private AnEnum enumField;
private AnEnumBean enumBean;
private BigDecimal bigDecimal;
private BigInteger bigInteger;
/**
* @return the byteField
*/
public byte getByteField() {
return this.byteField;
}
/**
* @param byteField
* the byteField to set
*/
public void setByteField(byte byteField) {
this.byteField = byteField;
}
public boolean isBooleanField() {
return this.booleanField;
}
public void setBooleanField(boolean booleanField) {
this.booleanField = booleanField;
}
public char getCharField() {
return this.charField;
}
public void setCharField(char charField) {
this.charField = charField;
}
public double getDoubleField() {
return this.doubleField;
}
public void setDoubleField(double doubleField) {
this.doubleField = doubleField;
}
public float getFloatField() {
return this.floatField;
}
public void setFloatField(float floatField) {
this.floatField = floatField;
}
public int getIntField() {
return this.intField;
}
public void setIntField(int intField) {
this.intField = intField;
}
public long getLongField() {
return this.longField;
}
public void setLongField(long longField) {
this.longField = longField;
}
public Object getObjectField() {
return this.objectField;
}
public void setObjectField(Object objectField) {
this.objectField = objectField;
}
public String getStringField() {
return this.stringField;
}
public void setStringField(String stringField) {
this.stringField = stringField;
}
public AnEnum getEnumField() {
return enumField;
}
public void setEnumField(AnEnum enumField) {
this.enumField = enumField;
}
public AnEnumBean getEnumBean() {
return enumBean;
}
public void setEnumBean(AnEnumBean enumBean) {
this.enumBean = enumBean;
}
public BigInteger getBigInteger() {
return bigInteger;
}
public void setBigInteger(BigInteger bigInteger) {
this.bigInteger = bigInteger;
}
public BigDecimal getBigDecimal() {
return bigDecimal;
}
public void setBigDecimal(BigDecimal bigDecimal) {
this.bigDecimal = bigDecimal;
}
}
@@ -0,0 +1,127 @@
/*
* $Id$
*
* 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.json;
import java.util.Map;
import junit.framework.TestCase;
/**
* Test serialization of an Enum in the two supported modes: - Enum as a
* name=value pair; - Enum as a bean
*/
public class JSONEnumTest extends TestCase {
/**
* Asserts that a bean can be serialized to JSON and restored as a map
*/
public void testEnumAsNameValue() throws Exception {
Bean bean1 = new Bean();
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(AnEnum.ValueA);
bean1.setEnumBean(AnEnumBean.Two);
JSONWriter jsonWriter = new JSONWriter();
jsonWriter.setEnumAsBean(false);
String json = jsonWriter.write(bean1);
Map result = (Map) JSONUtil.deserialize(json);
assertEquals("str", result.get("stringField"));
assertEquals(true, result.get("booleanField"));
assertEquals("s", result.get("charField")); // note: this is a
// String
assertEquals(10.1, result.get("doubleField"));
assertEquals(1.5, result.get("floatField")); // note: this is a
// Double
assertEquals(10L, result.get("intField")); // note: this is a
// Long
assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) result.get("enumField"))); // note:
// this
// is a
// String
assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) result.get("enumBean"))); // note:
// this
// is a
// String
}
/**
* Asserts that a bean can be serialized to JSON and restored as a map <p/>
* In this case, the name of the enum is in _name and the two properties of
* AnEnumBean are also serialized
*/
public void testEnumAsBean() throws Exception {
Bean bean1 = new Bean();
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(AnEnum.ValueA);
bean1.setEnumBean(AnEnumBean.Two);
JSONWriter jsonWriter = new JSONWriter();
jsonWriter.setEnumAsBean(true);
String json = jsonWriter.write(bean1);
Map result = (Map) JSONUtil.deserialize(json);
assertEquals("str", result.get("stringField"));
assertEquals(true, result.get("booleanField"));
assertEquals("s", result.get("charField")); // note: this is a
// String
assertEquals(10.1, result.get("doubleField"));
assertEquals(1.5, result.get("floatField")); // note: this is a
// Double
assertEquals(10L, result.get("intField")); // note: this is a
// Long
Map enumBean1 = (Map) result.get("enumField");
assertNotNull(enumBean1);
assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) enumBean1.get("_name"))); // get
// the
// special
// name
// property
Map enumBean2 = (Map) result.get("enumBean");
assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) enumBean2.get("_name"))); // get
// the
// special
// name
// property
assertEquals(AnEnumBean.Two.getPropA(), (String) enumBean2.get("propA")); // get
// the
// propA
// property
assertEquals(AnEnumBean.Two.getPropB(), (String) enumBean2.get("propB")); // get
// the
// propA
// property
}
}
@@ -0,0 +1,502 @@
/*
* $Id$
*
* 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.json;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.StrutsTestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.util.ValueStack;
public class JSONInterceptorTest extends StrutsTestCase {
private MockActionInvocationEx invocation;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private void setRequestContent(String fileName) throws Exception {
String content = TestUtils.readContent(JSONInterceptorTest.class.getResource(fileName));
this.request.setContent(content.getBytes());
}
public void testBadJSON1() throws Exception {
tryBadJSON("bad-1.txt");
}
public void testBadJSON2() throws Exception {
tryBadJSON("bad-2.txt");
}
public void testBadJSON3() throws Exception {
tryBadJSON("bad-3.txt");
}
public void testBadJSON4() throws Exception {
tryBadJSON("bad-4.txt");
}
public void testBadJSON5() throws Exception {
tryBadJSON("bad-5.txt");
}
public void testBadToTheBoneJSON4() throws Exception {
tryBadJSON("bad-to-the-bone.txt");
}
private void tryBadJSON(String fileName) throws Exception {
// request
setRequestContent(fileName);
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
// JSON is not well formed, throw exception
try {
interceptor.intercept(this.invocation);
fail("Should have thrown an exception");
} catch (JSONException e) {
// I can't get JUnit to ignore the exception
// @Test(expected = JSONException.class)
}
}
public void testSMDDisabledSMD() throws Exception {
// request
setRequestContent("smd-3.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
// SMD was not enabled so invocation must happen
try {
interceptor.intercept(this.invocation);
} catch (JSONException e) {
fail("Should have not thrown an exception");
}
}
public void testSMDAliasedMethodCall1() throws Exception {
// request
setRequestContent("smd-14.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest2 action = new SMDActionTest2();
this.invocation.setAction(action);
interceptor.intercept(this.invocation);
// method was aliased, but was invoked with the regular name
// so method must not be invoked
assertFalse(this.invocation.isInvoked());
assertFalse(action.isDoSomethingInvoked());
}
public void testSMDAliasedMethodCall2() throws Exception {
// request
setRequestContent("smd-15.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest2 action = new SMDActionTest2();
this.invocation.setAction(action);
interceptor.intercept(this.invocation);
// method was aliased, but was invoked with the aliased name
// so method must be invoked
assertFalse(this.invocation.isInvoked());
assertTrue(action.isDoSomethingInvoked());
}
public void testSMDNoMethod() throws Exception {
// request
setRequestContent("smd-4.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
// SMD was enabled so invocation must happen
interceptor.intercept(this.invocation);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-13.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertFalse(this.invocation.isInvoked());
}
public void testSMDMethodWithoutAnnotations() throws Exception {
// request
setRequestContent("smd-9.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
// SMD was enabled so invocation must happen
try {
interceptor.intercept(this.invocation);
assertTrue("Exception was expected here!", true);
} catch (Exception e) {
// ok
}
assertFalse(this.invocation.isInvoked());
}
public void testSMDPrimitivesNoResult() throws Exception {
// request
setRequestContent("smd-6.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
// can't be invoked
interceptor.intercept(this.invocation);
assertFalse(this.invocation.isInvoked());
// asert values were passed properly
assertEquals("string", action.getStringParam());
assertEquals(1, action.getIntParam());
assertEquals(true, action.isBooleanParam());
assertEquals('c', action.getCharParam());
assertEquals(2, action.getLongParam());
assertEquals(new Float(3.3), action.getFloatParam());
assertEquals(4.4, action.getDoubleParam());
assertEquals(5, action.getShortParam());
assertEquals(6, action.getByteParam());
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-11.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json-rpc;charset=ISO-8859-1", response.getContentType());
}
public void testSMDReturnObject() throws Exception {
// request
setRequestContent("smd-10.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest2 action = new SMDActionTest2();
this.invocation.setAction(action);
// can't be invoked
interceptor.intercept(this.invocation);
assertFalse(this.invocation.isInvoked());
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-12.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json-rpc;charset=ISO-8859-1", response.getContentType());
}
@SuppressWarnings("unchecked")
public void testSMDObjectsNoResult() throws Exception {
// request
setRequestContent("smd-7.txt");
this.request.addHeader("content-type", "application/json-rpc");
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setEnableSMD(true);
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
// can't be invoked
interceptor.intercept(this.invocation);
assertFalse(this.invocation.isInvoked());
// asert values were passed properly
Bean bean = action.getBeanParam();
assertNotNull(bean);
assertTrue(bean.isBooleanField());
assertEquals(bean.getStringField(), "test");
assertEquals(bean.getIntField(), 10);
assertEquals(bean.getCharField(), 's');
assertEquals(bean.getDoubleField(), 10.1);
assertEquals(bean.getByteField(), 3);
List list = action.getListParam();
assertNotNull(list);
assertEquals("str0", list.get(0));
assertEquals("str1", list.get(1));
Map map = action.getMapParam();
assertNotNull(map);
assertNotNull(map.get("a"));
assertEquals(new Long(1), map.get("a"));
assertNotNull(map.get("c"));
List insideList = (List) map.get("c");
assertEquals(1.0d, insideList.get(0));
assertEquals(2.0d, insideList.get(1));
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-11.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json-rpc;charset=ISO-8859-1", response.getContentType());
}
@SuppressWarnings( { "unchecked", "unchecked" })
public void testReadEmpty() throws Exception {
// request
setRequestContent("json-6.txt");
this.request.addHeader("content-type", "application/json");
// interceptor
JSONInterceptor interceptor = new JSONInterceptor();
TestAction action = new TestAction();
this.invocation.setAction(action);
interceptor.intercept(this.invocation);
}
@SuppressWarnings( { "unchecked", "unchecked" })
public void test() throws Exception {
// request
setRequestContent("json-1.txt");
this.request.addHeader("content-type", "application/json");
// interceptor
JSONInterceptor interceptor = new JSONInterceptor();
TestAction action = new TestAction();
this.invocation.setAction(action);
interceptor.intercept(this.invocation);
// serialize and compare
List list = action.getList();
assertNotNull(list);
assertEquals(list.size(), 10);
list = action.getCollection();
assertNotNull(list);
assertEquals(list.size(), 3);
assertEquals(list.get(0), "b");
assertEquals(list.get(1), 1L);
list = (List) list.get(2);
assertNotNull(list);
assertEquals(list.size(), 2);
assertEquals(list.get(0), 10L);
assertEquals(list.get(1), 12L);
list = action.getCollection2();
assertNotNull(list);
assertEquals(list.size(), 1);
// inside a map any primitive is either: String, Long, Boolean or Double
Map bean = (Map) list.get(0);
assertNotNull(bean);
assertTrue((Boolean) bean.get("booleanField"));
assertEquals(bean.get("charField"), "s");
assertEquals(bean.get("doubleField"), 10.1);
assertEquals(bean.get("floatField"), 1.5);
assertEquals(bean.get("intField"), 10L);
assertEquals(bean.get("longField"), 100L);
assertEquals(bean.get("stringField"), "str");
bean = (Map) bean.get("objectField");
assertNotNull(bean);
assertFalse((Boolean) bean.get("booleanField"));
assertEquals(bean.get("charField"), "\u0000");
assertEquals(bean.get("doubleField"), 2.2);
assertEquals(bean.get("floatField"), 1.1);
assertEquals(bean.get("intField"), 0L);
assertEquals(bean.get("longField"), 0L);
assertEquals(bean.get("stringField"), " ");
assertEquals(action.getFoo(), "foo");
Map map = action.getMap();
assertNotNull(map);
assertEquals(map.size(), 2);
assertEquals(map.get("a"), 1L);
list = (List) map.get("c");
assertNotNull(list);
assertEquals(list.size(), 2);
assertEquals(list.get(0), 1.0);
assertEquals(list.get(1), 2.0);
assertEquals(action.getResult(), null);
Bean bean2 = action.getBean();
assertNotNull(bean2);
assertTrue(bean2.isBooleanField());
assertEquals(bean2.getStringField(), "test");
assertEquals(bean2.getIntField(), 10);
assertEquals(bean2.getCharField(), 's');
assertEquals(bean2.getDoubleField(), 10.1);
assertEquals(bean2.getByteField(), 3);
String[] strArray = action.getArray();
assertNotNull(strArray);
assertEquals(strArray.length, 2);
assertEquals(strArray[0], "str0");
assertEquals(strArray[1], "str1");
int[] intArray = action.getIntArray();
assertNotNull(intArray);
assertEquals(intArray.length, 2);
assertEquals(intArray[0], 1);
assertEquals(intArray[1], 2);
Bean[] beanArray = action.getBeanArray();
assertNotNull(beanArray);
assertNotNull(beanArray[0]);
assertEquals(beanArray[0].getStringField(), "bean1");
assertNotNull(beanArray[1]);
assertEquals(beanArray[1].getStringField(), "bean2");
Calendar calendar = Calendar.getInstance();
calendar.setTime(action.getDate());
assertEquals(calendar.get(Calendar.YEAR), 1999);
assertEquals(calendar.get(Calendar.MONTH), Calendar.DECEMBER);
assertEquals(calendar.get(Calendar.DAY_OF_MONTH), 31);
assertEquals(calendar.get(Calendar.HOUR), 11);
assertEquals(calendar.get(Calendar.MINUTE), 59);
assertEquals(calendar.get(Calendar.SECOND), 59);
calendar.setTime(action.getDate2());
assertEquals(calendar.get(Calendar.YEAR), 1999);
assertEquals(calendar.get(Calendar.MONTH), Calendar.DECEMBER);
assertEquals(calendar.get(Calendar.DAY_OF_MONTH), 31);
// test desrialize=false
assertNull(action.getFoo2());
}
public void testRoot() throws Exception {
setRequestContent("json-5.txt");
this.request.addHeader("content-type", "application/json");
// interceptor
JSONInterceptor interceptor = new JSONInterceptor();
interceptor.setRoot("bean");
TestAction4 action = new TestAction4();
this.invocation.setAction(action);
this.invocation.getStack().push(action);
interceptor.intercept(this.invocation);
Bean bean2 = action.getBean();
assertNotNull(bean2);
assertTrue(bean2.isBooleanField());
assertEquals(bean2.getStringField(), "test");
assertEquals(bean2.getIntField(), 10);
assertEquals(bean2.getCharField(), 's');
assertEquals(bean2.getDoubleField(), 10.1);
assertEquals(bean2.getByteField(), 3);
}
@Override
protected void setUp() throws Exception {
super.setUp();
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
ActionContext context = ActionContext.getContext();
ValueStack stack = context.getValueStack();
ActionContext.setContext(context);
context.put(StrutsStatics.HTTP_REQUEST, this.request);
context.put(StrutsStatics.HTTP_RESPONSE, this.response);
MockServletContext servletContext = new MockServletContext();
context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
this.invocation = new MockActionInvocationEx();
this.invocation.setInvocationContext(context);
this.invocation.setStack(stack);
}
}
class MockActionInvocationEx extends MockActionInvocation {
private boolean invoked;
@Override
public String invoke() throws Exception {
this.invoked = true;
return super.invoke();
}
public boolean isInvoked() {
return this.invoked;
}
public void setInvoked(boolean invoked) {
this.invoked = invoked;
}
}
@@ -0,0 +1,163 @@
/*
* $Id$
*
* 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.json;
import java.beans.IntrospectionException;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
public class JSONPopulatorTest extends TestCase {
public void testNulls() throws IntrospectionException, InvocationTargetException, NoSuchMethodException,
JSONException, InstantiationException, IllegalAccessException {
JSONPopulator populator = new JSONPopulator();
OtherBean bean = new OtherBean();
Map jsonMap = new HashMap();
jsonMap.put("intField", null);
jsonMap.put("booleanField", null);
jsonMap.put("charField", null);
jsonMap.put("longField", null);
jsonMap.put("floatField", null);
jsonMap.put("doubleField", null);
jsonMap.put("byteField", null);
populator.populateObject(bean, jsonMap);
assertNull(bean.getIntField());
assertNull(bean.isBooleanField());
assertNull(bean.getCharField());
assertNull(bean.getLongField());
assertNull(bean.getDoubleField());
assertNull(bean.getByteField());
}
public void testPrimitiveBean() throws Exception {
StringReader stringReader = new StringReader(TestUtils.readContent(JSONInterceptorTest.class
.getResource("json-7.txt")));
Object json = JSONUtil.deserialize(stringReader);
assertNotNull(json);
assertTrue(json instanceof Map);
Map jsonMap = (Map) json;
JSONPopulator populator = new JSONPopulator();
Bean bean = new Bean();
populator.populateObject(bean, jsonMap);
assertTrue(bean.isBooleanField());
assertEquals("test\u000E\u000f", bean.getStringField());
assertEquals(10, bean.getIntField());
assertEquals('s', bean.getCharField());
assertEquals(10.1d, bean.getDoubleField(), 0d);
assertEquals(3, bean.getByteField());
assertEquals(new BigDecimal(111111.5d), bean.getBigDecimal());
assertEquals(new BigInteger("111111"), bean.getBigInteger());
}
public void testObjectBean() throws Exception {
String text = TestUtils.readContent(JSONInterceptorTest.class.getResource("json-7.txt"));
Object json = JSONUtil.deserialize(text);
assertNotNull(json);
assertTrue(json instanceof Map);
Map jsonMap = (Map) json;
JSONPopulator populator = new JSONPopulator();
WrapperClassBean bean = new WrapperClassBean();
populator.populateObject(bean, jsonMap);
assertEquals(Boolean.TRUE, bean.getBooleanField());
assertEquals(true, bean.isPrimitiveBooleanField1());
assertEquals(false, bean.isPrimitiveBooleanField2());
assertEquals(false, bean.isPrimitiveBooleanField3());
assertEquals("test\u000E\u000f", bean.getStringField());
assertEquals(new Integer(10), bean.getIntField());
assertEquals(0, bean.getNullIntField());
assertEquals(new Character('s'), bean.getCharField());
assertEquals(10.1d, bean.getDoubleField());
assertEquals(new Byte((byte) 3), bean.getByteField());
assertEquals(2, bean.getListField().size());
assertEquals("1", bean.getListField().get(0).getValue());
assertEquals("2", bean.getListField().get(1).getValue());
assertEquals(1, bean.getListMapField().size());
assertEquals(2, bean.getListMapField().get(0).size());
assertEquals(new Long(2073501), bean.getListMapField().get(0).get("id1"));
assertEquals(new Long(3), bean.getListMapField().get(0).get("id2"));
assertEquals(2, bean.getMapListField().size());
assertEquals(3, bean.getMapListField().get("id1").size());
assertEquals(new Long(2), bean.getMapListField().get("id1").get(1));
assertEquals(4, bean.getMapListField().get("id2").size());
assertEquals(new Long(3), bean.getMapListField().get("id2").get(1));
assertEquals(1, bean.getArrayMapField().length);
assertEquals(2, bean.getArrayMapField()[0].size());
assertEquals(new Long(2073501), bean.getArrayMapField()[0].get("id1"));
assertEquals(new Long(3), bean.getArrayMapField()[0].get("id2"));
}
public void testObjectBeanWithStrings() throws Exception {
StringReader stringReader = new StringReader(TestUtils.readContent(JSONInterceptorTest.class
.getResource("json-8.txt")));
Object json = JSONUtil.deserialize(stringReader);
assertNotNull(json);
assertTrue(json instanceof Map);
Map jsonMap = (Map) json;
JSONPopulator populator = new JSONPopulator();
WrapperClassBean bean = new WrapperClassBean();
populator.populateObject(bean, jsonMap);
assertEquals(Boolean.TRUE, bean.getBooleanField());
assertEquals("test", bean.getStringField());
assertEquals(new Integer(10), bean.getIntField());
assertEquals(new Character('s'), bean.getCharField());
assertEquals(10.1d, bean.getDoubleField());
assertEquals(new Byte((byte) 3), bean.getByteField());
assertEquals(null, bean.getListField());
assertEquals(null, bean.getListMapField());
assertEquals(null, bean.getMapListField());
assertEquals(null, bean.getArrayMapField());
}
public void testInfiniteLoop() throws JSONException {
try {
JSONReader reader = new JSONReader();
reader.read("[1,\"a]");
fail("Should have thrown an exception");
} catch (JSONException e) {
// I can't get JUnit to ignore the exception
// @Test(expected = JSONException.class)
}
}
public void testParseBadInput() throws JSONException {
try {
JSONReader reader = new JSONReader();
reader.read("[1,\"a\"1]");
fail("Should have thrown an exception");
} catch (JSONException e) {
// I can't get JUnit to ignore the exception
// @Test(expected = JSONException.class)
}
}
}
@@ -0,0 +1,534 @@
/*
+ * $Id$
+ *
* 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.json;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.StrutsTestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.util.ValueStack;
/**
* JSONResultTest
*/
public class JSONResultTest extends StrutsTestCase {
MockActionInvocation invocation;
MockHttpServletResponse response;
MockServletContext servletContext;
ActionContext context;
ValueStack stack;
MockHttpServletRequest request;
public void testJSONUtilNPEOnNullMehtod() {
Map map = new HashMap();
map.put("createtime", new Date());
try {
JSONUtil.serialize(map);
} catch (JSONException e) {
fail(e.getMessage());
}
}
public void testJSONWriterEndlessLoopOnExludedProperties() throws JSONException {
Pattern all = Pattern.compile(".*");
JSONWriter writer = new JSONWriter();
writer.write(Arrays.asList("a", "b"), Arrays.asList(all), null, false);
}
public void testSMDDisabledSMD() throws Exception {
JSONResult result = new JSONResult();
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
result.execute(this.invocation);
String smd = response.getContentAsString();
String normalizedActual = TestUtils.normalize(smd, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-8.txt"));
assertEquals(normalizedExpected, normalizedActual);
}
public void testSMDDefault() throws Exception {
JSONResult result = new JSONResult();
result.setEnableSMD(true);
SMDActionTest1 action = new SMDActionTest1();
this.invocation.setAction(action);
result.execute(this.invocation);
String smd = response.getContentAsString();
String normalizedActual = TestUtils.normalize(smd, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-1.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
public void testSMDDefaultAnnotations() throws Exception {
JSONResult result = new JSONResult();
result.setEnableSMD(true);
SMDActionTest2 action = new SMDActionTest2();
this.invocation.setAction(action);
result.execute(this.invocation);
String smd = response.getContentAsString();
String normalizedActual = TestUtils.normalize(smd, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-2.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
public void testExcludeNullPropeties() throws Exception {
JSONResult result = new JSONResult();
result.setExcludeNullProperties(true);
TestAction action = new TestAction();
action.setFoo("fool");
this.invocation.setAction(action);
result.execute(this.invocation);
String smd = response.getContentAsString();
String normalizedActual = TestUtils.normalize(smd, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("nulls-1.txt"));
assertEquals(normalizedExpected, normalizedActual);
}
public void testWrapPrefix() throws Exception {
JSONResult result = new JSONResult();
result.setWrapPrefix("_prefix_");
TestAction2 action = new TestAction2();
this.invocation.setAction(action);
result.execute(this.invocation);
String out = response.getContentAsString();
String normalizedActual = TestUtils.normalize(out, true);
String normalizedExpected = "_prefix_{\"name\":\"name\"}";
assertEquals(normalizedExpected, normalizedActual);
}
public void testSuffix() throws Exception {
JSONResult result = new JSONResult();
result.setWrapSuffix("_suffix_");
TestAction2 action = new TestAction2();
this.invocation.setAction(action);
result.execute(this.invocation);
String out = response.getContentAsString();
String normalizedActual = TestUtils.normalize(out, true);
String normalizedExpected = "{\"name\":\"name\"}_suffix_";
assertEquals(normalizedExpected, normalizedActual);
}
public void testPrefixAndSuffix() throws Exception {
JSONResult result = new JSONResult();
result.setWrapPrefix("_prefix_");
result.setWrapSuffix("_suffix_");
TestAction2 action = new TestAction2();
this.invocation.setAction(action);
result.execute(this.invocation);
String out = response.getContentAsString();
String normalizedActual = TestUtils.normalize(out, true);
String normalizedExpected = "_prefix_{\"name\":\"name\"}_suffix_";
assertEquals(normalizedExpected, normalizedActual);
}
public void testPrefix() throws Exception {
JSONResult result = new JSONResult();
result.setExcludeNullProperties(true);
result.setPrefix(true);
TestAction action = new TestAction();
action.setFoo("fool");
this.invocation.setAction(action);
result.execute(this.invocation);
String smd = response.getContentAsString();
String normalizedActual = TestUtils.normalize(smd, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("prefix-1.txt"));
assertEquals(normalizedExpected, normalizedActual);
}
@SuppressWarnings("unchecked")
public void test() throws Exception {
JSONResult result = new JSONResult();
TestAction action = new TestAction();
// test scape characters
action.setArray(new String[] { "a", "a", "\"", "\\", "/", "\b", "\f", "\n", "\r", "\t" });
List list = new ArrayList();
list.add("b");
list.add(1);
list.add(new int[] { 10, 12 });
action.setCollection(list);
// beans
List collection2 = new ArrayList();
Bean bean1 = new Bean();
bean1.setBigDecimal(new BigDecimal("111111.111111"));
bean1.setBigInteger(new BigInteger("111111111111"));
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(AnEnum.ValueA);
bean1.setEnumBean(AnEnumBean.One);
Bean bean2 = new Bean();
bean2.setStringField(" ");
bean2.setBooleanField(false);
bean2.setFloatField(1.1f);
bean2.setDoubleField(2.2);
bean2.setEnumField(AnEnum.ValueB);
bean2.setEnumBean(AnEnumBean.Two);
// circular reference
bean1.setObjectField(bean2);
bean2.setObjectField(bean1);
collection2.add(bean1);
action.setCollection2(collection2);
// keep order in map
Map map = new LinkedHashMap();
map.put("a", 1);
map.put("c", new float[] { 1.0f, 2.0f });
action.setMap(map);
action.setFoo("foo");
// should be ignored, marked 'transient'
action.setBar("bar");
// date
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 1999);
calendar.set(Calendar.MONTH, Calendar.DECEMBER);
calendar.set(Calendar.DAY_OF_MONTH, 31);
calendar.set(Calendar.HOUR_OF_DAY, 11);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
action.setDate(calendar.getTime());
action.setDate2(calendar.getTime());
this.invocation.setAction(action);
result.execute(this.invocation);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
@SuppressWarnings("unchecked")
public void testHierarchy() throws Exception {
JSONResult result = new JSONResult();
result.setIgnoreHierarchy(false);
TestAction3 action = new TestAction3();
this.invocation.setAction(action);
result.execute(this.invocation);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-4.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
@SuppressWarnings("unchecked")
public void testCommentWrap() throws Exception {
JSONResult result = new JSONResult();
TestAction action = new TestAction();
// test scape characters
action.setArray(new String[] { "a", "a", "\"", "\\", "/", "\b", "\f", "\n", "\r", "\t" });
List list = new ArrayList();
list.add("b");
list.add(1);
list.add(new int[] { 10, 12 });
action.setCollection(list);
// beans
List collection2 = new ArrayList();
Bean bean1 = new Bean();
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(null);
bean1.setEnumBean(null);
Bean bean2 = new Bean();
bean2.setStringField(" ");
bean2.setBooleanField(false);
bean2.setFloatField(1.1f);
bean2.setDoubleField(2.2);
bean2.setEnumField(AnEnum.ValueC);
bean2.setEnumBean(AnEnumBean.Three);
// circular reference
bean1.setObjectField(bean2);
bean2.setObjectField(bean1);
collection2.add(bean1);
action.setCollection2(collection2);
// keep order in map
Map map = new LinkedHashMap();
map.put("a", 1);
map.put("c", new float[] { 1.0f, 2.0f });
action.setMap(map);
action.setFoo("foo");
// should be ignored, marked 'transient'
action.setBar("bar");
// date
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 1999);
calendar.set(Calendar.MONTH, Calendar.DECEMBER);
calendar.set(Calendar.DAY_OF_MONTH, 31);
calendar.set(Calendar.HOUR_OF_DAY, 11);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
action.setDate(calendar.getTime());
action.setDate2(calendar.getTime());
this.invocation.setAction(action);
result.setWrapWithComments(true);
result.execute(this.invocation);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-3.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
private void executeTest2Action(JSONResult result) throws Exception {
TestAction action = new TestAction();
// beans
Bean bean1 = new Bean();
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(AnEnum.ValueA);
bean1.setEnumBean(AnEnumBean.One);
// set root
action.setBean(bean1);
result.setRoot("bean");
stack.push(action);
this.invocation.setStack(stack);
this.invocation.setAction(action);
result.execute(this.invocation);
}
public void test2() throws Exception {
JSONResult result = new JSONResult();
executeTest2Action(result);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-2.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
public void testJSONP() throws Exception {
JSONResult result = new JSONResult();
result.setCallbackParameter("callback");
request.addParameter("callback", "exec");
executeTest2Action(result);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("jsonp-1.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
public void testNoCache() throws Exception {
JSONResult result = new JSONResult();
result.setNoCache(true);
executeTest2Action(result);
assertEquals("no-cache", response.getHeader("Cache-Control"));
assertEquals("0", response.getHeader("Expires"));
assertEquals("No-cache", response.getHeader("Pragma"));
}
public void testContentType() throws Exception {
JSONResult result = new JSONResult();
result.setContentType("some_super_content");
executeTest2Action(result);
assertEquals("some_super_content;charset=ISO-8859-1", response.getContentType());
}
public void testStatusCode() throws Exception {
JSONResult result = new JSONResult();
result.setStatusCode(HttpServletResponse.SC_CONTINUE);
executeTest2Action(result);
assertEquals(HttpServletResponse.SC_CONTINUE, response.getStatus());
}
/**
* Repeats test2 but with the Enum serialized as a bean
*/
public void test2WithEnumBean() throws Exception {
JSONResult result = new JSONResult();
result.setEnumAsBean(true);
executeTest2Action(result);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-2-enum.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
/**
* Ensures that properties of given root object are read as shallow
* (non-recursive) unless specifically included.
*/
public void testIncludeProperties() throws Exception {
JSONResult result = new JSONResult();
result.setIncludeProperties("foo");
TestAction action = new TestAction();
action.setFoo("fooValue");
action.setBean(new Bean());
this.invocation.setAction(action);
result.execute(this.invocation);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-9.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
public void testIncludePropertiesWithList() throws Exception {
JSONResult result = new JSONResult();
result.setIncludeProperties("^list\\[\\d+\\]\\.booleanField");
TestAction action = new TestAction();
List list = new ArrayList();
list.add(new Bean());
list.add(new Bean());
list.add(new Bean());
action.setList(list);
this.invocation.setAction(action);
result.execute(this.invocation);
String json = response.getContentAsString();
String normalizedActual = TestUtils.normalize(json, true);
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-10.txt"));
assertEquals(normalizedExpected, normalizedActual);
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
}
@Override
protected void setUp() throws Exception {
super.setUp();
this.response = new MockHttpServletResponse();
this.request = new MockHttpServletRequest();
this.request.setRequestURI("http://sumeruri");
this.context = ActionContext.getContext();
this.context.put(StrutsStatics.HTTP_RESPONSE, this.response);
this.context.put(StrutsStatics.HTTP_REQUEST, this.request);
this.stack = context.getValueStack();
this.servletContext = new MockServletContext();
this.context.put(StrutsStatics.SERVLET_CONTEXT, this.servletContext);
this.invocation = new MockActionInvocation();
this.invocation.setInvocationContext(this.context);
}
}
@@ -0,0 +1,66 @@
/*
* $Id$
*
* 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.json;
import java.util.Map;
import junit.framework.TestCase;
public class JSONUtilTest extends TestCase {
/**
* Asserts that a bean can be serialized to JSON and restored as a map
*/
public void testSerializeDeserialize() throws Exception {
Bean bean1 = new Bean();
bean1.setStringField("str");
bean1.setBooleanField(true);
bean1.setCharField('s');
bean1.setDoubleField(10.1);
bean1.setFloatField(1.5f);
bean1.setIntField(10);
bean1.setLongField(100);
bean1.setEnumField(AnEnum.ValueA);
bean1.setEnumBean(AnEnumBean.Two);
String json = JSONUtil.serialize(bean1);
Map result = (Map) JSONUtil.deserialize(json);
assertEquals("str", result.get("stringField"));
assertEquals(true, result.get("booleanField"));
assertEquals("s", result.get("charField")); // note: this is a
// String
assertEquals(10.1, result.get("doubleField"));
assertEquals(1.5, result.get("floatField")); // note: this is a
// Double
assertEquals(10L, result.get("intField")); // note: this is a
// Long
assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) result.get("enumField"))); // note:
// this
// is a
// String
assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) result.get("enumBean"))); // note:
// this
// is a
// String
}
}
@@ -0,0 +1,97 @@
/*
* $Id$
*
* 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.json;
public class OtherBean {
private int primitiveInt;
private Integer intField;
private Boolean booleanField;
private Character charField;
private Long longField;
private Float floatField;
private Double doubleField;
private Byte byteField;
public Boolean isBooleanField() {
return booleanField;
}
public void setBooleanField(Boolean booleanField) {
this.booleanField = booleanField;
}
public Byte getByteField() {
return byteField;
}
public void setByteField(Byte byteField) {
this.byteField = byteField;
}
public Character getCharField() {
return charField;
}
public void setCharField(Character charField) {
this.charField = charField;
}
public Double getDoubleField() {
return doubleField;
}
public void setDoubleField(Double doubleField) {
this.doubleField = doubleField;
}
public Float getFloatField() {
return floatField;
}
public void setFloatField(Float floatField) {
this.floatField = floatField;
}
public Integer getIntField() {
return intField;
}
public void setIntField(Integer intField) {
this.intField = intField;
}
public Long getLongField() {
return longField;
}
public void setLongField(Long longField) {
this.longField = longField;
}
public int getPrimitiveInt() {
return primitiveInt;
}
public void setPrimitiveInt(int primitiveInt) {
this.primitiveInt = primitiveInt;
}
}
@@ -0,0 +1,197 @@
/*
* $Id$
*
* 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.json;
import java.util.List;
import java.util.Map;
import org.apache.struts2.json.annotations.SMDMethod;
public class SMDActionTest1 {
private boolean addWasCalled;
@SuppressWarnings("unchecked")
private List listParam;
@SuppressWarnings("unchecked")
private Map mapParam;
private Bean beanParam;
private String stringParam;
private int intParam;
private boolean booleanParam;
private char charParam;
private long longParam;
private float floatParam;
private double doubleParam;
private short shortParam;
private Object objectParam;
private byte byteParam;
@SMDMethod
public void doSomethingPrimitives(String stringParam, int intParam, boolean booleanParam, char charParam,
long longParam, float floatParam, double doubleParam, short shortParam, byte byteParam) {
this.stringParam = stringParam;
this.intParam = intParam;
this.booleanParam = booleanParam;
this.charParam = charParam;
this.longParam = longParam;
this.floatParam = floatParam;
this.doubleParam = doubleParam;
this.byteParam = byteParam;
this.shortParam = shortParam;
}
@SuppressWarnings("unchecked")
@SMDMethod
public void doSomethingObjects(Bean beanParam, Map mapParam, List listParam) {
this.beanParam = beanParam;
this.mapParam = mapParam;
this.listParam = listParam;
}
@SMDMethod
public void add(int a, int b) {
this.addWasCalled = true;
}
@SMDMethod
public void doSomething() {
}
public void methodWithoutAnnotation() {
}
public boolean isAddWasCalled() {
return this.addWasCalled;
}
public void setAddWasCalled(boolean addWasCalled) {
this.addWasCalled = addWasCalled;
}
@SuppressWarnings("unchecked")
public List getListParam() {
return this.listParam;
}
@SuppressWarnings("unchecked")
public void setListParam(List listParam) {
this.listParam = listParam;
}
@SuppressWarnings("unchecked")
public Map getMapParam() {
return this.mapParam;
}
@SuppressWarnings("unchecked")
public void setMapParam(Map mapParam) {
this.mapParam = mapParam;
}
public Bean getBeanParam() {
return this.beanParam;
}
public void setBeanParam(Bean beanParam) {
this.beanParam = beanParam;
}
public String getStringParam() {
return this.stringParam;
}
public void setStringParam(String stringParam) {
this.stringParam = stringParam;
}
public int getIntParam() {
return this.intParam;
}
public void setIntParam(int intParam) {
this.intParam = intParam;
}
public boolean isBooleanParam() {
return this.booleanParam;
}
public void setBooleanParam(boolean booleanParam) {
this.booleanParam = booleanParam;
}
public char getCharParam() {
return this.charParam;
}
public void setCharParam(char charParam) {
this.charParam = charParam;
}
public long getLongParam() {
return this.longParam;
}
public void setLongParam(long longParam) {
this.longParam = longParam;
}
public float getFloatParam() {
return this.floatParam;
}
public void setFloatParam(float floatParam) {
this.floatParam = floatParam;
}
public double getDoubleParam() {
return this.doubleParam;
}
public void setDoubleParam(double doubleParam) {
this.doubleParam = doubleParam;
}
public Object getObjectParam() {
return this.objectParam;
}
public void setObjectParam(Object objectParam) {
this.objectParam = objectParam;
}
public byte getByteParam() {
return this.byteParam;
}
public void setByteParam(byte byteParam) {
this.byteParam = byteParam;
}
public short getShortParam() {
return this.shortParam;
}
public void setShortParam(short shortParam) {
this.shortParam = shortParam;
}
}
@@ -0,0 +1,58 @@
/*
* $Id$
*
* 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.json;
import org.apache.struts2.json.annotations.SMD;
import org.apache.struts2.json.annotations.SMDMethod;
import org.apache.struts2.json.annotations.SMDMethodParameter;
@SMD(objectName = "testaction", serviceType = "service", version = "10.0")
public class SMDActionTest2 {
private boolean doSomethingInvoked;
@SMDMethod
public void add(@SMDMethodParameter(name = "a")
int a, @SMDMethodParameter(name = "b")
int b) {
}
@SMDMethod(name = "doSomethingElse")
public void doSomething() {
doSomethingInvoked = true;
}
@SMDMethod
public Bean getBean() {
Bean bean = new Bean();
bean.setStringField("str");
bean.setBooleanField(true);
bean.setCharField('s');
bean.setDoubleField(10.1);
bean.setFloatField(1.5f);
bean.setIntField(10);
bean.setLongField(100);
return bean;
}
public boolean isDoSomethingInvoked() {
return doSomethingInvoked;
}
}
@@ -0,0 +1,195 @@
/*
* $Id$
*
* 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.json;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import junit.framework.TestCase;
import org.apache.struts2.json.annotations.SMDMethod;
/**
* Tests that the SMDMethod annotation can be found in interfaces when
* ignoreSMDMethodInterface is false
*/
public class SMDMethodInterfaceTest extends TestCase {
public interface InterfaceA {
String getA();
}
public interface InterfaceB {
@SMDMethod
String getB();
}
public interface InterfaceC {
String getC();
}
public interface InterfaceD {
String getD();
}
public interface InterfaceE {
String getE();
}
public static class ClassA extends ClassB implements InterfaceA {
private String a;
private String z;
public ClassA(String a, String b, String c, String d, String e, String x, String y, String z) {
super(b, c, d, e, x, y);
this.a = a;
this.z = z;
}
public String getA() {
return a;
}
@SMDMethod
public String getZ() {
return z;
}
}
public static class ClassB extends ClassC implements InterfaceB, InterfaceC {
private String b;
private String c;
private String y;
public ClassB(String b, String c, String d, String e, String x, String y) {
super(d, e, x);
this.b = b;
this.c = c;
this.y = y;
}
public String getC() {
return c;
}
public String getB() {
return b;
}
public String getY() {
return y;
}
}
public static class ClassC implements InterfaceD, InterfaceE {
private String d;
private String e;
private String x;
public ClassC(String d, String e, String x) {
this.d = d;
this.e = e;
this.x = x;
}
public String getD() {
return d;
}
public String getE() {
return e;
}
@SMDMethod
public String getX() {
return x;
}
}
/**
* Asserts that the SMDMethod annotation is only detected on the classes
* when ignoreSMDMethodInterfaces is true
*/
public void testBaseClassOnly() {
Method[] smdMethodsA = JSONUtil.listSMDMethods(ClassA.class, true);
assertEquals(2, smdMethodsA.length);
assertEquals("getZ", smdMethodsA[0].getName());
assertEquals("getX", smdMethodsA[1].getName());
Method[] smdMethodsB = JSONUtil.listSMDMethods(ClassB.class, true);
assertEquals(1, smdMethodsB.length);
assertEquals("getX", smdMethodsB[0].getName());
Method[] smdMethodsC = JSONUtil.listSMDMethods(ClassC.class, true);
assertEquals(1, smdMethodsC.length);
assertEquals("getX", smdMethodsC[0].getName());
}
/**
* Asserts that the SMDMethod annotation is also detected on the interfaces
* and superclasses when ignoreSMDMethodInterfaces is false
*/
public void testInterfaces() {
Method[] smdMethodsA = JSONUtil.listSMDMethods(ClassA.class, false);
assertEquals(3, smdMethodsA.length);
assertEquals("getZ", smdMethodsA[0].getName());
assertEquals("getX", smdMethodsA[1].getName());
assertEquals("getB", smdMethodsA[2].getName());
Method[] smdMethodsB = JSONUtil.listSMDMethods(ClassB.class, false);
assertEquals(2, smdMethodsB.length);
assertEquals("getX", smdMethodsB[0].getName());
assertEquals("getB", smdMethodsB[1].getName());
Method[] smdMethodsC = JSONUtil.listSMDMethods(ClassC.class, false);
assertEquals(1, smdMethodsC.length);
assertEquals("getX", smdMethodsC[0].getName());
}
/**
* This is the important case: detects the SMDMethod annotation on a proxy
*/
public void testWithProxy() {
InvocationHandler handler = new InvocationHandler() {
// dummy implementation
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
};
// proxy is proxy to an impl of ClassA
InterfaceA proxy = (InterfaceA) Proxy.newProxyInstance(ClassA.class.getClassLoader(), new Class[] {
InterfaceA.class, InterfaceB.class, InterfaceC.class }, handler);
// first, without the recursion
Method[] smdMethodsA = JSONUtil.listSMDMethods(proxy.getClass(), true);
assertEquals(0, smdMethodsA.length);
// now with the recursion
Method[] smdMethodsB = JSONUtil.listSMDMethods(proxy.getClass(), false);
assertEquals(1, smdMethodsB.length);
assertEquals("getB", smdMethodsB[0].getName());
}
}
@@ -0,0 +1,37 @@
/*
* $Id$
*
* 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.json;
public class SimpleValue {
private String value;
public SimpleValue() {
}
public String getValue() {
return value;
}
public void setValue(String v) {
this.value = v;
}
}
@@ -0,0 +1,188 @@
/*
* $Id$
*
* 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.json;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.struts2.json.annotations.JSON;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
/**
*/
@SuppressWarnings("unchecked")
public class TestAction extends ActionSupport {
private static final long serialVersionUID = -8891365561914451494L;
private List collection;
private List collection2;
private Map map;
private String foo;
private String result;
private String[] array;
private Bean[] beanArray;
private int[] intArray;
private List list;
private String bar;
private String nogetter;
private Date date2;
private Bean bean;
private Date date;
private String foo2 = null;
public Bean getBean() {
return this.bean;
}
public void setBean(Bean bean) {
this.bean = bean;
}
public List getCollection() {
return this.collection;
}
public void setCollection(List collection) {
this.collection = collection;
}
public List getCollection2() {
return this.collection2;
}
public void setCollection2(List collection2) {
this.collection2 = collection2;
}
public Map getMap() {
return this.map;
}
public void setMap(Map map) {
this.map = map;
}
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getResult() {
return this.result;
}
public void setResult(String result) {
this.result = result;
}
public String[] getArray() {
return this.array;
}
public void setArray(String[] array) {
this.array = array;
}
public List getList() {
return this.list;
}
public void setList(List list) {
this.list = list;
}
@Override
public String execute() throws Exception {
if (this.result == null) {
this.result = Action.SUCCESS;
}
return this.result;
}
public String doInput() throws Exception {
return INPUT;
}
public void setBar(String bar) {
this.bar = bar;
}
@JSON(serialize = false)
public String getBar() {
return this.bar;
}
public void setNogetter(String nogetter) {
this.nogetter = nogetter;
}
@JSON(serialize = false)
public int[] getIntArray() {
return this.intArray;
}
public void setIntArray(int[] intArray) {
this.intArray = intArray;
}
@JSON(serialize = false)
public Bean[] getBeanArray() {
return this.beanArray;
}
public void setBeanArray(Bean[] beanArray) {
this.beanArray = beanArray;
}
public Date getDate() {
return this.date;
}
public void setDate(Date date) {
this.date = date;
}
@JSON(format = "dd/MM/yy")
public Date getDate2() {
return this.date2;
}
@JSON(format = "dd/MM/yy")
public void setDate2(Date date2) {
this.date2 = date2;
}
@JSON(serialize = false)
public String getFoo2() {
return this.foo2;
}
@JSON(deserialize = false)
public void setFoo2(String foo2) {
this.foo2 = foo2;
}
}
@@ -0,0 +1,27 @@
/*
* $Id$
*
* 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.json;
public class TestAction2 {
public String getName() {
return "name";
}
}
@@ -0,0 +1,27 @@
/*
* $Id$
*
* 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.json;
public class TestAction3 extends TestAction2 {
public String getName2() {
return "name";
}
}
@@ -0,0 +1,35 @@
/*
* $Id$
*
* 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.json;
public class TestAction4 {
private Bean bean;
public Bean getBean() {
if (this.bean == null)
this.bean = new Bean();
return this.bean;
}
public void setBean(Bean bean) {
this.bean = bean;
}
}
@@ -0,0 +1,95 @@
/*
* $Id$
*
* 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.json;
import java.io.InputStream;
import java.net.URL;
import java.util.StringTokenizer;
/**
* Utility methods for test classes
*/
public class TestUtils {
/**
* normalizes a string so that strings generated on different platforms can
* be compared. any group of one or more space, tab, \r, and \n characters
* are converted to a single space character
*
* @param obj
* the object to be normalized. normalize will perform its
* operation on obj.toString().trim() ;
* @param appendSpace
* @return the normalized string
*/
public static String normalize(Object obj, boolean appendSpace) {
StringTokenizer st = new StringTokenizer(obj.toString().trim(), " \t\r\n");
StringBuffer buffer = new StringBuffer(128);
while (st.hasMoreTokens()) {
buffer.append(st.nextToken());
}
return buffer.toString();
}
public static String normalize(URL url) throws Exception {
return normalize(readContent(url), true);
}
/**
* Attempt to verify the contents of text against the contents of the URL
* specified. Performs a trim on both ends
*
* @param url
* the HTML snippet that we want to validate against
* @throws Exception
* if the validation failed
*/
public static boolean compare(URL url, String text) throws Exception {
/**
* compare the trimmed values of each buffer and make sure they're
* equivalent. however, let's make sure to normalize the strings first
* to account for line termination differences between platforms.
*/
String writerString = TestUtils.normalize(text, true);
String bufferString = TestUtils.normalize(readContent(url), true);
return bufferString.equals(writerString);
}
public static String readContent(URL url) throws Exception {
if (url == null)
throw new Exception("unable to verify a null URL");
StringBuffer buffer = new StringBuffer(128);
InputStream in = url.openStream();
byte[] buf = new byte[4096];
int nbytes;
while ((nbytes = in.read(buf)) > 0) {
buffer.append(new String(buf, 0, nbytes));
}
in.close();
return buffer.toString();
}
}
@@ -0,0 +1,181 @@
/*
* $Id$
*
* 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.json;
import java.util.List;
import java.util.Map;
public class WrapperClassBean {
private String stringField;
private Integer intField;
private int nullIntField;
private Boolean booleanField;
private boolean primitiveBooleanField1;
private boolean primitiveBooleanField2;
private boolean primitiveBooleanField3;
private Character charField;
private Long longField;
private Float floatField;
private Double doubleField;
private Object objectField;
private Byte byteField;
private List<SimpleValue> listField;
private List<Map<String, Long>> listMapField;
private Map<String, List<Long>> mapListField;
private Map<String, Long>[] arrayMapField;
public List<SimpleValue> getListField() {
return listField;
}
public void setListField(List<SimpleValue> listField) {
this.listField = listField;
}
public List<Map<String, Long>> getListMapField() {
return listMapField;
}
public void setListMapField(List<Map<String, Long>> listMapField) {
this.listMapField = listMapField;
}
public Map<String, List<Long>> getMapListField() {
return mapListField;
}
public void setMapListField(Map<String, List<Long>> mapListField) {
this.mapListField = mapListField;
}
public Map<String, Long>[] getArrayMapField() {
return arrayMapField;
}
public void setArrayMapField(Map<String, Long>[] arrayMapField) {
this.arrayMapField = arrayMapField;
}
public Boolean getBooleanField() {
return booleanField;
}
public void setBooleanField(Boolean booleanField) {
this.booleanField = booleanField;
}
public boolean isPrimitiveBooleanField1() {
return primitiveBooleanField1;
}
public void setPrimitiveBooleanField1(boolean primitiveBooleanField1) {
this.primitiveBooleanField1 = primitiveBooleanField1;
}
public boolean isPrimitiveBooleanField2() {
return primitiveBooleanField2;
}
public void setPrimitiveBooleanField2(boolean primitiveBooleanField2) {
this.primitiveBooleanField2 = primitiveBooleanField2;
}
public boolean isPrimitiveBooleanField3() {
return primitiveBooleanField3;
}
public void setPrimitiveBooleanField3(boolean primitiveBooleanField3) {
this.primitiveBooleanField3 = primitiveBooleanField3;
}
public Byte getByteField() {
return byteField;
}
public void setByteField(Byte byteField) {
this.byteField = byteField;
}
public Character getCharField() {
return charField;
}
public void setCharField(Character charField) {
this.charField = charField;
}
public Double getDoubleField() {
return doubleField;
}
public void setDoubleField(Double doubleField) {
this.doubleField = doubleField;
}
public Float getFloatField() {
return floatField;
}
public void setFloatField(Float floatField) {
this.floatField = floatField;
}
public Integer getIntField() {
return intField;
}
public void setIntField(Integer intField) {
this.intField = intField;
}
public int getNullIntField() {
return nullIntField;
}
public void setNullIntField(int nullIntField) {
this.nullIntField = nullIntField;
}
public Long getLongField() {
return longField;
}
public void setLongField(Long longField) {
this.longField = longField;
}
public Object getObjectField() {
return objectField;
}
public void setObjectField(Object objectField) {
this.objectField = objectField;
}
public String getStringField() {
return stringField;
}
public void setStringField(String stringField) {
this.stringField = stringField;
}
}
@@ -0,0 +1,3 @@
{
aField: NaN
}
@@ -0,0 +1,2 @@
{
aField: 10
@@ -0,0 +1,3 @@
aField: 10
}
@@ -0,0 +1 @@
bad 10
@@ -0,0 +1 @@
{ a: "aaaa }
@@ -0,0 +1,49 @@
{
"date": "1999-12-31T23:59:59",
"date2": "31\/12\/99",
"array": ["str0", "str1"],
"intArray": [1, 2],
"beanArray": [ {
"stringField": "bean1"
},
{
"stringField": "bean2"
}
],
"list": ["a", "a", "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t"],
"collection": ["b", 1, [10, 12]],
"collection2": [{
"booleanField": true,
"charField": "s",
"doubleField": 10.1,
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": {
"booleanField": false,
"charField": "\u0000",
"doubleField": 2.2,
"floatField": 1.1,
"intField": 0,
"longField":0,
"objectField":null,
"stringField":" "
},
"stringField": "str"
}],
"foo": "foo",
"map": {
"a": 1,
"c": [1.0, 2.0]
},
"result": null,
"bean": {
"booleanField": true,
"stringField" : "test",
"intField" : 10,
"charField": "s",
"doubleField": 10.1,
"byteField": 3
},
"foo2" : "ignoreme"
}
@@ -0,0 +1,3 @@
{
"list":[{"booleanField":false},{"booleanField":false},{"booleanField":false}]
}
@@ -0,0 +1,21 @@
{
"bigDecimal": null,
"bigInteger": null,
"booleanField": true,
"byteField": 0,
"charField": "s",
"doubleField": 10.1,
"enumBean": {
"propA":"A",
"propB":"B",
"_name":"One"
},
"enumField": {
"_name":"ValueA"
},
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": null,
"stringField": "str"
}
@@ -0,0 +1,15 @@
{
"bigDecimal": null,
"bigInteger": null,
"booleanField": true,
"byteField": 0,
"charField": "s",
"doubleField": 10.1,
"enumBean": "One",
"enumField": "ValueA",
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": null,
"stringField": "str"
}
@@ -0,0 +1,43 @@
/* {
"array": ["a", "a", "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t"],
"bean": null,
"collection": ["b", 1, [10, 12]],
"collection2": [{
"bigDecimal": null,
"bigInteger": null,
"booleanField": true,
"byteField": 0,
"charField": "s",
"doubleField": 10.1,
"enumBean": null,
"enumField": null,
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": {
"bigDecimal": null,
"bigInteger": null,
"booleanField": false,
"byteField": 0,
"charField": "\u0000",
"doubleField": 2.2,
"enumBean": "Three",
"enumField": "ValueC",
"floatField": 1.1,
"intField": 0,
"longField":0,
"objectField":null,
"stringField":" "
},
"stringField": "str"
}],
"date": "1999-12-31T11:59:59",
"date2": "31\/12\/99",
"foo": "foo",
"list": null,
"map": {
"a": 1,
"c": [1.0, 2.0]
},
"result":null
} */
@@ -0,0 +1,4 @@
{
"name":"name",
"name2":"name"
}
@@ -0,0 +1,8 @@
{
"booleanField": true,
"stringField" : "test",
"intField" : 10,
"charField": "s",
"doubleField": 10.1,
"byteField": 3
}
@@ -0,0 +1,20 @@
{
"bigDecimal": 111111.5,
"bigInteger": 111111,
"booleanField": true,
"primitiveBooleanField1": true,
"primitiveBooleanField2": false,
"primitiveBooleanField3": null,
"stringField" : "test\u000e\u000F",
"intField" : 10,
"nullIntField" : null,
"charField": "s",
"doubleField": 10.1,
"byteField": 3,
"objectField": { "empty": "to test issue 28 (http://code.google.com/p/jsonplugin/issues/detail?id=28) the value of objectField should be simply empty curly braces { } " },
"enumField": "ValueA",
"listField": [{"value":"1"},{"value":"2"}],
"listMapField": [{"id1":2073501,"id2":3}],
"mapListField": {"id1":[1,2,3],"id2":[4,3,2,1]},
"arrayMapField": [{"id1":2073501,"id2":3}]
}
@@ -0,0 +1,13 @@
{
"booleanField": "true",
"stringField" : "test",
"intField" : "10",
"charField": "s",
"doubleField": "10.1",
"byteField": "3",
"objectField": { "empty": "to test issue 28 (http://code.google.com/p/jsonplugin/issues/detail?id=28) the value of objectField should be simply empty curly braces { } " },
"listField": null,
"listMapField": null,
"mapListField": null,
"arrayMapField": null
}
@@ -0,0 +1,3 @@
{
"foo":"fooValue"
}
@@ -0,0 +1,43 @@
{
"array": ["a", "a", "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t"],
"bean": null,
"collection": ["b", 1, [10, 12]],
"collection2": [{
"bigDecimal": 111111.111111,
"bigInteger": 111111111111,
"booleanField": true,
"byteField": 0,
"charField": "s",
"doubleField": 10.1,
"enumBean": "One",
"enumField": "ValueA",
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": {
"bigDecimal": null,
"bigInteger": null,
"booleanField": false,
"byteField": 0,
"charField": "\u0000",
"doubleField": 2.2,
"enumBean": "Two",
"enumField": "ValueB",
"floatField": 1.1,
"intField": 0,
"longField":0,
"objectField":null,
"stringField":" "
},
"stringField": "str"
}],
"date": "1999-12-31T11:59:59",
"date2": "31\/12\/99",
"foo": "foo",
"list": null,
"map": {
"a": 1,
"c": [1.0, 2.0]
},
"result":null
}
@@ -0,0 +1,15 @@
exec({
"bigDecimal": null,
"bigInteger": null,
"booleanField": true,
"byteField": 0,
"charField": "s",
"doubleField": 10.1,
"enumBean": "One",
"enumField": "ValueA",
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": null,
"stringField": "str"
})
@@ -0,0 +1 @@
{"foo":"fool"}
@@ -0,0 +1 @@
{}&& {"foo":"fool"}
@@ -0,0 +1,41 @@
{
"methods":[
{
"name":"add",
"parameters":[
{"name":"p0"},
{"name":"p1"}
]
},
{
"name":"doSomething",
"parameters":[]
},
{
"name":"doSomethingObjects",
"parameters":[
{"name":"p0"},
{"name":"p1"},
{"name":"p2"}
]
},
{
"name":"doSomethingPrimitives",
"parameters":[
{"name":"p0"},
{"name":"p1"},
{"name":"p2"},
{"name":"p3"},
{"name":"p4"},
{"name":"p5"},
{"name":"p6"},
{"name":"p7"},
{"name":"p8"}
]
}
],
"objectName":null,
"serviceType":"JSON-RPC",
"serviceUrl":"http:\/\/sumeruri",
"version":".1"
}
@@ -0,0 +1,4 @@
{
"method": "getBean",
"id":15
}
@@ -0,0 +1,5 @@
{
"error":null,
"id":"2",
"result":null
}
@@ -0,0 +1,19 @@
{
"error":null,
"id":"15",
"result": {
"bigDecimal": null,
"bigInteger": null,
"booleanField": true,
"byteField": 0,
"charField": "s",
"doubleField": 10.1,
"enumBean": null,
"enumField": null,
"floatField": 1.5,
"intField": 10,
"longField": 100,
"objectField": null,
"stringField": "str"
}
}
@@ -0,0 +1,10 @@
{
"error": {
"code":100,
"message": "'method' is required for JSON RPC",
"name":null,
"stack":null
},
"id":"1",
"result":null
}
@@ -0,0 +1,5 @@
{
"params": [],
"method": "doSomething",
"id":1
}
@@ -0,0 +1,5 @@
{
"params": [],
"method": "doSomethingElse",
"id":1
}
@@ -0,0 +1,24 @@
{
"methods": [
{
"name": "add",
"parameters": [
{"name":"a"},
{"name":"b"}
]
},
{
"name": "doSomethingElse",
"parameters":[]
},
{
"name": "getBean",
"parameters":[]
}
],
"objectName": "testaction",
"serviceType": "service",
"serviceUrl":"http:\/\/sumeruri",
"version": "10.0"
}
@@ -0,0 +1,5 @@
{
"params": [0, 0],
"method": "add",
"id":1
}
@@ -0,0 +1,4 @@
{
"params": [0, 0],
"id":1
}
@@ -0,0 +1,5 @@
{
"params": [0, 0],
"method": "zzz",
"id":1
}
@@ -0,0 +1,5 @@
{
"params": ["string", 1, true, 'c', 2, 3.3, 4.4, 5, 6],
"method": "doSomethingPrimitives",
"id":"2"
}
@@ -0,0 +1,19 @@
{
"params": [
{
"booleanField": true,
"stringField" : "test",
"intField" : 10,
"charField": "s",
"doubleField": 10.1,
"byteField": 3
},
{
"a": 1,
"c": [1.0, 2.0]
},
["str0", "str1"]
],
"method": "doSomethingObjects",
"id":"2"
}
@@ -0,0 +1,16 @@
{
"addWasCalled":false,
"beanParam":null,
"booleanParam":false,
"byteParam":0,
"charParam":"\u0000",
"doubleParam":0.0,
"floatParam":0.0,
"intParam":0,
"listParam":null,
"longParam":0,
"mapParam":null,
"objectParam":null,
"shortParam":0,
"stringParam":null
}
@@ -0,0 +1,5 @@
{
"params": [0, 0],
"method": "methodWithoutAnnotation",
"id":1
}
@@ -0,0 +1 @@
{}