/* * Copyright 2013 Jive Software, 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 com.jivesoftware.os.amza.ui.endpoints; import com.fasterxml.jackson.databind.ObjectMapper; import com.jivesoftware.os.amza.api.ring.RingHost; import com.jivesoftware.os.amza.api.ring.RingMemberAndHost; import com.jivesoftware.os.amza.service.AmzaInstance; import com.jivesoftware.os.amza.service.ring.AmzaRingReader; import com.jivesoftware.os.amza.service.ring.RingTopology; import com.jivesoftware.os.amza.service.stats.AmzaStats; import com.jivesoftware.os.amza.ui.soy.SoyService; import com.jivesoftware.os.amza.ui.utils.MinMaxLong; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Singleton; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; /** * @author jonathan.colt */ @Singleton @Path("/amza/ui") public class AmzaUIEndpoints { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); private final AmzaClusterName amzaClusterName; private final ObjectMapper mapper = new ObjectMapper(); public static class AmzaClusterName { private final String name; public AmzaClusterName(String name) { this.name = name; } } private final AmzaInstance amzaInstance; private final AmzaStats amzaStats; private final RingHost host; private final AmzaRingReader ringReader; private final SoyService soyService; public AmzaUIEndpoints(@Context AmzaClusterName amzaClusterName, @Context AmzaStats amzaStats, @Context RingHost host, @Context AmzaRingReader ringReader, @Context AmzaInstance amzaInstance, @Context SoyService soyService) { this.amzaClusterName = amzaClusterName; this.amzaStats = amzaStats; this.host = host; this.amzaInstance = amzaInstance; this.ringReader = ringReader; this.soyService = soyService; } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/chord") public Response chord() { try { RingTopology ring = ringReader.getRing(AmzaRingReader.SYSTEM_RING, -1); List<List<Integer>> matrix = new ArrayList<>(); for (RingMemberAndHost e1 : ring.entries) { List<Integer> weights = new ArrayList<>(); for (RingMemberAndHost e2 : ring.entries) { if (host.equals(e1.ringHost)) { if (!host.equals(e2.ringHost)) { weights.add((int) (amzaStats.getTotalTakes(e2.ringMember))); } else { weights.add(0); } } else if (host.equals(e2.ringHost)) { weights.add((int) (amzaStats.getTotalTakes(e1.ringMember))); } else { weights.add(0); } } for (RingMemberAndHost e2 : ring.entries) { weights.add(0); } matrix.add(weights); weights = new ArrayList<>(); for (RingMemberAndHost e2 : ring.entries) { weights.add(0); } for (RingMemberAndHost e2 : ring.entries) { if (host.equals(e1.ringHost)) { if (!host.equals(e2.ringHost)) { weights.add(0); //TODO FIX (int) (amzaStats.getTotalOffered(r2.getKey()))); } else { weights.add(0); } } else if (host.equals(e2.ringHost)) { weights.add(0); //TODO FIX (int) (amzaStats.getTotalOffered(r.getKey()))); } else { weights.add(0); } } matrix.add(weights); } return Response.ok(mapper.writeValueAsString(matrix), MediaType.APPLICATION_JSON).build(); } catch (Exception x) { LOG.error("Failed genrating chords.", x); return Response.serverError().build(); } } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/arc") public Response arc() { try { RingTopology ring = ringReader.getRing(AmzaRingReader.SYSTEM_RING, -1); Map<String, Integer> index = new HashMap<>(); Map<String, Object> arcData = new HashMap<>(); int i = 0; int g = 1; List<Map<String, Object>> nodes = new ArrayList<>(); for (RingMemberAndHost entry : ring.entries) { String name = entry.ringHost.getHost() + ":" + entry.ringHost.getPort(); addNode(nodes, index, name + "-received", i, g); i++; addNode(nodes, index, name + "-take", i, g); i++; if (entry.ringHost.equals(host)) { addNode(nodes, index, name, i, g); i++; } addNode(nodes, index, name + "-took", i, g); i++; addNode(nodes, index, name + "-replicate", i, g); i++; for (int b = 0; b < 10; b++) { addNode(nodes, index, "", i, 0); i++; } g++; } MinMaxLong range = new MinMaxLong(); range.value(0); String sourceName = hostAsString(host); long[] offered = new long[ring.entries.size()]; long[] takes = new long[ring.entries.size()]; i = 0; for (RingMemberAndHost entry : ring.entries) { long totalOffered = 0; //TODO FIX amzaStats.getTotalOffered(r.getKey()); offered[i] = totalOffered; if (totalOffered > 0) { range.value(totalOffered); } long totalTakes = amzaStats.getTotalTakes(entry.ringMember); takes[i] = totalTakes; if (totalTakes > 0) { range.value(totalOffered); } long total = totalOffered + totalTakes; if (total > 0) { range.value(total); } i++; } List<Map<String, Integer>> links = new ArrayList<>(); Integer root = index.get(sourceName); i = 0; for (RingMemberAndHost entry : ring.entries) { long totalOffered = offered[i]; long totalTakes = takes[i]; long total = totalOffered + totalTakes; // TODO use applied from replicated vs took if (totalOffered > 0) { Integer source = index.get(sourceName + "-replicate"); Integer target = index.get(hostAsString(entry.ringHost) + "-received"); addLink(source, target, range, totalOffered, links); addLink(root, target, range, total, links); } if (totalTakes > 0) { Integer source = index.get(sourceName + "-take"); Integer target = index.get(hostAsString(entry.ringHost) + "-took"); addLink(source, target, range, totalTakes, links); addLink(root, target, range, total, links); } i++; } arcData.put("nodes", nodes); arcData.put("links", links); return Response.ok(mapper.writeValueAsString(arcData), MediaType.APPLICATION_JSON).build(); } catch (Exception x) { LOG.error("Failed genrating chords.", x); return Response.serverError().build(); } } private void addLink(Integer source, Integer target, MinMaxLong range, long value, List<Map<String, Integer>> links) { Map<String, Integer> map = new HashMap<>(); map.put("source", source); map.put("target", target); map.put("value", 1 + (int) (range.zeroToOne(value) * 10)); links.add(map); } private String hostAsString(RingHost ringHost) { return ringHost.getHost() + ":" + ringHost.getPort(); } private void addNode(List<Map<String, Object>> nodes, Map<String, Integer> index, String nodeName, int i, int g) { index.put(nodeName, i); Map<String, Object> node = new HashMap<>(); node.put("nodeName", nodeName); node.put("group", g); nodes.add(node); } @GET @Produces(MediaType.APPLICATION_OCTET_STREAM) @Path("/propagator/download") public StreamingOutput pull() throws Exception { return (OutputStream os) -> { try { File f = new File(System.getProperty("user.dir"), "amza.jar"); try { byte[] buf = new byte[8192]; InputStream is = new FileInputStream(f); int c = 0; while ((c = is.read(buf, 0, buf.length)) > 0) { os.write(buf, 0, c); os.flush(); } os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } catch (Exception e) { throw new WebApplicationException(e); } }; } @GET @Produces(MediaType.TEXT_HTML) public Response get(@Context UriInfo uriInfo) { String rendered = soyService.render(uriInfo.getAbsolutePath() + "/propagator/download", amzaClusterName.name); return Response.ok(rendered).build(); } }