<commit_before>/*
 * 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.common.heartbeat;

import java.util.Calendar;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

import javax.jcr.Session;

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.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.scheduler.Scheduler;
import org.apache.sling.discovery.impl.Config;
import org.apache.sling.discovery.impl.DiscoveryServiceImpl;
import org.apache.sling.discovery.impl.cluster.voting.VotingHandler;
import org.apache.sling.discovery.impl.cluster.voting.VotingHelper;
import org.apache.sling.discovery.impl.cluster.voting.VotingView;
import org.apache.sling.discovery.impl.common.ViewHelper;
import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
import org.apache.sling.discovery.impl.topology.announcement.AnnouncementRegistry;
import org.apache.sling.discovery.impl.topology.connector.ConnectorRegistry;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.BundleException;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The heartbeat handler is responsible and capable of issuing both local and
 * remote heartbeats and registers a periodic job with the scheduler for doing so.
 * <p>
 * Local heartbeats are stored in the repository. Remote heartbeats are POSTs to
 * remote TopologyConnectorServlets.
 */
@Component
@Service(value = { HeartbeatHandler.class })
public class HeartbeatHandler implements Runnable {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /** the name used for the period job with the scheduler **/
    private static final String NAME = "discovery.impl.heartbeat.runner";

    @Reference
    private SlingSettingsService slingSettingsService;

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private ConnectorRegistry connectorRegistry;

    @Reference
    private AnnouncementRegistry announcementRegistry;

    @Reference
    private Scheduler scheduler;

    @Reference
    private Config config;

    @Reference
    private VotingHandler votingHandler;

    /** the discovery service reference is used to get properties updated before heartbeats are sent **/
    private DiscoveryServiceImpl discoveryService;

    /** the sling id of the local instance **/
    private String slingId;

    /** the id which is to be used for the next voting **/
    private String nextVotingId = UUID.randomUUID().toString();

    /** whether or not to reset the leaderElectionId at next heartbeat time **/
    private boolean resetLeaderElectionId = false;

    /** lock object for synchronizing the run method **/
    private final Object lock = new Object();
    
    /** SLING-2892: remember first heartbeat written to repository by this instance **/
    private long firstHeartbeatWritten = -1;
    
    /** SLING-2892: remember the value of the heartbeat this instance has written the last time **/
    private Calendar lastHeartbeatWritten = null;
    
    /** SLING-2895: avoid heartbeats after deactivation **/
    private boolean activated = false;
    
    /** SLING-2901: the runtimeId is a unique id, set on activation, used for robust duplicate sling.id detection **/
    private String runtimeId;
    
    /** keep a reference to the component context **/
    private ComponentContext context;
    
    @Activate
    protected void activate(ComponentContext context) {
    	synchronized(lock) {
    		this.context = context;
    		
	        slingId = slingSettingsService.getSlingId();
	        // on activate the resetLeaderElectionId is set to true to ensure that
	        // the 'leaderElectionId' property is reset on next heartbeat issuance.
	        // the idea being that a node which leaves the cluster should not
	        // become leader on next join - and by resetting the leaderElectionId
	        // to the current time, this is ensured.
	        resetLeaderElectionId = true;
	        runtimeId = UUID.randomUUID().toString();

	        // SLING-2895: reset variables to avoid unnecessary log.error
	        firstHeartbeatWritten = -1;
	        lastHeartbeatWritten = null;
	        
	        activated = true;
    	}
    }

    @Deactivate
    protected void deactivate() {
    	synchronized(lock) {
	    	activated = false;
    	}
    	scheduler.removeJob(NAME);
    }

    /**
     * The initialize method is called by the DiscoveryServiceImpl.activate
     * as we require the discoveryService (and the discoveryService has
     * a reference on us - but we cant have circular references in osgi).
     * <p>
     * The initialVotingId is used to avoid an unnecessary topologyChanged event
     * when switching form isolated to established view but with only the local
     * instance in the view.
     */
    public void initialize(final DiscoveryServiceImpl discoveryService,
            final String initialVotingId) {
        this.discoveryService = discoveryService;
        synchronized(lock) {
        	this.nextVotingId = initialVotingId;
            issueHeartbeat();
        }

        try {
            scheduler.addPeriodicJob(NAME, this,
                    null, config.getHeartbeatInterval(), false);
        } catch (Exception e) {
            logger.error("activate: Could not start heartbeat runner: " + e, e);
        }
    }<commit_msg>SLING-2945 : moving setting of discoveryService also under synchronized protection<commit_after>/*
 * 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.common.heartbeat;

import java.util.Calendar;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

import javax.jcr.Session;

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.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.scheduler.Scheduler;
import org.apache.sling.discovery.impl.Config;
import org.apache.sling.discovery.impl.DiscoveryServiceImpl;
import org.apache.sling.discovery.impl.cluster.voting.VotingHandler;
import org.apache.sling.discovery.impl.cluster.voting.VotingHelper;
import org.apache.sling.discovery.impl.cluster.voting.VotingView;
import org.apache.sling.discovery.impl.common.ViewHelper;
import org.apache.sling.discovery.impl.common.resource.ResourceHelper;
import org.apache.sling.discovery.impl.topology.announcement.AnnouncementRegistry;
import org.apache.sling.discovery.impl.topology.connector.ConnectorRegistry;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.BundleException;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The heartbeat handler is responsible and capable of issuing both local and
 * remote heartbeats and registers a periodic job with the scheduler for doing so.
 * <p>
 * Local heartbeats are stored in the repository. Remote heartbeats are POSTs to
 * remote TopologyConnectorServlets.
 */
@Component
@Service(value = { HeartbeatHandler.class })
public class HeartbeatHandler implements Runnable {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /** the name used for the period job with the scheduler **/
    private static final String NAME = "discovery.impl.heartbeat.runner";

    @Reference
    private SlingSettingsService slingSettingsService;

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private ConnectorRegistry connectorRegistry;

    @Reference
    private AnnouncementRegistry announcementRegistry;

    @Reference
    private Scheduler scheduler;

    @Reference
    private Config config;

    @Reference
    private VotingHandler votingHandler;

    /** the discovery service reference is used to get properties updated before heartbeats are sent **/
    private DiscoveryServiceImpl discoveryService;

    /** the sling id of the local instance **/
    private String slingId;

    /** the id which is to be used for the next voting **/
    private String nextVotingId = UUID.randomUUID().toString();

    /** whether or not to reset the leaderElectionId at next heartbeat time **/
    private boolean resetLeaderElectionId = false;

    /** lock object for synchronizing the run method **/
    private final Object lock = new Object();
    
    /** SLING-2892: remember first heartbeat written to repository by this instance **/
    private long firstHeartbeatWritten = -1;
    
    /** SLING-2892: remember the value of the heartbeat this instance has written the last time **/
    private Calendar lastHeartbeatWritten = null;
    
    /** SLING-2895: avoid heartbeats after deactivation **/
    private boolean activated = false;
    
    /** SLING-2901: the runtimeId is a unique id, set on activation, used for robust duplicate sling.id detection **/
    private String runtimeId;
    
    /** keep a reference to the component context **/
    private ComponentContext context;
    
    @Activate
    protected void activate(ComponentContext context) {
    	synchronized(lock) {
    		this.context = context;
    		
	        slingId = slingSettingsService.getSlingId();
	        // on activate the resetLeaderElectionId is set to true to ensure that
	        // the 'leaderElectionId' property is reset on next heartbeat issuance.
	        // the idea being that a node which leaves the cluster should not
	        // become leader on next join - and by resetting the leaderElectionId
	        // to the current time, this is ensured.
	        resetLeaderElectionId = true;
	        runtimeId = UUID.randomUUID().toString();

	        // SLING-2895: reset variables to avoid unnecessary log.error
	        firstHeartbeatWritten = -1;
	        lastHeartbeatWritten = null;
	        
	        activated = true;
    	}
    }

    @Deactivate
    protected void deactivate() {
    	synchronized(lock) {
	    	activated = false;
    	}
    	scheduler.removeJob(NAME);
    }

    /**
     * The initialize method is called by the DiscoveryServiceImpl.activate
     * as we require the discoveryService (and the discoveryService has
     * a reference on us - but we cant have circular references in osgi).
     * <p>
     * The initialVotingId is used to avoid an unnecessary topologyChanged event
     * when switching form isolated to established view but with only the local
     * instance in the view.
     */
    public void initialize(final DiscoveryServiceImpl discoveryService,
            final String initialVotingId) {
        synchronized(lock) {
        	this.discoveryService = discoveryService;
        	this.nextVotingId = initialVotingId;
            issueHeartbeat();
        }

        try {
            scheduler.addPeriodicJob(NAME, this,
                    null, config.getHeartbeatInterval(), false);
        } catch (Exception e) {
            logger.error("activate: Could not start heartbeat runner: " + e, e);
        }
    }
{'fraction_non_alphanumeric': 0.07733254472867071, 'fraction_numerical': 0.004435901227266006, 'mean_word_length': 3.4350819672131148, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3879163', 'n_tokens_mistral': 3658, 'n_tokens_neox': 3490, 'n_words': 1178}