/*
* Copyright 2014-2016 CyberVision, Inc.
*
* 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 org.kaaproject.kaa.server.common.admin;
import org.apache.http.HttpHost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.net.URI;
import java.util.Random;
public class KaaRestTemplate extends RestTemplate {
private static final Logger logger = LoggerFactory.getLogger(AdminClient.class);
private static final int DEFAULT_PORT = 80;
private static final String restApiSuffix = "/kaaAdmin/rest/api/";
private String[] hosts;
private int[] ports;
private String currentUrl;
private String username;
private String password;
private int index;
public KaaRestTemplate(String host, int port) {
checkHostPortLists(new String[]{host}, new int[]{port});
}
/**
* Initialize KaaRestTempalte using following format host1:port1,host2:port2.
*/
public KaaRestTemplate(String hostPortList) {
if (hostPortList == null) {
throw new IllegalArgumentException("String of addresses must be not null");
}
String[] splitedAddresses = hostPortList.split(",");
String[] hosts = new String[splitedAddresses.length];
int[] ports = new int[splitedAddresses.length];
for (int i = 0; i < hosts.length; i++) {
String[] separatedAddresses = splitedAddresses[i].split(":");
hosts[i] = separatedAddresses[0];
if (separatedAddresses.length == 2) {
ports[i] = Integer.parseInt(separatedAddresses[1]);
} else {
ports[i] = DEFAULT_PORT;
}
}
checkHostPortLists(hosts, ports);
}
private void checkHostPortLists(String[] hosts, int[] ports) {
if (hosts == null) {
throw new IllegalArgumentException("Parameter hosts can't be null.");
}
if (ports == null) {
throw new IllegalArgumentException("Parameter ports can't be null.");
}
if (hosts.length != ports.length) {
throw new IllegalArgumentException("Length of arrays of hosts "
+ "and ports must be the same length");
} else {
this.hosts = hosts;
this.ports = ports;
index = new Random().nextInt(hosts.length);
setNewRequestFactory(index);
}
}
public String getUrl() {
return currentUrl;
}
public void setPassword(String password) {
this.password = password;
}
public void setUsername(String username) {
this.username = username;
}
@Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor)
throws ResourceAccessException {
int maxRetry = hosts.length;
while (true) {
try {
return super.doExecute(url, method, requestCallback, responseExtractor);
} catch (ResourceAccessException ex) {
logger.info("Connect to ({}:{}) failed", getCurHost(), getCurPort(), ex);
boolean isRequestFactorySet = false;
while (!isRequestFactorySet) {
if (index < hosts.length - 1) {
index++;
} else {
index = 0;
}
logger.info("Trying connect to ({}:{})", getCurHost(), getCurPort(), ex);
if (maxRetry <= 0) {
logger.error("Failed to connect to ({}:{})", getCurHost(), getCurPort(), ex);
throw new ResourceAccessException(
"I/O error on " + method.name() + " request for \"" + url + "\":"
+ ex.getMessage(), new IOException(ex));
} else {
maxRetry--;
}
try {
setNewRequestFactory(index);
} catch (Exception exception) {
logger.info("Failed to initialize new request factory ({}:{})",
getCurHost(), getCurPort(), exception);
continue;
}
url = updateUrl(url);
isRequestFactorySet = true;
}
} catch (RestClientException ex) {
throw ex;
}
}
}
private URI updateUrl(URI url) {
String currentUri = url.toString();
int sufixPartIdx = currentUri.indexOf(restApiSuffix);
String defaultUriPartWithVariableHostPort = currentUri.substring(0, sufixPartIdx);
String sufixPart = currentUri.substring(sufixPartIdx);
defaultUriPartWithVariableHostPort = defaultUriPartWithVariableHostPort
.replaceFirst(url.getHost(), getCurHost());
defaultUriPartWithVariableHostPort = defaultUriPartWithVariableHostPort
.replaceFirst(String.valueOf(url.getPort()),
String.valueOf(getCurPort()));
return URI.create(defaultUriPartWithVariableHostPort + sufixPart);
}
private int getCurPort() {
return ports[index];
}
private String getCurHost() {
return hosts[index];
}
private void setNewRequestFactory(int index) {
String host = hosts[index];
int port = ports[index];
setRequestFactory(new HttpComponentsRequestFactoryBasicAuth(new HttpHost(host, port, "http")));
currentUrl = "http://" + host + ":" + port + restApiSuffix;
if (username != null && password != null) {
login(username, password);
}
}
/**
* Login to Kaa server.
*
* @param username user name
* @param password password
*/
public void login(String username, String password) {
this.username = username;
this.password = password;
HttpComponentsRequestFactoryBasicAuth requestFactory =
(HttpComponentsRequestFactoryBasicAuth) getRequestFactory();
requestFactory.setCredentials(username, password);
}
}