package act.controller; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import static org.osgl.http.H.Format.HTML; import act.Act; import act.app.ActionContext; import act.conf.AppConfigKey; import act.controller.meta.HandlerMethodMetaInfo; import act.data.Versioned; import act.route.Router; import act.util.$$; import act.util.FastJsonIterable; import act.util.JsonUtilConfig.JsonWriter; import act.util.PropertySpec; import act.view.*; import org.osgl.$; import org.osgl.http.H; import org.osgl.mvc.result.*; import org.osgl.storage.ISObject; import org.osgl.util.*; import java.io.File; import java.io.InputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.net.URL; import java.util.Map; import javax.inject.Inject; /** * Mark a class as Controller, which contains at least one of the following: * */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface Controller { /** * Indicate the context path for all action methods declared * in this controller. *

*

Default value: "{@code /}"

* * @return the controller context path */ String value() default "/"; /** * Specify the port(s) this controller's action method shall be * routed from. * * @return the port name * @see AppConfigKey#NAMED_PORTS */ String[] port() default {}; /** * Provides utilities for controller action methods to emit rendering results */ class Util { public static final Ok OK = Ok.get(); public static final Created CREATED = Created.INSTANCE; public static final Result CREATED_JSON = new Result(H.Status.CREATED, "{\"message\": \"Created\"}") { }; public static final Result CREATED_XML = new Result(H.Status.CREATED, "Created") { }; public static final Result OK_JSON = new Result(H.Status.OK, "{\"message\": \"Okay\"}") { }; public static final Result OK_XML = new Result(H.Status.OK, "Okay") { }; public static final NoContent NO_CONTENT = NoContent.get(); /** * Returns an {@link Ok} result */ public static Result ok() { H.Format accept = ActionContext.current().accept(); if (H.Format.JSON == accept) { return OK_JSON; } else if (H.Format.XML == accept) { return OK_XML; } return OK; } /** * Returns a {@link Created} result * * @param resourceGetUrl * the URL to access the new resource been created * @return the result as described */ public static Created created(String resourceGetUrl) { return Created.withLocation(resourceGetUrl); } /** * Return a {@link Created} result * * @return the result as described */ public static Created created() { return Created.INSTANCE; } public static NotModified notModified() { return NotModified.get(); } public static NotModified notModified(String etag, Object... args) { return NotModified.of(etag, args); } /** * Returns a {@link Accepted} result * * @param statusMonitorUrl * the URL to check the request process status * @return the result as described */ public static Result accepted(String statusMonitorUrl) { return new Accepted(statusMonitorUrl); } public static Result notAcceptable() { return NotAcceptable.get(); } public static Result notAcceptable(String msg, Object... args) { return NotAcceptable.of(msg, args); } /** * Returns an {@link NotFound} result */ public static Result notFound() { return ActNotFound.create(); } /** * Returns an {@link NotFound} result with custom message * template and arguments. The final message is rendered with * the template and arguments using {@link String#format(String, Object...)} * * @param msg * the message template * @param args * the message argument */ public static Result notFound(String msg, Object... args) { return ActNotFound.create(msg, args); } /** * Throws out an {@link NotFound} result if the object specified is * {@code null} * * @param o * the object to be evaluated */ public static T notFoundIfNull(T o) { if (null == o) { throw ActNotFound.create(); } return o; } /** * Throws out an {@link NotFound} result with custom message template and * arguments if the object specified is {@code null}. The final message is * rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param o * the object to be evaluated * @param msg * the message template * @param args * the message argument */ public static T notFoundIfNull(T o, String msg, Object... args) { if (null == o) { throw ActNotFound.create(msg, args); } return o; } /** * Throws out an {@link NotFound} result if the boolean expression specified * is {@code true} * {@code null} * * @param test * the boolean expression to be evaluated */ public static void notFoundIf(boolean test) { if (test) { throw ActNotFound.create(); } } /** * Throws out an {@link NotFound} result with custom message template and * arguments if the expression specified is {@code true}. The final message is * rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param test * the boolean expression * @param msg * the message template * @param args * the message argument */ public static void notFoundIf(boolean test, String msg, Object... args) { if (test) { throw ActNotFound.create(msg, args); } } /** * Throws out an {@link NotFound} result if the boolean expression specified * is {@code false} * {@code null} * * @param test * the boolean expression to be evaluated */ public static void notFoundIfNot(boolean test) { notFoundIf(!test); } /** * Throws out an {@link NotFound} result with custom message template and * arguments if the expression specified is {@code false}. The final message is * rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param test * the boolean expression * @param msg * the message template * @param args * the message argument */ public static void notFoundIfNot(boolean test, String msg, Object... args) { notFoundIf(!test, msg, args); } /** * Returns a {@link BadRequest} instance. * * @return a bad request response */ public static BadRequest badRequest() { return ActBadRequest.create(); } /** * Returns a {@link BadRequest} instance along with error message. * * @param msg * the message pattern * @param args * the message argument * @return a bad request with error message */ public static BadRequest badRequest(String msg, Object... args) { return ActBadRequest.create(msg, args); } /** * Throws out a {@link BadRequest} if test evaluated to `true`. * * @param test * the test */ public static void badRequestIf(boolean test) { if (test) { throw ActBadRequest.create(); } } /** * Throws out a {@link BadRequest} with error message if test evaluated to `true`. * * @param test * the test * @param msg * the error message pattern * @param args * the error message arguments */ public static void badRequestIf(boolean test, String msg, Object... args) { if (test) { throw ActBadRequest.create(msg, args); } } /** * Throws out a {@link BadRequest} if test string {@link S#blank(String)}. * * @param test * the string to be checked */ public static void badRequestIfBlank(String test) { if (S.blank(test)) { throw ActBadRequest.create(); } } /** * Throws out a {@link BadRequest} with error message if test string is blank * * @param test * the string to be checked * @param msg * the error message pattern * @param args * the error message argument */ public static void badRequestIfBlank(String test, String msg, Object... args) { if (S.blank(test)) { throw ActBadRequest.create(msg, args); } } /** * Throws out a {@link BadRequest} if test object is `null` * * @param test * the object to be checked */ public static void badRequestIfNull(Object test) { if (null == test) { throw ActBadRequest.create(); } } /** * Throws out a {@link BadRequest} with error message if test object is null * * @param test * the object to be checked * @param msg * the error message pattern * @param args * the error message argument */ public static void badRequestIfNull(Object test, String msg, Object... args) { if (null == test) { throw ActBadRequest.create(msg, args); } } /** * Throws out a {@link BadRequest} if test evaluated to `false`. * * @param test * the test */ public static void badRequestIfNot(boolean test) { if (!test) { throw ActBadRequest.create(); } } /** * Throws out a {@link BadRequest} with error message if test evaluated to `false`. * * @param test * the test * @param msg * the error message pattern * @param args * the error message arguments */ public static void badRequestIfNot(boolean test, String msg, Object... args) { badRequestIf(!test, msg, args); } /** * Returns a {@link BadRequest} instance with error code specified. * * @param code * the error code * @return the bad request instance */ public static BadRequest badRequest(int code) { return ActBadRequest.create(code); } /** * Returns a {@link BadRequest} instance with error code and message specified. * * @param code * the error code * @param msg * the error message pattern * @param args * the error message arguments * @return the bad request */ public static BadRequest badRequest(int code, String msg, Object... args) { return ActBadRequest.create(code, msg, args); } /** * Throws out a {@link BadRequest} with error code if `test` evaluated to `true`. * * @param test * the test * @param code * the error code */ public static void badRequestIf(boolean test, int code) { if (test) { throw ActBadRequest.create(code); } } /** * Throws out a {@link BadRequest} with error code and message if `test` evaluated to `true`. * * @param test * the test * @param msg * the error message pattern * @param args * the error message arguments * @param code * the error code */ public static void badRequestIf(boolean test, int code, String msg, Object... args) { if (test) { throw ActBadRequest.create(code, msg, args); } } /** * Throws out a {@link BadRequest} with error code if `test` string {@link S#blank(String) is blank}. * * @param test * the test string * @param code * the error code */ public static void badRequestIfBlank(String test, int code) { if (S.blank(test)) { throw ActBadRequest.create(code); } } /** * Throws out a {@link BadRequest} with error code and message if `test` string {@link S#blank(String) is blank}. * * @param test * the test string * @param msg * the error message pattern * @param args * the error message arguments * @param code * the error code */ public static void badRequestIfBlank(String test, int code, String msg, Object... args) { if (S.blank(test)) { throw ActBadRequest.create(code, msg, args); } } /** * Throws out a {@link BadRequest} with error code if `test` is `null`. * * @param test * the test object * @param code * the error code */ public static void badRequestIfNull(Object test, int code) { if (null == test) { throw ActBadRequest.create(code); } } /** * Throws out a {@link BadRequest} with error code and message if `test` is `null`. * * @param test * the test object * @param msg * the error message pattern * @param args * the error message arguments * @param code * the error code */ public static void badRequestIfNull(Object test, int code, String msg, Object... args) { if (null == test) { throw ActBadRequest.create(code, msg, args); } } /** * Throws out a {@link BadRequest} with error code if `test` is `false`. * * @param test * the test object * @param code * the error code */ public static void badRequestIfNot(boolean test, int code) { if (!test) { throw ActBadRequest.create(code); } } /** * Throws out a {@link BadRequest} with error code and message if `test` is `false`. * * @param test * the test object * @param msg * the error message pattern * @param args * the error message arguments * @param code * the error code */ public static void badRequestIfNot(boolean test, int code, String msg, Object... args) { badRequestIf(!test, code, msg, args); } /** * Returns a {@link Conflict} instance. * * @return a 409 Conflict result */ public static Conflict conflict() { return ActConflict.create(); } /** * Returns a {@link Conflict} instance with error message. * * @param message * the error message pattern * @param args * the error message arguments * @return a 409 Conflict result */ public static Conflict conflict(String message, Object... args) { return ActConflict.create(message, args); } /** * Throws out a {@link Conflict} if `test` is `true`. * * @param test * the test */ public static void conflictIf(boolean test) { if (test) { throw ActConflict.create(); } } /** * Throws out a {@link Conflict} with error message specified if `test` is `true`. * * @param test * the test * @param message * the error message pattern * @param args * the error message arguments */ public static void conflictIf(boolean test, String message, Object... args) { if (test) { throw ActConflict.create(message, args); } } /** * Throws out a {@link Conflict} if `test` is `false`. * * @param test * the test */ public static void conflictIfNot(boolean test) { conflictIf(!test); } /** * Throws out a {@link Conflict} with error message specified if `test` is `false`. * * @param test * the test * @param message * the error message pattern * @param args * the error message arguments */ public static void conflictIfNot(boolean test, String message, Object... args) { conflictIf(!test, message, args); } /** * Returns a {@link Conflict} instance with error code. * * @param code * the error message code * @return a 409 Conflict result */ public static Conflict conflict(int code) { return ActConflict.create(code); } /** * Returns a {@link Conflict} instance with error code and message specified. * * @param code * the error message code * @param message * the error message pattern * @param args * the error message arguments * @return a 409 Conflict result */ public static Conflict conflict(int code, String message, Object... args) { return ActConflict.create(code, message, args); } /** * Throws out a {@link Conflict} with error code if `test` is `true`. * * @param test * the test * @param code * the error code */ public static void conflictIf(boolean test, int code) { if (test) { throw ActConflict.create(code); } } /** * Throws out a {@link Conflict} with error code and message if `test` is `true`. * * @param test * the test * @param message * the error message pattern * @param args * the error message arguments * @param code * the error code */ public static void conflictIf(boolean test, int code, String message, Object... args) { if (test) { throw ActConflict.create(code, message, args); } } /** * Throws out a {@link Conflict} with error code if `test` is `false`. * * @param test * the test * @param code * the error code */ public static void conflictIfNot(boolean test, int code) { conflictIf(!test, code); } /** * Throws out a {@link Conflict} with error code and message if `test` is `false`. * * @param test * the test * @param message * the error message pattern * @param args * the error message arguments * @param code * the error code */ public static void conflictIfNot(boolean test, int code, String message, Object... args) { conflictIf(!test, code, message, args); } /** * Returns a {@link Unauthorized} instance. * * @return a 401 Unauthorized result */ public static Unauthorized unauthorized() { return ActUnauthorized.create(); } /** * Returns a {@link Unauthorized} instance with `realm` specified. * * @param realm * the realm of the `Unauthorized` response * @return a 401 Unauthorized result */ public static Unauthorized unauthorized(String realm) { return ActUnauthorized.create(realm); } /** * Returns a {@link Unauthorized} instance with `realm` and `digest` specified. * * @param realm * the realm of the `Unauthorized` response * @param digest * whether apply digest * @return a 401 Unauthorized result */ public static Unauthorized unauthorized(String realm, boolean digest) { return ActUnauthorized.create(realm, digest); } /** * Throws out an {@link Unauthorized} instance if `test ` is `true`. * * @param test * the test */ public static void unauthorizedIf(boolean test) { if (test) { throw ActUnauthorized.create(); } } /** * Throws out an {@link Unauthorized} instance with `realm` specified if `test ` is `true`. * * @param test * the test * @param realm * the realm */ public static void unauthorizedIf(boolean test, String realm) { if (test) { throw ActUnauthorized.create(realm); } } /** * Throws out an {@link Unauthorized} instance with `realm` and `digest` specified if `test ` is `true`. * * @param test * the test * @param realm * the realm * @param digest * whether apply digest */ public static void unauthorizedIf(boolean test, String realm, boolean digest) { if (test) { throw ActUnauthorized.create(realm, digest); } } /** * Throws out an {@link Unauthorized} instance if `test ` is `false`. * * @param test * the test */ public static void unauthorizedIfNot(boolean test) { unauthorizedIf(!test); } /** * Throws out an {@link Unauthorized} instance with `realm` specified if `test ` is `false`. * * @param test * the test * @param realm * the realm */ public static void unauthorizedIfNot(boolean test, String realm) { unauthorizedIf(!test, realm); } /** * Throws out an {@link Unauthorized} instance with `realm` and `digest` specified if `test ` is `false`. * * @param test * the test * @param realm * the realm * @param digest * whether apply digest */ public static void unauthorizedIfNot(boolean test, String realm, boolean digest) { unauthorizedIf(!test, realm, digest); } /** * Returns a {@link Forbidden} result */ public static Forbidden forbidden() { return ActForbidden.create(); } /** * Returns a {@link Forbidden} result with custom message * template and arguments. The final message is rendered with * the template and arguments using {@link String#format(String, Object...)} * * @param msg * the message template * @param args * the message argument */ public static Forbidden forbidden(String msg, Object... args) { return ActForbidden.create(msg, args); } /** * Throws a {@link Forbidden} result if the test condition is {@code true} * * @param test * the test condition */ public static void forbiddenIf(boolean test) { if (test) { throw ActForbidden.create(); } } /** * Throws a {@link Forbidden} result if the test condition is {@code false} * * @param test * the test condition */ public static void forbiddenIfNot(boolean test) { forbiddenIf(!test); } /** * Throws a {@link Forbidden} result if test condition is {@code true} * * @param test * the test condition * @param msg * the message format template * @param args * the message format arguments */ public static void forbiddenIf(boolean test, String msg, Object... args) { if (test) { throw ActForbidden.create(msg, args); } } /** * Throws a {@link Forbidden} result if the test condition is {@code false} * * @param test * the test condition * @param msg * the message format template * @param args * the message format arguments */ public static void forbiddenIfNot(boolean test, String msg, Object... args) { forbiddenIf(!test, msg, args); } /** * Returns a {@link Forbidden} result */ public static Forbidden forbidden(int code) { return ActForbidden.create(code); } /** * Returns a {@link Forbidden} result with custom message * template and arguments. The final message is rendered with * the template and arguments using {@link String#format(String, Object...)} * * @param msg * the message template * @param args * the message argument */ public static Forbidden forbidden(String msg, int code, Object... args) { return ActForbidden.create(msg, args); } /** * Throws a {@link Forbidden} result if the test condition is {@code true} * * @param test * the test condition */ public static void forbiddenIf(boolean test, int code) { if (test) { throw ActForbidden.create(code); } } /** * Throws a {@link Forbidden} result if the test condition is {@code false} * * @param test * the test condition */ public static void forbiddenIfNot(boolean test, int code) { forbiddenIf(!test, code); } /** * Throws a {@link Forbidden} result if test condition is {@code true} * * @param test * the test condition * @param msg * the message format template * @param args * the message format arguments */ public static void forbiddenIf(boolean test, int code, String msg, Object... args) { if (test) { throw ActForbidden.create(code, msg, args); } } /** * Throws a {@link Forbidden} result if the test condition is {@code false} * * @param test * the test condition * @param msg * the message format template * @param args * the message format arguments */ public static void forbiddenIfNot(boolean test, int code, String msg, Object... args) { forbiddenIf(!test, code, msg, args); } public static void forward(String url) { ActionContext ctx = ActionContext.current(); ctx.forward(url); } public static Redirect redirect(String url, Object... args) { return Redirect.of(redirectUrl(url, args)); } public static Redirect redirect(String url, Map reverseRoutingArguments) { return Redirect.of(redirectUrl(url, reverseRoutingArguments)); } public static void redirectIf(boolean test, String url, Object... args) { if (test) { throw redirect(url, args); } } public static void redirectIfNot(boolean test, String url, Object... args) { redirectIf(!test, url, args); } public static void redirectIf(boolean test, String url, Map reverseRoutingArguments) { if (test) { throw redirect(url, reverseRoutingArguments); } } public static void redirectIfNot(boolean test, String url, Map reverseRoutingArguments) { redirectIf(!test, url, reverseRoutingArguments); } public static MovedPermanently moved(String url, Object... args) { return MovedPermanently.of(redirectUrl(url, args)); } public static MovedPermanently moved(String url, Map reverseRoutingArguments) { return MovedPermanently.of(redirectUrl(url, reverseRoutingArguments)); } public static Found found(String url, Object... args) { return Found.of(redirectUrl(url, args)); } public static Found found(String url, Map reverseRoutingArguments) { return Found.of(redirectUrl(url, reverseRoutingArguments)); } public static void foundIf(boolean test, String url, Object... args) { if (test) { throw found(url, args); } } public static void foundIfNot(boolean test, String url, Object... args) { foundIf(!test, url, args); } public static SeeOther seeOther(String url, Object... args) { return SeeOther.of(redirectUrl(url, args)); } public static SeeOther seeOther(String url, Map reverseRoutingArguments) { return SeeOther.of(redirectUrl(url, reverseRoutingArguments)); } public static void seeOtherIf(boolean test, String url, Object... args) { if (test) { throw seeOther(url, args); } } public static void seeOtherIfNot(boolean test, String url, Object... args) { seeOtherIf(!test, url, args); } public static TemporaryRedirect temporaryRedirect(String url, Object... args) { return TemporaryRedirect.of(redirectUrl(url, args)); } public static TemporaryRedirect temporaryRedirect(String url, Map reverseRoutingArguments) { return TemporaryRedirect.of(redirectUrl(url, reverseRoutingArguments)); } public static PermanentRedirect permanentRedirect(String url, Object... args) { return PermanentRedirect.of(redirectUrl(url, args)); } public static PermanentRedirect permanentRedirect(String url, Map reverseRoutingArguments) { return PermanentRedirect.of(redirectUrl(url, reverseRoutingArguments)); } private static String redirectUrl(String url, Object... args) { url = S.fmt(url, args); if (url.contains(".") || url.contains("(")) { String inferFullActionPath = Router.inferFullActionPath(url); if (inferFullActionPath != url) { url = ActionContext.current().router().reverseRoute(url); } } else { if (!url.startsWith("/")) { ActionContext context = ActionContext.current(); String urlContext = context.urlContext(); if (S.notBlank(urlContext)) { url = S.pathConcat(urlContext, '/', url); } } } return url; } private static String redirectUrl(String url, Map reverseRoutingArguments) { url = Router.inferFullActionPath(url); url = ActionContext.current().router().reverseRoute(url, reverseRoutingArguments); return url; } /** * Returns a {@link RenderText} result with specified message template * and args. The final message is rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments */ public static RenderText text(String msg, Object... args) { return RenderText.of(successStatus(), msg, args); } /** * Alias of {@link #text(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderText renderText(String msg, Object... args) { return text(msg, args); } /** * Returns a {@link RenderText} result with specified message template * and args. The final message is rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderHtml html(String msg, Object... args) { return RenderHtml.of(successStatus(), msg, args); } /** * Alias of {@link #html(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderHtml renderHtml(String msg, Object args) { return html(msg, args); } /** * Returns a {@link RenderJSON} result with specified message template * and args. The final message is rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderJSON json(String msg, Object... args) { return RenderJSON.of(successStatus(), msg, args); } /** * Alias of {@link #json(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderJSON renderJson(String msg, Object... args) { return json(msg, args); } /** * Returns a {@link RenderJSON} result with any object. This method will * call underline JSON serializer to transform the object into a JSON string * * @param data * the data to be rendered as JSON string * @return the result */ public static RenderJSON json(Object data) { return RenderJSON.of(successStatus(), data); } /** * Alias of {@link #json(Object)} * * @param data * the data to be rendered as JSON string * @return the result */ public static RenderJSON renderJson(Object data) { return json(data); } /** * Returns a {@link RenderJsonMap} result with any object. This method will * generate a JSON object out from the {@link ActionContext#renderArgs}. * The response is always in JSON format and ignores the HTTP `Accept` * header setting * * @param data * the varargs of Object to be put into the JSON map * @return the result */ public static RenderJsonMap jsonMap(Object... data) { return RenderJsonMap.get(); } /** * Alias of {@link #jsonMap(Object...)} * * @param data * the data to be put into the JSON map * @return the result */ public static RenderJsonMap renderJsonMap(Object... data) { return jsonMap(data); } public static RenderJwt renderJwt() { return RenderJwt.get(); } public static RenderJwt jwt() { return RenderJwt.get(); } /** * Returns a {@link RenderXML} result with specified message template * and args. The final message is rendered with the template and arguments using * {@link String#format(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderXML xml(String msg, Object... args) { return RenderXML.of(successStatus(), msg, args); } /** * Alias of {@link #xml(String, Object...)} * * @param msg * the message format template * @param args * the message format arguments * @return the result */ public static RenderXML renderXml(String msg, Object... args) { return xml(msg, args); } /** * Returns a {@link RenderBinary} result with an {@link ISObject} instance. The result will render * the binary using "inline" content disposition * * @param sobj * the {@link ISObject} instance * @return the result */ public static RenderBinary binary(ISObject sobj) { return new RenderBinary(sobj.asInputStream(), sobj.getAttribute(ISObject.ATTR_FILE_NAME), sobj.getAttribute(ISObject.ATTR_CONTENT_TYPE), true); } /** * Alias of {@link #binary(ISObject)} * * @param sobj * the {@link ISObject} instance * @return the result */ public static RenderBinary renderBinary(ISObject sobj) { return binary(sobj); } /** * Returns a {@link RenderBinary} result with an {@link ISObject} instance. The result will render * the binary using "attachment" content disposition * * @param sobj * the {@link ISObject} instance */ public static RenderBinary download(ISObject sobj) { return new RenderBinary(sobj.asInputStream(), sobj.getAttribute(ISObject.ATTR_FILE_NAME), sobj.getAttribute(ISObject.ATTR_CONTENT_TYPE), false); } /** * Returns a {@link RenderBinary} result with a file. The result will render * the binary using "inline" content disposition. * * @param file * the file to be rendered * @return a result */ public static RenderBinary binary(File file) { return new RenderBinary(file); } /** * Alias of {@link #binary(File)} * * @param file * the file to be rendered * @return a result */ public static RenderBinary renderBinary(File file) { return binary(file); } /** * Returns a {@link RenderBinary} result with a delayed output stream writer. * The result will render the binary using "inline" content disposition. * * @param outputStreamWriter * the delayed writer * @return the result */ public static RenderBinary binary($.Visitor outputStreamWriter) { return new RenderBinary(outputStreamWriter); } /** * Alias of {@link #binary(org.osgl.Lang.Visitor)} * * @param outputStreamWriter * the delayed writer * @return the result */ public static RenderBinary renderBinary($.Visitor outputStreamWriter) { return binary(outputStreamWriter); } /** * Returns a {@link RenderBinary} result with a URL. The result will render * the binary using "attachment" content disposition. * * @param url * the URL to be rendered * @return a `RenderBinary` result as described above */ public static RenderBinary download(URL url) { String file = url.getPath(); String fileName = S.afterLast(file, "/"); return new RenderBinary(IO.is(url), fileName, false); } /** * Returns a {@link RenderBinary} result with a URL. The result will render * the binary using "attachment" content disposition. * * @param url * the URL to be rendered * @param attachmentName * the attachment name * @return a `RenderBinary` result as described above */ public static RenderBinary download(URL url, String attachmentName) { return new RenderBinary(IO.is(url), attachmentName, false); } /** * Returns a {@link RenderBinary} result with a file. The result will render * the binary using "attachment" content disposition. * * @param file * the file to be rendered * @return a `RenderBinary` result as described above */ public static RenderBinary download(File file) { return new RenderBinary(file, file.getName(), false); } /** * Returns a {@link RenderBinary} result with a file. The result will * render the binary using "attachment" content disposition, with * the `attachmentName` as the name of the download attachment. * * @param file * the file to be downloaded * @param attachmentName * the attachment file name. * @return a `RenderBinary` result as described above */ public static RenderBinary download(File file, String attachmentName) { return new RenderBinary(file, attachmentName, false); } /** * Returns a {@link RenderBinary} result with an `InputStream`. The result will * render the binary using "attachment" content disposition, with * the `attachmentName` as the name of the download attachment. * * @param inputStream * the input stream from which byte content will be written to the * attachment. * @param attachmentName * the attachment file name. * @return a `RenderBinary` result as described above */ public static RenderBinary download(InputStream inputStream, String attachmentName) { return new RenderBinary(inputStream, attachmentName, false); } /** * Render barcode for given content * * @param content * the content to generate the barcode * @return the barcode as a binary result */ public static ZXingResult barcode(String content) { return ZXingResult.barcode(content); } /** * Alias of {@link #barcode(String)} * * @param content * the content to generate the barcode * @return the barcode as a binary result */ public static ZXingResult renderBarcode(String content) { return barcode(content); } /** * Render QRCode for given content * * @param content * the content to generate the qrcode * @return the qrcode as a binary result */ public static ZXingResult qrcode(String content) { return ZXingResult.qrcode(content); } /** * Alias of {@link #qrcode(String)} * * @param content * the content to generate the barcode * @return the barcode as a binary result */ public static ZXingResult renderQrcode(String content) { return qrcode(content); } /** * Returns a {@link RenderTemplate} result with a render arguments map. * Note the template path should be set via {@link ActionContext#templatePath(String)} * method * * @param args * the template arguments * @return a result to render template */ public static RenderTemplate template(Map args) { return RenderTemplate.of(args); } /** * Alias of {@link #template(Map)} * * @param args * the template arguments * @return a result to render template */ public static RenderTemplate renderTemplate(Map args) { return template(args); } /** * This method is deprecated, please use {@link #template(Object...)} instead * * @param args * template argument list */ public static RenderTemplate renderTemplate(Object... args) { return RenderTemplate.get(); } /** * Kind of like {@link #render(Object...)}, the only differences is this method force to render a template * without regarding to the request format * * @param args * template argument list */ public static RenderTemplate template(Object... args) { return RenderTemplate.get(ActionContext.current().successStatus()); } /** * The caller to this magic {@code render} method is subject to byte code enhancement. All * parameter passed into this method will be put into the application context via * {@link ActionContext#renderArg(String, Object)} using the variable name found in the * local variable table. If the first argument is of type String and there is no variable name * associated with that variable then it will be treated as template path and get set to the * context via {@link ActionContext#templatePath(String)} method. *

This method returns different render results depends on the request format

* * * * * * * * * * * * * * * * * *
FormatResult type
{@link org.osgl.http.H.Format#json}A JSON string that map the arguments to their own local variable names
{@link org.osgl.http.H.Format#html} or any other text formats{@link RenderTemplate}
{@link org.osgl.http.H.Format#pdf} or any other binary formatIf first argument is of type File or InputStream, then outbound the * content as a binary stream, otherwise throw out {@link org.osgl.exception.UnsupportedException}
* * @param args * any argument that can be put into the returned JSON/XML data or as template arguments */ public static RenderAny render(Object... args) { return RenderAny.get(); } public static Result inferResult(Result r, ActionContext actionContext) { return r; } public static Result inferPrimitiveResult(Object v, ActionContext actionContext, boolean requireJSON, boolean requireXML, boolean isArray, boolean isDateTime) { H.Status status = actionContext.successStatus(); if (requireJSON) { if (isArray) { return RenderJSON.of(status, $.toString2(v)); } return RenderJSON.of(status, C.Map("result", v)); } else if (requireXML) { return RenderXML.of(status, S.concat("", S.string(v), "")); } else if (v instanceof byte[]) { H.Format fmt = actionContext.accept(); if (H.Format.UNKNOWN == fmt) { actionContext.resp().contentType("application/octet-stream"); } return new RenderBinary((byte[]) v); } else { H.Format fmt = actionContext.accept(); String s = $$.toString(v, isDateTime, isArray); if (HTML == fmt || H.Format.UNKNOWN == fmt) { return RenderHtml.of(status, s); } if (fmt.isText()) { return RenderText.of(status, fmt, s, status.toString()); } throw E.unexpected("Cannot apply text result to format: %s", fmt); } } public static Result inferResult(Map map, ActionContext actionContext) { if (actionContext.acceptJson()) { return RenderJSON.of(actionContext.successStatus(), map); } return RenderTemplate.of(actionContext.successStatus(), map); } /** * @param array * @param actionContext * @return */ public static Result inferResult(Object[] array, ActionContext actionContext) { if (actionContext.acceptJson()) { return RenderJSON.of(actionContext.successStatus(), array); } throw E.tbd("render template with render args in array"); } /** * Infer {@link Result} from an {@link InputStream}. If the current context is in * {@code JSON} format then it will render a {@link RenderJSON JSON} result from the content of the * input stream. Otherwise, it will render a {@link RenderBinary binary} result from the inputstream * * @param is * the inputstream * @param actionContext * @return a Result inferred from the inputstream specified */ public static Result inferResult(InputStream is, ActionContext actionContext) { if (actionContext.acceptJson()) { return RenderJSON.of(actionContext.successStatus(), IO.readContentAsString(is)); } else { // name must be a blank string, `null` will trigger // NPE in SObject lib return new RenderBinary(is, "", true).status(actionContext.successStatus()); } } /** * Infer {@link Result} from an {@link File}. If the current context is in * {@code JSON} format then it will render a {@link RenderJSON JSON} result from the content of the * file. Otherwise, it will render a {@link RenderBinary binary} result from the file specified * * @param file * the file * @param actionContext * @return a Result inferred from the file specified */ public static Result inferResult(File file, ActionContext actionContext) { if (actionContext.acceptJson()) { return RenderJSON.of(actionContext.successStatus(), IO.readContentAsString(file)); } else { return new RenderBinary(file).status(actionContext.successStatus()); } } public static Result inferResult(ISObject sobj, ActionContext context) { if (context.acceptJson()) { return RenderJSON.of(context.successStatus(), sobj.asString()); } else { return binary(sobj).status(context.successStatus()); } } /** * Infer a {@link Result} from a {@link Object object} value v: *
    *
  • If v is {@code null} then null returned
  • *
  • If v is instance of {@code Result} then it is returned directly
  • * to infer the {@code Result} *
  • If v is instance of {@code InputStream} then {@link #inferResult(InputStream, ActionContext)} is used * to infer the {@code Result}
  • *
  • If v is instance of {@code File} then {@link #inferResult(File, ActionContext)} is used * to infer the {@code Result}
  • *
  • If v is instance of {@code Map} then {@link #inferResult(Map, ActionContext)} is used * to infer the {@code Result}
  • *
  • If v is an array of {@code Object} then {@link #inferResult(Object[], ActionContext)} is used * to infer the {@code Result}
  • *
* * @param meta * the HandlerMethodMetaInfo * @param v * the value to be rendered * @param context * the action context * @param hasTemplate * a boolean flag indicate if the current handler method has corresponding template * @return the rendered result */ public static Result inferResult(HandlerMethodMetaInfo meta, Object v, ActionContext context, boolean hasTemplate) { if (v instanceof Result) { return (Result) v; } final H.Request req = context.req(); final H.Status status = context.successStatus(); if (Act.isProd() && v instanceof Versioned && req.method().safe()) { processEtag(meta, v, context, req); } if (hasTemplate) { if (v instanceof Map) { return inferToTemplate(((Map) v), context); } return inferToTemplate(v, context); } boolean requireJSON = context.acceptJson(); boolean requireXML = !requireJSON && context.acceptXML(); if (null == v) { // the following code breaks before handler without returning result //return requireJSON ? RenderJSON.of("{}") : requireXML ? RenderXML.of("") : null; return null; } Class vCls = v.getClass(); boolean isDateTimeType = $$.isDateTimeType(vCls); if ($.isSimpleType(vCls) || isDateTimeType) { boolean isArray = vCls.isArray(); return inferPrimitiveResult(v, context, requireJSON, requireXML, isArray, isDateTimeType); } else if (v instanceof InputStream) { return inferResult((InputStream) v, context); } else if (v instanceof File) { return inferResult((File) v, context); } else if (v instanceof ISObject) { return inferResult((ISObject) v, context); } else if (v instanceof Map) { return RenderJSON.of(status, v); } else { if (requireJSON) { boolean isIterable = v instanceof Iterable; if (isIterable) { v = new FastJsonIterable((Iterable) v); } // no need to check string case as it is already checked above if (v instanceof $.Visitor) { return RenderJSON.of(($.Visitor) v); } else if (v instanceof $.Func0) { return RenderJSON.of(($.Func0) v); } PropertySpec.MetaInfo propertySpec = PropertySpec.MetaInfo.withCurrent(meta, context); boolean possibleLargeResponse = context.isLargeResponse(); JsonWriter jsonWriter = new JsonWriter(v, propertySpec, false, context); return possibleLargeResponse ? RenderJSON.of(jsonWriter) : RenderJSON.of(jsonWriter.asContentProducer()); } else if (context.acceptXML()) { PropertySpec.MetaInfo propertySpec = PropertySpec.MetaInfo.withCurrent(meta, context); return new FilteredRenderXML(status, v, propertySpec, context); } else if (context.accept() == H.Format.CSV) { PropertySpec.MetaInfo propertySpec = PropertySpec.MetaInfo.withCurrent(meta, context); return RenderCSV.of(status, v, propertySpec, context); } else { boolean isArray = vCls.isArray(); return inferPrimitiveResult(v, context, false, requireXML, isArray, isDateTimeType); } } } private static void processEtag(HandlerMethodMetaInfo meta, Object v, ActionContext context, H.Request req) { if (!(v instanceof Versioned)) { return; } String version = ((Versioned) v)._version(); String etagVersion = etag(meta, version); if (req.etagMatches(etagVersion)) { throw NotModified.get(); } else { context.resp().etag(etagVersion); } } private static String etag(HandlerMethodMetaInfo meta, String version) { return S.newBuffer(version).append(meta.hashCode()).toString(); } private static Result inferToTemplate(Object v, ActionContext actionContext) { actionContext.renderArg("result", v); return RenderTemplate.get(); } private static Result inferToTemplate(Map map, ActionContext actionContext) { return RenderTemplate.of(map); } private static H.Status successStatus() { return ActionContext.current().successStatus(); } } /** * Controller class extends this class automatically get `ActionContext` injected * as a field */ class Base extends Util { @Inject protected ActionContext context; } }