/* * 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.core.sensor; import java.util.Collection; import java.util.Iterator; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.location.MachineProvisioningLocation; import org.apache.brooklyn.api.location.PortRange; import org.apache.brooklyn.api.location.PortSupplier; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.BasicConfigKey; import org.apache.brooklyn.core.entity.BrooklynConfigKeys; import org.apache.brooklyn.core.internal.BrooklynInitialization; import org.apache.brooklyn.core.location.Locations; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.guava.Maybe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.reflect.TypeToken; /** * A {@link Sensor} describing a port on a system, * with a {@link ConfigKey} which can be configured with a port range * (either a number e.g. 80, or a string e.g. "80" or "8080-8089" or even "80, 8080-8089, 8800+", or a list of these). * <p> * To convert at runtime a single port is chosen, respecting the entity. */ public class PortAttributeSensorAndConfigKey extends AttributeSensorAndConfigKey<PortRange,Integer> { private static final long serialVersionUID = 4680651022807491321L; public static final Logger LOG = LoggerFactory.getLogger(PortAttributeSensorAndConfigKey.class); static { BrooklynInitialization.initAll(); } public PortAttributeSensorAndConfigKey(String name) { this(name, name, null); } public PortAttributeSensorAndConfigKey(String name, String description) { this(name, description, null); } public PortAttributeSensorAndConfigKey(String name, String description, Object defaultValue) { super(PortRange.class, Integer.class, name, description, defaultValue); } public PortAttributeSensorAndConfigKey(PortAttributeSensorAndConfigKey orig, Object defaultValue) { super(orig, TypeCoercions.coerce(defaultValue, PortRange.class)); } public PortAttributeSensorAndConfigKey(BasicConfigKey.Builder<PortRange> builder) { super(builder, TypeToken.of(Integer.class)); } @Override protected Integer convertConfigToSensor(PortRange value, Entity entity) { if (value==null) return null; Collection<? extends Location> locations = entity.getLocations(); if (!locations.isEmpty()) { Maybe<? extends Location> lo = Locations.findUniqueMachineLocation(locations); if (!lo.isPresent()) { // Try a unique location which isn't a machine provisioner Iterator<? extends Location> li = Iterables.filter(locations, Predicates.not(Predicates.instanceOf(MachineProvisioningLocation.class))).iterator(); if (li.hasNext()) lo = Maybe.of(li.next()); if (li.hasNext()) lo = Maybe.absent(); } // Fall back to selecting the single location if (!lo.isPresent() && locations.size() == 1) { lo = Maybe.of(locations.iterator().next()); } if (LOG.isTraceEnabled()) { LOG.trace("Convert config to sensor for {} found locations: {}. Selected: {}", new Object[] {entity, locations, lo}); } if (lo.isPresent()) { Location l = lo.get(); Optional<Boolean> locationRunning = Optional.fromNullable(l.getConfig(BrooklynConfigKeys.SKIP_ENTITY_START_IF_RUNNING)); Optional<Boolean> entityRunning = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_START_IF_RUNNING)); Optional<Boolean> locationInstalled = Optional.fromNullable(l.getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION)); Optional<Boolean> entityInstalled = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION)); Optional<Boolean> entityStarted = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_START)); boolean skipCheck = locationRunning.or(entityRunning).or(locationInstalled).or(entityInstalled).or(entityStarted).or(false); if (l instanceof PortSupplier) { int p = ((PortSupplier) l).obtainPort(value); if (p != -1) { LOG.debug("{}: choosing port {} for {}", new Object[] { entity, p, getName() }); return p; } // If we are not skipping install or already started, fail now if (!skipCheck) { int rangeSize = Iterables.size(value); if (rangeSize == 0) { LOG.warn("{}: no port available for {} (empty range {})", new Object[] { entity, getName(), value }); } else if (rangeSize == 1) { Integer pp = value.iterator().next(); if (pp > 1024) { LOG.warn("{}: port {} not available for {}", new Object[] { entity, pp, getName() }); } else { LOG.warn("{}: port {} not available for {} (root may be required?)", new Object[] { entity, pp, getName() }); } } else { LOG.warn("{}: no port available for {} (tried range {})", new Object[] { entity, getName(), value }); } return null; // Definitively, no ports available } } // Ports may be available, we just can't tell from the location Integer v = (value.isEmpty() ? null : value.iterator().next()); LOG.debug("{}: choosing port {} (unconfirmed) for {}", new Object[] { entity, v, getName() }); return v; } else { LOG.warn("{}: ports not applicable, or not yet applicable, because has multiple locations {}; ignoring ", new Object[] { entity, locations, getName() }); } } else { LOG.warn("{}: ports not applicable, or not yet applicable, bacause has no locations; ignoring {}", entity, getName()); } return null; } @Override protected Integer convertConfigToSensor(PortRange value, ManagementContext managementContext) { LOG.warn("ports not applicable, because given managementContext rather than entity; ignoring {}", getName()); return null; } }