From e197f0547682ce01ce724229de6f79daa0860cb6 Mon Sep 17 00:00:00 2001 From: Stephane Gamard Date: Mon, 28 Apr 2014 11:39:30 +0200 Subject: [PATCH] SONAR-5237 - complete skeleton stack in rule2 package --- .../cluster/LocalNonBlockingWorkQueue.java | 73 ++++ .../org/sonar/server/cluster/WorkQueue.java | 8 +- .../java/org/sonar/server/db/BaseDao.java | 110 ++++++ .../main/java/org/sonar/server/db/Dao.java | 3 + .../org/sonar/server/rule2/RuleConstants.java | 7 + .../java/org/sonar/server/rule2/RuleDao.java | 26 ++ .../java/org/sonar/server/rule2/RuleDto.java | 344 ++++++++++++++++++ .../java/org/sonar/server/rule2/RuleImpl.java | 97 +++++ .../org/sonar/server/rule2/RuleIndex.java | 123 +++++++ .../org/sonar/server/rule2/RuleQuery.java | 5 + .../org/sonar/server/rule2/RuleService.java | 29 ++ .../org/sonar/server/search/BaseIndex.java | 182 +++++---- .../java/org/sonar/server/search/Index.java | 2 +- .../server/search/IndexSynchronizer.java | 28 +- .../LocalNonBlockingWorkQueueTest.java | 136 +++++++ .../RuleIndexTest.java} | 61 +--- .../sonar/server/search/BaseIndexTest.java | 132 +++++++ 17 files changed, 1243 insertions(+), 123 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java create mode 100644 sonar-server/src/main/java/org/sonar/server/db/BaseDao.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleConstants.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleDto.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleImpl.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java create mode 100644 sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java create mode 100644 sonar-server/src/test/java/org/sonar/server/cluster/LocalNonBlockingWorkQueueTest.java rename sonar-server/src/test/java/org/sonar/server/{es/BaseIndexTest.java => rule2/RuleIndexTest.java} (56%) create mode 100644 sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java diff --git a/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java b/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java new file mode 100644 index 00000000000..c3c53600c41 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/cluster/LocalNonBlockingWorkQueue.java @@ -0,0 +1,73 @@ +package org.sonar.server.cluster; + +import java.io.Serializable; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class LocalNonBlockingWorkQueue implements WorkQueue{ + + private final static int WORKQUEUE_INITIAL_CAPACITY = 20; + + private ConcurrentHashMap> index; + private ConcurrentHashMap> update; + private ConcurrentHashMap> delete; + + public LocalNonBlockingWorkQueue(){ + this.index = new ConcurrentHashMap>(WORKQUEUE_INITIAL_CAPACITY); + this.update = new ConcurrentHashMap>(WORKQUEUE_INITIAL_CAPACITY); + this.delete = new ConcurrentHashMap>(WORKQUEUE_INITIAL_CAPACITY); + } + + private Integer enqueue(Map> map, String indexName, Serializable key){ + if(!map.containsKey(indexName)){ + map.put(indexName, new ConcurrentLinkedQueue()); + } + map.get(indexName).offer(key); + return 0; + } + + private Object dequeue(Map> map, String indexName){ + return (map.containsKey(indexName))? + map.get(indexName).poll(): + null; + } + + @Override + public Integer enqueInsert(String indexName, Serializable key) { + return this.enqueue(index, indexName, key); + } + + @Override + public Integer enqueUpdate(String indexName, Serializable key) { + return this.enqueue(update, indexName, key); + } + + @Override + public Integer enqueDelete(String indexName, Serializable key) { + return this.enqueue(delete, indexName, key); + } + + @Override + public Object dequeInsert(String indexName) { + return this.dequeue(index, indexName); + } + + @Override + public Object dequeUpdate(String indexName) { + return this.dequeue(update, indexName); + } + + @Override + public Object dequeDelete(String indexName) { + return this.dequeue(delete, indexName); + } + + @Override + public Status getStatus(Integer workId) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/cluster/WorkQueue.java b/sonar-server/src/main/java/org/sonar/server/cluster/WorkQueue.java index 1b6a89117c9..79c3b520dc9 100644 --- a/sonar-server/src/main/java/org/sonar/server/cluster/WorkQueue.java +++ b/sonar-server/src/main/java/org/sonar/server/cluster/WorkQueue.java @@ -19,13 +19,15 @@ */ package org.sonar.server.cluster; +import java.io.Serializable; + public interface WorkQueue { - Integer enqueInsert(String indexName, Object key); + Integer enqueInsert(String indexName, Serializable key); - Integer enqueUpdate(String indexName, Object key); + Integer enqueUpdate(String indexName, Serializable key); - Integer enqueDelete(String indexName, Object key); + Integer enqueDelete(String indexName, Serializable key); Object dequeInsert(String indexName); diff --git a/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java b/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java new file mode 100644 index 00000000000..21a2270ef63 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/db/BaseDao.java @@ -0,0 +1,110 @@ +/* + * 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.db; + +import org.apache.ibatis.session.SqlSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.cluster.WorkQueue; + +import java.io.Serializable; + +public abstract class BaseDao, K extends Serializable> implements Dao { + + private MyBatis myBatis; + private WorkQueue workQueue; + + protected BaseDao(WorkQueue workQueue, MyBatis myBatis) { + this.myBatis = myBatis; + this.workQueue = workQueue; + } + + protected abstract String getIndexName(); + + protected void enqueInsert(K key) { + this.workQueue.enqueInsert(this.getIndexName(), key); + } + + protected void enqueUpdate(K key) { + this.workQueue.enqueUpdate(this.getIndexName(), key); + } + + protected void enqueDelete(K key) { + this.workQueue.enqueDelete(this.getIndexName(), key); + } + + protected MyBatis getMyBatis(){ + return this.myBatis; + } + + @Override + @SuppressWarnings("unchecked") + public E getByKey(K key) { + E item = null; + SqlSession session = getMyBatis().openSession(); + item = (E) session.getMapper(this.getClass()).getByKey(key); + MyBatis.closeQuietly(session); + return item; + } + + @Override + public E update(E item) { + SqlSession session = getMyBatis().openSession(); + E result = null; + try { + result = (E) session.getMapper(this.getClass()).update(item); + session.commit(); + } finally { + this.enqueUpdate(item.getKey()); + MyBatis.closeQuietly(session); + return result; + } + } + + @Override + public E insert(E item) { + SqlSession session = getMyBatis().openSession(); + E result = null; + try { + result = (E) session.getMapper(this.getClass()).insert(item); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + this.enqueInsert(item.getKey()); + return result; + } + } + + @Override + public void delete(E item) { + this.deleteByKey(item.getKey()); + } + + @Override + public void deleteByKey(K key) { + SqlSession session = getMyBatis().openSession(); + try { + session.getMapper(this.getClass()).deleteByKey(key); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + this.enqueDelete(key); + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/db/Dao.java b/sonar-server/src/main/java/org/sonar/server/db/Dao.java index 44fd54dd41a..e3fad2e59f5 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/Dao.java +++ b/sonar-server/src/main/java/org/sonar/server/db/Dao.java @@ -20,6 +20,7 @@ package org.sonar.server.db; import java.io.Serializable; +import java.util.Collection; public interface Dao, K extends Serializable> { @@ -33,4 +34,6 @@ public interface Dao, K extends Serializable> { public void deleteByKey(K key); + public Collection insertsSince(Long timestamp); + } diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleConstants.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleConstants.java new file mode 100644 index 00000000000..98abd6f7979 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleConstants.java @@ -0,0 +1,7 @@ +package org.sonar.server.rule2; + +public interface RuleConstants { + + public static final String INDEX_NAME = "rules"; + public static final String ES_TYPE = "rule"; +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java new file mode 100644 index 00000000000..3bc9875c398 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleDao.java @@ -0,0 +1,26 @@ +package org.sonar.server.rule2; + +import java.util.Collection; + +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.cluster.WorkQueue; +import org.sonar.api.rule.RuleKey; +import org.sonar.server.db.BaseDao; + +public class RuleDao extends BaseDao { + + protected RuleDao(WorkQueue workQueue, MyBatis myBatis) { + super(workQueue, myBatis); + } + + @Override + protected String getIndexName() { + return RuleConstants.INDEX_NAME; + } + + @Override + public Collection insertsSince(Long timestamp) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleDto.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleDto.java new file mode 100644 index 00000000000..621de3ae268 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleDto.java @@ -0,0 +1,344 @@ +/* + * 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.rule2; + +import org.sonar.api.rule.RuleKey; + +import org.sonar.core.rule.SeverityUtil; +import org.sonar.server.db.Dto; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.check.Cardinality; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import java.util.Date; + +public final class RuleDto implements Dto { + + public static final Integer DISABLED_CHARACTERISTIC_ID = -1; + + private String repositoryKey; + private String ruleKey; + private String description; + private String status; + private String name; + private String configKey; + private Integer severity; + private Cardinality cardinality; + private String language; + private Integer parentId; + private String noteData; + private String noteUserLogin; + private Date noteCreatedAt; + private Date noteUpdatedAt; + private Integer subCharacteristicId; + private Integer defaultSubCharacteristicId; + private String remediationFunction; + private String defaultRemediationFunction; + private String remediationCoefficient; + private String defaultRemediationCoefficient; + private String remediationOffset; + private String defaultRemediationOffset; + private String effortToFixDescription; + private Date createdAt; + private Date updatedAt; + + @Override + public RuleKey getKey() { + return RuleKey.of(this.getRepositoryKey(), this.getRuleKey()); + } + + public String getRepositoryKey() { + return repositoryKey; + } + + public RuleDto setRepositoryKey(String repositoryKey) { + this.repositoryKey = repositoryKey; + return this; + } + + public String getRuleKey() { + return ruleKey; + } + + public RuleDto setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + return this; + } + + public String getDescription() { + return description; + } + + public RuleDto setDescription(String description) { + this.description = description; + return this; + } + + public String getStatus() { + return status; + } + + public RuleDto setStatus(String status) { + this.status = status; + return this; + } + + public String getName() { + return name; + } + + public RuleDto setName(String name) { + this.name = name; + return this; + } + + public String getConfigKey() { + return configKey; + } + + public RuleDto setConfigKey(String configKey) { + this.configKey = configKey; + return this; + } + + public Integer getSeverity() { + return severity; + } + + public String getSeverityString() { + return SeverityUtil.getSeverityFromOrdinal(severity); + } + + public RuleDto setSeverity(String severity) { + this.severity = SeverityUtil.getOrdinalFromSeverity(severity); + return this; + } + + public RuleDto setSeverity(Integer severity) { + this.severity = severity; + return this; + } + + + public Cardinality getCardinality() { + return cardinality; + } + + public RuleDto setCardinality(Cardinality cardinality) { + this.cardinality = cardinality; + return this; + } + + public String getLanguage() { + return language; + } + + public RuleDto setLanguage(String language) { + this.language = language; + return this; + } + + @CheckForNull + public Integer getParentId() { + return parentId; + } + + public RuleDto setParentId(@Nullable Integer parentId) { + this.parentId = parentId; + return this; + } + + public String getNoteData() { + return noteData; + } + + public RuleDto setNoteData(String noteData) { + this.noteData = noteData; + return this; + } + + public String getNoteUserLogin() { + return noteUserLogin; + } + + public RuleDto setNoteUserLogin(String noteUserLogin) { + this.noteUserLogin = noteUserLogin; + return this; + } + + public Date getNoteCreatedAt() { + return noteCreatedAt; + } + + public RuleDto setNoteCreatedAt(Date noteCreatedAt) { + this.noteCreatedAt = noteCreatedAt; + return this; + } + + public Date getNoteUpdatedAt() { + return noteUpdatedAt; + } + + public RuleDto setNoteUpdatedAt(Date noteUpdatedAt) { + this.noteUpdatedAt = noteUpdatedAt; + return this; + } + + @CheckForNull + public Integer getSubCharacteristicId() { + return subCharacteristicId; + } + + public RuleDto setSubCharacteristicId(@Nullable Integer subCharacteristicId) { + this.subCharacteristicId = subCharacteristicId; + return this; + } + + @CheckForNull + public Integer getDefaultSubCharacteristicId() { + return defaultSubCharacteristicId; + } + + public RuleDto setDefaultSubCharacteristicId(@Nullable Integer defaultSubCharacteristicId) { + this.defaultSubCharacteristicId = defaultSubCharacteristicId; + return this; + } + + @CheckForNull + public String getRemediationFunction() { + return remediationFunction; + } + + public RuleDto setRemediationFunction(@Nullable String remediationFunction) { + this.remediationFunction = remediationFunction; + return this; + } + + @CheckForNull + public String getDefaultRemediationFunction() { + return defaultRemediationFunction; + } + + public RuleDto setDefaultRemediationFunction(@Nullable String defaultRemediationFunction) { + this.defaultRemediationFunction = defaultRemediationFunction; + return this; + } + + @CheckForNull + public String getRemediationCoefficient() { + return remediationCoefficient; + } + + public RuleDto setRemediationCoefficient(@Nullable String remediationCoefficient) { + this.remediationCoefficient = remediationCoefficient; + return this; + } + + @CheckForNull + public String getDefaultRemediationCoefficient() { + return defaultRemediationCoefficient; + } + + public RuleDto setDefaultRemediationCoefficient(@Nullable String defaultRemediationCoefficient) { + this.defaultRemediationCoefficient = defaultRemediationCoefficient; + return this; + } + + @CheckForNull + public String getRemediationOffset() { + return remediationOffset; + } + + public RuleDto setRemediationOffset(@Nullable String remediationOffset) { + this.remediationOffset = remediationOffset; + return this; + } + + @CheckForNull + public String getDefaultRemediationOffset() { + return defaultRemediationOffset; + } + + public RuleDto setDefaultRemediationOffset(@Nullable String defaultRemediationOffset) { + this.defaultRemediationOffset = defaultRemediationOffset; + return this; + } + + @CheckForNull + public String getEffortToFixDescription() { + return effortToFixDescription; + } + + public RuleDto setEffortToFixDescription(@Nullable String effortToFixDescription) { + this.effortToFixDescription = effortToFixDescription; + return this; + } + + public Date getCreatedAt() { + return createdAt; + } + + public RuleDto setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + return this; + } + + public Date getUpdatedAt() { + return updatedAt; + } + + public RuleDto setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + return this; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RuleDto)) { + return false; + } + if (this == obj) { + return true; + } + RuleDto other = (RuleDto) obj; + return new EqualsBuilder() + .append(repositoryKey, other.getRepositoryKey()) + .append(ruleKey, other.getRuleKey()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(repositoryKey) + .append(ruleKey) + .toHashCode(); + } + + @Override + public String toString() { + return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString(); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleImpl.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleImpl.java new file mode 100644 index 00000000000..89ccff40a50 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleImpl.java @@ -0,0 +1,97 @@ +package org.sonar.server.rule2; + +import org.sonar.api.rule.Severity; +import org.sonar.api.server.debt.DebtRemediationFunction; + +import java.util.Date; +import java.util.List; + +import org.sonar.api.rule.RuleKey; + +public class RuleImpl implements Rule { + + @Override + public RuleKey key() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String language() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String name() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String description() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Severity severity() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String status() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean template() { + // TODO Auto-generated method stub + return false; + } + + @Override + public List tags() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List params() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String debtCharacteristicKey() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String debtSubCharacteristicKey() { + // TODO Auto-generated method stub + return null; + } + + @Override + public DebtRemediationFunction debtRemediationFunction() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Date createdAt() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Date updatedAt() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java new file mode 100644 index 00000000000..d7bda890e89 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleIndex.java @@ -0,0 +1,123 @@ +package org.sonar.server.rule2; + +import org.elasticsearch.common.settings.ImmutableSettings; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.sonar.api.rule.RuleKey; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.profiling.Profiling; +import org.sonar.server.cluster.WorkQueue; +import org.sonar.server.search.BaseIndex; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; + + +public class RuleIndex extends BaseIndex { + + private static final Logger LOG = LoggerFactory.getLogger(RuleIndex.class); + + public RuleIndex(WorkQueue workQueue, RuleDao dao, Profiling profiling) { + super(workQueue, dao, profiling); + } + + @Override + public String getIndexName() { + return RuleConstants.INDEX_NAME; + } + + @Override + protected String getType() { + return RuleConstants.ES_TYPE; + } + + protected QueryBuilder getKeyQuery(RuleKey key){ + return QueryBuilders.boolQuery() + .must(QueryBuilders.termQuery("repositoryKey", key.repository())) + .must(QueryBuilders.termQuery("ruleKey", key.rule())); + } + + @Override + protected Settings getIndexSettings() { + try { + return ImmutableSettings.builder() + .loadFromSource( + jsonBuilder().startObject() + .startObject("index") + .field("number_of_replicas", 0) + .field("number_of_shards",4) + .startObject("mapper") + .field("dynamic", true) + .endObject() + .startObject("analysis") + .startObject("analyzer") + .startObject("path_analyzer") + .field("type", "custom") + .field("tokenizer", "path_hierarchy") + .endObject() + .endObject() + .endObject() + .endObject().toString()) + .build(); + } catch (IOException e) { + LOG.error("Could not create index settings for {}",this.getIndexName()); + return ImmutableSettings.builder().build(); + } + } + + @Override + protected XContentBuilder getMapping() { + try { + return jsonBuilder().startObject() + .startObject("issue") + .startObject("properties") + .startObject("component.path") + .field("type", "string") + .field("index_analyzer", "path_analyzer") + .field("search_analyzer", "keyword") + .endObject() + .startObject("rule.name") + .field("type", "string") + .field("analyzer", "keyword") + .endObject() + .startObject("root.id") + .field("type", "multi_field") + .startObject("fields") + .startObject("str") + .field("type", "string") + .field("index","analyzed") + .field("analyzer", "default") + .endObject() + .startObject("num") + .field("type", "long") + .field("index","analyzed") + .endObject() + .endObject() + .endObject() + .endObject().endObject(); + } catch (IOException e) { + LOG.error("Could not create mapping for {}",this.getIndexName()); + return null; + } + } + + @Override + public Map normalize(RuleKey key) { + //Use a MyBatis to normalize the Rule form multiple Table + return null; + } + + @Override + public Collection synchronizeSince(Long date) { + //Use MyBatis to get the RuleKey created since date X + return null; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java new file mode 100644 index 00000000000..2a7cc349988 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleQuery.java @@ -0,0 +1,5 @@ +package org.sonar.server.rule2; + +public class RuleQuery { + +} diff --git a/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java b/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java new file mode 100644 index 00000000000..1260fe98638 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/rule2/RuleService.java @@ -0,0 +1,29 @@ +package org.sonar.server.rule2; + +import org.sonar.server.search.Hit; + +import java.util.Collection; +import java.util.Collections; + +public class RuleService { + + private RuleDao dao; + private RuleIndex index; + + public RuleService(RuleDao dao, RuleIndex index){ + this.dao = dao; + this.index = index; + } + + public Collection search(RuleQuery query){ + return Collections.emptyList(); + } + + public static Rule toRule(RuleDto ruleDto){ + return new RuleImpl(); + } + + public static Rule toRule(Hit hit){ + return new RuleImpl(); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java b/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java index e3716781f89..9eaf598196e 100644 --- a/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java +++ b/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java @@ -20,29 +20,30 @@ package org.sonar.server.search; import org.elasticsearch.action.admin.cluster.stats.ClusterStatsNodes; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; +import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.query.QueryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.core.persistence.MyBatis; import org.sonar.core.profiling.Profiling; import org.sonar.core.profiling.Profiling.Level; import org.sonar.core.profiling.StopWatch; import org.sonar.server.cluster.WorkQueue; +import org.sonar.server.db.Dao; import java.io.Serializable; import java.util.Collection; -import java.util.Collections; -import java.util.Map; -public abstract class BaseIndex implements Index{ - - private static final String ES_EXECUTE_FAILED = "Failed execution of {}. Root is {}"; - - private static final String BULK_EXECUTE_FAILED = "Execution of bulk operation failed"; - private static final String BULK_INTERRUPTED = "Interrupted during bulk operation"; +public abstract class BaseIndex implements Index { private static final String PROFILE_DOMAIN = "es"; private static final Logger LOG = LoggerFactory.getLogger(BaseIndex.class); @@ -55,17 +56,51 @@ public abstract class BaseIndex implements Index{ private final Profiling profiling; private Client client; private WorkQueue workQueue; - private IndexSynchronizer synchronizer; + private IndexSynchronizer synchronizer; + protected Dao dao; - public BaseIndex(WorkQueue workQueue, Profiling profiling) { + public BaseIndex(WorkQueue workQueue, Dao dao, Profiling profiling) { this.profiling = profiling; this.workQueue = workQueue; this.synchronizer = IndexSynchronizer.getOnetimeSynchronizer(this, this.workQueue); + this.dao = dao; + } + + protected Dao getDao(){ + return this.dao; + } + + protected Client getClient(){ + return this.client; } + /* Component Methods */ + @Override public void start() { + /* Connect to the local ES Cluster */ + this.connect(); + + /* Setup the index if necessary */ + this.intializeIndex(); + + /* Launch synchronization */ + synchronizer.start(); + } + + @Override + public void stop() { + if (client != null) { + client.close(); + } + } + + private StopWatch createWatch() { + return profiling.start(PROFILE_DOMAIN, Level.FULL); + } + + public void connect(){ /* Settings to access our local ES node */ Settings settings = ImmutableSettings.settingsBuilder() .put("client.transport.sniff", true) @@ -76,30 +111,35 @@ public abstract class BaseIndex implements Index{ this.client = new TransportClient(settings) .addTransportAddress(new InetSocketTransportAddress(LOCAL_ES_NODE_HOST, LOCAL_ES_NODE_PORT)); - /* Cannot do that yet, need version >= 1.0 - ImmutableList nodes = client.connectedNodes(); - if (nodes.isEmpty()) { - throw new ElasticSearchUnavailableException("No nodes available. Verify ES is running!"); - } else { - log.info("connected to nodes: " + nodes.toString()); - } - */ - - /* Launch synchronization */ - synchronizer.start(); + /* + * Cannot do that yet, need version >= 1.0 + * ImmutableList nodes = client.connectedNodes(); + * if (nodes.isEmpty()) { + * throw new ElasticSearchUnavailableException("No nodes available. Verify ES is running!"); + * } else { + * log.info("connected to nodes: " + nodes.toString()); + * } + */ } - @Override - public void stop() { - if (client != null) { - client.close(); + /* Cluster And ES Stats/Client methods */ + + private void intializeIndex() { + + String index = this.getIndexName(); + + IndicesExistsResponse indexExistsResponse = client.admin().indices() + .prepareExists(index).execute().actionGet(); + + if (!indexExistsResponse.isExists()) { + + client.admin().indices().prepareCreate(index) + .setSettings(getIndexSettings()) + .addMapping(getType(), getMapping()) + .execute().actionGet(); } } - public Collection synchronizeSince(Long date) { - // TODO Auto-generated method stub - return Collections.EMPTY_LIST; - } public ClusterStatsNodes getNodesStats() { StopWatch watch = createWatch(); @@ -110,71 +150,87 @@ public abstract class BaseIndex implements Index{ } } - private StopWatch createWatch() { - return profiling.start(PROFILE_DOMAIN, Level.FULL); - } + + /* Index management and Tx methods */ + + protected abstract Settings getIndexSettings(); + + protected abstract String getType(); + + protected abstract XContentBuilder getMapping(); + + public abstract Collection synchronizeSince(Long date); + + + /* Base CRUD methods */ + + protected abstract QueryBuilder getKeyQuery(K key); @Override public Hit getByKey(K key) { - // TODO Auto-generated method stub + getClient().prepareSearch(this.getIndexName()) + .setQuery(getKeyQuery(key)) + .get(); return null; } @Override public void insert(K key) { - // TODO Auto-generated method stub - + this.update(key); } @Override - public void udpate(K key) { - // TODO Auto-generated method stub + public void update(K key) { + IndexResponse result = getClient().index(new IndexRequest() + .type(this.getType()) + .index(this.getIndexName()) + .source(this.normalize(key))).actionGet(); } @Override public void delete(K key) { - // TODO Auto-generated method stub - + DeleteByQueryResponse result = getClient().prepareDeleteByQuery(this.getIndexName()) + .setQuery(getKeyQuery(key)).get(); } - @Override - public K dequeueInsert() { - // TODO Auto-generated method stub - return null; - } + /* Synchronization methods */ - @Override - public K dequeueUpdate() { - // TODO Auto-generated method stub - return null; - } + Long lastSynch = 0l; + long cooldown = 30000; @Override - public K dequeueDelete() { - // TODO Auto-generated method stub - return null; + public void setLastSynchronization(Long time) { + if(time > (getLastSynchronization() + cooldown)){ + LOG.trace("Updating synchTime updating"); + lastSynch = time; + } else { + LOG.trace("Not updating synchTime, still cooling down"); + } + } @Override - public abstract Map normalize(K key); - + public Long getLastSynchronization() { + // need to read that in the admin index; + return 0l; + } @Override - public String getIndexName() { - // TODO Auto-generated method stub - return null; + @SuppressWarnings("unchecked") + public K dequeueInsert() { + return (K) this.workQueue.dequeUpdate(this.getIndexName()); } @Override - public void setLastSynchronization(Long time) { - // TODO Auto-generated method stub - + @SuppressWarnings("unchecked") + public K dequeueUpdate() { + return (K) this.workQueue.dequeUpdate(this.getIndexName()); } @Override - public Long getLastSynchronization() { - // TODO Auto-generated method stub - return null; + @SuppressWarnings("unchecked") + public K dequeueDelete() { + return (K) this.workQueue.dequeDelete(this.getIndexName()); } } diff --git a/sonar-server/src/main/java/org/sonar/server/search/Index.java b/sonar-server/src/main/java/org/sonar/server/search/Index.java index 4505806039c..25c91080bc1 100644 --- a/sonar-server/src/main/java/org/sonar/server/search/Index.java +++ b/sonar-server/src/main/java/org/sonar/server/search/Index.java @@ -34,7 +34,7 @@ public interface Index extends Startable { void insert(K key); - void udpate(K key); + void update(K key); void delete(K key); diff --git a/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java b/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java index a7a19d08577..0547675b535 100644 --- a/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java +++ b/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java @@ -23,9 +23,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.server.cluster.WorkQueue; -import java.util.Date; +import java.io.Serializable; -public class IndexSynchronizer { +public class IndexSynchronizer { private static final Logger LOG = LoggerFactory.getLogger(IndexSynchronizer.class); @@ -34,49 +34,49 @@ public class IndexSynchronizer { private long wait = 0; private boolean continuous; - private final Index index; + private final Index index; private final WorkQueue workQueue; - public static IndexSynchronizer getContinuousSynchronizer(Index index, WorkQueue workQueue) { - return new IndexSynchronizer(index, workQueue) + public static IndexSynchronizer getContinuousSynchronizer(Index index, WorkQueue workQueue) { + return new IndexSynchronizer(index, workQueue) .setContinuous(true) .setWait(DEFAULT_WAIT_TIME); } - public static IndexSynchronizer getContinuousSynchronizer(Index index, WorkQueue workQueue, Long wait) { - return new IndexSynchronizer(index, workQueue) + public static IndexSynchronizer getContinuousSynchronizer(Index index, WorkQueue workQueue, Long wait) { + return new IndexSynchronizer(index, workQueue) .setContinuous(true) .setWait(wait); } - public static IndexSynchronizer getOnetimeSynchronizer(Index index, WorkQueue workQueue) { - return new IndexSynchronizer(index, workQueue) + public static IndexSynchronizer getOnetimeSynchronizer(Index index, WorkQueue workQueue) { + return new IndexSynchronizer(index, workQueue) .setContinuous(false); } - private IndexSynchronizer(Index index, WorkQueue workQueue) { + private IndexSynchronizer(Index index, WorkQueue workQueue) { this.index = index; this.workQueue = workQueue; } - private IndexSynchronizer setWait(Long wait) { + private IndexSynchronizer setWait(Long wait) { this.wait = wait; return this; } - private IndexSynchronizer setContinuous(Boolean continuous) { + private IndexSynchronizer setContinuous(Boolean continuous) { this.continuous = continuous; return this; } - public IndexSynchronizer start() { + public IndexSynchronizer start() { LOG.info("Starting synchronization thread for ", index.getClass().getSimpleName()); Long since = index.getLastSynchronization(); index.setLastSynchronization(System.currentTimeMillis()); - for (Object key : index.synchronizeSince(since)) { + for (K key : index.synchronizeSince(since)) { if (LOG.isTraceEnabled()) { LOG.trace("Adding {} to workQueue for {}", key, index.getClass().getSimpleName()); } diff --git a/sonar-server/src/test/java/org/sonar/server/cluster/LocalNonBlockingWorkQueueTest.java b/sonar-server/src/test/java/org/sonar/server/cluster/LocalNonBlockingWorkQueueTest.java new file mode 100644 index 00000000000..117db387a9a --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/cluster/LocalNonBlockingWorkQueueTest.java @@ -0,0 +1,136 @@ +/* + * 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.cluster; + +import org.junit.Test; + +import java.io.Serializable; +import java.util.Map; + +import static org.fest.assertions.Assertions.assertThat; + + +public class LocalNonBlockingWorkQueueTest { + + private static final String WORKING_INDEX = "working_index"; + private static final String NON_WORKING_INDEX = "non_working_index"; + + @Test + public void test_insert_queue(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + + assertThat(queue.dequeInsert(WORKING_INDEX)).isNull(); + assertThat(queue.dequeInsert(NON_WORKING_INDEX)).isNull(); + + queue.enqueInsert(WORKING_INDEX, new Integer(0)); + assertThat(queue.dequeInsert(NON_WORKING_INDEX)).isNull(); + + Object dequeued = queue.dequeInsert(WORKING_INDEX); + assertThat(queue.dequeInsert(NON_WORKING_INDEX)).isNull(); + assertThat(queue.dequeInsert(WORKING_INDEX)).isNull(); + + assertThat(dequeued).isEqualTo(new Integer(0)); + } + + @Test + public void test_update_queue(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + + assertThat(queue.dequeUpdate(WORKING_INDEX)).isNull(); + assertThat(queue.dequeUpdate(NON_WORKING_INDEX)).isNull(); + + queue.enqueUpdate(WORKING_INDEX, new Integer(0)); + assertThat(queue.dequeUpdate(NON_WORKING_INDEX)).isNull(); + + Object dequeued = queue.dequeUpdate(WORKING_INDEX); + assertThat(queue.dequeUpdate(NON_WORKING_INDEX)).isNull(); + assertThat(queue.dequeUpdate(WORKING_INDEX)).isNull(); + + assertThat(dequeued).isEqualTo(new Integer(0)); + } + + @Test + public void test_delete_queue(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + + assertThat(queue.dequeDelete(WORKING_INDEX)).isNull(); + assertThat(queue.dequeDelete(NON_WORKING_INDEX)).isNull(); + + queue.enqueDelete(WORKING_INDEX, new Integer(0)); + assertThat(queue.dequeDelete(NON_WORKING_INDEX)).isNull(); + + Object dequeued = queue.dequeDelete(WORKING_INDEX); + assertThat(queue.dequeDelete(NON_WORKING_INDEX)).isNull(); + assertThat(queue.dequeDelete(WORKING_INDEX)).isNull(); + + assertThat(dequeued).isEqualTo(new Integer(0)); + } + + @Test + public void test_enque_seralizable_object(){ + + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + + class NonSerializable implements Serializable{ + private Object var1; + private Map objs; + } + + NonSerializable nonSer = new NonSerializable(); + assertThat(queue.enqueInsert(WORKING_INDEX, nonSer)).isNotNull(); + + Object dequeued = queue.dequeInsert(WORKING_INDEX); + assertThat(queue.dequeInsert(NON_WORKING_INDEX)).isNull(); + + assertThat(dequeued).isNotNull(); + assertThat(dequeued.getClass()).isEqualTo(NonSerializable.class); + } + + @Test + public void test_under_queue_capacity(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + + for(int i = 0; i < 10; i++){ + assertThat(queue.enqueDelete(WORKING_INDEX, i)).isNotNull(); + } + + for(int i = 0; i < 10; i++){ + assertThat(queue.dequeDelete(WORKING_INDEX)).isNotNull(); + } + assertThat(queue.dequeDelete(WORKING_INDEX)).isNull(); + + } + + @Test + public void test_over_queue_capacity(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + + for(int i = 0; i < 100; i++){ + assertThat(queue.enqueDelete(WORKING_INDEX, i)).isNotNull(); + } + + for(int i = 0; i < 100; i++){ + assertThat(queue.dequeDelete(WORKING_INDEX)).isNotNull(); + } + assertThat(queue.dequeDelete(WORKING_INDEX)).isNull(); + + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/es/BaseIndexTest.java b/sonar-server/src/test/java/org/sonar/server/rule2/RuleIndexTest.java similarity index 56% rename from sonar-server/src/test/java/org/sonar/server/es/BaseIndexTest.java rename to sonar-server/src/test/java/org/sonar/server/rule2/RuleIndexTest.java index 900efa8d6d3..a1e21f995b8 100644 --- a/sonar-server/src/test/java/org/sonar/server/es/BaseIndexTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule2/RuleIndexTest.java @@ -17,51 +17,43 @@ * 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.es; +package org.sonar.server.rule2; -import java.io.Serializable; -import java.util.Map; +import org.sonar.server.cluster.LocalNonBlockingWorkQueue; -import org.elasticsearch.client.transport.NoNodeAvailableException; -import org.elasticsearch.common.settings.ImmutableSettings; +import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchNode; +import com.github.tlrx.elasticsearch.test.support.junit.runners.ElasticsearchRunner; import org.elasticsearch.node.Node; -import org.elasticsearch.node.NodeBuilder; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; +import org.junit.runner.RunWith; import org.sonar.api.config.Settings; import org.sonar.core.profiling.Profiling; import org.sonar.server.search.BaseIndex; import static org.fest.assertions.Assertions.assertThat; -@Ignore -public class BaseIndexTest { +@RunWith(ElasticsearchRunner.class) +public class RuleIndexTest { private static final String TEST_NODE_NAME = "es_node_for_tests"; - private BaseIndex searchIndex; + @ElasticsearchNode(name = TEST_NODE_NAME, + clusterName = BaseIndex.ES_CLUSTER_NAME, + local = false, data = true) private Node node; @Before public void setUp() throws Exception { - this.node = NodeBuilder.nodeBuilder() - .settings(ImmutableSettings.settingsBuilder() - .put("node.name", TEST_NODE_NAME).build()) - .clusterName(BaseIndex.ES_CLUSTER_NAME).node(); } - private BaseIndex getBaseIndex(){ + private RuleIndex getRuleIndex(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); Settings settings = new Settings(); settings.setProperty("sonar.log.profilingLevel", "BASIC"); - return new BaseIndex(null, new Profiling(settings)) { - @Override - public Map normalize(Serializable key) { - // TODO Auto-generated method stub - return null; - } - }; + RuleIndex rindex = new RuleIndex(queue, null, new Profiling(settings)); + return rindex; } @After @@ -72,30 +64,15 @@ public class BaseIndexTest { } @Test - public void should_start_and_stop_properly() { + public void test_ruleIndex_conencts_to_es() { - searchIndex = getBaseIndex(); - searchIndex.start(); + RuleIndex ruleIndex = getRuleIndex(); + ruleIndex.connect(); assertThat(node.client().admin().cluster().prepareClusterStats().get().getNodesStats().getCounts().getTotal()) - .isEqualTo(searchIndex.getNodesStats().getCounts().getTotal()); - - searchIndex.stop(); - - } - - @Test(expected = NoNodeAvailableException.class) - public void fails_when_es_gone(){ - searchIndex = getBaseIndex(); - searchIndex.start(); - - node.stop(); - - - assertThat(searchIndex.getNodesStats().getCounts().getTotal()); - - node.start(); + .isEqualTo(ruleIndex.getNodesStats().getCounts().getTotal()); + ruleIndex.stop(); } } diff --git a/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java b/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java new file mode 100644 index 00000000000..0f75176d5e1 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java @@ -0,0 +1,132 @@ +/* + * 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.search; + +import org.sonar.server.cluster.LocalNonBlockingWorkQueue; + +import com.github.tlrx.elasticsearch.test.annotations.ElasticsearchNode; +import com.github.tlrx.elasticsearch.test.support.junit.runners.ElasticsearchRunner; +import org.elasticsearch.client.transport.NoNodeAvailableException; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.node.Node; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sonar.api.config.Settings; +import org.sonar.core.profiling.Profiling; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; + +import static org.fest.assertions.Assertions.assertThat; + +@RunWith(ElasticsearchRunner.class) +public class BaseIndexTest { + + private static final String TEST_NODE_NAME = "es_node_for_tests"; + + @ElasticsearchNode(name = TEST_NODE_NAME, + clusterName = BaseIndex.ES_CLUSTER_NAME, + local = false, data = true) + private Node node; + + @Before + public void setUp() throws Exception { + + } + + private BaseIndex getBaseIndex(){ + LocalNonBlockingWorkQueue queue = new LocalNonBlockingWorkQueue(); + Settings settings = new Settings(); + settings.setProperty("sonar.log.profilingLevel", "BASIC"); + return new BaseIndex(queue, null, new Profiling(settings)) { + + @Override + public String getIndexName() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected org.elasticsearch.common.settings.Settings getIndexSettings() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected String getType() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected XContentBuilder getMapping() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection synchronizeSince(Long date) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected QueryBuilder getKeyQuery(Serializable key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map normalize(Serializable key) { + // TODO Auto-generated method stub + return null; + } + }; + } + + @After + public void tearDown() { + if (node != null && !node.isClosed()) { + node.close(); + } + } + + @Test + public void baseIndex_connects_to_es() { + BaseIndex searchIndex = getBaseIndex(); + searchIndex.connect(); + assertThat(node.client().admin().cluster().prepareClusterStats().get().getNodesStats().getCounts().getTotal()) + .isEqualTo(searchIndex.getNodesStats().getCounts().getTotal()); + + searchIndex.stop(); + } + + @Test(expected = NoNodeAvailableException.class) + public void baseIndex_fails_when_es_gone(){ + BaseIndex searchIndex = getBaseIndex(); + searchIndex.connect(); + node.close(); + assertThat(searchIndex.getNodesStats().getCounts().getTotal()).isNotNull(); + } +} -- 2.39.5