Search code examples
jsfglassfishcdiinterceptor

Interceptor Method Not Invoked


The container is Glassfish. I have implemented an @PostConstruct life-cycle event interceptor method in a simple DAO bean class, but seems like for some reason it is not intercepting my business method at all. I do not anywhere manually instantiate the bean classes. beans.xml discovery mode is all and since I don't annotate DefaultUserDao bean, so it gets the default scope.

public class DefaultUserDao implements UserDao {

    private String userName;
    private String password;
    Scanner users = null;
    String[] userNamePasswordPairs = null;

    public DefaultUserDao() {
        try {
            users = new Scanner(Paths.get("/home/NetBeansProjects/EJBInAction/web/users"));
        } 

        catch (IOException ex) {
            Logger.getLogger(DefaultUserDao.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    /*The interceptor for this method is defined right below, that 
      interceptor method is not called at all. If I insert a
      System.out.println(userNamePasswordPairs) after the split()
      method below, it prints [userName:password] pair twice and there
      is only one line in the text file from which the pair was read  in the
      form like this admin:admin. Notice that I also insert a System.out.println()
      method in the interceptor method but if I remove the print()
      method from this init() method, I don't see it prints anything
      */
    @PostConstruct
    public void init() {
        while (users.hasNextLine()) {
            String line = users.nextLine();
            userNamePasswordPairs = line.split(":");
            //If I uncomment this, I see it prints [admin:admin] pair
            //twice, but when I comment it out, I don't see it prints  anything
           //System.out.println(Arrays.toString(userNamePasswordPairs));
            userName = userNamePasswordPairs[0];
            password = userNamePasswordPairs[1];
        }
    }

    @AroundConstruct
    private void printUser(InvocationContext ic) {
        //If this interceptor was invoked, it should print at least "Interceptor invoked: "
        //But it does not print this.
        System.out.println("Interceptor invoked: " + Arrays.toString(userNamePasswordPairs));
        try {
            ic.proceed();
        } 

        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

A simple JSF page gathering username and password

@Named
@RequestScoped
public class LoginBean {

    private String userName;
    private String password;

    @Inject
    private UserDao userDao;

    public LoginBean() {
    }

    public LoginBean(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public String validateUser() {

        if (userDao.getUserName().equals(userName) && userDao.getPassword().equals(password)) {
        }
    
        else {
            userName = "Error";
            password = "Error";
        }
        return "confirmation.xhtml";
    }

    //getter and setter for userName and password

}

Solution

  • First, note that @PostContruct is a lifecycle callback - and not an interceptor - while @AroundConstruct is an interceptor method. They have different meanings and are declared differently.

    Also note that the invocation order is typically the following:

    1. The constructor interceptor is called
    2. The constructor is called through the proceed() method in the interceptor.
    3. Injection is performed (basically @Inject fields/setters are set)
    4. The @PostConstruct lifecycle callback is called.

    Regarding the @PostConstruct lifecycle callback, you declare it correctly. And It seems to be called since it prints some text.


    Regarding the interceptor, the following is missing:

    • Your interceptor must be declared in a separate class, not in a bean.
    • Your interceptor class must be declared in the META-INF/beans.xml file under the <interceptors> element OR it must be annotated with the javax.annotations.Priority annotation specifying a priority value. The later option makes your interceptor application-wide, basically: it will apply to the whole app instead of just the jar containing the META-INF/beans.xml. To start, I recommend you declare it in the META-INF/beans.xml.
    • Your interceptor must be annotated with @Interceptor (javax.interceptor.Interceptor)
    • You must declare an interceptor binding on your interceptor and declare the beans/methods you want to intercept with it.

    To summarize, you have to create an interceptor binding for your interceptor:

    @InterceptorBinding
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Greet {
    
    }
    

    Then you have to create a class for your interceptor, using the interceptor binding:

    @Greet
    @Interceptor
    public class GreetInterceptor {
    
        @AroundConstruct
        public void aroundConstruct(InvocationContext ic) throws Exception {
            System.out.println("Hello!!");
            ic.proceed();
        }
    }
    

    Then declare it in your META-INF/beans.xml:

    <interceptors>
        <class>com.sandbox.GreetInterceptor</class>
    </interceptors>
    

    Finally, annotate the bean you want to intercept calls with the interceptor binding:

    @Greet
    public class DefaultUserDao implements UserDao {
        ...
    }