|
| 1 | +package com.amazonaws.serverless.proxy.internal.servlet; |
| 2 | + |
| 3 | +import com.amazonaws.serverless.exceptions.ContainerInitializationException; |
| 4 | +import com.amazonaws.serverless.proxy.*; |
| 5 | +import com.amazonaws.serverless.proxy.model.AwsProxyRequest; |
| 6 | +import com.amazonaws.serverless.proxy.model.AwsProxyResponse; |
| 7 | + |
| 8 | +import javax.servlet.http.HttpServletRequest; |
| 9 | +import java.util.ArrayList; |
| 10 | +import java.util.List; |
| 11 | + |
| 12 | +/** |
| 13 | + * Base builder class for {@link AwsLambdaServletContainerHandler}. Implmentations can extend this class to have setters |
| 14 | + * for the basic parameters. |
| 15 | + * @param <RequestType> The event object class |
| 16 | + * @param <ResponseType> The output object class |
| 17 | + * @param <ContainerRequestType> The container request type. For proxy implementations, this is {@link AwsProxyHttpServletRequest}. |
| 18 | + * The response type is hardcoded to {@link AwsHttpServletResponse} since it is a generic |
| 19 | + * servlet response implementation. |
| 20 | + * @param <HandlerType> The type of the handler we are building |
| 21 | + * @param <Builder> The builder object itself. This is used to allow implementations to re-use the setter method from this |
| 22 | + * abstract class through <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=133275"> |
| 23 | + * "curiously recurring generic patterns"</a> |
| 24 | + */ |
| 25 | +public abstract class ServletLambdaContainerHandlerBuilder< |
| 26 | + RequestType, |
| 27 | + ResponseType, |
| 28 | + ContainerRequestType extends HttpServletRequest, |
| 29 | + HandlerType extends AwsLambdaServletContainerHandler<RequestType, ResponseType, ContainerRequestType, AwsHttpServletResponse>, |
| 30 | + Builder extends ServletLambdaContainerHandlerBuilder<RequestType, ResponseType, ContainerRequestType, HandlerType, Builder>> |
| 31 | +{ |
| 32 | + private static final String MISSING_FIELD_ERROR = "Missing %s in lambda container handler builder"; |
| 33 | + |
| 34 | + protected InitializationWrapper initializationWrapper; |
| 35 | + protected RequestReader<RequestType, ContainerRequestType> requestReader; |
| 36 | + protected ResponseWriter<AwsHttpServletResponse, ResponseType> responseWriter; |
| 37 | + protected SecurityContextWriter<RequestType> securityContextWriter; |
| 38 | + protected ExceptionHandler<ResponseType> exceptionHandler; |
| 39 | + protected Class<RequestType> requestTypeClass; |
| 40 | + protected Class<ResponseType> responseTypeClass; |
| 41 | + |
| 42 | + /** |
| 43 | + * Validates that all of the required fields are populated. |
| 44 | + * @throws ContainerInitializationException If values have not been set on the builder. The message in the exception |
| 45 | + * contains a standard error message {@link ServletLambdaContainerHandlerBuilder#MISSING_FIELD_ERROR} populated with |
| 46 | + * the list of missing fields. |
| 47 | + */ |
| 48 | + protected void validate() throws ContainerInitializationException { |
| 49 | + List<String> errFields = new ArrayList<>(); |
| 50 | + if (requestTypeClass == null) { |
| 51 | + errFields.add("request type class"); |
| 52 | + } |
| 53 | + if (responseTypeClass == null) { |
| 54 | + errFields.add("response type class"); |
| 55 | + } |
| 56 | + if (requestReader == null) { |
| 57 | + errFields.add("request reader"); |
| 58 | + } |
| 59 | + if (responseWriter == null) { |
| 60 | + errFields.add("response writer"); |
| 61 | + } |
| 62 | + if (securityContextWriter == null) { |
| 63 | + errFields.add("security context writer"); |
| 64 | + } |
| 65 | + if (exceptionHandler == null) { |
| 66 | + errFields.add("exception handler"); |
| 67 | + } |
| 68 | + if (initializationWrapper == null) { |
| 69 | + errFields.add("initialization wrapper"); |
| 70 | + } |
| 71 | + if (!errFields.isEmpty()) { |
| 72 | + throw new ContainerInitializationException(String.format(MISSING_FIELD_ERROR, String.join(", ", errFields)), null); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + /** |
| 77 | + * Sets all of the required fields in the builder to the default settings for a Servlet-compatible framework that wants |
| 78 | + * to support AWS proxy event and output types. |
| 79 | + * @return A populated builder |
| 80 | + */ |
| 81 | + public Builder defaultProxy() { |
| 82 | + initializationWrapper(new InitializationWrapper()) |
| 83 | + .requestReader((RequestReader<RequestType, ContainerRequestType>) new AwsProxyHttpServletRequestReader()) |
| 84 | + .responseWriter((ResponseWriter<AwsHttpServletResponse, ResponseType>) new AwsProxyHttpServletResponseWriter()) |
| 85 | + .securityContextWriter((SecurityContextWriter<RequestType>) new AwsProxySecurityContextWriter()) |
| 86 | + .exceptionHandler((ExceptionHandler<ResponseType>) new AwsProxyExceptionHandler()) |
| 87 | + .requestTypeClass((Class<RequestType>) AwsProxyRequest.class) |
| 88 | + .responseTypeClass((Class<ResponseType>) AwsProxyResponse.class); |
| 89 | + return self(); |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * Sets the initialization wrapper to be used by the {@link ServletLambdaContainerHandlerBuilder#buildAndInitialize()} |
| 94 | + * method to start the framework implementations |
| 95 | + * @param initializationWrapper An implementation of <code>InitializationWrapper</code>. In most cases, this will be |
| 96 | + * set to {@link InitializationWrapper}. The {@link ServletLambdaContainerHandlerBuilder#asyncInit(long)} |
| 97 | + * method sets this to {@link AsyncInitializationWrapper}. |
| 98 | + * @return This builder object |
| 99 | + */ |
| 100 | + public Builder initializationWrapper(InitializationWrapper initializationWrapper) { |
| 101 | + this.initializationWrapper = initializationWrapper; |
| 102 | + return self(); |
| 103 | + } |
| 104 | + |
| 105 | + public Builder requestReader(RequestReader<RequestType, ContainerRequestType> requestReader) { |
| 106 | + this.requestReader = requestReader; |
| 107 | + return self(); |
| 108 | + } |
| 109 | + |
| 110 | + public Builder responseWriter(ResponseWriter<AwsHttpServletResponse, ResponseType> responseWriter) { |
| 111 | + this.responseWriter = responseWriter; |
| 112 | + return self(); |
| 113 | + } |
| 114 | + |
| 115 | + public Builder securityContextWriter(SecurityContextWriter<RequestType> securityContextWriter) { |
| 116 | + this.securityContextWriter = securityContextWriter; |
| 117 | + return self(); |
| 118 | + } |
| 119 | + |
| 120 | + public Builder exceptionHandler(ExceptionHandler<ResponseType> exceptionHandler) { |
| 121 | + this.exceptionHandler = exceptionHandler; |
| 122 | + return self(); |
| 123 | + } |
| 124 | + |
| 125 | + public Builder requestTypeClass(Class<RequestType> requestType) { |
| 126 | + this.requestTypeClass = requestType; |
| 127 | + return self(); |
| 128 | + } |
| 129 | + |
| 130 | + public Builder responseTypeClass(Class<ResponseType> responseType) { |
| 131 | + this.responseTypeClass = responseType; |
| 132 | + return self(); |
| 133 | + } |
| 134 | + |
| 135 | + public Builder asyncInit(long actualStartTime) { |
| 136 | + this.initializationWrapper = new AsyncInitializationWrapper(actualStartTime); |
| 137 | + return self(); |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * Implementations should implement this method to return their type. All of the builder methods in this abstract |
| 142 | + * class use this method to return the correct builder type. |
| 143 | + * @return The current builder. |
| 144 | + */ |
| 145 | + protected abstract Builder self(); |
| 146 | + public abstract HandlerType build() throws ContainerInitializationException; |
| 147 | + public abstract HandlerType buildAndInitialize() throws ContainerInitializationException; |
| 148 | +} |
0 commit comments