Checkmarx complains that "the file utilizes "format" that is accessed by other concurrent functionality in a way that is not thread-safe, which may result in a Race Condition over this resource. It highlights the format method. How do we resolve this?
String endDate =
configProperties.getDateFormatter().format(Date.from(date.plusMonths(-1L * auditTimeMonthLimit).atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant()));
Other part of code
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
public SimpleDateFormat getDateFormatter() {
return dateFormatter;
}
CodePudding user response:
SimpleDateFormat
is not thread safe. This is a good explanation.
There is not a lot of code in the example, but having a final
instance of SimpleDateFormat
implies it may be used by multiple threads.
Maybe configProperties
is a global singleton? It is hard to tell, but if that code is accessed by multiple threads (including as part of a web controller or other type of web endpoint handler) and that is a single instance for every thread then you have a problem.
One possible solution (maybe not ideal, but you can translate it to something that works for you):
public SimpleDateFormat getDateFormatter() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
}
There are likely better options for formatting than this, so maybe doing it entirely different would be better.
CodePudding user response:
Legacy date-time API is error-prone e.g. java.util.Date
and SimpleDateFormatter
aren’t thread-safe, leading to potential concurrency issues for users. It is recommended to stop using them completely and switch to the modern date-time API.
The output you are trying to achieve is already the default format of Instant
and therefore, you do not need to use a formatter.
However, probably you are not familiar with the modern date-time API and therefore, for the sake of your learning, I have also demonstrated the use of DateTimeFormatter
.
Demo using java.time API:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.now(); // Some date
int auditTimeMonthLimit = 5; // Some value
String endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.toString();
System.out.println(endDate);
// In case you wanted the UTC date-time out of the local date
endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneOffset.UTC)
.toString();
System.out.println(endDate);
// In case you wanted the start date of the default time-zone to be converted
// into the UTC date-time
endDate = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.toInstant()
.toString();
System.out.println(endDate);
// A custom format
ZonedDateTime zdt = date.minusMonths(auditTimeMonthLimit)
.atStartOfDay(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
endDate = formatter.format(zdt);
System.out.println(endDate);
}
}
Output in my time-zone:
2022-06-17T00:00 01:00[Europe/London]
2022-06-17T00:00Z
2022-06-16T23:00:00Z
2022-06-16T23:00:00.000Z
Important points:
- I recommend you use
LocalDate#minusMonths
i.e. instead of usingdate.plusMonths(-1L * auditTimeMonthLimit)
, you should usedate.minusMonths(auditTimeMonthLimit)
. - 'Z' is not the same as Z.
- For your use case, I recommend you use
LocalDate#atStartOfDay(ZoneId zone)
instead of the non-parametrizedLocalDate#atStartOfDay
. This will make you
Learn more about the modern Date-Time API from Trail: Date Time.