]> source.dussan.org Git - sonarqube.git/commitdiff
Merge branch-5.1
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 18 Mar 2015 08:17:50 +0000 (09:17 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 18 Mar 2015 08:18:46 +0000 (09:18 +0100)
19 files changed:
1  2 
server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java
server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java
server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java
server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
server/sonar-server/src/main/java/org/sonar/server/search/SearchClient.java
server/sonar-server/src/main/java/org/sonar/server/source/index/SourceFileResultSetIterator.java
server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java
server/sonar-web/src/main/less/components/navbar.less
server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb
sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/test/java/org/sonar/core/persistence/DbTester.java
sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/text/JsonWriter.java

index 87b5a0fa5b04bd2252bed760fc5e9d4e61b7b798,f327dc0fb1e848c9209ddfb73e846bf1b7073174..1e244992e02b82dde567d6d9f193bd32f03e7179
@@@ -123,17 -123,17 +123,17 @@@ public class SourceDbBenchmarkTest 
          .setScmAuthor("a_guy")
          .setSource("this is not java code " + i)
          .setUtLineHits(i)
 -        .setUtConditions(i+1)
 +        .setUtConditions(i + 1)
          .setUtCoveredConditions(i)
          .setItLineHits(i)
 -        .setItConditions(i+1)
 +        .setItConditions(i + 1)
          .setItCoveredConditions(i)
          .setOverallLineHits(i)
 -        .setOverallConditions(i+1)
 +        .setOverallConditions(i + 1)
          .setOverallCoveredConditions(i)
-         .setScmDate(150000000L)
+         .setScmDate(1_500_000_000_000L)
          .setHighlighting("2,9,k;9,18,k")
 -        .addAllDuplications(Arrays.asList(19,33,141))
 +        .addAllDuplication(Arrays.asList(19, 33, 141))
          .build();
      }
      return FileSourceDto.encodeData(dataBuilder.build());
index a5751ae8cb3df837589b00e5a2d45804ba7884f6,abb0d5dcd78a9ba3314cac32269a2915895a6a4e..1c552af441c6832d8490edee6d5617ebf8badadc
@@@ -28,16 -26,19 +26,11 @@@ import org.junit.Test
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.sonar.server.es.EsClient;
- import org.sonar.server.source.index.SourceLineDoc;
- import org.sonar.server.source.index.SourceLineIndex;
- import org.sonar.server.source.index.SourceLineIndexDefinition;
- import org.sonar.server.source.index.SourceLineIndexer;
- import org.sonar.server.source.index.SourceLineResultSetIterator;
+ import org.sonar.server.source.db.FileSourceDb;
 -import org.sonar.server.source.index.SourceFileResultSetIterator;
 -import org.sonar.server.source.index.SourceLineDoc;
 -import org.sonar.server.source.index.SourceLineIndex;
 -import org.sonar.server.source.index.SourceLineIndexDefinition;
 -import org.sonar.server.source.index.SourceLineIndexer;
++import org.sonar.server.source.index.*;
  import org.sonar.server.tester.ServerTester;
  
 -import java.util.Arrays;
 -import java.util.Date;
--import java.util.Iterator;
--import java.util.List;
--import java.util.Timer;
++import java.util.*;
  import java.util.concurrent.atomic.AtomicLong;
  
  import static org.assertj.core.api.Assertions.assertThat;
@@@ -130,28 -132,29 +124,29 @@@ public class SourceIndexBenchmarkTest 
      }
  
      @Override
-     public SourceLineResultSetIterator.SourceFile next() {
+     public SourceFileResultSetIterator.Row next() {
+       String projectUuid = "P" + currentProject;
        String fileUuid = "FILE" + count.get();
-       SourceLineResultSetIterator.SourceFile file = new SourceLineResultSetIterator.SourceFile(fileUuid, System.currentTimeMillis());
+       dataBuilder.clear();
        for (int indexLine = 1; indexLine <= nbLinesPerFile; indexLine++) {
-         SourceLineDoc line = new SourceLineDoc(Maps.<String, Object>newHashMap());
-         line.setFileUuid(fileUuid);
-         line.setLine(indexLine);
-         line.setHighlighting(StringUtils.repeat("HIGHLIGHTING", 5));
-         line.setItConditions(4);
-         line.setItCoveredConditions(2);
-         line.setItLineHits(2);
-         line.setOverallConditions(8);
-         line.setOverallCoveredConditions(2);
-         line.setOverallLineHits(2);
-         line.setUtConditions(8);
-         line.setUtCoveredConditions(2);
-         line.setUtLineHits(2);
-         line.setProjectUuid("PROJECT" + currentProject);
-         line.setScmAuthor("a_guy");
-         line.setScmRevision("ABCDEFGHIJKL");
-         line.setSource(StringUtils.repeat("SOURCE", 10));
-         file.addLine(line);
+         dataBuilder.addLinesBuilder()
+           .setLine(indexLine)
+           .setScmRevision("REVISION_" + indexLine)
+           .setScmAuthor("a_guy")
+           .setSource("this is not java code " + indexLine)
+           .setUtLineHits(2)
+           .setUtConditions(8)
+           .setUtCoveredConditions(2)
+           .setItLineHits(2)
+           .setItConditions(8)
+           .setItCoveredConditions(2)
+           .setOverallLineHits(2)
+           .setOverallConditions(8)
+           .setOverallCoveredConditions(2)
+           .setScmDate(1_500_000_000_000L)
+           .setHighlighting("2,9,k;9,18,k")
 -          .addAllDuplications(Arrays.asList(19, 33, 141))
++          .addAllDuplication(Arrays.asList(19, 33, 141))
+           .build();
        }
        count.incrementAndGet();
        if (count.get() % 500 == 0) {
index 701d649ff8f4f30f9482d3499394b5dece30609f,9bb8cdd7b359c2574354f98c367ecf39ab2c8afe..5e6aae8131129d5fd9bb53f5f046974c787a47a3
  package org.sonar.server.activity.index;
  
  import org.apache.commons.lang.StringUtils;
+ import org.elasticsearch.action.update.UpdateRequest;
  import org.sonar.api.utils.KeyValueFormat;
+ import org.sonar.api.utils.text.JsonWriter;
  import org.sonar.server.db.DbClient;
  import org.sonar.server.db.ResultSetIterator;
+ import org.sonar.server.es.EsUtils;
+ import org.sonar.server.util.DateCollector;
  
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Timestamp;
- import java.util.HashMap;
+ import java.io.ByteArrayOutputStream;
+ import java.io.OutputStreamWriter;
 -import java.sql.Connection;
 -import java.sql.PreparedStatement;
 -import java.sql.ResultSet;
 -import java.sql.SQLException;
 -import java.sql.Timestamp;
++import java.sql.*;
+ import java.util.Date;
  
  /**
   * Scrolls over table ACTIVITIES and reads documents to populate
index dba501d891c55212bc334bd36d901cfa2209b49a,d7ebc315c6a691f09d712efe4a5a1b0869075b14..72fb8da8e708540a56e73cad106877e07f836a19
@@@ -96,9 -93,7 +95,11 @@@ public interface DatabaseMigrations 
      FeedEventsLongDates.class,
      AddNewCharacteristics.class,
      RemovePermissionsOnModulesMigration.class,
 -    DropIssuesColumns.class
+     AddIssuesColumns.class,
++    DropIssuesColumns.class,
 +
 +    // 5.2
 +    FeedProjectLinksComponentUuid.class,
 +    FeedEventsComponentUuid.class
      );
  }
index 837754cf20a9d47bb0fc2fa2e10630e9ef94b3da,451ac8ea9233386ade178aec5c4a680ea9d2f1a0..5c2f6f8e37ae01a008fd967c54bf760fc6b18aa5
@@@ -26,10 -27,13 +27,10 @@@ import org.elasticsearch.search.SearchH
  import org.elasticsearch.search.aggregations.bucket.terms.Terms;
  import org.sonar.server.search.BaseDoc;
  
- import java.util.ArrayList;
- import java.util.LinkedHashMap;
- import java.util.List;
- import java.util.Map;
+ import javax.annotation.CheckForNull;
+ import javax.annotation.Nullable;
 -import java.util.ArrayList;
 -import java.util.Date;
 -import java.util.LinkedHashMap;
 -import java.util.List;
 -import java.util.Map;
++
++import java.util.*;
  
  public class EsUtils {
  
index f1522fcbdf4327c628da190260a24550aca85b38,c9654435c6ef5359c0c070bf1407f452c9151a32..acb87886f665081fe236e58d191b4123d7ca4e72
@@@ -20,9 -20,8 +20,8 @@@
  
  package org.sonar.server.es.request;
  
 -import com.google.common.collect.HashMultiset;
 +import com.google.common.collect.LinkedHashMultiset;
  import com.google.common.collect.Multiset;
- import com.google.common.collect.Multiset.Entry;
  import org.elasticsearch.action.ActionRequest;
  import org.elasticsearch.action.ListenableActionFuture;
  import org.elasticsearch.action.bulk.BulkRequestBuilder;
index 83bc7c3d368e855372a446a5786234562307e3cd,b8003977b5edb56243cd02fbd35c96fdf28dc7e7..ddf5c512ad2a8aa20da8595d2f65de8d1d7f0b7c
@@@ -54,18 -54,17 +54,7 @@@ import org.picocontainer.Startable
  import org.sonar.api.config.Settings;
  import org.sonar.process.LoopbackAddress;
  import org.sonar.process.ProcessConstants;
--import org.sonar.server.es.request.ProxyBulkRequestBuilder;
--import org.sonar.server.es.request.ProxyCountRequestBuilder;
--import org.sonar.server.es.request.ProxyCreateIndexRequestBuilder;
- import org.sonar.server.es.request.ProxyDeleteByQueryRequestBuilder;
--import org.sonar.server.es.request.ProxyDeleteRequestBuilder;
--import org.sonar.server.es.request.ProxyGetRequestBuilder;
--import org.sonar.server.es.request.ProxyIndicesExistsRequestBuilder;
--import org.sonar.server.es.request.ProxyMultiGetRequestBuilder;
--import org.sonar.server.es.request.ProxyPutMappingRequestBuilder;
--import org.sonar.server.es.request.ProxyRefreshRequestBuilder;
--import org.sonar.server.es.request.ProxySearchRequestBuilder;
--import org.sonar.server.es.request.ProxySearchScrollRequestBuilder;
++import org.sonar.server.es.request.*;
  
  /**
   * ElasticSearch Node used to connect to index.
index 0000000000000000000000000000000000000000,cf346eaf8b0d49fba4b880cd017723391bcc2f33..ab01ae4d44dff2717e07b4c93d8e0d868c1c06ca
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,209 +1,209 @@@
 -      writer.name(SourceLineIndexDefinition.FIELD_DUPLICATIONS).valueObject(line.getDuplicationsList());
+ /*
+  * SonarQube, open source software quality management tool.
+  * Copyright (C) 2008-2014 SonarSource
+  * mailto:contact AT sonarsource DOT com
+  *
+  * SonarQube is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+  * License as published by the Free Software Foundation; either
+  * version 3 of the License, or (at your option) any later version.
+  *
+  * SonarQube is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * Lesser General Public License for more details.
+  *
+  * You should have received a copy of the GNU Lesser General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+  */
+ package org.sonar.server.source.index;
+ import org.apache.commons.lang.StringUtils;
+ import org.elasticsearch.action.update.UpdateRequest;
+ import org.sonar.api.utils.text.JsonWriter;
+ import org.sonar.core.source.db.FileSourceDto;
+ import org.sonar.server.db.DbClient;
+ import org.sonar.server.db.ResultSetIterator;
+ import org.sonar.server.es.EsUtils;
+ import org.sonar.server.source.db.FileSourceDb;
+ import java.io.ByteArrayOutputStream;
+ import java.io.OutputStreamWriter;
+ import java.sql.Connection;
+ import java.sql.PreparedStatement;
+ import java.sql.ResultSet;
+ import java.sql.SQLException;
+ import java.util.ArrayList;
+ import java.util.Date;
+ import java.util.List;
+ /**
+  * Scroll over table FILE_SOURCES and directly parse data required to
+  * populate the index sourcelines
+  */
+ public class SourceFileResultSetIterator extends ResultSetIterator<SourceFileResultSetIterator.Row> {
+   public static class Row {
+     private final String fileUuid, projectUuid;
+     private final long updatedAt;
+     private final List<UpdateRequest> lineUpdateRequests = new ArrayList<>();
+     public Row(String projectUuid, String fileUuid, long updatedAt) {
+       this.projectUuid = projectUuid;
+       this.fileUuid = fileUuid;
+       this.updatedAt = updatedAt;
+     }
+     public String getProjectUuid() {
+       return projectUuid;
+     }
+     public String getFileUuid() {
+       return fileUuid;
+     }
+     public long getUpdatedAt() {
+       return updatedAt;
+     }
+     public List<UpdateRequest> getLineUpdateRequests() {
+       return lineUpdateRequests;
+     }
+   }
+   private static final String[] FIELDS = {
+     "project_uuid",
+     "file_uuid",
+     "updated_at",
+     "binary_data"
+   };
+   private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from file_sources";
+   private static final String SQL_AFTER_DATE = SQL_ALL + " where updated_at>?";
+   public static SourceFileResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) {
+     try {
+       String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
+       // rows are big, so they are scrolled once at a time (one row in memory at a time)
+       PreparedStatement stmt = dbClient.newScrollingSingleRowSelectStatement(connection, sql);
+       if (afterDate > 0L) {
+         stmt.setLong(1, afterDate);
+       }
+       return new SourceFileResultSetIterator(stmt);
+     } catch (SQLException e) {
+       throw new IllegalStateException("Fail to prepare SQL request to select all file sources", e);
+     }
+   }
+   private SourceFileResultSetIterator(PreparedStatement stmt) throws SQLException {
+     super(stmt);
+   }
+   @Override
+   protected Row read(ResultSet rs) throws SQLException {
+     String projectUuid = rs.getString(1);
+     String fileUuid = rs.getString(2);
+     Date updatedAt = new Date(rs.getLong(3));
+     FileSourceDb.Data data = FileSourceDto.decodeData(rs.getBinaryStream(4));
+     return toRow(projectUuid, fileUuid, updatedAt, data);
+   }
+   /**
+    * Convert protobuf message to data required for Elasticsearch indexing
+    */
+   public static Row toRow(String projectUuid, String fileUuid, Date updatedAt, FileSourceDb.Data data) {
+     Row result = new Row(projectUuid, fileUuid, updatedAt.getTime());
+     for (FileSourceDb.Line line : data.getLinesList()) {
+       ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+       // all the fields must be present, even if value is null
+       JsonWriter writer = JsonWriter.of(new OutputStreamWriter(bytes)).setSerializeNulls(true);
+       writer.beginObject();
+       writer.prop(SourceLineIndexDefinition.FIELD_PROJECT_UUID, projectUuid);
+       writer.prop(SourceLineIndexDefinition.FIELD_FILE_UUID, fileUuid);
+       writer.prop(SourceLineIndexDefinition.FIELD_LINE, line.getLine());
+       writer.prop(SourceLineIndexDefinition.FIELD_UPDATED_AT, EsUtils.formatDateTime(updatedAt));
+       writer.prop(SourceLineIndexDefinition.FIELD_SCM_REVISION, line.getScmRevision());
+       writer.prop(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, line.getScmAuthor());
+       writer.prop(SourceLineIndexDefinition.FIELD_SCM_DATE, EsUtils.formatDateTime(line.hasScmDate() ? new Date(line.getScmDate()) : null));
+       // unit tests
+       if (line.hasUtLineHits()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_UT_LINE_HITS,  line.getUtLineHits());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_UT_LINE_HITS).valueObject(null);
+       }
+       if (line.hasUtConditions()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_UT_CONDITIONS,  line.getUtConditions());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_UT_CONDITIONS).valueObject(null);
+       }
+       if (line.hasUtCoveredConditions()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_UT_COVERED_CONDITIONS,  line.getUtCoveredConditions());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_UT_COVERED_CONDITIONS).valueObject(null);
+       }
+       // IT
+       if (line.hasItLineHits()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_IT_LINE_HITS,  line.getItLineHits());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_IT_LINE_HITS).valueObject(null);
+       }
+       if (line.hasItConditions()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_IT_CONDITIONS,  line.getItConditions());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_IT_CONDITIONS).valueObject(null);
+       }
+       if (line.hasItCoveredConditions()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_IT_COVERED_CONDITIONS,  line.getItCoveredConditions());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_IT_COVERED_CONDITIONS).valueObject(null);
+       }
+       // Overall coverage
+       if (line.hasOverallLineHits()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_OVERALL_LINE_HITS,  line.getOverallLineHits());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_OVERALL_LINE_HITS).valueObject(null);
+       }
+       if (line.hasOverallConditions()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_OVERALL_CONDITIONS,  line.getOverallConditions());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_OVERALL_CONDITIONS).valueObject(null);
+       }
+       if (line.hasOverallCoveredConditions()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_OVERALL_COVERED_CONDITIONS,  line.getOverallCoveredConditions());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_OVERALL_COVERED_CONDITIONS).valueObject(null);
+       }
+       if (line.hasHighlighting()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_HIGHLIGHTING,  line.getHighlighting());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_HIGHLIGHTING).valueObject(null);
+       }
+       if (line.hasSymbols()) {
+         writer.prop(SourceLineIndexDefinition.FIELD_SYMBOLS,  line.getSymbols());
+       } else {
+         writer.name(SourceLineIndexDefinition.FIELD_SYMBOLS).valueObject(null);
+       }
++      writer.name(SourceLineIndexDefinition.FIELD_DUPLICATIONS).valueObject(line.getDuplicationList());
+       writer.prop(SourceLineIndexDefinition.FIELD_SOURCE, line.hasSource() ? line.getSource() : null);
+       writer.endObject().close();
+       // This is an optimization to reduce memory consumption and multiple conversions from Map to JSON.
+       // UpdateRequest#doc() and #upsert() take the same parameter values, so:
+       // - passing the same Map would execute two JSON serializations
+       // - Map is a useless temporarily structure: read JDBC result set -> convert to map -> convert to JSON. Generating
+       // directly JSON from result set is more efficient.
+       byte[] jsonDoc = bytes.toByteArray();
+       UpdateRequest updateRequest = new UpdateRequest(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, SourceLineIndexDefinition.docKey(fileUuid, line.getLine()))
+         .routing(projectUuid)
+         .doc(jsonDoc)
+         .upsert(jsonDoc);
+       result.lineUpdateRequests.add(updateRequest);
+     }
+     return result;
+   }
+ }
index f00a5b8c4573f714b4d1e7c7858e3325716f16c5,5303a37d684061e487b5e9c4df5afc7d24a45877..ddf68949df2dc4feda78107f8fe214fb88dd9805
@@@ -25,6 -25,6 +25,7 @@@ import org.sonar.server.search.IndexUti
  
  import javax.annotation.CheckForNull;
  import javax.annotation.Nullable;
++
  import java.util.Collection;
  import java.util.Date;
  import java.util.HashMap;
index 8560c032717267262648b640cc0d8b8424b794c8,58ba4796f547fbcd46acce4f59edf16fdda21bce..14b6c10b1a7d90d1b6b22af7e4a7e0915394bea1
@@@ -82,31 -81,26 +81,26 @@@ public class SourceLineIndexerTest 
      assertThat(countDocuments()).isEqualTo(3);
    }
  
+   /**
+    * File F1 in project P1 has one line -> to be updated
+    * File F2 in project P1 has one line -> untouched
+    */
    @Test
    public void update_already_indexed_lines() throws Exception {
-     prepareIndex()
-       .setSource(IOUtils.toString(new FileInputStream(TestUtils.getResource(this.getClass(), "line2.json"))))
-       .get();
-     prepareIndex()
-       .setSource(IOUtils.toString(new FileInputStream(TestUtils.getResource(this.getClass(), "line2_other_file.json"))))
-       .setRefresh(true)
-       .get();
+     indexLine("P1", "F1", 1);
+     indexLine("P1", "F2", 1);
  
      List<Integer> duplications = ImmutableList.of(1, 2, 3);
-     SourceLineDoc line1 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
-       .put(FIELD_PROJECT_UUID, "abcd")
-       .put(FIELD_FILE_UUID, "efgh")
-       .put(FIELD_LINE, 1)
-       .put(FIELD_SCM_REVISION, "cafebabe")
-       .put(FIELD_SCM_DATE, DateUtils.parseDateTime("2014-01-01T12:34:56+0100"))
-       .put(FIELD_SCM_AUTHOR, "polop")
-       .put(FIELD_SOURCE, "package org.sonar.server.source;")
-       .put(FIELD_DUPLICATIONS, duplications)
-       .put(FIELD_UPDATED_AT, new Date())
-       .build());
-     SourceLineResultSetIterator.SourceFile file = new SourceLineResultSetIterator.SourceFile("efgh", System.currentTimeMillis());
-     file.addLine(line1);
-     indexer.index(Iterators.singletonIterator(file));
+     FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder();
+     dataBuilder.addLinesBuilder()
+       .setLine(1)
+       .setScmRevision("new_revision")
+       .setScmAuthor("new_author")
+       .setSource("new source")
 -      .addAllDuplications(duplications)
++      .addAllDuplication(duplications)
+       .build();
+     SourceFileResultSetIterator.Row dbRow = SourceFileResultSetIterator.toRow("P1", "F1", new Date(), dataBuilder.build());
+     indexer.index(Iterators.singletonIterator(dbRow));
  
      assertThat(countDocuments()).isEqualTo(2L);
  
index dc5847f7564e60b4742f5fe66dc1bd20b156b508,8ab131f21688b43fb1d02e1c759bd85dcebea7fd..fdf32504847f1f13e757fa85db525e8e9a84337c
@@@ -27,13 -27,14 +27,9 @@@ import org.sonar.api.database.DatabaseS
  import org.sonar.api.measures.Metric;
  import org.sonar.api.rules.RulePriority;
  
 -import javax.persistence.Column;
 -import javax.persistence.Entity;
 -import javax.persistence.EnumType;
 -import javax.persistence.Enumerated;
 -import javax.persistence.GeneratedValue;
 -import javax.persistence.Id;
 -import javax.persistence.Table;
 +import javax.persistence.*;
 +
  import java.io.UnsupportedEncodingException;
- import java.util.Date;
- import static org.sonar.api.utils.DateUtils.dateToLong;
- import static org.sonar.api.utils.DateUtils.longToDate;
  
  /**
   * This class is the Hibernate model to store a measure in the DB
index f8645826374765689aaa836792dc27f6054bc5dc,dae87d0522f30f758af5b606d7ae9a855ea0148e..8d231bcc1a5507816ab39bbb1fd6f89c22336bf5
@@@ -22,6 -22,6 +22,7 @@@ package org.sonar.api.utils.text
  import org.sonar.api.utils.DateUtils;
  
  import javax.annotation.Nullable;
++
  import java.io.Writer;
  import java.util.Date;
  import java.util.Map;