package com.codetroopers.play.elasticsearch;
import com.codetroopers.play.elasticsearch.annotations.IndexMapping;
import com.codetroopers.play.elasticsearch.annotations.IndexName;
import com.codetroopers.play.elasticsearch.annotations.IndexType;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.SettingsException;
import org.reflections.Reflections;
import play.Application;
import play.Configuration;
import play.Logger;
import play.libs.Json;
import play.libs.ReflectionsCache;
import java.util.*;
/**
* User: nboire
*
* example :
* elasticsearch.local=false
* elasticsearch.client="192.168.0.46:9300"
* elasticsearch.cluster.name=myCluster
* elasticsearch.index.name=play2-elasticsearch
* elasticsearch.index.settings="{ analysis: { analyzer: { my_analyzer: { type: \"custom\", tokenizer: \"standard\" } } } }"
* elasticsearch.index.clazzs="indexing.*"
* elasticsearch.index.show_request=true
*/
public class IndexConfig {
/**
* elasticsearch.local= true / false
* Mode local or network
*/
public Boolean local = false;
/**
* elasticsearch.client.sniff = true / false
* Sniff for nodes.
*/
public Boolean sniffing = true;
/**
* elasticsearch.local.config = configuration file load on local mode.
* eg : conf/elasticsearch.yml
*/
public String localConfig = null;
/**
* elasticsearch.client = list of client separate by commas ex : 192.168.0.1:9300,192.168.0.2:9300
*/
public String client = null;
/**
* elasticsearch.cluster.name = name of the elasticsearch cluster
*/
public String clusterName = null;
/**
* Debug mode for log search request and response
*/
public Boolean showRequest = false;
/**
* The name of the index
*/
public String[] indexNames = new String[0];
/**
* Custom settings to apply when creating the index. ex: "{ analysis: { analyzer: { my_analyzer: { type : "custom", tokenizer: "standard" } } } }"
*/
public Map<String, String> indexSettings = new HashMap<>();
/**
* list of class extends "Index" ex: myPackage.myClass,myPackage2.*
*/
public String indexClazzs = null;
/**
* List of IndexType and IndexMapping associate
*/
public Map<IndexQueryPath, String> indexMappings = new HashMap<>();
/**
* Drop the index on application shutdown
* Should probably be used only in tests
*/
public boolean dropOnShutdown = false;
/**
* Play application
*/
public Application application;
public IndexConfig(Application app) {
this.application = app;
this.client = app.configuration().getString("elasticsearch.client");
this.sniffing = app.configuration().getBoolean("elasticsearch.sniff", true);
this.local = app.configuration().getBoolean("elasticsearch.local");
this.localConfig = app.configuration().getString("elasticsearch.config.resource");
this.clusterName = app.configuration().getString("elasticsearch.cluster.name");
this.showRequest = app.configuration().getBoolean("elasticsearch.index.show_request", false);
this.dropOnShutdown = app.configuration().getBoolean("elasticsearch.index.dropOnShutdown", false);
this.indexClazzs = app.configuration().getString("elasticsearch.index.clazzs");
String indexNameConf = app.configuration().getString("elasticsearch.index.name");
if(indexNameConf != null) {
LinkedList<String> indexNamesL = new LinkedList<>();
String[] indexNamesTab = indexNameConf.split(",");
for (String indexNameElem : indexNamesTab) {
String indexNameTmp = indexNameElem.trim();
indexNamesL.add(indexNameTmp);
}
indexNames = indexNamesL.toArray(indexNames);
for (String indexName : indexNames) {
// Load settings
loadSettingsFromConfig(indexName);
// Load Mapping from conf
loadMappingFromConfig(indexName);
}
} else {
Logger.info("ElasticSearch : no indexNames(s) defined in property 'elasticsearch.index.name'");
}
}
private void loadSettingsFromConfig(String indexName) {
String setting = application.configuration().getString("elasticsearch." + indexName + ".settings");
if(StringUtils.isNotEmpty(setting)) {
indexSettings.put(indexName, setting);
}
}
/**
* Load classes with @IndexType,@IndexName and initialize mapping if present on the @IndexMapping
*/
public void loadFromAnnotations() {
Set<String> classes = getClazzs();
for (String aClass : classes) {
Class<?> klass;
try {
// Loading class and annotation for set mapping if is present
Logger.debug("ElasticSearch : Registering class " + aClass);
klass = Class.forName(aClass, true, application.classloader());
Object o = klass.newInstance();
String indexType = getIndexType(o);
String indexMapping = getIndexMapping(o);
String indexName = getIndexName(o, indexNames);
if (indexType != null) {
IndexQueryPath path = new IndexQueryPath(indexName, indexType);
indexMappings.put(path, indexMapping);
}
} catch (Throwable e) {
Logger.error(e.getMessage());
}
}
}
/**
* Load additional mappings from config entry "elasticsearch.index.mapping"
* @param indexName
*/
private void loadMappingFromConfig(String indexName) {
Configuration mappingConfig = application.configuration().getConfig("elasticsearch." + indexName + ".mappings");
if (mappingConfig != null) {
Map<String, Object> mappings = mappingConfig.asMap();
for (String indexType : mappings.keySet()) {
IndexQueryPath indexQueryPath = new IndexQueryPath(indexName, indexType);
if (mappings.get(indexType) instanceof String) {
indexMappings.put(indexQueryPath, (String) mappings.get(indexType));
} else {
try {
indexMappings.put(indexQueryPath, Json.toJson(mappings.get(indexType)).toString());
} catch (Exception e) {
Logger.warn("Incorrect value in elasticsearch.index.mappings", e);
}
}
}
}
}
private static String getIndexName(Object instance, String[] indexNames) {
IndexName indexNameAnnotation = instance.getClass().getAnnotation(IndexName.class);
if (indexNameAnnotation == null) {
if(indexNames.length>0) {
return indexNames[0];
}
return null;
}
return indexNameAnnotation.name();
}
private static String getIndexType(Object instance) {
IndexType indexTypeAnnotation = instance.getClass().getAnnotation(IndexType.class);
if (indexTypeAnnotation == null) {
return null;
}
return indexTypeAnnotation.name();
}
private static String getIndexMapping(Object instance) {
IndexMapping indexMapping = instance.getClass().getAnnotation(IndexMapping.class);
if (indexMapping == null) {
return null;
}
return indexMapping.value();
}
private Set<String> getClazzs() {
Set<String> classes = new HashSet<>();
if (indexClazzs != null) {
String[] toLoad = indexClazzs.split(",");
for (String load : toLoad) {
load = load.trim();
if (load.endsWith(".*")) {
Reflections reflections = ReflectionsCache.getReflections(application.classloader(), load.substring(0, load.length() - 2));
for(Class c :reflections.getTypesAnnotatedWith(IndexName.class)){
classes.add(c.getName());
}
for(Class c :reflections.getTypesAnnotatedWith(IndexType.class)){
classes.add(c.getName());
}
} else {
classes.add(load);
}
}
}
return classes;
}
@Override
public String toString() {
return "IndexConfig{" +
"local=" + local +
", localConfig='" + localConfig + '\'' +
", clusterName='" + clusterName + '\'' +
", showRequest=" + showRequest +
", sniffing=" + sniffing +
", indexNames=" + (indexNames == null ? null : Arrays.asList(indexNames)) +
", indexSettings=" + indexSettings +
", indexClazzs='" + indexClazzs + '\'' +
", indexMappings=" + indexMappings +
", dropOnShutdown=" + dropOnShutdown +
'}';
}
/**
* Checks if is local mode.
*
* @return true, if is local mode
*/
public boolean isLocalMode() {
try {
if (client == null) {
return true;
}
if (client.equalsIgnoreCase("false") || client.equalsIgnoreCase("true")) {
return true;
}
return local;
} catch (Exception e) {
Logger.error("Error! Starting in Local Model: %s", e);
return true;
}
}
/**
* Load settings from resource file
*
* @return
* @throws Exception
*/
ImmutableSettings.Builder loadSettings() throws Exception {
ImmutableSettings.Builder settings = ImmutableSettings.settingsBuilder();
// set default settings
settings.put("client.transport.sniff", sniffing);
if (clusterName != null) {
settings.put("cluster.name", clusterName);
}
// load settings
if (localConfig != null) {
Logger.debug("Elasticsearch : Load settings from " + localConfig);
try {
settings.loadFromClasspath(localConfig);
} catch (SettingsException settingsException) {
Logger.error("Elasticsearch : Error when loading settings from " + localConfig);
throw new Exception(settingsException);
}
}
settings.build();
Logger.info("Elasticsearch : Settings " + settings.internalMap().toString());
return settings;
}
}