BaseResource.java

1
package com.reallifedeveloper.common.resource;
2
3
import static com.reallifedeveloper.common.domain.LogUtil.removeCRLF;
4
5
import java.io.FileNotFoundException;
6
import java.net.MalformedURLException;
7
import java.net.URI;
8
import java.net.URISyntaxException;
9
import java.net.URL;
10
import java.time.LocalDate;
11
import java.time.format.DateTimeFormatter;
12
import java.util.Arrays;
13
import java.util.Collections;
14
import java.util.List;
15
import java.util.stream.Collectors;
16
17
import org.checkerframework.checker.nullness.qual.Nullable;
18
import org.slf4j.Logger;
19
import org.slf4j.LoggerFactory;
20
21
import jakarta.ws.rs.WebApplicationException;
22
23
import com.reallifedeveloper.common.domain.ErrorHandling;
24
25
/**
26
 * A base class for JAX-RS resources.
27
 *
28
 * @author RealLifeDeveloper
29
 */
30
public class BaseResource {
31
32
    /**
33
     * The optional query parameter that holds the API key of the calling system. If authentication is required, either this query parameter
34
     * or the HTTP header {@link #API_KEY_HTTP_HEADER} must be included in the request.
35
     */
36
    public static final String API_KEY_QUERY_PARAMETER = "apikey";
37
38
    /**
39
     * The optional HTTP header that holds the API key of the calling system. If authentication is required, either this HTTP header or the
40
     * query parameter {@link #API_KEY_QUERY_PARAMETER} must be included in the request.
41
     */
42
    public static final String API_KEY_HTTP_HEADER = "SvkAuthSvc-ApiKey";
43
44
    /**
45
     * The standard format for dates, without time.
46
     */
47
    public static final String DATE_FORMAT = "yyyy-MM-dd";
48
49
    /**
50
     * A handy value to provide to the {@link ResourceUtil#cacheControl(int)} method to cache a result for one hour.
51
     */
52
    protected static final int CACHE_1_HOUR = 60 * 60;
53
54
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
55
56
    private final Logger logger = LoggerFactory.getLogger(getClass());
57
58
    /**
59
     * Creates a new {@code BaseResource}, intended to be used by sub-classes.
60
     */
61
    protected BaseResource() {
62
        // The only constructor is protected, to disallow direct instantiation.
63
    }
64
65
    /**
66
     * Gives the {@code org.slf4j.Logger} to use for logging.
67
     *
68
     * @return the {@code org.slf4j.Logger}
69
     */
70
    protected Logger logger() {
71 1 1. logger : replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::logger → KILLED
        return logger;
72
    }
73
74
    /**
75
     * Use this method to translate an exception to the appropriate {@code WebApplicationException}. The problem is also logged.
76
     *
77
     * @param methodName        the name of the method where the problem occurred
78
     * @param originalException the exception that should be translated
79
     *
80
     * @return an appropriate {@code WebApplicationException}, depending on {@code originalException}
81
     *
82
     * @throws IllegalArgumentException if any argument is {@code null}
83
     */
84
    protected WebApplicationException handleError(String methodName, Exception originalException) {
85 1 1. handleError : removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED
        ErrorHandling.checkNull("Arguments must not be null: methodName=%s, originalException=%s", methodName, originalException);
86
        WebApplicationException webApplicationException;
87 1 1. handleError : negated conditional → KILLED
        if (originalException instanceof IllegalArgumentException) {
88
            logger().debug("{}: {}", removeCRLF(methodName), removeCRLF(originalException));
89
            webApplicationException = ResourceUtil.badRequest(originalException.getMessage());
90 1 1. handleError : negated conditional → KILLED
        } else if (originalException instanceof FileNotFoundException) {
91
            logger().debug("{}: {}", removeCRLF(methodName), removeCRLF(originalException));
92
            webApplicationException = ResourceUtil.notFound(originalException.getMessage());
93
        } else {
94
            logger().error(removeCRLF(methodName), removeCRLF(originalException));
95
            webApplicationException = ResourceUtil.serverError(originalException.toString());
96
        }
97 1 1. handleError : replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::handleError → KILLED
        return webApplicationException;
98
    }
99
100
    /**
101
     * Parses a string as a {@code java.time.LocalDate}, using the date format {@value #DATE_FORMAT}.
102
     *
103
     * @param date the string to parse
104
     *
105
     * @return the {@code java.time.LocalDate} representation of {@code date}
106
     *
107
     * @throws IllegalArgumentException if {@code date} is {@code null} or could not be parsed as a date
108
     */
109
    protected LocalDate parseDate(String date) {
110 1 1. parseDate : removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED
        ErrorHandling.checkNull("date must not be null", date);
111 1 1. parseDate : replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::parseDate → KILLED
        return LocalDate.parse(date, DATE_FORMATTER);
112
    }
113
114
    /**
115
     * Parses a string as a {@code java.net.URL}.
116
     *
117
     * @param url the string to parse
118
     *
119
     * @return the {@code java.net.URL} representation of {@code url}
120
     *
121
     * @throws IllegalArgumentException if {@code url} could not be parsed as a URL
122
     */
123
    protected URL parseUrl(String url) {
124 1 1. parseUrl : removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED
        ErrorHandling.checkNull("url must not be null", url);
125
        try {
126 1 1. parseUrl : replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::parseUrl → KILLED
            return new URI(url).toURL();
127
        } catch (MalformedURLException | URISyntaxException e) {
128
            throw new IllegalArgumentException(String.format("The string '%s' could not be parsed as a url", url), e);
129
        }
130
    }
131
132
    /**
133
     * Given a comma-separated list, this methods returns a list containing the constituent strings, with leading and trailing whitespace
134
     * removed.
135
     * <p>
136
     * Example: Given the string " foo ,bar , baz " the result is the list ["foo","bar","baz"].
137
     * <p>
138
     * A {@code null} string gives an empty list, the method does not throw an exception.
139
     *
140
     * @param s a comma-separated list, or {@code null}
141
     *
142
     * @return a list with the constituent strings, with whitespace removed
143
     */
144
    protected List<String> commaSeparatedStringToList(@Nullable String s) {
145 2 1. commaSeparatedStringToList : negated conditional → KILLED
2. commaSeparatedStringToList : negated conditional → KILLED
        if (s == null || s.isBlank()) {
146
            return Collections.emptyList();
147
        } else {
148 1 1. commaSeparatedStringToList : replaced return value with Collections.emptyList for com/reallifedeveloper/common/resource/BaseResource::commaSeparatedStringToList → KILLED
            return Arrays.asList(s.split("\\s*,\\s*")).stream().map(String::trim).collect(Collectors.toList());
149
        }
150
    }
151
}

Mutations

71

1.1
Location : logger
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:handleErrorNullPointerException()]
replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::logger → KILLED

85

1.1
Location : handleError
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:handleErrorOriginalExceptionNull()]
removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED

87

1.1
Location : handleError
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:handleErrorNullPointerException()]
negated conditional → KILLED

90

1.1
Location : handleError
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:handleErrorNullPointerException()]
negated conditional → KILLED

97

1.1
Location : handleError
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:handleErrorNullPointerException()]
replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::handleError → KILLED

110

1.1
Location : parseDate
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:parseNullDate()]
removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED

111

1.1
Location : parseDate
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:parseDate()]
replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::parseDate → KILLED

124

1.1
Location : parseUrl
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:parseNullUrl()]
removed call to com/reallifedeveloper/common/domain/ErrorHandling::checkNull → KILLED

126

1.1
Location : parseUrl
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:parseUrl()]
replaced return value with null for com/reallifedeveloper/common/resource/BaseResource::parseUrl → KILLED

145

1.1
Location : commaSeparatedStringToList
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:commaSeparatedStringToListWithOnlyWhitespaceGivesEmptyList()]
negated conditional → KILLED

2.2
Location : commaSeparatedStringToList
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:nullCommaSeparatedStringToListGivesEmptyList()]
negated conditional → KILLED

148

1.1
Location : commaSeparatedStringToList
Killed by : com.reallifedeveloper.common.resource.ResourceTest.[engine:junit-jupiter]/[class:com.reallifedeveloper.common.resource.ResourceTest]/[method:commaSeparatedStringToList()]
replaced return value with Collections.emptyList for com/reallifedeveloper/common/resource/BaseResource::commaSeparatedStringToList → KILLED

Active mutators

Tests examined


Report generated by PIT 1.20.2