com.continuuity.loom.layout.ClusterLayoutUpdater.java Source code

Java tutorial

Introduction

Here is the source code for com.continuuity.loom.layout.ClusterLayoutUpdater.java

Source

/*
 * Copyright 2012-2014, Continuuity, Inc.
 *
 * Licensed 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 com.continuuity.loom.layout;

import com.continuuity.loom.admin.Constraints;
import com.continuuity.loom.admin.ServiceConstraint;
import com.continuuity.loom.cluster.Cluster;
import com.continuuity.loom.cluster.Node;
import com.continuuity.loom.layout.change.AddServiceChangeIterator;
import com.continuuity.loom.layout.change.ClusterLayoutChange;
import com.continuuity.loom.layout.change.ClusterLayoutTracker;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;

/**
 * Class that takes in an existing cluster and a request to update the cluster in some way, whether its by adding
 * services, removing services, adding nodes, or removing nodes.
 */
public class ClusterLayoutUpdater {
    private static final ServiceMaxComparator serviceComparator = new ServiceMaxComparator();

    public ClusterLayoutTracker addServicesToCluster(Cluster cluster, Set<Node> clusterNodes,
            Set<String> servicesToAdd) throws Exception {
        Preconditions.checkArgument(cluster != null, "Cannot add services to a nonexistant cluster.");
        Preconditions.checkArgument(clusterNodes != null && !clusterNodes.isEmpty(),
                "Cannot add services to nonexistant nodes.");

        Constraints clusterConstraints = cluster.getClusterTemplate().getConstraints();
        ClusterLayout clusterLayout = ClusterLayout.fromNodes(clusterNodes, clusterConstraints);

        Set<String> servicesToAddCopy = Sets.newHashSet(servicesToAdd);
        SortedSet<Map.Entry<String, ServiceConstraint>> sortedConstraints = Sets.newTreeSet(serviceComparator);
        sortedConstraints.addAll(clusterConstraints.getServiceConstraints().entrySet());
        Queue<String> sortedServices = Lists.newLinkedList();
        for (Map.Entry<String, ServiceConstraint> entry : sortedConstraints) {
            if (servicesToAddCopy.contains(entry.getKey())) {
                sortedServices.add(entry.getKey());
                servicesToAddCopy.remove(entry.getKey());
            }
        }
        // any service without a constraint has no limit on the number of nodes it can be placed on, so add them to the end
        sortedServices.addAll(servicesToAddCopy);

        ClusterLayoutTracker tracker = new ClusterLayoutTracker(clusterLayout);
        return canAddServicesToCluster(tracker, sortedServices) ? tracker : null;
    }

    private boolean canAddServicesToCluster(ClusterLayoutTracker tracker, Queue<String> servicesToAdd) {
        if (servicesToAdd.isEmpty()) {
            return true;
        }

        String service = servicesToAdd.remove();
        ClusterLayout currentLayout = tracker.getCurrentLayout();
        // find valid moves, where a move is adding some number of the first service in the queue to nodes in the cluster
        Iterator<ClusterLayoutChange> changes = new AddServiceChangeIterator(currentLayout, service);

        while (changes.hasNext()) {
            // expand the cluster
            ClusterLayoutChange change = changes.next();
            if (tracker.addChangeIfValid(change)) {
                // though the change was applied, the layout may not satisfy all constraints
                ClusterLayout nextLayout = tracker.getCurrentLayout();
                if (!nextLayout.isValid()) {
                    // if constraints were not all satisfied, remove the last change and keep searching
                    tracker.removeLastChange();
                    continue;
                }

                // successfully added the service. See if we can add the rest of the services.
                if (canAddServicesToCluster(tracker, servicesToAdd)) {
                    return true;
                } else {
                    // we were not able to add the rest of the services. Move on to the next change for this service.
                    tracker.removeLastChange();
                }
            }
        }
        return false;
    }

    /**
     * Comparator to sort services constraints so that services are sorted first by their max count (lower max count means
     * the constraint is lower), sorted next by their min count (higher min count means the constraint is lower), and
     * finally by the service name if all else is equal.
     */
    private static class ServiceMaxComparator implements Comparator<Map.Entry<String, ServiceConstraint>> {

        @Override
        public int compare(Map.Entry<String, ServiceConstraint> entry1,
                Map.Entry<String, ServiceConstraint> entry2) {
            ServiceConstraint constraint1 = entry1.getValue();
            ServiceConstraint constraint2 = entry2.getValue();
            if (constraint1 == null && constraint2 != null) {
                return 1;
            } else if (constraint1 != null && constraint2 == null) {
                return -1;
            } else if (constraint1 != null) {
                int compare = ((Integer) constraint1.getMaxCount()).compareTo(constraint2.getMaxCount());
                if (compare != 0) {
                    return compare;
                }
                compare = 0 - ((Integer) constraint1.getMinCount()).compareTo(constraint2.getMinCount());
                if (compare != 0) {
                    return compare;
                }
            }
            return entry1.getKey().compareTo(entry2.getKey());
        }
    }
}