/*
* Copyright 2013 JBoss Inc
*
* 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.
*/
package org.overlord.rtgov.analytics.situation.store.elasticsearch;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.overlord.commons.services.ServiceClose;
import org.overlord.commons.services.ServiceInit;
import org.overlord.rtgov.analytics.situation.Situation;
import org.overlord.rtgov.analytics.situation.store.SituationStore;
import org.overlord.rtgov.analytics.situation.store.SituationsQuery;
import org.overlord.rtgov.analytics.situation.store.AbstractSituationStore;
import org.overlord.rtgov.analytics.situation.store.ResolutionState;
import org.overlord.rtgov.analytics.util.SituationUtil;
import org.overlord.rtgov.common.elasticsearch.ElasticsearchClient;
import org.overlord.rtgov.common.util.RTGovProperties;
/**
* This class provides the Elastcsearch based implementation of the SituationsStore
* interface.
*
*/
public class ElasticsearchSituationStore extends AbstractSituationStore implements SituationStore {
private static final Logger LOG = Logger.getLogger(ElasticsearchSituationStore.class.getName());
private static String SITUATIONSTORE_UNIT_INDEX = "SituationStore.Elasticsearch.index";
private static String SITUATIONSTORE_UNIT_TYPE = "SituationStore.Elasticsearch.type";
private static String SITUATIONSTORE_RESPONSE_SIZE = "SituationStore.Elasticsearch.responseSize";
private static String SITUATIONSTORE_TIMEOUT = "SituationStore.Elasticsearch.timeout";
private static final int PROPERTY_VALUE_MAX_LENGTH = 250;
private static int DEFAULT_RESPONSE_SIZE = 100000;
private static long DEFAULT_TIMEOUT = 10000L;
private int _responseSize;
private long _timeout;
private ElasticsearchClient _client=new ElasticsearchClient();
/**
* Constructor.
*/
public ElasticsearchSituationStore() {
}
/**
* Initialize the situation store.
*/
@ServiceInit
public void init() {
_client.setIndex(RTGovProperties.getProperty(SITUATIONSTORE_UNIT_INDEX, "rtgov"));
_client.setType(RTGovProperties.getProperty(SITUATIONSTORE_UNIT_TYPE, "situation"));
_responseSize = RTGovProperties.getPropertyAsInteger(SITUATIONSTORE_RESPONSE_SIZE, DEFAULT_RESPONSE_SIZE);
_timeout = RTGovProperties.getPropertyAsLong(SITUATIONSTORE_TIMEOUT, DEFAULT_TIMEOUT);
try {
_client.init();
} catch (Exception e) {
LOG.log(Level.SEVERE, java.util.PropertyResourceBundle
.getBundle("situation-store-elasticsearch.Messages").getString("SITUATION-STORE-ELASTICSEARCH-1"), e);
}
}
/**
* This method sets the response size.
*
* @param size The size
*/
protected void setResponseSize(int size) {
_responseSize = size;
}
/**
* This method sets the response size.
*
* @return The size
*/
protected int getResponseSize() {
return (_responseSize);
}
/**
* {@inheritDoc}
*/
protected void doStore(final Situation situation) throws Exception {
if (_client != null) {
_client.add(situation.getId(), ElasticsearchClient.convertTypeToJson(situation));
}
}
/**
* {@inheritDoc}
*/
protected Situation doGetSituation(final String id) {
Situation ret=null;
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Get situation: "+id); //$NON-NLS-1$
}
if (_client != null) {
String json=_client.get(id);
if (json != null) {
try {
ret = SituationUtil.deserializeSituation(json.getBytes());
} catch (Exception e) {
LOG.log(Level.SEVERE, java.util.PropertyResourceBundle
.getBundle("situation-store-elasticsearch.Messages").getString("SITUATION-STORE-ELASTICSEARCH-2"), e);
}
}
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Situation="+ret); //$NON-NLS-1$
}
return (ret);
}
/**
* {@inheritDoc}
*/
public List<Situation> getSituations(final SituationsQuery sitQuery) {
List<Situation> situations = new java.util.ArrayList<Situation>();
SearchResponse response=_client.getElasticsearchClient().prepareSearch(_client.getIndex())
.setTypes(_client.getType())
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setTimeout(TimeValue.timeValueMillis(_timeout))
.setSize(_responseSize)
.setQuery(getQueryBuilder(sitQuery))
.execute().actionGet();
long num=response.getHits().getTotalHits();
if (num > _responseSize) {
num = _responseSize;
}
for (int i=0; i < num; i++) {
SearchHit hit=response.getHits().getAt(i);
try {
situations.add(SituationUtil.deserializeSituation(hit.getSourceAsString().getBytes()));
} catch (Exception e) {
LOG.log(Level.SEVERE, java.util.PropertyResourceBundle
.getBundle("situation-store-elasticsearch.Messages").getString("SITUATION-STORE-ELASTICSEARCH-2"), e);
}
}
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Situations="+situations); //$NON-NLS-1$
}
return (situations);
}
protected QueryBuilder getQueryBuilder(SituationsQuery sitQuery) {
QueryBuilder qb=QueryBuilders.matchAllQuery();
FilterBuilder filter=null;
if (sitQuery != null) {
BoolQueryBuilder bool=QueryBuilders.boolQuery();
if (!isNullOrEmpty(sitQuery.getResolutionState())) {
if (sitQuery.getResolutionState().equalsIgnoreCase(ResolutionState.UNRESOLVED.name())) {
filter = FilterBuilders.missingFilter("properties."+SituationStore.RESOLUTION_STATE_PROPERTY);
} else if (sitQuery.getResolutionState().equalsIgnoreCase(ResolutionState.OPEN.name())) {
bool.mustNot(QueryBuilders.matchQuery("properties."+SituationStore.RESOLUTION_STATE_PROPERTY, ResolutionState.RESOLVED.name()));
} else {
bool.must(QueryBuilders.matchQuery("properties."+SituationStore.RESOLUTION_STATE_PROPERTY, sitQuery.getResolutionState()));
}
}
if (sitQuery.getProperties() != null && !sitQuery.getProperties().isEmpty()) {
Set<Entry<Object,Object>> entrySet = sitQuery.getProperties().entrySet();
for (Entry<Object, Object> entry : entrySet) {
Object key = entry.getKey();
Object value = entry.getValue();
if (value instanceof String) {
bool.must(QueryBuilders.fuzzyLikeThisFieldQuery("properties."+key).likeText((String)value));
} else {
bool.must(QueryBuilders.matchQuery("properties."+key, value));
}
}
}
if (!isNullOrEmpty(sitQuery.getDescription())) {
bool.must(QueryBuilders.fuzzyLikeThisFieldQuery("description").likeText(sitQuery.getDescription()));
}
if (!isNullOrEmpty(sitQuery.getSubject())) {
bool.must(QueryBuilders.fuzzyLikeThisFieldQuery("subject").likeText(sitQuery.getSubject()));
}
if (!isNullOrEmpty(sitQuery.getType())) {
bool.must(QueryBuilders.fuzzyLikeThisFieldQuery("type").likeText(sitQuery.getType()));
}
if (sitQuery.getSeverity() != null) {
bool.must(QueryBuilders.matchQuery("severity", sitQuery.getSeverity().name()));
}
if (sitQuery.getFromTimestamp() > 0 || sitQuery.getToTimestamp() > 0) {
long from=(sitQuery.getFromTimestamp() > 0 ? sitQuery.getFromTimestamp() : 0);
long to=(sitQuery.getToTimestamp() > 0 ? sitQuery.getToTimestamp() : System.currentTimeMillis()+2000);
bool.must(QueryBuilders.rangeQuery("timestamp").from(from).to(to));
}
if (bool.hasClauses()) {
qb = bool;
}
}
if (filter != null) {
return (QueryBuilders.filteredQuery(qb, filter));
}
return (qb);
}
/**
* Check if the supplied string is null or empty (after removing
* whitespaces).
*
* @param str The string to test
* @return Whether the supplied string is null or empty
*/
protected boolean isNullOrEmpty(String str) {
return (str == null || str.trim().length() == 0);
}
/**
* {@inheritDoc}
*/
public void assignSituation(final String situationId, final String userName) {
Situation sit=getSituation(situationId);
if (sit != null) {
doAssignSituation(sit, userName);
// Save the updated situation
_client.update(situationId, ElasticsearchClient.convertTypeToJson(sit));
}
}
/**
* {@inheritDoc}
*/
public void unassignSituation(final String situationId) {
Situation sit=getSituation(situationId);
if (sit != null) {
doUnassignSituation(sit);
// Save the updated situation
_client.update(situationId, ElasticsearchClient.convertTypeToJson(sit));
}
}
/**
* {@inheritDoc}
*/
public void updateResolutionState(final String situationId, final ResolutionState resolutionState) {
Situation sit=getSituation(situationId);
if (sit != null) {
doUpdateResolutionState(sit, resolutionState);
// Save the updated situation
_client.update(situationId, ElasticsearchClient.convertTypeToJson(sit));
}
}
@Override
public void recordSuccessfulResubmit(final String situationId, final String userName) {
Situation sit=getSituation(situationId);
if (sit != null) {
doRecordSuccessfulResubmit(sit, userName);
// Save the updated situation
_client.update(situationId, ElasticsearchClient.convertTypeToJson(sit));
}
}
@Override
public void recordResubmitFailure(final String situationId, final String errorMessage, final String userName) {
Situation sit=getSituation(situationId);
if (sit != null) {
String message = (errorMessage == null ? "" : errorMessage);
if (message.length() > PROPERTY_VALUE_MAX_LENGTH) {
message = message.substring(0, PROPERTY_VALUE_MAX_LENGTH);
}
doRecordResubmitFailure(sit, message, userName);
// Save the updated situation
_client.update(situationId, ElasticsearchClient.convertTypeToJson(sit));
}
}
/**
* This method deletes the supplied situation.
*
* @param situation The situation
*/
protected void doDelete(final Situation situation) {
if (_client != null) {
try {
_client.remove(situation.getId());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
/**
* This method returns the elasticsearch client.
*
* @return The client
*/
protected ElasticsearchClient getClient() {
return (_client);
}
/**
* Close the situation store.
*/
@ServiceClose
public void close() {
if (_client != null) {
_client.close();
_client = null;
}
}
}