2 * Copyright 2016-2017 ZTE, Inc. and others.
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
14 package org.onap.msb.sdclient.wrapper.consul.util;
16 import java.math.BigInteger;
17 import java.util.List;
20 import javax.ws.rs.ServerErrorException;
21 import javax.ws.rs.WebApplicationException;
22 import javax.ws.rs.client.InvocationCallback;
23 import javax.ws.rs.client.WebTarget;
24 import javax.ws.rs.core.GenericType;
25 import javax.ws.rs.core.MediaType;
26 import javax.ws.rs.core.Response;
28 import org.onap.msb.sdclient.wrapper.consul.ConsulException;
29 import org.onap.msb.sdclient.wrapper.consul.async.ConsulResponseCallback;
30 import org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse;
31 import org.onap.msb.sdclient.wrapper.consul.option.CatalogOptions;
32 import org.onap.msb.sdclient.wrapper.consul.option.ParamAdder;
33 import org.onap.msb.sdclient.wrapper.consul.option.QueryOptions;
35 import com.google.common.base.Optional;
36 import com.google.common.collect.ImmutableList;
37 import com.google.common.collect.ImmutableMap;
40 * A collection of stateless utility methods for use in constructing requests and responses to the
43 public class ClientUtil {
46 * Applies all key/values from the params map to query string parameters.
48 * @param webTarget The JAX-RS target to apply the query parameters.
49 * @param params Map of parameters.
50 * @return The new target with the parameters applied.
52 public static WebTarget queryParams(WebTarget webTarget, Map<String, String> params) {
53 WebTarget target = webTarget;
56 for (Map.Entry<String, String> entry : params.entrySet()) {
57 target = target.queryParam(entry.getKey(), entry.getValue());
65 * Given a {@link org.onap.msb.sdclient.wrapper.consul.option.ParamAdder} object, adds the
66 * appropriate query string parameters to the request being built.
68 * @param webTarget The base {@link javax.ws.rs.client.WebTarget}.
69 * @param paramAdder will add specific params to the target.
70 * @return A {@link javax.ws.rs.client.WebTarget} with all appropriate query string parameters.
72 public static WebTarget addParams(WebTarget webTarget, ParamAdder paramAdder) {
73 return paramAdder == null ? webTarget : paramAdder.apply(webTarget);
77 * Generates a {@link org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse} for a specific
78 * datacenter, set of {@link org.onap.msb.sdclient.wrapper.consul.option.QueryOptions}, and a
81 * @param target The base {@link javax.ws.rs.client.WebTarget}.
82 * @param catalogOptions Catalog specific options to use.
83 * @param queryOptions The Query Options to use.
84 * @param type The generic type to marshall the resulting data to.
85 * @param <T> The result type.
86 * @return A {@link org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse}.
88 public static <T> ConsulResponse<T> response(WebTarget target, CatalogOptions catalogOptions,
89 QueryOptions queryOptions, GenericType<T> type) {
90 target = addParams(target, catalogOptions);
91 target = addParams(target, queryOptions);
93 return response(target, type);
97 * Generates a {@link org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse} for a specific
98 * datacenter, set of {@link org.onap.msb.sdclient.wrapper.consul.option.QueryOptions}, and a
101 * @param target The base {@link javax.ws.rs.client.WebTarget}.
102 * @param catalogOptions Catalog specific options to use.
103 * @param queryOptions The Query Options to use.
104 * @param type The generic type to marshall the resulting data to.
105 * @param <T> The result type.
107 public static <T> void response(WebTarget target, CatalogOptions catalogOptions, QueryOptions queryOptions,
108 GenericType<T> type, ConsulResponseCallback<T> callback) {
110 target = addParams(target, catalogOptions);
111 target = addParams(target, queryOptions);
113 response(target, type, callback);
117 * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall the result JSON
118 * into, complete the HTTP GET request.
120 * @param webTarget The JAX-RS target.
121 * @param responseType The class to marshall the JSON into.
122 * @param <T> The class to marshall the JSON into.
123 * @return A {@link org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse} containing the
126 public static <T> ConsulResponse<T> response(WebTarget webTarget, GenericType<T> responseType) {
127 Response response = webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).get();
129 return consulResponse(responseType, response);
133 * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall the result JSON
134 * into, complete the HTTP GET request.
136 * @param webTarget The JAX-RS target.
137 * @param responseType The class to marshall the JSON into.
138 * @param callback The callback object to handle the result on a different thread.
139 * @param <T> The class to marshall the JSON into.
141 public static <T> void response(WebTarget webTarget, final GenericType<T> responseType,
142 final ConsulResponseCallback<T> callback) {
143 webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).async().get(new InvocationCallback<Response>() {
146 public void completed(Response response) {
148 callback.onComplete(consulResponse(responseType, response));
149 } catch (Exception ex) {
150 callback.onFailure(ex);
155 public void failed(Throwable throwable) {
156 callback.onFailure(throwable);
162 * Extracts Consul specific headers and adds them to a
163 * {@link org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse} object, which also contains
164 * the returned JSON entity.
166 * @param responseType The class to marshall the JSON to.
167 * @param response The HTTP response.
168 * @param <T> The class to marshall the JSON to.
169 * @return A {@link org.onap.msb.sdclient.wrapper.consul.model.ConsulResponse} object.
171 private static <T> ConsulResponse<T> consulResponse(GenericType<T> responseType, Response response) {
172 handleErrors(response);
174 String indexHeaderValue = response.getHeaderString("X-Consul-Index");
175 String lastContactHeaderValue = response.getHeaderString("X-Consul-Lastcontact");
176 String knownLeaderHeaderValue = response.getHeaderString("X-Consul-Knownleader");
178 BigInteger index = new BigInteger(indexHeaderValue);
179 long lastContact = lastContactHeaderValue == null ? -1 : Long.parseLong(lastContactHeaderValue);
180 boolean knownLeader = knownLeaderHeaderValue == null ? false : Boolean.valueOf(knownLeaderHeaderValue);
182 ConsulResponse<T> consulResponse =
183 new ConsulResponse<T>(readResponse(response, responseType), lastContact, knownLeader, index);
187 return consulResponse;
191 * Converts a {@link Response} object to the generic type provided, or an empty representation
194 * @param response response
195 * @param responseType response type
199 @SuppressWarnings("unchecked")
200 private static <T> T readResponse(Response response, GenericType<T> responseType) {
201 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
202 // would be nice I knew a better way to do this
203 if (responseType.getRawType() == List.class) {
204 return (T) ImmutableList.of();
205 } else if (responseType.getRawType() == Optional.class) {
206 return (T) Optional.absent();
207 } else if (responseType.getRawType() == Map.class) {
208 return (T) ImmutableMap.of();
210 // Not sure if this case will be reached, but if it is it'll be nice to know
211 throw new IllegalStateException(
212 "Cannot determine empty representation for " + responseType.getRawType());
215 return response.readEntity(responseType);
219 * Since Consul returns plain text when an error occurs, check for unsuccessful HTTP status
220 * code, and throw an exception with the text from Consul as the message.
222 * @param response The HTTP response.
224 public static void handleErrors(Response response) {
226 if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL
227 || response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
233 final String message = response.hasEntity() ? response.readEntity(String.class) : null;
234 if (response.getStatusInfo().getFamily() == Response.Status.Family.SERVER_ERROR) {
235 throw new ServerErrorException(message, response);
237 throw new WebApplicationException(message, response);
239 } catch (Exception e) {
240 throw new ConsulException(e.getLocalizedMessage(), e);