org.dominokit.rest.shared.request.ServerRequest Maven / Gradle / Ivy
/*
* Copyright © 2019 Dominokit
*
* 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
*
* http://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 org.dominokit.rest.shared.request;
import static java.util.Objects.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Logger;
import javax.ws.rs.HttpMethod;
import org.dominokit.rest.shared.RestfulRequest;
/**
* This class represents all the requests sent using domino rest, it provides the state workflow for
* the request.
*
* @param the request type
* @param the response type
*/
public class ServerRequest extends BaseRequest
implements Response, HasComplete, HasHeadersAndParameters {
private static final Logger LOGGER = Logger.getLogger(ServerRequest.class.getName());
private static final String CONTENT_TYPE = "Content-type";
private static final String ACCEPT = "Accept";
private final SenderSupplier senderSupplier =
new SenderSupplier<>(() -> new RequestSender() {});
private final Map headers = new HashMap<>();
private final Map> queryParameters = new HashMap<>();
private final Map pathParameters = new HashMap<>();
private final Map metaParameters = new HashMap<>();
private RequestMeta requestMeta;
private R requestBean;
private RestfulRequest httpRequest;
private String url;
private String httpMethod = HttpMethod.GET;
private String path = "";
private String serviceRoot = "";
private Integer[] successCodes = new Integer[] {200, 201, 202, 203, 204};
private boolean voidResponse = false;
private int timeout = -1;
private int maxRetries = -1;
private Optional withCredentialsRequest = Optional.empty();
private RequestWriter requestWriter = request -> null;
private ResponseReader responseReader = request -> null;
private Success success = response -> {};
private final RequestState executedOnServer =
context -> {
success.onSuccess((S) context.responseBean);
state = completed;
completeHandler.onCompleted();
};
private final RequestState aborted =
context -> {
LOGGER.info("Request have already been aborted.!");
completeHandler.onCompleted();
};
private final RequestState sent =
context -> {
if (state.equals(aborted)) {
LOGGER.info("Request aborted, not response will be processed.");
} else {
if (context.nextContext instanceof ServerSuccessRequestStateContext) {
state = executedOnServer;
ServerRequest.this.applyState(context.nextContext);
} else if (context.nextContext instanceof ServerFailedRequestStateContext) {
state = failedOnServer;
ServerRequest.this.applyState(context.nextContext);
} else {
throw new InvalidRequestState(
"Request cannot be processed until a responseBean is received from the server");
}
}
};
private String responseType;
private NullQueryParamStrategy nullQueryParamStrategy;
private boolean multipartForm = false;
protected ServerRequest() {}
protected ServerRequest(RequestMeta requestMeta, R requestBean) {
this.requestMeta = requestMeta;
this.requestBean = requestBean;
}
/** prepare the request and execute it. */
@Override
public final void send() {
execute();
}
/**
* Sets the request body as a request bean
*
* @param requestBean the body
* @return same instance to support builder pattern
* @see RequestBean
*/
public ServerRequest setBean(R requestBean) {
this.requestBean = requestBean;
return this;
}
/**
* Use this method to intercept the request before it is sent to the server, this is good for
* setting headers or adding extra parameters.
*
* @param interceptor {@link Consumer} of {@link HasHeadersAndParameters}
* @return same instance to support builder pattern
*/
public ServerRequest intercept(Consumer> interceptor) {
interceptor.accept(this);
return this;
}
/** {@inheritDoc} */
@Override
public void startRouting() {
state = sent;
requestContext.getConfig().getServerRouter().routeRequest(this);
}
public void setHttpRequest(RestfulRequest httpRequest) {
this.httpRequest = httpRequest;
}
/** {@inheritDoc} */
@Override
public void abort() {
if (state.equals(ready)) {
state = aborted;
} else if (state.equals(sent)) {
if (nonNull(httpRequest)) {
httpRequest.abort();
}
state = aborted;
LOGGER.info("Request have been aborted : " + this.getClass().getCanonicalName());
} else if (state.equals(completed)) {
LOGGER.info("Could not abort request, request have already been completed.!");
}
}
/**
* Sets with credentials for this request
*
* @param withCredentials boolean to indicate if this request supports {@code withCredentials}
* @return same instance to support builder pattern
*/
public ServerRequest setWithCredentials(boolean withCredentials) {
this.withCredentialsRequest = Optional.of(new WithCredentialsRequest(withCredentials));
return this;
}
/** @return Optional if the with credentials is supported by this request */
public Optional getWithCredentialsRequest() {
return withCredentialsRequest;
}
/** {@inheritDoc} */
@Override
public RequestMeta getMeta() {
return requestMeta;
}
/**
* @return the rest sender associated with this request
* @see RequestRestSender
*/
public RequestRestSender getSender() {
return senderSupplier.get();
}
/**
* @return the request bean of the request
* @see RequestBean
*/
public R requestBean() {
return this.requestBean;
}
/** {@inheritDoc} */
@Override
public ServerRequest setHeader(String name, String value) {
headers.put(name, value);
return this;
}
/** {@inheritDoc} */
@Override
public ServerRequest setHeaders(Map headers) {
if (nonNull(headers) && !headers.isEmpty()) {
this.headers.putAll(headers);
}
return this;
}
/** {@inheritDoc} */
@Override
public ServerRequest setQueryParameter(String name, String value) {
queryParameters.put(name, new ArrayList<>());
addQueryParameter(name, value);
return this;
}
@Override
public HasHeadersAndParameters addQueryParameter(String name, String value) {
if (queryParameters.containsKey(name)) {
queryParameters.get(name).add(value);
} else {
setQueryParameter(name, value);
}
return this;
}
/** {@inheritDoc} */
@Override
public ServerRequest setQueryParameters(Map> parameters) {
parameters
.keySet()
.forEach(name -> parameters.get(name).forEach(value -> addQueryParameter(name, value)));
return this;
}
/** {@inheritDoc} */
@Override
public HasHeadersAndParameters addQueryParameters(Map> parameters) {
parameters.forEach(
(key, values) -> {
values.forEach(value -> addQueryParameter(key, value));
});
return this;
}
/** {@inheritDoc} */
@Override
public HasHeadersAndParameters setPathParameters(Map pathParameters) {
if (nonNull(pathParameters) && !pathParameters.isEmpty()) {
this.pathParameters.putAll(pathParameters);
}
return this;
}
/** {@inheritDoc} */
@Override
public HasHeadersAndParameters setPathParameter(String name, String value) {
pathParameters.put(name, value);
return this;
}
/** {@inheritDoc} */
@Override
public HasHeadersAndParameters setHeaderParameters(Map headerParameters) {
headers.putAll(headerParameters);
return this;
}
/** {@inheritDoc} */
@Override
public HasHeadersAndParameters setHeaderParameter(String name, String value) {
headers.put(name, value);
return this;
}
/** @return new map containing all headers defined in the request */
public Map headers() {
return new HashMap<>(headers);
}
/** @return new map containing all headers defined in the request */
public Map> queryParameters() {
return new HashMap<>(queryParameters);
}
/** @return new map containing all headers defined in the request */
public Map pathParameters() {
return new HashMap<>(pathParameters);
}
/**
* apply the service root and resource root configuration and replace the variable parameters in
* the request url.
*/
public void normalizeUrl() {
if (isNull(this.url)) {
String root =
(isNull(this.serviceRoot) || this.serviceRoot.isEmpty())
? ServiceRootMatcher.matchedServiceRoot(path)
: (this.serviceRoot + path);
UrlFormatter urlFormatter = new UrlFormatter(pathParameters);
this.setUrl(urlFormatter.formatUrl(root));
}
}
/**
* These parameters will not be part of the generated code or will be by default part of the final
* rest request We can use those parameters inside of a request interceptor to apply some
* conditional logic
*
* @param key parameter key
* @param value parameter value
* @return the current request instance
*/
public ServerRequest setMetaParameter(String key, String value) {
this.metaParameters.put(key, value);
return this;
}
/**
* @param key the key of the meta parameter
* @return the value of the meta parameter of the specified key
*/
public String getMetaParameter(String key) {
return metaParameters.get(key);
}
/** @return a copy of the request current meta parameters */
public Map getMetaParameters() {
return new HashMap<>(metaParameters);
}
/**
* override the request url, when the url is set it is used as is, and no configuration or
* parameter replacement is used.
*
* @param url the url of the request
* @return same request instance.
*/
public ServerRequest setUrl(String url) {
this.url = url;
return this;
}
/**
* add an on before send handler
*
* @param handler the handler to be called
* @return same request instance.
*/
public ServerRequest onBeforeSend(BeforeSendHandler handler) {
handler.onBeforeSend();
return this;
}
/** @return new map of all added call arguments. */
public Map> getRequestParameters() {
Map> result = new HashMap<>();
result.putAll(queryParameters);
pathParameters.forEach((key, value) -> result.put(key, Collections.singletonList(value)));
headers.forEach((key, value) -> result.put(key, Collections.singletonList(value)));
return result;
}
/**
* define the on success handler
*
* @param success the handler
* @return same instance to support builder pattern
*/
@Override
public HasComplete onSuccess(Success success) {
this.success = success;
return this;
}
/**
* define the on complete handler
*
* @param completeHandler the handler
* @return same instance to support builder pattern
*/
@Override
public CanFailOrSend onComplete(CompleteHandler completeHandler) {
if (nonNull(completeHandler)) {
this.completeHandler = completeHandler;
}
return this;
}
/**
* sets the Content-type header
*
* @param contentType the content type array
* @return same request instance.
*/
public ServerRequest setContentType(String[] contentType) {
requestMeta.setConsume(contentType);
setHeader(CONTENT_TYPE, String.join(", ", contentType));
return this;
}
/**
* sets the Accept header
*
* @param accept the accept array
* @return same request instance.
*/
public ServerRequest setAccept(String[] accept) {
requestMeta.setProduce(accept);
setHeader(ACCEPT, String.join(", ", accept));
return this;
}
/**
* sets the http method
*
* @param httpMethod the method
* @return same request instance.
*/
public ServerRequest setHttpMethod(String httpMethod) {
requireNonNull(httpMethod);
this.httpMethod = httpMethod.toUpperCase();
return this;
}
/** @return the request http method */
public String getHttpMethod() {
return httpMethod;
}
/** @return the accepted succees codes */
public Integer[] getSuccessCodes() {
return successCodes;
}
/**
* sets an array of integers to be considered as success response status code as success.
*
* @param successCodes {@link Integer[]}
* @return same instance
*/
public ServerRequest setSuccessCodes(Integer[] successCodes) {
this.successCodes = successCodes;
return this;
}
/** @return the custom service root for this request. */
public String getServiceRoot() {
return serviceRoot;
}
/**
* sets the service root for this request
*
* @param serviceRoot String
* @return same request instance.
*/
public ServerRequest setServiceRoot(String serviceRoot) {
this.serviceRoot = serviceRoot;
return this;
}
/** @return the writer class to be used for serializing the request body */
public RequestWriter getRequestWriter() {
if (nonNull(requestWriter)) {
return requestWriter;
} else {
Optional extends RequestWriter>> reader = CustomMappersRegistry.INSTANCE.findWriter(this);
if (reader.isPresent()) {
return (RequestWriter) reader.get();
} else {
throw new NoRequestWriterFoundForRequest(this);
}
}
}
/**
* sets the writer to be used to serialize the request body
*
* @param requestWriter {@link RequestWriter}
* @return same instance
*/
public ServerRequest setRequestWriter(RequestWriter requestWriter) {
this.requestWriter = requestWriter;
return this;
}
/** @return the response reader associated with this request */
public ResponseReader getResponseReader() {
if (nonNull(responseReader)) {
return responseReader;
} else {
Optional extends ResponseReader>> reader =
CustomMappersRegistry.INSTANCE.findReader(this);
if (reader.isPresent()) {
return (ResponseReader) reader.get();
} else {
throw new NoResponseReaderFoundForRequest(this);
}
}
}
/**
* Sets the response reader for this request
*
* @param responseReader the reader
* @return same instance to support builder pattern
*/
public ServerRequest setResponseReader(ResponseReader responseReader) {
this.responseReader = responseReader;
return this;
}
/** @return the path of the request */
public String getPath() {
return path;
}
/**
* Sets the path of the request
*
* @param path the path
* @return same instance to support builder pattern
*/
public ServerRequest setPath(String path) {
this.path = path;
return this;
}
/** {@inheritDoc} */
@Override
public CanCompleteOrSend onFailed(Fail fail) {
this.fail = fail;
return this;
}
/** {@inheritDoc} */
@Override
public boolean isAborted() {
return state.equals(aborted);
}
/** @return true if the request does not have body, false otherwise */
public boolean isVoidRequest() {
return requestBean instanceof VoidRequest;
}
/** @return true if the request does not have response */
public boolean isVoidResponse() {
return voidResponse;
}
protected void markAsVoidResponse() {
this.voidResponse = true;
}
/** @return the url of the request */
public String getUrl() {
return this.url;
}
/** @return the timeout in milliseconds */
public int getTimeout() {
return timeout;
}
/**
* Sets the timeout of the request in milliseconds
*
* @param timeout integer represents the timeout in milliseconds
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/** @return the maximum retries of the request */
public int getMaxRetries() {
return maxRetries;
}
/**
* Sets the maximum retries for this request
*
* @param maxRetries number of maximum retries
*/
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
/**
* Sets the response type
*
* @param responseType the type
* @return same instance to support builder pattern
*/
public ServerRequest setResponseType(String responseType) {
this.responseType = responseType;
return this;
}
/** @return the response type */
public String getResponseType() {
return responseType;
}
/**
* Helper method called from the generated requests that return empty or the string of the value
* returned by the supplier
*
* @param supplier the supplier
* @return the supplier value as a string, empty if null
*/
public String emptyOrStringValue(Supplier> supplier) {
if (isNull(supplier) || isNull(supplier.get())) {
return "";
}
return String.valueOf(supplier.get());
}
/**
* Helper method called from the generated requests that format the date based on a pattern
*
* @param supplier the date supplier
* @param pattern the pattern
* @return the formatted date
*/
public String formatDate(Supplier supplier, String pattern) {
if (isNull(supplier) || isNull(supplier.get())) {
return "";
}
return emptyOrStringValue(
() -> requestContext.getConfig().getDateParamFormatter().format(supplier.get(), pattern));
}
/**
* @return the request {@link NullQueryParamStrategy} and if not set fallback to the Global
* strategy defined in
*/
public NullQueryParamStrategy getNullQueryParamStrategy() {
if (isNull(nullQueryParamStrategy)) {
return DominoRestContext.make().getConfig().getNullQueryParamStrategy();
}
return nullQueryParamStrategy;
}
/**
* Overrides the {@link NullQueryParamStrategy} defined in {@link
* RestConfig#getDateParamFormatter()} for this request
*
* @param strategy {@link NullQueryParamStrategy}
* @return same instance
*/
public ServerRequest setNullQueryParamStrategy(NullQueryParamStrategy strategy) {
if (nonNull(strategy)) {
this.nullQueryParamStrategy = strategy;
}
return this;
}
/** @return true if the request is a multipart form data, false otherwise */
public boolean isMultipartForm() {
return multipartForm;
}
/** @param multipartForm true to mark the request as a multipart form data */
public void setMultipartForm(boolean multipartForm) {
this.multipartForm = multipartForm;
}
/** A function that get called right before sending the request to the server */
@FunctionalInterface
public interface BeforeSendHandler {
void onBeforeSend();
}
/** A config class to configure the credential flag for the request */
public static class WithCredentialsRequest {
private final boolean withCredentials;
public WithCredentialsRequest(boolean withCredentials) {
this.withCredentials = withCredentials;
}
public boolean isWithCredentials() {
return withCredentials;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy