org.apache.brooklyn.location.multi.MultiLocation.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.location.multi.MultiLocation.java

Source

/*
 * 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.brooklyn.location.multi;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.location.LocationSpec;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.MachineProvisioningLocation;
import org.apache.brooklyn.api.location.NoMachinesAvailableException;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.location.AbstractLocation;
import org.apache.brooklyn.core.location.cloud.AbstractAvailabilityZoneExtension;
import org.apache.brooklyn.core.location.cloud.AvailabilityZoneExtension;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.exceptions.CompoundRuntimeException;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.text.Strings;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;

/** A location which consists of multiple locations stitched together to form availability zones.
 * The first location will be used by default, but if an {@link AvailabilityZoneExtension}-aware entity
 * is used, it may stripe across each of the locations.  See notes at {@link AvailabilityZoneExtension}. */
public class MultiLocation<T extends MachineLocation> extends AbstractLocation
        implements MachineProvisioningLocation<T> {

    @SuppressWarnings("serial")
    @SetFromFlag("subLocations")
    public static final ConfigKey<List<MachineProvisioningLocation<?>>> SUB_LOCATIONS = ConfigKeys
            .newConfigKey(new TypeToken<List<MachineProvisioningLocation<?>>>() {
            }, "subLocations", "The sub-machines that this location can delegate to");

    @Override
    public void init() {
        super.init();
        List<MachineProvisioningLocation<?>> subLocs = getSubLocations();
        checkState(subLocs.size() >= 1, "sub-locations must not be empty");
        AvailabilityZoneExtension azExtension = new AvailabilityZoneExtensionImpl(getManagementContext(), subLocs);
        addExtension(AvailabilityZoneExtension.class, azExtension);
    }

    public T obtain() throws NoMachinesAvailableException {
        return obtain(MutableMap.of());
    }

    /** finds (creates) and returns a {@link MachineLocation}; 
     * this always tries the first sub-location, moving on the second and subsequent if the first throws {@link NoMachinesAvailableException}.
     * (if you want striping across locations, see notes in {@link AvailabilityZoneExtension}.) */
    @SuppressWarnings("unchecked")
    @Override
    public T obtain(Map<?, ?> flags) throws NoMachinesAvailableException {
        List<MachineProvisioningLocation<?>> sublocsList = getSubLocations();
        Iterator<MachineProvisioningLocation<?>> sublocs = sublocsList.iterator();
        List<NoMachinesAvailableException> errors = MutableList.of();
        while (sublocs.hasNext()) {
            try {
                return (T) sublocs.next().obtain(flags);
            } catch (NoMachinesAvailableException e) {
                errors.add(e);
            }
        }
        Exception wrapped;
        String msg;
        if (errors.size() > 1) {
            wrapped = new CompoundRuntimeException(
                    errors.size() + " sublocation exceptions, including: " + Exceptions.collapseText(errors.get(0)),
                    errors);
            msg = Exceptions.collapseText(wrapped);
        } else if (errors.size() == 1) {
            wrapped = errors.get(0);
            msg = wrapped.getMessage();
            if (Strings.isBlank(msg))
                msg = wrapped.toString();
        } else {
            msg = "no sub-locations set for this multi-location";
            wrapped = null;
        }
        throw new NoMachinesAvailableException("No machines available in any of the " + sublocsList.size()
                + " location" + Strings.s(sublocsList.size()) + " configured here: " + msg, wrapped);
    }

    public List<MachineProvisioningLocation<?>> getSubLocations() {
        return getRequiredConfig(SUB_LOCATIONS);
    }

    @SuppressWarnings("unchecked")
    @Override
    public MachineProvisioningLocation<T> newSubLocation(Map<?, ?> newFlags) {
        // TODO shouldn't have to copy config bag as it should be inherited (but currently it is not used inherited everywhere; just most places)
        return getManagementContext().getLocationManager()
                .createLocation(LocationSpec.create(getClass()).parent(this)
                        .configure(config().getLocalBag().getAllConfig()) // FIXME Should this just be inherited?
                        .configure(newFlags));
    }

    @SuppressWarnings("unchecked")
    @Override
    public void release(T machine) {
        ((MachineProvisioningLocation<T>) machine.getParent()).release(machine);
    }

    @Override
    public Map<String, Object> getProvisioningFlags(Collection<String> tags) {
        return Maps.<String, Object>newLinkedHashMap();
    }

    @SuppressWarnings("unchecked")
    protected MachineProvisioningLocation<T> firstSubLoc() {
        return (MachineProvisioningLocation<T>) Iterables.get(getSubLocations(), 0);
    }

    protected <K> K getRequiredConfig(ConfigKey<K> key) {
        return checkNotNull(getConfig(key), key.getName());
    }

    public static class AvailabilityZoneExtensionImpl extends AbstractAvailabilityZoneExtension
            implements AvailabilityZoneExtension {

        private final List<MachineProvisioningLocation<?>> subLocations;

        public AvailabilityZoneExtensionImpl(ManagementContext managementContext,
                List<MachineProvisioningLocation<?>> subLocations) {
            super(managementContext);
            this.subLocations = ImmutableList.copyOf(subLocations);
        }

        @Override
        protected List<Location> doGetAllSubLocations() {
            return ImmutableList.<Location>copyOf(subLocations);
        }

        @Override
        protected boolean isNameMatch(Location loc, Predicate<? super String> namePredicate) {
            return namePredicate.apply(loc.getDisplayName());
        }
    }
}