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.Response;
10  import javax.xml.bind.annotation.XmlAttribute;
11  import javax.xml.bind.annotation.XmlElement;
12  import javax.xml.bind.annotation.XmlElementWrapper;
13  import javax.xml.bind.annotation.XmlRootElement;
14  import java.util.Collection;
15  import java.util.Collections;
16  import java.util.List;
17  
18  import static javax.ws.rs.core.MediaType.*;
19  
20  /**
21   * Status entity for those responses that don't have any other entity body.
22   */
23  @XmlRootElement
24  public class Status
25  {
26      /**
27       * This is the plugin that exposes the REST api
28       */
29      @XmlElement
30      private final Plugin plugin;
31  
32      /**
33       * The HTTP reponse code, 200 for ok, 404 for not found, etc.
34       * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</a> for more information on those response codes.
35       */
36      @XmlElement(name = "status-code")
37      private final Integer code;
38  
39      /**
40       * The plugin specific response code. This is used to differenciate possible error code for example.
41       */
42      @XmlElement(name = "sub-code")
43      private final Integer subCode;
44  
45      /**
46       * A humane readable message for the given status.
47       */
48      @XmlElement
49      private final String message;
50  
51      /**
52       * <p>the eTag.</p>
53       * <p>See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</a> for more information about ETag.
54       */
55      @XmlElement(name = "etag")
56      private final String eTag;
57  
58      /**
59       * Resource that have been updated during this request.
60       */
61      @XmlElementWrapper(name = "resources-created")
62      @XmlElement(name = "link")
63      private final Collection<Link> resourcesCreated;
64  
65      /**
66       * Resource that have been updated during this request.
67       */
68      @XmlElementWrapper(name = "resources-updated")
69      @XmlElement(name = "link")
70      private final Collection<Link> resourcesUpdated;
71  
72      // For JAXB's usage
73      private Status()
74      {
75          this.plugin = null;
76          this.code = -1;
77          this.subCode = -1;
78          this.message = null;
79          this.eTag = null;
80          this.resourcesCreated = null;
81          this.resourcesUpdated = null;
82      }
83  
84      private Status(Plugin plugin, Integer code, Integer subCode, String message, String eTag, Collection<Link> resourcesCreated, Collection<Link> resourcesUpdated)
85      {
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      {
97          return new StatusResponseBuilder(Response.Status.OK);
98      }
99  
100     public static StatusResponseBuilder notFound()
101     {
102         return new StatusResponseBuilder(Response.Status.NOT_FOUND);
103     }
104 
105     public static StatusResponseBuilder error()
106     {
107         // errors are not cached
108         return new StatusResponseBuilder(Response.Status.INTERNAL_SERVER_ERROR).noCache().noStore();
109     }
110 
111     public static StatusResponseBuilder badRequest()
112     {
113         // errors are not cached
114         return new StatusResponseBuilder(Response.Status.BAD_REQUEST).noCache().noStore();
115     }
116 
117     public static StatusResponseBuilder forbidden()
118     {
119         return new StatusResponseBuilder(Response.Status.FORBIDDEN);
120     }
121 
122     public static StatusResponseBuilder unauthorized()
123     {
124         return new StatusResponseBuilder(Response.Status.UNAUTHORIZED);
125     }
126 
127     public static StatusResponseBuilder created(Link link)
128     {
129         return new StatusResponseBuilder(Response.Status.CREATED).created(Preconditions.checkNotNull(link));
130     }
131 
132     public Plugin getPlugin()
133     {
134         return plugin;
135     }
136 
137     public int getCode()
138     {
139         return code;
140     }
141 
142     public int getSubCode()
143     {
144         return subCode;
145     }
146 
147     public String getMessage()
148     {
149         return message;
150     }
151 
152     public String getETag()
153     {
154         return eTag;
155     }
156 
157     public Collection<Link> getResourcesCreated()
158     {
159         return Collections.unmodifiableCollection(resourcesCreated);
160     }
161 
162     public Collection<Link> getResourcesUpdated()
163     {
164         return Collections.unmodifiableCollection(resourcesUpdated);
165     }
166 
167     @XmlRootElement
168     public static class Plugin
169     {
170         @XmlAttribute
171         private final String key;
172 
173         @XmlAttribute
174         private final String version;
175 
176         // For JAXB's usage
177         private Plugin()
178         {
179             this.key = null;
180             this.version = null;
181         }
182 
183         public Plugin(String key, String version)
184         {
185             this.key = Preconditions.checkNotNull(key);
186             this.version = Preconditions.checkNotNull(version);
187         }
188 
189         public String getKey()
190         {
191             return key;
192         }
193 
194         public String getVersion()
195         {
196             return version;
197         }
198 
199         @Override
200         public int hashCode()
201         {
202             return new HashCodeBuilder(3, 5).append(key).append(version).toHashCode();
203         }
204 
205         @Override
206         public boolean equals(Object obj)
207         {
208             if (obj == null)
209             {
210                 return false;
211             }
212             if (obj == this)
213             {
214                 return true;
215             }
216             if (obj.getClass() != getClass())
217             {
218                 return false;
219             }
220             final Plugin plugin = (Plugin) obj;
221             return new EqualsBuilder().append(key, plugin.key).append(version, plugin.version).isEquals();
222         }
223     }
224 
225     public static class StatusResponseBuilder
226     {
227         private final CacheControl cacheControl;
228         private final Response.Status status;
229         private String eTag;
230         private Plugin plugin;
231         private String message;
232         private List<Link> created;
233         private List<Link> updated;
234 
235         private StatusResponseBuilder(Response.Status status)
236         {
237             this(status, new CacheControl());
238         }
239 
240         private StatusResponseBuilder(Response.Status status, CacheControl cacheControl)
241         {
242             this.status = Preconditions.checkNotNull(status);
243             this.cacheControl = Preconditions.checkNotNull(cacheControl);
244         }
245 
246         public StatusResponseBuilder plugin(String key, String version)
247         {
248             plugin = new Plugin(key, version);
249             return this;
250         }
251 
252         public StatusResponseBuilder message(String message)
253         {
254             this.message = message;
255             return this;
256         }
257 
258         public StatusResponseBuilder tag(String eTag)
259         {
260             this.eTag = eTag;
261             return this;
262         }
263 
264         public StatusResponseBuilder noCache()
265         {
266             cacheControl.setNoCache(true);
267             return this;
268         }
269 
270         public StatusResponseBuilder noStore()
271         {
272             cacheControl.setNoStore(true);
273             return this;
274         }
275 
276         public Status build()
277         {
278             return new Status(plugin, status.getStatusCode(), null, message, eTag, created, updated);
279         }
280 
281         public Response response()
282         {
283             return responseBuilder().build();
284         }
285 
286         public Response.ResponseBuilder responseBuilder()
287         {
288             final Response.ResponseBuilder builder = 
289                     Response.status(status).cacheControl(cacheControl).tag(eTag).entity(build()).type(APPLICATION_XML);
290 
291             final List<Link> c = getCreated();
292             final List<Link> u = getUpdated();
293             if (c.size() == 1 && u.isEmpty())
294             {
295                 builder.location(c.get(0).getHref());
296             }
297             else if (u.size() == 1 && c.isEmpty())
298             {
299                 builder.location(u.get(0).getHref());
300             }
301             return builder;
302         }
303 
304         public StatusResponseBuilder created(final Link link)
305         {
306             getCreated().add(link);
307             return this;
308         }
309 
310         public StatusResponseBuilder updated(final Link link)
311         {
312             getUpdated().add(link);
313             return this;
314         }
315 
316         private List<Link> getCreated()
317         {
318             if (created == null)
319             {
320                 created = Lists.newLinkedList();
321             }
322             return created;
323         }
324 
325         private List<Link> getUpdated()
326         {
327             if (updated == null)
328             {
329                 updated = Lists.newLinkedList();
330             }
331             return updated;
332         }
333     }
334 }