com.solidfire.core.client.ServiceBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of solidfire-sdk-java Show documentation
Show all versions of solidfire-sdk-java Show documentation
Library for interfacing with the Public and Incubating SolidFire Element API.
The newest version!
/*
* Copyright © 2014-2016 NetApp, Inc. All Rights Reserved.
*
* 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.solidfire.core.client;
import com.solidfire.gson.*;
import com.solidfire.gson.stream.JsonReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.String.format;
/**
* The base class for SolidFire services.
*/
public class ServiceBase {
private static final Logger log = LoggerFactory.getLogger(ServiceBase.class);
private final ArrayList secretKeys = new ArrayList<>(Arrays.asList(
"clusterPairingKey",
"volumePairingKey",
"password",
"initiatorSecret",
"scriptParameters",
"targetSecret",
"searchBindPassword"
));
static {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
}
private static final AtomicLong sessionId = new AtomicLong(0);
private final RequestDispatcher requestDispatcher;
private final GsonBuilder gsonBuilder = com.solidfire.core.serialization.GsonUtil.getDefaultBuilder();
/**
* Create a service with the given connection.
*
* @param requestDispatcher is the mechanism for sending requests to some location.
*/
protected ServiceBase(RequestDispatcher requestDispatcher) {
this.requestDispatcher = requestDispatcher;
}
/**
* Get the communication mechanism of this service.
*/
public RequestDispatcher getRequestDispatcher() {
return requestDispatcher;
}
/**
* Getter for property 'gsonBuilder'.
*
* @return Value for property 'gsonBuilder'.
*/
protected GsonBuilder getGsonBuilder() {
return gsonBuilder;
}
/**
* Send the request to the remote system.
*
* @param method the api method name
* @param requestParams the object containing the request parameters
* @param requestParamsClass the class (type) of the request object
* @param resultParamsClass the class (type) of the results object
* @return the result (response) of the the API call
*/
@SuppressWarnings("unchecked")
public TResult sendRequest(String method,
TRequest requestParams,
Class requestParamsClass,
Class resultParamsClass) {
if (null == method || method.trim().isEmpty()) throw new IllegalArgumentException("method is null or empty");
//if (null == requestParams) throw new IllegalArgumentException("request params is null");
//if (null == requestParamsClass) throw new IllegalArgumentException("request params class is null");
if (null == resultParamsClass) throw new IllegalArgumentException("result params class is null");
if (requestParams != null) {
final Map> versionConstructorMap = VersioningUtils.mapConstructorVersions(requestParams);
if (versionConstructorMap.size() > 1) {
final Map valuesOfMethodsWithGreaterVersion = VersioningUtils.getValuesOfMethodsWithGreaterVersion(requestParams, requestDispatcher.getVersion());
if (!valuesOfMethodsWithGreaterVersion.isEmpty()) {
throw new ApiException("The following parameters(s) are not applicable to this version of the API. " + VersioningUtils.methodParametersToString(valuesOfMethodsWithGreaterVersion.keySet()));
}
}
}
boolean isNode = requestDispatcher.getPort() == 442;
if (!ConnectionTypeUtils.isValidConnectionType(this, method, isNode)){
if (isNode){
throw new ApiException(String.format("%s cannot be called on a Node connection. It is a Cluster-Only method.", method));
} else {
throw new ApiException(String.format("%s cannot be called on a Cluster connection. It is a Node-Only method.", method));
}
}
final String jsonRequest = encodeRequest(method, requestParams, requestParamsClass);
try {
final String response;
response = getRequestDispatcher()
.dispatchRequest(jsonRequest)
.replaceAll("\"attributes\":\"\"", "\"attributes\":null");
return decodeResponse(response, resultParamsClass);
} catch (IOException ioe) {
throw new ApiException(ioe);
}
}
/**
* Constructs the JSON-RPC request.
*
* @param method the api method name
* @param requestParams the object containing the request parameters
* @param requestParamsClass the class (type) of the request object
* @return the request encoded in JSON-RPC format
*/
protected String encodeRequest(String method,
TRequest requestParams,
Class requestParamsClass) {
if (null == method || method.trim().isEmpty()) throw new IllegalArgumentException("method is null or empty");
//if (null == requestParams) throw new IllegalArgumentException("request params is null");
//if (null == requestParamsClass) throw new IllegalArgumentException("request params class is null");
final Gson gson = getGsonBuilder().create();
final JsonObject requestObj = new JsonObject();
requestObj.addProperty("id", sessionId.incrementAndGet());
requestObj.addProperty("method", method);
requestObj.addProperty("json-rpc", "2.0");
if (requestParams != null) {
requestObj.add("params", gson.toJsonTree(requestParams, requestParamsClass));
}
log.debug("Request: {}", obfuscateSecretKeys(requestObj, false).toString());
return gson.toJson(requestObj);
}
/**
* Decodes the JSON-RPC response.
*
* @param response the JSON encoded response
* @param resultParamsClass the class (type) of the result object returned
* @return the result (response) object
*/
protected TResult decodeResponse(String response, Class resultParamsClass) {
final Gson gson = getGsonBuilder().create();
try {
final JsonReader reader = new JsonReader(new StringReader(response));
reader.setLenient(true);
final JsonObject resultObj = gson.fromJson(reader, JsonObject.class);
log.debug("Response: {}", obfuscateSecretKeys(resultObj, false).toString());
if(resultObj == null) {
throw new NullPointerException();
}
checkForError(resultObj);
TResult result = gson.fromJson(resultObj.get("result"), resultParamsClass);
com.solidfire.core.serialization.OptionalAdaptorUtils.initializeAllNullOptionalFieldsAsEmpty(result);
com.solidfire.core.serialization.ArrayAdaptorUtils.convertAllArrayListsToArrays(result);
return result;
} catch (ClassCastException e) {
final Pattern pattern = Pattern.compile(" (.*?)
");
final Matcher matcher = pattern.matcher(response);
if (matcher.find()) {
throw new ApiServerException("Not Found", "404", matcher.group(1));
}
// Removes the html tags from the response.
response.replaceAll("<.*?>", "");
throw new ApiException(format("There was a problem parsing the response from the server. ( response=%s )", response), e);
} catch (NullPointerException | JsonParseException e) {
log.debug(response);
throw new ApiException(format("There was a problem parsing the response from the server. ( response=%s )", response), e);
}
}
private JsonElement obfuscateSecretKeys(JsonElement element, Boolean obfuscate) {
if(element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
JsonObject newObj = new JsonObject();
Set> entries = obj.entrySet();
for(Map.Entry entry : entries) {
String myKey = entry.getKey();
if(secretKeys.contains(myKey)) {
newObj.add(myKey, obfuscateSecretKeys(entry.getValue(), true));
}
else {
newObj.add(myKey, obfuscateSecretKeys(entry.getValue(), false));
}
}
return newObj;
}
if(element.isJsonArray()) {
JsonArray arr = element.getAsJsonArray();
JsonArray newArr = new JsonArray();
for(JsonElement myElement : arr) {
newArr.add(obfuscateSecretKeys(myElement, false));
}
return newArr;
}
if(element.isJsonPrimitive()) {
if(obfuscate) {
return new JsonPrimitive("*****");
} else {
return element;
}
}
return null;
}
protected void checkForError(JsonObject resultObj) throws ApiServerException {
if (resultObj == null){
throw new ApiServerException("NullResult", "", "Result object was null");
}
if (resultObj.has("error")) {
throw extractApiError(resultObj.get("error"));
}
}
/**
* Decodes the error portion, if it exists, from the JSON-RPC response.
*
* @param errorElem the JSON error object
* @return a ApiServerException with name, code and message
*/
protected ApiServerException extractApiError(JsonElement errorElem) {
return getGsonBuilder().create().fromJson(errorElem, ApiServerException.class);
}
}