org.apache.brooklyn.enricher.stock.Propagator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.enricher.stock.Propagator.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.enricher.stock;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.api.sensor.SensorEvent;
import org.apache.brooklyn.api.sensor.SensorEventListener;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.enricher.AbstractEnricher;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.core.sensor.SensorPredicates;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.core.task.ValueResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.reflect.TypeToken;

@SuppressWarnings("serial")
//@Catalog(name="Propagator", description="Propagates attributes from one entity to another; see Enrichers.builder().propagating(...)")
public class Propagator extends AbstractEnricher implements SensorEventListener<Object> {

    private static final Logger LOG = LoggerFactory.getLogger(Propagator.class);

    public static final Set<Sensor<?>> SENSORS_NOT_USUALLY_PROPAGATED = ImmutableSet.<Sensor<?>>of(
            Attributes.SERVICE_UP, Attributes.SERVICE_NOT_UP_INDICATORS, Attributes.SERVICE_STATE_ACTUAL,
            Attributes.SERVICE_STATE_EXPECTED, Attributes.SERVICE_PROBLEMS);

    @SetFromFlag("producer")
    public static ConfigKey<Entity> PRODUCER = ConfigKeys.newConfigKey(Entity.class, "enricher.producer");

    @SetFromFlag("propagatingAllBut")
    public static ConfigKey<Collection<Sensor<?>>> PROPAGATING_ALL_BUT = ConfigKeys
            .newConfigKey(new TypeToken<Collection<Sensor<?>>>() {
            }, "enricher.propagating.propagatingAllBut");

    @SetFromFlag("propagatingAll")
    public static ConfigKey<Boolean> PROPAGATING_ALL = ConfigKeys
            .newBooleanConfigKey("enricher.propagating.propagatingAll");

    @SetFromFlag("propagating")
    public static ConfigKey<Collection<? extends Sensor<?>>> PROPAGATING = ConfigKeys
            .newConfigKey(new TypeToken<Collection<? extends Sensor<?>>>() {
            }, "enricher.propagating.inclusions");

    @SetFromFlag("sensorMapping")
    public static ConfigKey<Map<? extends Sensor<?>, ? extends Sensor<?>>> SENSOR_MAPPING = ConfigKeys
            .newConfigKey(new TypeToken<Map<? extends Sensor<?>, ? extends Sensor<?>>>() {
            }, "enricher.propagating.sensorMapping");

    protected Entity producer;
    protected Map<? extends Sensor<?>, ? extends Sensor<?>> sensorMapping;
    protected boolean propagatingAll;
    protected Collection<Sensor<?>> propagatingAllBut;
    protected Predicate<Sensor<?>> sensorFilter;

    public Propagator() {
    }

    @Override
    public void setEntity(EntityLocal entity) {
        super.setEntity(entity);

        this.producer = getConfig(PRODUCER) == null ? entity : getConfig(PRODUCER);
        boolean sensorMappingSet = getConfig(SENSOR_MAPPING) != null;
        MutableMap<Sensor<?>, Sensor<?>> sensorMappingTemp = MutableMap.copyOf(getConfig(SENSOR_MAPPING));
        this.propagatingAll = Boolean.TRUE.equals(getConfig(PROPAGATING_ALL))
                || getConfig(PROPAGATING_ALL_BUT) != null;

        if (getConfig(PROPAGATING) != null) {
            if (propagatingAll) {
                throw new IllegalStateException("Propagator enricher " + this
                        + " must not have 'propagating' set at same time as either 'propagatingAll' or 'propagatingAllBut'");
            }

            for (Object sensorO : getConfig(PROPAGATING)) {
                Sensor<?> sensor = Tasks.resolving(sensorO).as(Sensor.class).timeout(ValueResolver.REAL_QUICK_WAIT)
                        .context(producer).get();
                if (!sensorMappingTemp.containsKey(sensor)) {
                    sensorMappingTemp.put(sensor, sensor);
                }
            }
            this.sensorMapping = ImmutableMap.copyOf(sensorMappingTemp);
            this.sensorFilter = new Predicate<Sensor<?>>() {
                @Override
                public boolean apply(Sensor<?> input) {
                    // TODO kept for deserialization of inner classes, but shouldn't be necessary, as with other inner classes (qv);
                    // NB: previously this did this check:
                    //                    return input != null && sensorMapping.keySet().contains(input);
                    // but those clauses seems wrong (when would input be null?) and unnecessary (we are doing an explicit subscribe in this code path) 
                    return true;
                }
            };
        } else if (sensorMappingSet) {
            if (propagatingAll) {
                throw new IllegalStateException("Propagator enricher " + this
                        + " must not have 'sensorMapping' set at same time as either 'propagatingAll' or 'propagatingAllBut'");
            }
            this.sensorMapping = ImmutableMap.copyOf(sensorMappingTemp);
            this.sensorFilter = Predicates.alwaysTrue();
        } else {
            this.sensorMapping = ImmutableMap.<Sensor<?>, Sensor<?>>of();
            if (!propagatingAll) {
                // default if nothing specified is to do all but the ones not usually propagated
                propagatingAll = true;
                // user specified nothing, so *set* the all_but to the default set
                // if desired, we could allow this to be dynamically reconfigurable, remove this field and always look up;
                // slight performance hit (always looking up), and might need to recompute subscriptions, so not supported currently
                // TODO this default is @Beta behaviour! -- maybe better to throw?
                propagatingAllBut = SENSORS_NOT_USUALLY_PROPAGATED;
            } else {
                propagatingAllBut = getConfig(PROPAGATING_ALL_BUT);
            }
            this.sensorFilter = new Predicate<Sensor<?>>() {
                @Override
                public boolean apply(Sensor<?> input) {
                    Collection<Sensor<?>> exclusions = propagatingAllBut;
                    // TODO this anonymous inner class and getConfig check kept should be removed / confirmed for rebind compatibility.
                    // we *should* be regenerating these fields on each rebind (calling to this method), 
                    // so serialization of this class shouldn't be needed (and should be skipped), but that needs to be checked.
                    if (propagatingAllBut == null)
                        exclusions = getConfig(PROPAGATING_ALL_BUT);
                    return input != null && (exclusions == null || !exclusions.contains(input));
                }
            };
        }

        Preconditions.checkState(propagatingAll ^ sensorMapping.size() > 0,
                "Nothing to propagate; detected: propagatingAll (%s, excluding %s), sensorMapping (%s)",
                propagatingAll, getConfig(PROPAGATING_ALL_BUT), sensorMapping);

        if (propagatingAll) {
            subscriptions().subscribe(producer, null, this);
        } else {
            for (Sensor<?> sensor : sensorMapping.keySet()) {
                subscriptions().subscribe(producer, sensor, this);
            }
        }

        emitAllAttributes();
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public void onEvent(SensorEvent<Object> event) {
        // propagate upwards
        Sensor<?> sourceSensor = event.getSensor();
        Sensor<?> destinationSensor = getDestinationSensor(sourceSensor);

        if (!sensorFilter.apply(sourceSensor)) {
            return; // ignoring excluded sensor
        }

        if (LOG.isTraceEnabled())
            LOG.trace("enricher {} got {}, propagating via {}{}", new Object[] { this, event, entity,
                    (sourceSensor == destinationSensor ? "" : " (as " + destinationSensor + ")") });

        emit((Sensor) destinationSensor, event.getValue());
    }

    /** useful once sensors are added to emit all values */
    public void emitAllAttributes() {
        emitAllAttributes(false);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void emitAllAttributes(boolean includeNullValues) {
        Iterable<? extends Sensor<?>> sensorsToPopulate = propagatingAll
                ? Iterables.filter(producer.getEntityType().getSensors(), sensorFilter)
                : sensorMapping.keySet();

        for (Sensor<?> s : sensorsToPopulate) {
            if (s instanceof AttributeSensor) {
                AttributeSensor destinationSensor = (AttributeSensor<?>) getDestinationSensor(s);
                Object v = producer.getAttribute((AttributeSensor<?>) s);
                // TODO we should keep a timestamp for the source sensor and echo it 
                // (this pretends timestamps are current, which probably isn't the case when we are propagating)
                if (v != null || includeNullValues)
                    entity.sensors().set(destinationSensor, v);
            }
        }
    }

    private Sensor<?> getDestinationSensor(final Sensor<?> sourceSensor) {
        // sensor equality includes the type; we want just name-equality so will use predicate.
        Optional<? extends Sensor<?>> mappingSensor = Iterables.tryFind(sensorMapping.keySet(),
                SensorPredicates.nameEqualTo(sourceSensor.getName()));

        return mappingSensor.isPresent() ? sensorMapping.get(mappingSensor.get()) : sourceSensor;
    }

}