/******************************************************************************* * Copyright © 2012-2015 eBay Software Foundation * This program is dual licensed under the MIT and Apache 2.0 licenses. * Please see LICENSE for more information. *******************************************************************************/ package com.ebay.jetstream.management; import java.io.IOException; import java.net.InetAddress; import java.net.URLDecoder; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jmx.export.annotation.ManagedResource; import com.ebay.jetstream.util.CommonUtils; @ManagedResource(objectName = "Meta/Management", description = "http object management interface") public class ManagementServlet extends HttpServlet { private static final Logger LOGGER = LoggerFactory.getLogger(ManagementServlet.class.getName()); private static final long serialVersionUID = 1L; private static int MAX_URL_LENGTH = 250; private Validator validator ; static { Management.registerResourceFormatter("xml", XmlResourceFormatter.class); Management.registerResourceFormatter("spring", SpringResourceFormatter.class); Management.registerResourceFormatter("html", HtmlResourceFormatter.class); Management.registerResourceFormatter("json", JsonResourceFormatter.class); Management.registerResourceFormatter("help", HelpFormatter.class); } public ManagementServlet() { Management.addBean(toString(), this); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (!checkAuthorized(request, response, false)) return; response.setCharacterEncoding("UTF-8"); Map<String, String[]> parameters = getParameterMap(request); boolean isHelp = parameters.remove("help") != null; if (isHelp && parameters.size() > 0) { sendError(response, HttpServletResponse.SC_BAD_REQUEST, "help cannot be combined with other parameters"); return; } String format = getParameter(parameters, AbstractResourceFormatter.BEAN_FORMAT_PARAM); if (isHelp || CommonUtils.isEmptyTrimmed(format)) { format = "help"; } String beanLocation[] = getBeanLocation(request); for(String bloc : beanLocation){ if(!validate(bloc)){ sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid Request URL"); } } try { response.setHeader("Cache-Control", "no-cache"); BeanController bc = new BeanController(beanLocation[0], beanLocation[1]); bc.setRequestedFields(request.getParameterValues("field")); parameters.remove("field"); if (parameters.size() > 0) { if (!checkAuthorized(request, response, true)) return; bc.process(parameters); } else { bc.setFormat(format); response.setContentType(bc.getContentType()); bc.write(response.getWriter()); } } catch (Throwable t) { sendException(response, "failed for " + beanLocation[1], t); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (!checkAuthorized(request, response, true)) return; // Reading post content must happen early String content = CommonUtils.getStreamAsString(request.getInputStream(), "\n"); Map<String, String[]> parameters = getParameterMap(request); String format = getParameter(parameters, AbstractResourceFormatter.BEAN_FORMAT_PARAM); String form[] = parameters.remove("form"); String actions[] = parameters.remove("action"); String properties[] = parameters.remove("property"); if (format == null) { format = "spring"; } if (form != null) { content = URLDecoder.decode(content, "UTF-8"); if (form.length != 1 && CommonUtils.isEmptyTrimmed(form[0]) || properties != null) { sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Cannot specify both form and property"); return; } int p = content.indexOf("="); properties = new String[] { content.substring(0, p) }; content = content.substring(p + 1); } if (actions != null && properties != null) { sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Cannot specify both action and property"); return; } if (properties != null && CommonUtils.isEmptyTrimmed(content)) { sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Must send content to set property"); return; } if (actions != null) for (String action : actions) parameters.put(action, null); if (properties != null) for (String property : properties) parameters.put(property, new String[] { format, content }); if(!validate(request.getPathInfo()) || !validate(request.getRequestURL().toString())){ sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid Request URL"); } String beanLocation[] = getBeanLocation(request); for(String bloc : beanLocation){ if(!validate(bloc)){ sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid Request URL"); } } try { response.setHeader("Cache-Control", "no-cache"); BeanController bc = new BeanController(beanLocation[0], beanLocation[1]); bc.process(parameters); response.setContentType(bc.getContentType()); } catch (Throwable t) { sendException(response, "POST failed", t); } } private boolean checkAuthorized(HttpServletRequest request, HttpServletResponse response, boolean forWrite) { ManagementNetworkSecurity mns = ManagementNetworkSecurity.getInstance(); boolean authorized = mns == null; try { if (!authorized) authorized = mns.isAuthorized(InetAddress.getByName(request.getRemoteAddr()), forWrite); } catch (UnknownHostException e) { } if (!authorized) sendError(response, HttpServletResponse.SC_UNAUTHORIZED, request.getRemoteAddr() + " access not allowed"); return authorized; } private String[] getBeanLocation(HttpServletRequest request) { String result[] = new String[2]; String base = request.getPathInfo(); result[1] = base == null ? "" : base.substring(1); int suffixToCut = result[1].length(); String url = request.getRequestURL().toString(); result[0] = url.substring(0, url.length() - suffixToCut); return result; } private boolean validate(String reqUrl){ if(validator != null) return validator.validate(reqUrl); else return true; } private String getParameter(Map<String, String[]> map, String key) { String values[] = map.remove(key); return values == null || values.length == 0 ? null : values[0]; } @SuppressWarnings("unchecked") private Map<String, String[]> getParameterMap(HttpServletRequest request) { return new HashMap<String, String[]>(request.getParameterMap()); } private void sendError(HttpServletResponse response, int statusCode, String message) { try { response.sendError(statusCode, message); } catch (Throwable t) { throw CommonUtils.runtimeException(t); } } private void sendException(HttpServletResponse response, String message, Throwable cause) { String errorText = message + ": " + cause + ". " + CommonUtils.redirectPrintStackTraceToString(cause); if (cause instanceof IOException) LOGGER.warn("IOException: " + errorText); else if (cause instanceof IllegalArgumentException) { LOGGER.warn("Bad client request: " + errorText); sendError(response, HttpServletResponse.SC_BAD_REQUEST, errorText); } else { LOGGER.error("INTERNAL SERVER ERROR: " + errorText); sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorText); } } }