/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.camel.component.elasticsearch5; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.impl.DefaultProducer; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.MultiGetRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents an Elasticsearch producer. */ public class ElasticsearchProducer extends DefaultProducer { private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchProducer.class); protected final ElasticsearchConfiguration configuration; private TransportClient client; public ElasticsearchProducer(ElasticsearchEndpoint endpoint, ElasticsearchConfiguration configuration) { super(endpoint); this.configuration = configuration; } private ElasticsearchOperation resolveOperation(Exchange exchange) { // 1. Operation can be driven by either (in order of preference): // a. If the body is an ActionRequest the operation is set by the type // of request. // b. If the body is not an ActionRequest, the operation is set by the // header if it exists. // c. If neither the operation can not be derived from the body or // header, the configuration is used. // In the event we can't discover the operation from a, b or c we throw // an error. Object request = exchange.getIn().getBody(); if (request instanceof IndexRequest) { return ElasticsearchOperation.INDEX; } else if (request instanceof GetRequest) { return ElasticsearchOperation.GET_BY_ID; } else if (request instanceof MultiGetRequest) { return ElasticsearchOperation.MULTIGET; } else if (request instanceof UpdateRequest) { return ElasticsearchOperation.UPDATE; } else if (request instanceof BulkRequest) { // do we want bulk or bulk_index? if (configuration.getOperation() == ElasticsearchOperation.BULK_INDEX) { return configuration.getOperation().BULK_INDEX; } else { return configuration.getOperation().BULK; } } else if (request instanceof DeleteRequest) { return ElasticsearchOperation.DELETE; } else if (request instanceof SearchRequest) { return ElasticsearchOperation.SEARCH; } else if (request instanceof MultiSearchRequest) { return ElasticsearchOperation.MULTISEARCH; } else if (request instanceof DeleteIndexRequest) { return ElasticsearchOperation.DELETE_INDEX; } ElasticsearchOperation operationConfig = exchange.getIn().getHeader(ElasticsearchConstants.PARAM_OPERATION, ElasticsearchOperation.class); if (operationConfig == null) { operationConfig = configuration.getOperation(); } if (operationConfig == null) { throw new IllegalArgumentException(ElasticsearchConstants.PARAM_OPERATION + " value '" + operationConfig + "' is not supported"); } return operationConfig; } public void process(Exchange exchange) throws Exception { // 2. Index and type will be set by: // a. If the incoming body is already an action request // b. If the body is not an action request we will use headers if they // are set. // c. If the body is not an action request and the headers aren't set we // will use the configuration. // No error is thrown by the component in the event none of the above // conditions are met. The java es client // will throw. Message message = exchange.getIn(); final ElasticsearchOperation operation = resolveOperation(exchange); // Set the index/type headers on the exchange if necessary. This is used // for type conversion. boolean configIndexName = false; String indexName = message.getHeader(ElasticsearchConstants.PARAM_INDEX_NAME, String.class); if (indexName == null) { message.setHeader(ElasticsearchConstants.PARAM_INDEX_NAME, configuration.getIndexName()); configIndexName = true; } boolean configIndexType = false; String indexType = message.getHeader(ElasticsearchConstants.PARAM_INDEX_TYPE, String.class); if (indexType == null) { message.setHeader(ElasticsearchConstants.PARAM_INDEX_TYPE, configuration.getIndexType()); configIndexType = true; } boolean configWaitForActiveShards = false; Integer waitForActiveShards = message.getHeader(ElasticsearchConstants.PARAM_WAIT_FOR_ACTIVE_SHARDS, Integer.class); if (waitForActiveShards == null) { message.setHeader(ElasticsearchConstants.PARAM_WAIT_FOR_ACTIVE_SHARDS, configuration.getWaitForActiveShards()); configWaitForActiveShards = true; } if (operation == ElasticsearchOperation.INDEX) { IndexRequest indexRequest = message.getBody(IndexRequest.class); message.setBody(client.index(indexRequest).actionGet().getId()); } else if (operation == ElasticsearchOperation.UPDATE) { UpdateRequest updateRequest = message.getBody(UpdateRequest.class); message.setBody(client.update(updateRequest).actionGet().getId()); } else if (operation == ElasticsearchOperation.GET_BY_ID) { GetRequest getRequest = message.getBody(GetRequest.class); message.setBody(client.get(getRequest)); } else if (operation == ElasticsearchOperation.MULTIGET) { MultiGetRequest multiGetRequest = message.getBody(MultiGetRequest.class); message.setBody(client.multiGet(multiGetRequest)); } else if (operation == ElasticsearchOperation.BULK) { BulkRequest bulkRequest = message.getBody(BulkRequest.class); message.setBody(client.bulk(bulkRequest).actionGet()); } else if (operation == ElasticsearchOperation.BULK_INDEX) { BulkRequest bulkRequest = message.getBody(BulkRequest.class); List<String> indexedIds = new ArrayList<String>(); for (BulkItemResponse response : client.bulk(bulkRequest).actionGet().getItems()) { indexedIds.add(response.getId()); } message.setBody(indexedIds); } else if (operation == ElasticsearchOperation.DELETE) { DeleteRequest deleteRequest = message.getBody(DeleteRequest.class); message.setBody(client.delete(deleteRequest).actionGet()); } else if (operation == ElasticsearchOperation.EXISTS) { // ExistsRequest API is deprecated, using SearchRequest instead with size=0 and terminate_after=1 SearchRequest searchRequest = new SearchRequest(exchange.getIn().getHeader(ElasticsearchConstants.PARAM_INDEX_NAME, String.class)); try { client.prepareSearch(searchRequest.indices()).setSize(0).setTerminateAfter(1).get(); message.setBody(true); } catch (IndexNotFoundException e) { message.setBody(false); } } else if (operation == ElasticsearchOperation.SEARCH) { SearchRequest searchRequest = message.getBody(SearchRequest.class); message.setBody(client.search(searchRequest).actionGet()); } else if (operation == ElasticsearchOperation.MULTISEARCH) { MultiSearchRequest multiSearchRequest = message.getBody(MultiSearchRequest.class); message.setBody(client.multiSearch(multiSearchRequest)); } else if (operation == ElasticsearchOperation.DELETE_INDEX) { DeleteIndexRequest deleteIndexRequest = message.getBody(DeleteIndexRequest.class); message.setBody(client.admin().indices().delete(deleteIndexRequest).actionGet()); } else { throw new IllegalArgumentException(ElasticsearchConstants.PARAM_OPERATION + " value '" + operation + "' is not supported"); } // If we set params via the configuration on this exchange, remove them // now. This preserves legacy behavior for this component and enables a // use case where one message can be sent to multiple elasticsearch // endpoints where the user is relying on the endpoint configuration // (index/type) rather than header values. If we do not clear this out // sending the same message (index request, for example) to multiple // elasticsearch endpoints would have the effect overriding any // subsequent endpoint index/type with the first endpoint index/type. if (configIndexName) { message.removeHeader(ElasticsearchConstants.PARAM_INDEX_NAME); } if (configIndexType) { message.removeHeader(ElasticsearchConstants.PARAM_INDEX_TYPE); } if (configWaitForActiveShards) { message.removeHeader(ElasticsearchConstants.PARAM_WAIT_FOR_ACTIVE_SHARDS); } } @Override @SuppressWarnings("unchecked") protected void doStart() throws Exception { super.doStart(); if (client == null) { LOG.info("Connecting to the ElasticSearch cluster: " + configuration.getClusterName()); if (configuration.getIp() != null) { client = new PreBuiltTransportClient(getSettings()) .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(configuration.getIp()), configuration.getPort())); } else if (configuration.getTransportAddressesList() != null && !configuration.getTransportAddressesList().isEmpty()) { List<TransportAddress> addresses = new ArrayList<TransportAddress>(configuration.getTransportAddressesList().size()); for (TransportAddress address : configuration.getTransportAddressesList()) { addresses.add(address); } client = new PreBuiltTransportClient(getSettings()).addTransportAddresses(addresses.toArray(new TransportAddress[addresses.size()])); } else { LOG.info("Incorrect ip address and port parameters settings for ElasticSearch cluster"); } } } private Settings getSettings() { return Settings.builder() .put("cluster.name", configuration.getClusterName()) .put("client.transport.ignore_cluster_name", false) .put("client.transport.sniff", configuration.getClientTransportSniff()) .build(); } @Override protected void doStop() throws Exception { if (client != null) { LOG.info("Disconnecting from ElasticSearch cluster: " + configuration.getClusterName()); client.close(); client = null; } super.doStop(); } }