View Javadoc

1   /*
2    * Copyright (C) 2010 Atlassian
3    *
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
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
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.
15   */
16  
17  package com.atlassian.jira.rest.client.internal.jersey;
18  
19  import com.atlassian.jira.rest.client.ProgressMonitor;
20  import com.atlassian.jira.rest.client.RestClientException;
21  import com.atlassian.jira.rest.client.internal.json.JsonArrayParser;
22  import com.atlassian.jira.rest.client.internal.json.JsonParseUtil;
23  import com.atlassian.jira.rest.client.internal.json.JsonParser;
24  import com.atlassian.jira.rest.client.internal.json.gen.JsonGenerator;
25  import com.sun.jersey.api.client.UniformInterfaceException;
26  import com.sun.jersey.api.client.WebResource;
27  import com.sun.jersey.client.apache.ApacheHttpClient;
28  import org.codehaus.jettison.json.JSONArray;
29  import org.codehaus.jettison.json.JSONException;
30  import org.codehaus.jettison.json.JSONObject;
31  
32  import javax.annotation.Nullable;
33  import java.net.URI;
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.concurrent.Callable;
37  
38  /**
39   * Parent class for Jersey-based implementation of REST clients
40   *
41   * @since v0.1
42   */
43  public abstract class AbstractJerseyRestClient {
44  	protected final ApacheHttpClient client;
45  	protected final URI baseUri;
46  
47  	public AbstractJerseyRestClient(URI baseUri, ApacheHttpClient client) {
48  		this.baseUri = baseUri;
49  		this.client = client;
50  	}
51  
52  	protected <T> T invoke(Callable<T> callable) throws RestClientException {
53  		try {
54  			return callable.call();
55  		} catch (UniformInterfaceException e) {
56  //			// maybe captcha?
57  //			if (e.getResponse().getClientResponseStatus() == ClientResponse.Status.FORBIDDEN || e.getResponse().getClientResponseStatus() == ClientResponse.Status.UNAUTHORIZED) {
58  //
59  //			}
60  //			final List<String> headers = e.getResponse().getHeaders().get("X-Authentication-Denied-Reason");
61  //			if (headers != null) {
62  //				System.out.println(headers);
63  //			}
64  //
65  			try {
66  				final String body = e.getResponse().getEntity(String.class);
67  				final Collection<String> errorMessages = extractErrors(body);
68  				throw new RestClientException(errorMessages, e);
69  			} catch (JSONException e1) {
70  				throw new RestClientException(e);
71  			}
72  		} catch (RestClientException e) {
73  			throw e;
74  		} catch (Exception e) {
75  			throw new RestClientException(e);
76  		}
77  	}
78  
79  	protected <T> T getAndParse(final URI uri, final JsonParser<T> parser, ProgressMonitor progressMonitor) {
80  		return invoke(new Callable<T>() {
81  			@Override
82  			public T call() throws Exception {
83  				final WebResource webResource = client.resource(uri);
84  				final JSONObject s = webResource.get(JSONObject.class);
85  				return parser.parse(s);
86  			}
87  		});
88  
89  	}
90  
91  	protected <T> T getAndParse(final URI uri, final JsonArrayParser<T> parser, ProgressMonitor progressMonitor) {
92  		return invoke(new Callable<T>() {
93  			@Override
94  			public T call() throws Exception {
95  				final WebResource webResource = client.resource(uri);
96  				final JSONArray jsonArray = webResource.get(JSONArray.class);
97  				return parser.parse(jsonArray);
98  			}
99  		});
100 	}
101 
102 	protected void delete(final URI uri, ProgressMonitor progressMonitor) {
103 		invoke(new Callable<Void>() {
104 			@Override
105 			public Void call() throws Exception {
106 				final WebResource webResource = client.resource(uri);
107 				webResource.delete();
108 				return null;
109 			}
110 		});
111 	}
112 
113 	protected <T> T postAndParse(final URI uri, @Nullable final JSONObject postEntity, final JsonParser<T> parser, ProgressMonitor progressMonitor) {
114 		return invoke(new Callable<T>() {
115 			@Override
116 			public T call() throws Exception {
117 				final WebResource webResource = client.resource(uri);
118 				final JSONObject s = postEntity != null ? webResource.post(JSONObject.class, postEntity) : webResource.post(JSONObject.class);
119 				return parser.parse(s);
120 			}
121 		});
122 	}
123 
124 	protected void post(final URI uri, @Nullable final JSONObject postEntity, ProgressMonitor progressMonitor) {
125 		post(uri, new Callable<JSONObject>() {
126 			@Override
127 			public JSONObject call() throws Exception {
128 				return postEntity;
129 
130 			}
131 		}, progressMonitor);
132 	}
133 
134 	protected void post(final URI uri, final Callable<JSONObject> callable, ProgressMonitor progressMonitor) {
135 		invoke(new Callable<Void>() {
136 			@Override
137 			public Void call() throws Exception {
138 				final WebResource webResource = client.resource(uri);
139 				final JSONObject postEntity = callable.call();
140 				if (postEntity != null) {
141 					webResource.post(postEntity);
142 				} else {
143 					webResource.post();
144 				}
145 				return null;
146 			}
147 		});
148 
149 	}
150 
151 	protected <T> T postAndParse(final URI uri, final Callable<JSONObject> callable, final JsonParser<T> parser, ProgressMonitor progressMonitor) {
152 		return impl(uri, Method.POST, callable, parser);
153 	}
154 
155 	protected <T> T putAndParse(final URI uri, final Callable<JSONObject> callable, final JsonParser<T> parser, ProgressMonitor progressMonitor) {
156 		return impl(uri, Method.PUT, callable, parser);
157 	}
158 
159 	enum Method {
160 		PUT, POST
161 	}
162 
163 	private <T> T impl(final URI uri, final Method method, final Callable<JSONObject> callable, final JsonParser<T> parser) {
164 		return invoke(new Callable<T>() {
165 			@Override
166 			public T call() throws Exception {
167 				final WebResource webResource = client.resource(uri);
168 				final JSONObject postEntity = callable.call();
169 				final JSONObject s;
170 				s = doHttpMethod(webResource, postEntity, method);
171 				return parser.parse(s);
172 			}
173 		});
174 	}
175 
176 	private JSONObject doHttpMethod(WebResource webResource, @Nullable JSONObject postEntity, Method method) {
177 		if (postEntity != null) {
178 			if (method == Method.POST) {
179 				return webResource.post(JSONObject.class, postEntity);
180 			} else {
181 				return webResource.put(JSONObject.class, postEntity);
182 			}
183 		} else {
184 			if (method == Method.POST) {
185 				return webResource.post(JSONObject.class);
186 			} else {
187 				return webResource.put(JSONObject.class);
188 			}
189 		}
190 	}
191 
192 
193 	static Collection<String> extractErrors(String body) throws JSONException {
194 		JSONObject jsonObject = new JSONObject(body);
195 		final Collection<String> errorMessages = new ArrayList<String>();
196 		final JSONArray errorMessagesJsonArray = jsonObject.optJSONArray("errorMessages");
197 		if (errorMessagesJsonArray != null) {
198 			errorMessages.addAll(JsonParseUtil.toStringCollection(errorMessagesJsonArray));
199 		}
200 		final JSONObject errorJsonObject = jsonObject.optJSONObject("errors");
201 		if (errorJsonObject != null) {
202 			final JSONArray valuesJsonArray = errorJsonObject.toJSONArray(errorJsonObject.names());
203 			if (valuesJsonArray != null) {
204 				errorMessages.addAll(JsonParseUtil.toStringCollection(valuesJsonArray));
205 			}
206 		}
207 		return errorMessages;
208 	}
209 
210 
211 	protected static class InputGeneratorCallable<T> implements Callable<JSONObject> {
212 
213 		private final JsonGenerator<T> generator;
214 		private final T bean;
215 
216 		public static <T> InputGeneratorCallable<T> create(JsonGenerator<T> generator, T bean) {
217 			return new InputGeneratorCallable<T>(generator, bean);
218 		}
219 
220 		public InputGeneratorCallable(JsonGenerator<T> generator, T bean) {
221 			this.generator = generator;
222 			this.bean = bean;
223 		}
224 
225 		@Override
226 		public JSONObject call() throws Exception {
227 			return generator.generate(bean);
228 		}
229 	}
230 
231 
232 }