/** * Copyright 2013 LiveRamp * * 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.liveramp.hank.ui; import java.io.IOException; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.google.common.base.CaseFormat; import com.google.common.collect.Maps; import org.apache.thrift.TException; import org.apache.thrift.TSerializer; import org.apache.thrift.protocol.TSimpleJSONProtocol; import org.json.JSONObject; import com.liveramp.hank.coordinator.Coordinator; import com.liveramp.hank.coordinator.Domain; import com.liveramp.hank.coordinator.DomainAndVersion; import com.liveramp.hank.coordinator.DomainGroup; import com.liveramp.hank.coordinator.DomainVersion; import com.liveramp.hank.coordinator.DomainVersionProperties; import com.liveramp.hank.coordinator.DomainVersions; import com.liveramp.hank.coordinator.Host; import com.liveramp.hank.coordinator.HostState; import com.liveramp.hank.coordinator.Hosts; import com.liveramp.hank.coordinator.PartitionServerAddress; import com.liveramp.hank.coordinator.Ring; import com.liveramp.hank.coordinator.RingGroup; import com.liveramp.hank.coordinator.RingGroups; import com.liveramp.hank.coordinator.ServingStatus; import com.liveramp.hank.generated.ClientMetadata; import com.liveramp.hank.generated.RuntimeStatisticsSummary; import com.liveramp.hank.ring_group_conductor.RingGroupConductorMode; /** * This class does all the logic for the HankApiServlet. */ public class HankApiHelper { private static TSerializer JSON_SERIALIZER = new TSerializer(new TSimpleJSONProtocol.Factory()); public static class HankApiData { public Map<String, Object> asMap() { Map<String, Object> map = new HashMap<String, Object>(); for (Field field : getClass().getFields()) { try { String name = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName()); if (field.getType() == Map.class) { map.put(name, generify((Map)field.get(this))); } else { map.put(name, field.get(this)); } } catch (IllegalAccessException e) { e.printStackTrace(); } } return map; } } public static class DomainVersionData extends HankApiData { public int versionNumber; public long totalNumBytes; public long totalNumRecords; public boolean isClosed; public Long closedAt; public DomainVersionProperties properties; public boolean isDefunct; } public static class DomainData extends HankApiData { public String name; public int numPartitions; public Map<Integer, DomainVersionData> versionsMap; public String storageEngine; public Map<String, Object> storageEngineOptions; } public static class DomainGroupData extends HankApiData { public String name; public Map<String, Integer> domainVersions; } public static class DomainDeployStatus extends HankApiData { public String domainName; public Map<String, DomainDeployStatusForRingGroup> ringGroupsMap; } public static class DomainDeployStatusForRingGroup extends HankApiData { public String ringGroupName; public Integer targetDomainVersion; public int numPartitions; public int numPartitionsServedAndUpToDate; } public static class DomainGroupDeployStatus extends HankApiData { public String domainGroupName; public Map<String, DomainGroupDeployStatusForRingGroup> ringGroupsMap; } public static class DomainGroupDeployStatusForRingGroup extends HankApiData { public String ringGroupName; public int numPartitions; public int numPartitionsServedAndUpToDate; } public static class RingData extends HankApiData { public int ringNumber; public Map<String, HostData> hostsMap; } public static class RingGroupData extends HankApiData { public String name; public boolean isRingGroupConductorOnline; public RingGroupConductorMode ringGroupConductorMode; public String domainGroupName; public int numPartitions; public int numPartitionsServedAndUpToDate; public Map<Integer, RingData> ringsMap; public Map<String, ConnectedHostData> clients; } public static class ConnectedHostData extends HankApiData { public String host; public String connectedAt; public String type; public String version; public ConnectedHostData(String host, String connectedAt, String type, String version) { this.host = host; this.connectedAt = connectedAt; this.type = type; this.version = version; } } public static class HostData extends HankApiData { public PartitionServerAddress address; public HostState state; public boolean isOnline; public String statisticsString; public String statisticsJson; } private final Coordinator coordinator; public HankApiHelper(Coordinator coordinator) { this.coordinator = coordinator; } private static Map<String, Object> generify(Map map) { Map<String, Object> gMap = new HashMap<String, Object>(); for (Object obj : map.entrySet()) { Map.Entry entry = (Map.Entry)obj; Object value = entry.getValue() instanceof HankApiData ? ((HankApiData)entry.getValue()).asMap() : entry.getValue(); gMap.put(String.valueOf(entry.getKey()), value); } return gMap; } protected RingGroupData getRingGroupData(RingGroup ringGroup) throws IOException { RingGroupData data = new RingGroupData(); data.name = ringGroup.getName(); data.isRingGroupConductorOnline = ringGroup.isRingGroupConductorOnline(); data.ringGroupConductorMode = ringGroup.getRingGroupConductorMode(); data.domainGroupName = ringGroup.getDomainGroup().getName(); ServingStatus servingStatus = RingGroups.computeServingStatusAggregator(ringGroup, ringGroup.getDomainGroup()).computeServingStatus(); data.numPartitions = servingStatus.getNumPartitions(); data.numPartitionsServedAndUpToDate = servingStatus.getNumPartitionsServedAndUpToDate(); Map<Integer, RingData> ringsMap = new HashMap<Integer, RingData>(); for (Ring ring : ringGroup.getRings()) { ringsMap.put(ring.getRingNumber(), getRingData(ring)); } data.ringsMap = ringsMap; data.clients = Maps.newHashMap(); for (ClientMetadata clientData : ringGroup.getClients()) { data.clients.put(clientData.get_host(), new ConnectedHostData( clientData.get_host(), Long.toString(clientData.get_connected_at()), clientData.get_type(), clientData.get_version() )); } return data; } protected RingData getRingData(Ring ring) throws IOException { RingData data = new RingData(); data.ringNumber = ring.getRingNumber(); Map<String, HostData> hostMap = new HashMap<String, HostData>(); for (Host host : ring.getHosts()) { hostMap.put(host.getAddress().toString(), getHostData(host)); } data.hostsMap = hostMap; return data; } protected HostData getHostData(Host host) throws IOException { HostData data = new HostData(); data.address = host.getAddress(); data.isOnline = Hosts.isOnline(host); data.state = host.getState(); data.statisticsString = host.getStatistic(Hosts.RUNTIME_STATISTICS_KEY); data.statisticsJson = getRuntimeStatsString(host); return data; } private String getRuntimeStatsString(Host host) throws IOException { RuntimeStatisticsSummary summary = host.getRuntimeStatisticsSummary(); // just booting up, not deployed, etc if(summary == null){ return new JSONObject().toString(); } try { return JSON_SERIALIZER.toString(summary); } catch (TException e) { throw new IOException(e); } } protected DomainGroupData getDomainGroupData(DomainGroup domainGroup) throws IOException { DomainGroupData data = new DomainGroupData(); data.name = domainGroup.getName(); Map<String, Integer> versionsMap = new HashMap<String, Integer>(); for (DomainAndVersion v : domainGroup.getDomainVersions()) { versionsMap.put(v.getDomain().getName(), v.getVersionNumber()); } data.domainVersions = versionsMap; return data; } protected DomainGroupDeployStatus getDomainGroupDeployStatus(DomainGroup domainGroup) throws IOException { DomainGroupDeployStatus status = new DomainGroupDeployStatus(); status.domainGroupName = domainGroup.getName(); Map<String, DomainGroupDeployStatusForRingGroup> ringGroupsMap = new HashMap<String, DomainGroupDeployStatusForRingGroup>(); Set<RingGroup> ringGroups = coordinator.getRingGroupsForDomainGroup(domainGroup); for (RingGroup ringGroup : ringGroups) { ringGroupsMap.put(ringGroup.getName(), getDomainGroupDeployStatusForRingGroup(ringGroup)); } status.ringGroupsMap = ringGroupsMap; return status; } protected DomainDeployStatus getDomainDeployStatus(Domain domain) throws IOException { DomainDeployStatus status = new DomainDeployStatus(); status.domainName = domain.getName(); Map<String, DomainDeployStatusForRingGroup> ringGroupsMap = new HashMap<String, DomainDeployStatusForRingGroup>(); for (RingGroup ringGroup : coordinator.getRingGroups()) { DomainGroup domainGroup = ringGroup.getDomainGroup(); if (domainGroup != null && domainGroup.getDomainVersion(domain) != null) { ringGroupsMap.put(ringGroup.getName(), getDomainDeployStatusForRingGroup(domainGroup, domain, ringGroup)); } } status.ringGroupsMap = ringGroupsMap; return status; } private DomainDeployStatusForRingGroup getDomainDeployStatusForRingGroup(DomainGroup domainGroup, Domain domain, RingGroup ringGroup) throws IOException { DomainDeployStatusForRingGroup status = new DomainDeployStatusForRingGroup(); status.ringGroupName = ringGroup.getName(); if (domainGroup != null) { status.targetDomainVersion = domainGroup.getDomainVersion(domain) == null ? null : domainGroup.getDomainVersion(domain).getVersionNumber(); } ServingStatus servingStatus = RingGroups.computeServingStatusAggregator(ringGroup, domainGroup).computeServingStatus(); status.numPartitions = servingStatus.getNumPartitions(); status.numPartitionsServedAndUpToDate = servingStatus.getNumPartitionsServedAndUpToDate(); return status; } protected DomainGroupDeployStatusForRingGroup getDomainGroupDeployStatusForRingGroup(RingGroup ringGroup) throws IOException { DomainGroupDeployStatusForRingGroup status = new DomainGroupDeployStatusForRingGroup(); status.ringGroupName = ringGroup.getName(); ServingStatus servingStatus = RingGroups.computeServingStatusAggregator(ringGroup, ringGroup.getDomainGroup()).computeServingStatus(); status.numPartitions = servingStatus.getNumPartitions(); status.numPartitionsServedAndUpToDate = servingStatus.getNumPartitionsServedAndUpToDate(); return status; } protected DomainVersionData getDomainVersionData(DomainVersion version) throws IOException { DomainVersionData data = new DomainVersionData(); data.versionNumber = version.getVersionNumber(); data.totalNumBytes = DomainVersions.getTotalNumBytes(version); data.totalNumRecords = DomainVersions.getTotalNumRecords(version); data.isClosed = DomainVersions.isClosed(version); data.closedAt = version.getClosedAt(); data.properties = version.getProperties(); data.isDefunct = version.isDefunct(); return data; } protected DomainData getDomainData(Domain domain) throws IOException { DomainData data = new DomainData(); data.name = domain.getName(); data.numPartitions = domain.getNumParts(); data.storageEngine = domain.getStorageEngineFactoryClassName(); data.storageEngineOptions = domain.getStorageEngineOptions(); Map<Integer, DomainVersionData> versionsMap = new HashMap<Integer, DomainVersionData>(); for (DomainVersion v : domain.getVersions()) { versionsMap.put(v.getVersionNumber(), getDomainVersionData(v)); } data.versionsMap = versionsMap; return data; } public DomainData getDomainData(String domainName) throws IOException { Domain domain = coordinator.getDomain(domainName); if (domain != null) { return getDomainData(domain); } return null; } }