/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.toolkit.modules.concurrency.api.threadcontext; /** * Builder for immutable {@link ThreadContext} instances. * * @author Robert Mischke */ public final class ThreadContextBuilder { private ThreadContext currentContextHead; /** * Internal {@link ThreadContext} implementation representing the empty "root" context. Implemented as a separate class to avoid * frequent "parent == null" checks during aspect lookup. * * @author Robert Mischke */ private static final class ImmutableRootThreadContextImpl implements ThreadContext { @Override public <T> T getAspect(Class<T> aspectType) { return null; } } /** * Internal {@link ThreadContext} implementation. Instead of a Map, which would require frequent copying on modification, this is * implemented as a backwards-linked list of immutable elements. Lookup is performed by simple linear backwards search, which should * also be more efficient for the low expected number of aspects per context. * * @author Robert Mischke */ private static final class ImmutableThreadContextImpl implements ThreadContext { private final ThreadContext parentContext; private final Object aspectKey; private final Object aspectValue; /** * @param inputMap the map to wrap into this {@link ThreadContext}; note that for efficiency, this map is NOT copied but referenced * - therefore, it MUST NOT be modified externally after it has been passed to this constructor */ <T> ImmutableThreadContextImpl(ThreadContext parentContext, Class<T> aspectKey, T aspectValue) { this.parentContext = parentContext; this.aspectKey = aspectKey; this.aspectValue = aspectValue; } @Override @SuppressWarnings("unchecked") public <T> T getAspect(Class<T> aspectType) { if (aspectType == this.aspectKey) { // note: expects key types to be instantiated by the same classloader return (T) aspectValue; } else { return parentContext.getAspect(aspectType); } } @Override public String toString() { ThreadContextNameProvider nameProvider = getAspect(ThreadContextNameProvider.class); if (nameProvider != null) { return nameProvider.getName(this); } else { return super.toString(); // fallback } } } private ThreadContextBuilder(ThreadContext parent) { this.currentContextHead = parent; } /** * @return a new builder containing no aspects */ public static ThreadContextBuilder empty() { return new ThreadContextBuilder(new ImmutableRootThreadContextImpl()); } /** * @param parentContext the context to inherit all aspects from * @return a new builder containing all aspects of the provided context */ public static ThreadContextBuilder from(ThreadContext parentContext) { return new ThreadContextBuilder(parentContext); } /** * @return a new builder inheriting all aspects of the current thread's context; delegates to {@link #empty()} if the current context is * null */ public static ThreadContextBuilder fromCurrent() { final ThreadContext currentContext = ThreadContextHolder.getCurrentContext(); if (currentContext != null) { return from(currentContext); } else { return empty(); // creates the required "root" parent } } /** * Registers an "aspect" - an arbitrary object matching the provided key type. * * @param <T> the type (usually an interface) to register the aspect object under * @param key the type (usually an interface) to register the aspect object under * @param value the aspect object to register * @return the builder (for call chaining) */ public <T> ThreadContextBuilder setAspect(Class<T> key, T value) { currentContextHead = new ImmutableThreadContextImpl(currentContextHead, key, value); return this; } /** * Sets a static name for the {@link ThreadContext} under construction. If the name contains dynamic parts depending on context data, * consider using {@link #setNameProvider(ThreadContextNameProvider)} and generating the name on the fly instead. * * @param staticName the name to set * @return the builder (for call chaining) */ public ThreadContextBuilder setName(String staticName) { setAspect(ThreadContextNameProvider.class, new ThreadContextStaticNameProvider(staticName)); return this; } /** * Sets a dynamic name provider for the {@link ThreadContext} under construction. It is a typical use case to set a shared * {@link ThreadContextNameProvider} for similar {@link ThreadContext}s, and have the provider generate the specific name for each * context on the fly. * * @param nameProvider a dynamic generator for names of {@link ThreadContext}s * @return the builder (for call chaining) */ public ThreadContextBuilder setNameProvider(ThreadContextNameProvider nameProvider) { setAspect(ThreadContextNameProvider.class, nameProvider); return this; } /** * Wraps the current builder state into an immutable {@link ThreadContext}. * * @return the generated {@link ThreadContext} */ public ThreadContext build() { return currentContextHead; } }