juzu.Response Maven / Gradle / Ivy
/*
* Copyright 2013 eXo Platform SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package juzu;
import juzu.impl.common.MethodHandle;
import juzu.impl.common.Tools;
import juzu.io.Stream;
import juzu.io.Streamable;
import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* A response object signalling to the portal the action to take after an interaction. This object is usually
* returned after the invocation of a controller method and instructs Juzu the action to take.
*
* Action response
*
* Redirection response
* A Response.Process.Action.Redirect
response instructs Juzu to make a redirection to a valid
* URL after the interaction, this kind of response is created using the factory method {@link Response#redirect(String)}:
*
* return Response.redirect("http://www.exoplatform.org");
*
*
*
* Proceed to render phase
* A Response.View
response instructs Juzu to proceed to the render phase of a valid view
* controller, this kind of response can be created using an {@link juzu.request.ActionContext}, however the best
* way is to use a controller companion class that carries method factories for creating render responses.
*
* Type safe {@link juzu.Response.View} factory method are generated for each view or resource controller
* methods. The signature of an render factory is obtained by using the same signature of the controller method.
*
*
* public class MyController {
*
* @Action
* public {@link juzu.Response.View} myAction() {
* return MyController_.myRender("hello");
* }
*
* @View
* public void myRender(String param) {
* }
* }
*
*
* Mime response
*
* Mime response are used by the {@link juzu.request.Phase#VIEW} and the {@link juzu.request.Phase#RESOURCE} phases.
* Both contains a content to be streamed to the client but still they have some noticeable differences.
*
* The {@link juzu.Response.Content} class is the base response class which will work well for the two phases.
* However the {@link juzu.request.Phase#VIEW} can specify an optional title and the {@link juzu.request.Phase#RESOURCE}
* can specify an optional status code for the user agent response.
*
* Responses are created using the {@link Response} factory methods such as
*
*
* - {@link Response#ok} creates an ok response
* - {@link Response#notFound} creates a not found response
*
*
* Response can also created from {@link juzu.template.Template} directly:
*
*
* public class MyController {
*
* @Inject @Path("index.gtmpl") {@link juzu.template.Template} index;
*
* @View
* public {@link juzu.Response.Render} myView() {
* return index.render();
* }
*
* @Inject @Path("error.gtmpl") {@link juzu.template.Template} error;
*
* @Resource
* public {@link juzu.Response.Content} myView() {
* return error.notFound();
* }
* }
*
*
* The {@link juzu.template.Template.Builder} can also create responses:
*
*
* public class MyController {
*
* @Inject @Path("index.gtmpl") index index;
*
* @View
* public {@link juzu.Response.Content} myView() {
* return index.with().label("hello").render();
* }
* }
*
*
* @author Julien Viet
*/
public abstract class Response {
/** . */
protected final PropertyMap properties;
protected Response() {
this.properties = new PropertyMap();
}
protected Response(PropertyMap properties) {
this.properties = properties;
}
/**
* Set a property, if the value is null, the property is removed.
*
* @param propertyType the property type
* @param propertyValue the property value
* @throws NullPointerException if the property type is null
*/
public Response with(PropertyType propertyType, T propertyValue) throws NullPointerException {
if (propertyType == null) {
throw new NullPointerException("No null property type allowed");
}
properties.setValue(propertyType, propertyValue);
return this;
}
/**
* Removes a property.
*
* @param propertyType the property type
* @throws NullPointerException if the property type is null
*/
public Response without(PropertyType propertyType) throws NullPointerException {
return with(propertyType, null);
}
/**
* Set a boolean property to true.
*
* @param propertyType the property type
* @throws NullPointerException if the property type is null
*/
public Response with(PropertyType propertyType) throws NullPointerException {
return with(propertyType, true);
}
/**
* Set a boolean property to false.
*
* @param propertyType the property type
* @throws NullPointerException if the property type is null
*/
public Response withNo(PropertyType propertyType) throws NullPointerException {
return with(propertyType, false);
}
public final PropertyMap getProperties() {
return properties;
}
public Response withHeader(String name, String... value) {
Iterable> values = properties.getValues(PropertyType.HEADER);
if (values != null) {
for (Map.Entry header : values) {
if (header.getKey().equals(name)) {
header.setValue(value);
return this;
}
}
}
properties.addValue(PropertyType.HEADER, new AbstractMap.SimpleEntry(name, value));
return this;
}
/**
* A response instructing to execute a render phase of a controller method after the current interaction.
*/
public static abstract class View extends Response {
public abstract MethodHandle getTarget();
public abstract Map getParameters();
@Override
public View with(PropertyType propertyType, T propertyValue) throws NullPointerException {
return (View)super.with(propertyType, propertyValue);
}
@Override
public View withHeader(String name, String... value) {
return (View)super.withHeader(name, value);
}
@Override
public View without(PropertyType propertyType) throws NullPointerException {
return (View)super.without(propertyType);
}
@Override
public View with(PropertyType propertyType) throws NullPointerException {
return (View)super.with(propertyType);
}
@Override
public View withNo(PropertyType propertyType) throws NullPointerException {
return (View)super.withNo(propertyType);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof View) {
View that = (View)obj;
return getParameters().equals(that.getParameters()) && properties.equals(that.properties);
}
return false;
}
@Override
public String toString() {
return "Response.View[target=" + getTarget() + ",parameters" + getParameters() + ",properties=" + properties + "]";
}
}
/**
* A response instructing to execute an HTTP redirection after the current interaction.
*/
public static class Redirect extends Response {
/** . */
private final String location;
public Redirect(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
@Override
public Redirect with(PropertyType propertyType, T propertyValue) throws NullPointerException {
return (Redirect)super.with(propertyType, propertyValue);
}
@Override
public Redirect withHeader(String name, String... value) {
return (Redirect)super.withHeader(name, value);
}
@Override
public Redirect without(PropertyType propertyType) throws NullPointerException {
return (Redirect)super.without(propertyType);
}
@Override
public Redirect with(PropertyType propertyType) throws NullPointerException {
return (Redirect)super.with(propertyType);
}
@Override
public Redirect withNo(PropertyType propertyType) throws NullPointerException {
return (Redirect)super.withNo(propertyType);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Redirect) {
Redirect that = (Redirect)obj;
return location.equals(that.location);
}
return false;
}
@Override
public String toString() {
return "Response.Redirect[location" + location + "]";
}
}
public static class Content extends Response {
/** . */
private int status;
/** . */
private final Class kind;
/** . */
private Streamable streamable;
protected Content(int status, Class kind) {
this.status = status;
this.kind = kind;
this.streamable = null;
}
protected Content(int status, Class kind, PropertyMap properties) {
super(properties);
//
this.status = status;
this.kind = kind;
this.streamable = null;
}
protected Content(int status, Class kind, Streamable streamable) {
this.status = status;
this.kind = kind;
this.streamable = streamable;
}
protected Content(int status, Class kind, PropertyMap properties, Streamable streamable) {
super(properties);
//
this.status = status;
this.kind = kind;
this.streamable = streamable;
}
public Class getKind() {
return kind;
}
public Streamable getStreamable() {
return streamable;
}
public String getMimeType() {
return properties.getValue(PropertyType.MIME_TYPE);
}
public Content withMimeType(String mimeType) {
properties.setValue(PropertyType.MIME_TYPE, mimeType);
return this;
}
@Override
public Content withHeader(String name, String... value) {
return (Content)super.withHeader(name, value);
}
@Override
public Content with(PropertyType propertyType, T propertyValue) throws NullPointerException {
return (Content)super.with(propertyType, propertyValue);
}
@Override
public Content without(PropertyType propertyType) throws NullPointerException {
return (Content)super.without(propertyType);
}
@Override
public Content with(PropertyType propertyType) throws NullPointerException {
return (Content)super.with(propertyType);
}
@Override
public Content withNo(PropertyType propertyType) throws NullPointerException {
return (Content)super.withNo(propertyType);
}
public Integer getStatus() {
return status;
}
/**
* Send the response on the stream argument, Juzu invokes it when it needs to render the content object.
*
* @param stream the stream for sending the response
* @throws IOException any io exception
*/
public void send(S stream) throws IOException {
streamable.send(stream);
}
}
public static class Render extends Content {
@Override
public Render with(PropertyType propertyType, T propertyValue) throws NullPointerException {
return (Render)super.with(propertyType, propertyValue);
}
@Override
public Render without(PropertyType propertyType) throws NullPointerException {
return (Render)super.without(propertyType);
}
@Override
public Render with(PropertyType propertyType) throws NullPointerException {
return (Render)super.with(propertyType);
}
@Override
public Render withNo(PropertyType propertyType) throws NullPointerException {
return (Render)super.withNo(propertyType);
}
@Override
public Render withMimeType(String mimeType) {
return (Render)super.withMimeType(mimeType);
}
public Render() {
super(200, Stream.Char.class);
}
public Render(int status, PropertyMap properties, Streamable streamable) {
super(status, Stream.Char.class, properties, streamable);
}
public Render(int status, Streamable streamable) {
super(status, Stream.Char.class, streamable);
}
public Render(PropertyMap properties, Streamable streamable) {
super(200, Stream.Char.class, properties, streamable);
}
public Render(Streamable streamable) {
super(200, Stream.Char.class, streamable);
}
@Override
public Render withHeader(String name, String... value) {
return (Render)super.withHeader(name, value);
}
public String getTitle() {
return properties.getValue(PropertyType.TITLE);
}
public Render withTitle(String title) {
properties.setValue(PropertyType.TITLE, title);
return this;
}
public Iterable getScripts() {
Iterable scripts = properties.getValues(PropertyType.SCRIPT);
return scripts != null ? scripts : Tools.emptyIterable();
}
public Render withScripts(String... scripts) throws NullPointerException {
if (scripts == null) {
throw new NullPointerException("No null script accepted");
}
properties.addValues(PropertyType.SCRIPT, scripts);
return this;
}
public Iterable getStylesheets() {
Iterable stylesheets = properties.getValues(PropertyType.STYLESHEET);
return stylesheets != null ? stylesheets : Tools.emptyIterable();
}
public Render withStylesheets(String... stylesheets) throws NullPointerException {
if (stylesheets == null) {
throw new NullPointerException("No null stylesheet accepted");
}
properties.addValues(PropertyType.STYLESHEET, stylesheets);
return this;
}
public Iterable> getMetaTags() {
Iterable> metas = properties.getValues(PropertyType.META_TAG);
return metas != null ? metas : Tools.>emptyIterable();
}
public Render withMetaTag(String name, String value) {
Iterable> values = properties.getValues(PropertyType.META_TAG);
if (values != null) {
for (Map.Entry meta : values) {
if (meta.getKey().equals(name)) {
meta.setValue(value);
return this;
}
}
}
properties.addValue(PropertyType.META_TAG, new AbstractMap.SimpleEntry(name, value));
return this;
}
@Override
public String toString() {
return "Response.Render[]";
}
}
public static class Error extends Response {
/** . */
private final List at;
/** . */
private final Throwable cause;
/** . */
private final String msg;
public Error(Throwable cause) {
this(null, cause);
}
public Error(String message) {
this(message, null);
}
private Error(String message, Throwable cause) {
this.at = Collections.unmodifiableList(Arrays.asList(new Exception().getStackTrace()));
this.cause = cause;
this.msg = message;
}
public List getAt() {
return at;
}
public Throwable getCause() {
return cause;
}
public String getMessage() {
return msg;
}
@Override
public String toString() {
return "Response.Error[" + (cause != null ? cause.getMessage() : "") + "]";
}
}
public static Response.Redirect redirect(String location) {
return new Response.Redirect(location);
}
public static Render ok(CharSequence content) {
return content(200, content);
}
public static Render notFound(CharSequence content) {
return content(404, content);
}
public static Render content(int code, CharSequence content) {
return content(code, new Streamable.CharSequence(content));
}
public static Render content(int code, Streamable content) {
return new Render(code, content).withMimeType("text/html");
}
private static Render content(int code, String mimeType, CharSequence content) {
return new Render(code, new Streamable.CharSequence(content)).withMimeType(mimeType);
}
public static Content ok(InputStream content) {
return content(200, null, content);
}
public static Content notFound(InputStream content) {
return content(404, null, content);
}
public static Content content(int code, InputStream content) {
return content(code, null, content);
}
private static Content content(int code, String mimeType, InputStream content) {
return new Content(code, Stream.Binary.class, new Streamable.InputStream(content)).withMimeType(mimeType);
}
public static Error error(Throwable t) {
return new Error(t);
}
public static Error error(String msg) {
return new Error(msg);
}
}