/**
* Copyright 2014 Comcast Cable Communications Management, LLC
*
* Licensed 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 com.comcast.viper.flume2storm.location;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
/**
* Manages a list of service providers and provides join/leave notifications
* based on a changing list of servers. This is thread-safe.
*
* @param <SP>
* The Service Provider class
*/
class ServiceProviderManager<SP extends ServiceProvider<?>> {
private static final Logger LOG = LoggerFactory.getLogger(ServiceProviderManager.class);
/** The one listener we'll notify (i.e. the location service) */
protected final ServiceListener<SP> listener;
/** Current list of active servers */
protected final Map<String, SP> serviceProviders;
public ServiceProviderManager(ServiceListener<SP> listener) {
this.listener = listener;
serviceProviders = new HashMap<String, SP>();
}
public synchronized List<SP> get() {
return ImmutableList.copyOf(serviceProviders.values());
}
/**
* Updates the list of service providers, providing add/remove notifications
* to the listener
*
* @param newList
* The new list of service providers
*/
public synchronized void set(final Collection<SP> newList) {
final List<SP> oldList = new ArrayList<SP>(serviceProviders.values());
final Iterator<SP> it = newList.iterator();
while (it.hasNext()) {
final SP current = it.next();
if (contains(current.getConnectionParameters().getId())) {
/*
* The element is in the current and the new list - no modification of
* the current list, but we remove it from the old list in order to see
* the ones we did not have already
*/
oldList.remove(current);
} else {
// The element is only in the new list - adding it
addServiceProvider(current);
}
}
/*
* At this point, all the elements of the old list that were in the old and
* the new list have been removed, therefore, the remaining elements have
* been removed
*/
for (final SP current : oldList) {
removeServiceProvider(current);
}
}
protected void addServiceProvider(final SP sp) {
SP previous = serviceProviders.put(sp.getConnectionParameters().getId(), sp);
if (previous == null) {
LOG.debug("Adding: {}", sp);
try {
listener.onProviderAdded(sp);
} catch (final Exception e) {
LOG.warn("Failed to notify listener about the addition of service provider {} : {}", sp,
e.getLocalizedMessage());
}
}
}
protected void removeServiceProvider(final SP sp) {
SP removed = serviceProviders.remove(sp.getConnectionParameters().getId());
if (removed != null) {
LOG.debug("Removing: {}", sp);
try {
listener.onProviderRemoved(sp);
} catch (final Exception e) {
LOG.warn("Failed to notify listener about the removal of service provider {} : {}", sp, e.getLocalizedMessage());
}
}
}
/**
* Adds a service provider, providing a notification to the listener
*
* @param sp
* The new service provider
*/
public synchronized void add(final SP sp) {
if (!contains(sp.getConnectionParameters().getId())) {
addServiceProvider(sp);
}
}
/**
* Adds a collection of service providers, providing a notification to the
* listener
*
* @param serviceProviders
* The new service providers to add
*/
public synchronized void addAll(final Collection<SP> serviceProviders) {
for (SP sp : serviceProviders) {
add(sp);
}
}
/**
* Removes a service provider, providing a notification to the listener
*
* @param sp
* A service provider
*/
public synchronized void remove(final SP sp) {
removeServiceProvider(sp);
}
/**
* Removes all the {@link ServiceProvider} from the list
*/
public synchronized void clear() {
for (SP sp : serviceProviders.values()) {
removeServiceProvider(sp);
}
}
/**
* @param spId
* A service provider
* @return True if the specified {@link ServiceProvider} is registered, false
* otherwise
*/
public synchronized boolean contains(final String spId) {
return serviceProviders.containsKey(spId);
}
}