com.rabbitmq.http.client.ReactorNettyClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http-client Show documentation
Show all versions of http-client Show documentation
Java client for the RabbitMQ HTTP API
/*
* Copyright 2018 the original author or authors.
*
* Licensed 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 com.rabbitmq.http.client;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.http.client.domain.AlivenessTestResult;
import com.rabbitmq.http.client.domain.BindingInfo;
import com.rabbitmq.http.client.domain.ChannelInfo;
import com.rabbitmq.http.client.domain.ClusterId;
import com.rabbitmq.http.client.domain.ConnectionInfo;
import com.rabbitmq.http.client.domain.CurrentUserDetails;
import com.rabbitmq.http.client.domain.Definitions;
import com.rabbitmq.http.client.domain.ExchangeInfo;
import com.rabbitmq.http.client.domain.NodeInfo;
import com.rabbitmq.http.client.domain.OverviewResponse;
import com.rabbitmq.http.client.domain.PolicyInfo;
import com.rabbitmq.http.client.domain.QueueInfo;
import com.rabbitmq.http.client.domain.ShovelInfo;
import com.rabbitmq.http.client.domain.ShovelStatus;
import com.rabbitmq.http.client.domain.TopicPermissions;
import com.rabbitmq.http.client.domain.UserInfo;
import com.rabbitmq.http.client.domain.UserPermissions;
import com.rabbitmq.http.client.domain.VhostInfo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import org.reactivestreams.Publisher;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufFlux;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpClientResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
/**
* Reactive client based on Reactor Netty.
* Use the {@link ReactorNettyClientOptions} constructors for
* advanced settings, e.g. TLS, authentication other than HTTP basic, etc.
* The default settings for this class are the following:
*
* - {@link HttpClient}: created with the {@link HttpClient#baseUrl(String)}.
*
* -
* {@link ObjectMapper}:
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
and
* MapperFeature.DEFAULT_VIEW_INCLUSION
are disabled.
*
* Mono<String> token
: basic HTTP authentication used for the
* authorization
header.
*
* BiConsumer<? super HttpRequest, ? super HttpResponse> responseCallback
:
* 4xx and 5xx responses on GET requests throw {@link HttpClientException} and {@link HttpServerException}
* respectively.
*
*
*
* @see ReactorNettyClientOptions
* @since 2.1.0
*/
public class ReactorNettyClient {
private static final Consumer JSON_HEADER = headers ->
headers.set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
private final ObjectMapper objectMapper;
private final HttpClient client;
private final Mono token;
private final Supplier byteBufSupplier;
private final Consumer responseCallback;
public ReactorNettyClient(String url, ReactorNettyClientOptions options) {
this(urlWithoutCredentials(url),
URI.create(url).getUserInfo().split(":")[0],
URI.create(url).getUserInfo().split(":")[1], options);
}
public ReactorNettyClient(String url) {
this(url, new ReactorNettyClientOptions());
}
public ReactorNettyClient(String url, String username, String password) {
this(url, username, password, new ReactorNettyClientOptions());
}
public ReactorNettyClient(String url, String username, String password, ReactorNettyClientOptions options) {
objectMapper = options.objectMapper() == null ? createDefaultObjectMapper() : options.objectMapper().get();
client = options.client() == null ?
HttpClient.create().baseUrl(url) : options.client().get();
this.token = options.token() == null ? createBasicAuthenticationToken(username, password) : options.token();
if (options.onResponseCallback() == null) {
this.responseCallback = response -> {
if (response.method() == HttpMethod.GET) {
if (response.status().code() >= 500) {
throw new HttpServerException(response.status().code(), response.status().reasonPhrase());
} else if (response.status().code() >= 400) {
throw new HttpClientException(response.status().code(), response.status().reasonPhrase());
}
}
};
} else {
this.responseCallback = response ->
options.onResponseCallback().accept(new HttpEndpoint(response.uri(), response.method().name()), toHttpResponse(response));
}
ByteBufAllocator byteBufAllocator = new PooledByteBufAllocator();
this.byteBufSupplier = () -> byteBufAllocator.buffer();
}
private static String urlWithoutCredentials(String url) {
URI url1 = URI.create(url);
return url.replace(url1.getUserInfo() + "@", "");
}
private static HttpResponse toHttpResponse(HttpClientResponse response) {
Map headers = new LinkedHashMap<>();
for (Map.Entry headerEntry : response.responseHeaders().entries()) {
headers.put(headerEntry.getKey(), headerEntry.getValue());
}
return new HttpResponse(response.status().code(), response.status().reasonPhrase(), headers);
}
public static ObjectMapper createDefaultObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
return objectMapper;
}
public static Mono createBasicAuthenticationToken(String username, String password) {
return Mono.fromSupplier(() -> basicAuthentication(username, password)).cache();
}
public static String basicAuthentication(String username, String password) {
String credentials = username + ":" + password;
byte[] credentialsAsBytes = credentials.getBytes(StandardCharsets.ISO_8859_1);
byte[] encodedBytes = Base64.getEncoder().encode(credentialsAsBytes);
String encodedCredentials = new String(encodedBytes, StandardCharsets.ISO_8859_1);
return "Basic " + encodedCredentials;
}
public Mono getOverview() {
return doGetMono(OverviewResponse.class, "overview");
}
public Flux getNodes() {
return doGetFlux(NodeInfo.class, "nodes");
}
public Mono getNode(String name) {
return doGetMono(NodeInfo.class, "nodes", enc(name));
}
public Flux getConnections() {
return doGetFlux(ConnectionInfo.class, "connections");
}
public Mono getConnection(String name) {
return doGetMono(ConnectionInfo.class, "connections", enc(name));
}
public Mono closeConnection(String name) {
return doDelete("connections", enc(name));
}
public Mono closeConnection(String name, String reason) {
return doDelete(request -> request.header("X-Reason", reason), "connections", enc(name));
}
public Mono declarePolicy(String vhost, String name, PolicyInfo info) {
return doPut(info, "policies", enc(vhost), enc(name));
}
public Flux getPolicies() {
return doGetFlux(PolicyInfo.class, "policies");
}
public Flux getPolicies(String vhost) {
return doGetFlux(PolicyInfo.class, "policies", enc(vhost));
}
public Mono deletePolicy(String vhost, String name) {
return doDelete("policies", enc(vhost), enc(name));
}
public Flux getChannels() {
return doGetFlux(ChannelInfo.class, "channels");
}
public Flux getChannels(String connectionName) {
return doGetFlux(ChannelInfo.class, "connections", enc(connectionName), "channels");
}
public Mono getChannel(String name) {
return doGetMono(ChannelInfo.class, "channels", enc(name));
}
public Flux getVhosts() {
return doGetFlux(VhostInfo.class, "vhosts");
}
public Mono getVhost(String name) {
return doGetMono(VhostInfo.class, "vhosts", enc(name));
}
public Mono createVhost(String name) {
return doPut("vhosts", enc(name));
}
public Mono deleteVhost(String name) {
return doDelete("vhosts", enc(name));
}
public Flux getPermissionsIn(String vhost) {
return doGetFlux(UserPermissions.class, "vhosts", enc(vhost), "permissions");
}
public Mono updatePermissions(String vhost, String username, UserPermissions permissions) {
return doPut(permissions, "permissions", enc(vhost), enc(username));
}
public Flux getTopicPermissionsIn(String vhost) {
return doGetFlux(TopicPermissions.class, "vhosts", enc(vhost), "topic-permissions");
}
public Mono updateTopicPermissions(String vhost, String username, TopicPermissions permissions) {
return doPut(permissions, "topic-permissions", enc(vhost), enc(username));
}
public Flux getUsers() {
return doGetFlux(UserInfo.class, "users");
}
public Mono getUser(String username) {
return doGetMono(UserInfo.class, "users", enc(username));
}
public Mono deleteUser(String username) {
return doDelete("users", enc(username));
}
public Mono createUser(String username, char[] password, List tags) {
if (username == null) {
throw new IllegalArgumentException("username cannot be null");
}
if (password == null) {
throw new IllegalArgumentException("password cannot be null or empty. If you need to create a user that "
+ "will only authenticate using an x509 certificate, use createUserWithPasswordHash with a blank hash.");
}
Map body = new HashMap();
body.put("password", new String(password));
if (tags == null || tags.isEmpty()) {
body.put("tags", "");
} else {
body.put("tags", String.join(",", tags));
}
return doPut(body, "users", enc(username));
}
public Mono updateUser(String username, char[] password, List tags) {
if (username == null) {
throw new IllegalArgumentException("username cannot be null");
}
Map body = new HashMap();
// only update password if provided
if (password != null) {
body.put("password", new String(password));
}
if (tags == null || tags.isEmpty()) {
body.put("tags", "");
} else {
body.put("tags", String.join(",", tags));
}
return doPut(body, "users", enc(username));
}
public Flux getPermissionsOf(String username) {
return doGetFlux(UserPermissions.class, "users", enc(username), "permissions");
}
public Flux getTopicPermissionsOf(String username) {
return doGetFlux(TopicPermissions.class, "users", enc(username), "topic-permissions");
}
public Mono createUserWithPasswordHash(String username, char[] passwordHash, List tags) {
if (username == null) {
throw new IllegalArgumentException("username cannot be null");
}
// passwordless authentication is a thing. See
// https://github.com/rabbitmq/hop/issues/94 and https://www.rabbitmq.com/authentication.html. MK.
if (passwordHash == null) {
passwordHash = "".toCharArray();
}
Map body = new HashMap();
body.put("password_hash", String.valueOf(passwordHash));
if (tags == null || tags.isEmpty()) {
body.put("tags", "");
} else {
body.put("tags", String.join(",", tags));
}
return doPut(body, "users", enc(username));
}
public Mono whoAmI() {
return doGetMono(CurrentUserDetails.class, "whoami");
}
public Flux getPermissions() {
return doGetFlux(UserPermissions.class, "permissions");
}
public Mono getPermissions(String vhost, String username) {
return doGetMono(UserPermissions.class, "permissions", enc(vhost), enc(username));
}
public Mono clearPermissions(String vhost, String username) {
return doDelete("permissions", enc(vhost), enc(username));
}
public Flux getTopicPermissions() {
return doGetFlux(TopicPermissions.class, "topic-permissions");
}
public Flux getTopicPermissions(String vhost, String username) {
return doGetFlux(TopicPermissions.class, "topic-permissions", enc(vhost), enc(username));
}
public Mono clearTopicPermissions(String vhost, String username) {
return doDelete("topic-permissions", enc(vhost), enc(username));
}
public Flux getExchanges() {
return doGetFlux(ExchangeInfo.class, "exchanges");
}
public Flux getExchanges(String vhost) {
return doGetFlux(ExchangeInfo.class, "exchanges", enc(vhost));
}
public Mono getExchange(String vhost, String name) {
return doGetMono(ExchangeInfo.class, "exchanges", enc(vhost), enc(name));
}
public Mono declareExchange(String vhost, String name, ExchangeInfo info) {
return doPut(info, "exchanges", enc(vhost), enc(name));
}
public Mono deleteExchange(String vhost, String name) {
return doDelete("exchanges", enc(vhost), enc(name));
}
public Mono alivenessTest(String vhost) {
return doGetMono(AlivenessTestResult.class, "aliveness-test", enc(vhost));
}
public Mono getClusterName() {
return doGetMono(ClusterId.class, "cluster-name");
}
public Mono setClusterName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("name cannot be null or blank");
}
return doPut(Collections.singletonMap("name", name), "cluster-name");
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Flux
© 2015 - 2024 Weber Informatics LLC | Privacy Policy