package fi.otavanopisto.muikku.plugins.search; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JSR310Module; import fi.otavanopisto.muikku.controller.PluginSettingsController; import fi.otavanopisto.muikku.search.IndexableEntityVault; import fi.otavanopisto.muikku.search.SearchIndexUpdater; import fi.otavanopisto.muikku.search.annotations.Indexable; import fi.otavanopisto.muikku.search.annotations.IndexableFieldMultiField; import fi.otavanopisto.muikku.search.annotations.IndexableFieldOption; @ApplicationScoped public class ElasticSearchIndexUpdater implements SearchIndexUpdater { @Inject private Logger logger; @Inject private PluginSettingsController pluginSettingsController; @Override public void init() { /* Builder settings = nodeBuilder().settings(); settings.put("cluster.routing.allocation.disk.watermark.high", "99%"); settings.put("path.home", "./"); node = nodeBuilder() .settings(settings) .local(true) .node(); elasticClient = node.client();*/ String clusterName = pluginSettingsController.getPluginSetting("elastic-search", "clusterName"); if (clusterName == null) { clusterName = System.getProperty("elasticsearch.cluster.name"); } if (clusterName == null) { clusterName = "elasticsearch"; } String portNumberProperty = System.getProperty("elasticsearch.node.port"); int portNumber; if (portNumberProperty != null) { portNumber = Integer.decode(portNumberProperty); } else { portNumber = 9300; } Settings settings = Settings.settingsBuilder() .put("cluster.name", clusterName).build(); try { elasticClient = TransportClient.builder().settings(settings).build() .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), portNumber)); } catch (UnknownHostException e) { logger.log(Level.SEVERE, "Failed to connect to elasticsearch cluster", e); return; } if (!indexExists()) { createIndex(); } for (Indexable indexable : IndexableEntityVault.getEntities()) { String propertyName = indexable.name(); Map<String, ElasticMappingProperty> properties = new HashMap<>(); if (StringUtils.isNotBlank(propertyName)) { IndexableFieldOption[] fieldOptions = indexable.options(); if (fieldOptions != null) { for (IndexableFieldOption fieldOption : fieldOptions) { switch (fieldOption.type()) { case "multi_field": if (StringUtils.isNotBlank(fieldOption.type())) { IndexableFieldMultiField[] multiFields = fieldOption.multiFields(); Map<String, ElasticMappingPropertyOptionField> fields = null; if (multiFields != null && multiFields.length > 0) { fields = new HashMap<>(); for (IndexableFieldMultiField multiField : multiFields) { fields.put(multiField.name(), new ElasticMappingPropertyOptionField(multiField.type(), multiField.index())); } } properties.put(fieldOption.name(), new ElasticMappingMultifieldProperty(fields)); } break; case "string": properties.put(fieldOption.name(), new ElasticMappingStringProperty(fieldOption.index())); break; default: logger.severe(String.format("Unknown field type %s", fieldOption.type())); break; } } } if (!properties.isEmpty()) { updateMapping(propertyName, new ElasticMappingProperties(properties)); } } } } private void createIndex() { elasticClient .admin() .indices() .prepareCreate("muikku") .execute() .actionGet(); } private boolean indexExists() { return elasticClient .admin() .indices() .prepareExists("muikku") .execute() .actionGet() .isExists(); } private void updateMapping(String propertyName, ElasticMappingProperties properties) { try { Map<String, ElasticMappingProperties> mappings = new HashMap<>(); mappings.put(propertyName, properties); String mapping = new ObjectMapper() .writeValueAsString(mappings); elasticClient.admin().indices() .preparePutMapping("muikku") .setType(propertyName) .setSource(mapping) .execute() .actionGet(); } catch (IOException e) { logger.severe("Failed to update ElasticSearch mappings"); } } public static class ElasticMappingProperties { public ElasticMappingProperties(Map<String, ElasticMappingProperty> properties) { super(); this.properties = properties; } public Map<String, ElasticMappingProperty> getProperties() { return properties; } private Map<String, ElasticMappingProperty> properties; } public abstract static class ElasticMappingProperty { public ElasticMappingProperty(String type) { this.type = type; } public String getType() { return type; } private String type; } public static class ElasticMappingStringProperty extends ElasticMappingProperty { public ElasticMappingStringProperty(String index) { super("string"); this.index = index; } public String getIndex() { return index; } private String index; } public static class ElasticMappingMultifieldProperty extends ElasticMappingProperty { public ElasticMappingMultifieldProperty(Map<String, ElasticMappingPropertyOptionField> fields) { super("multi_field"); this.fields = fields; } public Map<String, ElasticMappingPropertyOptionField> getFields() { return fields; } private Map<String, ElasticMappingPropertyOptionField> fields; } public static class ElasticMappingPropertyOptionField { public ElasticMappingPropertyOptionField(String type, String index) { super(); this.type = type; this.index = index; } public String getIndex() { return index; } public String getType() { return type; } private String type; private String index; } @Override public void deinit() { elasticClient.close(); //node.close(); } @Override public void addOrUpdateIndex(String typeName, Map<String, Object> entity) { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JSR310Module()); String json; try { json = mapper.writeValueAsString(entity); String id = entity.get("id").toString(); @SuppressWarnings("unused") IndexResponse response = elasticClient.prepareIndex("muikku", typeName, id).setSource(json).execute().actionGet(); } catch (IOException e) { logger.log(Level.WARNING, "Adding to index failed because of exception", e); } } @Override public void deleteFromIndex(String typeName, String id) { @SuppressWarnings("unused") DeleteResponse response = elasticClient.prepareDelete("muikku", typeName, id).execute().actionGet(); } @Override public String getName() { return "elastic-search"; } private Client elasticClient; //private Node node; }