org.apache.brooklyn.entity.proxy.AbstractControllerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of brooklyn-software-webapp Show documentation
Show all versions of brooklyn-software-webapp Show documentation
Brooklyn entities for Web App software processes
/*
* 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.proxy;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth;
import java.net.URI;
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.Group;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.policy.Policy;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.feed.ConfigToAttributes;
import org.apache.brooklyn.core.location.Machines;
import org.apache.brooklyn.core.location.access.BrooklynAccessUtils;
import org.apache.brooklyn.entity.group.AbstractMembershipTrackingPolicy;
import org.apache.brooklyn.entity.group.Cluster;
import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
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.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.net.HostAndPort;
/**
* Represents a controller mechanism for a {@link Cluster}.
*/
public abstract class AbstractControllerImpl extends SoftwareProcessImpl implements AbstractController {
// TODO Should review synchronization model. Currently, all changes to the serverPoolTargets
// (and checking for potential changes) is done while synchronized on serverPoolAddresses. That means it
// will also call update/reload while holding the lock. This is "conservative", but means
// sub-classes need to be extremely careful about any additional synchronization and of
// their implementations of update/reconfigureService/reload.
private static final Logger LOG = LoggerFactory.getLogger(AbstractControllerImpl.class);
protected volatile boolean isActive;
protected volatile boolean updateNeeded = true;
protected AbstractMembershipTrackingPolicy serverPoolMemberTrackerPolicy;
// final because this is the synch target
final protected Set serverPoolAddresses = Sets.newLinkedHashSet();
@Override
public void init() {
super.init();
sensors().set(SERVER_POOL_TARGETS, ImmutableMap.of());
}
protected void addServerPoolMemberTrackingPolicy() {
Group serverPool = getServerPool();
if (serverPool == null) {
return; // no-op
}
if (serverPoolMemberTrackerPolicy != null) {
LOG.debug("Call to addServerPoolMemberTrackingPolicy when serverPoolMemberTrackingPolicy already exists, removing and re-adding, in {}", this);
removeServerPoolMemberTrackingPolicy();
}
for (Policy p: policies()) {
if (p instanceof ServerPoolMemberTrackerPolicy) {
// TODO want a more elegant idiom for this!
LOG.info(this+" picking up "+p+" as the tracker (already set, often due to rebind)");
serverPoolMemberTrackerPolicy = (ServerPoolMemberTrackerPolicy) p;
return;
}
}
AttributeSensor> hostAndPortSensor = getConfig(HOST_AND_PORT_SENSOR);
AttributeSensor> hostnameSensor = getConfig(HOSTNAME_SENSOR);
AttributeSensor> portSensor = getConfig(PORT_NUMBER_SENSOR);
Set> sensorsToTrack;
if (hostAndPortSensor != null) {
sensorsToTrack = ImmutableSet.>of(hostAndPortSensor);
} else {
sensorsToTrack = ImmutableSet.>of(hostnameSensor, portSensor);
}
serverPoolMemberTrackerPolicy = policies().add(PolicySpec.create(ServerPoolMemberTrackerPolicy.class)
.displayName("Controller targets tracker")
.configure("group", serverPool)
.configure("sensorsToTrack", sensorsToTrack));
LOG.info("Added policy {} to {}", serverPoolMemberTrackerPolicy, this);
synchronized (serverPoolAddresses) {
// Initialize ourselves immediately with the latest set of members; don't wait for
// listener notifications because then will be out-of-date for short period (causing
// problems for rebind)
// if invoked on start, we'll have isActive=false at this point so other policies wont' run anyway,
// but synch in case invoked at other times; and note if !isActive during start means we miss some after this,
// we will update again on postStart after setting isActive=true
Map serverPoolTargets = Maps.newLinkedHashMap();
for (Entity member : serverPool.getMembers()) {
if (belongsInServerPool(member)) {
if (LOG.isTraceEnabled()) LOG.trace("Done {} checkEntity {}", this, member);
String address = getAddressOfEntity(member);
serverPoolTargets.put(member, address);
}
}
LOG.info("Resetting {}, server pool targets {}", new Object[] {this, serverPoolTargets});
sensors().set(SERVER_POOL_TARGETS, serverPoolTargets);
}
}
protected void removeServerPoolMemberTrackingPolicy() {
if (serverPoolMemberTrackerPolicy != null) {
policies().remove(serverPoolMemberTrackerPolicy);
}
}
public static class ServerPoolMemberTrackerPolicy extends AbstractMembershipTrackingPolicy {
@Override
protected void onEntityEvent(EventType type, Entity entity) {
// relies on policy-rebind injecting the implementation rather than the dynamic-proxy
((AbstractControllerImpl)super.entity).onServerPoolMemberChanged(entity);
}
}
@Override
public Set getServerPoolAddresses() {
return ImmutableSet.copyOf(Iterables.filter(getAttribute(SERVER_POOL_TARGETS).values(), Predicates.notNull()));
}
/**
* Opportunity to do late-binding of the cluster that is being controlled. Must be called before start().
* Can pass in the 'serverPool'.
*/
@Override
public void bind(Map,?> flags) {
if (flags.containsKey("serverPool")) {
setConfigEvenIfOwned(SERVER_POOL, (Group) flags.get("serverPool"));
}
}
@SuppressWarnings("deprecation")
@Override
public void onManagementNoLongerMaster() {
super.onManagementNoLongerMaster(); // TODO remove when deprecated method in parent removed
isActive = false;
removeServerPoolMemberTrackingPolicy();
}
private Group getServerPool() {
return getConfig(SERVER_POOL);
}
@Override
public boolean isActive() {
return isActive;
}
@Override
public boolean isSsl() {
return getSslConfig() != null;
}
@Override
public ProxySslConfig getSslConfig() {
return getConfig(SSL_CONFIG);
}
@Override
public String getProtocol() {
return getAttribute(PROTOCOL);
}
/** returns primary domain this controller responds to, or null if it responds to all domains */
@Override
public String getDomain() {
return getAttribute(DOMAIN_NAME);
}
protected String getDomainWithoutWildcard() {
String domain = getDomain();
if (domain != null && domain.startsWith("*.")) {
domain = domain.replace("*.", ""); // Strip wildcard
}
return domain;
}
@Override
public Integer getPort() {
if (isSsl())
return getAttribute(PROXY_HTTPS_PORT);
else
return getAttribute(PROXY_HTTP_PORT);
}
/** primary URL this controller serves, if one can / has been inferred */
@Override
public String getUrl() {
return Strings.toString( getAttribute(MAIN_URI) );
}
@Override
public AttributeSensor getPortNumberSensor() {
return getAttribute(PORT_NUMBER_SENSOR);
}
@Override
public AttributeSensor getHostnameSensor() {
return getAttribute(HOSTNAME_SENSOR);
}
@Override
public AttributeSensor getHostAndPortSensor() {
return getAttribute(HOST_AND_PORT_SENSOR);
}
@Override
public abstract void reload();
protected String inferProtocol() {
return isSsl() ? "https" : "http";
}
protected String inferUrlForSubnet() {
String domain = getDomainWithoutWildcard();
if (domain==null) domain = getAttribute(Attributes.SUBNET_ADDRESS);
return inferUrl(domain, Optional.absent());
}
protected String inferUrlForPublic() {
String domain = getDomainWithoutWildcard();
if (domain==null) domain = getAttribute(Attributes.ADDRESS);
return inferUrl(domain, Optional.absent());
}
/** returns URL, if it can be inferred; null otherwise */
protected String inferUrl(boolean requireManagementAccessible) {
String domain = getDomainWithoutWildcard();
Integer port = checkNotNull(getPort(), "no port configured (the requested port may be in use)");
if (requireManagementAccessible) {
HostAndPort accessible = BrooklynAccessUtils.getBrooklynAccessibleAddress(this, port);
if (accessible!=null) {
domain = accessible.getHostText();
port = accessible.getPort();
}
}
if (domain==null) domain = Machines.findSubnetHostname(this).orNull();
return inferUrl(domain, Optional.of(port));
}
protected String inferUrl(String host, Optional portOverride) {
if (host == null) return null;
String protocol = checkNotNull(getProtocol(), "no protocol configured");
int port = portOverride.isPresent() ? portOverride.get() : checkNotNull(getPort(), "no port configured (the requested port may be in use)");
String path = getConfig(SERVICE_UP_URL_PATH);
return protocol+"://"+host+":"+port+"/"+path;
}
protected String inferUrl() {
return inferUrl(false);
}
@Override
protected Collection getRequiredOpenPorts() {
Collection result = super.getRequiredOpenPorts();
if (groovyTruth(getAttribute(PROXY_HTTP_PORT))) result.add(getAttribute(PROXY_HTTP_PORT));
if (groovyTruth(getAttribute(PROXY_HTTPS_PORT))) result.add(getAttribute(PROXY_HTTPS_PORT));
return result;
}
@Override
protected void preStart() {
super.preStart();
computePortsAndUrls();
}
protected void computePortsAndUrls() {
AttributeSensor hostAndPortSensor = getConfig(HOST_AND_PORT_SENSOR);
Maybe