増分更新を取得するための REST API の Java の例
以下の例では、パブリックな REST API を使用して増分更新を取得する方法を示しています。
apmdevops104jp
以下の例では、パブリックな REST API を使用して増分更新を取得する方法を示しています。
package com.mycompany.app; import java.net.URI; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; public class IncrementalExample { public static void main(String[] args) throws Exception { final CloseableHttpClient httpclient = HttpClients.createDefault(); // specify the host, protocol, and port HttpHost target = new HttpHost("test.ca.com", 8081, "http"); String lastVersionForGraph = "0"; String lastVersionForVertexStatus = "0"; final GraphCache gc = new GraphCache(); for (;;) { try { final HttpGet request = new HttpGet(); request.addHeader("Content-Type", "application/json"); request.addHeader("Authorization", "Bearer f47ac10b-58cc-4372-a567-0e02b2c3d479"); request.addHeader("Accept", "application/hal+json"); if ("0".equals(lastVersionForGraph) || "0".equals(lastVersionForVertexStatus)) { lastVersionForGraph = "0"; lastVersionForVertexStatus = "0"; // reset cache, REST has decided to send you full snapshot gc.clear(); } { // query graph updates request.setURI(URI.create("/apm/appmap/graph/incremental?sinceVersion=" + lastVersionForGraph)); // execute the request final HttpResponse httpResponse = httpclient.execute(target, request); if (httpResponse.getStatusLine().getStatusCode() != 200) { throw new IllegalStateException("Error polling graph changes == " + httpResponse.getStatusLine()); } final HttpEntity entity = httpResponse.getEntity(); final String result = EntityUtils.toString(entity); // parse the results final ObjectMapper mapper = new ObjectMapper(); final JsonNode tree = mapper.readTree(result); lastVersionForGraph = tree.get("lastVersion").asText(); final long newVertices = tree.get("_embedded").get("vertex").size(); final long removedVertices = tree.get("_embedded").get("removedVertex").size(); final long newEdges = tree.get("_embedded").get("edge").size(); final long removedEdges = tree.get("_embedded").get("removedEdge").size(); System.out.println("polled graph changes == [" + newVertices + ", " + newEdges + ", " + removedVertices + ", " + removedEdges + "]"); // apply changes to cache gc.applyGraphChanges(tree); } { // query vertex status updates request.setURI(URI .create("/apm/appmap/graph/vertexstatus/incremental?sinceVersion=" + lastVersionForVertexStatus)); // execute the request final HttpResponse httpResponse = httpclient.execute(target, request); if (httpResponse.getStatusLine().getStatusCode() != 200) { throw new IllegalStateException("Error polling vertex status changes == " + httpResponse.getStatusLine()); } final HttpEntity entity = httpResponse.getEntity(); final String result = EntityUtils.toString(entity); // parse the results final ObjectMapper mapper = new ObjectMapper(); final JsonNode tree = mapper.readTree(result); lastVersionForVertexStatus = tree.get("lastVersion").asText(); final long changes = tree.get("_embedded").get("status").get("alerts").size(); System.out.println("polled vertex status changes == " + changes); // apply changes to cache gc.applyVertexStatusChanges(tree); } final GraphCache.Graph g = gc.getGraphForUI(); } catch (java.net.ConnectException | IllegalStateException e) { e.printStackTrace(); System.out.println("Will try to reretrieve complete graph at next call"); lastVersionForGraph = "0"; lastVersionForVertexStatus = "0"; } catch (Throwable t) { System.out.println("Unknown error : " + t); t.printStackTrace(); break; } if (!"0".equals(lastVersionForGraph)) { Thread.sleep(10000); } } httpclient.close(); } } package com.mycompany.app; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; /** * Example of simple cache holder which applies changes in the order they arrive, with no regard to * the timestamp of the change. * Keeps only latest snapshot, not the historical data * Includes all vertices and edges with no filtering them by type */ public class GraphCache { private Map<String, Vertex> vertices = new HashMap<String, Vertex>(); private List<Edge> edges = new ArrayList<Edge>(); private final static String CCC_VERTEX_IDENTIFICATION = "CCC.VertexIdentification" .toLowerCase(Locale.US); /** * resets the cache */ public void clear() { vertices.clear(); edges.clear(); } /** * parses output from GET /graph/incremental and applies it */ public void applyGraphChanges(JsonNode jsonTree) throws Exception { // added and changed vertices for (final JsonNode json : jsonTree.get("_embedded").get("vertex")) { // parse Vertex final Vertex v = new Vertex(); v.setVertexId(json.get("id").asText()); v.setExternalId(json.get("externalId").asText()); final Iterator<Entry<String, JsonNode>> attributes = json.get("attributes").fields(); while (attributes.hasNext()) { final Map.Entry<String, JsonNode> entry = attributes.next(); final Iterator<JsonNode> values = entry.getValue().elements(); while (values.hasNext()) { final JsonNode attrValue = values.next(); v.getAttributes().put(entry.getKey(), attrValue.asText()); } } // update graph vertices.put(v.getVertexId(), v); System.out.println("Added new vertex : '" + v + "'"); } // removed vertices for (final JsonNode json : jsonTree.get("_embedded").get("removedVertex")) { // parse Vertex final String vertexId = json.get("id").asText(); // update graph if (vertices.containsKey(vertexId)) { vertices.remove(vertexId); } else { System.out.println("Ignored delete for missing vertex " + json); } } // added and changed edges for (final JsonNode json : jsonTree.get("_embedded").get("edge")) { // parse Edge final Edge e = new Edge(); e.setSourceId(json.get("sourceId").asText()); e.setTargetId(json.get("targetId").asText()); e.setBusinessTransactionId(json.get("businessTransactionId").asText()); // update graph edges.add(e); System.out.println("Added new edge : " + e); } // removed edges for (final JsonNode json : jsonTree.get("_embedded").get("removedEdge")) { // parse Edge final String sourceId = json.get("sourceId").asText(); final String targetId = json.get("targetId").asText(); final String businessTransactionId = json.get("businessTransactionId").asText(); // update graph boolean wasRemoved = false; final Iterator<Edge> it = edges.iterator(); while (it.hasNext()) { final Edge e = it.next(); if (!e.getSourceId().equals(sourceId)) { continue; } if (!e.getTargetId().equals(targetId)) { continue; } boolean btEquals = (businessTransactionId == null ? e.getBusinessTransactionId() == null : businessTransactionId.equals(e.getBusinessTransactionId())); if (btEquals) { it.remove(); wasRemoved = true; } } if (!wasRemoved) { System.out.println("Ignored delete for missing edge " + json); } } } /** * parses output from GET /graph/vertexstatus/incremental and applies it */ public void applyVertexStatusChanges(JsonNode jsonTree) throws Exception { // parse alert updates for (final JsonNode json : jsonTree.get("_embedded").get("status").get("alerts")) { // parse Edge final String vertexId = json.get("vertexId").asText(); final String alertName = json.get("alertName").asText(); final String state = json.get("state").asText(); // update graph final Vertex v = vertices.get(vertexId); if (v != null) { v.getAlerts().put(alertName, state); System.out.println("set alert state for vertex '" + vertexId + "' : " + alertName + " --> " + state); } else { System.out.println("Ignored alert for missing vertex '" + vertexId + "'"); } } } /** * returns current snapshot with vertices correlated by externalId */ public Graph getGraphForUI() throws Exception { System.out.println("before CCC : total vertices : " + vertices.size() + " , total edges : " + edges.size()); final Graph ret = new Graph(); ret.getEdges().addAll(edges); ret.getVertices().putAll(vertices); correlateByExternalId(ret); System.out.println("after CCC : total vertices : " + ret.getVertices().size() + " , total edges : " + ret.getEdges().size()); return ret; } private static void correlateByExternalId(Graph mergeGraph) { Multimap<String, Vertex> verticesByExternalID = ArrayListMultimap.create(); Map<String, Vertex> removedVertices = new HashMap<String, Vertex>(); // map vertices by external_id for (Vertex v : mergeGraph.getVertices().values()) { String externalId = v.getExternalId(); if (externalId == null) { continue; } verticesByExternalID.put(externalId, v); } // create CC vertices by merging original vertices by external_id for (Map.Entry<String, Collection<Vertex>> entry : verticesByExternalID.asMap().entrySet()) { Collection<Vertex> v2 = entry.getValue(); if (v2.size() < 2) { continue; } // We have CC vertex. Let's create merged one String externalId = entry.getKey(); Vertex ccVertex = mergeVertices(externalId, v2); mergeGraph.getVertices().put(ccVertex.getVertexId(), ccVertex); // Collect source vertices to be removed as they are replaced by a CC vertex for (Vertex toRemove : v2) { removedVertices.put(toRemove.getVertexId(), toRemove); } } // Let's fixup edges List<Edge> ccEdges = new ArrayList<Edge>(); Iterator<Edge> it = mergeGraph.getEdges().iterator(); for (; it.hasNext();) { Edge e = it.next(); Vertex source = removedVertices.get(e.getSourceId()); Vertex target = removedVertices.get(e.getTargetId()); Vertex bt = removedVertices.get(e.getBusinessTransactionId()); source = source == null ? null : mergeGraph.getVertices().get(source.getExternalId()); target = target == null ? null : mergeGraph.getVertices().get(target.getExternalId()); bt = bt == null ? null : mergeGraph.getVertices().get(bt.getExternalId()); if (source != null || target != null || bt != null) { // Cross cluster edge detected Edge ccEdge = createCrossClusterEdge(e, source, target, bt); ccEdges.add(ccEdge); it.remove(); } } mergeGraph.getEdges().addAll(ccEdges); // Remove source vertices replaced by CC vertices for (String vertexIdToRemove : removedVertices.keySet()) { mergeGraph.getVertices().remove(vertexIdToRemove); } // check for CCC "stub" vertices and remove them, remove the corresponding edges as well Set<String> stubVertexIds = new HashSet<String>(); for (Vertex v : mergeGraph.getVertices().values()) { if (v.getAttributes().containsKey(CCC_VERTEX_IDENTIFICATION)) { stubVertexIds.add(v.getVertexId()); } } for (String stubVertexId : stubVertexIds) { mergeGraph.getVertices().remove(stubVertexId); mergeGraph.getEdges().removeIf( e -> e.getSourceId().equals(stubVertexId) || e.getTargetId().equals(stubVertexId)); } } private static Vertex mergeVertices(String externalId, Collection<Vertex> vertices) { if (vertices.size() < 2) { throw new IllegalArgumentException("vertices size is expected to be at least 2"); } final Vertex ret = new Vertex(); ret.setVertexId(externalId); for (Vertex v : vertices) { ret.getAlerts().putAll(v.getAlerts()); // we want to suppress CCC vertex attributes completely if (!v.getAttributes().containsKey(CCC_VERTEX_IDENTIFICATION)) { ret.getAttributes().putAll(v.getAttributes()); } } return ret; } private static Edge createCrossClusterEdge(Edge edge, Vertex source, Vertex target, Vertex bt) { final Edge ret = new Edge(); ret.setSourceId(source != null ? source.getVertexId() : edge.getSourceId()); ret.setTargetId(target != null ? target.getVertexId() : edge.getTargetId()); ret.setBusinessTransactionId(bt != null ? bt.getVertexId() : edge .getBusinessTransactionId()); return ret; } /** * Vertices and Edges for UI */ public static class Graph { private Map<String, Vertex> vertices = new HashMap<String, Vertex>(); private List<Edge> edges = new ArrayList<Edge>(); public Map<String, Vertex> getVertices() { return vertices; } public List<Edge> getEdges() { return edges; } } /** * single vertex */ public static class Vertex { private String vertexId; private String externalId; private Multimap<String, String> attributes = ArrayListMultimap.create(); private Map<String, String> alerts = new HashMap<>(); public void setVertexId(String vertexId) { this.vertexId = vertexId; } public String getVertexId() { return this.vertexId; } public void setExternalId(String externalId) { this.externalId = externalId; } public String getExternalId() { return this.externalId; } public Multimap<String, String> getAttributes() { return attributes; } public Map<String, String> getAlerts() { return alerts; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Vertex [vertexId="); sb.append(vertexId); sb.append(", externalId="); sb.append(externalId); sb.append(", attributes="); sb.append(attributes); sb.append(", alerts="); sb.append(alerts); sb.append("]"); return sb.toString(); } } /** * single edge */ public static class Edge { private String sourceId; private String targetId; private String businessTransactionId; public void setSourceId(String sourceId) { this.sourceId = sourceId; } public String getSourceId() { return sourceId; } public void setTargetId(String targetId) { this.targetId = targetId; } public String getTargetId() { return targetId; } public void setBusinessTransactionId(String businessTransactionId) { this.businessTransactionId = businessTransactionId; } public String getBusinessTransactionId() { return businessTransactionId; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Edge [sourceId="); builder.append(sourceId); builder.append(", targetId="); builder.append(targetId); builder.append(", businessTransactionId="); builder.append(businessTransactionId); builder.append("]"); return builder.toString(); } } }