2

Is it possible to inject(CDI) request scoped bean in servlet filter?

My login.xhtml as below -

<h:form>
    <h:inputText id="uname" value="#{login.uname}" />
    <h:inputSecret id="pwd" value="#{login.upwd}" />
     <h:commandButton action="#{loginc.login}" value="Login"  />

</h:form>

On a POST on this page, I need Login bean in my servlet filter but it is always null. My backing bean

@Named("login")
@RequestScoped
public class Login {
    private String uname;//with getters and setters
    private String upwd;
}

Below is how I'm injecting it in my filter -

public class LoginFilter implements Filter {
@Inject
private Login login;
...
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//System.out.println(((Login)request.getAttribute("login")).toString()); **Not Working**
System.out.println("login ->" + login);//not working
}

Now working in the sense, the container injects a reference but all the fields are empty. I am expecting posted form data values i.e, values for the properties mentioned the the backing bean.

If its not possible, then is there any other way around? Intention is avoid session creation by using other types of scoped beans.

Update-

I did some changes as suggested in the comment section but still the values for the bean in question are null

public class LoginFilter implements Filter {

@Inject
private BeanManager beanManager;


public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    findBean("login", req);
    chain.doFilter(req, res);
}

public void findBean(String beanName, ServletRequest req){
    FacesContext facesContext = FacesContext.getCurrentInstance();
    System.out.println(facesContext == null);//always true
    System.out.println(beanManager==null);//is not null

    Bean bean = beanManager.getBeans(beanName).iterator().next();
    CreationalContext ctx = beanManager.createCreationalContext(bean);
    Login loginBean = (Login) beanManager.getReference(bean, bean.getBeanClass(),ctx);
    System.out.println(loginBean);//prints null as value for properties for post request also.
}

What I thought of doing - I had a Login request and LoginController view scoped bean. This is how I started initially. So, for username and password Login and loginController.login on sign in action. This worked as expected. I was injecting Login into LoginController all field/s values were there.

My requirement was to manage user sessions and implement remember me functionality too. So, to do that I started by creating cookie/s in the loginController and it worked.

Here, with the above setup - first I wanted to avoid unnecessary session creation. I wanted request to land on the login page but without a session. This requirement is the driving force behind all this filter/s and related re/work. I resorted to <c:view transient="true">. This I'm not sure, worked or not. Now, in one of your answers you wrote that we should not put cookie and other authorization and authentication related stuff inside backing beans.

So, I said to my myself - Okay, I'm not following good practice in my code and why not write a filter and if I could intercept the Login bean there I should be able to execute my authentication/ authorization and cookie related stuff there itself. This way, I can avoid hitting the view scoped loginController and no unwanted session. I can control the session programmatically. This never worked and so that's why I asked this question.

In one of your answers, you wrote - session creation is cheap - but I still wanted to avoid it when the login page of my application is accessed.

Now, what I have finally done and working is - my login form uses plain html input fields and then on login action I intercept these field value in old servlet way. For a valid attempt - I create a session programmatically and redirect.

Please, let me know if I can improve.

Tirath
  • 2,294
  • 18
  • 27
  • It is possible, tested in Wildfly 8.2. What container are you using? Do yo have a `beans.xml`? Is `Login` injection working outside of your filter? – acm Oct 21 '16 at 12:35
  • @acm Using `Payara` latest. Yes, I have `beans.xml` in my project. And, yes I am able to inject and use `Login` bean otherwise. – Tirath Oct 21 '16 at 12:57
  • I tested it using `payara-4.1.1.163`. I added a filter and injected a request scoped bean into it and it is working as expected. Could you provide more clues on how to actually reproduce the described behaviour? A [MCVE] would help a lot. – acm Oct 21 '16 at 15:41
  • @acm Please see the update. – Tirath Oct 21 '16 at 16:56
  • That's exactly what I've tried. Have you tried it in a clean project containing only your `Login` bean and your `LoginFilter`? – acm Oct 21 '16 at 18:23
  • @acm NO. I will give it a try. Meanwhile, is it possible for you post what works for you as an answer. – Tirath Oct 21 '16 at 20:04
  • @Tirath does your filter have any bean defining annotations, e.g. `@RequestScoped`? If you could also post your `beans.xml` content. – John Ament Oct 21 '16 at 22:45
  • @JohnAment The Filter is annotated only with the `@WebFilter` annotation and `beans.xml` is empty. It doesn't have any content. – Tirath Oct 22 '16 at 03:09
  • Ok.. I added payara as a tag to help get attention to this. I believe this should work though, I would recommend using a `beans.xml` from CDI 1.1 and adding a bean defining annotation to your filter, e.g. `@RequestScoped`. Please give us some input if that worked, you already received one recommendation. – John Ament Oct 22 '16 at 15:06
  • The bean you're getting has null fields because using basic injection means you're getting a brand new instance of the bean. What you need here is a contextual instance i.e. a specific instance of that bean that's already active within your context. Start with [this answer](http://stackoverflow.com/a/15685982/1530938) to get a specific instance. The risk you run is that you *may* get more than one bean, depending on your circumstances – kolossus Oct 24 '16 at 18:11
  • This construct will never work. Filters run before servlets, including the JSF servlet. The JSF servlet is the one responsible for setting the submitted input values as bean properties. Before you call chain.doFilter(), JSF servlet hasn't run yet and thus the bean remains empty. If you inspect the bean *after* the chain.doFilter() line, you'll see the properties. However, this strongly indicates a http://xyproblem.info. If you elaborate the concrete problem for which you most likely incorrectly thought that this all would be the correct solution, then we may able to propose the right solution. – BalusC Oct 29 '16 at 18:41
  • @BalusC I have added an update. – Tirath Oct 29 '16 at 20:19

1 Answers1

-2

Try to use Spring Interceptors instead of Filters. The example you can find HERE

Oleksandr
  • 450
  • 1
  • 6
  • 13