Package org.jboss.elasticsearch.river.jira

Source Code of org.jboss.elasticsearch.river.jira.JiraRiver

package org.jboss.elasticsearch.river.jira;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.SettingsException;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.river.AbstractRiverComponent;
import org.elasticsearch.river.River;
import org.elasticsearch.river.RiverName;
import org.elasticsearch.river.RiverSettings;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortOrder;
import org.jboss.elasticsearch.tools.content.StructuredContentPreprocessorFactory;

import static org.elasticsearch.client.Requests.indexRequest;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

/**
* JIRA River implementation class.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class JiraRiver extends AbstractRiverComponent implements River, IESIntegration, IJiraRiverMgm {

  /**
   * Map of running river instances. Used for management operations dispatching. See {@link #getRunningInstance(String)}
   */
  protected static Map<String, IJiraRiverMgm> riverInstances = new HashMap<String, IJiraRiverMgm>();

  /**
   * Name of datetime property where permanent indexing stop date is stored
   *
   * @see #storeDatetimeValue(String, String, Date, BulkRequestBuilder)
   * @see #readDatetimeValue(String, String)
   */
  protected static final String PERMSTOREPROP_RIVER_STOPPED_PERMANENTLY = "river_stopped_permanently";

  /**
   * How often is project list refreshed from JIRA instance [ms].
   */
  protected static final long JIRA_PROJECTS_REFRESH_TIME = 30 * 60 * 1000;

  public static final String INDEX_ISSUE_TYPE_NAME_DEFAULT = "jira_issue";

  public static final String INDEX_ACTIVITY_TYPE_NAME_DEFAULT = "jira_river_indexupdate";

  /**
   * ElasticSearch client to be used for indexing
   */
  protected Client client;

  /**
   * Configured JIRA client to access data from JIRA
   */
  protected IJIRAClient jiraClient;

  /**
   * Configured JIRA issue index structure builder to be used.
   */
  protected IJIRAIssueIndexStructureBuilder jiraIssueIndexStructureBuilder;

  /**
   * Config - maximal number of parallel JIRA indexing threads
   */
  protected int maxIndexingThreads;

  /**
   * Config - index update period [ms]
   */
  protected long indexUpdatePeriod;

  /**
   * Config - index full update period [ms]
   */
  protected long indexFullUpdatePeriod = -1;

  /**
   * Config - name of ElasticSearch index used to store issues from this river
   */
  protected String indexName;

  /**
   * Config - name of ElasticSearch type used to store issues from this river in index
   */
  protected String typeName;

  /**
   * Config - Base URL of JIRA instance to index by this river
   */
  protected String jiraUrlBase = null;

  /**
   * Config - name of ElasticSearch index used to store river activity records - null means no activity stored
   */
  protected String activityLogIndexName;

  /**
   * Config - name of ElasticSearch type used to store river activity records in index
   */
  protected String activityLogTypeName;

  /**
   * Thread running {@link JIRAProjectIndexerCoordinator} is stored here.
   */
  protected Thread coordinatorThread;

  /**
   * USed {@link JIRAProjectIndexerCoordinator} instance is stored here.
   */
  protected IJIRAProjectIndexerCoordinator coordinatorInstance;

  /**
   * Flag set to true if this river is stopped from ElasticSearch server.
   */
  protected volatile boolean closed = true;

  /**
   * List of indexing excluded JIRA project keys loaded from river configuration
   *
   * @see #getAllIndexedProjectsKeys()
   */
  protected List<String> projectKeysExcluded = null;

  /**
   * List of all JIRA project keys to be indexed. Loaded from river configuration, or from remote JIRA (excludes
   * removed)
   *
   * @see #getAllIndexedProjectsKeys()
   */
  protected List<String> allIndexedProjectsKeys = null;

  /**
   * Next time when {@link #allIndexedProjectsKeys} need to be refreshed from remote JIRA instance.
   *
   * @see #getAllIndexedProjectsKeys()
   */
  protected long allIndexedProjectsKeysNextRefresh = 0;

  /**
   * Last project indexing info store. Key in map is project key.
   */
  protected Map<String, ProjectIndexingInfo> lastProjectIndexingInfo = new HashMap<String, ProjectIndexingInfo>();

  /**
   * Date of last restart of this river.
   */
  protected Date lastRestartDate;

  /**
   * Timestamp of permanent stop of this river.
   */
  protected Date permanentStopDate;

  /**
   * Public constructor used by ElasticSearch.
   *
   * @param riverName
   * @param settings
   * @param client
   * @throws MalformedURLException
   */
  @Inject
  public JiraRiver(RiverName riverName, RiverSettings settings, Client client) throws MalformedURLException {
    super(riverName, settings);
    this.client = client;
    configure(settings.settings());
  }

  /**
   * Configure jira river.
   *
   * @param settings used for configuration.
   */
  @SuppressWarnings({ "unchecked" })
  protected void configure(Map<String, Object> settings) {

    if (!closed)
      throw new IllegalStateException("Jira River must be stopped to configure it!");

    String jiraUser = null;
    String jiraJqlTimezone = TimeZone.getDefault().getDisplayName();

    if (settings.containsKey("jira")) {
      Map<String, Object> jiraSettings = (Map<String, Object>) settings.get("jira");
      jiraUrlBase = XContentMapValues.nodeStringValue(jiraSettings.get("urlBase"), null);
      if (Utils.isEmpty(jiraUrlBase)) {
        throw new SettingsException("jira/urlBase element of configuration structure not found or empty");
      }
      Integer timeout = new Long(Utils.parseTimeValue(jiraSettings, "timeout", 5, TimeUnit.SECONDS)).intValue();
      jiraUser = XContentMapValues.nodeStringValue(jiraSettings.get("username"), "Anonymous access");
      jiraClient = new JIRA5RestClient(jiraUrlBase, XContentMapValues.nodeStringValue(jiraSettings.get("username"),
          null), XContentMapValues.nodeStringValue(jiraSettings.get("pwd"), null), timeout,
          XContentMapValues.nodeStringValue(jiraSettings.get("restApiVersion"), null));
      jiraClient.setListJIRAIssuesMax(XContentMapValues.nodeIntegerValue(jiraSettings.get("maxIssuesPerRequest"), 50));
      if (jiraSettings.get("jqlTimeZone") != null) {
        TimeZone tz = TimeZone.getTimeZone(XContentMapValues.nodeStringValue(jiraSettings.get("jqlTimeZone"), null));
        jiraJqlTimezone = tz.getDisplayName();
        jiraClient.setJQLDateFormatTimezone(tz);
      }
      maxIndexingThreads = XContentMapValues.nodeIntegerValue(jiraSettings.get("maxIndexingThreads"), 1);
      indexUpdatePeriod = Utils.parseTimeValue(jiraSettings, "indexUpdatePeriod", 5, TimeUnit.MINUTES);
      indexFullUpdatePeriod = Utils.parseTimeValue(jiraSettings, "indexFullUpdatePeriod", 12, TimeUnit.HOURS);
      if (jiraSettings.containsKey("projectKeysIndexed")) {
        allIndexedProjectsKeys = Utils.parseCsvString(XContentMapValues.nodeStringValue(
            jiraSettings.get("projectKeysIndexed"), null));
        if (allIndexedProjectsKeys != null) {
          // stop loading from JIRA
          allIndexedProjectsKeysNextRefresh = Long.MAX_VALUE;
        }
      }
      if (jiraSettings.containsKey("projectKeysExcluded")) {
        projectKeysExcluded = Utils.parseCsvString(XContentMapValues.nodeStringValue(
            jiraSettings.get("projectKeysExcluded"), null));
      }
    } else {
      throw new SettingsException("'jira' element of river configuration structure not found");
    }

    Map<String, Object> indexSettings = null;
    if (settings.containsKey("index")) {
      indexSettings = (Map<String, Object>) settings.get("index");
      indexName = XContentMapValues.nodeStringValue(indexSettings.get("index"), riverName.name());
      typeName = XContentMapValues.nodeStringValue(indexSettings.get("type"), INDEX_ISSUE_TYPE_NAME_DEFAULT);
    } else {
      indexName = riverName.name();
      typeName = INDEX_ISSUE_TYPE_NAME_DEFAULT;
    }

    Map<String, Object> activityLogSettings = null;
    if (settings.containsKey("activity_log")) {
      activityLogSettings = (Map<String, Object>) settings.get("activity_log");
      activityLogIndexName = Utils
          .trimToNull(XContentMapValues.nodeStringValue(activityLogSettings.get("index"), null));
      if (activityLogIndexName == null) {
        throw new SettingsException(
            "'activity_log/index' element of river configuration structure must be defined with some string");
      }
      activityLogTypeName = Utils.trimToNull(XContentMapValues.nodeStringValue(activityLogSettings.get("type"),
          INDEX_ACTIVITY_TYPE_NAME_DEFAULT));
    }

    jiraIssueIndexStructureBuilder = new JIRA5RestIssueIndexStructureBuilder(riverName.getName(), indexName, typeName,
        jiraUrlBase, indexSettings);
    preparePreprocessors(indexSettings, jiraIssueIndexStructureBuilder);

    jiraClient.setIndexStructureBuilder(jiraIssueIndexStructureBuilder);

    logger
        .info(
            "Configured JIRA River '{}' for JIRA API base URL '{}', jira user '{}', JQL timezone '{}'. Search index name '{}', document type for issues '{}'.",
            riverName.getName(), jiraClient.getJiraAPIUrlBase(), jiraUser, jiraJqlTimezone, indexName, typeName);
    if (activityLogIndexName != null) {
      logger.info(
          "Activity log for JIRA River '{}' is enabled. Search index name '{}', document type for index updates '{}'.",
          riverName.getName(), activityLogIndexName, activityLogTypeName);
    }
  }

  @SuppressWarnings("unchecked")
  private void preparePreprocessors(Map<String, Object> indexSettings,
      IJIRAIssueIndexStructureBuilder indexStructureBuilder) {
    if (indexSettings != null) {
      List<Map<String, Object>> preproclist = (List<Map<String, Object>>) indexSettings.get("preprocessors");
      if (preproclist != null && preproclist.size() > 0) {
        for (Map<String, Object> ppc : preproclist) {
          try {
            indexStructureBuilder.addIssueDataPreprocessor(StructuredContentPreprocessorFactory.createPreprocessor(ppc,
                client));
          } catch (IllegalArgumentException e) {
            throw new SettingsException(e.getMessage(), e);
          }
        }
      }
    }
  }

  /**
   * Constructor for unit tests, nothing is initialized/configured in river.
   *
   * @param riverName
   * @param settings
   */
  protected JiraRiver(RiverName riverName, RiverSettings settings) {
    super(riverName, settings);
  }

  @Override
  public synchronized void start() {
    if (!closed)
      throw new IllegalStateException("Can't start already running river");
    logger.info("starting JIRA River");
    synchronized (riverInstances) {
      addRunningInstance(this);
    }
    refreshSearchIndex(getRiverIndexName());
    try {
      if ((permanentStopDate = readDatetimeValue(null, PERMSTOREPROP_RIVER_STOPPED_PERMANENTLY)) != null) {
        logger
            .info("JIRA River indexing process not started because stopped permanently, you can restart it over management REST API");
        return;
      }
    } catch (IOException e) {
      // OK, we will start river
    }
    logger.info("starting JIRA River indexing process");
    closed = false;
    lastRestartDate = new Date();
    coordinatorInstance = new JIRAProjectIndexerCoordinator(jiraClient, this, jiraIssueIndexStructureBuilder,
        indexUpdatePeriod, maxIndexingThreads, indexFullUpdatePeriod);
    coordinatorThread = acquireIndexingThread("jira_river_coordinator", coordinatorInstance);
    coordinatorThread.start();
  }

  @Override
  public synchronized void close() {
    logger.info("closing JIRA River on this node");
    closed = true;
    if (coordinatorThread != null) {
      coordinatorThread.interrupt();
    }
    // free instances created in #start()
    coordinatorThread = null;
    coordinatorInstance = null;
    synchronized (riverInstances) {
      riverInstances.remove(riverName().getName());
    }
  }

  /**
   * Stop jira river, but leave instance existing in {@link #riverInstances} so it can be found over management REST
   * calls and/or reconfigured and started later again. Note that standard ES river {@link #close()} method
   * implementation removes river instance from {@link #riverInstances}.
   *
   * @param permanent set to true if info about river stopped can be persisted
   */
  @Override
  public synchronized void stop(boolean permanent) {
    logger.info("stopping JIRA River indexing process");
    closed = true;
    if (coordinatorThread != null) {
      coordinatorThread.interrupt();
    }
    // free instances created in #start()
    coordinatorThread = null;
    coordinatorInstance = null;
    if (permanent) {
      try {
        permanentStopDate = new Date();
        storeDatetimeValue(null, PERMSTOREPROP_RIVER_STOPPED_PERMANENTLY, permanentStopDate, null);
        refreshSearchIndex(getRiverIndexName());
        logger.info("JIRA River indexing process stopped permanently, you can restart it over management REST API");
      } catch (IOException e) {
        logger.warn("Permanent stopped value storing failed {}", e.getMessage());
      }
    }
  }

  /**
   * Reconfigure jira river. Must be stopped!
   */
  public synchronized void reconfigure() {
    if (!closed)
      throw new IllegalStateException("Jira River must be stopped to reconfigure it!");

    logger.info("reconfiguring JIRA River");
    String riverIndexName = getRiverIndexName();
    refreshSearchIndex(riverIndexName);
    GetResponse resp = client.prepareGet(riverIndexName, riverName().name(), "_meta").execute().actionGet();
    if (resp.isExists()) {
      if (logger.isDebugEnabled()) {
        logger.debug("Configuration document: {}", resp.getSourceAsString());
      }
      Map<String, Object> newset = resp.getSource();
      configure(newset);
    } else {
      throw new IllegalStateException("Configuration document not found to reconfigure jira river "
          + riverName().name());
    }
  }

  /**
   * Restart jira river. Configuration of river is updated.
   */
  @Override
  public synchronized void restart() {
    logger.info("restarting JIRA River");
    boolean cleanPermanent = true;
    if (!closed) {
      cleanPermanent = false;
      stop(false);
      // wait a while to allow currently running indexers to finish??
      try {
        Thread.sleep(2000);
      } catch (InterruptedException e) {
        return;
      }
    } else {
      logger.debug("stopped already");
    }
    reconfigure();
    if (cleanPermanent) {
      deleteDatetimeValue(null, PERMSTOREPROP_RIVER_STOPPED_PERMANENTLY);
    }
    start();
    logger.info("JIRA River restarted");
  }

  @Override
  public boolean isClosed() {
    return closed;
  }

  /**
   * Force full index update for some project(s) in this jira river. Used for REST management operations handling.
   *
   * @param jiraProjectKey optional key of project to reindex, if null or empty then all projects are forced to full
   *          reindex
   * @return CSV list of projects forced to reindex. <code>null</code> if project passed over
   *         <code>jiraProjectKey</code> parameter was not found in this indexer
   * @throws Exception
   */
  @Override
  public String forceFullReindex(String jiraProjectKey) throws Exception {
    if (coordinatorInstance == null)
      return null;
    List<String> pkeys = getAllIndexedProjectsKeys();
    if (Utils.isEmpty(jiraProjectKey)) {
      if (pkeys != null) {
        for (String k : pkeys) {
          coordinatorInstance.forceFullReindex(k);
        }
        return Utils.createCsvString(pkeys);
      } else {
        return "";
      }

    } else {
      if (pkeys != null && pkeys.contains(jiraProjectKey)) {
        coordinatorInstance.forceFullReindex(jiraProjectKey);
        return jiraProjectKey;
      } else {
        return null;
      }
    }
  }

  /**
   * Get info about current operation of this river. Used for REST management operations handling.
   *
   * @return String with JSON formatted info.
   * @throws Exception
   */
  @Override
  public String getRiverOperationInfo(DiscoveryNode esNode, Date currentDate) throws Exception {

    XContentBuilder builder = jsonBuilder().prettyPrint();
    builder.startObject();
    builder.field("river_name", riverName().getName());
    builder.field("info_date", currentDate);
    builder.startObject("indexing");
    builder.field("state", closed ? "stopped" : "running");
    if (!closed)
      builder.field("last_restart", lastRestartDate);
    else if (permanentStopDate != null)
      builder.field("stopped_permanently", permanentStopDate);
    builder.endObject();
    if (esNode != null) {
      builder.startObject("node");
      builder.field("id", esNode.getId());
      builder.field("name", esNode.getName());
      builder.endObject();
    }
    if (coordinatorInstance != null) {
      List<ProjectIndexingInfo> currProjectIndexingInfo = coordinatorInstance.getCurrentProjectIndexingInfo();
      if (currProjectIndexingInfo != null) {
        builder.startArray("current_indexing");
        for (ProjectIndexingInfo pi : currProjectIndexingInfo) {
          pi.buildDocument(builder, true, false);
        }
        builder.endArray();
      }
    }
    List<String> pkeys = getAllIndexedProjectsKeys();
    if (pkeys != null) {
      builder.startArray("indexed_jira_projects");
      for (String projectKey : pkeys) {
        builder.startObject();
        builder.field("project_key", projectKey);
        ProjectIndexingInfo lastIndexing = getLastProjectIndexingInfo(projectKey);
        if (lastIndexing != null) {
          builder.field("last_indexing");
          lastIndexing.buildDocument(builder, false, true);
        }
        builder.endObject();
      }
      builder.endArray();
    }
    builder.endObject();
    return builder.string();
  }

  /**
   * @param projectKey to get info for
   * @return project indexing info or null if not found.
   */
  protected ProjectIndexingInfo getLastProjectIndexingInfo(String projectKey) {
    ProjectIndexingInfo lastIndexing = lastProjectIndexingInfo.get(projectKey);
    if (lastIndexing == null && activityLogIndexName != null) {
      try {
        refreshSearchIndex(activityLogIndexName);
        SearchResponse sr = client.prepareSearch(activityLogIndexName).setTypes(activityLogTypeName)
            .setPostFilter(FilterBuilders.termFilter(ProjectIndexingInfo.DOCFIELD_PROJECT_KEY, projectKey))
            .setQuery(QueryBuilders.matchAllQuery()).addSort(ProjectIndexingInfo.DOCFIELD_START_DATE, SortOrder.DESC)
            .addField("_source").setSize(1).execute().actionGet();
        if (sr.getHits().getTotalHits() > 0) {
          SearchHit hit = sr.getHits().getAt(0);
          lastIndexing = ProjectIndexingInfo.readFromDocument(hit.sourceAsMap());
        } else {
          logger.debug("No last indexing info found in activity log for project {}", projectKey);
        }
      } catch (Exception e) {
        logger.warn("Error during LastProjectIndexingInfo reading from activity log ES index: {} {}", e.getClass()
            .getName(), e.getMessage());
      }
    }
    return lastIndexing;
  }

  /**
   * Get running instance of jira river for given name. Used for REST management operations handling.
   *
   * @param riverName to get instance for
   * @return river instance or null if not found
   * @see #addRunningInstance(IJiraRiverMgm)
   * @see #getRunningInstances()
   */
  public static IJiraRiverMgm getRunningInstance(String riverName) {
    if (riverName == null)
      return null;
    return riverInstances.get(riverName);
  }

  /**
   * Put running instance of jira river into registry. Used for REST management operations handling.
   *
   * @param jiraRiver to get instance for
   * @see #getRunningInstances()
   * @see #getRunningInstance(String)
   */
  public static void addRunningInstance(IJiraRiverMgm jiraRiver) {
    riverInstances.put(jiraRiver.riverName().getName(), jiraRiver);
  }

  /**
   * Get running instances of all jira rivers. Used for REST management operations handling.
   *
   * @return Set with names of all jira river instances registered for management
   * @see #addRunningInstance(IJiraRiverMgm)
   * @see #getRunningInstance(String)
   */
  public static Set<String> getRunningInstances() {
    return Collections.unmodifiableSet((riverInstances.keySet()));
  }

  /**
   * Remove rivers of given names. Note: this method was added because of unit tests. Do not call this method in
   * production code.
   *
   * @param riverNames names of the rivers to remove
   */
  public static void removeRunningInstances(String... riverNames) {
    for (String riverName : riverNames) {
      riverInstances.remove(riverName);
    }
  }

  @Override
  public List<String> getAllIndexedProjectsKeys() throws Exception {
    if (allIndexedProjectsKeys == null || allIndexedProjectsKeysNextRefresh < System.currentTimeMillis()) {
      allIndexedProjectsKeys = jiraClient.getAllJIRAProjects();
      if (projectKeysExcluded != null) {
        allIndexedProjectsKeys.removeAll(projectKeysExcluded);
      }
      allIndexedProjectsKeysNextRefresh = System.currentTimeMillis() + JIRA_PROJECTS_REFRESH_TIME;
    }

    return allIndexedProjectsKeys;
  }

  @Override
  public void reportIndexingFinished(ProjectIndexingInfo indexingInfo) {
    lastProjectIndexingInfo.put(indexingInfo.projectKey, indexingInfo);
    if (coordinatorInstance != null) {
      try {
        coordinatorInstance.reportIndexingFinished(indexingInfo.projectKey, indexingInfo.finishedOK,
            indexingInfo.fullUpdate);
      } catch (Exception e) {
        logger.warn("Indexing finished reporting to coordinator failed due {}", e.getMessage());
      }
    }
    writeActivityLogRecord(indexingInfo);
  }

  /**
   * Write indexing info into activity log if enabled.
   *
   * @param indexingInfo to write
   */
  protected void writeActivityLogRecord(ProjectIndexingInfo indexingInfo) {
    if (activityLogIndexName != null) {
      try {
        client.prepareIndex(activityLogIndexName, activityLogTypeName)
            .setSource(indexingInfo.buildDocument(jsonBuilder(), true, true)).execute().actionGet();
      } catch (Exception e) {
        logger.error("Error during index update result writing to the audit log {}", e.getMessage());
      }
    }
  }

  @Override
  public void storeDatetimeValue(String projectKey, String propertyName, Date datetime, BulkRequestBuilder esBulk)
      throws IOException {
    String documentName = prepareValueStoreDocumentName(projectKey, propertyName);
    if (logger.isDebugEnabled())
      logger.debug(
          "Going to write {} property with datetime value {} for project {} using {} update. Document name is {}.",
          propertyName, datetime, projectKey, (esBulk != null ? "bulk" : "direct"), documentName);
    if (esBulk != null) {
      esBulk.add(indexRequest(getRiverIndexName()).type(riverName.name()).id(documentName)
          .source(storeDatetimeValueBuildDocument(projectKey, propertyName, datetime)));
    } else {
      client.prepareIndex(getRiverIndexName(), riverName.name(), documentName)
          .setSource(storeDatetimeValueBuildDocument(projectKey, propertyName, datetime)).execute().actionGet();
    }
  }

  /**
   * Constant for field in JSON document used to store values.
   *
   * @see #storeDatetimeValue(String, String, Date, BulkRequestBuilder)
   * @see #readDatetimeValue(String, String)
   * @see #storeDatetimeValueBuildDocument(String, String, Date)
   *
   */
  protected static final String STORE_FIELD_VALUE = "value";

  /**
   * Prepare JSON document to be stored inside {@link #storeDatetimeValue(String, String, Date, BulkRequestBuilder)}.
   *
   * @param projectKey key of project value is for
   * @param propertyName name of property
   * @param datetime value to store
   * @return JSON document
   * @throws IOException
   * @see #storeDatetimeValue(String, String, Date, BulkRequestBuilder)
   * @see #readDatetimeValue(String, String)
   */
  protected XContentBuilder storeDatetimeValueBuildDocument(String projectKey, String propertyName, Date datetime)
      throws IOException {
    XContentBuilder builder = jsonBuilder().startObject();
    if (projectKey != null)
      builder.field("projectKey", projectKey);
    builder.field("propertyName", propertyName).field(STORE_FIELD_VALUE, DateTimeUtils.formatISODateTime(datetime));
    builder.endObject();
    return builder;
  }

  @Override
  public Date readDatetimeValue(String projectKey, String propertyName) throws IOException {
    Date lastDate = null;
    String documentName = prepareValueStoreDocumentName(projectKey, propertyName);

    if (logger.isDebugEnabled())
      logger.debug("Going to read datetime value from {} property for project {}. Document name is {}.", propertyName,
          projectKey, documentName);

    refreshSearchIndex(getRiverIndexName());
    GetResponse lastSeqGetResponse = client.prepareGet(getRiverIndexName(), riverName.name(), documentName).execute()
        .actionGet();
    if (lastSeqGetResponse.isExists()) {
      Object timestamp = lastSeqGetResponse.getSourceAsMap().get(STORE_FIELD_VALUE);
      if (timestamp != null) {
        lastDate = DateTimeUtils.parseISODateTime(timestamp.toString());
      }
    } else {
      if (logger.isDebugEnabled())
        logger.debug("{} document doesn't exist in JIRA river persistent store", documentName);
    }
    return lastDate;
  }

  @Override
  public boolean deleteDatetimeValue(String projectKey, String propertyName) {
    String documentName = prepareValueStoreDocumentName(projectKey, propertyName);

    if (logger.isDebugEnabled())
      logger.debug("Going to delete datetime value from {} property for project {}. Document name is {}.",
          propertyName, projectKey, documentName);

    refreshSearchIndex(getRiverIndexName());

    DeleteResponse lastSeqGetResponse = client.prepareDelete(getRiverIndexName(), riverName.name(), documentName)
        .execute().actionGet();
    if (!lastSeqGetResponse.isFound()) {
      if (logger.isDebugEnabled()) {
        logger.debug("{} document doesn't exist in JIRA river persistent store", documentName);
      }
      return false;
    } else {
      return true;
    }

  }

  /**
   * @return
   */
  protected String getRiverIndexName() {
    return "_river";
    // return RiverIndexName.Conf.indexName(settings.globalSettings());
  }

  /**
   * Prepare name of document where jira project related persistent value is stored
   *
   * @param projectKey key of jira project stored value is for
   * @param propertyName name of value
   * @return document name
   *
   * @see #storeDatetimeValue(String, String, Date, BulkRequestBuilder)
   * @see #readDatetimeValue(String, String)
   */
  protected static String prepareValueStoreDocumentName(String projectKey, String propertyName) {
    if (projectKey != null)
      return "_" + propertyName + "_" + projectKey;
    else
      return "_" + propertyName;
  }

  @Override
  public BulkRequestBuilder prepareESBulkRequestBuilder() {
    return client.prepareBulk();
  }

  @Override
  public void executeESBulkRequest(BulkRequestBuilder esBulk) throws Exception {
    BulkResponse response = esBulk.execute().actionGet();
    if (response.hasFailures()) {
      throw new ElasticsearchException("Failed to execute ES index bulk update: " + response.buildFailureMessage());
    }
  }

  @Override
  public Thread acquireIndexingThread(String threadName, Runnable runnable) {
    return EsExecutors.daemonThreadFactory(settings.globalSettings(), threadName).newThread(runnable);
  }

  @Override
  public void refreshSearchIndex(String indexName) {
    client.admin().indices().prepareRefresh(indexName).execute().actionGet();
  }

  private static final long ES_SCROLL_KEEPALIVE = 60000;

  @Override
  public SearchRequestBuilder prepareESScrollSearchRequestBuilder(String indexName) {
    return client.prepareSearch(indexName).setScroll(new TimeValue(ES_SCROLL_KEEPALIVE)).setSearchType(SearchType.SCAN)
        .setSize(100);
  }

  public SearchResponse executeESSearchRequest(SearchRequestBuilder searchRequestBuilder) {
    return searchRequestBuilder.execute().actionGet();
  }

  @Override
  public SearchResponse executeESScrollSearchNextRequest(SearchResponse scrollResp) {
    return client.prepareSearchScroll(scrollResp.getScrollId()).setScroll(new TimeValue(ES_SCROLL_KEEPALIVE)).execute()
        .actionGet();
  }

}
TOP

Related Classes of org.jboss.elasticsearch.river.jira.JiraRiver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.