Skip to content

Commit

Permalink
Provide utilities to properly use Vert.x duplicated context and Conte…
Browse files Browse the repository at this point in the history
…xt locals.
  • Loading branch information
cescoffier committed Feb 14, 2022
1 parent 7d6bb5a commit f877f83
Show file tree
Hide file tree
Showing 6 changed files with 530 additions and 0 deletions.
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
<version.sundrio>0.80.0</version.sundrio>
<version.jandex-maven-plugin>1.2.2</version.jandex-maven-plugin>

<version.vertx>4.2.4</version.vertx>

<!-- Sonar settings -->
<sonar.projectName>SmallRye Common</sonar.projectName>
<sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
Expand Down Expand Up @@ -68,6 +70,7 @@
<module>net</module>
<module>os</module>
<module>version</module>
<module>vertx-context</module>
</modules>

<dependencyManagement>
Expand Down Expand Up @@ -122,6 +125,11 @@
<artifactId>smallrye-common-net</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.common</groupId>
<artifactId>smallrye-common-vertx-context</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
79 changes: 79 additions & 0 deletions vertx-context/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.smallrye.common</groupId>
<artifactId>smallrye-common-parent</artifactId>
<version>1.9.1-SNAPSHOT</version>
</parent>

<artifactId>smallrye-common-vertx-context</artifactId>
<name>SmallRye Common: Vert.x Context Utilities</name>
<description>A set of utility classes to ease the usage of Vert.x context locals and duplicated contexts.</description>

<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${version.vertx}</version>
</dependency>
<dependency>
<groupId>io.smallrye.common</groupId>
<artifactId>smallrye-common-constraint</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-junit5</artifactId>
<version>${version.vertx}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>coverage</id>
<properties>
<argLine>@{jacocoArgLine}</argLine>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.smallrye.common.vertx;

import java.util.Optional;

import io.smallrye.common.constraint.Assert;
import io.vertx.core.Context;
import io.vertx.core.Vertx;

/**
* Utilities to access Vert.x Context locals.
*/
public class ContextLocals {

private ContextLocals() {
// Avoid direct instantiation
}

private static Context ensureDuplicatedContext() {
Context current = Vertx.currentContext();
if (current == null || !VertxContext.isDuplicatedContext(current)) {
throw new UnsupportedOperationException("Access to Context Locals are forbidden from a 'root' context as " +
"it can leak data between unrelated processing. Make sure the method runs on a 'duplicated' (local)" +
" Context");
}
return current;
}

/**
* Gets the value from the context local associated with the given key.
*
* @param key the key, must not be {@code null}
* @param <T> the expected type of the associated value
* @return an optional containing the associated value if any, empty otherwise.
*/
public static <T> Optional<T> get(String key) {
Context current = ensureDuplicatedContext();
return Optional.ofNullable(current.getLocal(Assert.checkNotNullParam("key", key)));
}

/**
* Gets the value from the context local associated with the given key.
* If there is no associated value, it returns the given default.
*
* @param key the key, must not be {@code null}
* @param def the default value returned if there is no associated value with the given key.
* Can be {@code null}
* @param <T> the expected type of the associated value
* @return the associated value if any, the given default otherwise.
*/
public static <T> T get(String key, T def) {
Context current = ensureDuplicatedContext();
T local = current.getLocal(Assert.checkNotNullParam("key", key));
if (local == null) {
return def;
}
return local;
}

/**
* Stores the given key/value in the context local.
* This method overwrite the existing value if any.
*
* @param key the key, must not be {@code null}
* @param value the value, must not be {@code null}
* @param <T> the expected type of the associated value
*/
public static <T> void put(String key, T value) {
Context current = ensureDuplicatedContext();
current.putLocal(
Assert.checkNotNullParam("key", key),
Assert.checkNotNullParam("value", value));
}

/**
* Removes the value associated with the given key from the context locals.
*
* @param key the key, must not be {@code null}
* @return {@code true} if there were a value associated with the given key. {@code false} otherwise.
*/
public static boolean remove(String key) {
Context current = ensureDuplicatedContext();
return current.removeLocal(Assert.checkNotNullParam("key", key));
}

}
134 changes: 134 additions & 0 deletions vertx-context/src/main/java/io/smallrye/common/vertx/VertxContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package io.smallrye.common.vertx;

import io.smallrye.common.constraint.Assert;
import io.smallrye.common.constraint.Nullable;
import io.vertx.core.Context;
import io.vertx.core.Vertx;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.EventLoopContext;
import io.vertx.core.impl.WorkerContext;

/**
* Utility classes allowing to create duplicated contexts.
* <p>
* The rationale for this class is to avoid end-users to use the Vert.x internal API, and avoids common mistakes when
* dealing with Contexts and duplicated Contexts.
*/
public class VertxContext {

private VertxContext() {
// Avoid direct instantiation
}

/**
* Gets or creates a duplicated context.
* If the given context is already a duplicated context, it returns this duplicated context.
* Otherwise, it creates a new duplicated context, using the given context as <em>root</em> context.
*
* @param context the context, must not be {@code null}
* @return the given context if it's a duplicated context, or a new duplicated context using the given one as
* <em>root</em>.
*/
public static Context getOrCreateDuplicatedContext(Context context) {
if (isDuplicatedContext(context)) { // Also checks that the context is not null
return context;
} else {
return ((ContextInternal) context).duplicate();
}
}

/**
* Gets or creates a duplicated context.
* <p>
* If this method is called from a non-Vert.x thread (so, there is no current context), it creates a new (event loop)
* context and a duplicated context (and returns the duplicated context).
* <p>
* If this method is called from a Vert.x thread (so, there is a current context), and if the current context is
* already a duplicated context, it returns the current duplicated context.
* <p>
* It this method is called from a Vert.x thread (so, there is a current context), and if the current context is not
* a duplicated context, it creates a new duplicated context, using the current context as <em>root</em> context.
*
* @param vertx the Vert.x instance to use to create the context if needed. Must not be {@code null}
* @return the current context if it's a duplicated context, or a new duplicated context.
* @see #getOrCreateDuplicatedContext(Context)
*/
public static Context getOrCreateDuplicatedContext(Vertx vertx) {
Vertx actual = Assert.checkNotNullParam("vertx", vertx);
Context context = actual.getOrCreateContext(); // Creates an event loop context if none
if (isDuplicatedContext(context)) { // Also checks that the context is not null
return context;
} else {
return ((ContextInternal) context).duplicate();
}
}

/**
* Gets or creates a duplicated context.
* <p>
* If the method is not called from a Vert.x thread, it returns {@code null}.
* If the caller context is already a duplicated context, it returns this duplicated context.
* Otherwise, it creates a new duplicated context, using current context as <em>root</em> context.
*
* @return the current context if it's a duplicated context, a new duplicated context using the given one
* as <em>root</em>, {@code null} if not called from a Vert.x thread.
* @see #getOrCreateDuplicatedContext(Context)
*/
public static @Nullable Context getOrCreateDuplicatedContext() {
Context context = Vertx.currentContext();
if (context == null) {
return null;
}
return getOrCreateDuplicatedContext(context);
}

/**
* Creates a new duplicated context, even if the current one is already a duplicated context.
* If the method is not called from a Vert.x thread, it returns {@code null}.
*
* @return a new duplicated context if called from a Vert.x thread, {@code null} otherwise.
*/
public static @Nullable Context createNewDuplicatedContext() {
Context context = Vertx.currentContext();
if (context == null) {
return null;
}
// This creates a duplicated context from the root context of the current duplicated context (if that's one)
return ((ContextInternal) context).duplicate();
}

/**
* Checks if the given context is a duplicated context.
*
* @param context the context, must not be {@code null}
* @return {@code true} if the given context is a duplicated context, {@code false} otherwise.
*/
public static boolean isDuplicatedContext(Context context) {
Context actual = Assert.checkNotNullParam("context", context);
return !(actual instanceof EventLoopContext || actual instanceof WorkerContext);
}

/**
* Checks if the current context is a duplicated context.
* If the method is called from a Vert.x thread, it retrieves the current context and checks if it's a duplicated
* context. Otherwise, it returns false.
*
* @return {@code true} if the method is called from a duplicated context, {@code false} otherwise.
*/
public static boolean isOnDuplicatedContext() {
Context context = Vertx.currentContext();
return context != null && isDuplicatedContext(context);
}

/**
* Returns the parent context from a given Vert.x context.
* <p>
* A duplicate context returns the wrapped context otherwise the given context is returned.
*
* @param context the context, must not be {@code null}
* @return the <em>root</em> context if the given context is a duplicated context, returns the given context otherwise.
*/
public static Context getRootContext(Context context) {
return isDuplicatedContext(context) ? ((ContextInternal) context).unwrap() : context;
}
}
Loading

0 comments on commit f877f83

Please sign in to comment.