diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml
index d8837b163..1b1c85370 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml
@@ -27,8 +27,10 @@
spring-webmvc
- javax.servlet
- javax.servlet-api
+ jakarta.servlet
+ jakarta.servlet-api
+
+
org.springframework.boot
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyFilterRegistration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyFilterRegistration.java
new file mode 100644
index 000000000..4def16599
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyFilterRegistration.java
@@ -0,0 +1,93 @@
+package org.springframework.cloud.function.serverless.web;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+
+public class ProxyFilterRegistration implements FilterRegistration, FilterRegistration.Dynamic {
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ private final String name;
+
+ private final Filter filter;
+
+ public ProxyFilterRegistration(String name, Filter filter) {
+ this.name = name;
+ this.filter = filter;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getClassName() {
+ return this.filter.getClass().getName();
+ }
+
+ @Override
+ public boolean setInitParameter(String name, String value) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set setInitParameters(Map initParameters) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Map getInitParameters() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void setAsyncSupported(boolean isAsyncSupported) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void addMappingForServletNames(EnumSet dispatcherTypes, boolean isMatchAfter,
+ String... servletNames) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Collection getServletNameMappings() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void addMappingForUrlPatterns(EnumSet dispatcherTypes, boolean isMatchAfter,
+ String... urlPatterns) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Collection getUrlPatternMappings() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java
index 8a30e8557..46422927b 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -32,14 +33,14 @@ import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
@@ -65,41 +66,49 @@ public class ProxyMvc {
static final String MVC_RESULT_ATTRIBUTE = ProxyMvc.class.getName().concat(".MVC_RESULT_ATTRIBUTE");
- private final DispatcherServlet servlet;
-
- private final Filter[] filters;
+ private final DispatcherServlet dispatcher;
private final ConfigurableWebApplicationContext applicationContext;
- public static ProxyMvc INSTANCE(Class>... componentClasses) {
- Assert.notEmpty(componentClasses, "'componentClasses' must not be null or empty");
+ private ServletContext servletContext;
+ private volatile boolean initialized;
+
+ public ConfigurableWebApplicationContext getApplicationContext() {
+ return this.applicationContext;
+ }
+
+ public ServletContext getServletContext() {
+ return this.servletContext;
+ }
+
+ public static ProxyMvc INSTANCE(ConfigurableWebApplicationContext applpicationContext) {
ProxyServletContext servletContext = new ProxyServletContext();
- GenericWebApplicationContext applpicationContext = new GenericWebApplicationContext(servletContext);
+ applpicationContext.setServletContext(servletContext);
+ DispatcherServlet dispatcher = new DispatcherServlet(applpicationContext);
+ ServletRegistration.Dynamic reg = servletContext.addServlet("dispatcherServlet", dispatcher);
+ reg.setLoadOnStartup(1);
+
+ ProxyMvc mvc = new ProxyMvc(dispatcher, applpicationContext);
+ mvc.servletContext = servletContext;
+ return mvc;
+ }
+
+ public static ProxyMvc INSTANCE(Class>... componentClasses) {
+ GenericWebApplicationContext applpicationContext = new GenericWebApplicationContext();
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(applpicationContext);
- reader.register(componentClasses);
-
- reader.register(ProxyErrorController.class);
-
- try {
- DispatcherServlet servlet = new DispatcherServlet(applpicationContext);
- servlet.init(new ProxyServletConfig(servletContext));
- applpicationContext.registerBean(DispatcherServlet.class, servlet);
-
- return new ProxyMvc(servlet, applpicationContext);
- }
- catch (Exception e) {
- throw new IllegalStateException("Failed to create MVC Proxy", e);
+ if (!ObjectUtils.isEmpty(componentClasses)) {
+ reader.register(componentClasses);
}
+ return INSTANCE(applpicationContext);
}
/**
* Private constructor, not for direct instantiation.
*/
- ProxyMvc(DispatcherServlet servlet, ConfigurableWebApplicationContext applicationContext) {
+ ProxyMvc(DispatcherServlet dispatcher, ConfigurableWebApplicationContext applicationContext) {
this.applicationContext = applicationContext;
- this.servlet = servlet;
- this.filters = applicationContext.getBeansOfType(Filter.class).values().toArray(new Filter[0]);
+ this.dispatcher = dispatcher;
}
public void stop() {
@@ -118,8 +127,23 @@ public class ProxyMvc {
* @see org.springframework.test.web.servlet.result.MockMvcResultMatchers
*/
public void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
- ProxyFilterChain filterChain = new ProxyFilterChain(this.servlet, this.filters);
+ this.service(request, response, (CountDownLatch) null);
+ }
+
+ public void service(HttpServletRequest request, HttpServletResponse response, CountDownLatch latch) throws Exception {
+ synchronized (this) {
+ if (!this.initialized) {
+ this.dispatcher.init(new ProxyServletConfig(this.servletContext));
+ this.initialized = true;
+ }
+ }
+
+ ProxyFilterChain filterChain = new ProxyFilterChain(this.dispatcher);
filterChain.doFilter(request, response);
+
+ if (latch != null) {
+ latch.countDown();
+ }
}
private static class ProxyFilterChain implements FilterChain {
@@ -143,10 +167,12 @@ public class ProxyMvc {
* @param filters the {@link Filter}'s to invoke in this {@link FilterChain}
* @since 3.2
*/
- ProxyFilterChain(Servlet servlet, Filter... filters) {
+ ProxyFilterChain(DispatcherServlet servlet) {
+ List filters = new ArrayList<>();
+ servlet.getServletContext().getFilterRegistrations().values().forEach(fr -> filters.add(((ProxyFilterRegistration)fr).getFilter()));
Assert.notNull(filters, "filters cannot be null");
Assert.noNullElements(filters, "filters cannot contain null values");
- this.filters = initFilterList(servlet, filters);
+ this.filters = initFilterList(servlet, filters.toArray(new Filter[] {}));
}
private static List initFilterList(Servlet servlet, Filter... filters) {
@@ -210,24 +236,37 @@ public class ProxyMvc {
throws IOException, ServletException {
try {
- this.delegateServlet.service(request, response);
- if (((HttpServletResponse) response).getStatus() != HttpStatus.OK.value()) {
- ((ProxyHttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, ((HttpServletResponse) response).getStatus());
+ if (((HttpServletResponse) response).getStatus() != HttpStatus.OK.value() && request instanceof ProxyHttpServletRequest) {
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, ((HttpServletResponse) response).getStatus());
this.setErrorMessageAttribute((ProxyHttpServletRequest) request, (ProxyHttpServletResponse) response, null);
- ((ProxyHttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((ProxyHttpServletRequest) request).getRequestURI());
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI());
((ProxyHttpServletRequest) request).setRequestURI("/error");
this.delegateServlet.service(request, response);
}
+ else {
+ this.delegateServlet.service(request, response);
+ if (((HttpServletResponse) response).getStatus() != HttpStatus.OK.value() && request instanceof ProxyHttpServletRequest) {
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, ((HttpServletResponse) response).getStatus());
+ this.setErrorMessageAttribute((ProxyHttpServletRequest) request, (ProxyHttpServletResponse) response, null);
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI());
+
+ ((ProxyHttpServletRequest) request).setRequestURI("/error");
+ this.delegateServlet.service(request, response);
+ }
+ }
}
catch (Exception e) {
- ((ProxyHttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpStatus.INTERNAL_SERVER_ERROR);
- this.setErrorMessageAttribute((ProxyHttpServletRequest) request, (ProxyHttpServletResponse) response, e);
- ((ProxyHttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e);
- ((ProxyHttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((ProxyHttpServletRequest) request).getRequestURI());
+ if (request instanceof ProxyHttpServletRequest) {
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpStatus.INTERNAL_SERVER_ERROR);
+ this.setErrorMessageAttribute((ProxyHttpServletRequest) request, (ProxyHttpServletResponse) response, e);
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, e);
+ ((HttpServletRequest) request).setAttribute(RequestDispatcher.ERROR_REQUEST_URI, ((HttpServletRequest) request).getRequestURI());
+ ((ProxyHttpServletRequest) request).setRequestURI("/error");
+ }
+
+ LOG.error("Failed processing the request to: " + ((HttpServletRequest) request).getRequestURI(), e);
- LOG.error("Failed processing the request to: " + ((ProxyHttpServletRequest) request).getRequestURI(), e);
- ((ProxyHttpServletRequest) request).setRequestURI("/error");
this.delegateServlet.service(request, response);
}
}
@@ -270,7 +309,7 @@ public class ProxyMvc {
@Override
public String getServletName() {
- return "serverless-proxy";
+ return "spring-serverless-proxy";
}
@Override
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java
index e9fa8e4e4..d8e694862 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java
@@ -20,12 +20,17 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
@@ -200,9 +205,14 @@ public class ProxyServletContext implements ServletContext {
throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
}
+ Map registrations = new HashMap<>();
+
@Override
public Dynamic addServlet(String servletName, Servlet servlet) {
- throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
+
+ ProxyServletRegistration registration = new ProxyServletRegistration(servletName, servlet, this);
+ this.registrations.put(servletName, registration);
+ return registration;
}
@Override
@@ -222,7 +232,7 @@ public class ProxyServletContext implements ServletContext {
@Override
public ServletRegistration getServletRegistration(String servletName) {
- throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
+ return this.registrations.get(servletName);
}
@Override
@@ -240,9 +250,18 @@ public class ProxyServletContext implements ServletContext {
throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
}
+ Map filterRegistrations = new HashMap<>();
+
@Override
public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Class extends Filter> filterClass) {
- throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
+ try {
+ Filter filter = filterClass.getDeclaredConstructor().newInstance();
+ ProxyFilterRegistration registration = new ProxyFilterRegistration(filterName, filter);
+ filterRegistrations.put(filterName, registration);
+ return registration;
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
}
@Override
@@ -252,12 +271,12 @@ public class ProxyServletContext implements ServletContext {
@Override
public FilterRegistration getFilterRegistration(String filterName) {
- throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
+ return this.filterRegistrations.get(filterName);
}
@Override
public Map getFilterRegistrations() {
- throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
+ return this.filterRegistrations;
}
@Override
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletRegistration.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletRegistration.java
new file mode 100644
index 000000000..a7e52669a
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletRegistration.java
@@ -0,0 +1,120 @@
+package org.springframework.cloud.function.serverless.web;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletSecurityElement;
+
+public class ProxyServletRegistration implements ServletRegistration, ServletRegistration.Dynamic, Comparable {
+
+ private final String servletName;
+
+ private final Servlet servlet;
+
+ private final ServletContext servletContext;
+
+ private int loadOnStartup;
+
+ public ProxyServletRegistration(String servletName, Servlet servlet, ServletContext servletContext) {
+ this.servlet = servlet;
+ this.servletName = servletName;
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ public String getName() {
+ return this.servletName;
+ }
+
+ @Override
+ public String getClassName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean setInitParameter(String name, String value) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set setInitParameters(Map initParameters) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Map getInitParameters() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void setAsyncSupported(boolean isAsyncSupported) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setLoadOnStartup(int loadOnStartup) {
+ this.loadOnStartup = loadOnStartup;
+ }
+
+ public int getLoadOnStartup() {
+ return this.loadOnStartup;
+ }
+
+ @Override
+ public Set setServletSecurity(ServletSecurityElement constraint) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void setMultipartConfig(MultipartConfigElement multipartConfig) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setRunAsRole(String roleName) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Set addMapping(String... urlPatterns) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Collection getMappings() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRunAsRole() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int compareTo(ProxyServletRegistration o) {
+ return Integer.compare(this.loadOnStartup, o.getLoadOnStartup());
+ }
+
+}