| /******************************************************************************** |
| * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| ********************************************************************************/ |
| |
| package org.eclipse.mdm.freetextindexer.boundary; |
| |
| import java.io.IOException; |
| import java.text.SimpleDateFormat; |
| |
| import javax.ejb.Stateless; |
| import javax.ejb.TransactionAttribute; |
| import javax.ejb.TransactionAttributeType; |
| import javax.inject.Inject; |
| |
| import org.apache.commons.httpclient.HttpClient; |
| import org.apache.commons.httpclient.HttpMethod; |
| import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; |
| import org.apache.commons.httpclient.methods.DeleteMethod; |
| import org.apache.commons.httpclient.methods.GetMethod; |
| import org.apache.commons.httpclient.methods.PutMethod; |
| import org.eclipse.mdm.freetextindexer.entities.MDMEntityResponse; |
| import org.eclipse.mdm.property.GlobalProperty; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| |
| /** |
| * This Boundary is back-end only to the ElasticSearch Server. It is responsible |
| * for the actual indexing work. |
| * |
| * @author CWE |
| * |
| */ |
| @TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) |
| @Stateless |
| public class ElasticsearchBoundary { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchBoundary.class); |
| |
| private ObjectMapper jsonMapper; |
| private HttpClient client; |
| |
| @Inject |
| @GlobalProperty(value = "elasticsearch.url") |
| String esAddress; |
| |
| @Inject |
| @GlobalProperty(value = "freetext.active") |
| String active; |
| |
| /** |
| * Connects to the ElasticSearch Server |
| * |
| * @throws IOException |
| */ |
| public ElasticsearchBoundary() { |
| jsonMapper = new ObjectMapper(); |
| jsonMapper.setDateFormat(new SimpleDateFormat("yyyyMMdd'T'HHmmssZ")); |
| client = new HttpClient(); |
| } |
| |
| public void index(MDMEntityResponse document) { |
| try { |
| PutMethod put = new PutMethod(esAddress + getPath(document) + "?ignore_conflicts=true"); |
| |
| byte[] json = jsonMapper.writeValueAsBytes(document); |
| LOGGER.trace("Document {}: {}", getPath(document), new String(json)); |
| |
| put.setRequestEntity(new ByteArrayRequestEntity(json, "application/json")); |
| |
| execute(put); |
| |
| } catch (IOException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| private String getPath(MDMEntityResponse document) { |
| return document.source.toLowerCase() + "/" + document.type + "/" + document.id; |
| } |
| |
| private void execute(HttpMethod put) { |
| try { |
| int status = client.executeMethod(put); |
| |
| checkError(status, put); |
| } catch (IOException e) { |
| throw new IllegalStateException("Problems querying ElasticSearch.", e); |
| } |
| } |
| |
| private void checkError(int status, HttpMethod method) { |
| String text = String.format("ElasticSearch answered %d. ", status); |
| |
| int httpCategory = status / 100; |
| switch (httpCategory) { |
| case 4: |
| text = text + "This indicates a Client error: "; |
| break; |
| case 5: |
| text = text + "This indicates a Server error. The ES instance must be checked (" + esAddress + "): "; |
| break; |
| } |
| |
| try { |
| if (httpCategory != 2) { |
| throw new IllegalStateException(text + method.getResponseBodyAsString()); |
| } |
| } catch (IOException e) { |
| throw new IllegalStateException(text + "\nError occured during reading the elastic search response!", e); |
| } |
| } |
| |
| public void delete(String api, String type, String id) { |
| String path = api.toLowerCase() + "/" + type + "/" + id; |
| DeleteMethod put = new DeleteMethod(esAddress + path); |
| |
| execute(put); |
| |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug("Document '{}' has been deleted!", path); |
| } |
| } |
| |
| public boolean hasIndex(String source) { |
| boolean hasIndex = false; |
| |
| if (active()) { |
| try { |
| GetMethod get = new GetMethod(esAddress + source.toLowerCase()); |
| int status = client.executeMethod(get); |
| LOGGER.info("Checking index {}: {}", source, status); |
| |
| hasIndex = status / 100 == 2; |
| } catch (IOException e) { |
| LOGGER.warn("Querying ElasticSearch for the Index failed... Assuming no index is there!", e); |
| hasIndex = false; |
| } |
| } |
| |
| return hasIndex; |
| } |
| |
| public void createIndex(String source) { |
| if (Boolean.valueOf(active)) { |
| execute(new PutMethod(esAddress + source.toLowerCase())); |
| LOGGER.info("New Index created!"); |
| } |
| } |
| |
| public void validateConnectionIsPossible() { |
| if (active()) { |
| try { |
| GetMethod get = new GetMethod(esAddress); |
| int status = client.executeMethod(get); |
| if (status / 100 != 2) { |
| LOGGER.error( |
| "Cannot connect to elasticsearch at {} but free text search is enabled! http status was: {}", |
| esAddress, status); |
| throw new RuntimeException("Cannot connect to elasticsearch."); |
| } else { |
| LOGGER.info("Successfully connected to elasticsearch!"); |
| } |
| |
| } catch (IOException e) { |
| LOGGER.error("Cannot connect to elasticsearch at {} but free text search is enabled!", esAddress, e); |
| throw new RuntimeException("Cannot connect to elasticsearch.", e); |
| } |
| } |
| } |
| |
| public boolean active() { |
| return Boolean.valueOf(active); |
| } |
| } |