How to avoid wrapping my custom exception that handled in error handler with CompletionException when I use my custom filter

552 views
Skip to first unread message

almot...@capellasolutions.com

unread,
Jan 8, 2017, 7:51:48 AM1/8/17
to Play Framework
Hello, I have an issue with my customer ErrorHandler and custom filter with custom exception, the issue simply that I throw custom exception inside my controller and I handle it inside error handle so it returns as a result, but when I apply my filter I got some an exception that make my error handler not working correctly, when I throw my exception (as example BusinessException) I must return it as a bad request with my custom message so the error handler in my front end catch it and show the user what the problem, the problem when I enable my TLSFilter the only error I get to client is internal error with message Execution exception[[CompletionException: @null: null]] .

I know the issue that my filter sit between the controller and my error handler, so it will return an exception to filter "next.apply(rh)" before the error handler gets the chance to handle the error and return a result.

How to solve this, how to make my filter after the error handler, or any workaround to get the same result as what I do in my filter now.

My filter:

public class TLSFilter extends Filter {

@Inject
public TLSFilter(Materializer mat) {
super(mat);
}

@Override
public CompletionStage<Result> apply(Function<Http.RequestHeader, CompletionStage<Result>> next, Http.RequestHeader rh) {
if (Play.current().isProd()) {
String[] httpsHeader = rh.headers().getOrDefault(Http.HeaderNames.X_FORWARDED_PROTO, new String[]{"http"});
if (Strings.isNullOrEmpty(httpsHeader[0]) || httpsHeader[0].equalsIgnoreCase("http")) {
return CompletableFuture.completedFuture(Results.movedPermanently("https://".concat(rh.host().concat(rh.uri()))));
}
}
return next.apply(rh).toCompletableFuture();
}
}

And to us it:

public class AppFilters extends DefaultHttpFilters {

@Inject
public AppFilters(TLSFilter tlsFilter, GzipFilter gzipFilter) {
super(tlsFilter, gzipFilter);
}
}

Then enabled it:
play.http.filters = "filters.AppFilters"
play.http.errorHandler = "handlers.ErrorHandler"

And this is my error handler:

public class ErrorHandler extends DefaultHttpErrorHandler {

@Inject
public ErrorHandler(Configuration configuration, Environment environment,
OptionalSourceMapper sourceMapper, Provider<Router> routes) {
super(configuration, environment, sourceMapper, routes);
}

@Override
protected CompletionStage<Result> onProdServerError(RequestHeader request, UsefulException exception) {
if (exception instanceof NoRequestBodyException) {
Logger.warn(exception.getMessage());
return CompletableFuture.completedFuture(
Results.badRequest(exception.getMessage()));
} else if (exception instanceof BusinessException) {
Logger.warn(exception.getMessage());
return CompletableFuture.completedFuture(
Results.badRequest(exception.getMessage()));
} else if (exception instanceof ApplicationException) {
Logger.warn(exception.getMessage());
exception.printStackTrace();
return CompletableFuture.completedFuture(
Results.badRequest(exception.getMessage()));
} else {
exception.printStackTrace();
return CompletableFuture.completedFuture(
Results.internalServerError("A server error occurred: " + exception.getMessage())
);
}
}

@Override
protected void logServerError(RequestHeader request, UsefulException exception) {
if (exception instanceof BusinessException) {
Logger.warn(String.format("\n\n! @%s - Business Exception, for (%s) [%s] ->\n",
exception.id, request.method(), request.uri())
);
} else {
super.logServerError(request, exception);
}
}

@Override
protected CompletionStage<Result> onForbidden(RequestHeader request, String message) {
return CompletableFuture.completedFuture(
Results.forbidden("You're not allowed to access this resource.")
);
}

@Override
protected CompletionStage<Result> onDevServerError(RequestHeader request, UsefulException exception) {
Logger.error(AppUtils.concatStrings("Exception: method: ", request.path(), " time:", DateTime.now().toString(),
" uri=", request.uri(), " remote-address=", request.remoteAddress(), "cause: ", exception.getMessage()));
if (exception instanceof NoRequestBodyException) {
return CompletableFuture.completedFuture(
Results.badRequest(exception.getMessage()));
} else if (exception instanceof BusinessException) {
return CompletableFuture.completedFuture(
Results.badRequest(exception.getMessage()));
} else if (exception instanceof ApplicationException) {
return CompletableFuture.completedFuture(
Results.badRequest(exception.getMessage()));
}
return super.onDevServerError(request, exception);
}

}


The exception:

[error] - application - 

! @72jcm46io - Internal server error, for (POST) [/api/users/checkUsername] ->

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[CompletionException: @null: null]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:293)
at play.api.http.HttpErrorHandlerExceptions.throwableToUsefulException(HttpErrorHandler.scala)
at play.http.DefaultHttpErrorHandler.throwableToUsefulException(DefaultHttpErrorHandler.java:169)
at play.http.DefaultHttpErrorHandler.onServerError(DefaultHttpErrorHandler.java:131)
at play.core.j.JavaHttpErrorHandlerAdapter$$anonfun$onServerError$1.apply(JavaHttpErrorHandlerAdapter.scala:22)
at play.core.j.JavaHttpErrorHandlerAdapter$$anonfun$onServerError$1.apply(JavaHttpErrorHandlerAdapter.scala:22)
at play.core.j.JavaHelpers$$anonfun$invokeWithContext$1.apply(JavaHelpers.scala:142)
at play.core.j.JavaHelpers$$anonfun$invokeWithContext$1.apply(JavaHelpers.scala:141)
at play.core.j.JavaHelpers$class.withContext(JavaHelpers.scala:153)
at play.core.j.JavaHelpers$.withContext(JavaHelpers.scala:162)
at play.core.j.JavaHelpers$class.invokeWithContext(JavaHelpers.scala:141)
at play.core.j.JavaHelpers$.invokeWithContext(JavaHelpers.scala:162)
at play.core.j.JavaHttpErrorHandlerAdapter.onServerError(JavaHttpErrorHandlerAdapter.scala:22)
at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:100)
at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:99)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:346)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:345)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:70)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248)
at scala.concurrent.Promise$class.complete(Promise.scala:55)
at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:153)
at scala.concurrent.Future$$anonfun$recoverWith$1$$anonfun$apply$6.apply(Future.scala:346)
at scala.concurrent.Future$$anonfun$recoverWith$1$$anonfun$apply$6.apply(Future.scala:346)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.processBatch$1(BatchingExecutor.scala:63)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.apply$mcV$sp(BatchingExecutor.scala:78)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.apply(BatchingExecutor.scala:55)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.apply(BatchingExecutor.scala:55)
at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:72)
at scala.concurrent.BatchingExecutor$Batch.run(BatchingExecutor.scala:54)
at scala.concurrent.Future$InternalCallbackExecutor$.unbatchedExecute(Future.scala:601)
at scala.concurrent.BatchingExecutor$class.execute(BatchingExecutor.scala:106)
at scala.concurrent.Future$InternalCallbackExecutor$.execute(Future.scala:599)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40)
at scala.concurrent.impl.Promise$DefaultPromise.scala$concurrent$impl$Promise$DefaultPromise$$dispatchOrAddCallback(Promise.scala:280)
at scala.concurrent.impl.Promise$DefaultPromise.onComplete(Promise.scala:270)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:346)
at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:345)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55)
at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply$mcV$sp(BatchingExecutor.scala:91)
at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply(BatchingExecutor.scala:91)
at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply(BatchingExecutor.scala:91)
at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:72)
at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:90)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:415)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.util.concurrent.CompletionException: @null: null
at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:292)
at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:308)
at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:593)
at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:577)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1977)
at scala.concurrent.java8.FuturesConvertersImpl$CF.apply(FutureConvertersImpl.scala:21)
at scala.concurrent.java8.FuturesConvertersImpl$CF.apply(FutureConvertersImpl.scala:18)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.processBatch$1(BatchingExecutor.scala:63)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.apply$mcV$sp(BatchingExecutor.scala:78)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.apply(BatchingExecutor.scala:55)
at scala.concurrent.BatchingExecutor$Batch$$anonfun$run$1.apply(BatchingExecutor.scala:55)
at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:72)
at scala.concurrent.BatchingExecutor$Batch.run(BatchingExecutor.scala:54)
at scala.concurrent.Future$InternalCallbackExecutor$.unbatchedExecute(Future.scala:601)
at scala.concurrent.BatchingExecutor$class.execute(BatchingExecutor.scala:106)
at scala.concurrent.Future$InternalCallbackExecutor$.execute(Future.scala:599)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248)
at scala.concurrent.Promise$class.tryFailure(Promise.scala:112)
at scala.concurrent.impl.Promise$DefaultPromise.tryFailure(Promise.scala:153)
at play.api.mvc.Filter$$anon$1$$anonfun$apply$4$$anonfun$apply$1.applyOrElse(Filters.scala:83)
at play.api.mvc.Filter$$anon$1$$anonfun$apply$4$$anonfun$apply$1.applyOrElse(Filters.scala:77)
... 15 common frames omitted
Caused by: exceptions.BusinessException: The username you choose is unavailable.
at controllers.SecurityController.checkUsername(SecurityController.java:80)
at users.Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(Routes.scala:411)
at users.Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(Routes.scala:411)
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:157)
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:156)
at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:136)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:73)
at play.http.HttpRequestHandler$1.call(HttpRequestHandler.java:54)
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:108)
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:108)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:56)
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:70)
at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:48)
at scala.concurrent.impl.Future$.apply(Future.scala:31)
at scala.concurrent.Future$.apply(Future.scala:494)
at play.core.j.JavaAction.apply(JavaAction.scala:108)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5$$anonfun$apply$6.apply(Action.scala:112)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5$$anonfun$apply$6.apply(Action.scala:112)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5.apply(Action.scala:111)
at play.api.mvc.Action$$anonfun$apply$2$$anonfun$apply$5.apply(Action.scala:110)
at scala.Option.map(Option.scala:146)
at play.api.mvc.Action$$anonfun$apply$2.apply(Action.scala:110)
at play.api.mvc.Action$$anonfun$apply$2.apply(Action.scala:103)
at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:253)
at scala.concurrent.Future$$anonfun$flatMap$1.apply(Future.scala:251)
... 13 common frames omitted
[error] - application - Exception: method: /api/users/checkUsername time:2017-01-08T13:32:56.967+02:00 uri=/api/users/checkUsername remote-address=127.0.0.1cause: Execution exception[[CompletionException: @null: null]]

Reply all
Reply to author
Forward
0 new messages