diff options
Diffstat (limited to 'apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java')
-rw-r--r-- | apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java new file mode 100644 index 0000000..ab184c1 --- /dev/null +++ b/apiroute/apiroute-service/src/main/java/org/openo/msb/wrapper/consul/util/ClientUtil.java @@ -0,0 +1,246 @@ +/** +* Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE) +* +* 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.openo.msb.wrapper.consul.util; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import javax.ws.rs.ServerErrorException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.InvocationCallback; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.openo.msb.wrapper.consul.ConsulException; +import org.openo.msb.wrapper.consul.async.ConsulResponseCallback; +import org.openo.msb.wrapper.consul.model.ConsulResponse; +import org.openo.msb.wrapper.consul.option.CatalogOptions; +import org.openo.msb.wrapper.consul.option.ParamAdder; +import org.openo.msb.wrapper.consul.option.QueryOptions; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +/** + * A collection of stateless utility methods for use in constructing + * requests and responses to the Consul HTTP API. + */ +public class ClientUtil { + + /** + * Applies all key/values from the params map to query string parameters. + * + * @param webTarget The JAX-RS target to apply the query parameters. + * @param params Map of parameters. + * @return The new target with the parameters applied. + */ + public static WebTarget queryParams(WebTarget webTarget, Map<String, String> params) { + WebTarget target = webTarget; + + if(params != null) { + for(Map.Entry<String, String> entry : params.entrySet()) { + target = target.queryParam(entry.getKey(), entry.getValue()); + } + } + + return target; + } + + /** + * Given a {@link org.openo.msb.wrapper.consul.option.ParamAdder} object, adds the + * appropriate query string parameters to the request being built. + * + * @param webTarget The base {@link javax.ws.rs.client.WebTarget}. + * @param paramAdder will add specific params to the target. + * @return A {@link javax.ws.rs.client.WebTarget} with all appropriate query + * string parameters. + */ + public static WebTarget addParams(WebTarget webTarget, ParamAdder paramAdder) { + return paramAdder == null ? webTarget : paramAdder.apply(webTarget); + } + + /** + * Generates a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} for a specific datacenter, + * set of {@link org.openo.msb.wrapper.consul.option.QueryOptions}, and a result type. + * + * @param target The base {@link javax.ws.rs.client.WebTarget}. + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @param type The generic type to marshall the resulting data to. + * @param <T> The result type. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse}. + */ + public static <T> ConsulResponse<T> response(WebTarget target, CatalogOptions catalogOptions, + QueryOptions queryOptions, + GenericType<T> type) { + target = addParams(target, catalogOptions); + target = addParams(target, queryOptions); + + return response(target, type); + } + + /** + * Generates a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} for a specific datacenter, + * set of {@link org.openo.msb.wrapper.consul.option.QueryOptions}, and a result type. + * + * @param target The base {@link javax.ws.rs.client.WebTarget}. + * @param catalogOptions Catalog specific options to use. + * @param queryOptions The Query Options to use. + * @param type The generic type to marshall the resulting data to. + * @param <T> The result type. + */ + public static <T> void response(WebTarget target, CatalogOptions catalogOptions, + QueryOptions queryOptions, + GenericType<T> type, + ConsulResponseCallback<T> callback) { + + target = addParams(target, catalogOptions); + target = addParams(target, queryOptions); + + response(target, type, callback); + } + + /** + * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall + * the result JSON into, complete the HTTP GET request. + * + * @param webTarget The JAX-RS target. + * @param responseType The class to marshall the JSON into. + * @param <T> The class to marshall the JSON into. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing the result. + */ + public static <T> ConsulResponse<T> response(WebTarget webTarget, GenericType<T> responseType) { + Response response = webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).get(); + + return consulResponse(responseType, response); + } + + /** + * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall + * the result JSON into, complete the HTTP GET request. + * + * @param webTarget The JAX-RS target. + * @param responseType The class to marshall the JSON into. + * @param callback The callback object to handle the result on a different thread. + * @param <T> The class to marshall the JSON into. + */ + public static <T> void response(WebTarget webTarget, final GenericType<T> responseType, + final ConsulResponseCallback<T> callback) { + webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).async().get(new InvocationCallback<Response>() { + + @Override + public void completed(Response response) { + try { + callback.onComplete(consulResponse(responseType, response)); + } catch (Exception ex) { + callback.onFailure(ex); + } + } + + @Override + public void failed(Throwable throwable) { + callback.onFailure(throwable); + } + }); + } + + /** + * Extracts Consul specific headers and adds them to a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} + * object, which also contains the returned JSON entity. + * + * @param responseType The class to marshall the JSON to. + * @param response The HTTP response. + * @param <T> The class to marshall the JSON to. + * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} object. + */ + private static <T> ConsulResponse<T> consulResponse(GenericType<T> responseType, Response response) { + handleErrors(response); + + String indexHeaderValue = response.getHeaderString("X-Consul-Index"); + String lastContactHeaderValue = response.getHeaderString("X-Consul-Lastcontact"); + String knownLeaderHeaderValue = response.getHeaderString("X-Consul-Knownleader"); + + BigInteger index = new BigInteger(indexHeaderValue); + long lastContact = lastContactHeaderValue == null ? -1 : Long.valueOf(lastContactHeaderValue); + boolean knownLeader = knownLeaderHeaderValue == null ? false : Boolean.valueOf(knownLeaderHeaderValue); + + ConsulResponse<T> consulResponse = new ConsulResponse<T>(readResponse(response, responseType), lastContact, knownLeader, index); + + response.close(); + + return consulResponse; + } + + /** + * Converts a {@link Response} object to the generic type provided, or an empty + * representation if appropriate + * + * @param response response + * @param responseType response type + * @param <T> + * @return the re + */ + private static <T> T readResponse(Response response, GenericType<T> responseType) { + if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + // would be nice I knew a better way to do this + if (responseType.getRawType() == List.class) { + return (T) ImmutableList.of(); + } else if (responseType.getRawType() == Optional.class) { + return (T) Optional.absent(); + } else if(responseType.getRawType() == Map.class) { + return (T) ImmutableMap.of(); + } else { + // Not sure if this case will be reached, but if it is it'll be nice to know + throw new IllegalStateException("Cannot determine empty representation for " + responseType.getRawType()); + } + } + return response.readEntity(responseType); + } + + /** + * Since Consul returns plain text when an error occurs, check for + * unsuccessful HTTP status code, and throw an exception with the text + * from Consul as the message. + * + * @param response The HTTP response. + */ + public static void handleErrors(Response response) { + + if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL + || response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { + // not an error + return; + } + + try { + final String message = response.hasEntity() ? response.readEntity(String.class) : null; + if (response.getStatusInfo().getFamily() == Response.Status.Family.SERVER_ERROR) { + throw new ServerErrorException(message, response); + } else { + throw new WebApplicationException(message, response); + } + } catch (Exception e) { + throw new ConsulException(e.getLocalizedMessage(), e); + } finally { + response.close(); + } + } +} |