Skip to content

Commit

Permalink
Issue #103 propagation of third-party context types
Browse files Browse the repository at this point in the history
Signed-off-by: Nathan Rauh <nathan.rauh@us.ibm.com>
  • Loading branch information
njr-11 committed Oct 18, 2021
1 parent 780edbe commit a6e95ac
Show file tree
Hide file tree
Showing 9 changed files with 770 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,9 @@ public interface ContextService {
* @throws java.lang.IllegalArgumentException - if the {@code intf} argument
* is null or the instance does not implement the specified
* interface.
*
* @throws java.lang.UnsupportedOperationException - if the {@code intf}
* interface is {@link java.io.Serializable serializable}
* but a thread context type does not support serialization.
*/
public <T> T createContextualProxy(T instance, Class<T> intf);

Expand Down Expand Up @@ -312,7 +314,9 @@ public interface ContextService {
* @throws java.lang.IllegalArgumentException - if the {@code interfaces}
* argument is null or the instance does not implement
* all the specified interfaces.
*
* @throws java.lang.UnsupportedOperationException - if any of the {@code interfaces}
* are {@link java.io.Serializable serializable} but a thread context type
* does not support serialization.
*/
public Object createContextualProxy(Object instance, Class<?>... interfaces);

Expand Down Expand Up @@ -405,6 +409,9 @@ public interface ContextService {
*
* @throws java.lang.IllegalArgumentException - if the {@code intf} argument
* null or the instance does not implement the specified interface.
* @throws java.lang.UnsupportedOperationException - if the {@code intf}
* interface is {@link java.io.Serializable serializable}
* but a thread context type does not support serialization.
*/
public <T> T createContextualProxy(T instance,
Map<String, String> executionProperties,
Expand All @@ -428,6 +435,9 @@ public <T> T createContextualProxy(T instance,
* @throws java.lang.IllegalArgumentException - if the {@code interfaces}
* argument is null or the instance does not implement all the specified
* interfaces.
* @throws java.lang.UnsupportedOperationException - if any of the {@code interfaces}
* are {@link java.io.Serializable serializable} but a thread context type
* does not support serialization.
*/
public Object createContextualProxy(Object instance,
Map<String, String> executionProperties,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.enterprise.concurrent.spi;

import java.io.IOException;
import java.util.Map;

/**
* Third party providers of thread context implement this interface to
* participate in thread context capture and propagation.
* <p>
* Application code must never access the classes within this {@code spi}
* package. Instead, application code uses the various interfaces that are defined
* by the Jakarta Concurrency specification, such as
* {@link jakarta.enterprise.concurrent.ManagedExecutorService ManagedExecutorService}
* and {@link jakarta.enterprise.concurrent.ContextService ContextService}.
* <p>
* The {@code ThreadContextProvider} implementation and related classes are
* packaged within the third party provider's JAR file. The implementation is made
* discoverable via the {@link java.util.ServiceLoader ServiceLoader} mechanism. The JAR file
* that packages it must include a file with the following name and location,
* <p>
* {@code META-INF/services/jakarta.enterprise.concurrent.spi.ThreadContextProvider}
* <p>
* The content of the aforementioned file must be one or more lines, each specifying
* the fully qualified name of a {@code ThreadContextProvider} implementation
* that is provided within the JAR file.
* <p>
* {@code ThreadContextProvider} implementations can return a
* {@link ThreadContextSnapshot} that is also {@link java.io.Serializable Serializable}
* from the {@link #currentContext} and {@link #clearedContext} methods
* to allow for {@code Serializable} contextual proxies.
* <p>
* The Jakarta EE Product Provider must use the
* {@code ServiceLoader} to identify all available implementations of
* {@code ThreadContextProvider} that can participate in thread context capture
* and propagation and must invoke them either to capture current thread context or establish
* default thread context per the configuration of the
* {@link jakarta.enterprise.concurrent.ContextServiceDefinition ContextServiceDefinition},
* or vendor-specific configuration, and execution properties such as
* {@link jakarta.enterprise.concurrent.ManagedTask#TRANSACTION ManagedTask.TRANSACTION}
* that override context propagation configuration.
*
* @since 3.0
*/
// TODO Should we include the second-to-last paragraph above allowing for captured context
// to be serialized across JVMs? This might be useful for persistent timers/batch, but it is
// also a lot of extra complexity that would be simpler to leave out.
public interface ThreadContextProvider {
/**
* Captures from the current thread a snapshot of the provided thread context type.
*
* @param props execution properties, which are optionally provided
* by some types of tasks and contextual proxies.
* Thread context providers that do not supply or use execution properties
* can ignore this parameter.
* @return immutable snapshot of the provided type of context, captured from the
* current thread.
*/
public ThreadContextSnapshot currentContext(Map<String, String> props);

/**
* Returns empty/cleared context of the provided type. This context is not
* captured from the current thread, but instead represents the behavior that you
* get for this context type when no particular context has been applied to the
* thread.
* <p>
* This is used in cases where the provided type of thread context should not be
* propagated from the requesting thread or inherited from the thread of execution,
* in which case it is necessary to establish an empty/cleared context in its place,
* so that an action does not unintentionally inherit context of the thread that
* happens to run it.
* <p>
* For example, a security context provider's empty/cleared context ensures there
* is no authenticated user on the thread. A transaction context provider's
* empty/cleared context ensures that any active transaction is suspended.
* And so forth.
*
* @param props execution properties, which are optionally provided
* by some types of tasks and contextual proxies.
* Thread context providers that do not supply or use execution properties
* can ignore this parameter.
* @return immutable empty/default context of the provided type.
*/
public ThreadContextSnapshot clearedContext(Map<String, String> props);

// Similar to previous TODO comment, consider if we really want to support
// serializable contextual proxies for third-party context types.
/**
* Deserializes a previously serialized {@link ThreadContextSnapshot}.
*
* @param bytes a {@code ThreadContextSnapshot} from this provider
* that was serialized to an array of bytes by an
* {@link java.io.ObjectOutputStream ObjectOutputStream}.
* @return immutable snapshot of the provided type of context, deserialized
* from the byte array.
* @throws ClassNotFoundException if an error occurs during deserialization.
* @throws IOException if an error occurs during deserialization.
*/
public ThreadContextSnapshot deserialize(byte[] bytes) throws ClassNotFoundException, IOException;

/**
* Returns a human readable identifier for the type of thread context that is
* captured by this {@code ThreadContextProvider} implementation.
* <p>
* To ensure portability of applications, this is typically be a keyword that
* is defined by the same specification that defines the thread context type.
* <p>
* {@link jakarta.enterprise.concurrent.ContextServiceDefinition ContextServiceDefinition}
* defines identifiers for built-in thread context types, including
* {@link jakarta.enterprise.concurrent.ContextServiceDefinition#APPLICATION Application},
* {@link jakarta.enterprise.concurrent.ContextServiceDefinition#SECURITY Security}, and
* {@link jakarta.enterprise.concurrent.ContextServiceDefinition#TRANSACTION Transaction},
* as well as the
* {@link jakarta.enterprise.concurrent.ContextServiceDefinition#ALL_REMAINING Remaining}
* identifer which covers all remaining context.
* These identifiers must not be returned from this method.
* <p>
* Applications use a combination of built-in identifiers and those that are
* defined by other specifications and third-party context
* types when configuring a {@code ContextServiceDefinition}
* to capture and propagate only specific types of thread context.
* <p>
* For example:
* <pre>
* {@code @ManagedExecutorDefinition(}
* name = "java:module/concurrent/MyCustomContextExecutor",
* maxAsync = 3,
* context = {@code @ContextServiceDefinition(}
* name = "java:module/concurrent/MyCustomContext",
* propagated = MyCustomContextProvider.CONTEXT_NAME,
* cleared = { ContextServiceDefinition.SECURITY, ContextServiceDefinition.TRANSACTION },
* unchanged = ContextServiceDefinition.ALL_REMAINING))
* </pre>
* <p>
* It is an error for multiple thread context providers of an identical type to be
* simultaneously available.
*
* @return identifier for the provided type of thread context.
*/
public String getThreadContextType();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.enterprise.concurrent.spi;

/**
* Restores the prior context on a thread after a contextual task or action completes.
*/
@FunctionalInterface
public interface ThreadContextRestorer {
/**
* Invoked by the Jakarta EE Product Provider to remove the thread context that
* the {@link ThreadContextSnapshot} began on this thread and restore the previous
* context that was on the thread prior to that point. The Jakarta EE Product Provider
* must invoke the {@link #endContext endContext} method exactly
* once for each {@code ThreadContextRestorer} instance that it obtains
* and on the same thread from which the Jakarta EE Product Provider obtained it
* by invoking {@link ThreadContextSnapshot#begin ThreadContextSnapshot.begin}.
* <p>
* Typically, patterns such as the following will be observed:
* <pre>
* restorerA1 = contextA_snapshot1.begin();
* restorerB1 = contextB_snapshot1.begin();
* restorerC1 = contextC_snapshot1.begin();
* ...
* restorerC1.endContext();
* restorerB1.endContext();
* restorerA1.endContext();
* </pre>
* However, more advanced sequences such as the following are also valid:
* <pre>
* restorerA1 = contextA_snapshot1.begin();
* restorerB1 = contextB_snapshot1.begin();
* ...
* restorerC1 = contextC_snapshot1.begin();
* ...
* restorerC1.endContext();
* ...
* restorerB2 = contextB_snapshot2.begin();
* restorerC2 = contextC_snapshot2.begin();
* ...
* restorerC2.endContext();
* restorerB2.endContext();
* ...
* restorerB1.endContext();
* restorerA1.endContext();
* </pre>
*
* @throws IllegalStateException if invoked more than once on the same instance.
*
* @since 3.0
*/
public void endContext() throws IllegalStateException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.enterprise.concurrent.spi;

/**
* An immutable snapshot of a particular type of thread context.
* <p>
* The captured context represented by this snapshot can be applied to
* any number of threads, including concurrently.
* <p>
* Any state that is associated with context applied to a thread should
* be kept, not within the snapshot, but within the distinct
* {@link ThreadContextRestorer} instance that this
* {@code ThreadContextSnapshot} creates each time it is applied
* to a thread.
*
* @since 3.0
*/
@FunctionalInterface
public interface ThreadContextSnapshot {
/**
* Applies the captured thread context snapshot to the current thread and
* returns a distinct {@link ThreadContextRestorer} instance. The
* {@code ThreadContextRestorer} instance tracks the context's life cycle,
* including any state that is associated with it or that is necessary for
* restoring the previous context.
* <p>
* For each invocation of this method, the Jakarta EE Product Provider
* must invoke the corresponding
* {@link ThreadContextRestorer#endContext endContext}
* method exactly once, such that the previous context is restored
* on the thread. If the Jakarta EE Product Provider sequentially begins
* multiple {@code ThreadContextRestorer} instances on a thread,
* it must invoke the corresponding {@code endContext} methods in reverse
* order.
*
* @return restorer instance that reverts the state of this context type
* on the thread to what it was prior to applying this snapshot.
*/
public ThreadContextRestorer begin();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

/**
* Interfaces for third-party providers of thread context to implement.
*/
package jakarta.enterprise.concurrent.spi;

0 comments on commit a6e95ac

Please sign in to comment.