org.apache.brooklyn.entity.group.DynamicFabricImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.entity.group.DynamicFabricImpl.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.entity.group;

import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.elvis;
import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.truth;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.core.effector.Effectors;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.factory.EntityFactory;
import org.apache.brooklyn.core.entity.factory.EntityFactoryForLocation;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.trait.Changeable;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.enricher.stock.Enrichers;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.groovy.GroovyJavaMethods;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

/**
 * When a dynamic fabric is started, it starts an entity in each of its locations. 
 * This entity will be the parent of each of the started entities. 
 */
public class DynamicFabricImpl extends AbstractGroupImpl implements DynamicFabric {
    private static final Logger logger = LoggerFactory.getLogger(DynamicFabricImpl.class);

    public DynamicFabricImpl() {
    }

    @Override
    public void init() {
        super.init();

        enrichers().add(Enrichers.builder().aggregating(Changeable.GROUP_SIZE).publishing(FABRIC_SIZE).fromMembers()
                .computingSum().valueToReportIfNoSensors(0).build());

        sensors().set(SERVICE_UP, false);
    }

    protected EntitySpec<?> getMemberSpec() {
        return getConfig(MEMBER_SPEC);
    }

    protected EntityFactory<?> getFactory() {
        return getConfig(FACTORY);
    }

    protected String getDisplayNamePrefix() {
        return getConfig(DISPLAY_NAME_PREFIX);
    }

    protected String getDisplayNameSuffix() {
        return getConfig(DISPLAY_NAME_SUFFIX);
    }

    @Override
    public void setMemberSpec(EntitySpec<?> memberSpec) {
        setConfigEvenIfOwned(MEMBER_SPEC, memberSpec);
    }

    @Override
    public void setFactory(EntityFactory<?> factory) {
        setConfigEvenIfOwned(FACTORY, factory);
    }

    @Override
    public void start(Collection<? extends Location> locsO) {
        addLocations(locsO);
        List<Location> locationsToStart = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locsO, this));

        Preconditions.checkNotNull(locationsToStart, "locations must be supplied");
        Preconditions.checkArgument(locationsToStart.size() >= 1, "One or more locations must be supplied");

        int locIndex = 0;

        ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
        try {
            Map<Entity, Task<?>> tasks = Maps.newLinkedHashMap();

            // first look at existing Startable children - start them with the locations passed in here,
            // if they have no locations yet
            for (Entity child : getChildren()) {
                if (child instanceof Startable) {
                    addMember(child);
                    Location it = null;
                    if (child.getLocations().isEmpty())
                        // give him any of these locations if he has none, allowing round robin here
                        if (!locationsToStart.isEmpty()) {
                            it = locationsToStart.get(locIndex++ % locationsToStart.size());
                            ((EntityInternal) child).addLocations(Arrays.asList(it));
                        }

                    tasks.put(child,
                            Entities.submit(this,
                                    Effectors
                                            .invocation(child, START,
                                                    ImmutableMap.of("locations",
                                                            it == null ? ImmutableList.of() : ImmutableList.of(it)))
                                            .asTask()));
                }
            }
            // remove all the locations we applied to existing nodes
            while (locIndex-- > 0 && !locationsToStart.isEmpty())
                locationsToStart.remove(0);

            // finally (and usually) we create new entities for locations passed in
            // (unless they were consumed by pre-existing children which didn't have locations)
            for (Location it : locationsToStart) {
                Entity e = addCluster(it);

                ((EntityInternal) e).addLocations(Arrays.asList(it));
                if (e instanceof Startable) {
                    Task<?> task = Entities.submit(this, Effectors
                            .invocation(e, START, ImmutableMap.of("locations", ImmutableList.of(it))).asTask());
                    tasks.put(e, task);
                }
            }

            waitForTasksOnStart(tasks);
            ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
            sensors().set(SERVICE_UP, true);
        } catch (Exception e) {
            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
            throw Exceptions.propagate(e);
        }
    }

    protected void waitForTasksOnStart(Map<Entity, Task<?>> tasks) {
        // TODO Could do best-effort for waiting for remaining tasks, rather than failing on first?

        for (Map.Entry<Entity, Task<?>> entry : tasks.entrySet()) {
            try {
                entry.getValue().get();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw Throwables.propagate(e);
            } catch (ExecutionException ee) {
                throw Throwables.propagate(ee.getCause());
            }
        }
    }

    @Override
    public void stop() {
        ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPING);
        try {
            Iterable<Entity> stoppableChildren = Iterables.filter(getChildren(),
                    Predicates.instanceOf(Startable.class));
            Task<?> invoke = Entities.invokeEffector(this, stoppableChildren, Startable.STOP);
            if (invoke != null)
                invoke.get();
            ServiceStateLogic.setExpectedState(this, Lifecycle.STOPPED);
            sensors().set(SERVICE_UP, false);
        } catch (Exception e) {
            ServiceStateLogic.setExpectedState(this, Lifecycle.ON_FIRE);
            throw Exceptions.propagate(e);
        }
    }

    @Override
    public void restart() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Integer getFabricSize() {
        int result = 0;
        for (Entity child : getChildren()) {
            result += GroovyJavaMethods.<Integer>elvis(child.getAttribute(Changeable.GROUP_SIZE), 0);
        }
        return result;
    }

    @Override
    public boolean removeChild(Entity child) {
        boolean changed = super.removeChild(child);
        if (changed) {
            removeMember(child);
        }
        return changed;
    }

    protected Map getCustomChildFlags() {
        Map result = getConfig(CUSTOM_CHILD_FLAGS);
        return (result == null) ? ImmutableMap.of() : result;
    }

    protected Entity addCluster(Location location) {
        String locationName = elvis(location.getDisplayName(), location.getDisplayName(), null);
        Map creation = Maps.newLinkedHashMap();
        creation.putAll(getCustomChildFlags());
        if (truth(getDisplayNamePrefix()) || truth(getDisplayNameSuffix())) {
            String displayName = "" + elvis(getDisplayNamePrefix(), "") + elvis(locationName, "unnamed")
                    + elvis(getDisplayNameSuffix(), "");
            creation.put("displayName", displayName);
        }
        logger.info("Creating entity in fabric {} at {}{}", new Object[] { this, location,
                (creation != null && !creation.isEmpty() ? ", properties " + creation : "") });

        Entity entity = createCluster(location, creation);

        if (locationName != null) {
            if (entity.getDisplayName() == null)
                ((EntityLocal) entity)
                        .setDisplayName(entity.getEntityType().getSimpleName() + " (" + locationName + ")");
            else if (!entity.getDisplayName().contains(locationName))
                ((EntityLocal) entity).setDisplayName(entity.getDisplayName() + " (" + locationName + ")");
        }
        if (entity.getParent() == null)
            entity.setParent(this);

        // Continue to call manage(), because some uses of NodeFactory (in tests) still instantiate the
        // entity via its constructor
        Entities.manage(entity);

        addMember(entity);

        return entity;
    }

    protected Entity createCluster(Location location, Map flags) {
        EntitySpec<?> memberSpec = getMemberSpec();
        if (memberSpec != null) {
            return addChild(EntitySpec.create(memberSpec).configure(flags));
        }

        EntityFactory<?> factory = getFactory();
        if (factory == null) {
            throw new IllegalStateException(
                    "No member spec nor entity factory supplied for dynamic fabric " + this);
        }
        EntityFactory<?> factoryToUse = (factory instanceof EntityFactoryForLocation)
                ? ((EntityFactoryForLocation) factory).newFactoryForLocation(location)
                : factory;
        Entity entity = factoryToUse.newEntity(flags, this);
        if (entity == null)
            throw new IllegalStateException("EntityFactory factory routine returned null entity, in " + this);

        return entity;
    }

}