/* * 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.sling.discovery.impl; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.StringTokenizer; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.felix.webconsole.AbstractWebConsolePlugin; import org.apache.felix.webconsole.WebConsoleConstants; import org.apache.sling.discovery.ClusterView; import org.apache.sling.discovery.InstanceDescription; import org.apache.sling.discovery.InstanceFilter; import org.apache.sling.discovery.TopologyEvent; import org.apache.sling.discovery.TopologyEvent.Type; import org.apache.sling.discovery.TopologyEventListener; import org.apache.sling.discovery.TopologyView; import org.apache.sling.discovery.base.commons.ClusterViewService; import org.apache.sling.discovery.base.connectors.announcement.Announcement; import org.apache.sling.discovery.base.connectors.announcement.AnnouncementRegistry; import org.apache.sling.discovery.base.connectors.announcement.CachedAnnouncement; import org.apache.sling.discovery.base.connectors.ping.ConnectorRegistry; import org.apache.sling.discovery.base.connectors.ping.TopologyConnectorClientInformation; import org.apache.sling.discovery.commons.providers.spi.base.ClusterSyncHistory; import org.apache.sling.discovery.commons.providers.spi.base.SyncTokenService; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Simple webconsole which gives an overview of the topology visible by the * discovery service */ @Component @Service(value = { TopologyEventListener.class, Servlet.class }) @Properties({ @Property(name=org.osgi.framework.Constants.SERVICE_DESCRIPTION, value="Apache Sling Web Console Plugin to display Background servlets and ExecutionEngine status"), @Property(name=WebConsoleConstants.PLUGIN_LABEL, value=TopologyWebConsolePlugin.LABEL), @Property(name=WebConsoleConstants.PLUGIN_TITLE, value=TopologyWebConsolePlugin.TITLE), @Property(name="felix.webconsole.category", value="Sling"), @Property(name="felix.webconsole.configprinter.modes", value={"zip"}) }) @SuppressWarnings("serial") public class TopologyWebConsolePlugin extends AbstractWebConsolePlugin implements TopologyEventListener { public static final String LABEL = "topology"; public static final String TITLE = "Topology Management"; private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** the truncated log of topology events, filtered by property change types. shown in webconsole **/ private final List propertyChangeLog = new LinkedList(); /** the truncated log of topology events, shown in webconsole **/ private final List topologyLog = new LinkedList(); /** the date format used in the truncated log of topology events **/ private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); @Reference private ClusterViewService clusterViewService; @Reference private AnnouncementRegistry announcementRegistry; @Reference private ConnectorRegistry connectorRegistry; @Reference private SyncTokenService syncTokenService; @Reference private Config config; private TopologyView currentView; @Override public String getLabel() { return LABEL; } @Override public String getTitle() { return TITLE; } @Activate @Override public void activate(final BundleContext bundleContext) { super.activate(bundleContext); } @Deactivate @Override public void deactivate() { super.deactivate(); } @Override protected void renderContent(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException { Object rawRoot = req.getAttribute(WebConsoleConstants.ATTR_PLUGIN_ROOT); if (!(rawRoot instanceof String)) { throw new ServletException("Illegal attr: " + WebConsoleConstants.ATTR_PLUGIN_ROOT); } String root = rawRoot.toString(); String pathInfo = req.getRequestURI().substring(root.length()); final PrintWriter pw = res.getWriter(); if (pathInfo.equals("")) { if ( this.currentView != null ) { renderOverview(pw, currentView); } } else { StringTokenizer st = new StringTokenizer(pathInfo, "/"); final String nodeId = st.nextToken(); renderProperties(pw, req.getContextPath(), nodeId); } } /** * Render the properties page of a particular instance */ private void renderProperties(final PrintWriter pw, final String contextPath, final String nodeId) { if (logger.isDebugEnabled()) { logger.debug("renderProperties: nodeId=" + nodeId); } final TopologyView tv = this.currentView; @SuppressWarnings("unchecked") Set instances = ( tv == null ? (Set)Collections.EMPTY_SET : tv.findInstances(new InstanceFilter() { @Override public boolean accept(InstanceDescription instance) { String slingId = instance.getSlingId(); if (logger.isDebugEnabled()) { logger.debug("renderProperties/picks: slingId={}", slingId); } return (slingId.equals(nodeId)); } })); if (instances != null && instances.size() == 1) { InstanceDescription instance = instances.iterator().next(); pw.println("Properties of " + instance.getSlingId() + ":
"); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); boolean odd = true; for (Iterator> it = instance.getProperties() .entrySet().iterator(); it.hasNext();) { Entry entry = it.next(); String oddEven = odd ? "odd" : "even"; odd = !odd; pw.println(""); pw.println(""); pw.println(""); pw.println(""); } pw.println(""); pw.println("
KeyValue
" + entry.getKey() + "" + entry.getValue() + "
"); } } /** * Render the overview of the entire topology */ private void renderOverview(final PrintWriter pw, final TopologyView topology) { pw.println("

Configuration

"); pw.println("
"); pw.print("Configure Discovery Service"); pw.println("
"); pw.println("
"); final String changing; if (!topology.isCurrent()) { changing = " CHANGING! (the view is no longer current!)"; } else { changing = ""; } pw.println("

Topology"+changing+"

"); pw.println("
"); pw.println("Instances in the topology"); pw.println("
"); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); Set clusters = topology.getClusterViews(); ClusterView myCluster = topology.getLocalInstance().getClusterView(); boolean odd = true; renderCluster(pw, myCluster, myCluster, odd, topology.isCurrent()); for (Iterator it = clusters.iterator(); it.hasNext();) { ClusterView clusterView = it.next(); if (clusterView.equals(myCluster)) { // skip - I already rendered that continue; } odd = !odd; renderCluster(pw, clusterView, myCluster, odd, topology.isCurrent()); } pw.println(""); pw.println("
Sling id (click for properties)ClusterView idLocal instanceLeader instanceIn local clusterAnnounced by instance
"); pw.println("
"); pw.println("
"); pw.println("

Connectors

"); listIncomingTopologyConnectors(pw); listOutgoingTopologyConnectors(pw); pw.println("
"); pw.println("

Topology Change History

"); pw.println("
");
        for (Iterator it = topologyLog
                .iterator(); it.hasNext();) {
            String aLogEntry = it.next();
            pw.println(aLogEntry);
        }
        pw.println("
"); pw.println("
"); pw.println("

Property Change History

"); pw.println("
");
        for (Iterator it = propertyChangeLog
                .iterator(); it.hasNext();) {
            String aLogEntry = it.next();
            pw.println(aLogEntry);
        }
        pw.println("
"); pw.println("
"); pw.println("

SyncTokenService History

"); pw.println("
");
        if (!config.useSyncTokenService()) {
            pw.println("(disabled - useSyncTokenService flag is false)");
        } else if (syncTokenService == null) {
            pw.println("(no SyncTokenService available)");
        } else {
            ClusterSyncHistory clusterSyncHistory = syncTokenService.getClusterSyncHistory();
            if (clusterSyncHistory == null) {
                pw.println("(no history available)");
            } else {
                for (String syncHistoryEntry : clusterSyncHistory.getSyncHistory()) {
                    pw.println(syncHistoryEntry);
                }
            }
        }
        pw.println("
"); pw.println("
"); } /** * Render a particular cluster (into table rows) */ private void renderCluster(final PrintWriter pw, final ClusterView renderCluster, final ClusterView localCluster, final boolean odd, final boolean current) { final Collection announcements = announcementRegistry.listAnnouncementsInSameCluster(localCluster); for (Iterator it = renderCluster.getInstances() .iterator(); it.hasNext();) { final InstanceDescription instanceDescription = it.next(); final boolean inLocalCluster = renderCluster == localCluster; Announcement parentAnnouncement = null; for (Iterator it2 = announcements.iterator(); it2 .hasNext();) { Announcement announcement = it2.next(); for (Iterator it3 = announcement .listInstances().iterator(); it3.hasNext();) { InstanceDescription announcedInstance = it3.next(); if (announcedInstance.getSlingId().equals( instanceDescription.getSlingId())) { parentAnnouncement = announcement; break; } } } final String oddEven = odd ? "odd" : "even"; if (current && (inLocalCluster || (parentAnnouncement!=null))) { pw.println(""); } else { pw.println(""); } final boolean isLocal = instanceDescription.isLocal(); final String slingId = instanceDescription.getSlingId(); pw.print(""); if ( isLocal) { pw.print(""); } pw.print(""); pw.print(slingId); pw.print(""); if ( isLocal) { pw.print(""); } pw.println(""); pw.println("" + (instanceDescription.getClusterView() == null ? "null" : instanceDescription.getClusterView().getId()) + ""); pw.println("" + (isLocal ? "true" : "false") + ""); pw.println("" + (instanceDescription.isLeader() ? "true" : "false") + ""); if (inLocalCluster) { pw.println("local"); pw.println("n/a"); } else { pw.println("remote"); if (parentAnnouncement != null) { pw.println("" + parentAnnouncement.getOwnerId() + ""); } else { pw.println("(changing)"); } } pw.println(""); } } /** * Render the outgoing topology connectors - including the header-div and table */ private void listOutgoingTopologyConnectors(final PrintWriter pw) { boolean odd = false; pw.println("
"); pw.println("Outgoing topology connectors"); pw.println("
"); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); // pw.println(""); pw.println(""); pw.println(""); pw.println(""); Collection outgoingConnections = connectorRegistry .listOutgoingConnectors(); for (Iterator it = outgoingConnections .iterator(); it.hasNext();) { TopologyConnectorClientInformation topologyConnectorClient = it .next(); final String oddEven = odd ? "odd" : "even"; odd = !odd; final String remoteSlingId = topologyConnectorClient.getRemoteSlingId(); final boolean isConnected = topologyConnectorClient.isConnected() && remoteSlingId != null; final boolean autoStopped = topologyConnectorClient.isAutoStopped(); final boolean representsLoop = topologyConnectorClient.representsLoop(); if (isConnected || autoStopped || representsLoop) { pw.println(""); } else { pw.println(""); } pw.println(""); if (autoStopped) { pw.println(""); pw.println(""); } else if (isConnected && !representsLoop) { pw.println(""); pw.println(""); } else if (representsLoop) { pw.println(""); pw.println(""); } else { final int statusCode = topologyConnectorClient.getStatusCode(); final String statusDetails = topologyConnectorClient.getStatusDetails(); final String tooltipText; switch(statusCode) { case HttpServletResponse.SC_UNAUTHORIZED: tooltipText = HttpServletResponse.SC_UNAUTHORIZED + ": possible setup issue of discovery.impl on target instance, or wrong URL"; break; case HttpServletResponse.SC_NOT_FOUND: tooltipText = HttpServletResponse.SC_NOT_FOUND + ": possible white list rejection by target instance"; break; case -1: tooltipText = "-1: check error log. possible connection refused."; break; default: tooltipText = null; } final String tooltip = tooltipText==null ? "" : (" title=\""+tooltipText+"\""); pw.println(""); pw.println("not ok (HTTP Status-Code: "+statusCode+", "+statusDetails+")"); } pw.println(""); pw.println(""); pw.println(""); pw.println(""); // //TODO fallback urls are not yet implemented! // String fallbackConnectorUrls; // List urls = topologyConnectorClient // .listFallbackConnectorUrls(); // if (urls == null || urls.size() == 0) { // fallbackConnectorUrls = "n/a"; // } else { // fallbackConnectorUrls = ""; // for (Iterator it2 = urls.iterator(); it2.hasNext();) { // String aFallbackConnectorUrl = it2.next(); // fallbackConnectorUrls = fallbackConnectorUrls // + aFallbackConnectorUrl + "
"; // } // } // pw.println("
"); } pw.println(""); pw.println("
Connector urlConnected to slingIdConnector statusLast heartbeat Next heartbeat Request encoding Response encoding Fallback connector urls
" + topologyConnectorClient.getConnectorUrl().toString() + "auto-stoppedauto-stopped due to local-loop" + remoteSlingId + "ok, in use" + remoteSlingId + "ok, unused (loop or duplicate): standbynot connected"+beautifiedTimeDiff(topologyConnectorClient.getLastPingSent())+""+beautifiedDueTime(topologyConnectorClient.getNextPingDue())+""+topologyConnectorClient.getLastRequestEncoding()+""+topologyConnectorClient.getLastResponseEncoding()+"" + fallbackConnectorUrls + "
"); } private String beautifiedDueTime(long secondsDue) { if (secondsDue<-1) { return "overdue"; } else if (secondsDue<=0) { return "now-ish"; } else if (secondsDue==1) { return "in 1 second"; } else { int minsDue = (int) (secondsDue / 60); if (minsDue<5) { return "in "+secondsDue+" seconds"; } else { return "in "+minsDue+" minutes"; } } } private String beautifiedTimeDiff(long heartbeatTime) { final long diff = System.currentTimeMillis() - heartbeatTime; long seconds = (diff/1000); if (heartbeatTime<=0) { return "n/a"; } else if (seconds==0) { return diff+" millis ago"; } else if (seconds==1) { return "1 second ago"; } else if (seconds<300) { // then print seconds return seconds+" seconds ago"; } else { // then print seconds return (seconds/60)+" minute ago"; } } /** * Render the incoming topology connectors - including the header-div and table */ private void listIncomingTopologyConnectors(final PrintWriter pw) { boolean odd = false; pw.println("
"); pw.println("Incoming topology connectors"); pw.println("
"); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); pw.println(""); Collection incomingConnections = announcementRegistry.listLocalIncomingAnnouncements(); for (Iterator it = incomingConnections.iterator(); it .hasNext();) { CachedAnnouncement incomingCachedAnnouncement = it.next(); Announcement incomingAnnouncement = incomingCachedAnnouncement.getAnnouncement(); String oddEven = odd ? "odd" : "even"; odd = !odd; pw.println(""); pw.println(""); if (incomingAnnouncement.getServerInfo() != null) { pw.println(""); } else { pw.println(""); } pw.println(""); pw.println(""); pw.println(""); } pw.println(""); pw.println("
Owner slingIdServer infoLast heartbeatTimeout
" + incomingAnnouncement.getOwnerId() + "" + incomingAnnouncement.getServerInfo() + "n/a"+beautifiedTimeDiff(incomingCachedAnnouncement.getLastPing())+""+beautifiedDueTime(incomingCachedAnnouncement.getSecondsUntilTimeout())+"
"); pw.println("
"); pw.println("
"); } /** * keep a truncated history of the log events for information purpose (to be shown in the webconsole) */ @Override public void handleTopologyEvent(final TopologyEvent event) { if (event.getType() == Type.PROPERTIES_CHANGED) { this.currentView = event.getNewView(); Set newInstances = event.getNewView() .getInstances(); StringBuilder sb = new StringBuilder(); for (Iterator it = newInstances.iterator(); it .hasNext();) { final InstanceDescription newInstanceDescription = it.next(); InstanceDescription oldInstanceDescription = findInstance( event.getOldView(), newInstanceDescription.getSlingId()); if (oldInstanceDescription == null) { logger.error("handleTopologyEvent: got a property changed but did not find instance " + newInstanceDescription + " in oldview.. event=" + event); addEventLog(event.getType(), event.getType().toString()); return; } Map oldProps = oldInstanceDescription .getProperties(); Map newProps = newInstanceDescription .getProperties(); StringBuilder diff = diff(oldProps, newProps); if (diff.length() > 0) { if (sb.length() != 0) { sb.append(", "); } sb.append("on instance " + newInstanceDescription.getSlingId() + (newInstanceDescription.isLeader() ? " [isLeader]" : "") + ": " + diff); } } addEventLog(event.getType(), sb.toString()); } else if (event.getType() == Type.TOPOLOGY_INIT) { this.currentView = event.getNewView(); StringBuilder details = new StringBuilder(); for (Iterator it = event.getNewView() .getInstances().iterator(); it.hasNext();) { InstanceDescription newInstance = it.next(); if (details.length() != 0) { details.append(", "); } details.append(newInstance.getSlingId()); if (newInstance.isLeader()) { details.append(" [isLeader]"); } } addEventLog(event.getType(), "view: " + shortViewInfo(event.getNewView()) + ". " + details); } else if (event.getType() == Type.TOPOLOGY_CHANGING) { this.currentView = event.getOldView(); addEventLog(event.getType(), "old view: " + shortViewInfo(event.getOldView())); } else { this.currentView = event.getNewView(); if (event.getOldView() == null) { addEventLog(event.getType(), "new view: " + shortViewInfo(event.getNewView())); } else { StringBuilder details = new StringBuilder(); for (Iterator it = event.getNewView() .getInstances().iterator(); it.hasNext();) { InstanceDescription newInstance = it.next(); if (findInstance(event.getOldView(), newInstance.getSlingId()) == null) { if (details.length() != 0) { details.append(", "); } details.append(newInstance.getSlingId() + " joined"); } } for (Iterator it = event.getOldView() .getInstances().iterator(); it.hasNext();) { InstanceDescription oldInstance = it.next(); if (findInstance(event.getNewView(), oldInstance.getSlingId()) == null) { if (details.length() != 0) { details.append(", "); } details.append(oldInstance.getSlingId() + " left"); } } final InstanceDescription li = event.getNewView().getLocalInstance(); if (li!=null) { ClusterView clusterView = li.getClusterView(); if (clusterView!=null) { final InstanceDescription leader = clusterView.getLeader(); if (leader!=null) { if (details.length() !=0) { details.append(", "); } details.append("[isLeader: "+leader.getSlingId()+"]"); } } } addEventLog( event.getType(), "old view: " + shortViewInfo(event.getOldView()) + ", new view: " + shortViewInfo(event.getNewView()) + ". " + details); } } } /** * find a particular instance in the topology */ private InstanceDescription findInstance(final TopologyView view, final String slingId) { Set foundInstances = view .findInstances(new InstanceFilter() { @Override public boolean accept(InstanceDescription instance) { return instance.getSlingId().equals(slingId); } }); if (foundInstances.size() == 1) { return foundInstances.iterator().next(); } else { return null; } } /** * add a log entry and truncate the log entries if necessary */ private synchronized void addEventLog(final Type type, final String info) { final String dateStr = sdf.format(Calendar.getInstance().getTime()); final String logEntry = dateStr + ": " + type + ". " + info; if (type == Type.PROPERTIES_CHANGED) { propertyChangeLog.add(logEntry); while (propertyChangeLog.size() > 12) { propertyChangeLog.remove(0); } } else { topologyLog.add(logEntry); while (topologyLog.size() > 12) { topologyLog.remove(0); } } } /** * compile a short information string of the topology, including * number of clusters and instances */ private String shortViewInfo(final TopologyView view) { int clusters = view.getClusterViews().size(); int instances = view.getInstances().size(); return ((clusters == 1) ? "1 cluster" : clusters + " clusters") + ", " + ((instances == 1) ? "1 instance" : instances + " instances"); } /** * calculate the difference between two sets of properties */ private StringBuilder diff(final Map oldProps, final Map newProps) { final Set oldKeys = new HashSet(oldProps.keySet()); final Set newKeys = new HashSet(newProps.keySet()); StringBuilder sb = new StringBuilder(); for (Iterator it = oldKeys.iterator(); it.hasNext();) { String oldKey = it.next(); if (newKeys.contains(oldKey)) { if (oldProps.get(oldKey).equals(newProps.get(oldKey))) { // perfect } else { sb.append("(" + oldKey + " changed from " + oldProps.get(oldKey) + " to " + newProps.get(oldKey) + ")"); } newKeys.remove(oldKey); } else { sb.append("(" + oldKey + " was removed)"); } it.remove(); } for (Iterator it = newKeys.iterator(); it.hasNext();) { String newKey = it.next(); sb.append("(" + newKey + " was added)"); } return sb; } public void printConfiguration( final PrintWriter pw ) { final TopologyView topology = this.currentView; pw.println(TITLE); pw.println("---------------------------------------"); pw.println(); if ( topology == null ) { pw.println("No topology available yet!"); return; } pw.print("Topology"); if (!topology.isCurrent()) { pw.print(" CHANGING! (the view is no longer current!)"); } pw.println(); pw.println(); final Set clusters = topology.getClusterViews(); final ClusterView myCluster = topology.getLocalInstance().getClusterView(); printCluster(pw, myCluster, myCluster); for (Iterator it = clusters.iterator(); it.hasNext();) { ClusterView clusterView = it.next(); if (clusterView.equals(myCluster)) { // skip - I already rendered that continue; } printCluster(pw, clusterView, myCluster); } pw.println(); pw.println(); final Collection incomingConnections = announcementRegistry.listLocalIncomingAnnouncements(); if ( incomingConnections.size() > 0 ) { pw.println("Incoming topology connectors"); pw.println("---------------------------------------"); for(final CachedAnnouncement incomingCachedAnnouncement : incomingConnections) { Announcement incomingAnnouncement = incomingCachedAnnouncement.getAnnouncement(); pw.print("Owner Sling Id : "); pw.print(incomingAnnouncement.getOwnerId()); pw.println(); if (incomingAnnouncement.getServerInfo() != null) { pw.print("Server Info : "); pw.print(incomingAnnouncement.getServerInfo()); pw.println(); } pw.println("Last heartbeat received : "+beautifiedTimeDiff(incomingCachedAnnouncement.getLastPing())); pw.println("Timeout : "+beautifiedDueTime(incomingCachedAnnouncement.getSecondsUntilTimeout())); pw.println(); } pw.println(); pw.println(); } final Collection outgoingConnections = connectorRegistry.listOutgoingConnectors(); if ( outgoingConnections.size() > 0 ) { pw.println("Outgoing topology connectors"); pw.println("---------------------------------------"); for(final TopologyConnectorClientInformation topologyConnectorClient : outgoingConnections) { final String remoteSlingId = topologyConnectorClient.getRemoteSlingId(); final boolean autoStopped = topologyConnectorClient.isAutoStopped(); final boolean isConnected = topologyConnectorClient.isConnected() && remoteSlingId != null; pw.print("Connector URL : "); pw.print(topologyConnectorClient.getConnectorUrl()); pw.println(); if (autoStopped) { pw.println("Conncted to Sling Id : auto-stopped"); pw.println("Connector status : auto-stopped due to local-loop"); } else if (isConnected && !topologyConnectorClient.representsLoop()) { pw.print("Connected to Sling Id : "); pw.println(remoteSlingId); pw.println("Connector status : ok, in use"); } else if (topologyConnectorClient.representsLoop()) { pw.print("Connected to Sling Id : "); pw.println(remoteSlingId); pw.println("Connector status : ok, unused (loop or duplicate): standby"); } else { final int statusCode = topologyConnectorClient.getStatusCode(); final String statusDetails = topologyConnectorClient.getStatusDetails(); final String tooltipText; switch(statusCode) { case HttpServletResponse.SC_UNAUTHORIZED: tooltipText = HttpServletResponse.SC_UNAUTHORIZED + ": possible setup issue of discovery.impl on target instance, or wrong URL"; break; case HttpServletResponse.SC_NOT_FOUND: tooltipText = HttpServletResponse.SC_NOT_FOUND + ": possible white list rejection by target instance"; break; case -1: tooltipText = "-1: check error log. possible connection refused."; break; default: tooltipText = null; } pw.println("Connected to Sling Id : not connected"); pw.print("Connector status : not ok"); if ( tooltipText != null ) { pw.print(" ("); pw.print(tooltipText); pw.print(")"); } pw.print(" (HTTP StatusCode: "+statusCode+", "+statusDetails+")"); pw.println(); pw.println("Last heartbeat sent : "+beautifiedTimeDiff(topologyConnectorClient.getLastPingSent())); pw.println("Next heartbeat due : "+beautifiedDueTime(topologyConnectorClient.getNextPingDue())); } pw.println(); } pw.println(); pw.println(); } if ( topologyLog.size() > 0 ) { pw.println("Topology Change History"); pw.println("---------------------------------------"); for(final String aLogEntry : topologyLog) { pw.println(aLogEntry); } pw.println(); pw.println(); } if ( propertyChangeLog.size() > 0 ) { pw.println("Property Change History"); pw.println("---------------------------------------"); for(final String aLogEntry : propertyChangeLog) { pw.println(aLogEntry); } pw.println(); } pw.println("SyncTokenService History"); pw.println("---------------------------------------"); if (!config.useSyncTokenService()) { pw.println("(disabled - useSyncTokenService flag is false)"); } else if (syncTokenService == null) { pw.println("(no SyncTokenService available)"); } else { ClusterSyncHistory clusterSyncHistory = syncTokenService.getClusterSyncHistory(); if (clusterSyncHistory == null) { pw.println("(no history available)"); } else { for (String syncHistoryEntry : clusterSyncHistory.getSyncHistory()) { pw.println(syncHistoryEntry); } } } pw.println(); pw.println(); } /** * Render a particular cluster */ private void printCluster(final PrintWriter pw, final ClusterView renderCluster, final ClusterView localCluster) { final Collection announcements = announcementRegistry.listAnnouncementsInSameCluster(localCluster); for(final InstanceDescription instanceDescription : renderCluster.getInstances() ) { final boolean inLocalCluster = renderCluster == localCluster; Announcement parentAnnouncement = null; for (Iterator it2 = announcements.iterator(); it2 .hasNext();) { Announcement announcement = it2.next(); for (Iterator it3 = announcement .listInstances().iterator(); it3.hasNext();) { InstanceDescription announcedInstance = it3.next(); if (announcedInstance.getSlingId().equals( instanceDescription.getSlingId())) { parentAnnouncement = announcement; break; } } } final boolean isLocal = instanceDescription.isLocal(); final String slingId = instanceDescription.getSlingId(); pw.print("Sling ID : "); pw.print(slingId); pw.println(); pw.print("Cluster View ID : "); pw.print(instanceDescription.getClusterView() == null ? "null" : instanceDescription.getClusterView().getId()); pw.println(); pw.print("Local instance : "); pw.print(isLocal); pw.println(); pw.print("Leader instance : "); pw.print(instanceDescription.isLeader()); pw.println(); pw.print("In local cluster : "); if (inLocalCluster) { pw.print("local"); } else { pw.print("remote"); } pw.println(); pw.print("Announced by : "); if (inLocalCluster) { pw.print("n/a"); } else { if (parentAnnouncement != null) { pw.print(parentAnnouncement.getOwnerId()); } else { pw.print("(changing)"); } } pw.println(); pw.println("Properties:"); for(final Map.Entry entry : instanceDescription.getProperties().entrySet()) { pw.print("- "); pw.print(entry.getKey()); pw.print(" : "); pw.print(entry.getValue()); pw.println(); } pw.println(); pw.println(); } } }