<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.commons.providers.spi.base;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

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.SlingConstants;
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.api.resource.ValueMap;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.discovery.commons.providers.util.ResourceHelper;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;

/**
 * The IdMapService is responsible for storing a slingId-clusterNodeId
 * pair to the repository and given all other instances in the cluster
 * do the same can map clusterNodeIds to slingIds (or vice-versa)
 */
@Component(immediate = false)
@Service(value = { IdMapService.class })
public class IdMapService extends AbstractServiceWithBackgroundCheck implements EventHandler {

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private SlingSettingsService settingsService;

    @Reference
    private DiscoveryLiteConfig commonsConfig;
    
    private boolean initialized = false;
    
    private String slingId;

    private long me;

    private final Map<Integer, String> oldIdMapCache = new HashMap<Integer, String>();
    private final Map<Integer, String> idMapCache = new HashMap<Integer, String>();

    private long lastCacheInvalidation = -1;

    private BundleContext bundleContext;

    private ServiceRegistration eventHandlerRegistration;
    
    /** test-only constructor **/
    public static IdMapService testConstructor(
            DiscoveryLiteConfig commonsConfig,
            SlingSettingsService settingsService, 
            ResourceResolverFactory resourceResolverFactory) {
        IdMapService service = new IdMapService();
        service.commonsConfig = commonsConfig;
        service.settingsService = settingsService;
        service.resourceResolverFactory = resourceResolverFactory;
        service.activate(null);
        return service;
    }

    @Activate
    protected void activate(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
        registerEventHandler();
        
        startBackgroundCheck("IdMapService-initializer", new BackgroundCheck() {
            
            @Override
            public boolean check() {
                try {
                    return init();
                } catch (Exception e) {
                    logger.error("initializer: could not init due to "+e, e);
                    return false;
                }
            }
        }, null, -1, 1000 /* = 1sec interval */);
    }
    
    @Deactivate
    protected void deactivate() {
        if (eventHandlerRegistration != null) {
            eventHandlerRegistration.unregister();
            eventHandlerRegistration = null;
        }
    }
    
    private void registerEventHandler() {
        if (bundleContext == null) {
            logger.info("registerEventHandler: bundleContext is null - cannot register");
            return;
        }
        Dictionary<String,Object> properties = new Hashtable<String,Object>();
        properties.put(Constants.SERVICE_DESCRIPTION, "IdMap Change Listener.");
        String[] topics = new String[] {
                SlingConstants.TOPIC_RESOURCE_ADDED,
                SlingConstants.TOPIC_RESOURCE_CHANGED,
                SlingConstants.TOPIC_RESOURCE_REMOVED };
        properties.put(EventConstants.EVENT_TOPIC, topics);
        String path = getIdMapPath().endsWith("/") ? getIdMapPath() + "*" : getIdMapPath() + "/*";
        properties.put(EventConstants.EVENT_FILTER, "(&(path="+path+"))");
        eventHandlerRegistration = bundleContext.registerService(
                EventHandler.class.getName(), this, properties);
    }

    /** Get or create a ResourceResolver **/
    private ResourceResolver getResourceResolver() throws LoginException {
        return resourceResolverFactory.getAdministrativeResourceResolver(null);
    }
    
    public synchronized long getMyId() {
        if (!initialized) {
            return -1;
        }
        return me;<commit_msg>SLING-4603 : minor fix to previous commit : turns out the path must be /var/xy/idMap not /var/xy/idMap/* as that would apply to children only<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.commons.providers.spi.base;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

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.SlingConstants;
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.api.resource.ValueMap;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.discovery.commons.providers.util.ResourceHelper;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;

/**
 * The IdMapService is responsible for storing a slingId-clusterNodeId
 * pair to the repository and given all other instances in the cluster
 * do the same can map clusterNodeIds to slingIds (or vice-versa)
 */
@Component(immediate = false)
@Service(value = { IdMapService.class })
public class IdMapService extends AbstractServiceWithBackgroundCheck implements EventHandler {

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Reference
    private SlingSettingsService settingsService;

    @Reference
    private DiscoveryLiteConfig commonsConfig;
    
    private boolean initialized = false;
    
    private String slingId;

    private long me;

    private final Map<Integer, String> oldIdMapCache = new HashMap<Integer, String>();
    private final Map<Integer, String> idMapCache = new HashMap<Integer, String>();

    private long lastCacheInvalidation = -1;

    private BundleContext bundleContext;

    private ServiceRegistration eventHandlerRegistration;
    
    /** test-only constructor **/
    public static IdMapService testConstructor(
            DiscoveryLiteConfig commonsConfig,
            SlingSettingsService settingsService, 
            ResourceResolverFactory resourceResolverFactory) {
        IdMapService service = new IdMapService();
        service.commonsConfig = commonsConfig;
        service.settingsService = settingsService;
        service.resourceResolverFactory = resourceResolverFactory;
        service.activate(null);
        return service;
    }

    @Activate
    protected void activate(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
        registerEventHandler();
        
        startBackgroundCheck("IdMapService-initializer", new BackgroundCheck() {
            
            @Override
            public boolean check() {
                try {
                    return init();
                } catch (Exception e) {
                    logger.error("initializer: could not init due to "+e, e);
                    return false;
                }
            }
        }, null, -1, 1000 /* = 1sec interval */);
    }
    
    @Deactivate
    protected void deactivate() {
        if (eventHandlerRegistration != null) {
            eventHandlerRegistration.unregister();
            eventHandlerRegistration = null;
        }
    }
    
    private void registerEventHandler() {
        if (bundleContext == null) {
            logger.info("registerEventHandler: bundleContext is null - cannot register");
            return;
        }
        Dictionary<String,Object> properties = new Hashtable<String,Object>();
        properties.put(Constants.SERVICE_DESCRIPTION, "IdMap Change Listener.");
        String[] topics = new String[] {
                SlingConstants.TOPIC_RESOURCE_ADDED,
                SlingConstants.TOPIC_RESOURCE_CHANGED,
                SlingConstants.TOPIC_RESOURCE_REMOVED };
        properties.put(EventConstants.EVENT_TOPIC, topics);
        String path = getIdMapPath();
        if (path.endsWith("/")) {
            path = path.substring(0, path.length()-1);
        }
        properties.put(EventConstants.EVENT_FILTER, "(&(path="+path+"))");
        eventHandlerRegistration = bundleContext.registerService(
                EventHandler.class.getName(), this, properties);
    }

    /** Get or create a ResourceResolver **/
    private ResourceResolver getResourceResolver() throws LoginException {
        return resourceResolverFactory.getAdministrativeResourceResolver(null);
    }
    
    public synchronized long getMyId() {
        if (!initialized) {
            return -1;
        }
        return me;
{'fraction_non_alphanumeric': 0.08585944115156646, 'fraction_numerical': 0.002540220152413209, 'mean_word_length': 3.203202846975089, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, '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': '989525', 'n_tokens_mistral': 3023, 'n_tokens_neox': 2975, 'n_words': 819}