All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sling.discovery.oak.TopologyWebConsolePlugin Maven / Gradle / Ivy

/*
 * 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.oak;

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.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.WebConsoleConstants;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
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.InstancesDiff;
import org.apache.sling.discovery.commons.providers.spi.base.DiscoveryLiteDescriptor;
import org.apache.sling.discovery.commons.providers.spi.base.OakBacklogClusterSyncService;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.propertytypes.ServiceDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Simple webconsole which gives an overview of the topology visible by the
 * discovery service
 */
@Component(service = { TopologyEventListener.class, Servlet.class },
        property = {
                WebConsoleConstants.PLUGIN_LABEL+"="+TopologyWebConsolePlugin.LABEL,
                WebConsoleConstants.PLUGIN_TITLE+"="+TopologyWebConsolePlugin.TITLE,
                "felix.webconsole.category=Sling",
                "felix.webconsole.configprinter.modes={zip}"
        })
@ServiceDescription("Apache Sling Web Console Plugin to display Background servlets and ExecutionEngine status")
@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
    protected ResourceResolverFactory resourceResolverFactory;

    @Reference
    private OakBacklogClusterSyncService clusterSyncService;

    private TopologyView currentView;

    private List discoveryLiteHistory = new LinkedList();

    /**
     * keeps hold of the last DiscoveryLiteDescriptor that was added
     * to the discoveryLiteHistory - in order to de-duplicate as we go
     */
    private DiscoveryLiteDescriptor lastDiscoveryLiteDescriptor = null;

    @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 {
                pw.println("

No view available

"); pw.println("
"); pw.println("No TOPOLOGY_INIT received yet, therefore no view available yet."); } } 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() + "
"); } } protected ResourceResolver getResourceResolver() throws LoginException { return resourceResolverFactory.getServiceResourceResolver(null); } /** * 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.Oak 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("

Oak Discovery-Lite Descriptor History

"); updateDiscoveryLiteHistory(); pw.println("
");
        for (String discoLiteHistoryEntry : discoveryLiteHistory) {
            pw.println(discoLiteHistoryEntry);
        }
        pw.println("
"); pw.println("
"); pw.println("

ClusterSyncService History

"); pw.println("
");
        for (String syncHistoryEntry : clusterSyncService.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.oak 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 (getBundleContext() == null) { logger.info("handleTopologyEvent: ignoring event when not activated: " + event); return; } if (event.getType() == Type.PROPERTIES_CHANGED) { this.currentView = event.getNewView(); StringBuilder sb = new StringBuilder(); InstancesDiff instanceDiff = new InstancesDiff(event.getOldView(), event.getNewView()); // there shouldn't be any instances added, but for paranoia reason: Collection added = instanceDiff.added().get(); if (!added.isEmpty()) { sb.append("instances were added:"); for (InstanceDescription instance : added) { sb.append(" "); sb.append(instance.getSlingId()); } sb.append("."); } // there shouldn't be any instances removed as well, but again for paranoia reason: Collection removed = instanceDiff.removed().get(); if (!removed.isEmpty()) { sb.append("instances were removed:"); for (InstanceDescription instance : added) { sb.append(" "); sb.append(instance.getSlingId()); } sb.append("."); } Set newInstances = event.getNewView() .getInstances(); 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); sb.append("did not find instance in old view: " + newInstanceDescription.getSlingId() + "."); continue; } 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(), "details: "+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); } } updateDiscoveryLiteHistory(); } /** * 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 logEntry = getCurrentDateFormatted() + ": " + 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); } } } /** * if there's any change add a discoveryLite descriptor entry to the history, truncating if necessary */ private synchronized void updateDiscoveryLiteHistory() { ResourceResolver resourceResolver = null; try{ resourceResolver = getResourceResolver(); } catch(Exception e) { logger.error("addDiscoveryLiteHistoryEntry: Exception: "+e, e); return; } try { DiscoveryLiteDescriptor descriptor = DiscoveryLiteDescriptor.getDescriptorFrom(resourceResolver); if (lastDiscoveryLiteDescriptor!=null && descriptor.getDescriptorStr().equals(lastDiscoveryLiteDescriptor.getDescriptorStr())) { // de-duplication - then there's nothing to update return; } final String logEntry = getCurrentDateFormatted() + ": " + descriptor.getDescriptorStr(); lastDiscoveryLiteDescriptor = descriptor; discoveryLiteHistory.add(logEntry); while (discoveryLiteHistory.size() > 12) { discoveryLiteHistory.remove(0); } } catch(Exception e) { // SLING-10204 : log less noisy as this can legitimately happen logger.warn("addDiscoveryLiteHistoryEntry: got Exception (enable debug logging to see stacktrace) : " + e); logger.debug("addDiscoveryLiteHistoryEntry: Exception stacktrace", e); } finally { if (resourceResolver != null) { resourceResolver.close(); } } } /** * 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.oak 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("Oak Discovery-Lite Descriptor History"); pw.println("---------------------------------------"); updateDiscoveryLiteHistory(); for (String discoLiteHistoryEntry : discoveryLiteHistory) { pw.println(discoLiteHistoryEntry); } pw.println(); pw.println(); pw.println("ClusterSyncService History"); pw.println("---------------------------------------"); for (String syncHistoryEntry : clusterSyncService.getSyncHistory()) { pw.println(syncHistoryEntry); } pw.println(); pw.println(); } private String getCurrentDateFormatted() { return sdf.format(Calendar.getInstance().getTime()); } /** * 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(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy