Skip to content
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

Attribute binders for ServletConfig/ServletContext #708

Merged
merged 4 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ class JettyParameterBinding2Spec extends Specification {
response.body() == 'Hello text/plain;q=1.0'
}

void "test context binding"() {
given:
def request = HttpRequest.GET("/parameters/context")
request.header(HttpHeaders.CONTENT_TYPE, "text/plain;q=1.0")
def response = client.toBlocking().exchange(request, String)

expect:
response.status() == HttpStatus.OK
response.contentType.get() == MediaType.TEXT_PLAIN_TYPE
response.body() == 'Hello micronaut /'
}

void "test request and response"() {
given:
def request = HttpRequest.GET("/parameters/reqAndRes")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.micronaut.context.annotation.Factory;
import io.micronaut.core.annotation.Order;
import io.micronaut.core.order.Ordered;
import io.micronaut.servlet.engine.annotation.ServletFilterBean;
import io.micronaut.servlet.api.annotation.ServletFilterBean;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.GenericFilter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import io.micronaut.http.annotation.*;
import io.micronaut.http.cookie.Cookie;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
Expand Down Expand Up @@ -34,6 +36,11 @@ String headerValue(@Header(HttpHeaders.CONTENT_TYPE) String contentType) {
return "Hello " + contentType;
}

@Get(value = "/context", produces = MediaType.TEXT_PLAIN)
String context(ServletConfig config, ServletContext servletContext) {
return "Hello " + config.getServletName() + " " + servletContext.getServletContextName();
}

@Get("/cookies")
io.micronaut.http.HttpResponse<String> cookies(@CookieValue String myCookie) {
return io.micronaut.http.HttpResponse.ok(myCookie)
Expand Down
12 changes: 12 additions & 0 deletions servlet-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id("io.micronaut.build.internal.servlet.module")
}

dependencies {
api(projects.micronautServletCore)
api(libs.managed.servlet.api)
}

micronautBuild {
binaryCompatibility.enabled.set(false)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2017-2024 original 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
*
* https://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 io.micronaut.servlet.api;

import io.micronaut.core.annotation.NonNull;

/**
* Attributes to lookup servlet related types from the request.
*
* @see io.micronaut.http.HttpAttributes
*/
public enum ServletAttributes implements CharSequence {

/**
* Attribute to lookup the {@link jakarta.servlet.ServletConfig}.
*/
SERVLET_CONFIG("io.micronaut.servlet.SERVLET_CONFIG"),

/**
* Attribute to lookup the {@link jakarta.servlet.ServletContext}.
*/
SERVLET_CONTEXT("io.micronaut.servlet.SERVLET_CONTEXT");

private final String name;

ServletAttributes(String name) {
this.name = name;
}

@Override
public int length() {
return name.length();
}

@Override
public char charAt(int index) {
return name.charAt(index);
}

@Override
@NonNull
public CharSequence subSequence(int start, int end) {
return name.substring(start, end);
}

@Override
@NonNull
public String toString() {
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.servlet.engine.annotation;
package io.micronaut.servlet.api.annotation;

import io.micronaut.context.annotation.AliasFor;
import io.micronaut.context.annotation.Bean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.servlet.engine.annotation;
package io.micronaut.servlet.api.annotation;

import io.micronaut.context.annotation.AliasFor;
import io.micronaut.context.annotation.Bean;
Expand Down
2 changes: 1 addition & 1 deletion servlet-engine/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dependencies {
annotationProcessor mn.micronaut.graal
annotationProcessor(projects.micronautServletProcessor)

api(projects.micronautServletCore)
api(projects.micronautServletApi)
api libs.managed.servlet.api

implementation(mnReactor.micronaut.reactor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import io.micronaut.context.ApplicationContextBuilder;
import io.micronaut.context.env.Environment;
import io.micronaut.core.annotation.TypeHint;

import io.micronaut.servlet.api.ServletAttributes;
import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;
import jakarta.servlet.annotation.WebServlet;
Expand Down Expand Up @@ -71,6 +71,8 @@ public DefaultMicronautServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
if (handler != null) {
req.setAttribute(ServletAttributes.SERVLET_CONFIG.toString(), getServletConfig());
req.setAttribute(ServletAttributes.SERVLET_CONTEXT.toString(), getServletContext());
handler.service(req, resp);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.value.MutableConvertibleValues;
import io.micronaut.core.convert.value.MutableConvertibleValuesMap;
import io.micronaut.core.execution.ExecutionFlow;
import io.micronaut.core.io.buffer.ByteBuffer;
import io.micronaut.core.type.Argument;
Expand Down Expand Up @@ -52,12 +51,6 @@
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.reactivestreams.Subscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -76,8 +69,12 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import org.reactivestreams.Subscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

/**
* Implementation of {@link HttpRequest} ontop of the Servlet API.
Expand All @@ -87,15 +84,15 @@
* @since 1.0.0
*/
@Internal
public final class DefaultServletHttpRequest<B> extends MutableConvertibleValuesMap<Object> implements
public final class DefaultServletHttpRequest<B> implements
ServletHttpRequest<HttpServletRequest, B>,
MutableConvertibleValues<Object>,
ServletExchange<HttpServletRequest, HttpServletResponse>,
StreamedServletMessage<B, byte[]>,
FullHttpRequest<B>,
ParsedBodyHolder<B> {

private static final Logger LOG = LoggerFactory.getLogger(DefaultServletHttpRequest.class);
private static final String NULL_KEY = "Attribute key cannot be null";

private final ConversionService conversionService;
private final HttpServletRequest delegate;
Expand All @@ -105,6 +102,7 @@ public final class DefaultServletHttpRequest<B> extends MutableConvertibleValues
private final ServletParameters parameters;
private final DefaultServletHttpResponse<B> response;
private final MediaTypeCodecRegistry codecRegistry;
private final MutableConvertibleValues<Object> attributes;
private DefaultServletCookies cookies;
private Supplier<Optional<B>> body;

Expand All @@ -126,7 +124,7 @@ protected DefaultServletHttpRequest(ConversionService conversionService,
HttpServletResponse response,
MediaTypeCodecRegistry codecRegistry,
BodyBuilder bodyBuilder) {
super(new ConcurrentHashMap<>(), conversionService);
super();
this.conversionService = conversionService;
this.delegate = delegate;
this.codecRegistry = codecRegistry;
Expand All @@ -153,6 +151,54 @@ protected DefaultServletHttpRequest(ConversionService conversionService,
B built = parsedBody != null ? parsedBody : (B) bodyBuilder.buildBody(this::getInputStream, this);
return Optional.ofNullable(built);
});
this.attributes = new MutableConvertibleValues<>() {

@Override
public <T> Optional<T> get(CharSequence name, ArgumentConversionContext<T> conversionContext) {
Objects.requireNonNull(conversionContext, "Conversion context cannot be null");
Objects.requireNonNull(name, NULL_KEY);
Object attribute = delegate.getAttribute(name.toString());
return Optional.ofNullable(attribute)
.flatMap(v -> conversionService.convert(v, conversionContext));
}

@Override
public Set<String> names() {
return CollectionUtils.enumerationToSet(delegate.getAttributeNames());
}

@Override
public Collection<Object> values() {
return names().stream().map(delegate::getAttribute).toList();
}

@Override
public MutableConvertibleValues<Object> put(CharSequence key, @Nullable Object value) {
Objects.requireNonNull(key, NULL_KEY);
delegate.setAttribute(key.toString(), value);
return this;
}

@Override
public MutableConvertibleValues<Object> remove(CharSequence key) {
Objects.requireNonNull(key, NULL_KEY);
delegate.removeAttribute(key.toString());
return this;
}

@Override
public MutableConvertibleValues<Object> clear() {
names().forEach(delegate::removeAttribute);
return this;
}
};
}

/**
* @return The conversion service.
*/
public ConversionService getConversionService() {
return conversionService;
}

@Override
Expand All @@ -164,11 +210,6 @@ public HttpVersion getHttpVersion() {
};
}

@Override
public ConversionService getConversionService() {
return this.conversionService;
}

/**
* @return The codec registry.
*/
Expand Down Expand Up @@ -334,7 +375,7 @@ public HttpHeaders getHeaders() {
@NonNull
@Override
public MutableConvertibleValues<Object> getAttributes() {
return this;
return this.attributes;
}

@Override
Expand All @@ -348,17 +389,6 @@ public Optional<B> getBody() {
return this.body.get();
}

@Override
public MutableConvertibleValues<Object> put(CharSequence key, @Nullable Object value) {
String name = Objects.requireNonNull(key, "Key cannot be null").toString();
if (value == null) {
super.remove(name);
} else {
super.put(name, value);
}
return this;
}

@SuppressWarnings("unchecked")
@Override
public ServletHttpRequest<HttpServletRequest, ? super Object> getRequest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ public MutableHttpHeaders getHeaders() {
@NonNull
@Override
public MutableConvertibleValues<Object> getAttributes() {
return request;
return request.getAttributes();
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import io.micronaut.servlet.http.ServletBodyBinder;
import io.micronaut.servlet.http.StreamedServletMessage;
import jakarta.inject.Singleton;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import org.reactivestreams.Processor;
import reactor.core.publisher.Flux;

Expand Down Expand Up @@ -80,6 +82,8 @@ public DefaultServletBinderRegistry(
super(mediaTypeCodecRegistry, conversionService, binders, defaultBodyAnnotationBinder);
byType.put(HttpServletRequest.class, new ServletRequestBinder());
byType.put(HttpServletResponse.class, new ServletResponseBinder());
byType.put(ServletConfig.class, new ServletConfigBinder());
byType.put(ServletContext.class, new ServletContextBinder());
byType.put(CompletedPart.class, new CompletedPartRequestArgumentBinder());
byAnnotation.put(Part.class, new ServletPartBinder<>(mediaTypeCodecRegistry));
}
Expand Down
Loading
Loading