1

Say I have a web service / a REST resource that is called with some HTTP header parameters. The resource method builds a complex data object (currently a POJO) and eventually returns it to the client (via Gson as JSON, but that doesn't matter).

So I have this call hierarchy:

@Path(foo) ProjectResource @GET getProject()
-> new Project()
-> new List<Participant> which contains lots of new Participant()s
-> new Affiliation()

If I want the Affiliation object to be e.g. populated in English or German depending on a header parameter, I have to pass that as a parameter down the chain. I want to avoid having to do that. Maybe this is just fundamentally impossible, but it feels so wrong. All these objects only live inside the request, so wouldn't it be convenient to be able to access information tied to the request from anywhere?

I was hoping I could e.g. define a CDI @RequestScoped object that initialized itself (or gets populated by some WebFilter) and that I can then inject where I might need it.

But obviously that doesn't work from inside the POJOs, and I also had trouble getting hold of the headers from inside the request-scoped object.

I've read many SO questions/answers about EJBs and JAX-RS Context and CDI but I can't wrap my head around it.

Am I expecting too much? Is passing down the parameter really the preferred option?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Antares42
  • 1,406
  • 1
  • 15
  • 45

1 Answers1

6

If I understand what you need, you can try the following (just wrote this solution from the top of my head, but it should work):

Defining a class to store the data you need

Define a class annotated with @RequestScoped which will store the data you need:

@RequestScoped
public class RequestMetadata {

    private Locale language;

    // Default constructor, getters and setters ommited
}

Ensure you are using the @RequestScoped annotation from the javax.enterprise.context package.

Creating a request filter

Create a ContainerRequestFilter to populate the RequestMetadata:

@Provider
@PreMatching
public class RequestMetadataFilter implements ContainerRequestFilter {

    @Inject
    private RequestMetadata requestMetadata;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        requestMetadata.setLanguage(requestContext.getLanguage());
    }
}

Performing the injection

And then you can finally perform the injection of the RequestMetadata using @Inject:

@Stateless
public class Foo {

    @Inject
    private RequestMetadata requestMetadata;

    ...
}

Please, be aware that anywhere is too broad: The injection will work into beans managed by the container, such as servlets, JAX-RS classes, EJB and CDI beans, for example.

You won't be able to perform injections into beans created by yourself neither into JPA entities.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • Oh, that's pretty good and gets me at least halfway! I had experimented with a ``@RequestScoped`` class before, but instead of explicitly initializing a field inside it, I tried to inject e.g. ``HttpServletRequest`` there - which didn't work. Yours does, so that's great. But can I annotate my deep children as ``@Stateless``? In my example I have a ``new Project()`` which will in turn populate a list of ``new Participant()`` and in turn ``new Affiliation()``. Can I inject my resource class inside that Affiliation? – Antares42 Apr 04 '16 at 10:10
  • @Antares42 If `Project`, `Participant` and `Affiliation` are JPA entities, you won't be able to perform injection into them. It's important to remember the injection will work only for container managed beans (CDI and EJB, for example) and not for the beans created by yourself. – cassiomolin Apr 04 '16 at 10:17
  • They're not JPA entities, just simple POJOs. But that means there is no way to get a hold of request-global parameters from whereever unless I pass them as arguments? – Antares42 Apr 04 '16 at 11:08
  • @Antares42 AFAIK the injection won't work into beans created by yourself. Perform the injection into beans created by the container and pass the parameters to your beans. There's nothing wrong with that. – cassiomolin Apr 04 '16 at 11:30