]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9718 Fail with a clear message when sonar.projectDate is empty
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 11 Sep 2017 14:18:34 +0000 (16:18 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 15 Sep 2017 06:51:08 +0000 (08:51 +0200)
15 files changed:
server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
server/sonar-server/src/test/java/org/sonar/server/util/RubyUtilsTest.java
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/System2.java
sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/ProjectAnalysisInfoTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/QualityProfileProviderTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java

index 678de1ce81d7ca151b07bb04e7d31176733bb29f..579a53bd147d9a8e2b13ddf0a22c380642cb18f8 100644 (file)
@@ -25,6 +25,9 @@ import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Lists;
+import java.time.Clock;
+import java.time.OffsetDateTime;
+import java.time.Period;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -40,12 +43,9 @@ import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.BooleanUtils;
-import org.joda.time.DateTime;
-import org.joda.time.format.ISOPeriodFormat;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -90,12 +90,12 @@ public class IssueQueryFactory {
   private static final ComponentDto UNKNOWN_COMPONENT = new ComponentDto().setUuid(UNKNOWN).setProjectUuid(UNKNOWN);
 
   private final DbClient dbClient;
-  private final System2 system;
+  private final Clock clock;
   private final UserSession userSession;
 
-  public IssueQueryFactory(DbClient dbClient, System2 system, UserSession userSession) {
+  public IssueQueryFactory(DbClient dbClient, Clock clock, UserSession userSession) {
     this.dbClient = dbClient;
-    this.system = system;
+    this.clock = clock;
     this.userSession = userSession;
   }
 
@@ -138,8 +138,10 @@ public class IssueQueryFactory {
 
     Date actualCreatedAfter = createdAfter;
     if (createdInLast != null) {
-      actualCreatedAfter = new DateTime(system.now()).minus(
-        ISOPeriodFormat.standard().parsePeriod("P" + createdInLast.toUpperCase(Locale.ENGLISH))).toDate();
+      actualCreatedAfter = Date.from(
+        OffsetDateTime.now(clock)
+          .minus(Period.parse("P" + createdInLast.toUpperCase(Locale.ENGLISH)))
+          .toInstant());
     }
     return actualCreatedAfter;
   }
@@ -392,7 +394,7 @@ public class IssueQueryFactory {
     return mainBranchProjectUuid == null ? componentDto.projectUuid() : mainBranchProjectUuid;
   }
 
-  private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch){
+  private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch) {
     builder.branchUuid(branch == null ? null : component.projectUuid());
     builder.mainBranch(branch == null || !branch.equals(component.getBranch()));
   }
index 7b7fc68f854b91aaa6b8c19b0134d6f98bc581ab..b8dda7b6467e1145100aa4bd334f316562d09232 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.platform.platformlevel;
 
+import java.time.Clock;
 import java.util.Properties;
 import javax.annotation.Nullable;
 import org.sonar.NetworkUtils;
@@ -101,6 +102,7 @@ public class PlatformLevel1 extends PlatformLevel {
       TempFolderCleaner.class,
       new TempFolderProvider(),
       System2.INSTANCE,
+      Clock.systemDefaultZone(),
 
       // user session
       ThreadLocalUserSession.class,
index 2ac3e6455c4a12fc0d4cbb12cc2ef0053b6d9fe2..e1f4cd234c3bbab19d2f1f1072c1df0c921602cf 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.server.issue;
 
+import java.time.Clock;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.Date;
 import org.junit.Rule;
@@ -27,7 +29,6 @@ import org.junit.rules.ExpectedException;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
@@ -59,8 +60,8 @@ public class IssueQueryFactoryTest {
   @Rule
   public DbTester db = DbTester.create();
 
-  private System2 system = mock(System2.class);
-  private IssueQueryFactory underTest = new IssueQueryFactory(db.getDbClient(), system, userSession);
+  private Clock clock = mock(Clock.class);
+  private IssueQueryFactory underTest = new IssueQueryFactory(db.getDbClient(), clock, userSession);
 
   @Test
   public void create_from_parameters() {
@@ -126,6 +127,26 @@ public class IssueQueryFactoryTest {
     assertThat(query.createdBefore()).isEqualTo(DateUtils.parseDate("2013-04-18"));
   }
 
+  @Test
+  public void creation_date_support_localdate() {
+    SearchWsRequest request = new SearchWsRequest()
+      .setCreatedAt("2013-04-16");
+
+    IssueQuery query = underTest.create(request);
+
+    assertThat(query.createdAt()).isEqualTo(DateUtils.parseDate("2013-04-16"));
+  }
+
+  @Test
+  public void creation_date_support_zoneddatetime() {
+    SearchWsRequest request = new SearchWsRequest()
+      .setCreatedAt("2013-04-16T09:08:24+0200");
+
+    IssueQuery query = underTest.create(request);
+
+    assertThat(query.createdAt()).isEqualTo(DateUtils.parseDateTime("2013-04-16T09:08:24+0200"));
+  }
+
   @Test
   public void add_unknown_when_no_component_found() {
     SearchWsRequest request = new SearchWsRequest()
@@ -403,8 +424,8 @@ public class IssueQueryFactoryTest {
       .setComponentKeys(singletonList(file.getKey()))
       .setBranch(branch.getBranch())
       .setOnComponentOnly(true)))
-      .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()), IssueQuery::isMainBranch)
-      .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
+        .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.componentUuids()), IssueQuery::isMainBranch)
+        .containsOnly(branch.uuid(), singletonList(file.uuid()), false);
   }
 
   @Test
@@ -415,13 +436,13 @@ public class IssueQueryFactoryTest {
     assertThat(underTest.create(new SearchWsRequest()
       .setProjectKeys(singletonList(project.getKey()))
       .setBranch("master")))
-      .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
-      .containsOnly(project.uuid(), singletonList(project.uuid()), true);
+        .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+        .containsOnly(project.uuid(), singletonList(project.uuid()), true);
     assertThat(underTest.create(new SearchWsRequest()
       .setComponentKeys(singletonList(project.getKey()))
       .setBranch("master")))
-      .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
-      .containsOnly(project.uuid(), singletonList(project.uuid()), true);
+        .extracting(IssueQuery::branchUuid, query -> new ArrayList<>(query.projectUuids()), IssueQuery::isMainBranch)
+        .containsOnly(project.uuid(), singletonList(project.uuid()), true);
   }
 
   @Test
@@ -441,7 +462,8 @@ public class IssueQueryFactoryTest {
   @Test
   public void set_created_after_from_created_since() {
     Date now = DateUtils.parseDateTime("2013-07-25T07:35:00+0100");
-    when(system.now()).thenReturn(now.getTime());
+    when(clock.instant()).thenReturn(now.toInstant());
+    when(clock.getZone()).thenReturn(ZoneOffset.UTC);
     SearchWsRequest request = new SearchWsRequest()
       .setCreatedInLast("1y2m3w4d");
     assertThat(underTest.create(request).createdAfter()).isEqualTo(DateUtils.parseDateTime("2012-04-30T07:35:00+0100"));
index 8ad91f54f46ef92981bc61870c9ae0e2ae9f3254..25d10313b589189fde46334834c068ded877bd4b 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.issue.ws;
 
 import java.io.IOException;
+import java.time.Clock;
 import java.util.Arrays;
 import java.util.Date;
 import org.junit.Rule;
@@ -93,7 +94,7 @@ public class SearchActionComponentsTest {
   private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
   private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
   private ViewIndexer viewIndexer = new ViewIndexer(dbClient, es.client());
-  private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, System2.INSTANCE, userSession);
+  private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession);
   private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter();
   private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter);
   private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new ActionFinder(userSession),
index 850c3099fb381171d5a4fdd390515a8672dae41f..6699b0ab8b4b16478dbcdbd2d7bf8fd074b205f1 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.issue.ws;
 
+import java.time.Clock;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -95,10 +96,11 @@ public class SearchActionTest {
   private DbSession session = db.getSession();
   private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSessionRule, new AuthorizationTypeSupport(userSessionRule));
   private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
-  private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, System2.INSTANCE, userSessionRule);
+  private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSessionRule);
   private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter();
   private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter);
-  private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSessionRule, dbClient, new ActionFinder(userSessionRule), new TransitionService(userSessionRule, issueWorkflow));
+  private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSessionRule, dbClient, new ActionFinder(userSessionRule),
+    new TransitionService(userSessionRule, issueWorkflow));
   private Languages languages = new Languages();
   private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), new WsResponseCommonFormat(languages), languages, new AvatarResolverImpl());
   private WsActionTester ws = new WsActionTester(new SearchAction(userSessionRule, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat));
index 210403f9810d6d8461bff585d5b7a566829a272f..21448a106f112949e4a36dcd0c92c138a4c41990 100644 (file)
@@ -24,7 +24,6 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.SonarException;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -132,7 +131,7 @@ public class RubyUtilsTest {
 
   @Test
   public void toDate_bad_format() {
-    throwable.expect(SonarException.class);
+    throwable.expect(RuntimeException.class);
 
     RubyUtils.toDate("01/02/2013");
   }
index 2a210e519e2ad6ccfd40f51ef3043be1223d1f72..4ec66633e7eb91ab2165d3875c677f6e16abb484 100644 (file)
@@ -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);
     }
   }
index d226fe6270a2845ae2665843c5cdda6fb2c179d6..ea2329164713596e11c2b929438102e2e8b0b60b 100644 (file)
  */
 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());
   }
 
   /**
@@ -110,18 +122,57 @@ public final class DateUtils {
     return date;
   }
 
+  /**
+   * @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();
-    }
-  }
 }
index 22b1cecf794a87e271196c4e9b521ff291d3d278..0cea02ff7ea79088c8248ef796d7c974b2851bf0 100644 (file)
@@ -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();
   }
index cda37e7996f8b725db24c9fbfccf6c9268803aac..4c3dd17e2efd1e2a2219b6fae2a587557a16995b 100644 (file)
@@ -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);
-  }
 }
index c0eed526886b6da6a5223adfdfe2d23ac2589b46..33b8810aa7658174308b603c26d9245a69b150ed 100644 (file)
  */
 package org.sonar.scanner;
 
+import java.time.Clock;
 import java.util.Date;
 import java.util.Optional;
-
 import org.picocontainer.Startable;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.ScannerSide;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.SonarException;
-import org.sonar.api.utils.System2;
 
 /**
  * @since 6.3
@@ -37,15 +35,15 @@ import org.sonar.api.utils.System2;
  */
 @ScannerSide
 public class ProjectAnalysisInfo implements Startable {
-  private final System2 system2;
+  private final Clock clock;
   private Configuration settings;
 
   private Date analysisDate;
   private String analysisVersion;
 
-  public ProjectAnalysisInfo(Configuration settings, System2 system2) {
+  public ProjectAnalysisInfo(Configuration settings, Clock clock) {
     this.settings = settings;
-    this.system2 = system2;
+    this.clock = clock;
   }
 
   public Date analysisDate() {
@@ -59,15 +57,19 @@ public class ProjectAnalysisInfo implements Startable {
   private Date loadAnalysisDate() {
     Optional<String> value = settings.get(CoreProperties.PROJECT_DATE_PROPERTY);
     if (!value.isPresent()) {
-      return new Date(system2.now());
+      return Date.from(clock.instant());
     }
-    Date date;
     try {
       // sonar.projectDate may have been specified as a time
       return DateUtils.parseDateTime(value.get());
-    } catch (SonarException e) {
+    } catch (RuntimeException e) {
       // this is probably just a date
+    }
+    try {
+      // sonar.projectDate may have been specified as a date
       return DateUtils.parseDate(value.get());
+    } catch (RuntimeException e) {
+      throw new IllegalArgumentException("Illegal value for '" + CoreProperties.PROJECT_DATE_PROPERTY + "'", e);
     }
   }
 
index 94dbe459a7c7500ba60b83b67a454484de0f8b41..54510fc24ce63de072d3bc0f3c2fa367bd1bca16 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.scanner.bootstrap;
 
+import java.time.Clock;
 import java.util.List;
 import java.util.Map;
 import org.sonar.api.Plugin;
@@ -94,6 +95,7 @@ public class GlobalContainer extends ComponentContainer {
       UriReader.class,
       new FileCacheProvider(),
       System2.INSTANCE,
+      Clock.systemDefaultZone(),
       new MetricsRepositoryProvider(),
       UuidFactoryImpl.INSTANCE);
     addIfMissing(ScannerPluginInstaller.class, PluginInstaller.class);
index 0e0cdb0f3f52ec4a9becf412ec0f9efbcab30cf5..1a8046209a86a4c6d64b58d13f3a0babb2e02322 100644 (file)
  */
 package org.sonar.scanner;
 
+import java.time.Clock;
 import java.time.LocalDate;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.util.Date;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.utils.System2;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 public class ProjectAnalysisInfoTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void testSimpleDateTime() {
+    MapSettings settings = new MapSettings();
+    settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2017-01-01T12:13:14+0200");
+    settings.appendProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "version");
+    Clock clock = mock(Clock.class);
+    ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), clock);
+    info.start();
+    OffsetDateTime date = OffsetDateTime.of(2017, 1, 1, 12, 13, 14, 0, ZoneOffset.ofHours(2));
+
+    assertThat(info.analysisDate()).isEqualTo(Date.from(date.toInstant()));
+    assertThat(info.analysisVersion()).isEqualTo("version");
+  }
+
   @Test
   public void testSimpleDate() {
     MapSettings settings = new MapSettings();
     settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "2017-01-01");
-    settings.appendProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "version");
-    System2 system = mock(System2.class);
-    ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), system);
+    Clock clock = mock(Clock.class);
+    ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), clock);
     info.start();
     LocalDate date = LocalDate.of(2017, 1, 1);
 
     assertThat(info.analysisDate()).isEqualTo(Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
-    assertThat(info.analysisVersion()).isEqualTo("version");
+  }
+
+  @Test
+  public void emptyDate() {
+    MapSettings settings = new MapSettings();
+    settings.appendProperty(CoreProperties.PROJECT_DATE_PROPERTY, "");
+    settings.appendProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "version");
+    Clock clock = mock(Clock.class);
+    ProjectAnalysisInfo info = new ProjectAnalysisInfo(settings.asConfig(), clock);
+
+    thrown.expect(RuntimeException.class);
+
+    info.start();
   }
 }
index aaea7dde87770610c4f650c8304d90730f5c149b..255e008b6d79c8bba69831dcb35ffa5a44b29b68 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.scanner.repository;
 
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
@@ -28,6 +29,7 @@ import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.sonar.api.batch.bootstrap.ProjectKey;
+import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.scanner.analysis.AnalysisProperties;
@@ -69,7 +71,7 @@ public class QualityProfileProviderTest {
     when(projectRepo.exists()).thenReturn(true);
 
     response = new ArrayList<>(1);
-    response.add(QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").build());
+    response.add(QualityProfile.newBuilder().setKey("profile").setName("profile").setLanguage("lang").setRulesUpdatedAt(DateUtils.formatDateTime(new Date())).build());
   }
 
   @Test
index d6ff0c61f3e59cc3d74b1187568fd0b1ae0e2c77..5091ea813fd56367c940927c97753142d399b643 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.scanner.rule;
 
 import com.google.common.collect.ImmutableList;
+import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import org.junit.Before;
@@ -28,6 +29,7 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.DateUtils;
 import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -79,7 +81,7 @@ public class ActiveRulesProviderTest {
     List<QualityProfile> profiles = new LinkedList<>();
 
     for (String k : keys) {
-      QualityProfile p = QualityProfile.newBuilder().setKey(k).setLanguage(k).build();
+      QualityProfile p = QualityProfile.newBuilder().setKey(k).setLanguage(k).setRulesUpdatedAt(DateUtils.formatDateTime(new Date())).build();
       profiles.add(p);
     }