aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api/src
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2017-09-11 16:18:34 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2017-09-15 08:51:08 +0200
commitf76f82c6ef66e60e4986521d141514763e5c405a (patch)
tree87f9aae33bcf56de22a0f1f2306d2a209682d935 /sonar-plugin-api/src
parent3078c44cf8dd58238ac05ee39b77dff660a20560 (diff)
downloadsonarqube-f76f82c6ef66e60e4986521d141514763e5c405a.tar.gz
sonarqube-f76f82c6ef66e60e4986521d141514763e5c405a.zip
SONAR-9718 Fail with a clear message when sonar.projectDate is empty
Diffstat (limited to 'sonar-plugin-api/src')
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java186
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java3
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java91
4 files changed, 119 insertions, 164 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
index 2a210e519e2..4ec66633e7e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
@@ -32,7 +32,6 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.SonarException;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
@@ -246,7 +245,7 @@ public abstract class Request {
try {
return DateUtils.parseDate(s);
- } catch (SonarException notDateException) {
+ } catch (RuntimeException notDateException) {
throw new IllegalArgumentException(notDateException);
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java
index d226fe6270a..ea232916471 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java
@@ -19,15 +19,13 @@
*/
package org.sonar.api.utils;
-import java.io.NotSerializableException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-import java.text.DateFormat;
-import java.text.FieldPosition;
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
import java.util.Date;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -44,26 +42,44 @@ public final class DateUtils {
public static final String DATE_FORMAT = "yyyy-MM-dd";
public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
- private static final ThreadSafeDateFormat THREAD_SAFE_DATE_FORMAT = new ThreadSafeDateFormat(DATE_FORMAT);
- private static final ThreadSafeDateFormat THREAD_SAFE_DATETIME_FORMAT = new ThreadSafeDateFormat(DATETIME_FORMAT);
+ private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_FORMAT);
private DateUtils() {
}
+ /**
+ * Warning: relies on default timezone!
+ */
public static String formatDate(Date d) {
- return THREAD_SAFE_DATE_FORMAT.format(d);
+ return d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString();
}
+ /**
+ * Warning: relies on default timezone!
+ */
public static String formatDateTime(Date d) {
- return THREAD_SAFE_DATETIME_FORMAT.format(d);
+ return formatDateTime(OffsetDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault()));
}
+ /**
+ * Warning: relies on default timezone!
+ */
public static String formatDateTime(long ms) {
- return THREAD_SAFE_DATETIME_FORMAT.format(new Date(ms));
+ return formatDateTime(OffsetDateTime.ofInstant(Instant.ofEpochMilli(ms), ZoneId.systemDefault()));
+ }
+
+ /**
+ * @since 6.6
+ */
+ public static String formatDateTime(OffsetDateTime dt) {
+ return DATETIME_FORMATTER.format(dt);
}
+ /**
+ * Warning: relies on default timezone!
+ */
public static String formatDateTimeNullSafe(@Nullable Date date) {
- return date == null ? "" : THREAD_SAFE_DATETIME_FORMAT.format(date);
+ return date == null ? "" : formatDateTime(date);
}
@CheckForNull
@@ -77,16 +93,12 @@ public final class DateUtils {
}
/**
+ * Return a date at the start of day.
* @param s string in format {@link #DATE_FORMAT}
* @throws SonarException when string cannot be parsed
*/
public static Date parseDate(String s) {
- ParsePosition pos = new ParsePosition(0);
- Date result = THREAD_SAFE_DATE_FORMAT.parse(s, pos);
- if (pos.getIndex() != s.length()) {
- throw new SonarException("The date '" + s + "' does not respect format '" + DATE_FORMAT + "'");
- }
- return result;
+ return Date.from(parseLocalDate(s).atStartOfDay(ZoneId.systemDefault()).toInstant());
}
/**
@@ -111,17 +123,56 @@ public final class DateUtils {
}
/**
+ * @since 6.6
+ */
+ public static LocalDate parseLocalDate(String s) {
+ try {
+ return LocalDate.parse(s);
+ } catch (DateTimeParseException e) {
+ throw MessageException.of("The date '" + s + "' does not respect format '" + DATE_FORMAT + "'", e);
+ }
+ }
+
+ /**
+ * Parse format {@link #DATE_FORMAT}. This method never throws exception.
+ *
+ * @param s any string
+ * @return the date, {@code null} if parsing error or if parameter is {@code null}
+ * @since 6.6
+ */
+ @CheckForNull
+ public static LocalDate parseLocalDateQuietly(@Nullable String s) {
+ LocalDate date = null;
+ if (s != null) {
+ try {
+ date = parseLocalDate(s);
+ } catch (RuntimeException e) {
+ // ignore
+ }
+
+ }
+ return date;
+ }
+
+ /**
* @param s string in format {@link #DATETIME_FORMAT}
* @throws SonarException when string cannot be parsed
*/
-
public static Date parseDateTime(String s) {
- ParsePosition pos = new ParsePosition(0);
- Date result = THREAD_SAFE_DATETIME_FORMAT.parse(s, pos);
- if (pos.getIndex() != s.length()) {
- throw new SonarException("The date '" + s + "' does not respect format '" + DATETIME_FORMAT + "'");
+ return Date.from(parseOffsetDateTime(s).toInstant());
+ }
+
+ /**
+ * @param s string in format {@link #DATETIME_FORMAT}
+ * @throws SonarException when string cannot be parsed
+ * @since 6.6
+ */
+ public static OffsetDateTime parseOffsetDateTime(String s) {
+ try {
+ return OffsetDateTime.parse(s, DATETIME_FORMATTER);
+ } catch (DateTimeParseException e) {
+ throw MessageException.of("The date '" + s + "' does not respect format '" + DATETIME_FORMAT + "'", e);
}
- return result;
}
/**
@@ -145,6 +196,28 @@ public final class DateUtils {
}
/**
+ * Parse format {@link #DATETIME_FORMAT}. This method never throws exception.
+ *
+ * @param s any string
+ * @return the datetime, {@code null} if parsing error or if parameter is {@code null}
+ * @since 6.6
+ */
+ @CheckForNull
+ public static OffsetDateTime parseOffsetDateTimeQuietly(@Nullable String s) {
+ OffsetDateTime datetime = null;
+ if (s != null) {
+ try {
+ datetime = parseOffsetDateTime(s);
+ } catch (RuntimeException e) {
+ // ignore
+ }
+
+ }
+ return datetime;
+ }
+
+ /**
+ * Warning: may rely on default timezone!
* @throws IllegalArgumentException if stringDate is not a correctly formed date or datetime
* @return the datetime, {@code null} if stringDate is null
* @since 6.1
@@ -155,18 +228,19 @@ public final class DateUtils {
return null;
}
- Date date = parseDateTimeQuietly(stringDate);
- if (date != null) {
- return date;
+ OffsetDateTime odt = parseOffsetDateTimeQuietly(stringDate);
+ if (odt != null) {
+ return Date.from(odt.toInstant());
}
- date = parseDateQuietly(stringDate);
- checkArgument(date != null, "Date '%s' cannot be parsed as either a date or date+time", stringDate);
+ LocalDate ld = parseLocalDateQuietly(stringDate);
+ checkArgument(ld != null, "Date '%s' cannot be parsed as either a date or date+time", stringDate);
- return date;
+ return Date.from(ld.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
/**
+ * Warning: may rely on default timezone!
* @see #parseDateOrDateTime(String)
*/
@CheckForNull
@@ -176,7 +250,7 @@ public final class DateUtils {
/**
* Return the datetime if @param stringDate is a datetime, date + 1 day if stringDate is a date.
- * So '2016-09-01' would return a date equivalent to '2016-09-02T00:00:00+0000' in GMT
+ * So '2016-09-01' would return a date equivalent to '2016-09-02T00:00:00+0000' in GMT (Warning: relies on default timezone!)
* @see #parseDateOrDateTime(String)
* @throws IllegalArgumentException if stringDate is not a correctly formed date or datetime
* @return the datetime, {@code null} if stringDate is null
@@ -208,49 +282,7 @@ public final class DateUtils {
* @return the new date object with the amount added
*/
public static Date addDays(Date date, int numberOfDays) {
- return org.apache.commons.lang.time.DateUtils.addDays(date, numberOfDays);
+ return Date.from(date.toInstant().plus(numberOfDays, ChronoUnit.DAYS));
}
- static class ThreadSafeDateFormat extends DateFormat {
- private final String format;
- private final ThreadLocal<Reference<DateFormat>> cache = new ThreadLocal<Reference<DateFormat>>() {
- @Override
- public Reference<DateFormat> get() {
- Reference<DateFormat> softRef = super.get();
- if (softRef == null || softRef.get() == null) {
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- sdf.setLenient(false);
- softRef = new SoftReference<>(sdf);
- super.set(softRef);
- }
- return softRef;
- }
- };
-
- ThreadSafeDateFormat(String format) {
- this.format = format;
- }
-
- private DateFormat getDateFormat() {
- return cache.get().get();
- }
-
- @Override
- public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
- return getDateFormat().format(date, toAppendTo, fieldPosition);
- }
-
- @Override
- public Date parse(String source, ParsePosition pos) {
- return getDateFormat().parse(source, pos);
- }
-
- private void readObject(ObjectInputStream ois) throws NotSerializableException {
- throw new NotSerializableException();
- }
-
- private void writeObject(ObjectOutputStream ois) throws NotSerializableException {
- throw new NotSerializableException();
- }
- }
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java
index 22b1cecf794..0cea02ff7ea 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java
@@ -20,6 +20,7 @@
package org.sonar.api.utils;
import java.net.URL;
+import java.time.Clock;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
@@ -72,7 +73,9 @@ public class System2 {
/**
* Shortcut for {@link System#currentTimeMillis()}
+ * @deprecated since 6.6 use {@link Clock} that is available in pico.
*/
+ @Deprecated
public long now() {
return System.currentTimeMillis();
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java
index cda37e7996f..4c3dd17e2ef 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java
@@ -22,16 +22,13 @@ package org.sonar.api.utils;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import java.util.ArrayList;
import java.util.Date;
-import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Fail.fail;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.api.utils.DateUtils.parseDateOrDateTime;
import static org.sonar.api.utils.DateUtils.parseDateTime;
@@ -52,13 +49,13 @@ public class DateUtilsTest {
@Test
public void parseDate_not_valid_format() {
- expectedException.expect(SonarException.class);
+ expectedException.expect(MessageException.class);
DateUtils.parseDate("2010/05/18");
}
@Test
public void parseDate_not_lenient() {
- expectedException.expect(SonarException.class);
+ expectedException.expect(MessageException.class);
DateUtils.parseDate("2010-13-18");
}
@@ -71,7 +68,7 @@ public class DateUtilsTest {
@Test
public void parseDate_fail_if_additional_characters() {
- expectedException.expect(SonarException.class);
+ expectedException.expect(MessageException.class);
DateUtils.parseDate("1986-12-04foo");
}
@@ -83,13 +80,13 @@ public class DateUtilsTest {
@Test
public void parseDateTime_not_valid_format() {
- expectedException.expect(SonarException.class);
+ expectedException.expect(MessageException.class);
DateUtils.parseDate("2010/05/18 10:55");
}
@Test
public void parseDateTime_fail_if_additional_characters() {
- expectedException.expect(SonarException.class);
+ expectedException.expect(MessageException.class);
DateUtils.parseDateTime("1986-12-04T01:02:03+0300foo");
}
@@ -103,7 +100,7 @@ public class DateUtilsTest {
@Test
public void shouldFormatDate() {
assertThat(DateUtils.formatDate(new Date())).startsWith("20");
- assertThat(DateUtils.formatDate(new Date()).length()).isEqualTo(10);
+ assertThat(DateUtils.formatDate(new Date())).hasSize(10);
}
@Test
@@ -194,80 +191,4 @@ public class DateUtilsTest {
parseEndingDateOrDateTime("polop");
}
- /**
- * Cordially copied from XStream unit test
- * See http://koders.com/java/fid8A231D75F2C6E6909FB26BCA11C12D08AD05FB50.aspx?s=ThreadSafeDateFormatTest
- */
- @Test
- public void shouldBeThreadSafe() throws Exception {
- final DateUtils.ThreadSafeDateFormat format = new DateUtils.ThreadSafeDateFormat("yyyy-MM-dd'T'HH:mm:ss,S z");
- final Date now = new Date();
- final List<Throwable> throwables = new ArrayList<>();
-
- final ThreadGroup tg = new ThreadGroup("shouldBeThreadSafe") {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- throwables.add(e);
- super.uncaughtException(t, e);
- }
- };
-
- final int[] counter = new int[1];
- counter[0] = 0;
- final Thread[] threads = new Thread[10];
- for (int i = 0; i < threads.length; ++i) {
- threads[i] = new Thread(tg, "JUnit Thread " + i) {
-
- @Override
- public void run() {
- int i = 0;
- try {
- synchronized (this) {
- notifyAll();
- wait();
- }
- while (i < 1000 && !interrupted()) {
- String formatted = format.format(now);
- Thread.yield();
- assertThat(now).isEqualTo(format.parse(formatted));
- ++i;
- }
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
- synchronized (counter) {
- counter[0] += i;
- }
- }
-
- };
- }
-
- for (int i = 0; i < threads.length; ++i) {
- synchronized (threads[i]) {
- threads[i].start();
- threads[i].wait();
- }
- }
-
- for (int i = 0; i < threads.length; ++i) {
- synchronized (threads[i]) {
- threads[i].notifyAll();
- }
- }
-
- Thread.sleep(1000);
-
- for (int i = 0; i < threads.length; ++i) {
- threads[i].interrupt();
- }
- for (int i = 0; i < threads.length; ++i) {
- synchronized (threads[i]) {
- threads[i].join();
- }
- }
-
- assertThat(throwables).isEmpty();
- assertThat(counter[0]).isGreaterThanOrEqualTo(threads.length);
- }
}