View Javadoc

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   * Status entity for those responses that don't have any other entity body.
25   */
26  @XmlRootElement
27  public class Status
28  {
29      /**
30       * This is the plugin that exposes the REST api
31       */
32      @XmlElement
33      private final Plugin plugin;
34  
35      /**
36       * The HTTP reponse code, 200 for ok, 404 for not found, etc.
37       * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</a> for more information on those response codes.
38       */
39      @XmlElement(name = "status-code")
40      private final Integer code;
41  
42      /**
43       * The plugin specific response code. This is used to differenciate possible error code for example.
44       */
45      @XmlElement(name = "sub-code")
46      private final Integer subCode;
47  
48      /**
49       * A humane readable message for the given status.
50       */
51      @XmlElement
52      private final String message;
53  
54      /**
55       * <p>the eTag.</p>
56       * <p>See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</a> for more information about ETag.
57       */
58      @XmlElement(name = "etag")
59      private final String eTag;
60  
61      /**
62       * Resource that have been updated during this request.
63       */
64      @XmlElementWrapper(name = "resources-created")
65      @XmlElement(name = "link")
66      private final Collection<Link> resourcesCreated;
67  
68      /**
69       * Resource that have been updated during this request.
70       */
71      @XmlElementWrapper(name = "resources-updated")
72      @XmlElement(name = "link")
73      private final Collection<Link> resourcesUpdated;
74  
75      // For JAXB's usage
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         // errors are not cached
111         return new StatusResponseBuilder(Response.Status.INTERNAL_SERVER_ERROR).noCache().noStore();
112     }
113 
114     public static StatusResponseBuilder badRequest()
115     {
116         // errors are not cached
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         // For JAXB's usage
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      * These are the media types that a Status can be represented as.
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 }