co.cask.tigon.sql.manager.HubHttpHandler.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.tigon.sql.manager.HubHttpHandler.java

Source

/*
 * Copyright  2014 Cask Data, Inc.
 *
 * 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 co.cask.tigon.sql.manager;

import co.cask.http.AbstractHttpHandler;
import co.cask.http.HttpResponder;
import co.cask.tigon.sql.internal.HealthInspector;
import co.cask.tigon.sql.internal.MetricsRecorder;
import co.cask.tigon.sql.internal.ProcessMonitor;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

/**
 * HubHttpHandler
 * Handler used by the internal DiscoveryServer. Serves various REST end points as described in the GSHub documentation
 *
 * This HTTP server is only used by internal SQL Compiler processes. They use this service to communicate with other
 * internal processes, by getting and setting the state.
 *
 * SQL Compiler has 3 types of processes:
 * 1) RTS
 * 2) HFTA
 * 3) GSEXIT
 *
 * These processes use the REST endpoints to communicate with other processes. The expected order of requests is as
 * follows:
 *
 * 1) /announce-instance              from RTS process
 * 2) /announce-initialized-instance  from RTS process
 * 3) /discover-instance              from HFTA processes
 * 4) /discover-initialized-instance  from GSEXIT processes
 * 5) /discover-source                from GSEXIT processes
 * 6) /discover-sink                  from GSEXIT processes
 * 7) /announce-stream-subscription   from GSEXIT processes
 * 8) /discover-start-processing      from RTS processes
 * 9) /announce-fta-instance          from each FTA instance
 * 10) /log-metrics                    from each FTA instance
 *
 * Most of these processes poll on specific end points until  a 200 OK response is returned. If a request cannot be
 * served at a specific time because the state is not set or the specific information is not available then the response
 * is an error code like 400.
 */
@Path("/v1")
public class HubHttpHandler extends AbstractHttpHandler {
    private static final Gson gsonObject = new Gson();
    private final AtomicReference<HubDataStore> hubDataStoreReference;
    private final AtomicInteger gsexitCount;
    private final ProcessMonitor listener;
    private final HealthInspector healthInspector;
    private final MetricsRecorder metricsRecorder;
    private final Map<String, String> processNameMap;

    public HubHttpHandler(HubDataStore ds, HealthInspector inspector, MetricsRecorder recorder,
            ProcessMonitor monitor) {
        hubDataStoreReference = new AtomicReference<HubDataStore>(ds);
        gsexitCount = new AtomicInteger(ds.getHubDataSinks().size());
        listener = monitor;
        healthInspector = inspector;
        metricsRecorder = recorder;
        processNameMap = Maps.newHashMap();
    }

    private String getStringContent(HttpRequest request) {
        return request.getContent().toString(Charsets.UTF_8);
    }

    private class JSONRequestData {
        private JsonObject requestData;

        JSONRequestData(HttpRequest request) {
            try {
                requestData = HubHttpHandler.gsonObject.fromJson(getStringContent(request), JsonObject.class);
            } catch (Exception e) {
                throw new RuntimeException("Cannot parse JSON data from the HTTP response");
            }
        }

        String getInstanceName() {
            return requestData.get("name").getAsString();
        }

        String getProcessID() {
            return requestData.get("ftaid").toString();
        }

        String getProcessName() {
            return requestData.get("fta_name").getAsString();
        }

        String getIP() {
            return requestData.get("ip").getAsString();
        }

        Integer getPort() {
            return requestData.get("port").getAsInt();
        }

        JsonObject getMetrics() {
            return requestData.get("metrics").getAsJsonObject();
        }
    }

    private JsonObject createResponse(String ip, int port) {
        JsonObject res = new JsonObject();
        res.addProperty("ip", ip);
        res.addProperty("port", port);
        return res;
    }

    void setHubAddress(InetSocketAddress address) {
        HubDataStore hds;
        HubDataStore current;
        do {
            current = hubDataStoreReference.get();
            hds = new HubDataStore.Builder(current).setHubAddress(address).build();
        } while (!hubDataStoreReference.compareAndSet(current, hds));
    }

    @Path("/announce-instance")
    @POST
    public void announceInstance(HttpRequest request, HttpResponder responder) {
        JSONRequestData requestData = new JSONRequestData(request);
        HubDataStore hds;
        HubDataStore current;
        do {
            current = hubDataStoreReference.get();
            hds = new HubDataStore.Builder(current).setInstanceName(requestData.getInstanceName())
                    .setClearingHouseAddress(new InetSocketAddress(requestData.getIP(), requestData.getPort()))
                    .build();
        } while (!hubDataStoreReference.compareAndSet(current, hds));
        responder.sendStatus(HttpResponseStatus.OK);
    }

    @Path("/announce-initialized-instance")
    @POST
    public void announceInitializedInstance(HttpRequest request, HttpResponder responder) {
        JSONRequestData requestData = new JSONRequestData(request);
        if (hubDataStoreReference.get().getInstanceName().equals(requestData.getInstanceName())) {
            HubDataStore hds;
            HubDataStore current;
            do {
                current = hubDataStoreReference.get();
                hds = new HubDataStore.Builder(current).initialize().build();
            } while (!hubDataStoreReference.compareAndSet(current, hds));
            responder.sendStatus(HttpResponseStatus.OK);
            return;
        }
        responder.sendError(HttpResponseStatus.BAD_REQUEST, "Instance name does not match");
    }

    @Path("/announce-stream-subscription")
    @POST
    public void announceStreamSubscription(HttpRequest request, HttpResponder responder) {
        JSONRequestData requestData = new JSONRequestData(request);
        int val = 1;
        if (!hubDataStoreReference.get().getInstanceName().equals(requestData.getInstanceName())) {
            responder.sendError(HttpResponseStatus.BAD_REQUEST, "Instance name does not match");
            return;
        }
        val = gsexitCount.decrementAndGet();
        if (val <= 0) {
            HubDataStore hds;
            HubDataStore current;
            do {
                current = hubDataStoreReference.get();
                hds = new HubDataStore.Builder(current).outputReady().build();
            } while (!hubDataStoreReference.compareAndSet(current, hds));
        }
        responder.sendStatus(HttpResponseStatus.OK);
    }

    @Path("/announce-fta-instance")
    @POST
    public void announceMetrics(HttpRequest request, HttpResponder responder) {
        JSONRequestData requestData = new JSONRequestData(request);
        if (!requestData.getInstanceName().equals(hubDataStoreReference.get().getInstanceName())) {
            responder.sendError(HttpResponseStatus.BAD_REQUEST, "Instance name does not match");
            return;
        }
        healthInspector.register(requestData.getProcessID());
        processNameMap.put(requestData.getProcessID(), requestData.getProcessName());
        responder.sendStatus(HttpResponseStatus.OK);
    }

    @Path("/log-metrics")
    @POST
    public void logMetrics(HttpRequest request, HttpResponder responder) {
        JSONRequestData requestData = new JSONRequestData(request);
        if (!requestData.getInstanceName().equals(hubDataStoreReference.get().getInstanceName())) {
            responder.sendError(HttpResponseStatus.BAD_REQUEST, "Instance name does not match");
            return;
        }
        healthInspector.ping(requestData.getProcessID());
        metricsRecorder.recordMetrics(processNameMap.get(requestData.getProcessID()), requestData.getMetrics());
        responder.sendStatus(HttpResponseStatus.OK);
    }

    @Path("/discover-instance/{instance}")
    @GET
    public void discoverInstance(HttpRequest request, HttpResponder responder,
            @PathParam("instance") String instance) {
        if (!instance.equals(hubDataStoreReference.get().getInstanceName())) {
            responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
            return;
        }
        InetSocketAddress address = hubDataStoreReference.get().getClearingHouseAddress();
        if (address == null) {
            responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
            return;
        }
        responder.sendJson(HttpResponseStatus.OK,
                createResponse(address.getAddress().getHostAddress(), address.getPort()));
    }

    @Path("/discover-initialized-instance/{instance}")
    @GET
    public void discoverInitializedInstance(HttpRequest request, HttpResponder responder,
            @PathParam("instance") String instance) {
        if ((!hubDataStoreReference.get().isInitialized())
                || (!instance.equals(hubDataStoreReference.get().getInstanceName()))) {
            //TODO: Make sure Stream Engine accepts error Strings and then we can sendError instead of sendStatus
            //Instance name does not match or Service not initialized
            responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
            return;
        }
        InetSocketAddress address = this.hubDataStoreReference.get().getClearingHouseAddress();
        if (address == null) {
            //TODO: Make sure Stream Engine accepts error Strings and then we can sendError instead of sendStatus
            //Clearing house address not defined
            responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
            return;
        }
        responder.sendJson(HttpResponseStatus.OK,
                createResponse(address.getAddress().getHostAddress(), address.getPort()));
    }

    @Path("/discover-source/{dataSourceName}")
    @GET
    public void discoverSource(HttpRequest request, HttpResponder responder,
            @PathParam("dataSourceName") String dataSourceName) {
        List<HubDataSource> dataSources = hubDataStoreReference.get().getHubDataSources();
        HubDataSource ds = null;
        for (HubDataSource hds : dataSources) {
            if (dataSourceName.equals(hds.getName())) {
                ds = hds;
                break;
            }
        }
        if (ds == null) {
            //Data source not found
            responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
            return;
        }
        responder.sendJson(HttpResponseStatus.OK,
                createResponse(ds.getAddress().getAddress().getHostAddress(), ds.getAddress().getPort()));
    }

    @Path("/discover-sink/{dataSinkName}")
    @GET
    public void discoverSink(HttpRequest request, HttpResponder responder,
            @PathParam("dataSinkName") String dataSinkName) {
        List<HubDataSink> dataSink = hubDataStoreReference.get().getHubDataSinks();
        HubDataSink ds = null;
        for (HubDataSink hds : dataSink) {
            if (dataSinkName.equals(hds.getName())) {
                ds = hds;
                break;
            }
        }
        if (ds == null) {
            //Data sink not found
            responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
            return;
        }
        responder.sendJson(HttpResponseStatus.OK,
                createResponse(ds.getAddress().getAddress().getHostAddress(), ds.getAddress().getPort()));
    }

    @Path("/discover-start-processing/{instance}")
    @GET
    public void discoverStartProcessing(HttpRequest request, HttpResponder responder,
            @PathParam("instance") String instance) {
        if ((hubDataStoreReference.get().isOutputReady())
                && (hubDataStoreReference.get().getInstanceName().equals(instance))) {
            responder.sendJson(HttpResponseStatus.OK, new JsonObject());
            // Invokes Event Listener. All GSEXIT processes have completed /announce-stream-processing
            listener.announceReady();
            return;
        }
        //Instance name does not match
        responder.sendJson(HttpResponseStatus.BAD_REQUEST, new JsonObject());
    }
}