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

Mutations

70

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

84

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

86

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

89

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

96

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

109

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

110

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

123

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

125

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

142

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

145

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.0