/**
*
*/
package com.dianping.pigeon.console.servlet.json;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.SerializationException;
import org.apache.commons.lang.StringUtils;
import com.dianping.pigeon.config.ConfigManager;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.console.Utils;
import com.dianping.pigeon.console.domain.ResponseError;
import com.dianping.pigeon.console.servlet.ServiceServlet;
import com.dianping.pigeon.monitor.MonitorConstants;
import com.dianping.pigeon.remoting.common.codec.json.JacksonSerializer;
import com.dianping.pigeon.remoting.common.exception.SecurityException;
import com.dianping.pigeon.remoting.common.util.ContextUtils;
import com.dianping.pigeon.remoting.invoker.config.spring.ReferenceBean;
import com.dianping.pigeon.remoting.provider.config.ProviderConfig;
import com.dianping.pigeon.remoting.provider.config.ServerConfig;
import com.dianping.pigeon.remoting.provider.process.filter.SecurityFilter;
/**
* @author sean.wang
* @since Jul 22, 2012
*/
public class InvokeJsonServlet extends ServiceServlet {
public InvokeJsonServlet(ServerConfig serverConfig, int port) {
super(serverConfig, port);
}
private static ConfigManager configManager = ConfigManagerLoader.getConfigManager();
JacksonSerializer jacksonSerializer = new JacksonSerializer();
private static final long serialVersionUID = -4886018160888366456L;
private static Map<String, Class<?>> builtInMap = new HashMap<String, Class<?>>();
private static final String KEY_TOKEN_ENABLE = "pigeon.provider.token.enable";
private static final String KEY_CONSOLE_INVOKE_ENABLE = "pigeon.console.invoke.enable";
private static final String KEY_CONSOLE_INVOKE_LOG = "pigeon.console.invoke.log";
private static final String KEY_CONSOLE_TOKEN_HEADER = "pigeon.console.token.header";
static {
builtInMap.put("int", Integer.TYPE);
builtInMap.put("long", Long.TYPE);
builtInMap.put("double", Double.TYPE);
builtInMap.put("float", Float.TYPE);
builtInMap.put("boolean", Boolean.TYPE);
builtInMap.put("char", Character.TYPE);
builtInMap.put("byte", Byte.TYPE);
builtInMap.put("void", Void.TYPE);
builtInMap.put("short", Short.TYPE);
configManager.getBooleanValue(KEY_CONSOLE_INVOKE_LOG, true);
configManager.getBooleanValue(KEY_CONSOLE_INVOKE_ENABLE, true);
configManager.getBooleanValue(KEY_CONSOLE_TOKEN_HEADER, false);
}
public boolean needValidate(HttpServletRequest request) {
boolean needValidate = false;
String validate = request.getParameter("validate");
if (("true".equalsIgnoreCase(validate)) && isValidate) {
needValidate = true;
}
return needValidate;
}
public String getContentType() {
return "application/json; charset=UTF-8";
}
protected void generateView(HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
if (!configManager.getBooleanValue(KEY_CONSOLE_INVOKE_ENABLE, true)) {
response.getWriter().write("{\"msg\":\"pigeon console invocation is disabled!\"}");
return;
}
boolean direct = directInvoke;
if (StringUtils.isNotBlank(request.getParameter("direct"))) {
direct = request.getParameter("direct").equals("true") ? true : false;
}
String timeoutKey = request.getParameter("timeout");
int timeout = timeoutKey == null ? 15 * 1000 : Integer.parseInt(timeoutKey);
String methodName = request.getParameter("method");
String[] types = request.getParameterValues("parameterTypes");
if (types == null) { // for jquery ajax
types = request.getParameterValues("parameterTypes[]");
}
if (types != null && "".equals(types[0])) {
types = null;
}
String[] values = request.getParameterValues("parameters");
if (values == null) { // for jquery ajax
values = request.getParameterValues("parameters[]");
}
if (values != null) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null && values[i].equals("null")) {
values[i] = null;
}
}
}
boolean needValidate =needValidate(request);
String token = request.getParameter("token");
String serviceName = request.getParameter("url");
String expectToken = null;
String fromIp = Utils.getIpAddr(request);
if (configManager.getBooleanValue(KEY_TOKEN_ENABLE, false)) {
// expectToken = serviceToken;
needValidate = true;
String timestamp = null;
String app = null;
String authToken = null;
if (configManager.getBooleanValue(KEY_CONSOLE_TOKEN_HEADER, false)) {
timestamp = request.getHeader("Timestamp");
String auth = request.getHeader("Authorization");
if (StringUtils.isNotBlank(auth) && auth.startsWith("pigeon=")) {
int idx = auth.indexOf(":");
app = auth.substring(7, idx);
authToken = auth.substring(idx + 1);
}
} else {
timestamp = request.getParameter("timestamp");
app = request.getParameter("app");
ContextUtils.putRequestContext("RequestApp", app);
authToken = request.getParameter("token");
}
try {
SecurityFilter.authenticateRequestToken(app, fromIp, timestamp, "", authToken, serviceName, methodName);
} catch (SecurityException e) {
writeResponse(response, new ResponseError(e.getMessage(), null, 403));
return;
}
} else if (needValidate) {
expectToken = ServiceServlet.getToken();
if (token == null || !token.equals(expectToken)) {
writeResponse(response, new ResponseError("Invalid authentication code", null, 403));
return;
}
}
if (configManager.getBooleanValue(KEY_CONSOLE_INVOKE_LOG, true)) {
logger.info("pigeon console: invoking '" + serviceName + "@" + methodName + "', from " + fromIp);
}
Object result = null;
if (direct) {
try {
result = directInvoke(serviceName, methodName, types, values);
} catch (InvocationTargetException e) {
logger.error("console invoke error", e);
if (e.getTargetException() != null) {
result = new ResponseError("Error with service invocation", e.getTargetException(), 500);
} else {
result = new ResponseError(e.toString(), null, 400);
}
} catch (Throwable e) {
logger.error("console invoke error", e);
result = new ResponseError(e.toString(), null, 400);
}
} else {
ContextUtils.putRequestContext("RequestIp", fromIp);
try {
result = proxyInvoke(serviceName, methodName, types, values, timeout);
} catch (InvocationTargetException e) {
logger.error("console invoke error", e);
if (e.getTargetException() != null) {
result = new ResponseError("Error with service invocation", e.getTargetException(), 500);
} else {
result = new ResponseError(e.toString(), null, 400);
}
} catch (Throwable e) {
result = new ResponseError(e.toString(), null, 400);
}
}
String currentMessageId = (String) ContextUtils.getLocalContext(MonitorConstants.CURRENT_MSG_ID);
if (currentMessageId != null) {
Map localContext = ContextUtils.getLocalContext();
if (localContext != null && localContext.containsKey(MonitorConstants.CURRENT_MSG_ID)) {
response.addHeader(MonitorConstants.CURRENT_MSG_ID, (String) localContext.remove(MonitorConstants.CURRENT_MSG_ID));
}
}
writeResponse(response, result);
}
private void writeResponse(HttpServletResponse response, Object result) throws IOException {
if (result == null) {
return;
}
String json = jacksonSerializer.serializeObject(result);
response.setContentType(getContentType());
if (result instanceof ResponseError) {
response.setStatus(((ResponseError) result).getStatus());
}
response.getWriter().write(json);
}
private Object proxyInvoke(String serviceName, String methodName, String[] types, String[] values, int timeout)
throws Exception {
ProviderConfig<?> service = getServiceProviders().get(serviceName);
if (service == null) {
return null;
}
// if (!registered) {
// ClientManager.getInstance().registerServiceInvokers(serviceName,
// configManager.getGroup(),
// "console:" + serverConfig.getActualPort());
// }
ReferenceBean bean = new ReferenceBean();
bean.setUrl(serviceName);
bean.setInterfaceName(service.getServiceInterface().getName());
bean.setVip("console:" + service.getServerConfig().getActualPort());
bean.setTimeout(timeout);
bean.init();
Object proxy = bean.getObject();
return invoke(serviceName, methodName, types, values, proxy);
}
private Object directInvoke(String serviceName, String methodName, String[] types, String[] values)
throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
ProviderConfig<?> providerConfig = getServiceProviders().get(serviceName);
if (providerConfig == null || providerConfig.getService() == null) {
return null;
}
return invoke(serviceName, methodName, types, values, providerConfig.getService());
}
private Object invoke(String url, String methodName, String[] types, String[] values, Object service)
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Class<?> serviceClz = service.getClass();
Class<?>[] typesClz = null;
if (types != null) {
typesClz = new Class<?>[types.length];
for (int i = 0; i < types.length; i++) {
String className = types[i];
if ("".equals(className)) {
// skip
} else if (builtInMap.containsKey(className)) {
typesClz[i] = builtInMap.get(className);
} else {
typesClz[i] = Class.forName(className);
}
}
}
Method method = serviceClz.getMethod(methodName, typesClz);
method.setAccessible(true);
return method.invoke(service, formParameters(typesClz, values));
}
private Object[] formParameters(Class<?>[] types, String[] values) throws SerializationException,
ClassNotFoundException {
if (types == null || types.length == 0) {
return new Object[0];
}
Object[] valueObjs = new Object[types.length];
;
if (values == null) {
valueObjs = new Object[0];
} else {
for (int i = 0; i < values.length; i++) {
valueObjs[i] = jacksonSerializer.toObject(types[i], values[i]);
}
}
return valueObjs;
}
public static Map<String, Class<?>> getBuiltInMap() {
return builtInMap;
}
public static void setBuiltInMap(Map<String, Class<?>> builtInMap) {
InvokeJsonServlet.builtInMap = builtInMap;
}
}