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

org.apache.sling.discovery.commons.providers.spi.base.OakBacklogClusterSyncService 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.commons.providers.spi.base;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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.commons.providers.BaseTopologyView;
import org.apache.sling.discovery.commons.providers.spi.ClusterSyncService;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferencePolicyOption;

/**
 * The OakBacklogClusterSyncService will wait until all instances
 * in the local cluster are no longer in any backlog state.
 */
@Component(service = { ClusterSyncService.class, OakBacklogClusterSyncService.class },
            property = {
                    Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
            })
public class OakBacklogClusterSyncService extends AbstractServiceWithBackgroundCheck implements ClusterSyncService {

    static enum BacklogStatus {
        UNDEFINED /* when there was an error retrieving the backlog status with oak */,
        HAS_BACKLOG /* when oak's discovery lite descriptor indicated that there is still some backlog */,
        NO_BACKLOG /* when oak's discovery lite descriptor declared we're backlog-free now */
    }

    @Reference(policyOption=ReferencePolicyOption.GREEDY)
    private IdMapService idMapService;

    @Reference(policyOption=ReferencePolicyOption.GREEDY)
    protected DiscoveryLiteConfig commonsConfig;

    @Reference(policyOption=ReferencePolicyOption.GREEDY)
    protected ResourceResolverFactory resourceResolverFactory;

    @Reference(policyOption=ReferencePolicyOption.GREEDY)
    protected SlingSettingsService settingsService;

    private ClusterSyncHistory consistencyHistory = new ClusterSyncHistory();

    public static OakBacklogClusterSyncService testConstructorAndActivate(
            final DiscoveryLiteConfig commonsConfig,
            final IdMapService idMapService,
            final SlingSettingsService settingsService,
            ResourceResolverFactory resourceResolverFactory) {
        OakBacklogClusterSyncService service = testConstructor(commonsConfig, idMapService, settingsService, resourceResolverFactory);
        service.activate();
        return service;
    }

    /**
     * for testing only!
     */
    public static OakBacklogClusterSyncService testConstructor(
            final DiscoveryLiteConfig commonsConfig,
            final IdMapService idMapService,
            final SlingSettingsService settingsService,
            ResourceResolverFactory resourceResolverFactory) {
        OakBacklogClusterSyncService service = new OakBacklogClusterSyncService();
        if (commonsConfig == null) {
            throw new IllegalArgumentException("commonsConfig must not be null");
        }
        if (resourceResolverFactory == null) {
            throw new IllegalArgumentException("resourceResolverFactory must not be null");
        }
        if (settingsService == null) {
            throw new IllegalArgumentException("settingsService must not be null");
        }
        service.commonsConfig = commonsConfig;
        service.resourceResolverFactory = resourceResolverFactory;
        service.idMapService = idMapService;
        service.settingsService = settingsService;
        return service;
    }

    @Activate
    protected void activate() {
        this.slingId = getSettingsService().getSlingId();
        logger.info("activate: activated with slingId="+slingId);
    }

    public void setConsistencyHistory(ClusterSyncHistory consistencyHistory) {
        this.consistencyHistory = consistencyHistory;
    }

    public ClusterSyncHistory getConsistencyHistory() {
        return consistencyHistory;
    }

    /** Get or create a ResourceResolver **/
    protected ResourceResolver getResourceResolver() throws LoginException {
        return resourceResolverFactory.getServiceResourceResolver(null);
    }

    @Override
    public void cancelSync() {
        cancelPreviousBackgroundCheck();
    }

    @Override
    public void sync(final BaseTopologyView view, final Runnable callback) {
        // cancel the previous backgroundCheck if it's still running
        cancelPreviousBackgroundCheck();

        // first do the wait-for-backlog part
        logger.info("sync: doing wait-for-backlog part for view="+view.toShortString());
        waitWhileBacklog(view, callback);
    }

    private void waitWhileBacklog(final BaseTopologyView view, final Runnable runnable) {
        // start backgroundChecking until the backlogStatus
        // is NO_BACKLOG
        startBackgroundCheck("OakBacklogClusterSyncService-backlog-waiting", new BackgroundCheck() {

            @Override
            public boolean check() {
                try {
                    if (!idMapService.isInitialized()) {
                        logger.info("waitWhileBacklog: could not initialize...");
                        consistencyHistory.addHistoryEntry(view, "could not initialize idMapService");
                        return false;
                    }
                } catch (Exception e) {
                    logger.error("waitWhileBacklog: could not initialized due to "+e, e);
                    consistencyHistory.addHistoryEntry(view, "got Exception while initializing idMapService ("+e+")");
                    return false;
                }
                BacklogStatus backlogStatus = getBacklogStatus(view);
                if (backlogStatus == BacklogStatus.NO_BACKLOG) {
                    logger.info("waitWhileBacklog: no backlog (anymore), done.");
                    consistencyHistory.addHistoryEntry(view, "no backlog (anymore)");
                    return true;
                } else {
                    logger.info("waitWhileBacklog: backlogStatus still "+backlogStatus);
                    // clear the cache to make sure to get the latest version in case something changed
                    idMapService.clearCache();
                    consistencyHistory.addHistoryEntry(view, "backlog status "+backlogStatus);
                    return false;
                }
            }
        }, runnable, getCommonsConfig().getClusterSyncServiceTimeoutMillis(), getCommonsConfig().getClusterSyncServiceIntervalMillis());
    }

    private BacklogStatus getBacklogStatus(BaseTopologyView view) {
        logger.trace("getBacklogStatus: start");
        ResourceResolver resourceResolver = null;
        try{
            resourceResolver = getResourceResolver();
            DiscoveryLiteDescriptor descriptor =
                    DiscoveryLiteDescriptor.getDescriptorFrom(resourceResolver);

            // backlog-free means:
            // 1) 'deactivating' must be empty
            //     (otherwise we indeed have a backlog)
            // 2) all active ids of the descriptor must have a mapping to slingIds
            //     (otherwise the init failed or is pending for some instance(s))
            // 3) all 'active' instances must be in the view
            //     (otherwise discovery lite might not yet consider
            //     an instance dead but discovery-service does)
            // instead what is fine from a backlog point of view
            // * instances in the view but listed as 'inactive'
            //     (this might be the case for just-started instances)
            // * instances in the view but not contained in the descriptor at all
            //     (this might be the case for just-started instances)

            int[] activeIds = descriptor.getActiveIds();
            int[] deactivatingIds = descriptor.getDeactivatingIds();
            // we're not worried about 'inactive' ones - as that could
            // be a larger list filled with legacy entries too
            // plus once the instance is inactive there's no need to
            // check anything further - that one is then backlog-free

            // 1) 'deactivating' must be empty
            if (deactivatingIds.length!=0) {
                logger.info("getBacklogStatus: there are deactivating instances: "+Arrays.toString(deactivatingIds));
                return BacklogStatus.HAS_BACKLOG;
            }

            ClusterView cluster = view.getLocalInstance().getClusterView();
            Set slingIds = new HashSet<>();
            for (InstanceDescription instance : cluster.getInstances()) {
                slingIds.add(instance.getSlingId());
            }

            for(int i=0; i getSyncHistory() {
        return consistencyHistory.getSyncHistory();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy