How to inject a http session attribute to a bean using CDI
Asked Answered
S

2

5

I have some legacy code that put objects as http session attributes using code like this:

MyObject object = new MyObject();
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
sessionMap.put("attrname", object);

The old facelets accessed the code using

 @ManagedProperty("#{attrname}")
 private MyObject object;

Is there any way using CDI (@Inject) to inject this session attribute to a Bean?

In new code that uses CDI what's the better way to create and inject objects that need to be created in a controlled way.

Schumacher answered 4/3, 2015 at 12:57 Comment(0)
A
7

Get hold of it in a session scoped managed bean with a @Produces@Named on the getter.

@SessionScoped
public class MyObjectProducer implements Serializable {

    private MyObject myObject;

    @Produces
    @Named("attrname")
    public MyObject getMyObject() {
        return myObject;
    }

    public void setMyObject(MyObject myObject) {
        this.myObject = myObject;
    }

}

When you set it somehow via e.g. myObjectProducer.setMyObject(myObject) elsewhere (or perhaps a CDI @Observes event), then you can inject it anywhere using @Inject @Named.

@Inject
@Named("attrname")
private MyObject myObject;

And yes, it's still available via #{attrname} in EL the usual way. And no, it won't be auto-created when not set, it'll remain null until you actually set it as a property of the producer class.


Alternatively, if you really intend to keep the legacy way of setting the instance via ExternalContext#getSessionMap() (e.g. because it's third party and you can thus not change it), then you can alternatively also let the producer return it directly from the session map:

@SessionScoped
public class MyObjectProducer implements Serializable {

    @Produces
    @Named("attrname")
    public MyObject getMyObject() {
        return (MyObject) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("attrname");
    }

}

This however isn't guaranteed to work when injected in a non-JSF artifact, such as an arbitrary @WebServlet, as the FacesContext#getCurrentInstance() would obviously return null.

Absquatulate answered 4/3, 2015 at 14:1 Comment(2)
Thank you @Absquatulate for your quick and complete answer.Schumacher
Perhaps the answer could be even more complete saying that for accessing the producer from not managed code this code CDI.current().select(MyObjectProducer.class).get() can be used.Schumacher
J
1

This is the way to go, for both non-jsf and jsf artifacts.

@Qualifier
@Retention(RUNTIME)
@Target({TYPE,METHOD,FIELD,PARAMETER});
public @interface SessionAttribute {

   @NonBinding
   String value() default "";
}

@ApplicationScope
public class SessionAttributeService {

  //Dont worry, this is a proxy, and CDI will ensure that the right one called at the right time.
  @Inject
  private HttpServletRequest servletRequest;

  @Produces
  @SessionAttribute
  @RequestScope
  public String sessionAttribute(final InjectionPoint ip){
     final SessionAttribute sa = ip.getAnnotated().getAnnotation(SessionAttribute.class);
     final HttpSession session = servletRequest.getSession();
     return session.getAttribute(sa.value());
  }
}

And use case:

@RequestScope
public class MyServiceBean {

  @Inject
  @SessionAttribute("theAttribute")
  private String attribute;

}
Jarrell answered 5/3, 2015 at 12:15 Comment(2)
This is not capable of injecting non-String values like OP requires. I would very definitely not call it "the way to go" for "session attributes" in general.Absquatulate
Really @Absquatulate i simply gave an example using strings, he can simply change the type to whatever he wants???Jarrell

© 2022 - 2024 — McMap. All rights reserved.