package com.twitter.common.zookeeper; import java.net.InetSocketAddress; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Optional; 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.twitter.common.base.Command; import com.twitter.common.base.Commands; import com.twitter.common.zookeeper.Group.JoinException; import com.twitter.thrift.Endpoint; import com.twitter.thrift.ServiceInstance; import com.twitter.thrift.Status; /** * A server set that represents a fixed set of hosts. * This may be composed under {@link CompoundServerSet} to ensure a minimum set of hosts is * present. * A static server set does not support joining, but will allow normal join calls and status update * calls to be made. */ public class StaticServerSet implements ServerSet { private static final Logger LOG = Logger.getLogger(StaticServerSet.class.getName()); private static final Function<Endpoint, ServiceInstance> ENDPOINT_TO_INSTANCE = new Function<Endpoint, ServiceInstance>() { @Override public ServiceInstance apply(Endpoint endpoint) { return new ServiceInstance(endpoint, ImmutableMap.<String, Endpoint>of(), Status.ALIVE); } }; private final ImmutableSet<ServiceInstance> hosts; /** * Creates a static server set that will reply to monitor calls immediately and exactly once with * the provided service instances. * * @param hosts Hosts in the static set. */ public StaticServerSet(Set<ServiceInstance> hosts) { this.hosts = ImmutableSet.copyOf(hosts); } /** * Creates a static server set containing the provided endpoints (and no auxiliary ports) which * will all be in the {@link Status#ALIVE} state. * * @param endpoints Endpoints in the static set. * @return A static server set that will advertise the provided endpoints. */ public static StaticServerSet fromEndpoints(Set<Endpoint> endpoints) { return new StaticServerSet( ImmutableSet.copyOf(Iterables.transform(endpoints, ENDPOINT_TO_INSTANCE))); } private EndpointStatus join( InetSocketAddress endpoint, Map<String, InetSocketAddress> auxEndpoints, Optional<Integer> shardId) { LOG.warning("Attempt to join fixed server set ignored."); ServiceInstance joining = new ServiceInstance( ServerSets.toEndpoint(endpoint), Maps.transformValues(auxEndpoints, ServerSets.TO_ENDPOINT), Status.ALIVE); if (shardId.isPresent()) { joining.setShard(shardId.get()); } if (!hosts.contains(joining)) { LOG.log(Level.SEVERE, "Joining instance " + joining + " does not match any member of the static set."); } return new EndpointStatus() { @Override public void leave() throws UpdateException { LOG.warning("Attempt to adjust state of fixed server set ignored."); } @Override public void update(Status status) throws UpdateException { LOG.warning("Attempt to adjust state of fixed server set ignored."); } }; } @Override public EndpointStatus join( InetSocketAddress endpoint, Map<String, InetSocketAddress> auxEndpoints, Status status) { LOG.warning("This method is deprecated. Please do not specify a status field."); return join(endpoint, auxEndpoints, Optional.<Integer>absent()); } @Override public EndpointStatus join( InetSocketAddress endpoint, Map<String, InetSocketAddress> auxEndpoints) { LOG.warning("Joining a ServerSet without a shard ID is deprecated and will soon break."); return join(endpoint, auxEndpoints, Optional.<Integer>absent()); } @Override public EndpointStatus join( InetSocketAddress endpoint, Map<String, InetSocketAddress> auxEndpoints, int shardId) throws JoinException, InterruptedException { return join(endpoint, auxEndpoints, Optional.of(shardId)); } @Override public Command watch(HostChangeMonitor<ServiceInstance> monitor) { monitor.onChange(hosts); return Commands.NOOP; } @Override public void monitor(HostChangeMonitor<ServiceInstance> monitor) throws MonitorException { watch(monitor); } }