PageRenderTime 47ms CodeModel.GetById 1ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/sitebricks/src/main/java/com/google/sitebricks/headless/ReplyMaker.java

http://github.com/dhanji/sitebricks
Java | 239 lines | 184 code | 41 blank | 14 comment | 55 complexity | 625bc77963dad633ba6b75f6142487cd MD5 | raw file
  1package com.google.sitebricks.headless;
  2
  3import com.google.common.base.Preconditions;
  4import com.google.common.collect.Maps;
  5import com.google.common.io.ByteStreams;
  6import com.google.inject.Injector;
  7import com.google.inject.Key;
  8import com.google.sitebricks.client.Transport;
  9import com.google.sitebricks.client.transport.Text;
 10import com.google.sitebricks.rendering.Strings;
 11import com.google.sitebricks.rendering.Templates;
 12
 13import javax.servlet.http.HttpServletRequest;
 14import javax.servlet.http.HttpServletResponse;
 15import java.io.IOException;
 16import java.io.InputStream;
 17import java.util.Map;
 18
 19/**
 20 * A builder implementation of the Reply interface.
 21 */
 22class ReplyMaker<E> extends Reply<E> {
 23
 24  // By default, we cool.
 25  int status = HttpServletResponse.SC_OK;
 26
 27  String contentType;
 28
 29  String redirectUri;
 30  Map<String, String> headers = Maps.newHashMap();
 31
 32  Key<? extends Transport> transport = Key.get(Text.class);
 33  E entity;
 34  Class<?> templateKey;
 35
 36  public ReplyMaker(E entity) {
 37    this.entity = entity;
 38  }
 39
 40  @Override
 41  public Reply<E> seeOther(String uri) {
 42    redirectUri = uri;
 43    status = HttpServletResponse.SC_MOVED_PERMANENTLY;
 44    return this;
 45  }
 46
 47  @Override
 48  public Reply<E> seeOther(String uri, int statusCode) {
 49    Preconditions.checkArgument(statusCode >= 300 && statusCode < 400,
 50        "Redirect statuses must be between 300-399");
 51    redirectUri = uri;
 52    status = statusCode;
 53    return this;
 54  }
 55
 56  @Override
 57  public Reply<E> type(String mediaType) {
 58    Strings.nonEmpty(mediaType, "Media type cannot be null or empty");
 59    this.contentType = mediaType;
 60    return this;
 61  }
 62
 63  @Override
 64  public Reply<E> headers(Map<String, String> headers) {
 65    this.headers.putAll(headers);
 66    return this;
 67  }
 68
 69  @Override
 70  public Reply<E> notFound() {
 71    status = HttpServletResponse.SC_NOT_FOUND;
 72    return this;
 73  }
 74
 75  @Override
 76  public Reply<E> unauthorized() {
 77    status = HttpServletResponse.SC_UNAUTHORIZED;
 78    return this;
 79  }
 80
 81  @Override
 82  public Reply<E> as(Key<? extends Transport> transport) {
 83    Preconditions.checkArgument(null != transport, "Transport class cannot be null!");
 84    this.transport = transport;
 85    return this;
 86  }
 87
 88  @Override
 89  public Reply<E> as(Class<? extends Transport> transport) {
 90    Preconditions.checkArgument(null != transport, "Transport class cannot be null!");
 91    this.transport = Key.get(transport);
 92    return this;
 93  }
 94
 95  @Override
 96  public Reply<E> redirect(String url) {
 97    Strings.nonEmpty(url, "Redirect URL must be non empty!");
 98    this.redirectUri = url;
 99    status = HttpServletResponse.SC_MOVED_TEMPORARILY;
100    return this;
101  }
102
103  @Override
104  public Reply<E> forbidden() {
105    status = HttpServletResponse.SC_FORBIDDEN;
106    return this;
107  }
108
109  @Override
110  public Reply<E> noContent() {
111    status = HttpServletResponse.SC_NO_CONTENT;
112    return this;
113  }
114
115  @Override
116  public Reply<E> error() {
117    status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
118    return this;
119  }
120
121  @Override
122  public Reply<E> badRequest() {
123    status = HttpServletResponse.SC_BAD_REQUEST;
124    return this;
125  }
126
127  @Override
128  public Reply<E> status(int code) {
129    status = code;
130    return this;
131  }
132
133  @Override
134  public Reply<E> ok() {
135    status = HttpServletResponse.SC_OK;
136    return this;
137  }
138
139  @Override
140  public Reply<E> template(Class<?> templateKey) {
141    this.templateKey = templateKey;
142    return this;
143  }
144
145  @Override @SuppressWarnings("unchecked")
146  void populate(Injector injector, HttpServletResponse response) throws IOException {
147    // If we should not bother with the chain
148    if (Reply.NO_REPLY == this) {
149      injector.getInstance(HttpServletRequest.class).setAttribute(Reply.NO_REPLY_ATTR, Boolean.TRUE);
150      return;
151    }
152
153    // This is where we take all the builder values and encode them in the response.
154    Transport transport = injector.getInstance(this.transport);
155
156    // Set any headers (we do this first, so we can override any cheekily set headers).
157    if (!headers.isEmpty()) {
158      for (Map.Entry<String, String> header : headers.entrySet()) {
159        response.setHeader(header.getKey(), header.getValue());
160      }
161    }
162
163    // If the content type was already set, do nothing.
164    if (response.getContentType() == null) {
165      // By default we use the content type of the transport.
166      if (null == contentType) {
167        response.setContentType(transport.contentType());
168      } else {
169        response.setContentType(contentType);
170      }
171    }
172
173    // Send redirect
174    if (null != redirectUri) {
175      response.sendRedirect(redirectUri);
176      response.setStatus(status); // HACK to override whatever status the redirect sets.
177      return;
178    }
179
180    // Write out data.
181    response.setStatus(status);
182
183    if (null != templateKey) {
184      response.getWriter().write(injector.getInstance(Templates.class).render(templateKey, entity));
185    } else if (null != entity) {
186      if (entity instanceof InputStream) {
187        // Stream the response rather than marshalling it through a transport.
188        InputStream inputStream = (InputStream) entity;
189        try {
190          ByteStreams.copy(inputStream, response.getOutputStream());
191        } finally {
192          inputStream.close();
193        }
194      } else {
195        // TODO(dhanji): This feels wrong to me. We need a better way to obtain the entity type.
196        transport.out(response.getOutputStream(), (Class<E>) entity.getClass(), entity);
197      }
198    }
199  }
200  
201  @Override
202  public boolean equals(Object other) {
203	  if(!(other instanceof ReplyMaker<?>))
204		  return false;
205	  
206	  @SuppressWarnings("unchecked")
207	  ReplyMaker<E> o = (ReplyMaker<E>)other;
208	  if(this.status != o.status)
209		  return false;
210	  
211	  if((this.contentType != o.contentType)
212	  && (this.contentType != null && !this.contentType.equals(o.contentType))
213	  && (this.contentType == null && o.contentType != null))
214		  return false;
215	  
216	  if((this.redirectUri != o.redirectUri)
217	  && (this.redirectUri != null && !this.redirectUri.equals(o.redirectUri))
218	  && (this.redirectUri == null && o.redirectUri != null))
219		  return false;
220	  	  
221	  if(!this.headers.equals(o.headers))
222		  return false;
223	  
224	  if(!this.transport.equals(o.transport))
225		  return false;
226	  
227	  if(this.templateKey != o.templateKey)
228		  return false;
229
230	  if((this.entity != o.entity)
231	  && (this.entity != null && !this.entity.equals(o.entity))
232	  && (this.entity == null && o.entity != null))
233		  return false;
234	  
235	  // All tests passed, the objects must be equal
236	  return true;
237  }
238}
239