ratpack.handling.RequestId Maven / Gradle / Ivy
/*
* Copyright 2015 the original author or authors.
*
* 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 ratpack.handling;
import com.google.common.reflect.TypeToken;
import ratpack.handling.internal.DefaultRequestId;
import ratpack.handling.internal.HeaderBasedRequestIdGenerator;
import ratpack.handling.internal.UuidBasedRequestIdGenerator;
import ratpack.http.Request;
import ratpack.util.Types;
import java.util.concurrent.ThreadLocalRandom;
/**
* An opaque identifier for the request.
*
* The request ID can then be obtained from the registry and used in response headers or logging.
* A request ID is always available.
*
* The value is determined by the {@link Generator} present in the server registry.
* By default, a {@link Generator#randomUuid() random UUID value is used}.
*
* The following example demonstrates a custom request ID strategy using an incrementing long.
*
*
{@code
* import ratpack.handling.RequestId;
* import ratpack.http.client.ReceivedResponse;
* import ratpack.test.embed.EmbeddedApp;
* import static org.junit.Assert.*;
*
* public class Example {
* public static void main(String... args) throws Exception {
* EmbeddedApp.fromHandlers(chain -> chain
* .all(ctx -> {
* ctx.getResponse().getHeaders().add("X-Request-ID", ctx.get(RequestId.class));
* ctx.render("ok");
* })
* ).test(httpClient -> {
* ReceivedResponse response = httpClient.get();
* assertEquals("ok", response.getBody().getText());
*
* // Default request ID generator generates random UUIDs (i.e. 36 chars long)
* assertEquals(36, response.getHeaders().get("X-Request-ID").length());
* });
* }
* }
* }
*
* Please note, adding an implementation to the request or context registries will have no effect.
* The generator is always obtained from the server registry.
*
* @see Generator
*/
public interface RequestId extends CharSequence {
/**
* A type token for this type.
*
* @since 1.1
*/
TypeToken TYPE = Types.token(RequestId.class);
/**
* Creates a new request ID from the given string.
*
* @param requestId the string of the request id
* @return a new request id
*/
static RequestId of(CharSequence requestId) {
return new DefaultRequestId(requestId);
}
/**
* Generates, or assigns, an ID for requests.
*
* {@code
* import ratpack.handling.RequestId;
* import ratpack.test.embed.EmbeddedApp;
*
* import java.util.concurrent.atomic.AtomicLong;
*
* import static org.junit.Assert.assertEquals;
*
* public class Example {
* public static void main(String... args) throws Exception {
* AtomicLong counter = new AtomicLong();
* EmbeddedApp.of(s -> s
* .registryOf(r -> r
* .add(RequestId.Generator.class, request -> RequestId.of(Long.toString(counter.incrementAndGet())))
* )
* .handlers(c -> c
* .get(ctx ->
* ctx.render("ID: " + ctx.get(RequestId.class))
* )
* )
* ).test(http -> {
* assertEquals(http.getText(), "ID: 1");
* assertEquals(http.getText(), "ID: 2");
* });
* }
* }
* }
*
* @see #randomUuid()
* @see #header(CharSequence)
*/
interface Generator {
/**
* A type token for this type.
*
* @since 1.1
*/
TypeToken TYPE = Types.token(Generator.class);
/**
* Generates IDs based of a random UUID.
*
* This strategy is installed into the server registry.
* It is used if no other strategy is provided.
*
* Internally {@link ThreadLocalRandom#current()} is used to produce values.
* Please consult its documentation for the nature of the randomness of the UUIDs.
*
* @return a request id generator
*/
static Generator randomUuid() {
return UuidBasedRequestIdGenerator.INSTANCE;
}
/**
* Creates a generator that uses the value for the given header, falling back to a {@link #randomUuid()} generator if the header is not present.
*
* This strategy is particularly useful in any kind of distributed environment, where a logical ID for the work is generated (or known) by the thing making the request.
* This applies to cloud environments like Heroku, where the edge router assigns a request ID.
*
*
{@code
* import ratpack.handling.RequestId;
* import ratpack.test.embed.EmbeddedApp;
*
* import static org.junit.Assert.assertEquals;
*
* public class Example {
* public static void main(String... args) throws Exception {
* EmbeddedApp.of(s -> s
* .registryOf(r -> r
* .add(RequestId.Generator.header("X-Request-ID"))
* )
* .handlers(c -> c
* .get(ctx ->
* ctx.render("ID: " + ctx.get(RequestId.class))
* )
* )
* ).test(http -> {
* http.requestSpec(r -> r.getHeaders().add("X-Request-ID", "foo"));
* assertEquals(http.getText(), "ID: foo");
* });
* }
* }
* }
*
* @param headerName the name of the header containing the request ID
* @return a request ID generator
* @see #header(CharSequence, RequestId.Generator)
*/
static Generator header(CharSequence headerName) {
return new HeaderBasedRequestIdGenerator(headerName, randomUuid());
}
/**
* Creates a generator that uses the value for the given header, using the given fallback generator if the header is not present.
*
* @param headerName the name of the header containing the request ID
* @param fallback the generator to use if the header is not present
* @return a request ID generator
* @see #header(CharSequence)
*/
static Generator header(CharSequence headerName, Generator fallback) {
return new HeaderBasedRequestIdGenerator(headerName, fallback);
}
/**
* Generate the ID for the request.
*
* @param request the request
* @return a request ID
*/
RequestId generate(Request request);
}
}