]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6229 Indexing of activity logs consumes too much memory during server startup
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 2 Mar 2015 12:27:28 +0000 (13:27 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 3 Mar 2015 20:03:39 +0000 (21:03 +0100)
55 files changed:
server/sonar-server/src/main/java/org/sonar/server/activity/Activity.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java
server/sonar-server/src/main/java/org/sonar/server/activity/RubyQProfileActivityService.java
server/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexDefinition.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java
server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java
server/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java
server/sonar-server/src/main/java/org/sonar/server/computation/ReportActivity.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryWsAction.java
server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ChangeLogMigration.java
server/sonar-server/src/main/java/org/sonar/server/es/NewIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueResultSetIterator.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivity.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivityQuery.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java
server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java
server/sonar-server/src/main/java/org/sonar/server/search/IndexDefinition.java
server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java
server/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/activity/RubyQProfileActivityServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexDefinitionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/ReportActivityTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryWsActionMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ChangeLogMigrationTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeMediumTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/db/RuleDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java
server/sonar-server/src/test/resources/org/sonar/server/activity/index/ActivityResultSetIteratorTest/traverse.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryWsActionMediumTest/list_history_reports.json
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb
sonar-core/src/main/java/org/sonar/core/activity/Activity.java [deleted file]
sonar-core/src/main/java/org/sonar/core/activity/ActivityLog.java [deleted file]
sonar-core/src/main/java/org/sonar/core/activity/db/ActivityDto.java
sonar-core/src/main/java/org/sonar/core/activity/db/ActivityMapper.java
sonar-core/src/main/java/org/sonar/core/activity/package-info.java [deleted file]
sonar-core/src/main/resources/org/sonar/core/activity/db/ActivityMapper.xml

diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/Activity.java b/server/sonar-server/src/main/java/org/sonar/server/activity/Activity.java
new file mode 100644 (file)
index 0000000..f0a50a4
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.activity;
+
+import javax.annotation.Nullable;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class Activity {
+
+  public static enum Type {
+    QPROFILE, SERVER, ANALYSIS_REPORT
+  }
+
+  private Type type;
+  private String action;
+  private String message;
+  private final Map<String,Object> data = new LinkedHashMap<>();
+
+  public Type getType() {
+    return type;
+  }
+
+  public void setType(Type type) {
+    this.type = type;
+  }
+
+  public String getAction() {
+    return action;
+  }
+
+  public void setAction(String action) {
+    this.action = action;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+
+  public void setMessage(@Nullable String message) {
+    this.message = message;
+  }
+
+  public Map<String, Object> getData() {
+    return data;
+  }
+
+  public Activity setData(String key, Object val) {
+    this.data.put(key, val);
+    return this;
+  }
+}
index bb91bf20967d3883945b41e26b4e711fbdcf1074..a108c0db06378bd2cf0f88da3f2906631c7510c8 100644 (file)
  */
 package org.sonar.server.activity;
 
-import org.sonar.core.activity.Activity;
-import org.sonar.core.activity.ActivityLog;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.internal.Uuids;
 import org.sonar.core.activity.db.ActivityDto;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.server.activity.index.ActivityIndex;
-import org.sonar.server.activity.index.ActivityQuery;
+import org.sonar.server.activity.index.ActivityIndexer;
 import org.sonar.server.db.DbClient;
-import org.sonar.server.search.IndexClient;
-import org.sonar.server.search.QueryContext;
-import org.sonar.server.search.Result;
 import org.sonar.server.user.UserSession;
 
-import javax.annotation.Nullable;
-
-/**
- * Log service is used to log Activity classes which represents an event to DB and Index.
- *
- * @see org.sonar.core.activity.ActivityLog
- */
-public class ActivityService {
+public class ActivityService implements ServerComponent {
 
   private final DbClient dbClient;
-  private final IndexClient indexClient;
+  private final ActivityIndexer indexer;
 
-  public ActivityService(DbClient dbClient, IndexClient indexClient) {
+  public ActivityService(DbClient dbClient, ActivityIndexer indexer) {
     this.dbClient = dbClient;
-    this.indexClient = indexClient;
-  }
-
-  @Nullable
-  private String getAuthor() {
-    return (UserSession.get().login() != null) ? UserSession.get().login() : null;
-  }
-
-  private void save(DbSession session, ActivityDto log) {
-    dbClient.activityDao().insert(session,
-      log.setAuthor(getAuthor()));
-  }
-
-  public void write(DbSession session, Activity.Type type, String message) {
-    save(session, ActivityDto.createFor(message).setType(type));
-  }
-
-  public <L extends ActivityLog> void write(DbSession session, Activity.Type type, L log) {
-    this.save(session, ActivityDto.createFor(log)
-      .setType(type));
-  }
-
-  public ActivityQuery newActivityQuery() {
-    return new ActivityQuery();
+    this.indexer = indexer;
   }
 
-  public Result<Activity> search(ActivityQuery query, QueryContext options) {
-    ActivityIndex index = indexClient.get(ActivityIndex.class);
-    return new Result<Activity>(index, index.search(query, options));
+  public void save(Activity activity) {
+    ActivityDto dto = new ActivityDto()
+      .setKey(Uuids.create())
+      .setAuthor(UserSession.get().login())
+      .setAction(activity.getAction())
+      .setMessage(activity.getMessage())
+      .setData(KeyValueFormat.format(activity.getData()))
+      .setType(activity.getType().name());
+    dbClient.activityDao().insert(dto);
+    indexer.index();
   }
 }
index 744a3c05fd67ce08165f2f2d75a48d970f191ba8..d0e3c7a07942254b17f0926c533b5b722820bee5 100644 (file)
@@ -23,10 +23,10 @@ package org.sonar.server.activity;
 import org.picocontainer.Startable;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.utils.Paging;
+import org.sonar.server.es.SearchOptions;
 import org.sonar.server.qualityprofile.QProfileActivity;
 import org.sonar.server.qualityprofile.QProfileActivityQuery;
 import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.search.QueryContext;
 import org.sonar.server.search.Result;
 import org.sonar.server.util.RubyUtils;
 
@@ -51,11 +51,8 @@ public class RubyQProfileActivityService implements ServerComponent, Startable {
    */
   public QProfileActivityResult search(Map<String, Object> params) {
     QProfileActivityQuery query = new QProfileActivityQuery();
-    QueryContext queryContext = new QueryContext().setMaxLimit();
-    List<String> profileKeys = RubyUtils.toStrings(params.get("profileKeys"));
-    if (profileKeys != null) {
-      query.setQprofileKeys(profileKeys);
-    }
+
+    query.setQprofileKey((String)params.get("profileKey"));
     Date since = RubyUtils.toDate(params.get("since"));
     if (since != null) {
       query.setSince(since);
@@ -64,12 +61,14 @@ public class RubyQProfileActivityService implements ServerComponent, Startable {
     if (to != null) {
       query.setTo(to);
     }
+
+    SearchOptions options = new SearchOptions();
     Integer page = RubyUtils.toInteger(params.get("p"));
     int pageIndex = page != null ? page : 1;
-    queryContext.setPage(pageIndex, 50);
+    options.setPage(pageIndex, 50);
 
-    Result<QProfileActivity> result = service.searchActivities(query, queryContext);
-    return new QProfileActivityResult(result.getHits(), Paging.create(queryContext.getLimit(), pageIndex, (int) result.getTotal()));
+    Result<QProfileActivity> result = service.searchActivities(query, options);
+    return new QProfileActivityResult(result.getHits(), Paging.create(options.getLimit(), pageIndex, (int) result.getTotal()));
   }
 
   @Override
index 7d4a636947f7f7c9e2258ac33ce229e41cf10ff8..c6a9a271632349356363737683ff5f92e45d314e 100644 (file)
  */
 package org.sonar.server.activity.db;
 
+import org.sonar.api.ServerComponent;
 import org.sonar.api.utils.System2;
 import org.sonar.core.activity.db.ActivityDto;
 import org.sonar.core.activity.db.ActivityMapper;
+import org.sonar.core.persistence.DaoComponent;
 import org.sonar.core.persistence.DbSession;
-import org.sonar.server.db.BaseDao;
-import org.sonar.server.search.IndexDefinition;
+import org.sonar.core.persistence.MyBatis;
 
-import java.util.List;
+import java.util.Date;
 
-/**
- * @since 4.4
- */
-public class ActivityDao extends BaseDao<ActivityMapper, ActivityDto, String> {
+public class ActivityDao implements DaoComponent, ServerComponent {
 
-  public ActivityDao(System2 system) {
-    super(IndexDefinition.LOG, ActivityMapper.class, system);
-  }
+  private final MyBatis mybatis;
+  private final System2 system;
 
-  @Override
-  protected ActivityDto doGetNullableByKey(DbSession session, String key) {
-    throw new IllegalStateException("Cannot execute getByKey on Activities in DB");
+  public ActivityDao(MyBatis mybatis, System2 system) {
+    this.mybatis = mybatis;
+    this.system = system;
   }
 
-  @Override
-  protected ActivityDto doInsert(DbSession session, ActivityDto item) {
-    mapper(session).insert(item);
-    return item;
+  public void insert(ActivityDto dto) {
+    DbSession session = mybatis.openSession(false);
+    try {
+      insert(session, dto);
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
   }
 
-  @Override
-  protected ActivityDto doUpdate(DbSession session, ActivityDto item) {
-    throw new IllegalStateException("Cannot update Log!");
+  public void insert(DbSession session, ActivityDto dto) {
+    dto.setCreatedAt(new Date(system.now()));
+    session.getMapper(ActivityMapper.class).insert(dto);
   }
 
-  @Override
-  protected void doDeleteByKey(DbSession session, String key) {
-    throw new IllegalStateException("Cannot delete Log!");
-  }
-
-  public List<ActivityDto> findAll(DbSession session) {
-    return mapper(session).selectAll();
-  }
 }
index a438fa3a61d9d0927175cc399f6dfbd347761682..46e122bae512c0e976fd6b0fd13d20131593c7e8 100644 (file)
  */
 package org.sonar.server.activity.index;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
-import org.sonar.core.activity.Activity;
 import org.sonar.server.search.BaseDoc;
-import org.sonar.server.search.IndexUtils;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Map;
 
-/**
- * @since 4.4
- */
-public class ActivityDoc extends BaseDoc implements Activity {
+public class ActivityDoc extends BaseDoc {
 
-  protected ActivityDoc(Map<String, Object> fields) {
+  public ActivityDoc(Map<String, Object> fields) {
     super(fields);
   }
 
-  @Override
-  public Date time() {
-    return IndexUtils.parseDateTime((String) getField(ActivityNormalizer.LogFields.CREATED_AT.field()));
+  @VisibleForTesting
+  ActivityDoc() {
+    super(new HashMap<String, Object>());
+  }
+
+  public void setCreatedAt(Date date) {
+    setField(ActivityIndexDefinition.FIELD_CREATED_AT, date);
+  }
+
+  public Date getCreatedAt() {
+    return getFieldAsDate(ActivityIndexDefinition.FIELD_CREATED_AT);
+  }
+
+  public String getKey() {
+    return this.getField(ActivityIndexDefinition.FIELD_KEY);
+  }
+
+  public void setKey(String s) {
+    setField(ActivityIndexDefinition.FIELD_KEY, s);
+  }
+
+  @CheckForNull
+  public String getLogin() {
+    return this.getNullableField(ActivityIndexDefinition.FIELD_LOGIN);
+  }
+
+  public void setLogin(@Nullable String s) {
+    setField(ActivityIndexDefinition.FIELD_LOGIN, s);
+  }
+
+  public String getType() {
+    return ((String) getField(ActivityIndexDefinition.FIELD_TYPE));
+  }
+
+  public void setType(String s) {
+    setField(ActivityIndexDefinition.FIELD_TYPE, s);
+  }
+
+  @CheckForNull
+  public String getAction() {
+    return this.getNullableField(ActivityIndexDefinition.FIELD_ACTION);
   }
 
-  @Override
-  public String login() {
-    return this.getNullableField(ActivityNormalizer.LogFields.LOGIN.field());
+  public void setAction(@Nullable String s) {
+    setField(ActivityIndexDefinition.FIELD_ACTION, s);
   }
 
-  @Override
-  public Type type() {
-    return Type.valueOf((String) getField(ActivityNormalizer.LogFields.TYPE.field()));
+  @CheckForNull
+  public Map<String, String> getDetails() {
+    return this.getNullableField(ActivityIndexDefinition.FIELD_DETAILS);
   }
 
-  @Override
-  public String action() {
-    return this.getNullableField(ActivityNormalizer.LogFields.ACTION.field());
+  public void setDetails(Map<String, String> details) {
+    setField(ActivityIndexDefinition.FIELD_DETAILS, details);
   }
 
-  @Override
-  public Map<String, String> details() {
-    return this.getNullableField(ActivityNormalizer.LogFields.DETAILS.field());
+  @CheckForNull
+  public String getMessage() {
+    return this.getNullableField(ActivityIndexDefinition.FIELD_MESSAGE);
   }
 
-  @Override
-  public String message() {
-    return this.getNullableField(ActivityNormalizer.LogFields.MESSAGE.field());
+  public void setMessage(@Nullable String s) {
+    setField(ActivityIndexDefinition.FIELD_MESSAGE, s);
   }
 
-  @Override
   public String toString() {
     return ReflectionToStringBuilder.toString(this);
   }
index 0e1ced87c50356b4188dc8044c07a6c73408905d..ee082d63d43280b68c923e7790c5eb2f1871b3d2 100644 (file)
  */
 package org.sonar.server.activity.index;
 
+import com.google.common.base.Function;
 import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.action.search.SearchType;
-import org.elasticsearch.common.settings.ImmutableSettings;
-import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.index.query.*;
+import org.elasticsearch.index.query.AndFilterBuilder;
+import org.elasticsearch.index.query.FilterBuilders;
+import org.elasticsearch.index.query.OrFilterBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.search.sort.SortOrder;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.activity.db.ActivityDto;
-import org.sonar.server.search.*;
+import org.sonar.core.util.NonNullInputFunction;
+import org.sonar.server.es.BaseIndex;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.es.SearchResult;
 
-import javax.annotation.Nullable;
-
-import java.util.HashMap;
+import java.util.Date;
 import java.util.Map;
 
-/**
- * @since 4.4
- */
-public class ActivityIndex extends BaseIndex<Activity, ActivityDto, String> {
-
-  public ActivityIndex(ActivityNormalizer normalizer, SearchClient node) {
-    super(IndexDefinition.LOG, normalizer, node);
-  }
-
-  @Override
-  protected String getKeyValue(String key) {
-    return key;
-  }
-
-  @Override
-  protected Map mapKey() {
-    Map<String, Object> mapping = new HashMap<String, Object>();
-    mapping.put("path", ActivityNormalizer.LogFields.KEY.field());
-    return mapping;
-  }
-
-  @Override
-  protected ImmutableSettings.Builder addCustomIndexSettings(ImmutableSettings.Builder settings) {
-    return settings
-      .put("analysis.analyzer.default.type", "keyword");
-  }
+public class ActivityIndex extends BaseIndex {
 
-  @Override
-  protected Map mapProperties() {
-    Map<String, Object> mapping = new HashMap<String, Object>();
-    for (IndexField field : ActivityNormalizer.LogFields.ALL_FIELDS) {
-      mapping.put(field.field(), mapField(field));
+  /**
+   * Convert an Elasticsearch result (a map) to an {@link org.sonar.server.activity.index.ActivityDoc}. It's
+   * used for {@link org.sonar.server.es.SearchResult}.
+   */
+  private static final Function<Map<String, Object>, ActivityDoc> DOC_CONVERTER = new NonNullInputFunction<Map<String, Object>, ActivityDoc>() {
+    @Override
+    protected ActivityDoc doApply(Map<String, Object> input) {
+      return new ActivityDoc(input);
     }
-    return mapping;
-  }
-
-  @Override
-  protected Activity toDoc(final Map<String, Object> fields) {
-    return new ActivityDoc(fields);
-  }
+  };
 
-  public Result<Activity> findAll() {
-    SearchRequestBuilder request = getClient().prepareSearch(this.getIndexName())
-      .setQuery(QueryBuilders.matchAllQuery())
-      .setTypes(this.getIndexType())
-      .setSize(Integer.MAX_VALUE);
-    SearchResponse response = request.get();
-    return new Result<Activity>(this, response);
+  public ActivityIndex(EsClient esClient) {
+    super(esClient);
   }
 
-  public SearchResponse search(ActivityQuery query, QueryContext options) {
-    return search(query, options, null);
+  public SearchResult<ActivityDoc> search(ActivityQuery query, SearchOptions options) {
+    SearchResponse response = doSearch(query, options);
+    return new SearchResult<>(response, DOC_CONVERTER);
   }
 
-  public SearchResponse search(ActivityQuery query, QueryContext options,
-    @Nullable FilterBuilder domainFilter) {
-
-    // Prepare query
-    SearchRequestBuilder esSearch = getClient()
-      .prepareSearch(this.getIndexName())
-      .setTypes(this.getIndexType())
-      .setIndices(this.getIndexName());
-
-    // Integrate Pagination
-    esSearch.setFrom(options.getOffset());
-    esSearch.setSize(options.getLimit());
+  public SearchResponse doSearch(ActivityQuery query, SearchOptions options) {
+    SearchRequestBuilder requestBuilder = getClient()
+      .prepareSearch(ActivityIndexDefinition.INDEX)
+      .setTypes(ActivityIndexDefinition.TYPE);
 
-    // Sort Date Desc
-    esSearch.addSort(ActivityNormalizer.LogFields.CREATED_AT.field(), SortOrder.DESC);
+    requestBuilder.setFrom(options.getOffset());
+    requestBuilder.setSize(options.getLimit());
+    requestBuilder.addSort(ActivityIndexDefinition.FIELD_CREATED_AT, SortOrder.DESC);
 
     AndFilterBuilder filter = FilterBuilders.andFilter();
-
-    // implement Type Filtering
-    OrFilterBuilder typeFilter = FilterBuilders.orFilter();
-    for (Activity.Type type : query.getTypes()) {
-      typeFilter.add(FilterBuilders.termFilter(ActivityNormalizer.LogFields.TYPE.field(), type));
+    if (!query.getTypes().isEmpty()) {
+      OrFilterBuilder typeFilter = FilterBuilders.orFilter();
+      for (String type : query.getTypes()) {
+        typeFilter.add(FilterBuilders.termFilter(ActivityIndexDefinition.FIELD_TYPE, type));
+      }
+      filter.add(typeFilter);
     }
-    filter.add(typeFilter);
-
-    // Implement date Filter
-    filter.add(FilterBuilders.rangeFilter(ActivityNormalizer.LogFields.CREATED_AT.field())
-      .from(query.getSince())
-      .to(query.getTo()));
 
-    // Add any additional domain filter
-    if (domainFilter != null) {
-      filter.add(domainFilter);
+    if (!query.getDataOrFilters().isEmpty()) {
+      for (Map.Entry<String, Object> entry : query.getDataOrFilters().entrySet()) {
+        OrFilterBuilder orFilter = FilterBuilders.orFilter();
+        orFilter.add(FilterBuilders.nestedFilter(ActivityIndexDefinition.FIELD_DETAILS,
+          FilterBuilders.termFilter(ActivityIndexDefinition.FIELD_DETAILS + "." + entry.getKey(), entry.getValue())));
+        filter.add(orFilter);
+      }
     }
 
-    esSearch.setQuery(QueryBuilders.filteredQuery(
-      QueryBuilders.matchAllQuery(), filter));
-
-    if (options.isScroll()) {
-      esSearch.setSearchType(SearchType.SCAN);
-      esSearch.setScroll(TimeValue.timeValueMinutes(3));
+    Date since = query.getSince();
+    if (since != null) {
+      filter.add(FilterBuilders.rangeFilter(ActivityIndexDefinition.FIELD_CREATED_AT)
+        .gt(since)
+        .cache(false));
+    }
+    Date to = query.getTo();
+    if (to != null) {
+      filter.add(FilterBuilders.rangeFilter(ActivityIndexDefinition.FIELD_CREATED_AT)
+        .lt(to)
+        .cache(false));
     }
 
-    return esSearch.get();
+    requestBuilder.setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), filter));
+    return requestBuilder.get();
   }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexDefinition.java
new file mode 100644 (file)
index 0000000..9fd5756
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.activity.index;
+
+import com.google.common.collect.ImmutableMap;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.sonar.api.config.Settings;
+import org.sonar.process.ProcessConstants;
+import org.sonar.server.es.IndexDefinition;
+import org.sonar.server.es.NewIndex;
+
+/**
+ * Definition of ES index "activities", including settings and fields.
+ */
+public class ActivityIndexDefinition implements IndexDefinition {
+
+  public static final String INDEX = "activities";
+  public static final String TYPE = "activity";
+  public static final String FIELD_KEY = "key";
+  public static final String FIELD_TYPE = "type";
+  public static final String FIELD_ACTION = "action";
+  public static final String FIELD_CREATED_AT = "createdAt";
+  public static final String FIELD_LOGIN = "login";
+  public static final String FIELD_DETAILS = "details";
+  public static final String FIELD_MESSAGE = "message";
+
+  private final Settings settings;
+
+  public ActivityIndexDefinition(Settings settings) {
+    this.settings = settings;
+  }
+
+  @Override
+  public void define(IndexDefinitionContext context) {
+    NewIndex index = context.create(INDEX);
+    // refresh is always handled by ActivityIndexer
+    index.getSettings().put("index.refresh_interval", "-1");
+    index.getSettings().put("analysis.analyzer.default.type", "keyword");
+
+    // shards
+    boolean clusterMode = settings.getBoolean(ProcessConstants.CLUSTER_ACTIVATE);
+    if (clusterMode) {
+      index.getSettings().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 4);
+      index.getSettings().put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1);
+      // else keep defaults (one shard)
+    }
+
+    // type "activity"
+    NewIndex.NewIndexType mapping = index.createType(TYPE);
+    mapping.setAttribute("_id", ImmutableMap.of("path", FIELD_KEY));
+    mapping.stringFieldBuilder(FIELD_KEY).build();
+    mapping.stringFieldBuilder(FIELD_TYPE).build();
+    mapping.stringFieldBuilder(FIELD_ACTION).build();
+    mapping.stringFieldBuilder(FIELD_LOGIN).build();
+    mapping.createDynamicNestedField(FIELD_DETAILS);
+    mapping.stringFieldBuilder(FIELD_MESSAGE).build();
+    mapping.createDateTimeField(FIELD_CREATED_AT);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java
new file mode 100644 (file)
index 0000000..1a668bd
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.activity.index;
+
+import org.elasticsearch.action.update.UpdateRequest;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.BaseIndexer;
+import org.sonar.server.es.BulkIndexer;
+import org.sonar.server.es.EsClient;
+
+import java.sql.Connection;
+import java.util.Iterator;
+
+/**
+ * Add to Elasticsearch index {@link org.sonar.server.activity.index.ActivityIndexDefinition} the rows of
+ * db table ACTIVITIES that are not indexed yet
+ * <p/>
+ * TODO idea of improvement - index asynchronously with UpdateRequest#replicationType(ReplicationType.ASYNC)
+ */
+public class ActivityIndexer extends BaseIndexer {
+
+  private final DbClient dbClient;
+
+  public ActivityIndexer(DbClient dbClient, EsClient esClient) {
+    super(esClient, 0L, ActivityIndexDefinition.INDEX, ActivityIndexDefinition.TYPE);
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  protected long doIndex(long lastUpdatedAt) {
+    final BulkIndexer bulk = new BulkIndexer(esClient, ActivityIndexDefinition.INDEX);
+    bulk.setLarge(lastUpdatedAt == 0L);
+
+    DbSession dbSession = dbClient.openSession(false);
+    Connection dbConnection = dbSession.getConnection();
+    try {
+      ActivityResultSetIterator rowIt = ActivityResultSetIterator.create(dbClient, dbConnection, lastUpdatedAt);
+      long maxUpdatedAt = doIndex(bulk, rowIt);
+      rowIt.close();
+      return maxUpdatedAt;
+
+    } finally {
+      dbSession.close();
+    }
+  }
+
+  public long index(Iterator<ActivityDoc> activities) {
+    final BulkIndexer bulk = new BulkIndexer(esClient, ActivityIndexDefinition.INDEX);
+    return doIndex(bulk, activities);
+  }
+
+  private long doIndex(BulkIndexer bulk, Iterator<ActivityDoc> activities) {
+    long maxUpdatedAt = 0L;
+    bulk.start();
+    while (activities.hasNext()) {
+      ActivityDoc activity = activities.next();
+      bulk.add(newUpsertRequest(activity));
+
+      // it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance)
+      maxUpdatedAt = Math.max(maxUpdatedAt, activity.getCreatedAt().getTime());
+    }
+    bulk.stop();
+    return maxUpdatedAt;
+  }
+
+  private UpdateRequest newUpsertRequest(ActivityDoc doc) {
+    return new UpdateRequest(ActivityIndexDefinition.INDEX, ActivityIndexDefinition.TYPE, doc.getKey())
+      .doc(doc.getFields())
+      .upsert(doc.getFields());
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java
deleted file mode 100644 (file)
index 2e64ed4..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.activity.index;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import org.elasticsearch.action.support.replication.ReplicationType;
-import org.elasticsearch.action.update.UpdateRequest;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.core.activity.db.ActivityDto;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.search.BaseNormalizer;
-import org.sonar.server.search.IndexField;
-import org.sonar.server.search.Indexable;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @since 4.4
- */
-public class ActivityNormalizer extends BaseNormalizer<ActivityDto, String> {
-
-  public static final class LogFields extends Indexable {
-
-    public static final IndexField KEY = add(IndexField.Type.STRING, "key");
-    public static final IndexField TYPE = addSortable(IndexField.Type.STRING, "type");
-    public static final IndexField ACTION = addSortable(IndexField.Type.STRING, "action");
-    public static final IndexField CREATED_AT = addSortable(IndexField.Type.DATE, "createdAt");
-    public static final IndexField UPDATED_AT = addSortable(IndexField.Type.DATE, BaseNormalizer.UPDATED_AT_FIELD);
-    public static final IndexField LOGIN = addSearchable(IndexField.Type.STRING, "login");
-    public static final IndexField DETAILS = addSearchable(IndexField.Type.OBJECT, "details");
-    public static final IndexField MESSAGE = addSearchable(IndexField.Type.STRING, "message");
-    public static final Set<IndexField> ALL_FIELDS = ImmutableSet.of(KEY, TYPE, ACTION, CREATED_AT, UPDATED_AT, LOGIN, DETAILS, MESSAGE);
-  }
-
-  public ActivityNormalizer(DbClient db) {
-    super(db);
-  }
-
-  @Override
-  public List<UpdateRequest> normalize(ActivityDto dto) {
-
-    Map<String, Object> logDoc = new HashMap<String, Object>();
-    logDoc.put(LogFields.KEY.field(), dto.getKey());
-    logDoc.put(LogFields.TYPE.field(), dto.getType());
-    logDoc.put(LogFields.ACTION.field(), dto.getAction());
-    logDoc.put(LogFields.LOGIN.field(), dto.getAuthor());
-    logDoc.put(LogFields.MESSAGE.field(), dto.getMessage());
-    logDoc.put(LogFields.CREATED_AT.field(), dto.getCreatedAt());
-    logDoc.put(LogFields.UPDATED_AT.field(), dto.getUpdatedAt());
-
-    logDoc.put(LogFields.DETAILS.field(), KeyValueFormat.parse(dto.getData()));
-
-    /* Creating updateRequest */
-    return ImmutableList.of(new UpdateRequest()
-      .id(dto.getKey())
-      .replicationType(ReplicationType.ASYNC)
-      .doc(logDoc)
-      .upsert(logDoc));
-  }
-}
index 53c8749ae211d732b34a4cdf6e70c868fc563d65..b8e25d91c15507e338da2fb29579857b4c960001 100644 (file)
  */
 package org.sonar.server.activity.index;
 
-import com.google.common.collect.Lists;
-import org.sonar.core.activity.Activity;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
-/**
- * @since 4.4
- */
 public class ActivityQuery {
 
   private Date since;
   private Date to;
-  private Collection<Activity.Type> types;
-
-  public ActivityQuery() {
-    types = Lists.newArrayList();
-  }
+  private final Collection<String> types = new ArrayList<>();
+  private final Map<String, Object> dataOrFilters = new LinkedHashMap<>();
 
+  @CheckForNull
   public Date getSince() {
     return since;
   }
 
-  public ActivityQuery setSince(Date since) {
+  public ActivityQuery setSince(@Nullable Date since) {
     this.since = since;
     return this;
   }
 
+  @CheckForNull
   public Date getTo() {
     return to;
   }
 
-  public ActivityQuery setTo(Date to) {
+  public ActivityQuery setTo(@Nullable Date to) {
     this.to = to;
     return this;
   }
 
-  public Collection<Activity.Type> getTypes() {
+  public Collection<String> getTypes() {
     return types;
   }
 
-  public ActivityQuery setTypes(Collection<Activity.Type> types) {
-    this.types = types;
+  public ActivityQuery setTypes(@Nullable Collection<String> types) {
+    this.types.clear();
+    if (types != null) {
+      this.types.addAll(types);
+    }
+    return this;
+  }
+
+  public Map<String, Object> getDataOrFilters() {
+    return dataOrFilters;
+  }
+
+  public ActivityQuery addDataOrFilter(String dataKey, Object dataValue) {
+    dataOrFilters.put(dataKey, dataValue);
     return this;
   }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java
new file mode 100644 (file)
index 0000000..701d649
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.activity.index;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.db.ResultSetIterator;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.HashMap;
+
+/**
+ * Scrolls over table ACTIVITIES and reads documents to populate
+ * the index "activities/activity"
+ */
+class ActivityResultSetIterator extends ResultSetIterator<ActivityDoc> {
+
+  private static final String[] FIELDS = {
+    // column 1
+    "log_key",
+    "log_action",
+    "log_message",
+    "data_field",
+    "user_login",
+    "log_type",
+    "created_at"
+  };
+
+  private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from activities ";
+
+  private static final String SQL_AFTER_DATE = SQL_ALL + " where created_at>=?";
+
+  private ActivityResultSetIterator(PreparedStatement stmt) throws SQLException {
+    super(stmt);
+  }
+
+  static ActivityResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) {
+    try {
+      String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
+      PreparedStatement stmt = dbClient.newScrollingSelectStatement(connection, sql);
+      if (afterDate > 0L) {
+        stmt.setTimestamp(1, new Timestamp(afterDate));
+      }
+      return new ActivityResultSetIterator(stmt);
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to prepare SQL request to select activities", e);
+    }
+  }
+
+  @Override
+  protected ActivityDoc read(ResultSet rs) throws SQLException {
+    ActivityDoc doc = new ActivityDoc(new HashMap<String, Object>(10));
+
+    // all the fields must be present, even if value is null
+    doc.setKey(rs.getString(1));
+    doc.setAction(rs.getString(2));
+    doc.setMessage(rs.getString(3));
+    doc.setDetails(KeyValueFormat.parse(rs.getString(4)));
+    doc.setLogin(rs.getString(5));
+    doc.setType(rs.getString(6));
+    doc.setCreatedAt(rs.getTimestamp(7));
+    return doc;
+  }
+}
index aee5b31cdc650e819aa1dbecf6b5c14ab6b052a3..07a756335cff16472e990b281984fd0b324da23c 100644 (file)
@@ -20,9 +20,8 @@
 package org.sonar.server.activity.ws;
 
 import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.core.activity.Activity;
 import org.sonar.server.activity.index.ActivityDoc;
-import org.sonar.server.activity.index.ActivityNormalizer;
+import org.sonar.server.activity.index.ActivityIndexDefinition;
 import org.sonar.server.search.QueryContext;
 import org.sonar.server.search.ws.BaseMapping;
 
@@ -34,16 +33,16 @@ import java.util.Map;
 public class ActivityMapping extends BaseMapping<ActivityDoc, Object> {
 
   public ActivityMapping() {
-    map("type", ActivityNormalizer.LogFields.TYPE.field());
-    map("action", ActivityNormalizer.LogFields.ACTION.field());
-    mapDateTime("createdAt", ActivityNormalizer.LogFields.CREATED_AT.field());
-    map("login", ActivityNormalizer.LogFields.LOGIN.field());
-    map("message", ActivityNormalizer.LogFields.MESSAGE.field());
-    map("details", new IndexMapper<ActivityDoc, Object>(ActivityNormalizer.LogFields.DETAILS.field()) {
+    map("type", ActivityIndexDefinition.FIELD_TYPE);
+    map("action", ActivityIndexDefinition.FIELD_ACTION);
+    mapDateTime("createdAt", ActivityIndexDefinition.FIELD_CREATED_AT);
+    map("login", ActivityIndexDefinition.FIELD_LOGIN);
+    map("message", ActivityIndexDefinition.FIELD_MESSAGE);
+    map("details", new IndexMapper<ActivityDoc, Object>(ActivityIndexDefinition.FIELD_DETAILS) {
       @Override
       public void write(JsonWriter json, ActivityDoc activity, Object context) {
         json.name("details").beginObject();
-        for (Map.Entry<String, String> detail : activity.details().entrySet()) {
+        for (Map.Entry<String, String> detail : activity.getDetails().entrySet()) {
           json.prop(detail.getKey(), detail.getValue());
         }
         json.endObject();
@@ -51,8 +50,8 @@ public class ActivityMapping extends BaseMapping<ActivityDoc, Object> {
     });
   }
 
-  public void write(Activity activity, JsonWriter writer, QueryContext context) {
-    doWrite((ActivityDoc) activity, null, writer, context);
+  public void write(ActivityDoc activity, JsonWriter writer, QueryContext context) {
+    doWrite(activity, null, writer, context);
   }
 
 }
index 256028e0209b4071a308400427d264d7c069e0e9..0bb0647f753e12da0cc61e9f4276e0290cd3a5bd 100644 (file)
  */
 package org.sonar.server.activity.ws;
 
-import org.apache.commons.lang.StringUtils;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.core.activity.Activity;
-import org.sonar.server.activity.ActivityService;
+import org.sonar.server.activity.Activity;
+import org.sonar.server.activity.index.ActivityDoc;
+import org.sonar.server.activity.index.ActivityIndex;
 import org.sonar.server.activity.index.ActivityQuery;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.es.SearchResult;
 import org.sonar.server.search.QueryContext;
-import org.sonar.server.search.Result;
-import org.sonar.server.search.ws.SearchOptions;
 
-/**
- * @since 4.4
- */
 public class SearchAction implements RequestHandler {
 
   public static final String PARAM_TYPE = "type";
-
   public static final String SEARCH_ACTION = "search";
 
-  private final ActivityService logService;
-  private final ActivityMapping mapping;
+  private final ActivityIndex activityIndex;
+  private final ActivityMapping docToJsonMapping;
 
-  public SearchAction(ActivityService logService, ActivityMapping mapping) {
-    this.logService = logService;
-    this.mapping = mapping;
+  public SearchAction(ActivityIndex activityIndex, ActivityMapping docToJsonMapping) {
+    this.activityIndex = activityIndex;
+    this.docToJsonMapping = docToJsonMapping;
   }
 
   void define(WebService.NewController controller) {
@@ -57,36 +53,34 @@ public class SearchAction implements RequestHandler {
       .setInternal(true)
       .setHandler(this);
 
-    // Other parameters
     action.createParam(PARAM_TYPE)
-      .setDescription("Types of activities to search")
-      .setPossibleValues(Activity.Type.values())
-      .setDefaultValue(StringUtils.join(Activity.Type.values(), ","));
+      .setDescription("Activity type")
+      .setPossibleValues(Activity.Type.values());
 
-    // Generic search parameters
-    SearchOptions.defineFieldsParam(action, mapping.supportedFields());
-
-    SearchOptions.definePageParams(action);
+    action.addPagingParams(10);
+    action.addFieldsParam(docToJsonMapping.supportedFields());
   }
 
   @Override
   public void handle(Request request, Response response) {
-    ActivityQuery query = logService.newActivityQuery();
-    SearchOptions searchOptions = SearchOptions.create(request);
-    QueryContext queryContext = mapping.newQueryOptions(searchOptions);
+    ActivityQuery query = new ActivityQuery();
+    query.setTypes(request.paramAsStrings(PARAM_TYPE));
+
+    SearchOptions options = new SearchOptions();
+    options.setPage(request.mandatoryParamAsInt(WebService.Param.PAGE), request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE));
 
-    Result<Activity> results = logService.search(query, queryContext);
+    SearchResult<ActivityDoc> results = activityIndex.search(query, options);
 
     JsonWriter json = response.newJsonWriter().beginObject();
-    searchOptions.writeStatistics(json, results);
-    writeLogs(results, json, queryContext);
+    options.writeJson(json, results.getTotal());
+    writeActivities(results, json, new QueryContext().setFieldsToReturn(request.paramAsStrings(WebService.Param.FIELDS)));
     json.endObject().close();
   }
 
-  private void writeLogs(Result<Activity> result, JsonWriter json, QueryContext context) {
+  private void writeActivities(SearchResult<ActivityDoc> docs, JsonWriter json, QueryContext context) {
     json.name("logs").beginArray();
-    for (Activity log : result.getHits()) {
-      mapping.write(log, json, context);
+    for (ActivityDoc doc : docs.getDocs()) {
+      docToJsonMapping.write(doc, json, context);
     }
     json.endArray();
   }
index 8787e16ec9459e1cb722f945b13fc8e365c6b2ec..cdaca50a7d167380f32138b8b826d35d39de75e2 100644 (file)
@@ -31,11 +31,11 @@ import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.api.utils.log.Profiler;
 import org.sonar.batch.protocol.output.BatchReportReader;
-import org.sonar.core.activity.Activity;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.core.computation.db.AnalysisReportDto;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.ActivityService;
 import org.sonar.server.computation.step.ComputationStep;
 import org.sonar.server.computation.step.ComputationSteps;
@@ -44,6 +44,9 @@ import org.sonar.server.db.DbClient;
 import java.io.File;
 import java.io.IOException;
 
+import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe;
+import static org.sonar.api.utils.DateUtils.longToDate;
+
 public class ComputationService implements ServerComponent {
 
   private static final Logger LOG = Loggers.get(ComputationService.class);
@@ -87,7 +90,7 @@ public class ComputationService implements ServerComponent {
 
     } finally {
       item.dto.setFinishedAt(system.now());
-      logActivity(item.dto, project);
+      saveActivity(item.dto, project);
       profiler.stopInfo();
     }
   }
@@ -117,14 +120,19 @@ public class ComputationService implements ServerComponent {
     }
   }
 
-  private void logActivity(AnalysisReportDto report, ComponentDto project) {
-    DbSession session = dbClient.openSession(false);
-    try {
-      report.setFinishedAt(System2.INSTANCE.now());
-      activityService.write(session, Activity.Type.ANALYSIS_REPORT, new ReportActivity(report, project));
-      session.commit();
-    } finally {
-      MyBatis.closeQuietly(session);
-    }
+  private void saveActivity(AnalysisReportDto report, ComponentDto project) {
+    Activity activity = new Activity();
+    activity.setType(Activity.Type.ANALYSIS_REPORT);
+    activity.setAction("LOG_ANALYSIS_REPORT");
+    activity
+      .setData("key", String.valueOf(report.getId()))
+      .setData("projectKey", project.key())
+      .setData("projectName", project.name())
+      .setData("projectUuid", project.uuid())
+      .setData("status", String.valueOf(report.getStatus()))
+      .setData("submittedAt", formatDateTimeNullSafe(longToDate(report.getCreatedAt())))
+      .setData("startedAt", formatDateTimeNullSafe(longToDate(report.getStartedAt())))
+      .setData("finishedAt", formatDateTimeNullSafe(longToDate(report.getFinishedAt())));
+    activityService.save(activity);
   }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportActivity.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportActivity.java
deleted file mode 100644 (file)
index b4d7165..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.computation;
-
-import com.google.common.collect.ImmutableMap;
-import org.sonar.core.activity.ActivityLog;
-import org.sonar.core.component.ComponentDto;
-import org.sonar.core.computation.db.AnalysisReportDto;
-
-import java.util.Map;
-
-import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe;
-import static org.sonar.api.utils.DateUtils.longToDate;
-
-public class ReportActivity implements ActivityLog {
-
-  private static final String ACTION = "LOG_ANALYSIS_REPORT";
-
-  private final AnalysisReportDto report;
-  private final ComponentDto project;
-
-  public ReportActivity(AnalysisReportDto report, ComponentDto project) {
-    this.report = report;
-    this.project = project;
-  }
-
-  @Override
-  public Map<String, String> getDetails() {
-    return ImmutableMap.<String, String>builder()
-      .put("key", String.valueOf(report.getId()))
-      .put("projectKey", project.key())
-      .put("projectName", project.name())
-      .put("projectUuid", project.uuid())
-      .put("status", String.valueOf(report.getStatus()))
-      .put("submittedAt", formatDateTimeNullSafe(longToDate(report.getCreatedAt())))
-      .put("startedAt", formatDateTimeNullSafe(longToDate(report.getStartedAt())))
-      .put("finishedAt", formatDateTimeNullSafe(longToDate(report.getFinishedAt())))
-      .build();
-  }
-
-  @Override
-  public String getAction() {
-    return ACTION;
-  }
-}
index eec1336a1576a11eb271f65f42f805aa5cd5518b..82135a2c15fefb7e6e65777f5ce4505a1564986c 100644 (file)
@@ -25,29 +25,28 @@ import org.sonar.api.server.ws.RequestHandler;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.core.activity.Activity;
 import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.server.activity.ActivityService;
+import org.sonar.server.activity.Activity;
+import org.sonar.server.activity.index.ActivityDoc;
+import org.sonar.server.activity.index.ActivityIndex;
 import org.sonar.server.activity.index.ActivityQuery;
-import org.sonar.server.activity.ws.ActivityMapping;
-import org.sonar.server.search.QueryContext;
-import org.sonar.server.search.Result;
-import org.sonar.server.search.ws.SearchOptions;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.es.SearchResult;
+import org.sonar.server.issue.ws.IssuesWs;
 import org.sonar.server.user.UserSession;
 
 import java.util.Arrays;
 import java.util.Map;
 
+// FIXME replace by api/activities/search
 public class HistoryWsAction implements ComputationWsAction, RequestHandler {
 
   public static final String PARAM_TYPE = "type";
 
-  private final ActivityService activityService;
-  private final ActivityMapping mapping;
+  private final ActivityIndex activityIndex;
 
-  public HistoryWsAction(ActivityService activityService, ActivityMapping mapping) {
-    this.activityService = activityService;
-    this.mapping = mapping;
+  public HistoryWsAction(ActivityIndex activityIndex) {
+    this.activityIndex = activityIndex;
   }
 
   @Override
@@ -59,38 +58,31 @@ public class HistoryWsAction implements ComputationWsAction, RequestHandler {
       .setInternal(true)
       .setHandler(this);
 
-    // Generic search parameters
-    SearchOptions.defineFieldsParam(action, mapping.supportedFields());
-    SearchOptions.definePageParams(action);
+    action.addPagingParams(10);
   }
 
   @Override
   public void handle(Request request, Response response) {
-    checkUserRights();
-
-    ActivityQuery query = activityService.newActivityQuery();
-    query.setTypes(Arrays.asList(Activity.Type.ANALYSIS_REPORT));
+    UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
 
-    SearchOptions searchOptions = SearchOptions.create(request);
-    QueryContext queryContext = mapping.newQueryOptions(searchOptions);
+    ActivityQuery query = new ActivityQuery();
+    query.setTypes(Arrays.asList(Activity.Type.ANALYSIS_REPORT.name()));
 
-    Result<Activity> results = activityService.search(query, queryContext);
+    SearchOptions options = new SearchOptions();
+    options.setPage(request.mandatoryParamAsInt(IssuesWs.Param.PAGE), request.mandatoryParamAsInt(IssuesWs.Param.PAGE_SIZE));
+    SearchResult<ActivityDoc> results = activityIndex.search(query, options);
 
     JsonWriter json = response.newJsonWriter().beginObject();
-    searchOptions.writeStatistics(json, results);
+    options.writeJson(json, results.getTotal());
     writeReports(results, json);
     json.endObject().close();
   }
 
-  private void checkUserRights() {
-    UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
-  }
-
-  private void writeReports(Result<Activity> result, JsonWriter json) {
+  private void writeReports(SearchResult<ActivityDoc> result, JsonWriter json) {
     json.name("reports").beginArray();
-    for (Activity reportActivity : result.getHits()) {
+    for (ActivityDoc doc : result.getDocs()) {
       json.beginObject();
-      for (Map.Entry<String, String> detail : reportActivity.details().entrySet()) {
+      for (Map.Entry<String, String> detail : doc.getDetails().entrySet()) {
         json.prop(detail.getKey(), detail.getValue());
       }
       json.endObject();
index 41b28a488dd4fe91000f048170713e982e7dd96f..ca1ca126cd8e37c24d1898037acfc0afdafc956e 100644 (file)
@@ -21,13 +21,15 @@ package org.sonar.server.db.migrations.v44;
 
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.core.activity.Activity;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.internal.Uuids;
 import org.sonar.core.activity.db.ActivityDto;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.migration.v44.ChangeLog;
 import org.sonar.core.persistence.migration.v44.Migration44Mapper;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
 import org.sonar.core.rule.SeverityUtil;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.db.ActivityDao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.db.migrations.DatabaseMigration;
@@ -99,11 +101,16 @@ public class ChangeLogMigration implements DatabaseMigration {
   }
 
   private void saveActiveRuleChange(DbSession session, ActiveRuleChange ruleChange, String author, Date currentTimeStamp) {
-    ActivityDto activity = ActivityDto.createFor(ruleChange);
-    activity.setType(Activity.Type.QPROFILE);
-    activity.setAuthor(author);
-    activity.setCreatedAt(currentTimeStamp);
-    dao.insert(session, activity);
+    Activity activity = ruleChange.toActivity();
+    ActivityDto dto = new ActivityDto();
+    dto.setKey(Uuids.create());
+    dto.setType(Activity.Type.QPROFILE.name());
+    dto.setAction(activity.getAction());
+    dto.setMessage(activity.getMessage());
+    dto.setAuthor(author);
+    dto.setData(KeyValueFormat.format(activity.getData()));
+    dto.setCreatedAt(currentTimeStamp);
+    dao.insert(session, dto);
   }
 
   private void processRuleChange(ActiveRuleChange ruleChange, ChangeLog change) {
index 4459fd5b3dbf402923aabe311cd9671b33b079c9..22493030b9e7bd5fadc99ea62404801b7f374dae 100644 (file)
@@ -96,6 +96,10 @@ public class NewIndex {
       return setProperty(fieldName, ImmutableMap.of("type", "long"));
     }
 
+    public NewIndexType createDynamicNestedField(String fieldName) {
+      return setProperty(fieldName, ImmutableMap.of("type", "nested", "dynamic", "true"));
+    }
+
     public NewIndexType createShortField(String fieldName) {
       return setProperty(fieldName, ImmutableMap.of("type", "short"));
     }
index 7f24b0a56cdbdaa34071f0395e762ac0d54b7c2c..76de0d8236321e7cef969ff6811c97aa8828c5ad 100644 (file)
@@ -150,7 +150,7 @@ class IssueResultSetIterator extends ResultSetIterator<IssueDoc> {
     String key = rs.getString(1);
     String projectUuid = rs.getString(2);
 
-    // all the keys must be present, even if value is null
+    // all the fields must be present, even if value is null
     doc.setKey(key);
     doc.setProjectUuid(projectUuid);
     doc.setTechnicalUpdateDate(new Date(rs.getLong(3)));
index c5e4b4770a0e3e325e7e17b553719e217c3e120f..9898640cb76d92642e2d2981b9db72f8d02d9383 100644 (file)
@@ -63,7 +63,13 @@ import org.sonar.server.user.UserSession;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.newHashMap;
@@ -263,7 +269,7 @@ public class SearchAction implements BaseIssuesWsAction {
   @Override
   public final void handle(Request request, Response response) throws Exception {
     SearchOptions options = new SearchOptions();
-    options.setPage(request.mandatoryParamAsInt(IssuesWs.Param.PAGE), request.mandatoryParamAsInt(IssuesWs.Param.PAGE_SIZE));
+    options.setPage(request.mandatoryParamAsInt(WebService.Param.PAGE), request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE));
     options.addFacets(request.paramAsStrings(WebService.Param.FACETS));
 
     IssueQuery query = issueQueryService.createFromRequest(request);
index cf8302be667c551b6bfd1b7f0d52d913ebcbfa6f..dd409d3e8aef78bcad4243525275a89a2094fc45 100644 (file)
@@ -78,7 +78,8 @@ import org.sonar.server.activity.ActivityService;
 import org.sonar.server.activity.RubyQProfileActivityService;
 import org.sonar.server.activity.db.ActivityDao;
 import org.sonar.server.activity.index.ActivityIndex;
-import org.sonar.server.activity.index.ActivityNormalizer;
+import org.sonar.server.activity.index.ActivityIndexDefinition;
+import org.sonar.server.activity.index.ActivityIndexer;
 import org.sonar.server.activity.ws.ActivitiesWebService;
 import org.sonar.server.activity.ws.ActivityMapping;
 import org.sonar.server.authentication.ws.AuthenticationWs;
@@ -423,10 +424,6 @@ class ServerComponents {
       IssueIndex.class,
       IssueDao.class,
 
-      // Activity
-      ActivityService.class,
-      ActivityNormalizer.class,
-      ActivityIndex.class,
       ActivityDao.class
       ));
     components.addAll(CorePropertyDefinitions.all());
@@ -491,6 +488,12 @@ class ServerComponents {
     pico.addSingleton(IndexRegistry.class);
     pico.addSingleton(IndexCreator.class);
 
+    // Activity
+    pico.addSingleton(ActivityService.class);
+    pico.addSingleton(ActivityIndexDefinition.class);
+    pico.addSingleton(ActivityIndexer.class);
+    pico.addSingleton(ActivityIndex.class);
+
     // batch
     pico.addSingleton(BatchIndex.class);
     pico.addSingleton(GlobalRepositoryAction.class);
index 44391663eec10b91158a1dd8bc262219a30a8891..ae29ee90da52cdcbd8d4ac62eaa6eae6b1d9cc55 100644 (file)
@@ -22,16 +22,15 @@ package org.sonar.server.qualityprofile;
 import com.google.common.base.Objects;
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringUtils;
-import org.sonar.core.activity.ActivityLog;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
+import org.sonar.server.activity.Activity;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import java.util.HashMap;
 import java.util.Map;
 
-public class ActiveRuleChange implements ActivityLog {
+public class ActiveRuleChange {
 
   public static enum Type {
     ACTIVATED, DEACTIVATED, UPDATED
@@ -91,26 +90,26 @@ public class ActiveRuleChange implements ActivityLog {
     return this;
   }
 
-  @Override
-  public Map<String, String> getDetails() {
-    HashMap<String, String> details = new HashMap<String, String>();
-
-    details.put("key", getKey().toString());
-    details.put("ruleKey", getKey().ruleKey().toString());
-    details.put("profileKey", getKey().qProfile().toString());
+  public Activity toActivity() {
+    Activity activity = new Activity();
+    activity.setType(Activity.Type.QPROFILE);
+    activity.setAction(type.name());
+    activity.setData("key", getKey().toString());
+    activity.setData("ruleKey", getKey().ruleKey().toString());
+    activity.setData("profileKey", getKey().qProfile().toString());
 
     for (Map.Entry<String, String> param : parameters.entrySet()) {
       if (!param.getKey().isEmpty()) {
-        details.put("param_" + param.getKey(), param.getValue());
+        activity.setData("param_" + param.getKey(), param.getValue());
       }
     }
     if (StringUtils.isNotEmpty(severity)) {
-      details.put("severity", severity);
+      activity.setData("severity", severity);
     }
     if (inheritance != null) {
-      details.put("inheritance", inheritance.name());
+      activity.setData("inheritance", inheritance.name());
     }
-    return details;
+    return activity;
   }
 
   public static ActiveRuleChange createFor(Type type, ActiveRuleKey key) {
@@ -127,9 +126,4 @@ public class ActiveRuleChange implements ActivityLog {
       .add("parameters", parameters)
       .toString();
   }
-
-  @Override
-  public String getAction() {
-    return type.name();
-  }
 }
index 8b294dfa62f6e33f08a44bfd8a2c5a8e7e2fcff3..fa49d3695e4c28ff53303f1d2467c41effdc3564 100644 (file)
@@ -21,19 +21,17 @@ package org.sonar.server.qualityprofile;
 
 import com.google.common.collect.Maps;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.core.activity.Activity;
 import org.sonar.server.activity.index.ActivityDoc;
-import org.sonar.server.activity.index.ActivityNormalizer;
+import org.sonar.server.activity.index.ActivityIndexDefinition;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-
 import java.util.Map;
 
 /**
  * @since 4.4
  */
-public class QProfileActivity extends ActivityDoc implements Activity {
+public class QProfileActivity extends ActivityDoc {
 
   private String ruleName = null;
   private String authorName = null;
@@ -77,8 +75,8 @@ public class QProfileActivity extends ActivityDoc implements Activity {
 
   @Override
   @CheckForNull
-  public String login() {
-    return getNullableField(ActivityNormalizer.LogFields.LOGIN.field());
+  public String getLogin() {
+    return getNullableField(ActivityIndexDefinition.FIELD_LOGIN);
   }
 
   @CheckForNull
index cc303dab5a1525ea99ca68dd3cd48179a6c12ed8..5cce6da31fecc06e9ac6d9c62d4fe2a1d9ad21b7 100644 (file)
  */
 package org.sonar.server.qualityprofile;
 
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import org.sonar.core.activity.Activity;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.index.ActivityQuery;
 
-import java.util.Collection;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.util.Arrays;
 
 /**
  * @since 4.4
  */
 public class QProfileActivityQuery extends ActivityQuery {
 
-  Collection<String> qprofileKeys;
-
   public QProfileActivityQuery() {
     super();
-    this.setTypes(ImmutableSet.of(Activity.Type.QPROFILE));
-    qprofileKeys = Lists.newArrayList();
+    setTypes(Arrays.asList(Activity.Type.QPROFILE.name()));
   }
 
-  public Collection<String> getQprofileKeys() {
-    return qprofileKeys;
+  @CheckForNull
+  public String getQprofileKey() {
+    return (String)getDataOrFilters().get("profileKey");
   }
 
-  public QProfileActivityQuery setQprofileKeys(Collection<String> qprofileKeys) {
-    this.qprofileKeys = qprofileKeys;
+  public QProfileActivityQuery setQprofileKey(@Nullable String qprofileKey) {
+    addDataOrFilter("profileKey", qprofileKey);
     return this;
   }
 }
index 0e0a7acb4ac46e08a86fea9b6162aa5ae5425a8e..42a4ad0f0a3137c935a1886d30704c0b352853f2 100644 (file)
@@ -20,9 +20,6 @@
 package org.sonar.server.qualityprofile;
 
 import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.index.query.FilterBuilders;
-import org.elasticsearch.index.query.OrFilterBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.search.SearchHit;
 import org.sonar.api.ServerComponent;
 import org.sonar.core.permission.GlobalPermissions;
@@ -33,15 +30,13 @@ import org.sonar.core.rule.RuleDto;
 import org.sonar.core.user.UserDto;
 import org.sonar.server.activity.index.ActivityIndex;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
 import org.sonar.server.rule.index.RuleQuery;
-import org.sonar.server.search.IndexClient;
-import org.sonar.server.search.QueryContext;
 import org.sonar.server.search.Result;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
@@ -53,7 +48,7 @@ import java.util.Map;
 public class QProfileService implements ServerComponent {
 
   private final DbClient db;
-  private final IndexClient index;
+  private final ActivityIndex activityIndex;
   private final RuleActivator ruleActivator;
   private final QProfileFactory factory;
   private final QProfileBackuper backuper;
@@ -61,10 +56,10 @@ public class QProfileService implements ServerComponent {
   private final QProfileReset reset;
   private final QProfileExporters exporters;
 
-  public QProfileService(DbClient db, IndexClient index, RuleActivator ruleActivator, QProfileFactory factory,
+  public QProfileService(DbClient db, ActivityIndex activityIndex, RuleActivator ruleActivator, QProfileFactory factory,
                          QProfileBackuper backuper, QProfileCopier copier, QProfileReset reset, QProfileExporters exporters) {
     this.db = db;
-    this.index = index;
+    this.activityIndex = activityIndex;
     this.ruleActivator = ruleActivator;
     this.factory = factory;
     this.backuper = backuper;
@@ -214,23 +209,17 @@ public class QProfileService implements ServerComponent {
     UserSession.get().checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
   }
 
-  public Result<QProfileActivity> searchActivities(QProfileActivityQuery query, QueryContext options) {
+  public Result<QProfileActivity> searchActivities(QProfileActivityQuery query, SearchOptions options) {
     DbSession session = db.openSession(false);
     try {
-      OrFilterBuilder activityFilter = FilterBuilders.orFilter();
-      for (String profileKey : query.getQprofileKeys()) {
-        activityFilter.add(FilterBuilders.nestedFilter("details",
-          QueryBuilders.matchQuery("details.profileKey", profileKey)));
-      }
-
-      SearchResponse response = index.get(ActivityIndex.class).search(query, options, activityFilter);
+      SearchResponse response = activityIndex.doSearch(query, options);
       Result<QProfileActivity> result = new Result<QProfileActivity>(response);
       for (SearchHit hit : response.getHits().getHits()) {
         QProfileActivity profileActivity = new QProfileActivity(hit.getSource());
         RuleDto ruleDto = db.ruleDao().getNullableByKey(session, profileActivity.ruleKey());
         profileActivity.ruleName(ruleDto != null ? ruleDto.getName() : null);
 
-        String login = profileActivity.login();
+        String login = profileActivity.getLogin();
         if (login != null) {
           UserDto user = db.userDao().selectActiveUserByLogin(login, session);
           profileActivity.authorName(user != null ? user.getName() : null);
index 96bc7511f2f25fbf5044e45ea4c6ce262b9ffbe7..757334f710028da9c0ce4e16965cea5d5ac75b58 100644 (file)
@@ -23,7 +23,6 @@ import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.server.rule.RuleParamType;
-import org.sonar.core.activity.Activity;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.qualityprofile.db.ActiveRuleDto;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
@@ -63,16 +62,16 @@ public class RuleActivator implements ServerComponent {
   private final TypeValidations typeValidations;
   private final RuleActivatorContextFactory contextFactory;
   private final IndexClient index;
-  private final ActivityService log;
+  private final ActivityService activityService;
 
   public RuleActivator(DbClient db, IndexClient index,
     RuleActivatorContextFactory contextFactory, TypeValidations typeValidations,
-    ActivityService log) {
+    ActivityService activityService) {
     this.db = db;
     this.index = index;
     this.contextFactory = contextFactory;
     this.typeValidations = typeValidations;
-    this.log = log;
+    this.activityService = activityService;
   }
 
   public List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation, String profileKey) {
@@ -240,7 +239,7 @@ public class RuleActivator implements ServerComponent {
     } else if (change.getType() == ActiveRuleChange.Type.UPDATED) {
       activeRule = doUpdate(change, context, dbSession);
     }
-    log.write(dbSession, Activity.Type.QPROFILE, change);
+    activityService.save(change.toActivity());
     return activeRule;
   }
 
index 1a03ce1866231b0f3954c96f1e5a1c8be657eac0..65d82a82998ababa075fdaf0eacdff439932b0be 100644 (file)
@@ -55,6 +55,10 @@ import javax.annotation.Nullable;
 import java.io.Serializable;
 import java.util.*;
 
+/**
+ * @deprecated replaced by {@link org.sonar.server.es.BaseIndex}
+ */
+@Deprecated
 public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serializable>
   implements Index<DOMAIN, DTO, KEY> {
 
@@ -215,7 +219,7 @@ public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serial
   protected abstract String getKeyValue(KEY key);
 
   public final Settings getIndexSettings() {
-    ImmutableSettings.Builder settings = this.addCustomIndexSettings(this.getBaseIndexSettings());
+    ImmutableSettings.Builder settings = this.getBaseIndexSettings();
 
     // In case there is a replication factor set by the index,
     // it is removed since we're using global cluster state
@@ -225,10 +229,6 @@ public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serial
     return settings.build();
   }
 
-  protected ImmutableSettings.Builder addCustomIndexSettings(ImmutableSettings.Builder baseIndexSettings) {
-    return baseIndexSettings;
-  }
-
   protected abstract Map mapProperties();
 
   protected abstract Map mapKey();
index fc9b36ebe17e730ba8bb664459d2501240cd8f4c..ffafa74a18a43edb373624ee45296600dd318064 100644 (file)
@@ -40,7 +40,6 @@ public class IndexDefinition {
   public static final IndexDefinition RULE = new IndexDefinition("rules", "rule");
   public static final IndexDefinition ACTIVE_RULE = new IndexDefinition("rules", "activeRule");
   public static final IndexDefinition ISSUES = new IndexDefinition("issues", "issue");
-  public static final IndexDefinition LOG = new IndexDefinition("logs", "sonarLog");
 
   // Only used for test
   static final IndexDefinition TEST = new IndexDefinition("test", "test");
index 2aaa1e874b702b4a37f23905d39eefb7325297ba..b4499a4d20c8881226edfa59e97288fa23c3606b 100644 (file)
@@ -23,7 +23,7 @@ package org.sonar.server.search;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.persistence.DbSession;
-import org.sonar.server.activity.index.ActivityIndex;
+import org.sonar.server.activity.index.ActivityIndexer;
 import org.sonar.server.db.Dao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.issue.index.IssueAuthorizationIndexer;
@@ -47,9 +47,11 @@ public class IndexSynchronizer {
   private final IssueIndexer issueIndexer;
   private final UserIndexer userIndexer;
   private final ViewIndexer viewIndexer;
+  private final ActivityIndexer activityIndexer;
 
   public IndexSynchronizer(DbClient db, IndexClient index, SourceLineIndexer sourceLineIndexer,
-    IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, UserIndexer userIndexer, ViewIndexer viewIndexer) {
+                           IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer,
+                           UserIndexer userIndexer, ViewIndexer viewIndexer, ActivityIndexer activityIndexer) {
     this.db = db;
     this.index = index;
     this.sourceLineIndexer = sourceLineIndexer;
@@ -57,6 +59,7 @@ public class IndexSynchronizer {
     this.issueIndexer = issueIndexer;
     this.userIndexer = userIndexer;
     this.viewIndexer = viewIndexer;
+    this.activityIndexer = activityIndexer;
   }
 
   public void execute() {
@@ -64,12 +67,14 @@ public class IndexSynchronizer {
     try {
       synchronize(session, db.ruleDao(), index.get(RuleIndex.class));
       synchronize(session, db.activeRuleDao(), index.get(ActiveRuleIndex.class));
-      synchronize(session, db.activityDao(), index.get(ActivityIndex.class));
       session.commit();
     } finally {
       session.close();
     }
 
+    LOG.info("Index activities");
+    activityIndexer.index();
+
     LOG.info("Index issues");
     issueAuthorizationIndexer.index();
     issueIndexer.index();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java
deleted file mode 100644 (file)
index d89298a..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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.activity;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import org.elasticsearch.action.search.SearchResponse;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.activity.ActivityLog;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.server.activity.db.ActivityDao;
-import org.sonar.server.activity.index.ActivityIndex;
-import org.sonar.server.activity.index.ActivityQuery;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.search.QueryContext;
-import org.sonar.server.search.Result;
-import org.sonar.server.tester.ServerTester;
-
-import java.util.Iterator;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ActivityBackendMediumTest {
-
-  @ClassRule
-  public static ServerTester tester = new ServerTester();
-
-  ActivityService service = tester.get(ActivityService.class);
-  ActivityDao dao = tester.get(ActivityDao.class);
-  ActivityIndex index = tester.get(ActivityIndex.class);
-  DbSession dbSession;
-
-  @Before
-  public void before() {
-    tester.clearDbAndIndexes();
-    dbSession = tester.get(DbClient.class).openSession(false);
-  }
-
-  @After
-  public void after() {
-    dbSession.close();
-  }
-
-  @Test
-  public void insert_find_text_log() throws InterruptedException {
-    final String testValue = "hello world";
-    service.write(dbSession, Activity.Type.QPROFILE, testValue);
-    dbSession.commit();
-    assertThat(index.findAll().getTotal()).isEqualTo(1);
-
-    Activity activity = Iterables.getFirst(index.findAll().getHits(), null);
-    assertThat(activity).isNotNull();
-    assertThat(activity.message()).isEqualTo(testValue);
-  }
-
-  @Test
-  public void insert_find_loggable_log() {
-    final String testKey = "message";
-    final String testValue = "hello world";
-    service.write(dbSession, Activity.Type.QPROFILE, new ActivityLog() {
-
-      @Override
-      public Map<String, String> getDetails() {
-        return ImmutableMap.of(testKey, testValue);
-      }
-
-      @Override
-      public String getAction() {
-        return "myAction";
-      }
-    });
-    dbSession.commit();
-
-    assertThat(index.findAll().getTotal()).isEqualTo(1);
-
-    Activity activity = Iterables.getFirst(index.findAll().getHits(), null);
-    assertThat(activity).isNotNull();
-    assertThat(activity.details().get(testKey)).isEqualTo(testValue);
-  }
-
-  @Test
-  public void current_time_zone() {
-    service.write(dbSession, Activity.Type.QPROFILE, "now");
-    dbSession.commit();
-
-    Activity activity = service.search(new ActivityQuery(), new QueryContext()).getHits().get(0);
-    assertThat(System.currentTimeMillis() - activity.time().getTime()).isLessThan(1000L);
-  }
-
-  @Test
-  public void massive_insert() {
-
-    // 0 Assert no logs in DB
-    assertThat(dao.findAll(dbSession)).hasSize(0);
-    int max = 35;
-    final String testValue = "hello world";
-    for (int i = 0; i < max; i++) {
-
-      service.write(dbSession, Activity.Type.QPROFILE, testValue + "_" + i);
-    }
-    dbSession.commit();
-
-    // 1. assert both backends have all logs
-    assertThat(dao.findAll(dbSession)).hasSize(max);
-    assertThat(index.findAll().getHits()).hasSize(max);
-
-    // 2. assert scrollable
-    int count = 0;
-    SearchResponse result = index.search(new ActivityQuery(), new QueryContext().setScroll(true));
-    Iterator<Activity> logs = new Result<Activity>(index, result).scroll();
-    while (logs.hasNext()) {
-      logs.next();
-      count++;
-    }
-    assertThat(count).isEqualTo(max);
-
-    // 3 assert synchronize above IndexQueue threshold
-    tester.clearIndexes();
-    tester.get(Platform.class).executeStartupTasks();
-    result = index.search(new ActivityQuery(), new QueryContext().setScroll(true));
-    logs = new Result<Activity>(index, result).scroll();
-    count = 0;
-    while (logs.hasNext()) {
-      logs.next();
-      count++;
-    }
-    assertThat(count).isEqualTo(max);
-
-  }
-
-  @Test
-  public void massive_log_insert() {
-
-    // 0 Assert no logs in DB
-    assertThat(dao.findAll(dbSession)).hasSize(0);
-    int max = 40;
-    final String testValue = "hello world";
-    for (int i = 0; i < max; i++) {
-      TestActivityLog log = new TestActivityLog(testValue + "_" + i, Activity.Type.QPROFILE.toString());
-      service.write(dbSession, Activity.Type.QPROFILE, log);
-    }
-    dbSession.commit();
-
-    // 1. assert both backends have all logs
-    assertThat(dao.findAll(dbSession)).hasSize(max);
-    assertThat(index.findAll().getHits()).hasSize(max);
-
-    // 2. assert scrollable
-    int count = 0;
-    SearchResponse result = index.search(new ActivityQuery(), new QueryContext().setScroll(true));
-    Iterator<Activity> logs = new Result<Activity>(index, result).scroll();
-    while (logs.hasNext()) {
-      logs.next();
-      count++;
-    }
-    assertThat(count).isEqualTo(max);
-
-    // 3 assert synchronize above IndexQueue threshold
-    tester.clearIndexes();
-    tester.get(Platform.class).executeStartupTasks();
-    result = index.search(new ActivityQuery(), new QueryContext().setScroll(true));
-    logs = new Result<Activity>(index, result).scroll();
-    count = 0;
-    while (logs.hasNext()) {
-      logs.next();
-      count++;
-    }
-    assertThat(count).isEqualTo(max);
-
-  }
-
-  class TestActivityLog implements ActivityLog {
-
-    private final String name;
-    private final String action;
-
-    TestActivityLog(String name, String action) {
-      this.name = name;
-      this.action = action;
-    }
-
-    @Override
-    public Map<String, String> getDetails() {
-      return ImmutableMap.<String, String>of("name", name);
-    }
-
-    @Override
-    public String getAction() {
-      return action;
-    }
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java
deleted file mode 100644 (file)
index 71fec3c..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * 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.activity;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import org.elasticsearch.action.search.SearchResponse;
-import org.joda.time.DateTime;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.activity.ActivityLog;
-import org.sonar.core.activity.db.ActivityDto;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.server.activity.db.ActivityDao;
-import org.sonar.server.activity.index.ActivityIndex;
-import org.sonar.server.activity.index.ActivityQuery;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.search.QueryContext;
-import org.sonar.server.search.Result;
-import org.sonar.server.tester.ServerTester;
-
-import java.util.Iterator;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ActivityServiceMediumTest {
-
-  @ClassRule
-  public static ServerTester tester = new ServerTester();
-
-  ActivityService service = tester.get(ActivityService.class);
-  ActivityDao dao = tester.get(ActivityDao.class);
-  ActivityIndex index = tester.get(ActivityIndex.class);
-  DbSession dbSession;
-
-  @Before
-  public void before() {
-    tester.clearDbAndIndexes();
-    dbSession = tester.get(DbClient.class).openSession(false);
-  }
-
-  @After
-  public void after() {
-    dbSession.close();
-  }
-
-  @Test
-  public void find_all() throws InterruptedException {
-    service.write(dbSession, Activity.Type.QPROFILE, testValue);
-    dbSession.commit();
-    assertThat(index.findAll().getTotal()).isEqualTo(1);
-
-    Activity activity = Iterables.getFirst(index.findAll().getHits(), null);
-    assertThat(activity).isNotNull();
-    assertThat(activity.message()).isEqualTo(testValue);
-  }
-
-  @Test
-  public void search_message_log() throws InterruptedException {
-    service.write(dbSession, Activity.Type.QPROFILE, testValue);
-    dbSession.commit();
-    assertThat(index.findAll().getTotal()).isEqualTo(1);
-
-    SearchResponse result = index.search(service.newActivityQuery(), new QueryContext());
-    assertThat(result.getHits().getTotalHits()).isEqualTo(1L);
-    Result<Activity> activityResult = new Result<Activity>(index, result);
-    assertThat(activityResult.getHits().get(0).message()).isEqualTo(testValue);
-  }
-
-  @Test
-  public void search_activity_log() throws InterruptedException {
-
-    service.write(dbSession, Activity.Type.QPROFILE, getActivity());
-    dbSession.commit();
-    assertThat(index.findAll().getTotal()).isEqualTo(1);
-
-    SearchResponse result = index.search(service.newActivityQuery(), new QueryContext());
-    assertThat(result.getHits().getTotalHits()).isEqualTo(1L);
-    Result<Activity> activityResult = new Result<Activity>(index, result);
-    assertThat(activityResult.getHits().get(0).details().get(test_key)).isEqualTo(test_value);
-  }
-
-  @Test
-  public void filter_by_type() {
-    service.write(dbSession, Activity.Type.NONE, getActivity());
-    service.write(dbSession, Activity.Type.SERVER, getActivity());
-    service.write(dbSession, Activity.Type.SERVER, testValue);
-    service.write(dbSession, Activity.Type.QPROFILE, getActivity());
-    dbSession.commit();
-
-    assertThat(service.search(new ActivityQuery(),
-      new QueryContext()).getHits()).hasSize(4);
-
-    assertThat(service.search(new ActivityQuery()
-      .setTypes(ImmutableSet.of(Activity.Type.SERVER)),
-      new QueryContext()).getHits()).hasSize(2);
-
-    assertThat(service.search(new ActivityQuery()
-      .setTypes(ImmutableSet.of(Activity.Type.QPROFILE)),
-      new QueryContext()).getHits()).hasSize(1);
-  }
-
-  @Test
-  public void filter_by_date() throws InterruptedException {
-
-    DateTime t0 = new DateTime().minusHours(1);
-    ActivityDto activity = getActivityDto();
-    activity.setCreatedAt(t0.toDate());
-    dao.insert(dbSession, activity);
-    activity = getActivityDto();
-    activity.setCreatedAt(t0.toDate());
-    dao.insert(dbSession, activity);
-    dbSession.commit();
-    DateTime t1 = new DateTime();
-    activity = getActivityDto();
-    activity.setCreatedAt(t1.toDate());
-    dao.insert(dbSession, activity);
-    dbSession.commit();
-    DateTime t2 = new DateTime().plusHours(1);
-
-    assertThat(service.search(new ActivityQuery(),
-      new QueryContext()).getHits()).hasSize(3);
-
-    assertThat(service.search(new ActivityQuery()
-      .setSince(t0.minusSeconds(5).toDate()),
-      new QueryContext()).getHits()).hasSize(3);
-
-    assertThat(service.search(new ActivityQuery()
-      .setSince(t1.minusSeconds(5).toDate()),
-      new QueryContext()).getHits()).hasSize(1);
-
-    assertThat(service.search(new ActivityQuery()
-      .setSince(t2.minusSeconds(5).toDate()),
-      new QueryContext()).getHits()).hasSize(0);
-
-    assertThat(service.search(new ActivityQuery()
-      .setTo(t1.minusSeconds(5).toDate()),
-      new QueryContext()).getHits()).hasSize(2);
-
-    assertThat(service.search(new ActivityQuery()
-      .setSince(t1.minusSeconds(5).toDate())
-      .setTo(t2.plusSeconds(5).toDate()),
-      new QueryContext()).getHits()).hasSize(1);
-  }
-
-  private ActivityDto getActivityDto() {
-    return ActivityDto.createFor(testValue)
-      .setType(Activity.Type.NONE).setAuthor("testing");
-  }
-
-  @Test
-  public void iterate_all() throws InterruptedException {
-    int max = QueryContext.DEFAULT_LIMIT + 3;
-    final String testValue = "hello world";
-    for (int i = 0; i < max; i++) {
-      service.write(dbSession, Activity.Type.QPROFILE, testValue + "_" + i);
-    }
-    dbSession.commit();
-
-    // 0. assert Base case
-    assertThat(dao.findAll(dbSession)).hasSize(max);
-
-    SearchResponse result = index.search(service.newActivityQuery(), new QueryContext().setScroll(true));
-    assertThat(result.getHits().getTotalHits()).isEqualTo(max);
-    Result<Activity> activityResult = new Result<Activity>(index, result);
-
-    assertThat(activityResult.getTotal()).isEqualTo(max);
-    assertThat(activityResult.getHits()).hasSize(0);
-    int count = 0;
-    Iterator<Activity> logIterator = activityResult.scroll();
-    while (logIterator.hasNext()) {
-      count++;
-      logIterator.next();
-    }
-    assertThat(count).isEqualTo(max);
-  }
-
-  final String test_key = "hello";
-  final String test_value = "world";
-  final String testValue = "hello world";
-
-  private ActivityLog getActivity() {
-    return new ActivityLog() {
-      @Override
-      public Map<String, String> getDetails() {
-        return ImmutableMap.of(test_key, test_value);
-      }
-
-      @Override
-      public String getAction() {
-        return "myAction";
-      }
-    };
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceTest.java
new file mode 100644 (file)
index 0000000..5844794
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.activity;
+
+import org.apache.commons.io.IOUtils;
+import org.assertj.core.data.MapEntry;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.activity.db.ActivityDao;
+import org.sonar.server.activity.index.ActivityDoc;
+import org.sonar.server.activity.index.ActivityIndexDefinition;
+import org.sonar.server.activity.index.ActivityIndexer;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.issue.db.IssueDao;
+
+import java.sql.Clob;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ActivityServiceTest {
+
+  @ClassRule
+  public static DbTester db = new DbTester();
+
+  @ClassRule
+  public static EsTester es = new EsTester().addDefinitions(new ActivityIndexDefinition(new Settings()));
+
+  System2 system = mock(System2.class);
+  ActivityService service;
+
+  @Before
+  public void before() {
+    ActivityDao activityDao = new ActivityDao(db.myBatis(), system);
+    IssueDao issueDao = new IssueDao(db.myBatis());
+    DbClient dbClient = new DbClient(db.database(), db.myBatis(), issueDao, activityDao);
+    service = new ActivityService(dbClient, new ActivityIndexer(dbClient, es.client()));
+  }
+
+  @Test
+  public void insert_and_index() throws Exception {
+    when(system.now()).thenReturn(1_500_000_000_000L);
+
+    Activity activity = new Activity();
+    activity.setType(Activity.Type.ANALYSIS_REPORT);
+    activity.setAction("THE_ACTION");
+    activity.setMessage("THE_MSG");
+    activity.setData("foo", "bar");
+    service.save(activity);
+
+    Map<String, Object> dbMap = db.selectFirst("select log_type as \"type\", log_action as \"action\", log_message as \"msg\", data_field as \"data\" from activities");
+    assertThat(dbMap).containsEntry("type", "ANALYSIS_REPORT");
+    assertThat(dbMap).containsEntry("action", "THE_ACTION");
+    assertThat(dbMap).containsEntry("msg", "THE_MSG");
+    Clob data = (Clob) dbMap.get("data");
+    assertThat(IOUtils.toString(data.getAsciiStream())).isEqualTo("foo=bar");
+
+    List<ActivityDoc> docs = es.getDocuments("activities", "activity", ActivityDoc.class);
+    assertThat(docs).hasSize(1);
+    assertThat(docs.get(0).getKey()).isNotEmpty();
+    assertThat(docs.get(0).getAction()).isEqualTo("THE_ACTION");
+    assertThat(docs.get(0).getMessage()).isEqualTo("THE_MSG");
+    assertThat(docs.get(0).getDetails()).containsOnly(MapEntry.entry("foo", "bar"));
+  }
+
+}
index 10a2e55b65fdd825a25a8b985543dfcb1a2fe303..ef446052923e08964c117d0df23f85453816eb46 100644 (file)
@@ -30,11 +30,10 @@ import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.utils.DateUtils;
-import org.sonar.core.activity.Activity;
+import org.sonar.server.es.SearchOptions;
 import org.sonar.server.qualityprofile.QProfileActivity;
 import org.sonar.server.qualityprofile.QProfileActivityQuery;
 import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.search.QueryContext;
 import org.sonar.server.search.Result;
 
 import java.util.Date;
@@ -53,7 +52,7 @@ public class RubyQProfileActivityServiceTest {
   ArgumentCaptor<QProfileActivityQuery> activityArgumentCaptor;
 
   @Captor
-  ArgumentCaptor<QueryContext> queryOptionsArgumentCaptor;
+  ArgumentCaptor<SearchOptions> queryOptionsArgumentCaptor;
 
   RubyQProfileActivityService rubyQProfileActivityService;
 
@@ -70,16 +69,16 @@ public class RubyQProfileActivityServiceTest {
     Result<QProfileActivity> result = mock(Result.class);
     when(result.getHits()).thenReturn(Lists.<QProfileActivity>newArrayList());
     when(result.getTotal()).thenReturn(10L);
-    when(service.searchActivities(any(QProfileActivityQuery.class), any(QueryContext.class))).thenReturn(result);
+    when(service.searchActivities(any(QProfileActivityQuery.class), any(SearchOptions.class))).thenReturn(result);
 
-    rubyQProfileActivityService.search(ImmutableMap.<String, Object>of("profileKeys", "PROFILE_KEY", "since", since, "to", to));
+    rubyQProfileActivityService.search(ImmutableMap.<String, Object>of("profileKey", "PROFILE_KEY", "since", since, "to", to));
 
     verify(service).searchActivities(activityArgumentCaptor.capture(), queryOptionsArgumentCaptor.capture());
 
     assertThat(queryOptionsArgumentCaptor.getValue().getLimit()).isEqualTo(50);
 
-    assertThat(activityArgumentCaptor.getValue().getQprofileKeys()).containsOnly("PROFILE_KEY");
-    assertThat(activityArgumentCaptor.getValue().getTypes()).containsOnly(Activity.Type.QPROFILE);
+    assertThat(activityArgumentCaptor.getValue().getQprofileKey()).isEqualTo("PROFILE_KEY");
+    assertThat(activityArgumentCaptor.getValue().getTypes()).containsOnly(Activity.Type.QPROFILE.name());
     assertThat(activityArgumentCaptor.getValue().getSince()).isEqualTo(since);
     assertThat(activityArgumentCaptor.getValue().getTo()).isEqualTo(to);
   }
@@ -89,7 +88,7 @@ public class RubyQProfileActivityServiceTest {
     Result<QProfileActivity> result = mock(Result.class);
     when(result.getHits()).thenReturn(Lists.<QProfileActivity>newArrayList());
     when(result.getTotal()).thenReturn(10L);
-    when(service.searchActivities(any(QProfileActivityQuery.class), any(QueryContext.class))).thenReturn(result);
+    when(service.searchActivities(any(QProfileActivityQuery.class), any(SearchOptions.class))).thenReturn(result);
 
     rubyQProfileActivityService.search(ImmutableMap.<String, Object>of());
 
@@ -97,7 +96,7 @@ public class RubyQProfileActivityServiceTest {
 
     assertThat(queryOptionsArgumentCaptor.getValue().getLimit()).isEqualTo(50);
 
-    assertThat(activityArgumentCaptor.getValue().getQprofileKeys()).isEmpty();
+    assertThat(activityArgumentCaptor.getValue().getQprofileKey()).isNull();
     assertThat(activityArgumentCaptor.getValue().getSince()).isNull();
     assertThat(activityArgumentCaptor.getValue().getTo()).isNull();
   }
index 1a2cd967c17c58e58f38e3dd742d5ae8cd05fb7f..9ebf997f9acbe001c9bd8f9918e6356f50d4e2fb 100644 (file)
  */
 package org.sonar.server.activity.db;
 
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import org.junit.After;
+import org.apache.commons.io.IOUtils;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
-import org.sonar.api.utils.KeyValueFormat;
+import org.junit.experimental.categories.Category;
 import org.sonar.api.utils.System2;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.activity.ActivityLog;
 import org.sonar.core.activity.db.ActivityDto;
-import org.sonar.core.persistence.AbstractDaoTestCase;
-import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.test.DbTests;
 
+import java.sql.Clob;
+import java.sql.Timestamp;
 import java.util.Map;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@Category(DbTests.class)
+public class ActivityDaoTest {
 
-public class ActivityDaoTest extends AbstractDaoTestCase {
+  @Rule
+  public DbTester dbTester = new DbTester();
 
-  private ActivityDao dao;
-  private DbSession session;
+  System2 system = mock(System2.class);
+  ActivityDao sut;
 
   @Before
   public void before() throws Exception {
-    this.session = getMyBatis().openSession(false);
-    this.dao = new ActivityDao(mock(System2.class));
-  }
-
-  @After
-  public void after() {
-    session.close();
-  }
-
-  @Test
-  public void fail_insert_missing_type() {
-    String testValue = "hello world";
-    ActivityDto log = ActivityDto.createFor(testValue);
-    try {
-      dao.insert(session, log);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).isEqualTo("Type must be set");
-    }
-  }
-
-
-  @Test
-  public void fail_get_by_key() {
-    try {
-      dao.getByKey(session, "hello world");
-    } catch (IllegalStateException e) {
-      assertThat(e.getMessage()).isEqualTo("Cannot execute getByKey on Activities in DB");
-    }
-  }
-
-
-  @Test
-  public void fail_insert_missing_author() {
-    String testValue = "hello world";
-    ActivityDto log = ActivityDto.createFor(testValue)
-      .setType(Activity.Type.QPROFILE);
-    try {
-      dao.insert(session, log);
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).isEqualTo("Type must be set");
-    }
-  }
-
-  @Test
-  public void insert_text_log() {
-    String testValue = "hello world";
-    ActivityDto log = ActivityDto.createFor(testValue)
-      .setType(Activity.Type.QPROFILE)
-      .setAuthor("jUnit");
-    dao.insert(session, log);
-
-    ActivityDto newDto = Iterables.getFirst(dao.findAll(session), null);
-    assertThat(newDto).isNotNull();
-    assertThat(newDto.getAuthor()).isEqualTo(log.getAuthor());
-    assertThat(newDto.getMessage()).isEqualTo(testValue);
+    sut = new ActivityDao(dbTester.myBatis(), system);
   }
 
   @Test
-  public void insert_loggable_log() {
-    final String testKey = "message";
-    final String testValue = "hello world";
-    ActivityDto log = ActivityDto.createFor(new ActivityLog() {
-
-      @Override
-      public Map<String, String> getDetails() {
-        return ImmutableMap.of(testKey, testValue);
-      }
-
-      @Override
-      public String getAction() {
-        return "myAction";
-      }
-    })
-      .setAuthor("jUnit")
-      .setType(Activity.Type.QPROFILE);
-
-    dao.insert(session, log);
-
-    ActivityDto newDto = Iterables.getFirst(dao.findAll(session), null);
-    assertThat(newDto).isNotNull();
-    assertThat(newDto.getAuthor()).isEqualTo(log.getAuthor());
-    assertThat(newDto.getData()).isNotNull();
-    Map<String, String> details = KeyValueFormat.parse(newDto.getData());
-    assertThat(details.get(testKey)).isEqualTo(testValue);
+  public void insert() throws Exception {
+    when(system.now()).thenReturn(1_500_000_000_000L);
+    ActivityDto dto = new ActivityDto()
+      .setKey("UUID_1").setAction("THE_ACTION").setType("THE_TYPE")
+      .setAuthor("THE_AUTHOR").setData("THE_DATA");
+    sut.insert(dto);
+
+    Map<String, Object> map = dbTester.selectFirst("select created_at as \"createdAt\", log_action as \"action\", data_field as \"data\" from activities where log_key='UUID_1'");
+    assertThat(map.get("action")).isEqualTo("THE_ACTION");
+    assertThat(((Timestamp)map.get("createdAt")).getTime()).isEqualTo(1_500_000_000_000L);
+    Clob data = (Clob) map.get("data");
+    assertThat(IOUtils.toString(data.getAsciiStream())).isEqualTo("THE_DATA");
   }
-
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexDefinitionTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexDefinitionTest.java
new file mode 100644 (file)
index 0000000..80ef2f2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.activity.index;
+
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.process.ProcessConstants;
+import org.sonar.server.es.IndexDefinition;
+import org.sonar.server.es.NewIndex;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ActivityIndexDefinitionTest {
+
+  IndexDefinition.IndexDefinitionContext context = new IndexDefinition.IndexDefinitionContext();
+
+  @Test
+  public void define() throws Exception {
+    ActivityIndexDefinition def = new ActivityIndexDefinition(new Settings());
+    def.define(context);
+
+    assertThat(context.getIndices()).hasSize(1);
+    NewIndex index = context.getIndices().get("activities");
+    assertThat(index).isNotNull();
+    assertThat(index.getTypes().keySet()).containsOnly("activity");
+
+    // no cluster by default
+    assertThat(index.getSettings().get("index.number_of_shards")).isEqualTo("1");
+    assertThat(index.getSettings().get("index.number_of_replicas")).isEqualTo("0");
+  }
+
+  @Test
+  public void enable_cluster() throws Exception {
+    Settings settings = new Settings();
+    settings.setProperty(ProcessConstants.CLUSTER_ACTIVATE, true);
+    ActivityIndexDefinition def = new ActivityIndexDefinition(settings);
+    def.define(context);
+
+    NewIndex index = context.getIndices().get("activities");
+    assertThat(index.getSettings().get("index.number_of_shards")).isEqualTo("4");
+    assertThat(index.getSettings().get("index.number_of_replicas")).isEqualTo("1");
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexTest.java
new file mode 100644 (file)
index 0000000..8ace24a
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.activity.index;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.server.activity.Activity;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.es.SearchResult;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ActivityIndexTest {
+
+  @ClassRule
+  public static EsTester es = new EsTester().addDefinitions(new ActivityIndexDefinition(new Settings()));
+
+  ActivityIndex sut;
+
+  @Before
+  public void before() {
+    sut = new ActivityIndex(es.client());
+  }
+
+  @Test
+  public void search_all() throws Exception {
+    es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L));
+
+    SearchResult<ActivityDoc> results = sut.search(new ActivityQuery(), new SearchOptions());
+    assertThat(results.getTotal()).isEqualTo(2L);
+    assertThat(results.getDocs()).hasSize(2);
+    assertThat(results.getDocs()).extracting("message").containsOnly("THE_MSG 1", "THE_MSG 2");
+  }
+
+  @Test
+  public void search_by_type() throws Exception {
+    es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L));
+
+    ActivityQuery query = new ActivityQuery();
+    query.setTypes(Arrays.asList("ANALYSIS_REPORT"));
+    assertThat(sut.search(query, new SearchOptions()).getTotal()).isEqualTo(2L);
+
+    query = new ActivityQuery();
+    query.setTypes(Arrays.asList("OTHER", "TYPES"));
+    assertThat(sut.search(query, new SearchOptions()).getTotal()).isEqualTo(0L);
+  }
+
+  @Test
+  public void search_by_data() throws Exception {
+    es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L));
+
+    ActivityQuery query = new ActivityQuery();
+    query.addDataOrFilter("foo", "bar2");
+    SearchResult<ActivityDoc> results = sut.search(query, new SearchOptions());
+    assertThat(results.getDocs()).hasSize(1);
+    assertThat(results.getDocs().get(0).getKey()).isEqualTo("UUID2");
+  }
+
+  @Test
+  public void search_by_date() throws Exception {
+    es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L));
+
+    ActivityQuery query = new ActivityQuery();
+    query.setSince(new Date(1_550_000_000_000L));
+    SearchResult<ActivityDoc> results = sut.search(query, new SearchOptions());
+    assertThat(results.getDocs()).hasSize(1);
+    assertThat(results.getDocs().get(0).getKey()).isEqualTo("UUID2");
+
+    query = new ActivityQuery();
+    query.setTo(new Date(1_550_000_000_000L));
+    results = sut.search(query, new SearchOptions());
+    assertThat(results.getDocs()).hasSize(1);
+    assertThat(results.getDocs().get(0).getKey()).isEqualTo("UUID1");
+  }
+
+  ActivityDoc newDoc(int id, long date) {
+    ActivityDoc doc = new ActivityDoc();
+    doc.setKey("UUID" + id);
+    doc.setType(Activity.Type.ANALYSIS_REPORT.name());
+    doc.setAction("THE_ACTION " + id);
+    doc.setMessage("THE_MSG " + id);
+    doc.setDetails(ImmutableMap.of("foo", "bar" + id));
+    doc.setLogin("THE_GUY " + id);
+    doc.setCreatedAt(new Date(date));
+    return doc;
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java
new file mode 100644 (file)
index 0000000..6a98606
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.activity.index;
+
+import org.apache.commons.dbutils.DbUtils;
+import org.assertj.core.data.MapEntry;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.db.DbClient;
+import org.sonar.test.DbTests;
+
+import java.sql.Connection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class ActivityResultSetIteratorTest {
+
+  @ClassRule
+  public static DbTester dbTester = new DbTester();
+
+  DbClient client;
+  Connection connection;
+
+  @Before
+  public void setUp() throws Exception {
+    dbTester.truncateTables();
+    client = new DbClient(dbTester.database(), dbTester.myBatis());
+    connection = dbTester.openConnection();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    DbUtils.closeQuietly(connection);
+  }
+
+  @Test
+  public void traverse() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "traverse.xml");
+    ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, 0L);
+    assertThat(it.hasNext()).isTrue();
+    ActivityDoc doc = it.next();
+    assertThat(doc).isNotNull();
+    assertThat(doc.getKey()).isEqualTo("UUID1");
+    assertThat(doc.getAction()).isEqualTo("THE_ACTION");
+    assertThat(doc.getMessage()).isEqualTo("THE_MSG");
+    assertThat(doc.getDetails()).containsOnly(MapEntry.entry("foo", "bar"));
+    assertThat(doc.getLogin()).isEqualTo("THE_AUTHOR");
+
+    assertThat(it.hasNext()).isTrue();
+    assertThat(it.next()).isNotNull();
+    assertThat(it.hasNext()).isFalse();
+    it.close();
+  }
+
+  @Test
+  public void traverse_after_date() throws Exception {
+    dbTester.prepareDbUnit(getClass(), "traverse.xml");
+    ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, DateUtils.parseDate("2014-12-01").getTime());
+
+    assertThat(it.hasNext()).isTrue();
+    ActivityDoc doc = it.next();
+    assertThat(doc).isNotNull();
+    assertThat(doc.getKey()).isEqualTo("UUID2");
+
+    assertThat(it.hasNext()).isFalse();
+    it.close();
+  }
+}
index 36920b2346182880441242edb3c0526d7dd33139..f5462dc69fa5c3dc6516eec412c47164b174b5eb 100644 (file)
  */
 package org.sonar.server.activity.ws;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.persistence.DbSession;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.ActivityService;
-import org.sonar.server.db.DbClient;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.user.MockUserSession;
 import org.sonar.server.ws.WsTester;
@@ -39,22 +36,14 @@ public class ActivitiesWebServiceMediumTest {
   @ClassRule
   public static ServerTester tester = new ServerTester();
 
-  private ActivitiesWebService ws;
-  private ActivityService service;
-  private DbSession session;
-
+  ActivitiesWebService ws;
+  ActivityService service;
 
   @Before
   public void setUp() throws Exception {
     tester.clearDbAndIndexes();
     ws = tester.get(ActivitiesWebService.class);
     service = tester.get(ActivityService.class);
-    session = tester.get(DbClient.class).openSession(false);
-  }
-
-  @After
-  public void after() {
-    session.close();
   }
 
   @Test
@@ -70,14 +59,20 @@ public class ActivitiesWebServiceMediumTest {
   }
 
   @Test
-  public void search_logs() throws Exception {
-    service.write(session, Activity.Type.QPROFILE, "Hello World");
-    session.commit();
+  public void search() throws Exception {
+    Activity activity = new Activity();
+    activity.setType(Activity.Type.ANALYSIS_REPORT);
+    activity.setAction("THE_ACTION");
+    activity.setMessage("THE_MSG");
+    activity.setData("foo", "bar");
+    service.save(activity);
 
     MockUserSession.set();
 
-    // 1. List single Text log
-    WsTester.TestRequest request = tester.wsTester().newGetRequest(ActivitiesWebService.API_ENDPOINT, SearchAction.SEARCH_ACTION);
+    WsTester.TestRequest request = tester.wsTester().newGetRequest("api/activities", "search");
     WsTester.Result result = request.execute();
+    assertThat(result.outputAsString()).contains("\"total\":1");
+    assertThat(result.outputAsString()).contains("\"type\":\"ANALYSIS_REPORT\"");
+    assertThat(result.outputAsString()).contains("\"details\":{\"foo\":\"bar\"}");
   }
 }
index 61d9cc7ab881399fa57cda8729e3927bb3c4fa07..26682ddb642cd8b0d8cc5a83653ce191aee2bf4b 100644 (file)
@@ -52,10 +52,9 @@ import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.batch.protocol.output.BatchOutputWriter;
 import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.core.activity.Activity;
 import org.sonar.core.computation.db.AnalysisReportDto;
-import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.DbTester;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.ActivityService;
 import org.sonar.server.component.db.ComponentDao;
 import org.sonar.server.computation.step.ComputationStep;
@@ -122,7 +121,7 @@ public class ComputationServiceTest {
     verify(projectStep1).execute(any(ComputationContext.class));
     verify(projectStep2).execute(any(ComputationContext.class));
     verify(viewStep, never()).execute(any(ComputationContext.class));
-    verify(activityService).write(any(DbSession.class), eq(Activity.Type.ANALYSIS_REPORT), any(ReportActivity.class));
+    verify(activityService).save(any(Activity.class));
   }
 
   @Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportActivityTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportActivityTest.java
deleted file mode 100644 (file)
index 82ca71f..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.computation;
-
-import org.junit.Test;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.core.component.ComponentDto;
-import org.sonar.core.computation.db.AnalysisReportDto;
-import org.sonar.server.component.ComponentTesting;
-
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.computation.db.AnalysisReportDto.Status.FAILED;
-
-public class ReportActivityTest {
-
-  @Test
-  public void insert_find_analysis_report_log() {
-    AnalysisReportDto report = AnalysisReportDto.newForTests(1L)
-      .setProjectKey("projectKey")
-      .setStatus(FAILED)
-      .setCreatedAt(DateUtils.parseDate("2014-10-15").getTime())
-      .setUpdatedAt(DateUtils.parseDate("2014-10-16").getTime())
-      .setStartedAt(DateUtils.parseDate("2014-10-17").getTime())
-      .setFinishedAt(DateUtils.parseDate("2014-10-18").getTime());
-    ComponentDto project = ComponentTesting.newProjectDto();
-
-    ReportActivity activity = new ReportActivity(report, project);
-
-    Map<String, String> details = activity.getDetails();
-    assertThat(details.get("key")).isEqualTo(String.valueOf(report.getId()));
-    assertThat(details.get("projectKey")).isEqualTo(project.key());
-    assertThat(details.get("projectName")).isEqualTo(project.name());
-    assertThat(details.get("projectUuid")).isEqualTo(project.uuid());
-    assertThat(details.get("status")).isEqualTo("FAILED");
-    assertThat(details.get("submittedAt")).isEqualTo("2014-10-15T00:00:00+0200");
-    assertThat(details.get("startedAt")).isEqualTo("2014-10-17T00:00:00+0200");
-    assertThat(details.get("finishedAt")).isEqualTo("2014-10-18T00:00:00+0200");
-  }
-
-}
index 6852087e43f79c938bbec36fd6a90ecc11b8ac97..26fe86462b339e55e0e67e02e227f8705761bf6f 100644 (file)
 
 package org.sonar.server.computation.ws;
 
-import org.apache.commons.io.IOUtils;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
-import org.sonar.api.security.DefaultGroups;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.component.ComponentDto;
 import org.sonar.core.computation.db.AnalysisReportDto;
 import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.core.permission.PermissionFacade;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.user.UserDto;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.ActivityService;
-import org.sonar.server.component.ComponentTesting;
-import org.sonar.server.computation.ReportActivity;
-import org.sonar.server.computation.ReportQueue;
-import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.user.MockUserSession;
 import org.sonar.server.ws.WsTester;
 
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import java.util.Date;
 
 /**
-* TODO replace this medium test by a small test
-*/
+ * TODO replace this medium test by a small test
+ */
 public class HistoryWsActionMediumTest {
-  private static final String DEFAULT_PROJECT_KEY = "DefaultProjectKey";
-  private static final String DEFAULT_PROJECT_NAME = "DefaultProjectName";
-  private static final String DEFAULT_REPORT_DATA = "default-project";
 
   @ClassRule
   public static ServerTester tester = new ServerTester();
 
-  private DbClient dbClient;
-  private DbSession session;
-  private WsTester wsTester;
-  private ReportQueue queue;
-  private MockUserSession userSession;
-  private ActivityService activityService;
+  HistoryWsAction sut;
+  ActivityService activityService;
 
   @Before
-  public void before() {
+  public void setUp() throws Exception {
     tester.clearDbAndIndexes();
-    dbClient = tester.get(DbClient.class);
-    wsTester = tester.get(WsTester.class);
-    session = dbClient.openSession(false);
-    queue = tester.get(ReportQueue.class);
+    sut = tester.get(HistoryWsAction.class);
     activityService = tester.get(ActivityService.class);
-
-    UserDto connectedUser = new UserDto().setLogin("gandalf").setName("Gandalf");
-    dbClient.userDao().insert(session, connectedUser);
-
-    userSession = MockUserSession.set()
-      .setLogin(connectedUser.getLogin())
-      .setUserId(connectedUser.getId().intValue())
-      .setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
-  }
-
-  @After
-  public void after() {
-    MyBatis.closeQuietly(session);
   }
 
   @Test
-  public void add_and_try_to_retrieve_activities() throws Exception {
-    insertPermissionsForProject(DEFAULT_PROJECT_KEY);
-    queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA));
-    queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA));
-    queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA));
-
-    List<AnalysisReportDto> reports = queue.all();
-    ComponentDto project = ComponentTesting.newProjectDto()
-      .setName(DEFAULT_PROJECT_NAME)
-      .setKey(DEFAULT_PROJECT_KEY);
-    for (AnalysisReportDto report : reports) {
-      report.succeed();
-      activityService.write(session, Activity.Type.ANALYSIS_REPORT, new ReportActivity(report, project));
-    }
-
-    session.commit();
-    userSession.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
-
-    WsTester.TestRequest request = wsTester.newGetRequest(ComputationWebService.API_ENDPOINT, "history");
-    WsTester.Result result = request.execute();
-
-    assertThat(result).isNotNull();
-    result.assertJson(getClass(), "list_history_reports.json", false);
-  }
-
-  private ComponentDto insertPermissionsForProject(String projectKey) {
-    ComponentDto project = new ComponentDto().setKey(projectKey).setId(1L);
-    dbClient.componentDao().insert(session, project);
-
-    tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session);
-    userSession.addProjectPermissions(UserRole.ADMIN, project.key());
-    userSession.addProjectPermissions(UserRole.USER, project.key());
-
-    session.commit();
-
-    return project;
+  public void search() throws Exception {
+    Activity activity1 = new Activity();
+    activity1.setType(Activity.Type.ANALYSIS_REPORT);
+    activity1.setAction("LOG_ANALYSIS_REPORT");
+    activity1.setData("projectKey", "P1");
+    activity1.setData("projectName", "POne");
+    activity1.setData("projectUuid", "U1");
+    activity1.setData("status", AnalysisReportDto.Status.SUCCESS);
+    activity1.setData("submittedAt", new Date());
+    activityService.save(activity1);
+
+    Activity activity2 = new Activity();
+    activity2.setType(Activity.Type.ANALYSIS_REPORT);
+    activity2.setAction("LOG_ANALYSIS_REPORT");
+    activity2.setData("projectKey", "P2");
+    activity2.setData("projectName", "PTwo");
+    activity2.setData("projectUuid", "U2");
+    activity2.setData("status", AnalysisReportDto.Status.FAILED);
+    activity2.setData("submittedAt", new Date());
+    activityService.save(activity2);
+
+    MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+
+    WsTester.TestRequest request = tester.wsTester().newGetRequest("api/computation", "history");
+    request.execute().assertJson(getClass(), "list_history_reports.json", false);
   }
 
   @Test(expected = ForbiddenException.class)
-  public void user_rights_is_not_enough_throw_ForbiddenException() throws Exception {
-    insertPermissionsForProject(DEFAULT_PROJECT_KEY);
-    queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA));
-
-    AnalysisReportDto report = queue.all().get(0);
-    report.succeed();
-    // queue.remove(report);
-    userSession.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+  public void requires_admin_right() throws Exception {
+    MockUserSession.set();
 
-    WsTester.TestRequest sut = wsTester.newGetRequest(ComputationWebService.API_ENDPOINT, "history");
-    sut.execute();
+    WsTester.TestRequest request = tester.wsTester().newGetRequest("api/computation", "history");
+    request.execute();
   }
 }
index 699581a22b64dcbcd0587a073b6ff270bbd0a919..ceb22da3b6aa408f65731ff7f941b5a20e992476 100644 (file)
@@ -23,31 +23,23 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
-import org.sonar.core.activity.db.ActivityDto;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.DbTester;
 import org.sonar.server.activity.db.ActivityDao;
 import org.sonar.server.db.DbClient;
 
-import java.util.List;
-
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-@RunWith(MockitoJUnitRunner.class)
 public class ChangeLogMigrationTest {
 
   @ClassRule
   public static DbTester db = new DbTester().schema(ChangeLogMigrationTest.class, "schema.sql");
 
-  @Mock
-  System2 system2;
-
+  System2 system2 = mock(System2.class);
   DbClient dbClient;
   ActivityDao dao;
   ChangeLogMigration migration;
@@ -56,7 +48,7 @@ public class ChangeLogMigrationTest {
   @Before
   public void setUp() throws Exception {
     when(system2.now()).thenReturn(DateUtils.parseDate("2014-03-13").getTime());
-    dao = new ActivityDao(system2);
+    dao = new ActivityDao(db.myBatis(), system2);
     dbClient = new DbClient(db.database(), db.myBatis(), dao);
     migration = new ChangeLogMigration(dao, dbClient);
     session = dbClient.openSession(false);
@@ -71,10 +63,10 @@ public class ChangeLogMigrationTest {
   public void migrate() throws Exception {
     db.prepareDbUnit(getClass(), "active_rules_changes.xml");
     migration.execute();
-    assertThat(dao.findAll(session)).hasSize(5);
+    assertThat(db.countRowsOfTable("activities")).isEqualTo(5);
 
-    List<ActivityDto> changes = dao.findAll(session);
-    assertThat(changes.get(1).getData()).contains("param_PARAM1=TODO");
+    int count = db.countSql("select count(*) from activities where data_field like '%param_PARAM1=TODO%'");
+    assertThat(count).isGreaterThan(0);
   }
 
   @Test
@@ -82,6 +74,6 @@ public class ChangeLogMigrationTest {
     db.prepareDbUnit(getClass(), "migrate_when_no_changelog.xml");
     migration.execute();
 
-    assertThat(dao.findAll(session)).isEmpty();
+    assertThat(db.countRowsOfTable("activities")).isEqualTo(0);
   }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeMediumTest.java
deleted file mode 100644 (file)
index c57d29f..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.qualityprofile;
-
-import com.google.common.collect.Iterables;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.core.qualityprofile.db.ActiveRuleKey;
-import org.sonar.server.activity.ActivityService;
-import org.sonar.server.activity.index.ActivityIndex;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.tester.ServerTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ActiveRuleChangeMediumTest {
-
-  @ClassRule
-  public static ServerTester tester = new ServerTester();
-
-  ActivityService service = tester.get(ActivityService.class);
-  ActivityIndex index = tester.get(ActivityIndex.class);
-  DbSession dbSession;
-
-  @Before
-  public void before() {
-    tester.clearDbAndIndexes();
-    dbSession = tester.get(DbClient.class).openSession(false);
-  }
-
-  @After
-  public void after() {
-    dbSession.close();
-  }
-
-  @Test
-  public void insert_find_active_rule_change() {
-    ActiveRuleKey key = ActiveRuleKey.of("XOO_P1", RuleKey.of("xoo", "X1"));
-    ActiveRuleChange change = ActiveRuleChange
-      .createFor(ActiveRuleChange.Type.ACTIVATED, key)
-      .setInheritance(ActiveRule.Inheritance.INHERITED)
-      .setSeverity("BLOCKER")
-      .setParameter("param1", "value1");
-
-    service.write(dbSession, Activity.Type.QPROFILE, change);
-    dbSession.commit();
-
-    // 0. AssertBase case
-    assertThat(index.findAll().getHits()).hasSize(1);
-
-    Activity activity = Iterables.getFirst(index.findAll().getHits(), null);
-    assertThat(activity).isNotNull();
-    assertThat(activity.details().get("key")).isEqualTo(key.toString());
-  }
-}
index 2015af9ab51571ae555a0aff9b8f511a5ecb4249..034bb052ef97fe1a7975af796abf5823dbadd780 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.qualityprofile;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import org.junit.After;
@@ -36,19 +35,19 @@ import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RulePriority;
 import org.sonar.api.utils.ValidationMessages;
-import org.sonar.core.activity.Activity;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.qualityprofile.db.ActiveRuleKey;
 import org.sonar.core.qualityprofile.db.QualityProfileDto;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.core.user.UserDto;
+import org.sonar.server.activity.Activity;
 import org.sonar.server.activity.ActivityService;
 import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
 import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
 import org.sonar.server.rule.RuleTesting;
 import org.sonar.server.search.FacetValue;
-import org.sonar.server.search.QueryContext;
 import org.sonar.server.search.Result;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.user.MockUserSession;
@@ -187,24 +186,23 @@ public class QProfileServiceMediumTest {
     // We need an actual rule in DB to test RuleName in Activity
     RuleDto rule = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1);
 
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
-        .setSeverity(Severity.MAJOR)
-        .setParameter("max", "10")
-      );
     dbSession.commit();
 
-    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext());
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
+      .setSeverity(Severity.MAJOR)
+      .setParameter("max", "10").toActivity());
+
+    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions());
     assertThat(activities.getHits()).hasSize(1);
 
     QProfileActivity activity = activities.getHits().get(0);
-    assertThat(activity.type()).isEqualTo(Activity.Type.QPROFILE);
-    assertThat(activity.action()).isEqualTo(ActiveRuleChange.Type.ACTIVATED.name());
+    assertThat(activity.getType()).isEqualTo(Activity.Type.QPROFILE.name());
+    assertThat(activity.getAction()).isEqualTo(ActiveRuleChange.Type.ACTIVATED.name());
     assertThat(activity.ruleKey()).isEqualTo(RuleTesting.XOO_X1);
     assertThat(activity.profileKey()).isEqualTo(XOO_P1_KEY);
     assertThat(activity.severity()).isEqualTo(Severity.MAJOR);
     assertThat(activity.ruleName()).isEqualTo(rule.getName());
-    assertThat(activity.login()).isEqualTo("david");
+    assertThat(activity.getLogin()).isEqualTo("david");
     assertThat(activity.authorName()).isEqualTo("David");
 
     assertThat(activity.parameters()).hasSize(1);
@@ -217,13 +215,11 @@ public class QProfileServiceMediumTest {
 
     RuleKey ruleKey = RuleKey.of("xoo", "deleted_rule");
 
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.UPDATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey))
-        .setParameter("max", "10")
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.UPDATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey))
+      .setParameter("max", "10").toActivity()
       );
-    dbSession.commit();
 
-    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext());
+    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions());
     assertThat(activities.getHits()).hasSize(1);
 
     QProfileActivity activity = activities.getHits().get(0);
@@ -238,19 +234,20 @@ public class QProfileServiceMediumTest {
 
     // We need an actual rule in DB to test RuleName in Activity
     db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1);
+    dbSession.commit();
 
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
+    tester.get(ActivityService.class).save(
       ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))
         .setSeverity(Severity.MAJOR)
         .setParameter("max", "10")
+        .toActivity()
       );
-    dbSession.commit();
 
-    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext());
+    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions());
     assertThat(activities.getHits()).hasSize(1);
 
     QProfileActivity activity = activities.getHits().get(0);
-    assertThat(activity.login()).isEqualTo("david");
+    assertThat(activity.getLogin()).isEqualTo("david");
     assertThat(activity.authorName()).isNull();
   }
 
@@ -260,14 +257,14 @@ public class QProfileServiceMediumTest {
 
     RuleKey ruleKey = RuleKey.of("xoo", "deleted_rule");
 
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey))
-        .setSeverity(Severity.MAJOR)
-        .setParameter("max", "10")
+    tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey))
+      .setSeverity(Severity.MAJOR)
+      .setParameter("max", "10")
+      .toActivity()
       );
     dbSession.commit();
 
-    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext());
+    Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions());
     assertThat(activities.getHits()).hasSize(1);
 
     QProfileActivity activity = activities.getHits().get(0);
@@ -278,48 +275,36 @@ public class QProfileServiceMediumTest {
   @Test
   public void search_activity_by_qprofile() throws InterruptedException {
 
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1)));
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P2_KEY, RuleTesting.XOO_X1)));
-    dbSession.commit();
+    tester.get(ActivityService.class).save(
+      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1)).toActivity());
+    tester.get(ActivityService.class).save(
+      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P2_KEY, RuleTesting.XOO_X1)).toActivity());
 
     // 0. Base case verify 2 activities in index
-    assertThat(service.searchActivities(new QProfileActivityQuery(), new QueryContext()).getHits())
+    assertThat(service.searchActivities(new QProfileActivityQuery(), new SearchOptions()).getHits())
       .hasSize(2);
 
     // 1. filter by QProfile
     List<QProfileActivity> result = service.searchActivities(new QProfileActivityQuery()
-      .setQprofileKeys(ImmutableSet.of(XOO_P1_KEY)), new QueryContext()).getHits();
+      .setQprofileKey(XOO_P1_KEY), new SearchOptions()).getHits();
     assertThat(result).hasSize(1);
-
-    // 1. filter by QProfiles
-    assertThat(service.searchActivities(new QProfileActivityQuery()
-      .setQprofileKeys(ImmutableSet.of(XOO_P1_KEY, XOO_P2_KEY))
-      , new QueryContext()).getHits()).hasSize(2);
   }
 
   @Test
   public void search_activity_by_qprofile_having_dashes_in_keys() throws InterruptedException {
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-default", RuleTesting.XOO_X1)));
-    tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE,
-      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-toto", RuleTesting.XOO_X1)));
-    dbSession.commit();
+    tester.get(ActivityService.class).save(
+      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-default", RuleTesting.XOO_X1)).toActivity());
+    tester.get(ActivityService.class).save(
+      ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-toto", RuleTesting.XOO_X1)).toActivity());
 
     // 0. Base case verify 2 activities in index
-    assertThat(service.searchActivities(new QProfileActivityQuery(), new QueryContext()).getHits())
+    assertThat(service.searchActivities(new QProfileActivityQuery(), new SearchOptions()).getHits())
       .hasSize(2);
 
     // 1. filter by QProfile
     List<QProfileActivity> result = service.searchActivities(new QProfileActivityQuery()
-      .setQprofileKeys(ImmutableSet.of("java-default")), new QueryContext()).getHits();
+      .setQprofileKey("java-default"), new SearchOptions()).getHits();
     assertThat(result).hasSize(1);
-
-    // 1. filter by QProfiles
-    assertThat(service.searchActivities(new QProfileActivityQuery()
-      .setQprofileKeys(ImmutableSet.of("java-default", "java-toto"))
-      , new QueryContext()).getHits()).hasSize(2);
   }
 
   @Test
index 15ce8518841f079422c732665140e8418d1b45b2..2b82eb2aaa98e3efa662018129fd5ef82a3adfa0 100644 (file)
@@ -34,9 +34,7 @@ import org.sonar.core.persistence.DbSession;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.core.rule.RuleDto.Format;
 import org.sonar.core.rule.RuleParamDto;
-import org.sonar.server.rule.RuleTesting;
 
-import java.util.Date;
 import java.util.List;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -368,31 +366,4 @@ public class RuleDaoTest extends AbstractDaoTestCase {
 
     checkTables("update_parameter", "rules_parameters");
   }
-
-  @Test
-  public void findAfterDate() throws Exception {
-    long t0 = DateUtils.parseDate("2014-01-01").getTime();
-    when(system2.now()).thenReturn(t0);
-    dao.insert(session, RuleTesting.newXooX1());
-    session.commit();
-    assertThat(dao.getByKey(session, RuleTesting.XOO_X1).getCreatedAt().after(new Date(t0)));
-
-
-    long t1 = DateUtils.parseDate("2014-02-01").getTime();
-    when(system2.now()).thenReturn(t1);
-    dao.insert(session, RuleTesting.newXooX2());
-    session.commit();
-    assertThat(dao.getByKey(session, RuleTesting.XOO_X2).getCreatedAt().after(new Date(t1)));
-
-    long t2 = DateUtils.parseDate("2014-03-01").getTime();
-    when(system2.now()).thenReturn(t2);
-    session.flushStatements();
-
-
-    assertThat(dao.findAll(session)).hasSize(2);
-    assertThat(dao.findAfterDate(session, new Date(0))).hasSize(2);
-    assertThat(dao.findAfterDate(session, new Date(t0))).hasSize(2);
-    assertThat(dao.findAfterDate(session, new Date(t1))).hasSize(1);
-    assertThat(dao.findAfterDate(session, new Date(t2))).hasSize(0);
-  }
 }
index 9eeecef377d2cce1db19fb2b1b303907936e3d91..006e153ef06af734bd6dd4a2a648eb2a7eb881c5 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.search;
 
 import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
-import org.elasticsearch.common.settings.ImmutableSettings;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
@@ -90,11 +89,6 @@ public class BaseIndexTest {
       IndexDefinition.TEST,
       null, searchClient) {
 
-      @Override
-      protected ImmutableSettings.Builder addCustomIndexSettings(ImmutableSettings.Builder baseIndexSettings) {
-        return baseIndexSettings.put("index.number_of_replicas", 22);
-      }
-
       @Override
       protected String getKeyValue(Serializable key) {
         return null;
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/activity/index/ActivityResultSetIteratorTest/traverse.xml b/server/sonar-server/src/test/resources/org/sonar/server/activity/index/ActivityResultSetIteratorTest/traverse.xml
new file mode 100644 (file)
index 0000000..f92f767
--- /dev/null
@@ -0,0 +1,7 @@
+<dataset>
+  <activities id="1" log_key="UUID1" log_type="ANALYSIS_REPORT" log_action="THE_ACTION" log_message="THE_MSG"
+              created_at="2014-01-01" data_field="foo=bar" user_login="THE_AUTHOR"/>
+
+  <activities id="2" log_key="UUID2" log_type="ANALYSIS_REPORT" log_action="THE_ACTION" log_message="THE_MSG"
+              created_at="2015-01-01" data_field="foo=bar" user_login="THE_AUTHOR"/>
+</dataset>
index b0ed60a0876192518ba951004a14ec8172ec74ac..caa180819a44246f2adae403d1ec7679de026983 100644 (file)
@@ -2,18 +2,15 @@
   "reports": [
     {
       "status": "SUCCESS",
-      "projectName": "DefaultProjectName",
-      "projectKey": "DefaultProjectKey"
+      "projectName": "POne",
+      "projectKey": "P1",
+      "projectUuid": "U1"
     },
     {
-      "status": "SUCCESS",
-      "projectName": "DefaultProjectName",
-      "projectKey": "DefaultProjectKey"
-    },
-    {
-      "status": "SUCCESS",
-      "projectName": "DefaultProjectName",
-      "projectKey": "DefaultProjectKey"
+      "status": "FAILED",
+      "projectName": "PTwo",
+      "projectKey": "P2",
+      "projectUuid": "U2"
     }
   ]
-}
\ No newline at end of file
+}
index 3ac6eba4b8d2683a00c5d33e01212df66faaab9f..2b107edfefb6838cf4a618763fb0044b05acb34d 100644 (file)
@@ -268,7 +268,7 @@ class ProfilesController < ApplicationController
     require_parameters 'key'
 
     @profile = Internal.qprofile_loader.getByKey(params[:key])
-    search = {'profileKeys' => @profile.key().to_s, 'since' => params[:since], 'to' => params[:to], 'p' => params[:p]}
+    search = {'profileKey' => @profile.key().to_s, 'since' => params[:since], 'to' => params[:to], 'p' => params[:p]}
     result = Internal.component(Java::OrgSonarServerActivity::RubyQProfileActivityService.java_class).search(search)
     @changes = result.activities
     @paging = result.paging
diff --git a/sonar-core/src/main/java/org/sonar/core/activity/Activity.java b/sonar-core/src/main/java/org/sonar/core/activity/Activity.java
deleted file mode 100644 (file)
index c5ede01..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.core.activity;
-
-import java.util.Date;
-import java.util.Map;
-
-/**
- * @since 4.4
- */
-public interface Activity {
-
-  Type type();
-
-  String action();
-
-  Date time();
-
-  String login();
-
-  Map<String, String> details();
-
-  String message();
-
-  public static enum Type {
-    NONE, QPROFILE, SERVER, ANALYSIS_REPORT
-  }
-
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/activity/ActivityLog.java b/sonar-core/src/main/java/org/sonar/core/activity/ActivityLog.java
deleted file mode 100644 (file)
index 9b0a7f5..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.core.activity;
-
-import java.util.Map;
-
-/**
- * @since 4.4
- */
-public interface ActivityLog {
-
-  Map<String, String> getDetails();
-
-  public String getAction();
-
-}
index 517f0e998a75b18e8e5c09aac76fe31c6bd7db85..298b88faef99509e1586936af525f40cfa21992d 100644 (file)
@@ -21,44 +21,42 @@ package org.sonar.core.activity.db;
 
 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.api.utils.internal.Uuids;
-import org.sonar.core.activity.Activity;
-import org.sonar.core.activity.ActivityLog;
-import org.sonar.core.persistence.Dto;
-
-/**
- * @since 4.4
- */
-public final class ActivityDto extends Dto<String> {
+
+import java.util.Date;
+
+public class ActivityDto {
 
   private String key;
   private String message;
-  private Activity.Type type;
+  private String type;
   private String action;
   private String author;
-
   private String data;
+  private Date createdAt;
 
-  protected ActivityDto() {
-    this.key = Uuids.create();
+  public ActivityDto setKey(String key) {
+    this.key = key;
+    return this;
   }
 
-  @Override
   public String getKey() {
     return key;
   }
 
-  @Override
-  public String toString() {
-    return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
+  public Date getCreatedAt() {
+    return createdAt;
+  }
+
+  public ActivityDto setCreatedAt(Date createdAt) {
+    this.createdAt = createdAt;
+    return this;
   }
 
-  public Activity.Type getType() {
+  public String getType() {
     return type;
   }
 
-  public ActivityDto setType(Activity.Type type) {
+  public ActivityDto setType(String type) {
     this.type = type;
     return this;
   }
@@ -99,21 +97,8 @@ public final class ActivityDto extends Dto<String> {
     return this;
   }
 
-  public static ActivityDto createFor(String message) {
-    return new ActivityDto()
-      .setMessage(message);
-  }
-
-  public static ActivityDto createFor(String action, String message) {
-    return new ActivityDto()
-      .setAction(action)
-      .setMessage(message);
-  }
-
-  public static ActivityDto createFor(ActivityLog activityLog) {
-    return new ActivityDto()
-      .setAction(activityLog.getAction())
-      .setData(KeyValueFormat.format(activityLog.getDetails()));
-
+  @Override
+  public String toString() {
+    return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
   }
 }
index d7f5959c42234756086507518b4405b0d5c4aa5b..175bef8bc30d494bad0d5d91b2a7fa16bec9c831 100644 (file)
  */
 package org.sonar.core.activity.db;
 
-import org.apache.ibatis.annotations.Param;
-
-import javax.annotation.Nullable;
-
-import java.sql.Timestamp;
-import java.util.List;
-
-/**
- * @since 4.4
- */
 public interface ActivityMapper {
 
-  void insert(ActivityDto rule);
-
-  List<ActivityDto> selectAll();
+  void insert(ActivityDto dto);
 
-  List<ActivityDto> selectAfterDate(@Nullable @Param("date") Timestamp timestamp);
 }
diff --git a/sonar-core/src/main/java/org/sonar/core/activity/package-info.java b/sonar-core/src/main/java/org/sonar/core/activity/package-info.java
deleted file mode 100644 (file)
index 8c6baa9..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
-
-@ParametersAreNonnullByDefault
-package org.sonar.core.activity;
-
-import javax.annotation.ParametersAreNonnullByDefault;
index fecd0b0210f58470ea63aa76d5740c803ec9d7cf..c3de19773b64c7751e3d91b561f498115840cc46 100644 (file)
@@ -3,40 +3,13 @@
 
 <mapper namespace="org.sonar.core.activity.db.ActivityMapper">
 
-  <insert id="insert" parameterType="Activity" useGeneratedKeys="false" >
+  <insert id="insert" parameterType="Activity" useGeneratedKeys="false">
     insert into activities
     (created_at, log_key, log_type, log_action, user_login, data_field, log_message)
-    values (#{createdAt}, #{key}, #{type}, #{action}, #{author}, #{data}, #{message})
+    values (#{createdAt,jdbcType=TIMESTAMP}, #{key,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR},
+    #{action,jdbcType=VARCHAR},
+    #{author,jdbcType=VARCHAR}, #{data,jdbcType=VARCHAR}, #{message,jdbcType=VARCHAR})
   </insert>
 
-  <select id="selectAll" parameterType="map" resultType="Activity" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
-    SELECT
-    created_at as "createdAt",
-    log_type as "type",
-    user_login as "author",
-    data_field as "data",
-    log_message as "message",
-    log_key as "key",
-    log_action as "action"
-    FROM activities
-  </select>
-
-
-  <select id="selectAfterDate" parameterType="map" resultType="Activity" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
-    SELECT
-    created_at as "createdAt",
-    log_type as "type",
-    user_login as "author",
-    data_field as "data",
-    log_message as "message",
-    log_key as "key",
-    log_action as "action"
-    FROM activities
-    <where>
-      <if test="date != null">
-        created_at &gt;= #{date}
-      </if>
-    </where>
-  </select>
 </mapper>