package com.mossle.simulator.restful;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import com.mossle.core.mapper.JsonMapper;
import com.mossle.core.spring.ApplicationContextHelper;
import com.mossle.core.spring.DateConverter;
import com.mossle.core.util.IoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
public class RestFilter implements Filter {
private static Logger logger = LoggerFactory.getLogger(RestFilter.class);
private JsonMapper jsonMapper = new JsonMapper();
private DateConverter dateConverter = new DateConverter();
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
ApplicationContext ctx = ApplicationContextHelper
.getApplicationContext();
Map<String, Object> map = ctx.getBeansWithAnnotation(Path.class);
for (Object object : map.values()) {
Class clz = object.getClass();
for (Method method : clz.getDeclaredMethods()) {
if (this.matches(request, method)) {
Map<String, String> parameters = parseBody(request
.getInputStream());
this.invokeMethod(request, response, object, method,
parameters);
return;
}
}
}
filterChain.doFilter(req, res);
}
public Map<String, String> parseBody(InputStream inputStream)
throws IOException {
String body = IoUtils.readString(inputStream);
// body = URLDecoder.decode(body, "UTF-8");
logger.debug("body : {}", body);
Map<String, String> parameters = new HashMap<String, String>();
for (String text : body.split("&")) {
logger.debug("text : {}", text);
if (text.indexOf("=") == -1) {
continue;
}
int index = text.indexOf("=");
String key = text.substring(0, index);
String value = text.substring(index + 1);
// if (value != null) {
// value = URLDecoder.decode(value, "UTF-8");
// }
parameters.put(key, value);
}
return parameters;
}
public Map<String, String> getMetaData(Annotation[] annotations) {
Map<String, String> metaData = new HashMap<String, String>();
for (Annotation annotation : annotations) {
String name = null;
String type = null;
String value = null;
String defaultValue = null;
if (annotation instanceof PathParam) {
name = ((PathParam) annotation).value();
type = "path";
metaData.put("name", name);
metaData.put("type", type);
// value = this.getPathParam(request, name, method);
} else if (annotation instanceof QueryParam) {
name = ((QueryParam) annotation).value();
type = "query";
metaData.put("name", name);
metaData.put("type", type);
// value = request.getParameter(name);
} else if (annotation instanceof FormParam) {
name = ((FormParam) annotation).value();
type = "form";
metaData.put("name", name);
metaData.put("type", type);
// value = parameters.get(name);
} else if (annotation instanceof HeaderParam) {
name = ((HeaderParam) annotation).value();
type = "header";
metaData.put("name", name);
metaData.put("type", type);
// value = request.getHeader(name);
} else if (annotation instanceof DefaultValue) {
defaultValue = ((DefaultValue) annotation).value();
metaData.put("defaultValue", defaultValue);
}
}
return metaData;
}
public void invokeMethod(HttpServletRequest request,
HttpServletResponse response, Object object, Method method,
Map<String, String> parameters) {
try {
List arguments = new ArrayList();
Annotation[][] annotationArray = method.getParameterAnnotations();
Class[] parameterTypeArray = method.getParameterTypes();
for (int i = 0; i < annotationArray.length; i++) {
if (parameterTypeArray[i]
.isAssignableFrom(HttpServletRequest.class)) {
arguments.add(request);
continue;
}
if (parameterTypeArray[i]
.isAssignableFrom(HttpServletResponse.class)) {
arguments.add(response);
continue;
}
Annotation[] annotations = annotationArray[i];
Map<String, String> metaData = this.getMetaData(annotations);
String name = metaData.get("name");
String value = null;
if ("path".equals(metaData.get("type"))) {
value = this.getPathParam(request, name, method);
} else if ("query".equals(metaData.get("type"))) {
value = request.getParameter(name);
} else if ("form".equals(metaData.get("type"))) {
value = parameters.get(name);
} else if ("header".equals(metaData.get("type"))) {
value = request.getHeader(name);
}
if ((value == null) && metaData.containsKey("defaultValue")) {
value = metaData.get("defaultValue");
}
if (value != null) {
if (parameterTypeArray[i] == String.class) {
arguments.add(value);
} else if ((parameterTypeArray[i] == Boolean.class)
|| (parameterTypeArray[i] == boolean.class)) {
if (value.length() == 0) {
arguments.add(false);
} else {
arguments.add(Boolean.valueOf(value));
}
} else if ((parameterTypeArray[i] == Long.class)
|| (parameterTypeArray[i] == long.class)) {
if (value.length() == 0) {
arguments.add(0L);
} else {
arguments.add(Long.parseLong(value));
}
} else if ((parameterTypeArray[i] == Integer.class)
|| (parameterTypeArray[i] == int.class)) {
if (value.length() == 0) {
arguments.add(0);
} else {
arguments.add(Integer.parseInt(value));
}
} else if (parameterTypeArray[i] == Date.class) {
if (value.length() == 0) {
arguments.add(null);
} else {
arguments.add(dateConverter.convert(value));
}
} else {
throw new IllegalArgumentException("unsupport type : "
+ parameterTypeArray[i]);
}
} else {
if (parameterTypeArray[i] == boolean.class) {
arguments.add(false);
} else if (parameterTypeArray[i] == char.class) {
arguments.add((char) 0);
} else if (parameterTypeArray[i] == byte.class) {
arguments.add((byte) 0);
} else if (parameterTypeArray[i] == short.class) {
arguments.add((short) 0);
} else if (parameterTypeArray[i] == int.class) {
arguments.add(0);
} else if (parameterTypeArray[i] == long.class) {
arguments.add(0L);
} else if (parameterTypeArray[i] == float.class) {
arguments.add(0F);
} else if (parameterTypeArray[i] == double.class) {
arguments.add(0D);
} else if (parameterTypeArray[i] == String.class) {
arguments.add(null);
} else {
String message = "cannot process method argument, index is : "
+ i + ", name : " + name + ", value is null";
throw new IllegalArgumentException(message);
}
}
}
logger.debug("{}, {}, {}", object, method, arguments);
Object result = method.invoke(object, arguments.toArray());
if (result == null) {
logger.debug("return null");
} else if (result instanceof String) {
response.setContentType(MediaType.TEXT_HTML);
response.getOutputStream().write(
((String) result).getBytes("UTF-8"));
} else if (result instanceof InputStream) {
Produces produces = method.getAnnotation(Produces.class);
String contentType = MediaType.APPLICATION_OCTET_STREAM;
if (produces != null) {
String[] values = produces.value();
if ((values != null) && (values.length > 0)) {
contentType = values[0];
}
}
response.setContentType(contentType);
IoUtils.copyStream((InputStream) result,
response.getOutputStream());
} else {
response.setContentType(MediaType.APPLICATION_JSON);
response.getOutputStream().write(
jsonMapper.toJson(result).getBytes("UTF-8"));
}
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
}
public boolean matches(HttpServletRequest request, Method method) {
if ("GET".equalsIgnoreCase(request.getMethod())
&& (method.getAnnotation(GET.class) == null)) {
return false;
}
if ("POST".equalsIgnoreCase(request.getMethod())
&& (method.getAnnotation(POST.class) == null)) {
return false;
}
if ("PUT".equalsIgnoreCase(request.getMethod())
&& (method.getAnnotation(PUT.class) == null)) {
return false;
}
if ("DELETE".equalsIgnoreCase(request.getMethod())
&& (method.getAnnotation(DELETE.class) == null)) {
return false;
}
logger.debug("{}", request.getRequestURI());
String prefix = request.getContextPath() + "/rs";
String requestPath = request.getRequestURI().substring(prefix.length());
logger.debug("{}", requestPath);
String path = "/";
if (method.getDeclaringClass().getAnnotation(Path.class) != null) {
path += method.getDeclaringClass().getAnnotation(Path.class)
.value();
}
if (method.getAnnotation(Path.class) != null) {
path += ("/" + method.getAnnotation(Path.class).value());
}
logger.debug("{}, {}", method, path);
String[] src = requestPath.split("/");
String[] dest = path.split("/");
if (src.length != dest.length) {
return false;
}
for (int i = 0; i < src.length; i++) {
if (!this.matchPath(src[i], dest[i])) {
return false;
}
}
return true;
}
public boolean matchPath(String src, String dest) {
if (src.equals(dest)) {
return true;
}
if (dest.startsWith("{") && dest.endsWith("}")) {
return true;
}
return false;
}
public String getPathParam(HttpServletRequest request, String name,
Method method) {
String prefix = request.getContextPath() + "/rs";
String requestPath = request.getRequestURI().substring(prefix.length());
String path = "/";
if (method.getDeclaringClass().getAnnotation(Path.class) != null) {
path += method.getDeclaringClass().getAnnotation(Path.class)
.value();
}
if (method.getAnnotation(Path.class) != null) {
path += ("/" + method.getAnnotation(Path.class).value());
}
String[] src = requestPath.split("/");
String[] dest = path.split("/");
for (int i = 0; i < dest.length; i++) {
if (dest[i].equals("{" + name + "}")) {
return src[i];
}
}
return null;
}
}