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

io.reactivex.netty.contexts.ThreadLocalRequestCorrelator Maven / Gradle / Ivy

There is a newer version: 0.5.1
Show newest version
/*
 * 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 io.reactivex.netty.contexts;

import io.netty.channel.EventLoopGroup;
import io.netty.util.AttributeKey;
import io.netty.util.AttributeMap;
import io.netty.util.DefaultAttributeMap;
import io.reactivex.netty.client.RxClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PrintStream;
import java.util.Stack;
import java.util.concurrent.Callable;

/**
 * An implementation of {@link RequestCorrelator} that fetches {@link ContextsContainer} and request Identifer from
 * {@link ThreadLocal} variables. 
* The usage of this correlator assumes that every {@link EventLoopGroup} used by all {@link RxClient}s in use for * the propagation of these contexts, uses {@link ContextAwareEventLoopGroup}.
* In case, the application is using any other threadpools, the invariant of the thread state copied from one thread * to another is maintained. * * @author Nitesh Kant */ public class ThreadLocalRequestCorrelator implements RequestCorrelator { private static final Logger logger = LoggerFactory.getLogger(ThreadLocalRequestCorrelator.class); protected static final ThreadLocal state = new ThreadLocal() { @Override protected ThreadStateHolder initialValue() { return new ThreadStateHolder(); } }; @Override public String getRequestIdForClientRequest() { return state.get().getRequestId(); } @Override public ContextsContainer getContextForClientRequest(String requestId) { return state.get().getContainer(); } @Override public void onNewServerRequest(String requestId, ContextsContainer contextsContainer) { if (null == requestId) { throw new IllegalArgumentException("Request Id can not be null."); } if (null == contextsContainer) { throw new IllegalArgumentException("Context container can not be null."); } state.get().push(new DefaultAttributeMap()); state.get().setRequestId(requestId); state.get().setContainer(contextsContainer); } @Override public void beforeNewClientRequest(String requestId, ContextsContainer contextsContainer) { onNewServerRequest(requestId, contextsContainer); // same effect } @Override public void onClientProcessingEnd(String requestId) { state.get().pop(); } @Override public void onServerProcessingEnd(String requestId) { state.get().pop(); } public static ContextsContainer getCurrentContextContainer() { return state.get().getContainer(); } public static String getCurrentRequestId() { return state.get().getRequestId(); } @Override public Callable makeClosure(final Callable original) { final AttributeMap currentState = state.get().peek(); return new Callable() { @Override public V call() throws Exception { state.get().push(currentState); try { return original.call(); } finally { state.get().pop(); } } }; } @Override public Runnable makeClosure(final Runnable original) { final AttributeMap currentState = state.get().peek(); return new Runnable() { @Override public void run() { state.get().push(currentState); try { original.run(); } finally { state.get().pop(); } } }; } public void dumpThreadState() { if (logger.isDebugEnabled()) { ThreadStateHolder threadStateHolder = state.get(); logger.debug("******************** Thread Attributes ********************"); int count = 0; for (AttributeMap threadAttribute : threadStateHolder.threadAttributes) { logger.debug("Stack level==> " + ++count); logger.debug("Request Id: " + threadAttribute.attr(ThreadStateHolder.requestIdKey)); logger.debug("Context container: " + threadAttribute.attr(ThreadStateHolder.containerKey)); } logger.debug("***********************************************************"); } } public void dumpThreadState(PrintStream outputStream) { ThreadStateHolder threadStateHolder = state.get(); outputStream.println("******************** Thread Attributes ********************"); int count = 0; for (AttributeMap threadAttribute : threadStateHolder.threadAttributes) { outputStream.println("Stack level==> " + ++count); outputStream.println("Request Id: " + threadAttribute.attr(ThreadStateHolder.requestIdKey)); outputStream.println("Context container: " + threadAttribute.attr(ThreadStateHolder.containerKey)); } outputStream.println("***********************************************************"); } public static final class ThreadStateHolder { private static final AttributeKey requestIdKey = AttributeKey.valueOf("rxnetty-contexts-threadlocal-request-id-key"); private static final AttributeKey containerKey = AttributeKey.valueOf("rxnetty-contexts-threadlocal-context-container-key"); /** * This is a stack because the client and server processing can be on the same thread, either by chance or * just because there is only a single thread in the system. * This means we can not simply set & unset on start & stop of processing as it can step on other's feet. * The best way to handle is to use a stack where in states are pushed and popped. If we pop everything, it is * as good as clear state. * Here {@link AttributeMap} is used just to have a map that stores multiple objects and still is type safe. */ private final Stack threadAttributes = new Stack(); public void push(AttributeMap map) { threadAttributes.push(map); } public AttributeMap pop() { return threadAttributes.pop(); } public AttributeMap peek() { if (isEmpty()) { return null; } return threadAttributes.peek(); } public boolean isEmpty() { return threadAttributes.isEmpty(); } public void setRequestId(String requestId) { if (!isEmpty()) { peek().attr(requestIdKey).set(requestId); } } public void setContainer(ContextsContainer container) { if (!isEmpty()) { peek().attr(containerKey).set(container); } } public String getRequestId() { if (isEmpty()) { return null; } return peek().attr(requestIdKey).get(); } public ContextsContainer getContainer() { if (isEmpty()) { return null; } return peek().attr(containerKey).get(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy