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

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

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.sling.discovery.ClusterView;
import org.apache.sling.discovery.InstanceDescription;
import org.apache.sling.discovery.InstanceFilter;
import org.apache.sling.discovery.TopologyEvent.Type;
import org.apache.sling.discovery.commons.providers.BaseTopologyView;
import org.apache.sling.discovery.commons.providers.spi.LocalClusterView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Default Implementation of the topology view interface
 */
public class DefaultTopologyView extends BaseTopologyView {

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

    /** the instances that are part of this topology **/
    private final Set instances = new HashSet();

    private String localClusterSyncTokenId;

    /** Create a new empty topology **/
    public DefaultTopologyView() {
        // nothing to be initialized then
    }

    /** Create a new topology filled with the given list of instances **/
    public DefaultTopologyView(final Collection instances) {
        if (instances != null) {
            this.instances.addAll(instances);
        }
    }

    /**
     * Compare this topology with the given one and determine how they compare
     * @param other the other topology against which to compare
     * @return the type describing how these two compare
     * @see Type
     */
    public Type compareTopology(final DefaultTopologyView other) {
        if (other == null) {
            throw new IllegalArgumentException("other must not be null");
        }
        if ((localClusterSyncTokenId == null && other.localClusterSyncTokenId != null)
                || (other.localClusterSyncTokenId == null && localClusterSyncTokenId != null)
                || (localClusterSyncTokenId != null && !localClusterSyncTokenId.equals(other.localClusterSyncTokenId))) {
            logger.debug("compareTopology: different localClusterSyncTokenId");
            return Type.TOPOLOGY_CHANGED;
        }
        if (this.instances.size() != other.instances.size()) {
        	logger.debug("compareTopology: different number of instances");
            return Type.TOPOLOGY_CHANGED;
        }
        boolean propertiesChanged = false;
        for(final InstanceDescription instance : this.instances) {

            final Iterator it2 = other.instances.iterator();
            InstanceDescription matchingInstance = null;
            while (it2.hasNext()) {
                final InstanceDescription otherInstance = it2.next();
                if (instance.getSlingId().equals(otherInstance.getSlingId())) {
                    matchingInstance = otherInstance;
                    break;
                }
            }
            if (matchingInstance == null) {
            	if (logger.isDebugEnabled()) {
	            	logger.debug("compareTopology: no matching instance found for {}", instance);
            	}
                return Type.TOPOLOGY_CHANGED;
            }
            if (!instance.getClusterView().getId()
                    .equals(matchingInstance.getClusterView().getId())) {
            	logger.debug("compareTopology: cluster view id does not match");
                return Type.TOPOLOGY_CHANGED;
            }
            if (!instance.isLeader()==matchingInstance.isLeader()) {
                logger.debug("compareTopology: leaders differ");
                return Type.TOPOLOGY_CHANGED;
            }
            if (!instance.getProperties().equals(
                    matchingInstance.getProperties())) {
                propertiesChanged = true;
            }
        }
        if (propertiesChanged) {
            return Type.PROPERTIES_CHANGED;
        } else {
            return null;
        }
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj == null || !(obj instanceof DefaultTopologyView)) {
            return false;
        }
        DefaultTopologyView other = (DefaultTopologyView) obj;
        if (this.isCurrent() != other.isCurrent()) {
            return false;
        }
        Type diff = compareTopology(other);
        return diff == null;
    }

    @Override
    public int hashCode() {
        int code = 0;
        for (Iterator it = instances.iterator(); it
                .hasNext();) {
            InstanceDescription instance = it.next();
            code += instance.hashCode();
        }
        return code;
    }

    /**
     * @see org.apache.sling.discovery.TopologyView#getLocalInstance()
     */
    public InstanceDescription getLocalInstance() {
        for (Iterator it = instances.iterator(); it
                .hasNext();) {
            InstanceDescription instance = it.next();
            if (instance.isLocal()) {
                return instance;
            }
        }
        return null;
    }

    /**
     * @see org.apache.sling.discovery.TopologyView#getInstances()
     */
    public Set getInstances() {
        return Collections.unmodifiableSet(instances);
    }

    /**
     * Adds the instances of the given ClusterView to this topology
     */
    public void setLocalClusterView(final LocalClusterView localClusterView) {
        if (localClusterView == null) {
            throw new IllegalArgumentException("localClusterView must not be null");
        }
        final List instances = localClusterView.getInstances();
        addInstances(instances);
        
        this.localClusterSyncTokenId = localClusterView.getLocalClusterSyncTokenId();
    }
    
    /**
     * Adds the given instances to this topology
     */
    public void addInstances(final Collection instances) {
        if (instances == null) {
            return;
        }
        outerLoop: for (Iterator it = instances.iterator(); it
                .hasNext();) {
            InstanceDescription instanceDescription = it.next();
            for (Iterator it2 = this.instances.iterator(); it2.hasNext();) {
                InstanceDescription existingInstance = it2.next();
                if (existingInstance.getSlingId().equals(instanceDescription.getSlingId())) {
                    // SLING-3726:
                    // while 'normal duplicate instances' are filtered out here correctly,
                    // 'hidden duplicate instances' that are added via this instanceDescription's
                    // cluster, are not caught.
                    // there is, however, no simple fix for this. Since the reason is 
                    // inconsistent state information in /var/discovery/impl - either
                    // due to stale-announcements (SLING-4139) - or by some manualy
                    // copying of data from one cluster to the next (which will also
                    // be cleaned up by SLING-4139 though)
                    // so the fix for avoiding duplicate instances is really SLING-4139
                    logger.info("addInstance: cannot add same instance twice: "
                            + instanceDescription);
                    continue outerLoop;
                }
            }
            this.instances.add(instanceDescription);
        }
    }

    /**
     * @see org.apache.sling.discovery.TopologyView#findInstances(org.apache.sling.discovery.InstanceFilter)
     */
    public Set findInstances(final InstanceFilter picker) {
        if (picker == null) {
            throw new IllegalArgumentException("picker must not be null");
        }
        Set result = new HashSet();
        for (Iterator it = instances.iterator(); it
                .hasNext();) {
            InstanceDescription instance = it.next();
            if (picker.accept(instance)) {
                result.add(instance);
            }
        }
        return result;
    }

    /**
     * @see org.apache.sling.discovery.TopologyView#getClusterViews()
     */
    public Set getClusterViews() {
        Set result = new HashSet();
        for (Iterator it = instances.iterator(); it
                .hasNext();) {
            InstanceDescription instance = it.next();
            ClusterView cluster = instance.getClusterView();
            if (cluster != null) {
                result.add(cluster);
            }
        }
        return new HashSet(result);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        try{
            boolean firstCluster = true;
            for (ClusterView clusterView : getClusterViews()) {
                if (!firstCluster) {
                    sb.append(", ");
                }
                firstCluster = false;
                sb.append("[clusterId=" + clusterView.getId() + ", instances=");
                boolean firstInstance = true;
                for (InstanceDescription id : clusterView.getInstances()) {
                    if (!firstInstance) {
                        sb.append(", ");
                    }
                    firstInstance = false;
                    sb.append("[id=" + id.getSlingId() + ", isLeader=" + id.isLeader() + 
                            ", isLocal=" + id.isLocal() + "]");
                }
                sb.append("]");
            }
        } catch(Exception e) {
            // paranoia fallback
            sb = new StringBuilder(instances.toString());
        }
        return "DefaultTopologyView[current=" + isCurrent() + ", num=" + instances.size() + ", instances="
                + sb + "]";
    }

    @Override
    public String getLocalClusterSyncTokenId() {
        if (localClusterSyncTokenId==null) {
            throw new IllegalStateException("no syncToken set");
        } else {
            return localClusterSyncTokenId;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy