/*
* (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo
*/
package org.nuxeo.elasticsearch.web.admin;
import static org.jboss.seam.ScopeType.CONVERSATION;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.elasticsearch.action.count.CountResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.nuxeo.ecm.core.api.CoreInstance;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.platform.query.api.PageProvider;
import org.nuxeo.ecm.platform.query.api.PageProviderDefinition;
import org.nuxeo.ecm.platform.query.api.PageProviderService;
import org.nuxeo.elasticsearch.api.ElasticSearchAdmin;
import org.nuxeo.elasticsearch.api.ElasticSearchIndexing;
import org.nuxeo.elasticsearch.commands.IndexingCommand;
import org.nuxeo.elasticsearch.commands.IndexingCommand.Type;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.metrics.MetricsService;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
/**
* @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
*/
@Name("esAdmin")
@Scope(CONVERSATION)
public class ElasticSearchManager implements Serializable {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(ElasticSearchManager.class);
private static final String DEFAULT_NXQL_QUERY = "SELECT * FROM Document";
private static final String JSON_DELETE_CMD = "{\"id\":\"IndexingCommand-reindex\",\"type\":\"DELETE\",\"docId\":\"%s\",\"repo\":\"%s\",\"recurse\":true,\"sync\":true}";
private static final String ES_CLUSTER_INFO_PROPERTY = "elasticsearch.adminCenter.displayClusterInfo";
@In(create = true)
protected ElasticSearchAdmin esa;
@In(create = true)
protected ElasticSearchIndexing esi;
@In(create = true, required = false)
protected transient CoreSession documentManager;
protected List<PageProviderStatus> ppStatuses = null;
protected Timer indexTimer;
protected Timer bulkIndexTimer;
private String rootId;
private String nxql = DEFAULT_NXQL_QUERY;
private List<String> repositoryNames;
private String repositoryName;
private Boolean dropIndex = false;
public String getNodesInfo() {
NodesInfoResponse nodesInfo = esa.getClient().admin().cluster().prepareNodesInfo().execute().actionGet();
return nodesInfo.toString();
}
public String getNodesStats() {
NodesStatsResponse stats = esa.getClient().admin().cluster().prepareNodesStats().execute().actionGet();
return stats.toString();
}
public String getNodesHealth() {
String[] indices = getIndexNames();
ClusterHealthResponse health = esa.getClient().admin().cluster().prepareHealth(indices).get();
return health.toString();
}
public void startReindexAll() {
String repositoryName = getRepositoryName();
log.warn("Re-indexing the entire repository: " + repositoryName);
esa.dropAndInitRepositoryIndex(repositoryName);
esi.runReindexingWorker(repositoryName, "SELECT ecm:uuid FROM Document");
}
public void startReindexNxql() {
String repositoryName = getRepositoryName();
log.warn(String.format("Re-indexing from a NXQL query: %s on repository: %s", getNxql(), repositoryName));
esi.runReindexingWorker(repositoryName, getNxql());
}
public void startReindexFrom() {
String repositoryName = getRepositoryName();
try (CoreSession session = CoreInstance.openCoreSessionSystem(repositoryName)) {
log.warn(String.format("Try to remove %s and its children from %s repository index", rootId,
repositoryName));
String jsonCmd = String.format(JSON_DELETE_CMD, rootId, repositoryName);
IndexingCommand rmCmd = IndexingCommand.fromJSON(jsonCmd);
esi.indexNonRecursive(rmCmd);
DocumentRef ref = new IdRef(rootId);
if (session.exists(ref)) {
DocumentModel doc = session.getDocument(ref);
log.warn(String.format("Re-indexing document: %s and its children on repository: %s", doc,
repositoryName));
IndexingCommand cmd = new IndexingCommand(doc, Type.INSERT, false, true);
esi.runIndexingWorker(Arrays.asList(cmd));
}
}
}
public void flush() {
esa.flush();
}
public void optimize() {
esa.optimize();
}
protected void introspectPageProviders() {
ppStatuses = new ArrayList<>();
PageProviderService pps = Framework.getLocalService(PageProviderService.class);
for (String ppName : pps.getPageProviderDefinitionNames()) {
PageProviderDefinition def = pps.getPageProviderDefinition(ppName);
// Create an instance so class replacer is taken in account
PageProvider<?> pp = pps.getPageProvider(ppName, def, null, null, 0L, 0L, null);
String klass = pp.getClass().getCanonicalName();
ppStatuses.add(new PageProviderStatus(ppName, klass));
}
Collections.sort(ppStatuses);
}
public List<PageProviderStatus> getContentViewStatus() {
if (ppStatuses == null) {
introspectPageProviders();
}
return ppStatuses;
}
public Boolean isIndexingInProgress() {
return esa.isIndexingInProgress();
}
public Boolean displayClusterInfo() {
if (esa.isEmbedded()) {
return true;
}
return Boolean.parseBoolean(Framework.getProperty(ES_CLUSTER_INFO_PROPERTY, "false"));
}
public String getPendingWorkerCount() {
return Long.valueOf(esa.getPendingWorkerCount()).toString();
}
public String getRunningWorkerCount() {
return Long.valueOf(esa.getRunningWorkerCount()).toString();
}
public String getTotalCommandProcessed() {
return Integer.valueOf(esa.getTotalCommandProcessed()).toString();
}
public String getNumberOfDocuments() {
String[] indices = getIndexNames();
CountResponse ret = esa.getClient().prepareCount(indices).setQuery(QueryBuilders.matchAllQuery()).get();
return Long.valueOf(ret.getCount()).toString();
}
private String[] getIndexNames() {
List<String> repositoryNames = getRepositoryNames();
String indices[] = new String[repositoryNames.size()];
int i = 0;
for (String repo : repositoryNames) {
indices[i++] = esa.getIndexNameForRepository(repo);
}
return indices;
}
public String getIndexingRates() {
if (indexTimer == null) {
MetricRegistry registry = SharedMetricRegistries.getOrCreate(MetricsService.class.getName());
indexTimer = registry.timer(MetricRegistry.name("nuxeo", "elasticsearch", "service", "index"));
}
return String.format("%.2f, %.2f, %.2f", indexTimer.getOneMinuteRate(), indexTimer.getFiveMinuteRate(),
indexTimer.getFifteenMinuteRate());
}
public String getBulkIndexingRates() {
if (bulkIndexTimer == null) {
MetricRegistry registry = SharedMetricRegistries.getOrCreate(MetricsService.class.getName());
bulkIndexTimer = registry.timer(MetricRegistry.name("nuxeo", "elasticsearch", "service", "bulkIndex"));
}
return String.format("%.2f, %.2f, %.2f", bulkIndexTimer.getOneMinuteRate(), bulkIndexTimer.getFiveMinuteRate(),
bulkIndexTimer.getFifteenMinuteRate());
}
public String getRootId() {
return rootId;
}
public List<String> getRepositoryNames() {
if (repositoryNames == null) {
repositoryNames = esa.getRepositoryNames();
}
return repositoryNames;
}
public void setRootId(String rootId) {
this.rootId = rootId;
}
public String getNxql() {
return nxql;
}
public void setNxql(String nxql) {
this.nxql = nxql;
}
public String getRepositoryName() {
if (repositoryName == null) {
List<String> repositoryNames = getRepositoryNames();
if (!repositoryNames.isEmpty()) {
repositoryName = repositoryNames.get(0);
}
}
return repositoryName;
}
public void setRepositoryName(String repositoryName) {
this.repositoryName = repositoryName;
}
public Boolean getDropIndex() {
return dropIndex;
}
public void setDropIndex(Boolean dropIndex) {
this.dropIndex = dropIndex;
}
}