1 package com.atlassian.plugins.rest.common;
2
3 import com.google.common.base.Preconditions;
4 import com.google.common.collect.Lists;
5 import org.apache.commons.lang.builder.EqualsBuilder;
6 import org.apache.commons.lang.builder.HashCodeBuilder;
7
8 import javax.ws.rs.core.CacheControl;
9 import javax.ws.rs.core.MediaType;
10 import javax.ws.rs.core.Request;
11 import javax.ws.rs.core.Response;
12 import javax.ws.rs.core.Variant;
13 import javax.xml.bind.annotation.XmlAttribute;
14 import javax.xml.bind.annotation.XmlElement;
15 import javax.xml.bind.annotation.XmlElementWrapper;
16 import javax.xml.bind.annotation.XmlRootElement;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20
21 import static javax.ws.rs.core.MediaType.*;
22
23
24
25
26 @XmlRootElement
27 public class Status
28 {
29
30
31
32 @XmlElement
33 private final Plugin plugin;
34
35
36
37
38
39 @XmlElement(name = "status-code")
40 private final Integer code;
41
42
43
44
45 @XmlElement(name = "sub-code")
46 private final Integer subCode;
47
48
49
50
51 @XmlElement
52 private final String message;
53
54
55
56
57
58 @XmlElement(name = "etag")
59 private final String eTag;
60
61
62
63
64 @XmlElementWrapper(name = "resources-created")
65 @XmlElement(name = "link")
66 private final Collection<Link> resourcesCreated;
67
68
69
70
71 @XmlElementWrapper(name = "resources-updated")
72 @XmlElement(name = "link")
73 private final Collection<Link> resourcesUpdated;
74
75
76 private Status()
77 {
78 this.plugin = null;
79 this.code = -1;
80 this.subCode = -1;
81 this.message = null;
82 this.eTag = null;
83 this.resourcesCreated = null;
84 this.resourcesUpdated = null;
85 }
86
87 private Status(Plugin plugin, Integer code, Integer subCode, String message, String eTag, Collection<Link> resourcesCreated, Collection<Link> resourcesUpdated)
88 {
89 this.plugin = plugin;
90 this.code = code;
91 this.subCode = subCode;
92 this.message = message;
93 this.eTag = eTag;
94 this.resourcesCreated = resourcesCreated;
95 this.resourcesUpdated = resourcesUpdated;
96 }
97
98 public static StatusResponseBuilder ok()
99 {
100 return new StatusResponseBuilder(Response.Status.OK);
101 }
102
103 public static StatusResponseBuilder notFound()
104 {
105 return new StatusResponseBuilder(Response.Status.NOT_FOUND);
106 }
107
108 public static StatusResponseBuilder error()
109 {
110
111 return new StatusResponseBuilder(Response.Status.INTERNAL_SERVER_ERROR).noCache().noStore();
112 }
113
114 public static StatusResponseBuilder badRequest()
115 {
116
117 return new StatusResponseBuilder(Response.Status.BAD_REQUEST).noCache().noStore();
118 }
119
120 public static StatusResponseBuilder forbidden()
121 {
122 return new StatusResponseBuilder(Response.Status.FORBIDDEN);
123 }
124
125 public static StatusResponseBuilder unauthorized()
126 {
127 return new StatusResponseBuilder(Response.Status.UNAUTHORIZED);
128 }
129
130 public static StatusResponseBuilder created(Link link)
131 {
132 return new StatusResponseBuilder(Response.Status.CREATED).created(Preconditions.checkNotNull(link));
133 }
134
135 public Plugin getPlugin()
136 {
137 return plugin;
138 }
139
140 public int getCode()
141 {
142 return code;
143 }
144
145 public int getSubCode()
146 {
147 return subCode;
148 }
149
150 public String getMessage()
151 {
152 return message;
153 }
154
155 public String getETag()
156 {
157 return eTag;
158 }
159
160 public Collection<Link> getResourcesCreated()
161 {
162 return Collections.unmodifiableCollection(resourcesCreated);
163 }
164
165 public Collection<Link> getResourcesUpdated()
166 {
167 return Collections.unmodifiableCollection(resourcesUpdated);
168 }
169
170 @XmlRootElement
171 public static class Plugin
172 {
173 @XmlAttribute
174 private final String key;
175
176 @XmlAttribute
177 private final String version;
178
179
180 private Plugin()
181 {
182 this.key = null;
183 this.version = null;
184 }
185
186 public Plugin(String key, String version)
187 {
188 this.key = Preconditions.checkNotNull(key);
189 this.version = Preconditions.checkNotNull(version);
190 }
191
192 public String getKey()
193 {
194 return key;
195 }
196
197 public String getVersion()
198 {
199 return version;
200 }
201
202 @Override
203 public int hashCode()
204 {
205 return new HashCodeBuilder(3, 5).append(key).append(version).toHashCode();
206 }
207
208 @Override
209 public boolean equals(Object obj)
210 {
211 if (obj == null)
212 {
213 return false;
214 }
215 if (obj == this)
216 {
217 return true;
218 }
219 if (obj.getClass() != getClass())
220 {
221 return false;
222 }
223 final Plugin plugin = (Plugin) obj;
224 return new EqualsBuilder().append(key, plugin.key).append(version, plugin.version).isEquals();
225 }
226 }
227
228 public static class StatusResponseBuilder
229 {
230 private final CacheControl cacheControl;
231 private final Response.Status status;
232 private String eTag;
233 private Plugin plugin;
234 private String message;
235 private List<Link> created;
236 private List<Link> updated;
237
238 private StatusResponseBuilder(Response.Status status)
239 {
240 this(status, new CacheControl());
241 }
242
243 private StatusResponseBuilder(Response.Status status, CacheControl cacheControl)
244 {
245 this.status = Preconditions.checkNotNull(status);
246 this.cacheControl = Preconditions.checkNotNull(cacheControl);
247 }
248
249 public StatusResponseBuilder plugin(String key, String version)
250 {
251 plugin = new Plugin(key, version);
252 return this;
253 }
254
255 public StatusResponseBuilder message(String message)
256 {
257 this.message = message;
258 return this;
259 }
260
261 public StatusResponseBuilder tag(String eTag)
262 {
263 this.eTag = eTag;
264 return this;
265 }
266
267 public StatusResponseBuilder noCache()
268 {
269 cacheControl.setNoCache(true);
270 return this;
271 }
272
273 public StatusResponseBuilder noStore()
274 {
275 cacheControl.setNoStore(true);
276 return this;
277 }
278
279 public Status build()
280 {
281 return new Status(plugin, status.getStatusCode(), null, message, eTag, created, updated);
282 }
283
284 public Response response()
285 {
286 return responseBuilder().build();
287 }
288
289 public Response.ResponseBuilder responseBuilder()
290 {
291 final Response.ResponseBuilder builder =
292 Response.status(status).cacheControl(cacheControl).tag(eTag).entity(build()).type(APPLICATION_XML);
293
294 final List<Link> c = getCreated();
295 final List<Link> u = getUpdated();
296 if (c.size() == 1 && u.isEmpty())
297 {
298 builder.location(c.get(0).getHref());
299 }
300 else if (u.size() == 1 && c.isEmpty())
301 {
302 builder.location(u.get(0).getHref());
303 }
304 return builder;
305 }
306
307 public StatusResponseBuilder created(final Link link)
308 {
309 getCreated().add(link);
310 return this;
311 }
312
313 public StatusResponseBuilder updated(final Link link)
314 {
315 getUpdated().add(link);
316 return this;
317 }
318
319 private List<Link> getCreated()
320 {
321 if (created == null)
322 {
323 created = Lists.newLinkedList();
324 }
325 return created;
326 }
327
328 private List<Link> getUpdated()
329 {
330 if (updated == null)
331 {
332 updated = Lists.newLinkedList();
333 }
334 return updated;
335 }
336 }
337
338
339
340
341 private static final List<Variant> POSSIBLE_VARIANTS = Variant.mediaTypes(
342 MediaType.APPLICATION_XML_TYPE,
343 MediaType.APPLICATION_JSON_TYPE).add().build();
344
345 public static MediaType variantFor(Request request)
346 {
347 Variant v = request.selectVariant(POSSIBLE_VARIANTS);
348 if (v == null)
349 {
350 v = POSSIBLE_VARIANTS.get(0);
351 }
352
353 return v.getMediaType();
354 }
355 }