Package org.apache.cassandra.service

Source Code of org.apache.cassandra.service.HttpRequestVerbHandler

/**
* 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.cassandra.service;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.CalloutDeployMessage;
import org.apache.cassandra.db.CalloutManager;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.db.Table;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.gms.FailureDetector;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.net.EndPoint;
import org.apache.cassandra.net.IVerbHandler;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.http.ColumnFamilyFormatter;
import org.apache.cassandra.net.http.HTMLFormatter;
import org.apache.cassandra.net.http.HttpConnection;
import org.apache.cassandra.net.http.HttpRequest;
import org.apache.cassandra.net.http.HttpWriteResponse;
import org.apache.cassandra.procedures.GroovyScriptRunner;
import org.apache.cassandra.utils.LogUtil;

/*
* This class handles the incoming HTTP request after
* it has been parsed.
* Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com )
*/
public class HttpRequestVerbHandler implements IVerbHandler
{
    private static final Logger logger_ = Logger.getLogger(HttpRequestVerbHandler.class);
    /* These are the list of actions supported */
    private static final String DETAILS = "details";
    private static final String LOADME = "loadme";
    private static final String KILLME = "killme";
    private static final String COMPACTME = "compactme";
    private static final String LB_HEALTH_CHECK = "lb_health_check";
    private static final String LB_HEALTH_CHECK_RESPONSE = "I-AM-ALIVE";
    private static final String QUERY = "query";
    private static final String INSERT = "insert";
    private static final String SCRIPT = "script";
    private static final String QUERYRESULTSDIV = "queryResultsDiv";
    private static final String INSERTRESULTSDIV = "insertResultsDiv";
    private static final String SCRIPTRESULTSDIV = "insertResultsDiv";
    private static final String JS_UPDATE_QUERY_FUNCTION = "updateQueryResults";
    private static final String JS_UPDATE_INSERT_FUNCTION = "updateInsertResults";

    private StorageService storageService_;

    public HttpRequestVerbHandler(StorageService storageService)
    {
        storageService_ = storageService;
    }

    public void doVerb(Message message)
    {
        HttpConnection.HttpRequestMessage httpRequestMessage = (HttpConnection.HttpRequestMessage)message.getMessageBody()[0];
        try
        {
            HttpRequest httpRequest = httpRequestMessage.getHttpRequest();
            HttpWriteResponse httpServerResponse = new HttpWriteResponse(httpRequest);
            if(httpRequest.getMethod().toUpperCase().equals("GET"))
            {
                // handle the get request type
                doGet(httpRequest, httpServerResponse);
            }
            else if(httpRequest.getMethod().toUpperCase().equals("POST"))
            {
                // handle the POST request type
                doPost(httpRequest, httpServerResponse);
            }

            // write the response we have constructed into the socket
            ByteBuffer buffer = httpServerResponse.flush();
            httpRequestMessage.getHttpConnection().write(buffer);
        }
        catch(Exception e)
        {
            logger_.warn(LogUtil.throwableToString(e));
        }
    }

    private void doGet(HttpRequest httpRequest, HttpWriteResponse httpResponse)
    {
        boolean fServeSummary = true;
        HTMLFormatter formatter = new HTMLFormatter();
        String query = httpRequest.getQuery();
        /*
         * we do not care about the path for most requests except those
         * from the load balancer
         */
        String path = httpRequest.getPath();
        /* for the health checks, just return the string only */
        if(path.contains(LB_HEALTH_CHECK))
        {
          httpResponse.println(handleLBHealthCheck());
            return;
        }

        formatter.startBody(true, getJSFunctions(), true, true);
        formatter.appendLine("<h1><font color=\"white\"> Cluster map </font></h1>");

        StringBuilder sbResult = new StringBuilder();
        do
        {
            if(query.contains(DETAILS))
            {
                fServeSummary = false;
                sbResult.append(handleNodeDetails());
                break;
            }
            else if(query.contains(LOADME))
            {
                sbResult.append(handleLoadMe());
                break;
            }
            else if(query.contains(KILLME))
            {
                sbResult.append(handleKillMe());
                break;
            }
            else if(query.contains(COMPACTME))
            {
                sbResult.append(handleCompactMe());
                break;
            }
        }
        while(false);

        //formatter.appendLine("<br>-------END DEBUG INFO-------<br><br>");

        if(fServeSummary)
        {
            formatter.appendLine(handlePageDisplay(null, null, null));
        }

        formatter.appendLine("<br>");

        if(sbResult.toString() != null)
        {
            formatter.appendLine(sbResult.toString());
        }

        formatter.endBody();
        httpResponse.println(formatter.toString());
    }

    /*
     * As a result of the POST query, we currently only send back some
     * javascript that updates the data in some place on the browser.
    */
    private void doPost(HttpRequest httpRequest, HttpWriteResponse httpResponse)
    {
        String query = httpRequest.getQuery();

        HTMLFormatter formatter = new HTMLFormatter();
        formatter.startBody(true, getJSFunctions(), true, true);
        formatter.appendLine("<h1><font color=\"white\"> Cluster map </font></h1>");

        // write a shell for adding some javascript to do in-place updates
        StringBuilder sbResult = new StringBuilder();
        do
        {
            if(query.contains(QUERY))
            {
                sbResult.append(handleQuery(httpRequest));
                break;
            }
            else if(query.contains(INSERT))
            {
                sbResult.append(handleInsert(httpRequest));
                break;
            }
            else if(query.contains(SCRIPT))
            {
                sbResult.append(handleScript(httpRequest));
                break;
            }
        }
        while(false);

        if(sbResult.toString() != null)
        {
            formatter.appendLine(sbResult.toString());
        }

        formatter.endBody();

      httpResponse.println(formatter.toString());
    }

    private String handleNodeDetails()
    {
        HTMLFormatter formatter = new HTMLFormatter();

        formatter.appendLine("Token: " + storageService_.getToken());
        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
        formatter.appendLine("Up time (in seconds): " + (runtimeMxBean.getUptime()/1000));

        MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage memUsage = memoryMxBean.getHeapMemoryUsage();
        java.text.DecimalFormat df = new java.text.DecimalFormat("#0.00");
        String smemUsed = df.format((double)memUsage.getUsed()/(1024 * 1024));
        String smemMax = df.format((double)memUsage.getMax()/(1024 * 1024));
        formatter.appendLine("Heap memory usage (in MB): " + smemUsed + "/" + smemMax);

        formatter.appendLine("<br>");
        formatter.appendLine("<br>");

        /*
         * Display DB statatics if we have something to show.
        */
        displayDBStatistics(formatter, df);

        formatter.appendLine("<button onClick=\"window.location='" + StorageService.getHostUrl() + "?" + LOADME + "=T'\">Load Me</button>");
        formatter.appendLine("<button onClick=\"window.location='" + StorageService.getHostUrl() + "?" + COMPACTME + "=T'\">Compact Me</button>");
        formatter.appendLine("<button onClick=\"window.location='" + StorageService.getHostUrl() + "?" + KILLME + "=T'\">Kill Me</button>");

        formatter.appendLine("<br>");
        formatter.appendLine("<br><a href='" + StorageService.getHostUrl() + "'>Back to live nodes list" + "</a>");

        return formatter.toString();
    }

    private void displayDBStatistics(HTMLFormatter formatter, java.text.DecimalFormat df)
    {
        String tableStats = Table.open( DatabaseDescriptor.getTables().get(0) ).tableStats("\n<br>\n", df);

        if ( tableStats.length() == 0 )
            return;

        formatter.appendLine("DB statistics:");
        formatter.appendLine("<br>");
        formatter.appendLine("<br>");

        formatter.appendLine(tableStats);
        formatter.appendLine("<br>");
        formatter.appendLine("<br>");
    }

    private String handlePageDisplay(String queryFormData, String insertFormData, String scriptFormData)
    {
      StringBuilder sb = new StringBuilder();
    sb.append("\n<div id=\"header\"> \n");
    sb.append("<ul>\n");
    sb.append("  <li name=\"one\" onclick=\"javascript:selectTab('one')\"><a href=\"#\">Cluster</a></li>\n");
    sb.append("  <li name=\"two\" onclick=\"javascript:selectTab('two')\"><a href=\"#\">SQL</a></li>\n");
    sb.append("  <li name=\"three\" onclick=\"javascript:selectTab('three')\"><a href=\"#\">Ring</a></li>\n");
    sb.append("</ul>\n");
    sb.append("</div>\n\n");

    sb.append("<div name=\"one\" id=\"content\"> <!-- start tab one -->\n\n");
        sb.append(serveSummary());
        sb.append("</div> <!-- finish tab one -->\n\n");

        sb.append("<div name=\"two\" id=\"content\"> <!-- start tab two -->\n\n");
        sb.append(serveInsertForm(insertFormData));
        sb.append(serveQueryForm(queryFormData));
        sb.append(serveGroovyForm(scriptFormData));
        sb.append("</div> <!-- finish tab two -->\n\n");

        sb.append("<div name=\"three\" id=\"content\"> <!-- start tab three -->\n\n");
        sb.append(serveRingView());
        sb.append("</div> <!-- finish tab three -->\n\n");

        sb.append("\n<script type=\"text/javascript\">\n");
        if(queryFormData != null || insertFormData != null || scriptFormData != null)
          sb.append("selectTab(\"two\");\n");
        else
          sb.append("selectTab(\"one\");\n");

        sb.append("</script>\n");

        return (sb.toString() == null)?"":sb.toString();
    }

    /*
     * Serve the summary of the current node.
     */
    private String serveSummary()
    {
        HTMLFormatter formatter = new HTMLFormatter();

        Set<EndPoint> liveNodeList = Gossiper.instance().getAllMembers();
        // we want this set of live nodes sorted based on the hostname
        EndPoint[] liveNodes = liveNodeList.toArray(new EndPoint[0]);
        Arrays.sort(liveNodes);

        String[] sHeaders = {"Node No.", "Host:Port", "Status", "Leader", "Load Info", "Token", "Generation No."};
        formatter.startTable();
        formatter.addHeaders(sHeaders);
        int iNodeNumber = 0;
        for( EndPoint curNode : liveNodes )
        {
            formatter.startRow();
            ++iNodeNumber;

            // Node No.
            formatter.addCol("" + iNodeNumber);
            // Host:Port
            formatter.addCol("<a href='http://" + curNode.getHost() + ":" + DatabaseDescriptor.getHttpPort() + "/home?" + DETAILS + "=T'>" + curNode.getHost() + ":" + curNode.getPort() + "</a>");
            //Status
            String status = ( FailureDetector.instance().isAlive(curNode) ) ? "Up" : "Down";
            formatter.addCol(status);
            //Leader
            boolean isLeader = StorageService.instance().isLeader(curNode);
            formatter.addCol(Boolean.toString(isLeader));
            //Load Info
            String loadInfo = getLoadInfo(curNode);
            formatter.addCol(loadInfo);
            // Token
            if(curNode == null)
                formatter.addCol("NULL!");
            else
                formatter.addCol(storageService_.getToken(curNode));
            // Generation Number
            formatter.addCol(Integer.toString(Gossiper.instance().getCurrentGenerationNumber(curNode)));

            formatter.endRow();
        }

        formatter.endTable();

        return formatter.toString();
    }

    private String serveRingView()
    {
        HTMLFormatter formatter = new HTMLFormatter();
        String[] sHeaders = {"Range No.", "Range", "N1", "N2", "N3"};
        formatter.startTable();
        formatter.addHeaders(sHeaders);

        Map<Range, List<EndPoint>> oldRangeToEndPointMap = StorageService.instance().getRangeToEndPointMap();
        Set<Range> rangeSet = oldRangeToEndPointMap.keySet();

        int iNodeNumber = 0;
        for ( Range range : rangeSet )
        {
          formatter.startRow();
            ++iNodeNumber;

            // Range No.
            formatter.addCol("" + iNodeNumber);

            // Range
            formatter.addCol("(" + range.left() + ",<br>" + range.right() + "]");

            List<EndPoint> replicas = oldRangeToEndPointMap.get(range);
            for ( EndPoint replica : replicas )
            {
              // N1 N2 N3
              formatter.addCol(replica.toString());
            }

            formatter.endRow();
        }

        formatter.endTable();

        return formatter.toString();
    }

    private String getLoadInfo(EndPoint ep)
    {
        if ( StorageService.getLocalControlEndPoint().equals(ep) )
        {
            return StorageService.instance().getLoadInfo();
        }
        else
        {
            return StorageService.instance().getLoadInfo(ep);
        }
    }

    /*
     * Returns the HTML code for a form to query data from the db cluster.
     */
    private String serveQueryForm(String queryResult)
    {
        HTMLFormatter formatter = new HTMLFormatter();
        formatter.appendLine("<BR><fieldset><legend>Query the cluster</legend>");
        formatter.appendLine("<FORM action=\"" + StorageService.getHostUrl() + "/home?" + QUERY + "=T\" method=\"post\">");

        // get the list of column families
        Table table = Table.open("Mailbox");
        Set<String> columnFamilyComboBoxSet = table.getColumnFamilies();

        formatter.append("select from ");
        formatter.addCombobox(columnFamilyComboBoxSet, "columnfamily", 0);
        formatter.append(" : <INPUT name=columnName>");
        formatter.appendLine(" where key = <INPUT name=key>");
        formatter.appendLine("<BR>");
        formatter.appendLine("<INPUT type=\"submit\" value=\"Send\"> <INPUT type=\"reset\">");

        formatter.appendLine("</FORM>");
        formatter.addDivElement(QUERYRESULTSDIV, queryResult);
        formatter.appendLine("</fieldset><BR>");

        return formatter.toString();
    }

    /*
     * Returns the HTML code for a form to to run custom code on the cluster.
     */
    private String serveGroovyForm(String scriptResult)
    {
        HTMLFormatter formatter = new HTMLFormatter();
        formatter.appendLine("<BR><fieldset><legend>Run custom code on the cluster</legend>");
        formatter.appendLine("<FORM action=\"" + StorageService.getHostUrl() + "/home?" + SCRIPT + "=T\" method=\"post\">");
        formatter.append(" Callout name : <INPUT name=calloutName>");
        formatter.appendLine("<BR>");
        formatter.append("Groovy code to run on the server:<br>");
        formatter.append("<textarea name=scriptTextArea rows=\"10\" cols=\"100\"></textarea>");
        formatter.appendLine("<BR>");
        formatter.appendLine("<INPUT name=deploy type=\"submit\" value=\"Deploy\"> <INPUT name=execute type=\"submit\" value=\"Execute\"> <INPUT name=reset type=\"reset\">");

        formatter.appendLine("</FORM>");
        formatter.addDivElement(SCRIPTRESULTSDIV, scriptResult);
        formatter.appendLine("</fieldset><BR>");

        return formatter.toString();
    }

    /*
     * Returns the HTML code for a form to insert data into the db cluster.
     */
    private String serveInsertForm(String insertResult)
    {
        HTMLFormatter formatter = new HTMLFormatter();
        formatter.appendLine("<BR><fieldset>\n<legend>Insert data into the cluster</legend>\n");
        formatter.appendLine("<FORM action=\"" + StorageService.getHostUrl() + "/home?" + INSERT + "=T\" method=\"post\">");

        // get the list of column families
        Table table = Table.open("Mailbox");
        Set<String> columnFamilyComboBoxSet = table.getColumnFamilies();

        formatter.append("insert into ");
        formatter.addCombobox(columnFamilyComboBoxSet, "columnfamily", 0);
        formatter.append(" : <INPUT name=columnName>");
        formatter.append(" data = <INPUT name=data>");
        formatter.appendLine(" where key = <INPUT name=key>\n");
        formatter.appendLine("<BR>\n");
        formatter.appendLine("<INPUT type=\"submit\" value=\"Send\"> <INPUT type=\"reset\">\n");

        formatter.appendLine("</FORM>\n");
        formatter.addDivElement(INSERTRESULTSDIV, insertResult);
        formatter.appendLine("</fieldset>\n<BR>\n");

        return formatter.toString();
    }

    /*
     * Handle the query of some data from the client.
     */
    private String handleQuery(HttpRequest httpRequest)
    {
      boolean fQuerySuccess = false;
      String sRetVal = "";

      // get the various values for this HTTP request
      String sColumnFamily = httpRequest.getParameter("columnfamily");
      String sColumn = httpRequest.getParameter("columnName");
      String sKey = httpRequest.getParameter("key");

      // get the table name
      String sTableName = DatabaseDescriptor.getTables().get(0);

      StringBuilder sb = new StringBuilder();
      ColumnFamilyFormatter cformatter = new ColumnFamilyFormatter(sb);

        try
        {
        Table table = Table.open(sTableName);
        String queryFor = sColumnFamily;
        if(sColumn != null && !"*".equals(sColumn))
        {
          queryFor += ":" + sColumn;
        }
          ColumnFamily cf = table.get(sKey, queryFor);

          if (cf == null)
          {
              sRetVal = "Key [" + sKey + "], column family [" + sColumnFamily + "] not found.";
          }
          else
          {
            cformatter.printKeyColumnFamily(sb, sKey, cf);
            fQuerySuccess = true;
            sRetVal = sb.toString();
          }
        }
        catch (Exception e)
        {
          // write failed - return the reason
          sRetVal = e.getMessage();
        }

        if(fQuerySuccess)
          sRetVal = "Success: " + sRetVal;
        else
          sRetVal = "Error: " + sRetVal;

        return handlePageDisplay(sRetVal, null, null);
    }

    /*
     * Handle the query of some data from the client.
     */
    private String handleInsert(HttpRequest httpRequest)
    {
      boolean fInsertSuccess = false;
      String sRetVal = "";

      // get the various values for this HTTP request
      String sColumnFamily = httpRequest.getParameter("columnfamily");
      String sColumn = httpRequest.getParameter("columnName");
      String sKey = httpRequest.getParameter("key");
      String sDataToInsert = httpRequest.getParameter("data");

      // get the table name
      String sTableName = DatabaseDescriptor.getTables().get(0);

        try
        {
          // do the insert first
            RowMutation rm = new RowMutation(sTableName, sKey);
            rm.add(sColumnFamily + ":" + sColumn, sDataToInsert.getBytes(), 0);
            rm.apply();

            fInsertSuccess = true;
          sRetVal = "columnfamily=" + httpRequest.getParameter("columnfamily") + " key=" + httpRequest.getParameter("key") + " data=" + httpRequest.getParameter("data");
        }
        catch (Exception e)
        {
          // write failed - return the reason
          sRetVal = e.getMessage();
        }
        System.out.println("Write done ...");

        if(fInsertSuccess)
          sRetVal = "The insert was successful : " + sRetVal;
        else
          sRetVal = "The insert was failed : " + sRetVal;

        return handlePageDisplay(null, sRetVal, null);
    }

    /*
     * Handle the script to be run on the server.
     */
    private String handleScript(HttpRequest httpRequest)
    {
      boolean fQuerySuccess = false;
      String sRetVal = "";

      // get the various values for this HTTP request
        String callout = httpRequest.getParameter("calloutName");       
      String script = httpRequest.getParameter("scriptTextArea");
        String deploy = httpRequest.getParameter("deploy");
        String execute = httpRequest.getParameter("execute");
      try
      {
            if ( deploy != null )
            {
                if ( callout != null && script != null )
                {
                    doDeploy(callout, script);
                    sRetVal = "Finished deployment of callouts ...";
                }
            }
            if ( execute != null )
            {
                if ( script != null )
                {
                sRetVal = GroovyScriptRunner.evaluateString(script);               
                }
            }
            fQuerySuccess = true;
      }
      catch(Throwable t)
      {
        sRetVal = t.getMessage();
        logger_.warn(LogUtil.throwableToString(t));
      }

        if(fQuerySuccess)
          sRetVal = "Result: Success<br>\nReturn value: <br>\n" + sRetVal;
        else
          sRetVal = "Result: Error<br>\nError: <br>\n" + sRetVal;

        return handlePageDisplay(null, null, sRetVal);
    }
   
    private void doDeploy(String callout, String script)
    {
        Set<EndPoint> allMbrs = Gossiper.instance().getAllMembers();               
        /* Send the script to all mbrs to deploy it locally. */
        CalloutDeployMessage cdMessage = new CalloutDeployMessage(callout, script);
        try
        {
            Message message = CalloutDeployMessage.getCalloutDeployMessage(cdMessage);
            for ( EndPoint mbr : allMbrs )
            {
                if ( mbr.equals( StorageService.getLocalControlEndPoint() ) )
                {
                    /* Deploy locally */
                    CalloutManager.instance().addCallout(callout, script);
                }
                else
                {
                    EndPoint to = new EndPoint(mbr.getHost(), DatabaseDescriptor.getStoragePort());
                    logger_.debug("Deploying the script to " + mbr);
                    MessagingService.getMessagingInstance().sendOneWay(message, to);
                }
            }
        }
        catch ( IOException ex )
        {
            logger_.warn( LogUtil.throwableToString(ex) );
        }
    }

    private String getJSFunctions()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("function " + JS_UPDATE_QUERY_FUNCTION + "(text)\n");
        sb.append("{\n");
        sb.append("    obj = document.getElementById(\"" + QUERYRESULTSDIV + "\");\n");
        sb.append("    if(obj)\n");
        sb.append("        obj.innerHTML = text;\n");
        sb.append("}\n");
        sb.append("\n");
        sb.append("function " + JS_UPDATE_INSERT_FUNCTION + "(text)\n");
        sb.append("{\n");
        sb.append("    obj = document.getElementById(\"" + INSERTRESULTSDIV + "\");\n");
        sb.append("    if(obj)\n");
        sb.append("        obj.innerHTML = text;\n");
        sb.append("}\n");
        sb.append("\n");

        return sb.toString();
    }

    /*
     * Load the current node with data.
     */
    private String handleLoadMe()
    {
        return "Loading...";
    }

    private String handleCompactMe()
    {
        Table table = Table.open(DatabaseDescriptor.getTables().get(0));
        table.forceCompaction();
        return "Compacting ...";
    }

    private String handleLBHealthCheck()
    {
      if(StorageService.instance().isShutdown())
        return "";
      return LB_HEALTH_CHECK_RESPONSE;
    }

    /*
     * Kill the current node.
     */
    private String handleKillMe()
    {
      if(StorageService.instance().isShutdown())
        return "Already scheduled for being shutdown";
      /*
       * The storage service will wait for a period of time to let the
       * VIP know that we are shutting down, then will perform an actual
       * shutdown on a separate thread.
       */
        String status = "Service has been killed";
        try
        {
            StorageService.instance().killMe();
        }
        catch( Throwable th )
        {
            logger_.warn(LogUtil.throwableToString(th));
            status = "Failed to kill service.";
        }
      return status;
    }

}
TOP

Related Classes of org.apache.cassandra.service.HttpRequestVerbHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.