Skip to content

Add missing attributes to ServletRegistration annotation #45007

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

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -62,6 +62,7 @@
* @author Phillip Webb
* @author Brian Clozel
* @author Moritz Halbritter
* @author Dmytro Danilenkov
* @since 1.4.0
*/
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
Expand Down Expand Up @@ -321,10 +322,59 @@ private void configureFromAnnotation(ServletRegistrationBean<Servlet> bean, Serv
if (registration.urlMappings().length > 0) {
bean.setUrlMappings(Arrays.asList(registration.urlMappings()));
}

if (registration.initParameters().length > 0) {
bean.setInitParameters(parseInitParameters(registration.initParameters()));
}

ServletRegistration.MultipartConfigValues multipart = registration.multipartConfig();
boolean isMultipartConfigUsed = !(multipart.location().isEmpty()
&& multipart.maxFileSize() == -1L
&& multipart.maxRequestSize() == -1L
&& multipart.fileSizeThreshold() == 0);
if (isMultipartConfigUsed) {
bean.setMultipartConfig(new MultipartConfigElement(
multipart.location(),
multipart.maxFileSize(),
multipart.maxRequestSize(),
multipart.fileSizeThreshold()
));
}

for (Class<? extends ServletRegistrationBean<?>> beanClass : registration.servletRegistrationBeans()) {
ServletRegistrationBean<?> extraBean = this.beanFactory.getBean(beanClass);
bean.getInitParameters().putAll(extraBean.getInitParameters());
}

}

/**
* Parses an array of "key=value" strings into a Map.
* @param initParamsArray Array of strings, expected format "key=value".
* @return Map of parsed key-value pairs.
* @throws IllegalArgumentException if any string doesn't match the "key=value" format.
*/
private Map<String, String> parseInitParameters(String[] initParamsArray) {
Map<String, String> initParams = new LinkedHashMap<>();
for (String kv : initParamsArray) {
int index = kv.indexOf('=');
if (index != -1) {
String key = kv.substring(0, index).trim();
String value = kv.substring(index + 1).trim();
initParams.put(key, value);
}
else {
throw new IllegalArgumentException(
"initParameters must be in 'key=value' format, got: " + kv);
}
}
return initParams;
}

}



/**
* {@link RegistrationBeanAdapter} for {@link Filter} beans.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.lang.annotation.Target;

import jakarta.servlet.Servlet;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebInitParam;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.AliasFor;
Expand All @@ -33,6 +35,7 @@
* annotation-based alternative to {@link ServletRegistrationBean}.
*
* @author Moritz Halbritter
* @author Dmytro Danilenkov
* @since 3.5.0
* @see ServletRegistrationBean
*/
Expand Down Expand Up @@ -87,4 +90,50 @@
*/
int loadOnStartup() default -1;

/**
* Init parameters to set on the servlet, as {@code "key=value"} pairs.
*/
String[] initParameters() default {};

/**
* (Optional) Additional servlet-registration beans to apply.
* Usually left empty unless you need custom bean logic.
*/
Class<? extends ServletRegistrationBean<?>>[] servletRegistrationBeans() default {};

/**
* Multipart configuration. Mirrors {@link jakarta.servlet.annotation.MultipartConfig}.
* If you omit it (no fields changed), it will not set a multipart config.
*/
MultipartConfigValues multipartConfig() default @MultipartConfigValues;

/**
* Nested annotation that parallels the fields of
* {@link jakarta.servlet.annotation.MultipartConfig}. Used within
* {@link ServletRegistration#multipartConfig()}.
* @see jakarta.servlet.annotation.MultipartConfig
*/
@Target({})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface MultipartConfigValues {

/**
* @see jakarta.servlet.annotation.MultipartConfig#location()
*/
String location() default "";
/**
* @see jakarta.servlet.annotation.MultipartConfig#maxFileSize()
*/
long maxFileSize() default -1L;
/**
* @see jakarta.servlet.annotation.MultipartConfig#maxRequestSize()
*/
long maxRequestSize() default -1L;
/**
* @see jakarta.servlet.annotation.MultipartConfig#fileSizeThreshold()
*/
int fileSizeThreshold() default 0;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
*
* @author Andy Wilkinson
* @author Moritz Halbritter
* @author Dmytro Danilenkov
*/
class ServletContextInitializerBeansTests {

Expand Down Expand Up @@ -179,6 +180,55 @@ void shouldApplyOrderFromOrderAttribute() {
.isEqualTo(ServletConfigurationWithAnnotationAndOrder.ORDER));
}

@Test
void shouldApplyExtendedServletRegistrationAnnotation() {
load(ServletConfigurationWithExtendedAttributes.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);

ServletRegistrationBean<?> bean = findServletRegistrationBeanByName(initializerBeans, "extended");

assertThat(bean.getServletName()).isEqualTo("extended");
assertThat(bean.getUrlMappings()).containsExactly("/extended/*");

assertThat(bean.getInitParameters()).containsEntry("hello", "world")
.containsEntry("flag", "true");

assertThat(bean.getMultipartConfig()).isNotNull();
assertThat(bean.getMultipartConfig().getLocation()).isEqualTo("/tmp");
assertThat(bean.getMultipartConfig().getMaxFileSize()).isEqualTo(1024);
assertThat(bean.getMultipartConfig().getMaxRequestSize()).isEqualTo(4096);
assertThat(bean.getMultipartConfig().getFileSizeThreshold()).isEqualTo(128);
}

@Test
void shouldApplyServletRegistrationAnnotationWithExtraRegistrationBeans() {
load(ServletConfigurationWithExtendedAttributes.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);

ServletRegistrationBean<?> bean = findServletRegistrationBeanByName(initializerBeans, "extendedWithExtraBeans");
assertThat(bean).as("extendedWithExtraBeans registration bean").isNotNull();

assertThat(bean.getServletName()).isEqualTo("extendedWithExtraBeans");
assertThat(bean.getUrlMappings()).containsExactly("/extra/*");

assertThat(bean.getInitParameters()).containsEntry("extra", "fromExtraBean");
}

@SuppressWarnings("rawtypes")
private ServletRegistrationBean findServletRegistrationBeanByName(
ServletContextInitializerBeans initializerBeans, String servletName) {

return initializerBeans.stream()
.filter(ServletRegistrationBean.class::isInstance)
.map(ServletRegistrationBean.class::cast)
.filter((registrationBean) -> servletName.equals(registrationBean.getServletName()))
.findFirst()
.orElse(null);
}


private void load(Class<?>... configuration) {
this.context = new AnnotationConfigApplicationContext(configuration);
}
Expand Down Expand Up @@ -385,4 +435,50 @@ public void onStartup(ServletContext servletContext) {

}

@Configuration(proxyBeanMethods = false)
static class ServletConfigurationWithExtendedAttributes {

@Bean
@ServletRegistration(
name = "extended",
urlMappings = "/extended/*",
initParameters = { "hello=world", "flag=true" },
multipartConfig = @ServletRegistration.MultipartConfigValues(
location = "/tmp",
maxFileSize = 1024,
maxRequestSize = 4096,
fileSizeThreshold = 128
)
)
TestServlet testServletWithInitParametersAndMultipart() {
return new TestServlet();
}

@Bean
MyExtraServletRegistrationBean myExtraServletRegistrationBean() {
MyExtraServletRegistrationBean bean = new MyExtraServletRegistrationBean();
bean.addInitParameter("extra", "fromExtraBean");
return bean;
}

@Bean
@ServletRegistration(
name = "extendedWithExtraBeans",
urlMappings = "/extra/*",
servletRegistrationBeans = { MyExtraServletRegistrationBean.class }
)
TestServlet testServletWithExtraBean() {
return new TestServlet();
}

static class MyExtraServletRegistrationBean extends ServletRegistrationBean<HttpServlet> {

MyExtraServletRegistrationBean() {
super();
}

}
}


}
Loading