Add integration with AWS API latch

This commit is contained in:
Oleg Zhurakousky
2023-03-23 10:38:42 +01:00
parent 04baab62b7
commit e7e808916b
5 changed files with 318 additions and 45 deletions

View File

@@ -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<String> setInitParameters(Map<String, String> initParameters) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, String> getInitParameters() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setAsyncSupported(boolean isAsyncSupported) {
// TODO Auto-generated method stub
}
@Override
public void addMappingForServletNames(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
String... servletNames) {
// TODO Auto-generated method stub
}
@Override
public Collection<String> getServletNameMappings() {
// TODO Auto-generated method stub
return null;
}
@Override
public void addMappingForUrlPatterns(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
String... urlPatterns) {
// TODO Auto-generated method stub
}
@Override
public Collection<String> getUrlPatternMappings() {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -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<Filter> 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<Filter> 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

View File

@@ -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<String, ServletRegistration> 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<String, FilterRegistration> 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<String, ? extends FilterRegistration> getFilterRegistrations() {
throw new UnsupportedOperationException("This ServletContext does not represent a running web container");
return this.filterRegistrations;
}
@Override

View File

@@ -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<ProxyServletRegistration> {
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<String> setInitParameters(Map<String, String> initParameters) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, String> 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<String> 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<String> addMapping(String... urlPatterns) {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<String> 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());
}
}