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