2 * Copyright 2016 ZTE, Inc. and others.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * Copyright (C) 2016 ZTE, Inc. and others. All rights reserved. (ZTE)
19 * Licensed under the Apache License, Version 2.0 (the "License");
20 * you may not use this file except in compliance with the License.
21 * You may obtain a copy of the License at
23 * http://www.apache.org/licenses/LICENSE-2.0
25 * Unless required by applicable law or agreed to in writing, software
26 * distributed under the License is distributed on an "AS IS" BASIS,
27 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28 * See the License for the specific language governing permissions and
29 * limitations under the License.
32 package org.openo.msb.wrapper.consul.util;
34 import com.google.common.base.Optional;
35 import com.google.common.collect.ImmutableList;
36 import com.google.common.collect.ImmutableMap;
38 import javax.ws.rs.ServerErrorException;
39 import javax.ws.rs.WebApplicationException;
40 import javax.ws.rs.client.InvocationCallback;
41 import javax.ws.rs.client.WebTarget;
42 import javax.ws.rs.core.GenericType;
43 import javax.ws.rs.core.MediaType;
44 import javax.ws.rs.core.Response;
46 import org.openo.msb.wrapper.consul.ConsulException;
47 import org.openo.msb.wrapper.consul.async.ConsulResponseCallback;
48 import org.openo.msb.wrapper.consul.model.ConsulResponse;
49 import org.openo.msb.wrapper.consul.option.CatalogOptions;
50 import org.openo.msb.wrapper.consul.option.ParamAdder;
51 import org.openo.msb.wrapper.consul.option.QueryOptions;
53 import java.math.BigInteger;
54 import java.util.List;
58 * A collection of stateless utility methods for use in constructing
59 * requests and responses to the Consul HTTP API.
61 public class ClientUtil {
64 * Applies all key/values from the params map to query string parameters.
66 * @param webTarget The JAX-RS target to apply the query parameters.
67 * @param params Map of parameters.
68 * @return The new target with the parameters applied.
70 public static WebTarget queryParams(WebTarget webTarget, Map<String, String> params) {
71 WebTarget target = webTarget;
74 for(Map.Entry<String, String> entry : params.entrySet()) {
75 target = target.queryParam(entry.getKey(), entry.getValue());
83 * Given a {@link org.openo.msb.wrapper.consul.option.ParamAdder} object, adds the
84 * appropriate query string parameters to the request being built.
86 * @param webTarget The base {@link javax.ws.rs.client.WebTarget}.
87 * @param paramAdder will add specific params to the target.
88 * @return A {@link javax.ws.rs.client.WebTarget} with all appropriate query
91 public static WebTarget addParams(WebTarget webTarget, ParamAdder paramAdder) {
92 return paramAdder == null ? webTarget : paramAdder.apply(webTarget);
96 * Generates a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} for a specific datacenter,
97 * set of {@link org.openo.msb.wrapper.consul.option.QueryOptions}, and a result type.
99 * @param target The base {@link javax.ws.rs.client.WebTarget}.
100 * @param catalogOptions Catalog specific options to use.
101 * @param queryOptions The Query Options to use.
102 * @param type The generic type to marshall the resulting data to.
103 * @param <T> The result type.
104 * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse}.
106 public static <T> ConsulResponse<T> response(WebTarget target, CatalogOptions catalogOptions,
107 QueryOptions queryOptions,
108 GenericType<T> type) {
109 target = addParams(target, catalogOptions);
110 target = addParams(target, queryOptions);
112 return response(target, type);
116 * Generates a {@link org.openo.msb.wrapper.consul.model.ConsulResponse} for a specific datacenter,
117 * set of {@link org.openo.msb.wrapper.consul.option.QueryOptions}, and a result type.
119 * @param target The base {@link javax.ws.rs.client.WebTarget}.
120 * @param catalogOptions Catalog specific options to use.
121 * @param queryOptions The Query Options to use.
122 * @param type The generic type to marshall the resulting data to.
123 * @param <T> The result type.
125 public static <T> void response(WebTarget target, CatalogOptions catalogOptions,
126 QueryOptions queryOptions,
128 ConsulResponseCallback<T> callback) {
130 target = addParams(target, catalogOptions);
131 target = addParams(target, queryOptions);
133 response(target, type, callback);
137 * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall
138 * the result JSON into, complete the HTTP GET request.
140 * @param webTarget The JAX-RS target.
141 * @param responseType The class to marshall the JSON into.
142 * @param <T> The class to marshall the JSON into.
143 * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} containing the result.
145 public static <T> ConsulResponse<T> response(WebTarget webTarget, GenericType<T> responseType) {
146 Response response = webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).get();
148 return consulResponse(responseType, response);
152 * Given a {@link javax.ws.rs.client.WebTarget} object and a type to marshall
153 * the result JSON into, complete the HTTP GET request.
155 * @param webTarget The JAX-RS target.
156 * @param responseType The class to marshall the JSON into.
157 * @param callback The callback object to handle the result on a different thread.
158 * @param <T> The class to marshall the JSON into.
160 public static <T> void response(WebTarget webTarget, final GenericType<T> responseType,
161 final ConsulResponseCallback<T> callback) {
162 webTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).async().get(new InvocationCallback<Response>() {
165 public void completed(Response response) {
167 callback.onComplete(consulResponse(responseType, response));
168 } catch (Exception ex) {
169 callback.onFailure(ex);
174 public void failed(Throwable throwable) {
175 callback.onFailure(throwable);
181 * Extracts Consul specific headers and adds them to a {@link org.openo.msb.wrapper.consul.model.ConsulResponse}
182 * object, which also contains the returned JSON entity.
184 * @param responseType The class to marshall the JSON to.
185 * @param response The HTTP response.
186 * @param <T> The class to marshall the JSON to.
187 * @return A {@link org.openo.msb.wrapper.consul.model.ConsulResponse} object.
189 private static <T> ConsulResponse<T> consulResponse(GenericType<T> responseType, Response response) {
190 handleErrors(response);
192 String indexHeaderValue = response.getHeaderString("X-Consul-Index");
193 String lastContactHeaderValue = response.getHeaderString("X-Consul-Lastcontact");
194 String knownLeaderHeaderValue = response.getHeaderString("X-Consul-Knownleader");
196 BigInteger index = new BigInteger(indexHeaderValue);
197 long lastContact = lastContactHeaderValue == null ? -1 : Long.valueOf(lastContactHeaderValue);
198 boolean knownLeader = knownLeaderHeaderValue == null ? false : Boolean.valueOf(knownLeaderHeaderValue);
200 ConsulResponse<T> consulResponse = new ConsulResponse<T>(readResponse(response, responseType), lastContact, knownLeader, index);
204 return consulResponse;
208 * Converts a {@link Response} object to the generic type provided, or an empty
209 * representation if appropriate
211 * @param response response
212 * @param responseType response type
216 private static <T> T readResponse(Response response, GenericType<T> responseType) {
217 if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
218 // would be nice I knew a better way to do this
219 if (responseType.getRawType() == List.class) {
220 return (T) ImmutableList.of();
221 } else if (responseType.getRawType() == Optional.class) {
222 return (T) Optional.absent();
223 } else if(responseType.getRawType() == Map.class) {
224 return (T) ImmutableMap.of();
226 // Not sure if this case will be reached, but if it is it'll be nice to know
227 throw new IllegalStateException("Cannot determine empty representation for " + responseType.getRawType());
230 return response.readEntity(responseType);
234 * Since Consul returns plain text when an error occurs, check for
235 * unsuccessful HTTP status code, and throw an exception with the text
236 * from Consul as the message.
238 * @param response The HTTP response.
240 public static void handleErrors(Response response) {
242 if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL
243 || response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
249 final String message = response.hasEntity() ? response.readEntity(String.class) : null;
250 if (response.getStatusInfo().getFamily() == Response.Status.Family.SERVER_ERROR) {
251 throw new ServerErrorException(message, response);
253 throw new WebApplicationException(message, response);
255 } catch (Exception e) {
256 throw new ConsulException(e.getLocalizedMessage(), e);