![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.geode.management.api.RestTemplateClusterManagementServiceTransport Maven / Gradle / Ivy
/*
* 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.geode.management.api;
import static org.apache.geode.management.configuration.Links.URI_VERSION;
import static org.apache.geode.management.rest.internal.Constants.INCLUDE_CLASS_HEADER;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import org.apache.http.Header;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.message.BasicHeader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.apache.geode.annotations.Experimental;
import org.apache.geode.management.configuration.AbstractConfiguration;
import org.apache.geode.management.configuration.HasFile;
import org.apache.geode.management.rest.internal.exception.RestTemplateResponseErrorHandler;
import org.apache.geode.management.runtime.OperationResult;
import org.apache.geode.management.runtime.RuntimeInfo;
import org.apache.geode.util.internal.GeodeJsonMapper;
/**
* Concrete implementation of {@link ClusterManagementServiceTransport} which uses Spring's
* {@link RestTemplate} for communication between client and CMS endpoint.
*/
@Experimental
public class RestTemplateClusterManagementServiceTransport
implements ClusterManagementServiceTransport {
static final ResponseErrorHandler DEFAULT_ERROR_HANDLER =
new RestTemplateResponseErrorHandler();
private final RestTemplate restTemplate;
private final ScheduledExecutorService longRunningStatusPollingThreadPool =
Executors.newScheduledThreadPool(1);
public RestTemplateClusterManagementServiceTransport(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.restTemplate.setErrorHandler(DEFAULT_ERROR_HANDLER);
// configur the rest template to use a speciic jackson converter
List> messageConverters = restTemplate.getMessageConverters();
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = messageConverters.stream()
.filter(MappingJackson2HttpMessageConverter.class::isInstance)
.map(MappingJackson2HttpMessageConverter.class::cast)
.findFirst().orElse(null);
if (jackson2HttpMessageConverter == null) {
jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
messageConverters.add(jackson2HttpMessageConverter);
}
jackson2HttpMessageConverter.setPrettyPrint(false);
// the client should use a mapper that would ignore unknown properties in case the server
// is a newer version than the client
jackson2HttpMessageConverter
.setObjectMapper(GeodeJsonMapper.getMapperIgnoringUnknownProperties());
// if we don't set the default charset here, the request will use ServletRequest's default
// charset which may not be UTF-8
jackson2HttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
}
public RestTemplateClusterManagementServiceTransport(
ConnectionConfig connectionConfig) {
this(new RestTemplate(), connectionConfig);
}
public RestTemplateClusterManagementServiceTransport(RestTemplate restTemplate,
ConnectionConfig connectionConfig) {
this(restTemplate);
configureConnection(connectionConfig);
}
public void configureConnection(ConnectionConfig connectionConfig) {
if (connectionConfig == null) {
throw new IllegalStateException(
"ConnectionConfig cannot be null. Please use setConnectionConfig()");
}
if (connectionConfig.getHost() == null || connectionConfig.getPort() <= 0) {
throw new IllegalArgumentException(
"host and port needs to be specified in order to build the service.");
}
String schema = (connectionConfig.getSslContext() == null) ? "http" : "https";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(
schema + "://" + connectionConfig.getHost() + ":" + connectionConfig.getPort()
+ "/management");
restTemplate.setUriTemplateHandler(uriBuilderFactory);
// HttpComponentsClientHttpRequestFactory allows use to preconfigure httpClient for
// authentication and ssl context
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
if (connectionConfig.getFollowRedirects()) {
clientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
}
// configures the clientBuilder
if (connectionConfig.getAuthToken() != null) {
List defaultHeaders = Collections.singletonList(
new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + connectionConfig.getAuthToken()));
clientBuilder.setDefaultHeaders(defaultHeaders);
} else if (connectionConfig.getUsername() != null) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(connectionConfig.getHost(), connectionConfig.getPort()),
new UsernamePasswordCredentials(connectionConfig.getUsername(),
connectionConfig.getPassword()));
clientBuilder.setDefaultCredentialsProvider(credsProvider);
}
clientBuilder.setSSLContext(connectionConfig.getSslContext());
clientBuilder.setSSLHostnameVerifier(connectionConfig.getHostnameVerifier());
requestFactory.setHttpClient(clientBuilder.build());
restTemplate.setRequestFactory(requestFactory);
}
@Override
public > ClusterManagementRealizationResult submitMessage(
T configMessage, CommandType command) {
switch (command) {
case CREATE:
return create(configMessage, HttpMethod.POST);
case CREATE_OR_UPDATE:
return create(configMessage, HttpMethod.PUT);
case DELETE:
return delete(configMessage);
}
throw new IllegalArgumentException("Unable to process command " + command
+ ". Perhaps you need to use a different method in ClusterManagementServiceTransport.");
}
@Override
@SuppressWarnings("unchecked")
public , R extends RuntimeInfo> ClusterManagementGetResult submitMessageForGet(
T config) {
return restTemplate.exchange(getIdentityEndpoint(config), HttpMethod.GET, makeEntity(config),
ClusterManagementGetResult.class).getBody();
}
@Override
@SuppressWarnings("unchecked")
public , R extends RuntimeInfo> ClusterManagementListResult submitMessageForList(
T config) {
String endPoint = URI_VERSION + config.getLinks().getList();
return restTemplate
.exchange(endPoint + "?id={id}&group={group}", HttpMethod.GET, makeEntity(config),
ClusterManagementListResult.class, config.getId(), config.getGroup())
.getBody();
}
@Override
@SuppressWarnings("unchecked")
public , V extends OperationResult> ClusterManagementListOperationsResult submitMessageForListOperation(
A opType) {
return restTemplate.exchange(URI_VERSION + opType.getEndpoint(), HttpMethod.GET,
makeEntity(null), ClusterManagementListOperationsResult.class).getBody();
}
@Override
@SuppressWarnings("unchecked")
public , V extends OperationResult> ClusterManagementOperationResult submitMessageForGetOperation(
A op, String operationId) {
String uri = URI_VERSION + op.getEndpoint() + "/" + operationId;
return restTemplate
.exchange(uri, HttpMethod.GET, makeEntity(null), ClusterManagementOperationResult.class)
.getBody();
}
@Override
@SuppressWarnings("unchecked")
public , V extends OperationResult> ClusterManagementOperationResult submitMessageForStart(
A op) {
return restTemplate.exchange(URI_VERSION + op.getEndpoint(), HttpMethod.POST, makeEntity(op),
ClusterManagementOperationResult.class).getBody();
}
@Override
public boolean isConnected() {
try {
return "pong"
.equals(restTemplate.getForEntity(URI_VERSION + "/ping", String.class).getBody());
} catch (RestClientException e) {
return false;
}
}
@Override
public void close() {
longRunningStatusPollingThreadPool.shutdownNow();
}
private > ClusterManagementRealizationResult create(T config,
HttpMethod method) {
String endPoint = URI_VERSION + config.getLinks().getList();
// the response status code info is represented by the ClusterManagementResult.errorCode already
return restTemplate.exchange(endPoint, method, makeEntity(config),
ClusterManagementRealizationResult.class)
.getBody();
}
private > ClusterManagementRealizationResult delete(T config) {
String uri = getIdentityEndpoint(config);
return restTemplate.exchange(uri + "?group={group}",
HttpMethod.DELETE,
makeEntity(null),
ClusterManagementRealizationResult.class,
config.getGroup())
.getBody();
}
private static HttpEntity> makeEntity(T config) {
HttpHeaders headers = new HttpHeaders();
headers.add(INCLUDE_CLASS_HEADER, "true");
if (config instanceof HasFile) {
MultiValueMap content = new LinkedMultiValueMap<>();
File file = ((HasFile) config).getFile();
if (file != null) {
content.add(HasFile.FILE_PARAM, new FileSystemResource(file));
content.add(HasFile.CONFIG_PARAM, config);
return new HttpEntity<>(content, headers);
}
}
return new HttpEntity<>(config, headers);
}
private static String getIdentityEndpoint(AbstractConfiguration> config) {
String uri = config.getLinks().getSelf();
if (uri == null) {
throw new IllegalArgumentException(
"Unable to construct the URI with the current configuration.");
}
return URI_VERSION + uri;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy