diff --git a/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt b/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt index 3d34d81df1..1132701aea 100644 --- a/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt +++ b/commons/rapidoid-commons/src/main/resources/rapidoid-classes.txt @@ -423,6 +423,7 @@ org.rapidoid.http.HttpUtils org.rapidoid.http.HttpVerb org.rapidoid.http.HttpWrapper org.rapidoid.http.impl.AbstractViewResolver +org.rapidoid.http.impl.BodyRenderer org.rapidoid.http.impl.CachedResp org.rapidoid.http.impl.ChunkedResponse org.rapidoid.http.impl.ErrorHandlerResolver diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java index 7e1e3b5d2e..f076fb7fd1 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java @@ -7,9 +7,9 @@ * 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. @@ -34,6 +34,7 @@ import org.rapidoid.gui.reqinfo.ReqInfo; import org.rapidoid.http.customize.Customization; import org.rapidoid.http.customize.HttpResponseRenderer; +import org.rapidoid.http.handler.HandlerResultProcessor; import org.rapidoid.http.impl.MaybeReq; import org.rapidoid.io.Res; import org.rapidoid.lambda.Mapper; @@ -236,6 +237,12 @@ public static byte[] responseToBytes(Req req, Object result, MediaType contentTy } } + public static Object resultOf(Req req, Object result) { + result = HandlerResultProcessor.INSTANCE.postProcessResult(req, result); + HttpUtils.resultToResponse(req, result); + return req.response().result(); + } + public static void resultToResponse(Req req, Object result) { if (result instanceof Req) { diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpTxWrapper.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpTxWrapper.java index ae2423d91d..761d7a0e8f 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpTxWrapper.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpTxWrapper.java @@ -7,9 +7,9 @@ * 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. @@ -28,6 +28,7 @@ import org.rapidoid.http.HttpWrapper; import org.rapidoid.http.Req; import org.rapidoid.http.RespBody; +import org.rapidoid.http.impl.BodyRenderer; import org.rapidoid.http.impl.RespImpl; import org.rapidoid.jpa.JPA; import org.rapidoid.u.U; @@ -58,24 +59,29 @@ public Object wrap(final Req req, final HandlerInvocation invocation) { JPA.transaction(new Runnable() { @Override public void run() { + Object res; + try { - Object res = invocation.invoke(); + res = invocation.invoke(); - if (res instanceof Throwable) { - // throw to rollback - Throwable err = (Throwable) res; - throw U.rte("Error occurred inside the transactional web handler!", err); - } + } catch (Exception e) { + // throw to rollback + throw U.rte("Error occurred inside the transactional web handler!", e); + } + + if (res instanceof Throwable) { + // throw to rollback + Throwable err = (Throwable) res; + throw U.rte("Error occurred inside the transactional web handler!", err); + + } else { // serialize the result into a HTTP response body, while still inside tx (see #153) RespImpl resp = (RespImpl) req.response(); // TODO find a cleaner access - RespBody body = resp.resultToRespBody(res); + RespBody body = BodyRenderer.resultToRespBody(resp, res); result.set(body); - - } catch (Exception e) { - // throw to rollback - throw U.rte("Error occurred inside the transactional web handler!", e); } + } }, readOnly); diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpWrappers.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpWrappers.java index 9b701f6286..2888698374 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpWrappers.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/handler/HttpWrappers.java @@ -29,6 +29,7 @@ import org.rapidoid.http.impl.RouteOptions; import org.rapidoid.u.U; +import java.util.Collections; import java.util.List; @Authors("Nikolche Mihajlovski") @@ -36,16 +37,16 @@ public class HttpWrappers extends RapidoidThing { static HttpWrapper[] assembleWrappers(FastHttp http, RouteOptions options) { - List wrappers = U.list(getConfiguredWrappers(http, options)); + List wrappers = U.list(); + + wrappers.add(new HttpAuthWrapper(options.roles())); TransactionMode txMode = U.or(options.transaction(), TransactionMode.NONE); if (txMode != TransactionMode.NONE) { - HttpWrapper txWrapper = new HttpTxWrapper(txMode); - wrappers.add(0, txWrapper); + wrappers.add(new HttpTxWrapper(txMode)); } - HttpWrapper authWrapper = new HttpAuthWrapper(options.roles()); - wrappers.add(0, authWrapper); + Collections.addAll(wrappers, getConfiguredWrappers(http, options)); return U.arrayOf(HttpWrapper.class, wrappers); } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/BodyRenderer.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/BodyRenderer.java new file mode 100644 index 0000000000..d7f72e934e --- /dev/null +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/BodyRenderer.java @@ -0,0 +1,104 @@ +/*- + * #%L + * rapidoid-http-fast + * %% + * Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors + * %% + * 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% + */ + +package org.rapidoid.http.impl; + +import org.rapidoid.RapidoidThing; +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.http.HttpUtils; +import org.rapidoid.http.MediaType; +import org.rapidoid.http.Req; +import org.rapidoid.http.RespBody; +import org.rapidoid.http.customize.Customization; +import org.rapidoid.http.customize.HttpResponseRenderer; +import org.rapidoid.http.impl.lowlevel.HttpIO; +import org.rapidoid.log.Log; +import org.rapidoid.log.LogLevel; +import org.rapidoid.u.U; +import org.rapidoid.util.Msc; + +@Authors("Nikolche Mihajlovski") +@Since("5.5.2") +public class BodyRenderer extends RapidoidThing { + + static RespBody toRespBody(Req req, RespImpl resp) { + try { + return createRespBodyFromResult(req, resp); + + } catch (Throwable e) { + HttpIO.INSTANCE.error(req, e, LogLevel.ERROR); + + try { + return createRespBodyFromResult(req, resp); + + } catch (Exception e1) { + Log.error("Internal rendering error!", e1); + return new RespBodyBytes(HttpUtils.getErrorMessageAndSetCode(resp, e1).getBytes()); + } + } + } + + private static RespBody createRespBodyFromResult(Req req, RespImpl resp) { + Object result = resp.result(); + Object body = resp.body(); + + if (result instanceof RespBody) { + return (RespBody) result; + + } else if (body instanceof RespBody) { + return (RespBody) body; + + } else if (resp.mvc()) { + byte[] bytes = ResponseRenderer.renderMvc((ReqImpl) req, resp); + HttpUtils.postProcessResponse(resp); // the response might have been changed, so post-process again + return new RespBodyBytes(bytes); + + } else if (body != null) { + return new RespBodyBytes(Msc.toBytes(body)); + + } else if (result != null) { + return resultToRespBody(resp, result); + + } else { + throw U.rte("There's nothing to render!"); + } + } + + public static RespBody resultToRespBody(RespImpl resp, Object result) { + return new RespBodyBytes(HttpUtils.responseToBytes(resp.request(), result, resp.contentType(), mediaResponseRenderer(resp))); + } + + private static HttpResponseRenderer mediaResponseRenderer(RespImpl resp) { + Customization customization = Customization.of(resp.request()); + + if (resp.contentType().equals(MediaType.JSON)) { + return customization.jsonResponseRenderer(); + + } else if (resp.contentType().equals(MediaType.XML_UTF_8)) { + return customization.xmlResponseRenderer(); + + } else { + // defaults to json + return customization.jsonResponseRenderer(); + } + } + +} diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java index c5df012a99..a040a424d9 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java @@ -37,7 +37,6 @@ import org.rapidoid.http.impl.lowlevel.HttpIO; import org.rapidoid.io.Upload; import org.rapidoid.log.Log; -import org.rapidoid.log.LogLevel; import org.rapidoid.net.abstracts.Channel; import org.rapidoid.net.abstracts.IRequest; import org.rapidoid.u.U; @@ -566,31 +565,14 @@ private void renderResponse() { HttpIO.INSTANCE.done(this); } else { - // first create a response body from the result (with error handling) - RespBody body = toRespBody(); + // render the response body + RespBody body = BodyRenderer.toRespBody(this, response); - // then render the response + // render the response doRendering(response.code(), body); } } - private RespBody toRespBody() { - try { - return response.createRespBodyFromResult(); - - } catch (Throwable e) { - HttpIO.INSTANCE.error(this, e, LogLevel.ERROR); - - try { - return response.createRespBodyFromResult(); - - } catch (Exception e1) { - Log.error("Internal rendering error!", e1); - return new RespBodyBytes(HttpUtils.getErrorMessageAndSetCode(response, e1).getBytes()); - } - } - } - private String validateResponse() { if (response == null) { return "Response wasn't provided!"; diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java index 7fa012e067..ac7bf36afa 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java @@ -7,9 +7,9 @@ * 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. @@ -30,15 +30,16 @@ import org.rapidoid.config.Conf; import org.rapidoid.ctx.Ctxs; import org.rapidoid.ctx.UserInfo; -import org.rapidoid.http.*; +import org.rapidoid.http.HttpUtils; +import org.rapidoid.http.MediaType; +import org.rapidoid.http.Req; +import org.rapidoid.http.Resp; import org.rapidoid.http.customize.Customization; -import org.rapidoid.http.customize.HttpResponseRenderer; import org.rapidoid.http.customize.LoginProvider; import org.rapidoid.http.customize.RolesProvider; import org.rapidoid.io.IO; import org.rapidoid.net.AsyncLogic; import org.rapidoid.u.U; -import org.rapidoid.util.Msc; import org.rapidoid.util.MscOpts; import org.rapidoid.util.Tokens; import org.rapidoid.web.Screen; @@ -507,36 +508,6 @@ public String toString() { '}'; } - RespBody createRespBodyFromResult() { - Object result = result(); - Object body = body(); - - if (result instanceof RespBody) { - return (RespBody) result; - - } else if (body instanceof RespBody) { - return (RespBody) body; - - } else if (mvc()) { - byte[] bytes = ResponseRenderer.renderMvc(req, this); - HttpUtils.postProcessResponse(this); // the response might have been changed, so post-process again - return new RespBodyBytes(bytes); - - } else if (body != null) { - return new RespBodyBytes(Msc.toBytes(body)); - - } else if (result != null) { - return resultToRespBody(result); - - } else { - throw U.rte("There's nothing to render!"); - } - } - - public RespBody resultToRespBody(Object result) { - return new RespBodyBytes(HttpUtils.responseToBytes(req, result, contentType(), mediaResponseRenderer())); - } - @Override public Resp chunk(byte[] data) { OutputStream chnk = out(); @@ -591,17 +562,4 @@ void finish() { } } - private HttpResponseRenderer mediaResponseRenderer() { - if (contentType.equals(MediaType.JSON)) { - return Customization.of(req).jsonResponseRenderer(); - - } else if (contentType.equals(MediaType.XML_UTF_8)) { - return Customization.of(req).xmlResponseRenderer(); - - } else { - // defaults to json - return Customization.of(req).jsonResponseRenderer(); - } - } - } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java index 2239672dfe..b04df0d384 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/lowlevel/LowLevelHttpIO.java @@ -7,9 +7,9 @@ * 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. @@ -35,7 +35,6 @@ import org.rapidoid.data.JSON; import org.rapidoid.http.*; import org.rapidoid.http.customize.Customization; -import org.rapidoid.http.handler.HandlerResultProcessor; import org.rapidoid.http.impl.MaybeReq; import org.rapidoid.http.impl.ReqImpl; import org.rapidoid.job.Jobs; @@ -191,9 +190,7 @@ void error(final Req req, final Throwable error, LogLevel logLevel) { Resp resp = req.response().code(500).result(null); Object result = Customization.of(req).errorHandler().handleError(req, resp, error); - result = HandlerResultProcessor.INSTANCE.postProcessResult(req, result); - - HttpUtils.resultToResponse(req, result); + HttpUtils.resultOf(req, result); } catch (Exception e) { Log.error("An error occurred inside the error handler!", e); diff --git a/rapidoid-integration-tests/src/test/java/org/rapidoid/rest/DynamicClientTest.java b/rapidoid-integration-tests/src/test/java/org/rapidoid/rest/DynamicClientTest.java index fe5e22d8d9..4213cefa78 100644 --- a/rapidoid-integration-tests/src/test/java/org/rapidoid/rest/DynamicClientTest.java +++ b/rapidoid-integration-tests/src/test/java/org/rapidoid/rest/DynamicClientTest.java @@ -45,29 +45,18 @@ public void testDynamic() { On.get("/nums").managed(false).contentType(MediaType.JSON).serve("[1, 2, 3]"); - On.get("/size").json(new ReqHandler() { - @Override - public Object execute(Req req) throws Exception { - return req.param("s").length(); - } - }); + On.get("/size").json((ReqHandler) req -> req.param("s").length()); + + On.post("/echo").json((ReqHandler) req -> { + req.async(); + + Jobs.schedule(() -> { + U.must(Current.request() == req); + Resp resp = req.response(); + resp.result(req.data()).done(); + }, 1000, TimeUnit.MILLISECONDS); - On.post("/echo").json(new ReqHandler() { - @Override - public Object execute(final Req req) throws Exception { - req.async(); - - Jobs.schedule(new Runnable() { - @Override - public void run() { - U.must(Current.request() == req); - Resp resp = req.response(); - resp.result(req.data()).done(); - } - }, 1000, TimeUnit.MILLISECONDS); - - return req; - } + return req; }); eq(client.abc(), "abc-ok");