void ping()

in src/main/java/org/apache/sling/discovery/base/connectors/ping/TopologyConnectorClient.java [142:341]


    void ping(final boolean force) {
    	if (autoStopped) {
    		// then we suppress any further pings!
    		logger.debug("ping: autoStopped=true, hence suppressing any further pings.");
    		return;
    	}
    	if (force) {
    	    backoffPeriodEnd = -1;
    	} else if (backoffPeriodEnd>0) {
    	    if (System.currentTimeMillis()<backoffPeriodEnd) {
    	        logger.debug("ping: not issueing a heartbeat due to backoff instruction from peer.");
    	        return;
    	    } else {
                logger.debug("ping: backoff period ended, issuing another ping now.");
    	    }
    	}
        final String uri = connectorUrl.toString()+"."+clusterViewService.getSlingId()+".json";
    	if (logger.isDebugEnabled()) {
    		logger.debug("ping: connectorUrl=" + connectorUrl + ", complete uri=" + uri);
    	}
    	final HttpClientContext clientContext = HttpClientContext.create();
    	final CloseableHttpClient httpClient = createHttpClient();
    	final HttpPut putRequest = new HttpPut(uri);

    	// setting the connection timeout (idle connection, configured in seconds)
    	putRequest.setConfig(RequestConfig.
    			custom().
    			setConnectTimeout(1000*config.getSocketConnectTimeout()).
    			build());

        Announcement resultingAnnouncement = null;
        try {
            String userInfo = connectorUrl.getUserInfo();
            if (userInfo != null) {
                Credentials c = new UsernamePasswordCredentials(userInfo);
            	clientContext.getCredentialsProvider().setCredentials(
                        new AuthScope(putRequest.getURI().getHost(), putRequest
                                .getURI().getPort()), c);
            }

            Announcement topologyAnnouncement = new Announcement(
                    clusterViewService.getSlingId());
            topologyAnnouncement.setServerInfo(serverInfo);
            final ClusterView clusterView;
            try {
                clusterView = clusterViewService
                        .getLocalClusterView();
            } catch (UndefinedClusterViewException e) {
                // SLING-5030 : then we cannot ping
                logger.warn("ping: no clusterView available at the moment, cannot ping others now: "+e);
                return;
            }
            topologyAnnouncement.setLocalCluster(clusterView);
            if (force) {
                logger.debug("ping: sending a resetBackoff");
                topologyAnnouncement.setResetBackoff(true);
            }
            announcementRegistry.addAllExcept(topologyAnnouncement, clusterView, new AnnouncementFilter() {
                
                public boolean accept(final String receivingSlingId, final Announcement announcement) {
                    // filter out announcements that are of old cluster instances
                    // which I dont really have in my cluster view at the moment
                    final Iterator<InstanceDescription> it = 
                            clusterView.getInstances().iterator();
                    while(it.hasNext()) {
                        final InstanceDescription instance = it.next();
                        if (instance.getSlingId().equals(receivingSlingId)) {
                            // then I have the receiving instance in my cluster view
                            // all fine then
                            return true;
                        }
                    }
                    // looks like I dont have the receiving instance in my cluster view
                    // then I should also not propagate that announcement anywhere
                    return false;
                }
            });
            final String p = requestValidator.encodeMessage(topologyAnnouncement.asJSON());
            
            if (logger.isDebugEnabled()) {
                logger.debug("ping: topologyAnnouncement json is: " + p);
            }
            requestValidator.trustMessage(putRequest, p);
            if (config.isGzipConnectorRequestsEnabled()) {
                // tell the server that the content is gzipped:
                putRequest.addHeader("Content-Encoding", "gzip");
                // and gzip the body:
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                final GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
                gzipOut.write(p.getBytes("UTF-8"));
                gzipOut.close();
                final byte[] gzippedEncodedJson = baos.toByteArray();
                putRequest.setEntity(new ByteArrayEntity(gzippedEncodedJson, ContentType.APPLICATION_JSON));
                lastRequestEncoding = "gzip";
            } else {
                // otherwise plaintext:
            	final StringEntity plaintext = new StringEntity(p, "UTF-8");
            	plaintext.setContentType(ContentType.APPLICATION_JSON.getMimeType());
            	putRequest.setEntity(plaintext);
                lastRequestEncoding = "plaintext";
            }
            // independent of request-gzipping, we do accept the response to be gzipped,
            // so indicate this to the server:
            putRequest.addHeader("Accept-Encoding", "gzip");
            final CloseableHttpResponse response = httpClient.execute(putRequest, clientContext);
        	if (logger.isDebugEnabled()) {
	            logger.debug("ping: done. code=" + response.getStatusLine().getStatusCode() + " - "
	                    + response.getStatusLine().getReasonPhrase());
        	}
            lastStatusCode = response.getStatusLine().getStatusCode();
            lastResponseEncoding = null;
            if (response.getStatusLine().getStatusCode()==HttpServletResponse.SC_OK) {
                final Header contentEncoding = response.getFirstHeader("Content-Encoding");
                if (contentEncoding!=null && contentEncoding.getValue()!=null &&
                        contentEncoding.getValue().contains("gzip")) {
                    lastResponseEncoding = "gzip";
                } else {
                    lastResponseEncoding = "plaintext";
                }
                final String responseBody = requestValidator.decodeMessage(putRequest.getURI().getPath(), response); // limiting to 16MB, should be way enough
            	if (logger.isDebugEnabled()) {
            		logger.debug("ping: response body=" + responseBody);
            	}
                if (responseBody!=null && responseBody.length()>0) {
                    Announcement inheritedAnnouncement = Announcement
                            .fromJSON(responseBody);
                    final long backoffInterval = inheritedAnnouncement.getBackoffInterval();
                    if (backoffInterval>0) {
                        // then reset the backoffPeriodEnd:
                        
                        /* minus 1 sec to avoid slipping the interval by a few millis */
                        this.backoffPeriodEnd = System.currentTimeMillis() + (1000 * backoffInterval) - 1000;
                        logger.debug("ping: servlet instructed to backoff: backoffInterval="+backoffInterval+", resulting in period end of "+new Date(backoffPeriodEnd));
                    } else {
                        logger.debug("ping: servlet did not instruct any backoff-ing at this stage");
                        this.backoffPeriodEnd = -1;
                    }
                    if (inheritedAnnouncement.isLoop()) {
                    	if (logger.isDebugEnabled()) {
	                        logger.debug("ping: connector response indicated a loop detected. not registering this announcement from "+
	                                    inheritedAnnouncement.getOwnerId());
                    	}
                    	if (inheritedAnnouncement.getOwnerId().equals(clusterViewService.getSlingId())) {
                    		// SLING-3316 : local-loop detected. Check config to see if we should stop this connector
                    		
                    	    if (config.isAutoStopLocalLoopEnabled()) {
                    			inheritedAnnouncement = null; // results in connected -> false and representsloop -> true
                    			autoStopped = true; // results in isAutoStopped -> true
                    		}
                    	}
                    } else {
                        inheritedAnnouncement.setInherited(true);
                        if (announcementRegistry
                                .registerAnnouncement(inheritedAnnouncement)==-1) {
                        	if (logger.isDebugEnabled()) {
	                            logger.debug("ping: connector response is from an instance which I already see in my topology"
	                                    + inheritedAnnouncement);
                        	}
                            statusDetails = "receiving side is seeing me via another path (connector or cluster) already (loop)";
                            return;
                        }
                    }
                    resultingAnnouncement = inheritedAnnouncement;
                    statusDetails = null;
                } else {
                    statusDetails = "no response body received";
                }
            } else {
                statusDetails = "got HTTP Status-Code: "+lastStatusCode;
            }
        	// SLING-2882 : reset suppressPingWarnings_ flag in success case
    		suppressPingWarnings_ = false;
        } catch (IOException e) {
        	// SLING-2882 : set/check the suppressPingWarnings_ flag
        	if (suppressPingWarnings_) {
        		if (logger.isDebugEnabled()) {
        			logger.debug("ping: got IOException: " + e + ", uri=" + uri);
        		}
        	} else {
        		suppressPingWarnings_ = true;
    			logger.warn("ping: got IOException [suppressing further warns]: " + e + ", uri=" + uri);
        	}
            statusDetails = e.toString();
        } catch (JsonException e) {
            logger.warn("ping: got JSONException: " + e);
            statusDetails = e.toString();
        } catch (RuntimeException re) {
            logger.warn("ping: got RuntimeException: " + re, re);
            statusDetails = re.toString();
        } finally {
            putRequest.releaseConnection();
            lastInheritedAnnouncement = resultingAnnouncement;
            lastPingedAt = System.currentTimeMillis();
            try {
				httpClient.close();
			} catch (IOException e) {
				logger.error("disconnect: could not close httpClient: "+e, e);
			}
        }
    }