Intercepting & Customizing Provision Errors

Sett 47 ganger
Hopp til første uleste melding

jwa

ulest,
25. nov. 2015, 03:46:3625.11.2015
til google-guice
I would like to intercept some of the exceptions being thrown by Guice, with access to the full context of the underlying error, and use domain-specific details to come up with meaningful error messages.

To illustrate this problem I am using a vastly simplified domain, this may be a bit tenuous, bear with me.

I provide my clients with a simplified API which allows them to build-up the event without exposing them to the fact that Guice will be used to wire everything together:

AthelticsBuilder builder = new AthelticsBuilder()
 
.withTicketIssuer(ResultRecorded.class)  // <-- This is a singleton, one ticket issuer per event
 
.withObserver(ResultRecorded.class)      // <--- This is race-scoped, one created per race
 
.addRace("100 meter sprint")
 
.build();

Behind the scenes the AthleticsBuilder will use Guice to obtain instances of each of the classes described above. Framework-related dependencies may be injected into these classes, for example RaceObservers might want to gain access to the StopWatch that is timing the event they are responsible for observing:

public class ResultRecorder implements RaceObserver {
  @Inject
  public ResultRecorder(Race race, StopWatch watch) { /* ... */ }
}

The injection of these race-specific dependencies is facilitated by a custom "RaceScope" which is entered/exited as the AthelticsBuilder creates each race added to the builder.

It is possible for my API clients to make rather silly mistakes. For example, they might ask for a StopWatch to be injected into their TicketIssuer. This is not legal, as the TicketIssuer is a singleton, and has no access to members injected by the RaceScope. For eaxmple, if the following constructor is used:

public class TicketMachine implements TicketIssuer {

  @Inject
  public TicketMachine(StopWatch watch) { /* ... */ }
}

Guice will throw a ProvisionException when attempting to instantiate the TicketMachine. This will have the following error:

Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:

1) Error in custom provider, com.google.inject.OutOfScopeException: Cannot access Key[type=com.acme.StopWatch, annotation=[none]] outside of a scoping block
  at com.acme.RaceScopeModule.configure(RaceScopeModule.java:25)
  while locating com.acme.StopWatch
    for parameter 0 at com.acme.TicketMachine.<init>(TicketMachine.java:85)
  while locating com.acme.TicketMachine

This is rather unfortunate for two reasons:
  • It exposes my API clients to the fact I'm using Guice. My API completely insulates them from this, which is nice. It's only when errors are encountered that they suddenly become aware what's happening under-the-covers
  • The API consumer isn't likely to be able to decipher this error, and identifying the underlying issue
It would be much nicer if I could intercept this exception (more likely the underlying exception) and provide something far more meaningful: "TicketMachine cannot access com.acme.StopWatch as it does not have reference to a race".

To be able to achieve this, I'd need to be able to intercept the exact details of the binding error. Simply catching ProvisionException is of little use to me; it just contains "Messages", which don't have any of the rich context-specific information I need to produce more meaningful errors.

Is there some mechanism for handling / intercepting this errors, in order to produce more meaningful exceptions?

Any pointers would be greatly appreciated.

Tavian Barnes

ulest,
25. nov. 2015, 17:42:4125.11.2015
til google-guice
All the semantic information you want is thrown away very early by Guice, so there's probably not a way to get the kind of details that you want.  The internal implementation uses the same Message class that you see in the ProvisionException.

Sam Berlin

ulest,
25. nov. 2015, 17:54:1325.11.2015
til google-guice
You can write your own analysis using the SPI and fail eagerly.  This will work only if users don't inject the Injector.  We do this internally for singletons to ensure that only other singletons or unscoped things (or Providers) are injected into singletons. The APIs involved would be Binding.acceptTargetVisitor(BindingTargetVisitor), checking dependencies (often by casting to HasDependencies, for linked bindings getting the link, potentially implementing extension-specific BindingTargetVisitor subinterfaces), and keeping track of the path as you're recursing through dependencies.  The starting point for visiting would be each binding in injector.getAllBindings().values().  (And you could keep track of what you've visited to short-circuit on later visits, to avoid redoing the same work.)

sam



--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice...@googlegroups.com.
To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/9a4d8bd2-114c-4795-b8ca-90902ab9b984%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tavian Barnes

ulest,
25. nov. 2015, 20:38:4925.11.2015
til google-guice
Ah, that's another approach to http://stackoverflow.com/q/18474368/502399 :)

Sam Berlin

ulest,
25. nov. 2015, 20:53:2425.11.2015
til google-guice

Yup, for our internals we actually have a two-pronged approach where we try to eagerly validate whatever we can (using getAllBindings) at startup, and then continue to monitor for any stragglers using a ProvisionListener.

sam


Svar alle
Svar til forfatter
Videresend
0 nye meldinger