com.landawn.abacus.http.WebServiceServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of abacus-android Show documentation
Show all versions of abacus-android Show documentation
A general and simple library for Android
/*
* Copyright (C) 2015 HaiYang Li
*
* Licensed 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 com.landawn.abacus.http;
import static com.landawn.abacus.http.HTTP.jsonParser;
import static com.landawn.abacus.http.HTTP.kryoParser;
import static com.landawn.abacus.http.HTTP.xmlParser;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.landawn.abacus.exception.AbacusException;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.parser.JSONDeserializationConfig;
import com.landawn.abacus.parser.ParserFactory;
import com.landawn.abacus.parser.XMLDeserializationConfig;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.ClassUtil;
import com.landawn.abacus.util.WD;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.URLEncodedUtil;
/**
* It's a quick way to deploy json/xml web service by
* WebServiceServlet
*
*
* {@code
*
* Hello web service
* helloWebService
* helloWebService
* com.landawn.abacus.http.WebServiceServlet
*
* serviceImplClass
* com.landawn.ws.cxf.hello.HelloWebServiceImpl
*
*
*
*
*
* helloWebService
* /HelloWebService/*
*
* }
*
*
* @since 0.8
*
* @author Haiyang Li
*/
public class WebServiceServlet extends AbstractHttpServlet {
private static final long serialVersionUID = -7919235560292201779L;
protected static final String SERVICE_IMPL_CLASS = "serviceImplClass";
protected static final String SERVICE_FACTORY_CLASS = "serviceFactoryClass";
protected static final String SERVICE_FACTORY_METHOD = "serviceFactoryMethod";
protected static final String URL_MAPPER = "urlMapper";
protected static final String HTTP_METHOD_MAPPER = "httpMethodMapper";
protected static final String ENCRYPTION_USER_NAME = "encryptionUserName";
protected static final String ENCRYPTION_PASSWORD = "encryptionPassword";
protected static final String ENCRYPTION_MESSAGE = "encryptionMessage";
private final Map urlMethodMap = new HashMap<>();
private final Map> methodParameterClassMap = new HashMap<>();
private final Map parameterMethodMap = new HashMap<>();
private final Map eleNameMethodMap = new HashMap<>();
private final Map> eleNameParameterClassMap = new HashMap<>();
private final Map> methodHttpMethodMap = new HashMap<>();
private final Map methodParameterNamesMap = new HashMap<>();
private final Map>> methodParameterNamingTypesMap = new HashMap<>();
private final Map methodJSONDeserializationConfigMap = new HashMap<>();
private final Map methodXMLDeserializationConfigMap = new HashMap<>();
static {
LoggerFactory.getLogger(WebServiceServlet.class).warn(IOUtil.JAVA_VERSION);
}
private Object serviceImpl;
private String encryptionUserName;
private byte[] encryptionPassword;
private MessageEncryption encryptionMessage;
private Logger serviceRRLogger;
private Logger serviceImplLogger;
@Override
public void init() throws ServletException {
super.init();
}
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
String serviceImplClassParameter = getInitParameter(config, SERVICE_IMPL_CLASS);
String serviceFatoryClassParameter = getInitParameter(config, SERVICE_FACTORY_CLASS);
String serviceFatoryMethodParameter = getInitParameter(config, SERVICE_FACTORY_METHOD);
encryptionUserName = getInitParameter(config, ENCRYPTION_USER_NAME);
encryptionPassword = N.isNullOrEmpty(getInitParameter(config, ENCRYPTION_PASSWORD)) ? N.EMPTY_BYTE_ARRAY
: getInitParameter(config, ENCRYPTION_PASSWORD).getBytes();
encryptionMessage = N.isNullOrEmpty(getInitParameter(config, ENCRYPTION_MESSAGE)) ? MessageEncryption.NONE
: MessageEncryption.valueOf(getInitParameter(config, ENCRYPTION_MESSAGE).toUpperCase());
if (N.isNullOrEmpty(serviceImplClassParameter) && N.isNullOrEmpty(serviceFatoryClassParameter)) {
throw new AbacusException("serviceImplClass and serviceFactoryClass can't be null at the same time.");
}
Class> serviceImplClass = null;
Class> serviceFactoryClass = null;
if (N.isNullOrEmpty(serviceImplClassParameter)) {
serviceFactoryClass = ClassUtil.forClass(serviceFatoryClassParameter);
serviceImplClass = ClassUtil.getDeclaredMethod(serviceFactoryClass, serviceFatoryMethodParameter).getReturnType();
} else {
serviceImplClass = ClassUtil.forClass(serviceImplClassParameter);
}
if (N.isNullOrEmpty(serviceFatoryMethodParameter)) {
serviceImpl = N.newInstance(serviceImplClass);
} else {
if (N.isNullOrEmpty(serviceFatoryClassParameter)) {
serviceImpl = ClassUtil.invokeMethod(ClassUtil.getDeclaredMethod(serviceImplClass, serviceFatoryMethodParameter));
} else {
if (serviceFactoryClass == null) {
serviceFactoryClass = ClassUtil.forClass(serviceFatoryClassParameter);
}
serviceImpl = ClassUtil.invokeMethod(ClassUtil.getDeclaredMethod(serviceFactoryClass, serviceFatoryMethodParameter));
}
}
final Set> superClasses = new LinkedHashSet<>();
if (!(serviceImplClass.getSuperclass() == null || Object.class.equals(serviceImplClass))) {
superClasses.add(serviceImplClass.getSuperclass());
}
if (N.notNullOrEmpty(serviceImplClass.getInterfaces())) {
superClasses.addAll(Arrays.asList(serviceImplClass.getInterfaces()));
}
final Set declaredMethods = N.asLinkedHashSet(serviceImplClass.getDeclaredMethods());
for (Class> superClass : superClasses) {
declaredMethods.addAll(Arrays.asList(superClass.getDeclaredMethods()));
}
for (Method method : declaredMethods) {
if (!Modifier.isPublic(method.getModifiers())) {
continue;
}
final String methodName = method.getName();
final Class>[] parameterTypes = method.getParameterTypes();
final int parameterCount = parameterTypes.length;
boolean noFieldAnnotation = true;
Annotation[][] parameterAnnotationArrays = method.getParameterAnnotations();
for (int i = 0; i < parameterCount; i++) {
for (Annotation parameterAnnotation : parameterAnnotationArrays[i]) {
if (parameterAnnotation.annotationType() == Field.class) {
noFieldAnnotation = false;
break;
}
}
}
if (noFieldAnnotation) {
for (Class> superClass : superClasses) {
if (noFieldAnnotation == false) {
break;
}
for (Method dm : superClass.getDeclaredMethods()) {
if (dm.getName().equals(methodName) && N.equals(dm.getParameterTypes(), method.getParameterTypes())) {
parameterAnnotationArrays = dm.getParameterAnnotations();
for (int i = 0; i < parameterCount; i++) {
for (Annotation parameterAnnotation : parameterAnnotationArrays[i]) {
if (parameterAnnotation.annotationType() == Field.class) {
noFieldAnnotation = false;
break;
}
}
}
break;
}
}
}
}
urlMethodMap.put(methodName, method);
methodHttpMethodMap.put(methodName, N.asSet(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE));
if (parameterCount == 0) {
// ignore.
} else if (parameterCount == 1 && noFieldAnnotation) {
final Class> parameterClass = method.getParameterTypes()[0];
methodParameterClassMap.put(methodName, parameterClass);
parameterMethodMap.put(ClassUtil.getSimpleClassName(parameterClass), method);
if (N.isEntity(parameterClass)) {
Object tempInstance = N.newInstance(parameterClass);
String st = xmlParser.serialize(tempInstance);
int index = st.indexOf('>');
if (index > 1) {
String eleName = st.substring(1, index);
if (N.notNullOrEmpty(eleName)) {
eleNameMethodMap.put(eleName, method);
eleNameParameterClassMap.put(eleName, parameterClass);
}
}
}
ParserFactory.registerXMLBindingClass(parameterClass);
if (!void.class.equals(method.getReturnType())) {
ParserFactory.registerXMLBindingClass(method.getReturnType());
}
} else {
methodParameterClassMap.put(methodName, Map.class);
final String[] parameterNames = new String[parameterCount];
final Map> parameterNamingTypes = new HashMap<>();
for (int i = 0; i < parameterCount; i++) {
for (Annotation parameterAnnotation : parameterAnnotationArrays[i]) {
if (parameterAnnotation.annotationType() == Field.class) {
parameterNames[i] = ((Field) parameterAnnotation).value();
parameterNamingTypes.put(parameterNames[i], N.typeOf(parameterTypes[i]));
break;
}
}
}
methodParameterNamesMap.put(methodName, parameterNames);
methodParameterNamingTypesMap.put(methodName, parameterNamingTypes);
JSONDeserializationConfig jdc = new JSONDeserializationConfig();
jdc.setPropTypes(parameterNamingTypes);
methodJSONDeserializationConfigMap.put(methodName, jdc);
XMLDeserializationConfig xdc = new XMLDeserializationConfig();
xdc.setPropTypes(parameterNamingTypes);
methodXMLDeserializationConfigMap.put(methodName, xdc);
}
}
String urlMethodMapperParameter = getInitParameter(config, URL_MAPPER);
if (N.notNullOrEmpty(urlMethodMapperParameter)) {
urlMethodMapperParameter = urlMethodMapperParameter.trim();
String[] sts = urlMethodMapperParameter.split(WD.SEMICOLON);
for (String st : sts) {
String[] tmp = st.split(WD.EQUAL);
urlMethodMap.put(tmp[1].trim(), findDeclaredMethodByName(serviceImplClass, tmp[0].trim()));
}
}
String httpMethodMapperParameter = getInitParameter(config, HTTP_METHOD_MAPPER);
if (N.notNullOrEmpty(httpMethodMapperParameter)) {
httpMethodMapperParameter = httpMethodMapperParameter.trim();
String[] sts = httpMethodMapperParameter.split(WD.SEMICOLON);
for (String st : sts) {
String[] tmp = st.split(WD.EQUAL);
String[] httpMethods = tmp[1].split(WD.COMMA);
if (N.notNullOrEmpty(httpMethods) && "ALL".equalsIgnoreCase(httpMethods[0].trim())) {
continue;
}
Set set = new HashSet<>();
for (String e : httpMethods) {
set.add(HttpMethod.valueOf(e.trim().toUpperCase()));
}
methodHttpMethodMap.put(tmp[0].trim(), set);
}
}
// for (String key : new ArrayList<>(urlMethodMap.keySet())) {
// urlMethodMap.put(key.toLowerCase(), urlMethodMap.get(key));
// urlMethodMap.put(key.toUpperCase(), urlMethodMap.get(key));
// }
//
// for (String key : new ArrayList<>(methodParameterClassMap.keySet())) {
// methodParameterClassMap.put(key.toLowerCase(), methodParameterClassMap.get(key));
// methodParameterClassMap.put(key.toUpperCase(), methodParameterClassMap.get(key));
// }
//
// for (String key : new ArrayList<>(parameterMethodMap.keySet())) {
// parameterMethodMap.put(key.toLowerCase(), parameterMethodMap.get(key));
// parameterMethodMap.put(key.toUpperCase(), parameterMethodMap.get(key));
// }
//
// for (String key : new ArrayList<>(eleNameMethodMap.keySet())) {
// eleNameMethodMap.put(key.toLowerCase(), eleNameMethodMap.get(key));
// eleNameMethodMap.put(key.toUpperCase(), eleNameMethodMap.get(key));
// }
//
// for (String key : new ArrayList<>(eleNameParameterClassMap.keySet())) {
// eleNameParameterClassMap.put(key.toLowerCase(), eleNameParameterClassMap.get(key));
// eleNameParameterClassMap.put(key.toUpperCase(), eleNameParameterClassMap.get(key));
// }
//
// for (String key : new ArrayList<>(methodHttpMethodMap.keySet())) {
// methodHttpMethodMap.put(key.toLowerCase(), methodHttpMethodMap.get(key));
// methodHttpMethodMap.put(key.toUpperCase(), methodHttpMethodMap.get(key));
// }
//
// for (String key : new ArrayList<>(methodParameterNamesMap.keySet())) {
// methodParameterNamesMap.put(key.toLowerCase(), methodParameterNamesMap.get(key));
// methodParameterNamesMap.put(key.toUpperCase(), methodParameterNamesMap.get(key));
// }
//
// for (String key : new ArrayList<>(methodParameterNamingTypesMap.keySet())) {
// methodParameterNamingTypesMap.put(key.toLowerCase(), methodParameterNamingTypesMap.get(key));
// methodParameterNamingTypesMap.put(key.toUpperCase(), methodParameterNamingTypesMap.get(key));
// }
//
// for (String key : new ArrayList<>(methodJSONDeserializationConfigMap.keySet())) {
// methodJSONDeserializationConfigMap.put(key.toLowerCase(), methodJSONDeserializationConfigMap.get(key));
// methodJSONDeserializationConfigMap.put(key.toUpperCase(), methodJSONDeserializationConfigMap.get(key));
// }
//
// for (String key : new ArrayList<>(methodXMLDeserializationConfigMap.keySet())) {
// methodXMLDeserializationConfigMap.put(key.toLowerCase(), methodXMLDeserializationConfigMap.get(key));
// methodXMLDeserializationConfigMap.put(key.toUpperCase(), methodXMLDeserializationConfigMap.get(key));
// }
serviceRRLogger = LoggerFactory.getLogger(N.isNullOrEmpty(serviceImplClass.getInterfaces()) ? serviceImplClass : serviceImplClass.getInterfaces()[0]);
serviceImplLogger = LoggerFactory.getLogger(serviceImplClass);
}
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
execute(request, response);
}
@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
execute(request, response);
}
@Override
protected void doPut(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
execute(request, response);
}
@Override
protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
execute(request, response);
}
protected void execute(final HttpServletRequest request, final HttpServletResponse response) {
preExecute(request, response);
final String url = request.getRequestURL().toString();
final HttpMethod httpMethodName = HttpMethod.valueOf(request.getMethod());
final boolean isGetOrDelete = httpMethodName.equals(HttpMethod.GET) || httpMethodName.equals(HttpMethod.DELETE);
InputStream is = null;
Method method = null;
Object parameter = null;
Class> parameterClass = null;
Object result = null;
try {
int index = url.lastIndexOf('/');
if ((index > 0) && (index < url.length())) {
method = urlMethodMap.get(url.substring(index + 1));
}
if (isGetOrDelete) {
if (method == null) {
String msg = "The target web service method can't be identified by url";
serviceImplLogger.error(msg);
throw new AbacusException(msg);
}
parameterClass = methodParameterClassMap.get(method.getName());
if (parameterClass != null) {
if (Map.class.isAssignableFrom(parameterClass) && methodParameterNamesMap.containsKey(method.getName())) {
parameter = jsonParser.deserialize(parameterClass, jsonParser.serialize(URLEncodedUtil.decode(request.getQueryString())),
methodJSONDeserializationConfigMap.get(method.getName()));
} else {
Map parameterMap = request.getParameterMap();
parameter = URLEncodedUtil.parameters2Entity(parameterClass, parameterMap);
}
}
}
final ContentFormat contentFormat = getContentFormat(request);
if (N.notNullOrEmpty(encryptionUserName) && N.notNullOrEmpty(encryptionPassword)) {
if (((SecurityDTO) parameter).decrypt(encryptionUserName, encryptionPassword, encryptionMessage) == false) {
result = N.newInstance(method.getReturnType());
Method respCodeSetMethod = ClassUtil.getPropSetMethod(result.getClass(), "setResponseCode");
if (respCodeSetMethod == null) {
respCodeSetMethod = ClassUtil.getPropSetMethod(result.getClass(), "setRespCode");
}
if (respCodeSetMethod != null) {
ClassUtil.setPropValue(result, respCodeSetMethod, "1010");
}
Method respMessageSetMethod = ClassUtil.getPropSetMethod(result.getClass(), "setResponseMessage");
if (respMessageSetMethod == null) {
respMessageSetMethod = ClassUtil.getPropSetMethod(result.getClass(), "setRespMessage");
}
if (respMessageSetMethod != null) {
ClassUtil.setPropValue(result, respMessageSetMethod, "Security issue: Invalid request.");
}
postExecute(response, result, method, parameter, contentFormat);
setResponse(response, result, contentFormat);
return;
}
}
switch (contentFormat) {
case JSON:
case JSON_LZ4:
case JSON_SNAPPY:
case JSON_GZIP:
if (method == null) {
String msg = "Unsupported opearation";
serviceImplLogger.error(msg);
throw new AbacusException(msg);
} else {
checkHttpMethod(method, httpMethodName);
if (parameter == null) {
parameterClass = methodParameterClassMap.get(method.getName());
if (parameterClass == null) {
result = ClassUtil.invokeMethod(serviceImpl, method);
} else {
is = getInputStream(request, contentFormat);
Type