All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.cloud.autoscaling.HttpTriggerListener Maven / Gradle / Ivy

There is a newer version: 9.6.1
Show newest version
/*
 * 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.solr.cloud.autoscaling;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.StringJoiner;

import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventProcessorStage;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Simple HTTP callback that POSTs event data to a URL.
 * URL, payload and headers may contain property substitution patterns, with the following properties available:
 * 
    *
  • config.* - listener configuration
  • *
  • event.* - event properties
  • *
  • stage - current stage of event processing
  • *
  • actionName - optional current action name
  • *
  • context.* - optional {@link ActionContext} properties
  • *
  • error - optional error string (from {@link Throwable#toString()})
  • *
  • message - optional message
  • *
* The following listener configuration is supported: *
    *
  • url - a URL template
  • *
  • payload - string, optional payload template. If absent a JSON map of all properties listed above will be used.
  • *
  • contentType - string, optional payload content type. If absent then application/json will be used.
  • *
  • header.* - string, optional header template(s). The name of the property without "header." prefix defines the literal header name.
  • *
  • timeout - int, optional connection and socket timeout in milliseconds. Default is 60 seconds.
  • *
  • followRedirects - boolean, optional setting to follow redirects. Default is false.
  • *
*/ public class HttpTriggerListener extends TriggerListenerBase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private String urlTemplate; private String payloadTemplate; private String contentType; private Map headerTemplates = new HashMap<>(); private int timeout = HttpClientUtil.DEFAULT_CONNECT_TIMEOUT; private boolean followRedirects; public HttpTriggerListener() { super(); TriggerUtils.requiredProperties(requiredProperties, validProperties, "url"); TriggerUtils.validProperties(validProperties, "payload", "contentType", "timeout", "followRedirects"); validPropertyPrefixes.add("header."); } @Override public void configure(SolrResourceLoader loader, SolrCloudManager cloudManager, AutoScalingConfig.TriggerListenerConfig config) throws TriggerValidationException { super.configure(loader, cloudManager, config); urlTemplate = (String)config.properties.get("url"); payloadTemplate = (String)config.properties.get("payload"); contentType = (String)config.properties.get("contentType"); config.properties.forEach((k, v) -> { if (k.startsWith("header.")) { headerTemplates.put(k.substring(7), String.valueOf(v)); } }); timeout = PropertiesUtil.toInteger(String.valueOf(config.properties.get("timeout")), HttpClientUtil.DEFAULT_CONNECT_TIMEOUT); followRedirects = PropertiesUtil.toBoolean(String.valueOf(config.properties.get("followRedirects"))); } @Override public void onEvent(TriggerEvent event, TriggerEventProcessorStage stage, String actionName, ActionContext context, Throwable error, String message) { Properties properties = new Properties(); properties.setProperty("stage", stage.toString()); // if configuration used "actionName" but we're in a non-action related stage then PropertiesUtil will // throws an exception on missing value - so replace it with an empty string if (actionName == null) { actionName = ""; } properties.setProperty("actionName", actionName); if (context != null) { context.getProperties().forEach((k, v) -> { properties.setProperty("context." + k, String.valueOf(v)); }); } if (error != null) { properties.setProperty("error", error.toString()); } else { properties.setProperty("error", ""); } if (message != null) { properties.setProperty("message", message); } else { properties.setProperty("message", ""); } // add event properties properties.setProperty("event.id", event.getId()); properties.setProperty("event.source", event.getSource()); properties.setProperty("event.eventTime", String.valueOf(event.eventTime)); properties.setProperty("event.eventType", event.getEventType().toString()); event.getProperties().forEach((k, v) -> { properties.setProperty("event.properties." + k, String.valueOf(v)); }); // add config properties properties.setProperty("config.name", config.name); properties.setProperty("config.trigger", config.trigger); properties.setProperty("config.listenerClass", config.listenerClass); properties.setProperty("config.beforeActions", String.join(",", config.beforeActions)); properties.setProperty("config.afterActions", String.join(",", config.afterActions)); StringJoiner joiner = new StringJoiner(","); config.stages.forEach(s -> joiner.add(s.toString())); properties.setProperty("config.stages", joiner.toString()); config.properties.forEach((k, v) -> { properties.setProperty("config.properties." + k, String.valueOf(v)); }); String url = PropertiesUtil.substituteProperty(urlTemplate, properties); String payload; String type; if (payloadTemplate != null) { payload = PropertiesUtil.substituteProperty(payloadTemplate, properties); if (contentType != null) { type = contentType; } else { type = "application/json"; } } else { payload = Utils.toJSONString(properties); type = "application/json"; } Map headers = new HashMap<>(); headerTemplates.forEach((k, v) -> { String headerVal = PropertiesUtil.substituteProperty(v, properties); if (!headerVal.isEmpty()) { headers.put(k, headerVal); } }); headers.put("Content-Type", type); try { cloudManager.httpRequest(url, SolrRequest.METHOD.POST, headers, payload, timeout, followRedirects); } catch (IOException e) { log.warn("Exception sending request for event " + event, e); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy