com.betfair.cougar.core.impl.jmx.HtmlAdaptorParser.java Source code

Java tutorial

Introduction

Here is the source code for com.betfair.cougar.core.impl.jmx.HtmlAdaptorParser.java

Source

/*
 * Copyright 2014, The Sporting Exchange Limited
 *
 * 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.betfair.cougar.core.impl.jmx;

import com.betfair.cougar.core.api.jmx.JMXHttpParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jdmk.comm.HtmlParser;
import org.apache.commons.lang.StringEscapeUtils;

import javax.management.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;

public class HtmlAdaptorParser implements HtmlParser, DynamicMBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(HtmlAdaptorParser.class);
    private static final String ADMIN_QUERY = "/administration/";

    private List<JMXHttpParser> parsers = new ArrayList<JMXHttpParser>();
    private MBeanServer mbs;

    public HtmlAdaptorParser(MBeanServer server) {
        this.mbs = server;
    }

    @Override
    public String parsePage(String initialPage) {
        return initialPage;
    }

    void addCustomParser(JMXHttpParser parser) {
        parsers.add(parser);
    }

    @Override
    public String parseRequest(String request) {
        try {
            LOGGER.debug("Parsing JMX/HTTP request {}", request);
            if (!isValid(request)) {
                LOGGER.warn("XSS attempt detected for request: " + request);
                return "<h1>Illegal request</h1>";
            }

            if (request.startsWith(ADMIN_QUERY)) {
                int paramIndex = request.indexOf('?');
                String adminPage;
                if (paramIndex == -1) {
                    adminPage = request.substring(ADMIN_QUERY.length());
                } else {
                    adminPage = request.substring(ADMIN_QUERY.length(), paramIndex);
                }
                Map<String, String> params = getURIParams(request);

                if (adminPage.equals("")) {
                    StringBuilder sb = new StringBuilder();
                    for (JMXHttpParser p : parsers) {
                        sb.append("<a href='").append(p.getPath()).append("'>").append(p.getPath())
                                .append("</a><br/>");
                    }
                    return sb.toString();
                } else if (adminPage.equals("batchquery.jsp")) {
                    String objectName = params.get("on");
                    String attributeName = params.get("an");
                    String separator = "~";
                    String time = params.get("t");
                    StringBuilder buf = new StringBuilder();
                    if (time != null) {
                        buf.append("Time");
                        buf.append(separator);
                        DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                        buf.append(fmt.format(new Date()));
                        buf.append(separator);
                    }
                    if (objectName != null) {
                        if ("System".equalsIgnoreCase(objectName)) {
                            buf.append("System Properties");
                            Properties p = System.getProperties();
                            for (Map.Entry me : p.entrySet()) {
                                if (attributeName == null || "ALL".equals(attributeName)
                                        || attributeName.equals(me.getKey())) {
                                    buf.append(separator);
                                    buf.append(me.getKey());
                                    buf.append(separator);
                                    buf.append(me.getValue());
                                }
                            }
                        } else {
                            query(buf, objectName, attributeName, separator);
                        }
                    }
                    return buf.toString();
                }

                // Loop through the available JmxHttpParsers to see if one matches
                for (JMXHttpParser p : parsers) {
                    if (adminPage.equals(p.getPath())) {
                        return p.process(params);
                    }
                }
            }
            return null;
        } catch (Exception e) {
            LOGGER.debug("Unable to retrieve Bean information", e);
            return null;
        }

    }

    @Override
    public Object getAttribute(String attribute)
            throws AttributeNotFoundException, MBeanException, ReflectionException {
        return null;
    }

    @Override
    public AttributeList getAttributes(String[] attributes) {
        return null;
    }

    @Override
    public MBeanInfo getMBeanInfo() {
        return new MBeanInfo(getClass().getName(), "HTML JMX request parser", null, null, null, null);
    }

    @Override
    public Object invoke(String actionName, Object[] params, String[] signature)
            throws MBeanException, ReflectionException {
        if (actionName.equals("parseRequest")) {
            return parseRequest((String) params[0]);
        } else if (actionName.equals("parsePage")) {
            return parsePage((String) params[0]);
        }
        return null;
    }

    @Override
    public void setAttribute(Attribute attribute)
            throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
    }

    @Override
    public AttributeList setAttributes(AttributeList attributes) {
        return null;
    }

    private Map<String, String> getURIParams(String uri) throws Exception {
        Map<String, String> params = new HashMap<String, String>();
        int qmi = uri.indexOf('?');
        if (qmi >= 0) {
            decodeParams(uri.substring(qmi + 1), params);
        }
        return params;
    }

    private void decodeParams(String params, Map<String, String> p) throws Exception {
        StringTokenizer st = new StringTokenizer(params, "&");
        while (st.hasMoreTokens()) {
            String e = st.nextToken();
            int sep = e.indexOf('=');
            if (sep >= 0) {
                p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1)));
            } else {
                p.put(decodePercent(e).trim(), "true");
            }

        }
    }

    /**
     * Decodes the percent encoding scheme. <br/>
     * For example: "an+example%20string" -> "an example string"
     * @param str a string
     * @return altered string
     */
    private String decodePercent(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            switch (c) {
            case '+':
                sb.append(' ');
                break;
            case '%':
                sb.append((char) Integer.parseInt(str.substring(i + 1, i + 3), 16));
                i += 2;
                break;
            default:
                sb.append(c);
                break;
            }
        }
        str = sb.toString();
        if (str.contains("%")) {
            str = decodePercent(str);
        }
        return str;
    }

    /**
     * Validates request string for not containing malicious characters.
     *
     * @param request a request string
     * @return true - if request string is valid, false - otherwise
     */
    private boolean isValid(String request) {

        String tmp = decodePercent(request);
        if (tmp.indexOf('<') != -1 || tmp.indexOf('>') != -1) {
            return false;
        }

        return true;
    }

    private void query(StringBuilder buf, String son, String attrName, String separator) {
        try {
            ObjectName on = new ObjectName(son);
            if (on.isPattern()) {
                Set<ObjectInstance> res = mbs.queryMBeans(on, null);
                if (res != null && res.size() > 0) {
                    Iterator<ObjectInstance> j = res.iterator();
                    while (j.hasNext()) {
                        on = j.next().getObjectName();
                        appendMBean(mbs, on, attrName, separator, buf);
                        if (j.hasNext()) {
                            buf.append("|");
                        }
                    }
                }
            } else {
                appendMBean(mbs, on, attrName, separator, buf);
            }
        } catch (Exception e) {
            LOGGER.debug("Unable to retrieve Bean information for bean " + son, e);

        }
    }

    private void runOperation(StringBuilder buf, String son, String operation, String separator) {
        try {
            ObjectName on = new ObjectName(son);
            Object result = mbs.invoke(on, operation, new Object[0], new String[0]);
            buf.append(on.toString()).append(separator).append(operation).append(separator).append(result)
                    .append(separator);
        } catch (Exception e) {
            LOGGER.debug("Unable to run operation {} on bean {} ", operation, separator);

        }
    }

    private void appendMBean(MBeanServer server, ObjectName on, String attrName, String separator,
            StringBuilder buf) {
        StringBuilder local = new StringBuilder();
        try {

            MBeanInfo info = server.getMBeanInfo(on);
            local.append(on.toString());
            MBeanAttributeInfo[] attr = info.getAttributes();
            for (int i = 0; i < attr.length; i++) {
                if ((attrName == null || attrName.equals(attr[i].getName())) && attr[i].isReadable()) {
                    local.append(separator);
                    local.append(attr[i].getName());
                    local.append(separator);
                    local.append(server.getAttribute(on, attr[i].getName()));
                }
            }
        } catch (Exception e) {
            LOGGER.debug("Unable to retrieve Bean information for bean " + on, e);
            return;
        }
        buf.append(local);
    }
}