#Produces does not return cdi proxy, but real instance - cdi

I have a class
#RequestScoped
public class AFactory {
private final HttpServletRequest request;
protected AFactory () {
this.request = null;
}
#Inject
public AFactory (HttpServletRequest request) {
this.request = request;
}
#Produces
public A getA() {
int random = ...;
A a = new A(request);
a.setRandom(random);
return a;
}
}
I understand since I am doing new A(), I return the real instance.
Is this the expected way to use producers?
Is there a way to return the proxied instance?

CDI installs proxies for beans of all scopes except for the #Dependent pseudo scope. A #Produces annotated method (short producer) is called by CDI whenever it needs to get a (new) instance of a bean. This instance is then put into some pool for the corresponding scope.
The proxy will always return the bean-instance from the pool. Try adding some debug message to the producer method to see how often it will be called. A producer for an #ApplicationScoped bean should be called only once, and a #RequestScoped producer should be called once per request.
In the examples above no scope is given to the producer method (the scope of the factory class is not used for the producer method), so the default scope (#Dependent) will be used. And this means (as no proxy is used for this scope), a new instance will be injected every time #Inject A is found by CDI. See also this Question and Answer and the referenced documentation for more details.
So to your concrete questions:
The observed behavior is expected
Proxies will only be used if scope is not #Dependet which is the default.

Related

Extending PartialViewContext with a RequestScoped bean in JSF

In a JSF project, we wrote our own PartialViewContext to listen to some events fired by pages beans:
#RequestScoped
public class OurPartialViewContext extends PartialViewContextWrapper
{
...
// called by cdi
#SuppressWarnings("unused")
private void listenForUpdate(#Observes OurRefreshEvent event)
{
...
And we wrote the matching factory, injecting it:
public class OurPartialViewContextFactory extends PartialViewContextFactory
{
#Inject
private OurPartialViewContext customPartialViewContext;
...
Problem is that in the newest versions of JSF, the empty constructor for PartialViewContextWrapper is deprecated, asking us to use another constructor with the wrapped object in parameter.
Currently, our PartialViewContext needs to be tied to the request scope, in order to be modified during the request by the observed events and to be used by a custom PartialResponseWriter we also wrote.
So our PartialViewContext currently both:
must have an empty constructor, as it is a #RequestScoped bean;
should not have an empty constructor, as it is deprecated for PartialViewContextWrapper which it inherits from.
How could we find a solution there?
We tried to remove it from the scope and build it in the Factory with a simple new OurPartialViewContext(), but then the #Observes methods are never called.
You are required to pass the wrapped instance into the constructor and to use getWrapped() over all place in delegate methods. Otherwise your application will most probably not work when you install other JSF libraries which also ship with their own PartialViewContext implementation such as OmniFaces and PrimeFaces. You would be effectively completely skipping the functionality of their PartialViewContext implementation. This mistake was previously observed in too many custom implementations of factory-provided classes. Hence the urge to mark the default constructor as #Deprecated so that the developers are forced to use the proper design pattern.
Your specific issue can be solved by simply refactoring the listenForUpdate() method into a fullworthy request scoped CDI bean, which you then inject in the factory who in turn ultimately passes it into the constructor of your PartialViewContext implementation.
Thus, so:
#RequestScoped
public class OurEventObserver {
public void listenForUpdate(#Observes OurRefreshEvent event) {
// ...
}
}
public class OurPartialViewContextFactory extends PartialViewContextFactory {
#Inject
private OurEventObserver observer;
public OurPartialViewContextFactory(PartialViewContextFactory wrapped) {
super(wrapped);
}
#Override
public PartialViewContext getPartialViewContext(FacesContext context) {
PartialViewContext wrapped = getWrapped().getPartialViewContext(context);
return new OurPartialViewContext(wrapped, observer);
}
}
public class OurPartialViewContext extends PartialViewContextWrapper {
private OurEventObserver observer;
public OurPartialViewContext(PartialViewContext wrapped, OurEventObserver observer) {
super(wrapped);
this.observer = observer;
}
// ...
}
Inside any of the overridden methods of OurPartialViewContext you can simply access the state of the observer, provided that the listenForUpdate() modifies some instance variables representing the state.

Expire specific managed bean instance after time interval

I have 2 JSF managed beans A and B and I need to expire/destruct/destroy A after 2 minutes and B after 5 minutes. I checked this related question Timing out from a bean, but it is expiring whole session. I do not want to expire whole session.
How can I achieve this with a custom scope?
Given that you're using JSF bean management facility (and thus not CDI, which would require a completely different answer), you can achieve this with #CustomScoped. The #CustomScoped value must refer a Map implementation in a broader, usually existing, scope.
Something like:
#ManagedBean
#CustomScoped("#{timeoutScope}")
public class TimeoutBean {}
As the #CustomScoped annotation doesn't support passing additional arguments, setting the timeout can only be done via an additional custom annotation like below:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Timeout {
/** Minutes. */
int value();
}
#ManagedBean
#CustomScoped("#{timeoutScope}")
#Timeout(5) // Expires after 5 minutes.
public class TimeoutBean {}
Now, here's a kickoff example of how the #{timeoutScope} looks like, including #PostConstruct support (automatically) and #PreDestroy support (manually):
#ManagedBean
#SessionScoped
public class TimeoutScope extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
#Override
public Object put(String name, Object bean) {
Timeout timeout = bean.getClass().getAnnotation(Timeout.class);
if (timeout == null) {
throw new IllegalArgumentException("#Timeout annotation is required on bean " + name);
}
Long endtime = System.nanoTime() + (timeout.value() * (long) 6e10);
Object[] beanAndEndtime = new Object[] { bean, endtime };
return super.put(name, beanAndEndtime);
}
#Override
public Object get(Object key) {
Object[] beanAndEndtime = (Object[]) super.get(key);
if (beanAndEndtime == null) {
return null;
}
Object bean = beanAndEndtime[0];
Long endtime = (Long) beanAndEndtime[1];
if (System.nanoTime() > endtime) {
String name = (String) key;
ScopeContext scope = new ScopeContext("timeoutScope", Collections.singletonMap(name, bean));
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().publishEvent(context, PreDestroyCustomScopeEvent.class, scope);
return null;
}
return bean;
}
}
You see, it's session scoped and implements Map. As to the scope, this way it's tied to a specific user session, not to the whole application. If you actually want to share the bean across all user sessions in the application, then make it application scoped instead. As to the Map, henever JSF needs to find a managed bean, it first tries get(). If it returns null (i.e. bean doesn't exist yet), then it will auto-create the managed bean instance and perform a put().
Inside the put(), it's a matter of extracting and calculating the timeout and store it in the map. Inside the get(), you just check the timeout and return null to indicate JSF that bean doesn't exist anymore. JSF will then simply auto-create it and come back at put(), etc.
Do note that I'm using System#nanoTime() instead of System#currentTimeMillis() as the latter is tied to OS (operating system) time, not to hardware time (and it's thus sensitive to a.o. DST and enduser-controlled changes in time).

How to inject a http session attribute to a bean using CDI

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.
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.
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;
}

Rollback transaction inside managed bean

I would like to rollback transaction not inside EJB but inside JSF managed bean. Inside EJB we can use SessionContext.setRollBackOnly() but what can I use in managed bean ?
#Stateless
#Local(AccountLocal.class)
public class AccountBean implements AccountLocal {
public void test1() throws CustomException(){
...
}
public void test2() throws CustomException(){
...
throw new CustomException();
}
public void test3() throws CustomException(){
...
}
public void all() throws CustomException(){
test1();
test2();
test3();
}
}
In my managed bean :
#SessionScoped
public class LoginBean implements Serializable{
public void test(){
try{
accountBean.test1();
accountBean.test2();
accountBean.test3();
}catch(CustomException e){
// WHAT HERE TO ROLLBACK TRANSACTION ?
}
}
}
EDIT : How can I ensure that if one of the test1, test2 or test3 rolls back, others will roll back too ?
I tested this code and accountBean.test1(); is validated even if accountBean.test2(); rolls back.
Could the solution be only to nest this 3 methods inside one EJB method ?
#SessionScoped
public class LoginBean implements Serializable{
public void test(){
try{
accountBean.all();
}catch(CustomException e){
...
}
}
}
Transactions are automatically rolled back by the EJB container if an unchecked exception is thrown (note that JPA's PersistenceException is such one). Your CustomException seems to be a checked exception. If changing it to extend RuntimeException as follows
public class CustomException extends RuntimeException {
// ...
}
or creating a new one is not an option, then you need to set the #ApplicationException annotation on the class with the rollback attribute set to true.
E.g.
#ApplicationException(rollback=true)
public class CustomException extends Exception {
// ...
}
Please note that the concrete problem has nothing to do with JSF. The service layer and managing transactions is completely outside the responsibility of JSF. It's the responsibility of EJB instead. JSF should merely act as "view" in this perspective.
See also:
JSF Service Layer
Handling service layer exception in Java EE frontend method
I'm playing the Devil's advocate here, since BalusC's advice that you should not let your backing beans act as services is absolutely true.
But, purely as a technical excersise, it -is- possible to start a JTA transaction in a backing bean and then control start and commit or rollback programmatically.
You can do this by injecting a UserTransaction via #Resource. Prior to calling your EJB methods, call start on this instance, and after the last call either commit or rollback.
Again, this is a purely theoretical answer. In practice, don't do this and let the backing bean call 1 EJB method that calls out to other EJB beans if needed.

How do I force a managed bean to initialize first?

I have a navigation managed bean for each user.
and I need it to initialize first before any other bean because a value is required from the bean.
May I know how do I perform that?
I have tried eager="true" but it doesn't work.
any quick and easy solution via faceconfig would be greatly appreciated.
Just perform the desired initialization job in bean's #PostConstruct.
#PostConstruct
public void init() {
// Here.
}
It'll be invoked when the bean is injected/referenced from another bean for the first time.
The eager=true works only on application scoped beans.
From what I see you should reference the other bean. Let's assume a have a utility class that can pull a bean from the context.
Basically ->
//Bean A
public void doSomething()
{
String required = Utility.getBeanB().getRequiredValue();
use(required);
}
...
//Bean B
public String getRequiredValue()
{
return "Hi, I'm a required value";
}
I have several large web apps that have a "Session Bean" that stores stuff like user preferences, shared objects etc... and this method works perfectly. By using a reference to the bean you eliminate the need to chain the initialization. That method will always DEPEND on the method in the other bean, thus guaranteeing the order of initialization.
There's a variety of ways to access the bean but I usually go through the EL route ->
Get JSF managed bean by name in any Servlet related class
Best of luck, I try to stay "functionally pure" when I can--and I hope that get's a laugh considering the language!
Here's some cool hax for ya, in case other solutions aren't working for you due to various circumstances...
Let's say I have a class Alpha that I want initialized first:
public class Alpha {
#PostConstruct
public void init() {
}
}
I can put the following method in Alpha:
#ManagedBean(name = "Alpha", eager = true)
public class Alpha {
public static void requireAlpha() {
FacesContext context = FacesContext.getCurrentInstance();
Object alpha = context.getApplication().evaluateExpressionGet(context, "#{Alpha}", Object.class);
System.out.println("Alpha required: " + alpha.toString());
}
#PostConstruct
public void init() {
}
}
Then, in any classes that are initializing too early, simply call:
Alpha.requireAlpha();
// ...
// (Code that requires Alpha to be initialized first.)
And if there's a ChildAlpha class that extends Alpha that you really want to be initialized (rather than the parent), make sure to use "ChildAlpha" instead, in both the name="" and the EL Expression ("#{}").
See here for more infos: Get JSF managed bean by name in any Servlet related class

Resources