I needed to display some validation messages received from the service each in its proper place, and I solved it putting the messages in an exception:
class InvalidInputException extends RuntimeException {
def errors
InvalidInputException(String s) {
super(s)
}
InvalidInputException(String s, errors) {
super(s)
this.errors = errors
}
}
That way, I could throw the exception sending the errors:
if (errors) {
throw new InvalidInputException("There were some errors", errors)
}
.. and then I deal with the errors later in the controller, after catching the exception:
...
catch (InvalidInputException e) {
if (e.errors) {
// set them up to display appropriately
}
// render the view
}
Now, I've read somewhere that Groovy's exceptions can cost too much, so... is this too bad?
What problems may I encounter putting additional data in an Exception?
It's much easier than fiddling with returned error messages, and the code is much shorter.
If you are concerned about the performance of exceptions in Java, I suggest you to look at this other question.
If you not create a exception, the other possibility is to make your service return an object that represents the result of this flow. Something like:
class MyServiceResult {
List<String> errorCodes = [] //codes for i18n
void addErrorCode(String errorCode) {
errorCodes << errorCode //add error to list
}
boolean isValid() {
return (!(errorCodes.size() > 0)) //if we have errors then isn't valid.
}
List<String> getErrorCodes() {
return errorCodes.asImmutable()
}
}
And just use it in your service method
class MyService {
MyServiceResult someMethod() {
MyServiceResult result = new MyServiceResult()
...
result.addErrorCode('some.key.here')
...
return result
}
}
class MyController {
def myService
def action() {
MyServiceResult result = myService.someMethod()
if(!result.isValid()) {
//handle errors
}
}
}
But it's important to say that it can be 2x slower than creating an exception. You can check all details in this post.
Related
I wrote service for manual requeuing events from one queue to another.
public class ReQueueService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
InfoLog infoLog;
while (rabbitTemplate != null &&
(infoLog = (InfoLog) rabbitTemplate.receiveAndConvert(EVENT_WAITING_FOR_REQUEUE)) != null
) {
rabbitTemplate.convertAndSend(SOME_QUEUE, infoLog.getSomeEvent());
}
}
}
The problem I am facing is getting:
Too many invocations for:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
} (2 invocations)
Matching invocations (ordered by last occurrence):
2 * rabbitTemplate.convertAndSend(SOME_QUEUE, ...
while my code in test looks like this:
class ReQueueServiceTest extends Specification {
def "should resend single event to some queue" () {
given:
InfoLog infoLog = Fixtures.createInfoLog()
def rabbitTemplate = Mock(RabbitTemplate){
receiveAndConvert(EVENT_WAITING_FOR_REQUEUE) >> { infoLog }
}
ReQueueService reSyncService = new ReQueueService(rabbitTemplate)
when:
reSyncService.retry()
then:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
}
}
}
The question is why I have 2 invocations, if I stubb only one event?
EDIT:
link to repo with example: https://gitlab.com/bartekwichowski/spock-too-many
Thanks for the repo link. As soon as I could run the test and inspect the behaviour live, it was pretty easy to find out what was wrong. First I will make an educated guess about what you actually want to test:
The mock's receiveAndConvert method should return str when it is called first and then null when called again.
Subsequently you want to verify that the while loop runs exactly 1 iteration, i.e. that convertAndSend is called with exactly the parameters you expect.
This can be achieved by
receiveAndConvert("FOO") >>> [str, null]
1 * rabbitTemplate.convertAndSend("BAR", str) (no need for ugly assertions inside a stubbed method, the parameters are verified against your parameter constraints already)
If I refactor your specification a little bit for prettier variable names and less verbosity, it looks like this:
class ReSyncServiceTest extends Specification {
def "should resend single event to resource sync queue"() {
given:
def message = "someValue"
def rabbitTemplate = Mock(RabbitTemplate) {
receiveAndConvert("FOO") >>> [message, null]
}
when:
new ReSyncService(rabbitTemplate).retry()
then:
1 * rabbitTemplate.convertAndSend("BAR", message)
}
}
P.S.: Your version with the assertion inside does not return anything explicitly, but implicitly the result of the last assertion. Be careful with that. With >> { ... } you are stubbing the method result! It would always return true in the version you have in Git and the test only terminates because you added the 1 * limit. If it was not there, you would have an endless loop. Your code did not do what you thought it did. Maybe the Spock manual can help you there. :-)
P.P.S.: Maybe you want to refactor your application code to be a bit easier to understand and maintain and to be a little less "smart". Also there is no need to check that rabbitTemplate != null in every iteration, once should be enough. How about this?
#Slf4j
#Service
#AllArgsConstructor
public class ReSyncService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
if (rabbitTemplate == null)
return;
String event;
while (null != (event = getEventFromQueue()))
rabbitTemplate.convertAndSend("BAR", event);
}
protected String getEventFromQueue() {
return (String) rabbitTemplate.receiveAndConvert("FOO");
}
}
Hello good reactor folks - I trying to write some reactive code (surprising eh?) and have hit a slight snag. I think it might be a reactor bug, but thought I'd ask here first before posting a bug.
For context: I have a cache Map<Key, Mono<Value>>. A client will request data - we check the cache and use what is essentially computeIfAbsent to place a Mono with .cache() into the cache if nothing has yet been cached for that key. The client then takes the Mono and does magic (not relevant here). Now, the catch is that the population of the cache may encounter transient errors, so we don't want to cache errors - the current request will error but the "next" client, when it subscribes, should trigger the entire pipeline to rerun.
Having read around, for example this closed issue, I settled on Mono#cache(ttlForValue, ttlForError, ttlForEmpty).
This is where things get interesting.
As I don't want to cache error (or empty, but ignore that) signals I found the following documentation promising
If the relevant TTL generator throws any Exception, that exception will be propagated to the Subscriber that encountered the cache miss, but the cache will be immediately cleared, so further Subscribers might re-populate the cache in case the error was transient. In case the source was emitting an error, that error is dropped and added as a suppressed exception. In case the source was emitting a value, that value is dropped.
emphasis mine
So I tried the following (shamelessly cribbing the example in the linked GitHub issue)
public class TestBench {
public static void main(String[] args) throws Exception {
var sampleService = new SampleService();
var producer = Mono.fromSupplier(sampleService::call).cache(
__ -> Duration.ofHours(24),
//don't cache errors
e -> {throw Exceptions.propagate(e);},
//meh
() -> {throw new RuntimeException();});
try {
producer.block();
} catch (RuntimeException e) {
System.out.println("Caught exception : " + e);
}
sampleService.serverAvailable = true;
var result = producer.block();
System.out.println(result);
}
static final class SampleService {
volatile boolean serverAvailable = false;
String call() {
System.out.println("Calling service with availability: " + serverAvailable);
if (!serverAvailable) throw new RuntimeException("Error");
return "Success";
}
}
}
Output
09:12:23.991 [main] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework
Calling service with availability: false
09:12:24.034 [main] ERROR reactor.core.publisher.Operators - Operator called default onErrorDropped
java.lang.RuntimeException: Error
at uk.co.borismorris.testbench.TestBench$SampleService.call(TestBench.java:40)
at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:56)
at reactor.core.publisher.MonoCacheTime.subscribe(MonoCacheTime.java:123)
at reactor.core.publisher.Mono.block(Mono.java:1474)
at uk.co.borismorris..boris.testbench.TestBench.main(TestBench.java:26)
Caught exception : reactor.core.Exceptions$BubblingException: java.lang.RuntimeException: Error
Exception in thread "main" java.lang.RuntimeException: Error
at uk.co.borismorris.testbench.TestBench$SampleService.call(TestBench.java:40)
at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:56)
at reactor.core.publisher.MonoCacheTime.subscribe(MonoCacheTime.java:123)
at reactor.core.publisher.Mono.block(Mono.java:1474)
at uk.co.borismorris.testbench.TestBench.main(TestBench.java:26)
Suppressed: java.lang.Exception: #block terminated with an error
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:93)
at reactor.core.publisher.Mono.block(Mono.java:1475)
at uk.co.borismorris.testbench.TestBench.main(TestBench.java:31)
Well, that didn't work - the error is cached and the second subscriber just sees the same error.
Looking at the code the cause is obvious
Duration ttl = null;
try {
ttl = main.ttlGenerator.apply(signal);
}
catch (Throwable generatorError) {
signalToPropagate = Signal.error(generatorError);
STATE.set(main, signalToPropagate); //HERE
if (signal.isOnError()) {
//noinspection ThrowableNotThrown
Exceptions.addSuppressed(generatorError, signal.getThrowable());
}
}
The STATE is set to the error signal, not cleared at all. But this isn't the whole story,
the reason for the code not clearing the cache is below this block
if (ttl != null) {
main.clock.schedule(main, ttl.toMillis(), TimeUnit.MILLISECONDS);
}
else {
//error during TTL generation, signal != updatedSignal, aka dropped
if (signal.isOnNext()) {
Operators.onNextDropped(signal.get(), currentContext());
}
else if (signal.isOnError()) {
Operators.onErrorDropped(signal.getThrowable(), currentContext());
}
//immediate cache clear
main.run();
}
In this case ttl == null because the generation of the ttl threw an Exception. The signal is an error so that branch is entered and Operators.onErrorDropped is called
public static void onErrorDropped(Throwable e, Context context) {
Consumer<? super Throwable> hook = context.getOrDefault(Hooks.KEY_ON_ERROR_DROPPED,null);
if (hook == null) {
hook = Hooks.onErrorDroppedHook;
}
if (hook == null) {
log.error("Operator called default onErrorDropped", e);
throw Exceptions.bubble(e);
}
hook.accept(e);
}
So here we can see that if there is no onError hook in the context and no default set then throw Exceptions.bubble(e) is called and the code in MonoCacheTime returns early, failing to call main.run(). Hence the error stays cached indefinitely as there is no TTL!
The following code fixes that problem
public class TestBench {
private static final Logger logger = LoggerFactory.getLogger(TestBench.class);
private static final Consumer<Throwable> onErrorDropped = e -> logger.error("Dropped", e);
static {
//add default hook
Hooks.onErrorDropped(onErrorDropped);
}
public static void main(String[] args) throws Exception {
var sampleService = new SampleService();
var producer = Mono.fromSupplier(sampleService::call).cache(
__ -> Duration.ofHours(24),
//don't cache errors
e -> {throw Exceptions.propagate(e);},
//meh
() -> {throw new RuntimeException();});
try {
producer.block();
} catch (RuntimeException e) {
System.out.println("Caught exception : " + e);
}
sampleService.serverAvailable = true;
var result = producer.block();
System.out.println(result);
}
static final class SampleService {
volatile boolean serverAvailable = false;
String call() {
System.out.println("Calling service with availability: " + serverAvailable);
if (!serverAvailable) throw new RuntimeException("Error");
return "Success";
}
}
}
But this adds a global Hook, which isn't ideal. The code hints at the ability to add per-pipeline hooks, but I cannot figure out how to do that. The following works, but is obviously a hack
.subscriberContext(ctx -> ctx.put("reactor.onErrorDropped.local", onErrorDropped))
Questions
Is the above a bug, should the absence of a onErrorDropped hook cause errors to be cached indefinitely?
Is there a way to set the onErrorDropped hook in the subscriberContext rather than globally?
Follow up
From the code; it seems that returning null from a TTL generator function is supported and has the same behaviour when the signal is immediately cleared. In the case where it isn't, the subscriber sees the original error rather than the error from the TTL generator and a suppressed error - which seems perhaps neater
public static void main(String[] args) throws Exception {
var sampleService = new SampleService();
var producer = Mono.fromSupplier(sampleService::call).cache(
__ -> Duration.ofHours(24),
//don't cache errors
e -> null,
//meh
() -> null);
try {
producer.block();
} catch (RuntimeException e) {
System.out.println("Caught exception : " + e);
}
sampleService.serverAvailable = true;
var result = producer.block();
System.out.println(result);
}
Is this behaviour supported? Should it be documented?
You've indeed found a bug! And I think the documentation can also be improved for this variant of cache:
The focus on how it deals with exceptions inside TTL Function is probably misleading
There should be a documented straightforward way of "ignoring" a category of signals in the source (which is you case: you want subsequent subscribers to "retry" when the source is erroring).
The behavior is bugged due to the use of onErrorDropped (which defaults to throwing the dropped exception, thus preventing the main.run() state reset).
Unfortunately, the tests use StepVerifier#verifyThenAssertThat(), which set an onErrorDropped hook, so that last bug was never identified.
Returning null in the TTL function is not working better because the same bug happens, but this time with the original source exception being dropped/bubbled.
But there is an ideal semantic for propagating an error to the first subscriber and let the second subscriber retry: to return Duration.ZERO in the ttl Function. This is undocumented, but works right now:
IllegalStateException exception = new IllegalStateException("boom");
AtomicInteger count = new AtomicInteger();
Mono<Integer> source = Mono.fromCallable(() -> {
int c = count.incrementAndGet();
if (c == 1) throw exception;
return c;
});
Mono<Integer> cache = source.cache(v -> Duration.ofSeconds(10),
e -> Duration.ZERO,
() -> Duration.ofSeconds(10));
assertThat(cache.retry().block()).isEqualTo(2);
I'll open an issue to fix the state reset bug and focus the javadoc on the above solution, while moving the bit dealing with throwing TTL Functions in a separate shorter paragraph at the end.
edit: https://github.com/reactor/reactor-core/issues/1783
I'm working with Grails 2.4.5 and struggling to get Grails to delineate different exception types.
Suppose I want to mock the below:
class FooController {
def barService
...
def fooAction() {
try {
barService.someMethod(params)
} catch(e) {
if (e instanceof FooException) { ... }
else if (e instanceof BarException) { ... }
else { ... }
}
}
Given the test below
#TestFor(FooController)
class FooControllerSpec extends Specification {
def setup() { controller.barService = Mock(BarService) }
void "test"() {
given: "a mock dependency"
1* controller.barService.someMethod(_) >> { -> throw FooException('foo') }
when: "the action is requested"
controller.fooAction()
then: "expect the FooException behaviour from the action"
// some behaviour
}
I would expect the FooException within the mock dependency closure to have thrown
however, debugging shows the below instead:
groovy.lang.MissingMethodException: No signature of method: somePackage.FooControllerSpec$_$spock_feature_0_9_closure14.doCall() is applicable for argument types: (java.util.Arrays$ArrayList) values: [[[:]]]
Is this a bug? is there a way to mock different Exceptions in the fashion described above?
You need to specify behaviour in the then block, ie:
void "test"() {
when: "the action is requested"
controller.fooAction()
then: "expect the FooException behaviour from the action"
1 * controller.barService.someMethod(_) >> { -> throw new FooException('foo') }
response.redirectedUrl == "/some/other/path"
}
I'm trying to consolidate redundant code in my controller using a utility closure to do generic exception handler and response generation.
For instance, I want to consolidate this:
def newUser(){
def model = [:]
def errors
try{
model += [newUserObj:dao.newUser(...)]
}catch(Exception e){
errors = e.getMessage()
}
renderJson(model,error)
}
..to this:
def newUser(){
def model = [:]
def errors
doRequest(model, errors){ ->
model += [newUserObj:dao.newUser(...)]
}
}
..while moving boilerplate code to a closure:
def doRequest(model, errors, clsr){
try{
clsr.call()
}catch(Exception e){
errors = e.getMessage()
}
//!! model here is null !!
renderJson(model,error)
}
The problem happens in doRequest() when renderJson() is invoked; model is null, even though I confirmed it's assigned correctly inside the closure at model += [newUserObj:dao.newUser(...)].
Workaround
I managed to get around this issue by just returning the model from the closure:
def newUser(){
doRequest(){ ->
def model = [:]
def errors
model += [newUserObj:dao.newUser(...)]
[model:model, errors:errors]
}
}
def doRequest(clsr){
def model = [:]
def errors
try{
def r = clsr.call()
model = r['model']
errors = r['errors']
}catch(Exception e){
errors = e.getMessage()
}
renderJson(model,error)
}
..but this doesn't seem Groovy at all, I'm creating boiler plate code I was trying to avoid in the first place.
Is this what you were looking for?
import grails.converters.JSON
//Closure implementation
def doRequest(Closure clsr) {
def model = [:]
def errors = /No Error Message Yet/
try {
model = clsr(model)
} catch(Exception e) {
errors = e.getMessage()
}
renderJson(model, errors)
}
//Mimics an action method
def newUser() {
doRequest { model ->
model += [a:1] //Mimics the call to DAO in your question
//make sure to return the model after all operations completed
//model
}
}
//Mimics the render to JSON utility
private JSON renderJson(model, error) {
[model: model, errors: error] as JSON
}
//Mimics call to the action method
assert newUser().toString() ==
/{"model":{"a":1},"errors":"No Error Message Yet"}/
I could have implemented doRequest() something as below, but I did not because in that case model and errors would become part of the class (in your case it will be Controller's global properties) which we do not want.
def doRequest(Closure clsr) {
try {
clsr.resolveStrategy = Closure.DELEGATE_FIRST
clsr.delegate = this
clsr()
} catch(Exception e) {
errors = e.getMessage()
}
renderJson(model, errors)
}
Where should the separation of the UI message elements be if a grails service throws an exception? Should the message get loaded by the service and passed to the controller via the exception, or should the controller load the message based on the type of exception thrown? This assumes the message will have some parameter values that need to be filled in.
Here is an exception:
class CustomException extends RuntimeException {
String message
}
Loading the message source from the controller after catching the exception:
class MyService {
void doSomething() {
...
if (somethingBad) {
String value = 'Mary Smith'
throw new CustomException(value)
}
...
}
}
class MyController {
def myService
void processRequest() {
try {
myService.doSomething()
}
catch (CustomException e) {
flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
render view:'some_gsp'
}
...
}
}
Loading error from message source in the service where the controller pulls the message string the from the exception:
class MyService {
def messageSource
void doSomething() {
...
if (somethingBad) {
String value = 'Mary Smith'
throw new CustomException(messageSource.getMessage('thread.inactive.user', [value]))
}
...
}
}
class MyController {
def myService
void processRequest() {
try {
myService.doSomething()
}
catch (CustomException e) {
flash.error = e.message
render view:'some_gsp'
}
...
}
}
Frankly speaking, neither of those two places do you need the translations. :)
Separation Of Concern
Controller should only worry about HTTP methods and its delegation.
Services should take care of transactions and underlying business logic.
Declarative Error Handling
For 2.0.* and above, Grails provides you a sweet spot for handling errors. Guess what? Declarative Error Handling
All exception related code moves to a separate controller (in house) where they are handled properly, keeping your business controllers and services clean and abstracted from boiler plate codes.
For Grails 2.3.*, an added feature was to handle exception in the controller itself but most of the boiler plate (try catch stuff) is abstracted from the controller implementation.
Conclusion
If you are using v2.0.* and above then your controllers would look something like:
class MyController {
def myService
def processRequest() {
myService.doSomething()
...
}
}
//URL Mapping
static mappings = {
"500"(controller: "errors", action: "customException",
exception: CustomException)
}
//Error Controller
class ErrorsController {
def customException() {
def exception = request.exception
// perform desired processing to handle the exception
}
}
You can move the logic of error handling to a separate plugin if required in order to handle variety of errors/exception and unhappy paths. It becomes elegant to separate this concern.
If you are using v2.3.* then your controller would look something like:
class MyController {
def myService
def processRequest() {
myService.doSomething()
...
}
def handleCustomException(CustomException e) {
//Move this translation to src/groovy/utility if feasible
flash.error = g.message(code:'user.input.error', args:'[${e.value}]')
render view:'some_gsp'
}
}
In this case no handling required from services as well, you just need to throe that exception.
I suppose you would get more input from various sources as well if you look around and are interested to use this pattern.