Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.netflix.eventbus.impl;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.eventbus.spi.CatchAllSubscriber;
import com.netflix.eventbus.spi.EventBus;
import com.netflix.eventbus.spi.EventCreator;
import com.netflix.eventbus.spi.EventFilter;
import com.netflix.eventbus.spi.InvalidSubscriberException;
import com.netflix.eventbus.spi.Subscribe;
import com.netflix.eventbus.spi.SubscriberConfigProvider;
import com.netflix.eventbus.spi.SubscriberInfo;
import com.netflix.eventbus.utils.EventBusUtils;
import com.netflix.servo.monitor.Stopwatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
/**
* An implementation of {@link EventBus}. This implementation is based on the google eventbus
* {@link com.google.common.eventbus.EventBus} but the absence of descent extension points forces us to inspire but
* not extend that implementation. The critical parts that drove us towards this approach are:
*
Inability to add a filter for publisher/subscriber. We would need to copy part of code to do this.
Inability to easily create custom handler wrappers for our style of async dispatch.
*
* @author Nitesh Kant ([email protected])
*/
public class EventBusImpl implements EventBus {
private static final Logger LOGGER = LoggerFactory.getLogger(EventBusImpl.class);
static final DynamicIntProperty STATS_COLLECTION_DURATION_MILLIS =
DynamicPropertyFactory.getInstance().getIntProperty("eventbus.stats.collection.duration.millis", 60*1000);
/**
* Event type VS consumers map. Any event for which consumers are required, must query this collection for all the
* interfaces & classes the event implements/extends, directly or indirectly, typically by calling
* {@link EventBusImpl#getAllTypesForAnEvent(Object)}
*/
private final SetMultimap, EventConsumer> consumersByEventType =
Multimaps.newSetMultimap(new ConcurrentHashMap, Collection>(),
new Supplier>() {
@Override
public Set get() {
return new CopyOnWriteArraySet();
}
});
/**
* This is an index to aid in unregister of subscribers. This holds the mapping between, the class of the subscriber
* and the list of consumers it is registered with. During unregister we get a list of consumers for that class
* and then for every consumer get the event class it listens for and then delete the same from
* {@link EventBusImpl#consumersByEventType}
*/
private final ConcurrentHashMap, CopyOnWriteArrayList> consumersBySubscriberClass =
new ConcurrentHashMap, CopyOnWriteArrayList>();
/**
* Filters attached to an event type.
*/
private final SetMultimap, EventFilter> eventTypeVsFilters =
Multimaps.newSetMultimap(new ConcurrentHashMap, Collection>(),
new Supplier>() {
@Override
public Set get() {
return new CopyOnWriteArraySet();
}
});
/**
* Cache of the class hierarchy for an event type. This optimizes multiple publishing of the same event type which
* typically will be the case.
*/
private static LoadingCache, Set>> eventHierarchyCache =
CacheBuilder.newBuilder()
.weakKeys()
.build(new CacheLoader, Set>>() {
@Override
public Set> load(Class> concreteClass) throws Exception {
List> parents = Lists.newLinkedList();
Set> classes = Sets.newHashSet();
parents.add(concreteClass);
while (!parents.isEmpty()) {
Class> clazz = parents.remove(0);
classes.add(clazz);
Class> parent = clazz.getSuperclass();
if (parent != null && !parent.equals(Object.class)) { // Do not allow subs on java.lang.Object
parents.add(parent);
}
Collections.addAll(parents, clazz.getInterfaces());
}
return classes;
}
});
private ConsumerQueueSupplier consumerQueueSupplier = new DefaultConsumerQueueSupplier();
private EventBusStats stats = new EventBusStats(STATS_COLLECTION_DURATION_MILLIS.get());
private EventConsumer catchAllSubscriber;
private volatile CatchAllSubscriber catchAllSubInstance;
public EventBusImpl() {
}
@Override
public void publish(Object event) {
Stopwatch start = stats.publishStats.start();
try {
if (!applyEventLevelFilters(event)) {
return;
}
Set> allTypesForAnEvent = getAllTypesForAnEvent(event);
for (Class> eventType : allTypesForAnEvent) {
Set eventConsumers = consumersByEventType.get(eventType);
for (EventConsumer eventConsumer : eventConsumers) {
eventConsumer.enqueue(event);
}
}
if (null != catchAllSubInstance && catchAllSubInstance.isEnabled()) {
catchAllSubscriber.enqueue(event);
}
} catch (Throwable th) {
LOGGER.error("Error occured while publishing event. Swallowing the error to avoid publisher from failing.", th);
stats.publishErrors.increment();
} finally {
start.stop();
}
}
@Override
public void publishIffNotDead(EventCreator creator, Class>... eventTypes) {
Stopwatch start = stats.conditionalPublishStats.start();
try {
Map, Set> interestedConsumersByType = new HashMap, Set>();
for (Class> eventType : eventTypes) {
for (Class> anEventSubType : getAllTypesForAnEventType(eventType)) {
Set eventConsumers = consumersByEventType.get(anEventSubType);
if (!eventConsumers.isEmpty()) {
/*
* Since any change in the underlying consumers get reflected in this set, we get the benefit of any changes
* to the consumers after this check being reflected when we invoke these consumers.
* We add the evenType to the map and not the subType as the event creator is only aware of the high
* level types & not the entire hierarchy.
*/
interestedConsumersByType.put(eventType, eventConsumers);
}
}
}
if (interestedConsumersByType.isEmpty()) {
LOGGER.debug(String.format("Skipping publishing of events types %s as there are no interested listeners.",
Arrays.toString(eventTypes)));
return;
}
List events = creator.createEvent(interestedConsumersByType.keySet());
if (null == events) {
LOGGER.debug(String.format("No events created by event creator for event types %s",
interestedConsumersByType.keySet()));
return;
}
for (Object event : events) {
if (!applyEventLevelFilters(event)) {
continue;
}
Set eventConsumers = interestedConsumersByType.get(event.getClass());
for (EventConsumer eventConsumer : eventConsumers) {
eventConsumer.enqueue(event);
}
}
} catch (Throwable th) {
LOGGER.error("Error occured while publishing event. Swallowing the error to avoid publisher from failing.", th);
stats.conditionalPublishErrors.increment();
} finally {
start.stop();
}
}
@Override
public void registerSubscriber(@Nullable EventFilter filter, Object subscriber) throws InvalidSubscriberException {
List allConsumersForThisSubscriber = new ArrayList();
List subscriberMethods = findSubscriberMethods(subscriber);
for (Method subscriberMethod : subscriberMethods) {
Class> targetEventType = EventBusUtils.getInterestedEventType(subscriber, subscriberMethod);
EventConsumer consumer =
new EventConsumer(subscriberMethod, subscriber, filter, targetEventType, consumerQueueSupplier);
allConsumersForThisSubscriber.add(consumer);
consumersByEventType.put(targetEventType, consumer);
}
CopyOnWriteArrayList existingConsumers =
consumersBySubscriberClass.putIfAbsent(subscriber.getClass(),
new CopyOnWriteArrayList((allConsumersForThisSubscriber)));
if (null != existingConsumers) {
existingConsumers.addAll(allConsumersForThisSubscriber);
} else {
LOGGER.info(String.format("Registered a new subscriber: %s with filter: %s", subscriber, filter));
}
}
@Override
public void registerSubscriber(Object subscriber) throws InvalidSubscriberException {
registerSubscriber(null, subscriber);
}
@Override
public synchronized boolean enableCatchAllSubscriber(BlockingQueue catchAllSink) {
if (null == catchAllSubscriber) {
catchAllSubInstance = new CatchAllSubscriber();
List subscriberMethods;
try {
subscriberMethods = findSubscriberMethods(catchAllSubInstance);
if (!subscriberMethods.isEmpty()) {
Method method = subscriberMethods.get(0);
catchAllSubscriber = new EventConsumer(method, catchAllSubInstance, null, Object.class, consumerQueueSupplier);
}
} catch (InvalidSubscriberException e) {
// We know it can not happen as the subscriber is valid.
LOGGER.error("Catch all subscriber invalid!", e);
return false;
}
}
return catchAllSubInstance.enable(catchAllSink);
}
@Override
public synchronized void disableCatchAllSubscriber() {
if (null != catchAllSubInstance) {
catchAllSubInstance.disable();
} else {
LOGGER.info("Catch all subscriber is not enabled, disable call ignored.");
}
}
@Override
public Set