All Downloads are FREE. Search and download functionalities are using the official Maven repository.

juzu.Response Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/*
 * 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); } }