BaseResource.java
package com.reallifedeveloper.common.resource;
import static com.reallifedeveloper.common.domain.LogUtil.removeCRLF;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.WebApplicationException;
import com.reallifedeveloper.common.domain.ErrorHandling;
/**
* A base class for JAX-RS resources.
*
* @author RealLifeDeveloper
*/
public class BaseResource {
/**
* The optional query parameter that holds the API key of the calling system. If authentication is required, either this query parameter
* or the HTTP header {@link #API_KEY_HTTP_HEADER} must be included in the request.
*/
public static final String API_KEY_QUERY_PARAMETER = "apikey";
/**
* The optional HTTP header that holds the API key of the calling system. If authentication is required, either this HTTP header or the
* query parameter {@link #API_KEY_QUERY_PARAMETER} must be included in the request.
*/
public static final String API_KEY_HTTP_HEADER = "SvkAuthSvc-ApiKey";
/**
* The standard format for dates, without time.
*/
public static final String DATE_FORMAT = "yyyy-MM-dd";
/**
* A handy value to provide to the {@link ResourceUtil#cacheControl(int)} method to cache a result for one hour.
*/
protected static final int CACHE_1_HOUR = 60 * 60;
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* Creates a new {@code BaseResource}, intended to be used by sub-classes.
*/
protected BaseResource() {
// The only constructor is protected, to disallow direct instantiation.
}
/**
* Gives the {@code org.slf4j.Logger} to use for logging.
*
* @return the {@code org.slf4j.Logger}
*/
protected Logger logger() {
return logger;
}
/**
* Use this method to translate an exception to the appropriate {@code WebApplicationException}. The problem is also logged.
*
* @param methodName the name of the method where the problem occurred
* @param originalException the exception that should be translated
*
* @return an appropriate {@code WebApplicationException}, depending on {@code originalException}
*
* @throws IllegalArgumentException if any argument is {@code null}
*/
protected WebApplicationException handleError(String methodName, Exception originalException) {
ErrorHandling.checkNull("Arguments must not be null: methodName=%s, originalException=%s", methodName, originalException);
WebApplicationException webApplicationException;
if (originalException instanceof IllegalArgumentException) {
logger().debug("{}: {}", removeCRLF(methodName), removeCRLF(originalException));
webApplicationException = ResourceUtil.badRequest(originalException.getMessage());
} else if (originalException instanceof FileNotFoundException) {
logger().debug("{}: {}", removeCRLF(methodName), removeCRLF(originalException));
webApplicationException = ResourceUtil.notFound(originalException.getMessage());
} else {
logger().error(removeCRLF(methodName), removeCRLF(originalException));
webApplicationException = ResourceUtil.serverError(originalException.toString());
}
return webApplicationException;
}
/**
* Parses a string as a {@code java.time.LocalDate}, using the date format {@value #DATE_FORMAT}.
*
* @param date the string to parse
*
* @return the {@code java.time.LocalDate} representation of {@code date}
*
* @throws IllegalArgumentException if {@code date} is {@code null} or could not be parsed as a date
*/
protected LocalDate parseDate(String date) {
ErrorHandling.checkNull("date must not be null", date);
return LocalDate.parse(date, DATE_FORMATTER);
}
/**
* Parses a string as a {@code java.net.URL}.
*
* @param url the string to parse
*
* @return the {@code java.net.URL} representation of {@code url}
*
* @throws IllegalArgumentException if {@code url} could not be parsed as a URL
*/
protected URL parseUrl(String url) {
ErrorHandling.checkNull("url must not be null", url);
try {
return new URI(url).toURL();
} catch (MalformedURLException | URISyntaxException e) {
throw new IllegalArgumentException(String.format("The string '%s' could not be parsed as a url", url), e);
}
}
/**
* Given a comma-separated list, this methods returns a list containing the constituent strings, with leading and trailing whitespace
* removed.
*
* Example: Given the string " foo ,bar , baz " the result is the list ["foo","bar","baz"].
*
* @param s a comma-separated list
*
* @return a list with the constituent strings, with whitespace removed
*/
protected List<String> commaSeparatedStringToList(String s) {
if (s == null || s.isBlank()) {
return Collections.emptyList();
} else {
return Arrays.asList(s.split("\\s*,\\s*")).stream().map(String::trim).collect(Collectors.toList());
}
}
}