org.apache.hadoop.hdfs.qjournal.server.JournalNodeJspHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.qjournal.server.JournalNodeJspHelper.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.hadoop.hdfs.qjournal.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.DFSUtil;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

/**
 * Class used for connecting to journal nodes and obtaining
 * statistics about the underlying journals.
 */
public class JournalNodeJspHelper {

    private static final Log LOG = LogFactory.getLog(JournalNodeJspHelper.class);
    public static final ObjectMapper mapper = new ObjectMapper();

    private static final int HTTP_CONNECT_TIMEOUT = 2000; // 2 secs
    private static final int HTTP_READ_TIMEOUT = 5000; // 5 secs

    private final Configuration conf;
    private final String name;

    public JournalNodeJspHelper(JournalNode jn) {
        conf = jn.getConf();
        StringBuilder tempName = new StringBuilder("");
        for (InetSocketAddress addr : getJournals()) {
            tempName = tempName.append(getHostAddress(addr)).append(",");
        }
        if (tempName.length() > 0) {
            // Removing last ,
            tempName.deleteCharAt(tempName.length() - 1);
        }
        name = tempName.toString();
    }

    public String getName() {
        return name;
    }

    public QJMStatus generateQJMStatusReport() throws IOException {
        QJMStatus status = new QJMStatus(getJournals());

        Map<InetSocketAddress, Map<String, Map<String, String>>> temp = new HashMap<InetSocketAddress, Map<String, Map<String, String>>>();
        Map<InetSocketAddress, Boolean> aliveMap = new HashMap<InetSocketAddress, Boolean>();

        Set<String> allJournals = new HashSet<String>();

        for (InetSocketAddress jn : status.journalNodes) {
            LOG.info("Connecting to journal node: " + jn);
            // mapping from a single journal to key,value stats
            String json = fetchStats(jn);
            aliveMap.put(jn, json != null);
            Map<String, Map<String, String>> map = getStatsMap(json);
            // update the list of all available journals
            allJournals.addAll(map.keySet());
            // store the retrieved results
            temp.put(jn, map);
        }

        for (InetSocketAddress jn : temp.keySet()) {
            // if we haven't fetch anything, we need to handle this
            Map<String, Map<String, String>> stats = temp.get(jn);
            for (String journal : allJournals) {
                // for each journal
                // we create (node, stats) mapping
                status.addJournalStats(journal, jn, stats.get(journal), aliveMap.get(jn));
            }
        }
        return status;
    }

    public static class QJMStatus {
        // all journal nodes
        private final Collection<InetSocketAddress> journalNodes;

        // all stats exposed by journals
        private final Set<String> statNames;
        // all journal nodes
        private final Map<String, Boolean> aliveStatus;

        // (journalid) -> (list(node -> (k,v))) mapping
        final Map<String, List<StatsDescriptor>> stats;

        public QJMStatus(Collection<InetSocketAddress> jns) {
            statNames = new HashSet<String>();
            aliveStatus = new HashMap<String, Boolean>();
            journalNodes = jns;
            stats = new HashMap<String, List<StatsDescriptor>>();
        }

        void addJournalStats(String journal, InetSocketAddress jn, Map<String, String> stat, boolean alive) {
            StatsDescriptor sd = new StatsDescriptor(jn, stat);

            // update the set of available stats
            statNames.addAll(sd.statsPerJournal.keySet());
            // and the names of journalnodes
            aliveStatus.put(sd.journalNode, alive);

            // we add the mapping to the list of mappings for each journal
            List<StatsDescriptor> currentList = stats.get(journal);
            if (currentList == null) {
                currentList = new ArrayList<StatsDescriptor>();
                stats.put(journal, currentList);
            }
            currentList.add(sd);
        }

        public Map<String, List<StatsDescriptor>> getStatus() {
            return stats;
        }

        public Collection<String> getJournalIds() {
            return stats.keySet();
        }

        public Map<String, Boolean> getAliveMap() {
            return aliveStatus;
        }
    }

    public static class StatsDescriptor {
        String journalNode;
        Map<String, String> statsPerJournal;

        public StatsDescriptor(InetSocketAddress jn, Map<String, String> statsPerJournal) {
            this.journalNode = getHostAddress(jn) + " : " + jn.getPort();
            this.statsPerJournal = statsPerJournal == null ? new HashMap<String, String>() : statsPerJournal;
        }

        public String toString() {
            return journalNode + ":" + statsPerJournal.toString();
        }
    }

    /**
     * Retrieve value from the map corresponding to the given key.
     */
    private static String getValue(Map<String, String> map, String keyName) {
        String value = map.get(keyName);
        return value == null ? "-" : value;
    }

    /**
     * Fetch stats from a single given journal node over http.
     */
    private String fetchStats(InetSocketAddress jn) throws IOException {
        try {
            return DFSUtil.getHTMLContentWithTimeout(new URI("http", null, jn.getAddress().getHostAddress(),
                    jn.getPort(), "/journalStats", null, null).toURL(), HTTP_CONNECT_TIMEOUT, HTTP_READ_TIMEOUT);
        } catch (Exception e) {
            LOG.error("Problem connecting to " + getHostAddress(jn), e);
            return null;
        }
    }

    /**
     * Get the map corresponding to the JSON string
     */
    private static Map<String, Map<String, String>> getStatsMap(String json) throws IOException {
        if (json == null || json.isEmpty()) {
            return new HashMap<String, Map<String, String>>();
        }
        TypeReference<Map<String, Map<String, String>>> type = new TypeReference<Map<String, Map<String, String>>>() {
        };
        return mapper.readValue(json, type);
    }

    /**
     * Get the list of journal addresses to connect.
     */
    private Collection<InetSocketAddress> getJournals() {
        return JournalNode.getJournalHttpAddresses(conf);
    }

    /**
     * Generate health report for journal nodes
     */
    public static String getNodeReport(QJMStatus status) {
        StringBuilder sb = new StringBuilder();
        sb.append("<table border=1 cellpadding=1 cellspacing=0 title=\"Journals\">");
        sb.append("<thead><tr><td><b>Journal node</b></td><td><b>Alive</b></td></tr></thead>");
        for (Entry<String, Boolean> e : status.getAliveMap().entrySet()) {
            if (e.getValue()) {
                sb.append("<tr><td>" + e.getKey() + "</td><td><font color=green>Active</font></td></tr>");
            } else {
                sb.append("<tr><td>" + e.getKey() + "</td><td><font color=red>Failed</font></td></tr>");
            }
        }
        sb.append("</table>");
        return sb.toString();
    }

    /**
     * Generate report for all journals and all journal nodes
     */
    public static String getJournalReport(QJMStatus status) {
        StringBuilder sb = new StringBuilder();
        sb.append("<table border=1 cellpadding=1 cellspacing=0 title=\"Journals\">");
        sb.append("<thead><tr><td><b>JournalId</b></td><td><b>Statistics</b></td></tr></thead>");
        for (String journalId : status.getJournalIds()) {
            sb.append("<tr><td>" + journalId + "</td><td>");
            getHTMLTableForASingleJournal(status, journalId, sb);
            sb.append("</td></tr>");
        }
        sb.append("</table>");
        return sb.toString();
    }

    /**
     * Render html table for a single journal.
     */
    public static void getHTMLTableForASingleJournal(QJMStatus status, String journalName, StringBuilder sb) {
        List<StatsDescriptor> stats = status.stats.get(journalName);
        if (stats == null) {
            return;
        }
        Set<String> statsNames = status.statNames;

        // header
        sb.append("<table border=1 align=\"right\" cellpadding=1 " + "cellspacing=0 title=\"Journal statistics\">");
        sb.append("<thead><tr><td></td>");
        for (StatsDescriptor sd : stats) {
            sb.append("<td><b>" + sd.journalNode + "</b></td>");
        }
        sb.append("</tr></thead>");

        // contents
        for (String st : statsNames) {
            // for each available stat
            sb.append("<tr><td>" + st + "</td>");
            // for each available node
            for (StatsDescriptor sd : stats) {
                sb.append("<td align=\"right\">" + getValue(sd.statsPerJournal, st) + "</td>");
            }
            sb.append("</tr>");
        }
        sb.append("</table>");
    }

    /**
     * Returns the address of the host minimizing DNS lookups. 
     * @param addr
     * @return
     */
    private static String getHostAddress(InetSocketAddress addr) {
        String hostToAppend = "";
        if (addr.isUnresolved()) {
            hostToAppend = addr.getHostName();
        } else {
            hostToAppend = addr.getAddress().getHostAddress();
        }
        return hostToAppend;
    }
}