All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.eventbus.spi.SyncSubscribersGatekeeper Maven / Gradle / Ivy

package com.netflix.eventbus.spi;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.netflix.config.DynamicBooleanProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A gatekeeper to allow synchronous subscribers in {@link com.netflix.eventbus.impl.EventBusImpl}. This determines whether a particular
 * subscriber annotated with {@link com.netflix.eventbus.spi.Subscribe#syncIfAllowed()} as true  is
 * actually allowed to be synchronous. 
* A subscriber is allowed, iff, *
  • The property {@link SyncSubscribersGatekeeper#ALLOW_SYNC_SUBSCRIBERS} is true
  • If a property {@link SyncSubscribersGatekeeper#SYNC_SUBSCRIBERS_WHITELIST_JSON} exists, is non empty and contains this subscriber.
* *

Whitelisting:

* * One can cherry-pick subscribers which are allowed to be synchronous. This can be done by setting a property * {@link SyncSubscribersGatekeeper#SYNC_SUBSCRIBERS_WHITELIST_JSON} with a json of the following format: *
    {
        "[Fully qualified class name of the subscriber class]": 0 or more fully qualified class name of the events,
    }
 
Example:
 {
     "com.foo.bar.MyAllSubscriber": [], // Signifies all subscriber methods in this class will be sync, if configured so by setting {@link com.netflix.eventbus.spi.Subscribe#syncIfAllowed()} as true.
     "com.foo.bar.MySubscriber": ["com.foo.bar.EventOne", "com.foo.bar.EventTwo"], // Signifies only subscrber of events EventOne & EventTwo in this class will be sync, if configured so by setting {@link com.netflix.eventbus.spi.Subscribe#syncIfAllowed()} as true.
     "com.foo.bar.MyAnotherSubscriber": ["com.foo.bar.Event3", "com.foo.bar.Event4"]
 }

 
* * @author Nitesh Kant */ public class SyncSubscribersGatekeeper { private static final Logger LOGGER = LoggerFactory.getLogger(SyncSubscribersGatekeeper.class); public static final String ALLOW_SYNC_SUBSCRIBERS = "eventbus.allow.sync.subscribers"; /** * Property to define a whitelist of subscribers which are allowed to be synchronous.
* See {@link SyncSubscribersGatekeeper} javadocs for details of the format of this property. */ public static final String SYNC_SUBSCRIBERS_WHITELIST_JSON = "eventbus.sync.subscribers.whitelist.json"; public static final SetMultimap EMPTY_WHITELIST = Multimaps.forMap(Collections.emptyMap()); private static DynamicBooleanProperty allowSyncSubs; private static AtomicReference> syncSubsWhiteList; private static DynamicStringProperty syncSubsWhitelistJson; static { initState(); } private static final TypeToken> whitelistTypeToken = new TypeToken>() {}; public static final String ALLOW_ALL_EVENTS = "*"; private static final Gson whiteListJsonParser = new GsonBuilder().registerTypeAdapter(whitelistTypeToken.getType(), new JsonDeserializer>() { @Override public SetMultimap deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { final SetMultimap toReturn = Multimaps.newSetMultimap(new HashMap>(), new Supplier>() { @Override public Set get() { return new HashSet(); } }); for (Map.Entry entry : ((JsonObject) jsonElement).entrySet()) { for (JsonElement element : (JsonArray) entry.getValue()) { String value = element.getAsString(); if (null != value && !value.isEmpty()) { toReturn.get(entry.getKey()).add(value); } } if (!toReturn.containsKey(entry.getKey())) { toReturn.put(entry.getKey(), ALLOW_ALL_EVENTS); } } return toReturn; } }).create(); /** * Deduce whether the subscriber favors synchronous event consumption. See {@link SyncSubscribersGatekeeper} javadoc * for details of when will this be allowed. * * * @param subscribe The configuration for the subscriber in question. * @param eventClass Class of the event for which this check is to be done. * @param subscriberClass Class of the subscriber for which this check is to be done. * * @return true if the subscriber should be provided events synchronously. */ public static boolean isSyncSubscriber(SubscriberConfigProvider.SubscriberConfig subscribe, Class eventClass, Class subscriberClass) { if (subscribe.syncIfAllowed() && allowSyncSubs.get()) { SetMultimap whiteList = syncSubsWhiteList.get(); if (whiteList.isEmpty() || !whiteList.containsKey(subscriberClass.getName())) { return true; } else { Set allowedEvents = whiteList.get(subscriberClass.getName()); return allowedEvents.contains(ALLOW_ALL_EVENTS) || allowedEvents.contains(eventClass.getName()); } } return false; } @VisibleForTesting static void initState() { syncSubsWhiteList = new AtomicReference>(EMPTY_WHITELIST); allowSyncSubs = DynamicPropertyFactory.getInstance().getBooleanProperty(ALLOW_SYNC_SUBSCRIBERS, true); syncSubsWhitelistJson = DynamicPropertyFactory.getInstance().getStringProperty( SyncSubscribersGatekeeper.SYNC_SUBSCRIBERS_WHITELIST_JSON, "", new Runnable() { @Override public void run() { populateSubsWhiteList(); } } ); } private static void populateSubsWhiteList() { SetMultimap originalVal = syncSubsWhiteList.get(); String newWhiteListStr = syncSubsWhitelistJson.get(); SetMultimap newVal = EMPTY_WHITELIST; if (null != newWhiteListStr && !newWhiteListStr.isEmpty()) { try { SetMultimap parsed = whiteListJsonParser.fromJson(newWhiteListStr, whitelistTypeToken.getType()); if (null != parsed) { newVal = parsed; } } catch (JsonParseException e) { LOGGER.error(String.format("Illegal value %s for property %s. The value should be a json that can be converted to a type %s. Ignoring this change.", newWhiteListStr, SYNC_SUBSCRIBERS_WHITELIST_JSON, whitelistTypeToken.getType()), e); } } if (!syncSubsWhiteList.compareAndSet(originalVal, newVal)) { LOGGER.debug("Sync subscribers whitelist concurrently modified, ignoring this change: " + newWhiteListStr); } else { LOGGER.info("Sync subscribers whitelist updated to: " + newWhiteListStr); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy