Skip to content

Add RequestContextResolver Feature #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
java_version: [11, 17, 19]
java_version: [11, 17, 20]
os: [ubuntu-latest]

steps:
Expand Down
22 changes: 22 additions & 0 deletions http-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,26 @@
<tag>avaje-http-parent-1.19</tag>
</scm>

<dependencies>
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-inject</artifactId>
<version>9.0</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<forkCount>0</forkCount>
</configuration>
</plugin>
</plugins>
</build>
</project>
3 changes: 3 additions & 0 deletions http-api/src/main/java/io/avaje/http/api/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@

/** Specify the path mapping request to the controller. */
String value() default "";

/** Specify if the http request context should be instrumented via RequestContextResolver */
boolean instrumentRequestContext() default false;
}
12 changes: 6 additions & 6 deletions http-api/src/main/java/io/avaje/http/api/Delete.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
*
* }</pre>
*/
@Target(value=METHOD)
@Retention(value=RUNTIME)
@HttpMethod(value="DELETE")
@Target(METHOD)
@Retention(RUNTIME)
@HttpMethod("DELETE")
public @interface Delete {

/**
* Specify the path.
*/
/** Specify the path. */
String value() default "";

boolean instrumentRequestContext() default false;
}
10 changes: 7 additions & 3 deletions http-api/src/main/java/io/avaje/http/api/Get.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@
*
* }</pre>
*/
@Target(value=METHOD)
@Retention(value=RUNTIME)
@HttpMethod(value="GET")
@Target(METHOD)
@Retention(RUNTIME)
@HttpMethod("GET")
public @interface Get {

/** Specify the path. */
String value() default "";

boolean instrumentRequestContext() default false;
}
20 changes: 11 additions & 9 deletions http-api/src/main/java/io/avaje/http/api/Patch.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package io.avaje.http.api;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* Marks a method that handles HTTP PATCH requests.
*/
@Target(value = METHOD)
@Retention(value = RUNTIME)
@HttpMethod(value = "PATCH")
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/** Marks a method that handles HTTP PATCH requests. */
@Target(METHOD)
@Retention(RUNTIME)
@HttpMethod("PATCH")
public @interface Patch {

/** Specify the path. */
String value() default "";

boolean instrumentRequestContext() default false;
}
10 changes: 7 additions & 3 deletions http-api/src/main/java/io/avaje/http/api/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
...
* }
*/
@Target(value=METHOD)
@Retention(value=RUNTIME)
@HttpMethod(value="POST")
@Target(METHOD)
@Retention(RUNTIME)
@HttpMethod("POST")
public @interface Post {

/** Specify the path. */
String value() default "";

boolean instrumentRequestContext() default false;
}
10 changes: 7 additions & 3 deletions http-api/src/main/java/io/avaje/http/api/Put.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
/**
* Marks a method that handles HTTP PUT requests.
*/
@Target(value=METHOD)
@Retention(value=RUNTIME)
@HttpMethod(value="PUT")
@Target(METHOD)
@Retention(RUNTIME)
@HttpMethod("PUT")
public @interface Put {

/** Specify the path. */
String value() default "";

boolean instrumentRequestContext() default false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.avaje.http.api.context;

import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Supplier;

/**
* The holder for the current request context that is bound to instrumented threads. Allowing lookup
* of the current request if it is present. The Default implementation uses ThreadLocal. If you are
* able, you should provide an implementation using ScopedValues.
*/
public interface RequestContextResolver {

/**
* Wraps the execution of the given callable in request context processing.
*
* @param ctx The request context
* @param callable The callable
* @param <T> The return type of the callable
* @return The return value of the callable
* @throws Exception if the callable throws an exception
*/
<T> T callWith(ServerContext ctx, Callable<T> callable) throws Exception;

/**
* Wraps the execution of the given supplier in request context processing.
*
* @param ctx The request context
* @param supplier The supplier
* @param <T> The return type of the supplier
* @return The return value of the supplier
*/
<T> T supplyWith(ServerContext ctx, Supplier<T> supplier);

/**
* Wraps the execution of the given runnable in request context processing.
*
* @param ctx The request context
* @param runnable The runnable
*/
void runWith(ServerContext request, Runnable runnable);

/**
* Retrieve the current server context.
*
* @return The request context if it is present
*/
Optional<ServerContext> currentRequest();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.avaje.http.api.context;

/** Holder for the Server Request/Response Classes */
public class ServerContext {

private final Object request;
private final Object response;

public ServerContext(Object req, Object res) {
request = req;
response = res;
}

/**
* Retrieve the current server request.
*
* @return The request
*/
<T> T request() {
return (T) request;
}

/**
* Retrieve the current server response.
*
* @return The request
*/
<T> T response() {
return (T) response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.avaje.http.api.context;

import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Supplier;

public final class ThreadLocalRequestContextResolver implements RequestContextResolver {

private static final ThreadLocal<ServerContext> REQUEST = new ThreadLocal<>();

@Override
public <T> T callWith(ServerContext request, Callable<T> callable) throws Exception {
throwIfSet();
try {
REQUEST.set(request);
return callable.call();
} finally {
REQUEST.remove();
}
}

@Override
public <T> T supplyWith(ServerContext request, Supplier<T> supplier) {
throwIfSet();
try {
REQUEST.set(request);
return supplier.get();
} finally {
REQUEST.remove();
}
}

@Override
public void runWith(ServerContext request, Runnable runnable) {
throwIfSet();
try {
REQUEST.set(request);
runnable.run();
} finally {
REQUEST.remove();
}
}

@Override
public Optional<ServerContext> currentRequest() {
return Optional.ofNullable(REQUEST.get());
}

private void throwIfSet() {
if (REQUEST.get() != null) {
throw new IllegalStateException("Rebinding the ServerContext is not permitted");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.avaje.http.api.spi;

import io.avaje.http.api.context.RequestContextResolver;
import io.avaje.http.api.context.ThreadLocalRequestContextResolver;
import io.avaje.inject.BeanScopeBuilder;
import io.avaje.inject.spi.Plugin;

/** Plugin for avaje inject that provides a default RequestContextResolver instance. */
public final class DefaultResolverProvider implements Plugin {

@Override
public Class<?>[] provides() {
return new Class<?>[] {RequestContextResolver.class};
}

@Override
public void apply(BeanScopeBuilder builder) {
builder.provideDefault(null, RequestContextResolver.class, ThreadLocalRequestContextResolver::new);
}
}
7 changes: 6 additions & 1 deletion http-api/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
module io.avaje.http.api {

exports io.avaje.http.api;
exports io.avaje.http.api;
exports io.avaje.http.api.context;
exports io.avaje.http.api.spi;
requires static io.avaje.inject;

provides io.avaje.inject.spi.Plugin with io.avaje.http.api.spi.DefaultResolverProvider;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.avaje.http.api.spi.DefaultResolverProvider
Loading