Допустим, что мы используем Apache NiFi и в нашем потоке передачи данных мы встали перед необходимостью реализации сложной логики, требующей вызова дополнительных подзапросов к сторонним API внутри Groovy-скрипта. В силу некоторых причин мы не хотим (или не можем) разделять логику на несколько скриптов, связанных между собой встроенными NiFi-процессорами InvokeHttp для получения/отправки данных. В подобном случае при дополнительном условии, что взаимодействие со сторонней системой происходит по безопасному протоколу https, мы неизбежно сталкиваемся со специфичными для NiFi тонкостями отправки запросов в Groovy. Если мы говорим об отправке запросов в языке Groovy безотносительно NiFi, то источников информации на эту тему довольно много. Если говорить об особенностях NiFi, то информации оказывается чуть меньше и порой она несколько противоречива. В связи с этим приведем упрощенный фрагмент кода, который помог нам отправить https запрос прямо в коде Groovy-скрипта:
import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets
import org.apache.nifi.ssl.SSLContextService
import javax.net.ssl.HttpsURLConnection
import org.apache.nifi.ssl.SSLContextService.ClientAuth
import org.apache.nifi.controller.ControllerService
def flowFile = session.get()
if (!flowFile) return
def requestHttps(sslContext) {
def url ="https://#{external_url}:#{external_port}/something"
def get = new URL(url).openConnection() as HttpsURLConnection
get.setSSLSocketFactory(sslContext.getSocketFactory())
get.setDoOutput(true)
get.setRequestProperty("Accept", "application/json")
get.setRequestProperty("Content-Type", "application/json")
get.setRequestProperty("Host", "#{external_url}:#{external_port}")
def w_getResponseCode = get.getResponseCode();
if (w_getResponseCode.equals(200)) {
return get.getInputStream().getText()
} else {
return get.getErrorStream().getText()
}
}
try {
flowFile = session.write(flowFile,
{ inputStream, outputStream ->
def lookup = context.controllerServiceLookup
def serviceId = lookup.getControllerServiceIdentifiers(ControllerService).find {
cs -> lookup.getControllerServiceName(cs) == 'StandardRestrictedSSLContextService'
}
def sslContextService = lookup.getControllerService(serviceId)
def sslContext = sslContextService.createContext()
def result = requestHttps(sslContext)
outputStream.write(result.getBytes(StandardCharsets.UTF_8))
} as StreamCallback)
session.transfer(flowFile, REL_SUCCESS)
} catch (Exception e) {
session.transfer(flowFile, REL_FAILURE)
}
Самое важное место здесь -- это получение SSL контекста. В NiFi мы используем controllerServiceLookup чтобы "подглядеть" идентификатор Controller service'а, ответственного за SSL контекст. Ищем этот сервис мы по его имени "StandardRestrictedSSLContextService". Напомним, что создать и настроить данный сервис необходимо самостоятельно. Далее по id мы можем получить данный сервис и проинициализировать его в коде, получая тем самым возможность слать запросы, даже если сервер поддерживает взаимодействие только по https.