/* * * Copyright 2014 Netflix, Inc. * * 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.netflix.loadbalancer.reactive; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfigKey; import com.netflix.loadbalancer.reactive.ExecutionListener.AbortExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * Utility class to invoke the list of {@link ExecutionListener} with {@link ExecutionContext} * * @author Allen Wang */ public class ExecutionContextListenerInvoker<I, O> { private final static Logger logger = LoggerFactory.getLogger(ExecutionContextListenerInvoker.class); private final ExecutionContext<I> context; private final List<ExecutionListener<I, O>> listeners; private final IClientConfig clientConfig; private final ConcurrentHashMap<String, IClientConfigKey> classConfigKeyMap; public ExecutionContextListenerInvoker(ExecutionContext<I> context, List<ExecutionListener<I, O>> listeners) { this(context, listeners, null); } public ExecutionContextListenerInvoker(List<ExecutionListener<I, O>> listeners, IClientConfig config) { this(null, listeners, config); } public ExecutionContextListenerInvoker(ExecutionContext<I> context, List<ExecutionListener<I, O>> listeners, IClientConfig config) { this.listeners = Collections.unmodifiableList(listeners); this.context = context; this.clientConfig = config; if (clientConfig != null) { classConfigKeyMap = new ConcurrentHashMap<String, IClientConfigKey>(); } else { classConfigKeyMap = null; } } public ExecutionContextListenerInvoker(List<ExecutionListener<I, O>> listeners) { this(null, listeners); } public void onExecutionStart() { onExecutionStart(this.context); } public void onExecutionStart(ExecutionContext<I> context) { for (ExecutionListener<I, O> listener : listeners) { try { if (!isListenerDisabled(listener)) { listener.onExecutionStart(context.getChildContext(listener)); } } catch (Throwable e) { if (e instanceof AbortExecutionException) { throw (AbortExecutionException) e; } logger.error("Error invoking listener " + listener, e); } } } public void onStartWithServer(ExecutionInfo info) { onStartWithServer(this.context, info); } /** * Called when a server is chosen and the request is going to be executed on the server. * */ public void onStartWithServer(ExecutionContext<I> context, ExecutionInfo info) { for (ExecutionListener<I, O> listener: listeners) { try { if (!isListenerDisabled(listener)) { listener.onStartWithServer(context.getChildContext(listener), info); } } catch (Throwable e) { if (e instanceof AbortExecutionException) { throw (AbortExecutionException) e; } logger.error("Error invoking listener " + listener, e); } } } public void onExceptionWithServer(Throwable exception, ExecutionInfo info) { onExceptionWithServer(this.context, exception, info); } /** * Called when an exception is received from executing the request on a server. * * @param exception Exception received */ public void onExceptionWithServer(ExecutionContext<I> context, Throwable exception, ExecutionInfo info) { for (ExecutionListener<I, O> listener: listeners) { try { if (!isListenerDisabled(listener)) { listener.onExceptionWithServer(context.getChildContext(listener), exception, info); } } catch (Throwable e) { logger.error("Error invoking listener " + listener, e); } } } public void onExecutionSuccess(O response, ExecutionInfo info) { onExecutionSuccess(this.context, response, info); } /** * Called when the request is executed successfully on the server * * @param response Object received from the execution */ public void onExecutionSuccess(ExecutionContext<I> context, O response, ExecutionInfo info) { for (ExecutionListener<I, O> listener: listeners) { try { if (!isListenerDisabled(listener)) { listener.onExecutionSuccess(context.getChildContext(listener), response, info); } } catch (Throwable e) { logger.error("Error invoking listener " + listener, e); } } } public void onExecutionFailed(Throwable finalException, ExecutionInfo info) { onExecutionFailed(this.context, finalException, info); } /** * Called when the request is considered failed after all retries. * * @param finalException Final exception received. */ public void onExecutionFailed(ExecutionContext<I> context, Throwable finalException, ExecutionInfo info) { for (ExecutionListener<I, O> listener: listeners) { try { if (!isListenerDisabled(listener)) { listener.onExecutionFailed(context.getChildContext(listener), finalException, info); } } catch (Throwable e) { logger.error("Error invoking listener " + listener, e); } } } private boolean isListenerDisabled(ExecutionListener<?, ?> listener) { if (clientConfig == null) { return false; } else { String className = listener.getClass().getName(); IClientConfigKey key = classConfigKeyMap.get(className); if (key == null) { key = CommonClientConfigKey.valueOf("listener." + className + ".disabled"); IClientConfigKey old = classConfigKeyMap.putIfAbsent(className, key); if (old != null) { key = old; } } return clientConfig.getPropertyAsBoolean(key, false); } } }