Hot questions for Using Enterprise JavaBeans in interceptor

Question:

I have an EJB interceptor and I follow the BCE pattern suggested by Adam Bien, that is, all EJB calls on the boundary starts and finish a transaction which means there is no nested EJB calls (there might be nested CDI injected Bean calls though, but those should be inside the same transaction started at the ejb Boundary).

So in those ejb Boundaries I have an interceptor and I want to intercept or know if after the method call of the EJB the transacction commited already? (that is, if a EntityManager was involved that the COMMIT sql call was sent to the DB and returned succeesfuly)

  1. Will I get that info from inside an Interceptor ?
  2. If not, how can I get notified of a transaction that sucessfully commited or failed ?

NOTE: Of course, if I am the client of the EJB and I am calling the method, after the method call I know what happened with the transaction, but I am interested in intercepting that BEFORE the client receives the response from the EJB.

@AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
    Object proceed = null;
    try {
        proceed = ctx.proceed();
        // is the transacction finished/commited already?
        // is it still open ?
        return proceed;
    } catch (Exception e) {
        throw e;
    }
}

[UPDATE]: I accepted one good answer, but the thing is that THERE IS NO WAY in Java EE to receive an event of a transaction that HAS BEEN COMMITED. So regardless of the good answer, sadly there is no way to be notified in Java EE of a completed transaction, inside the server, of course, if you are the client caller, then you sure know the transaction commited or rolled back...


Answer:

unless otherwise stated on the exception thrown, if an ejb method invocation throws an exception, it shall be rolled-back. Additionally, provided all calls to the DB were in the same transaction, they shall be deemed committed at the end of the transaction cycle.

In retrospect, all interceptors are invoked within the same transaction on which the ejb method it intercepts, was invoked (That's the reason the interceptor may decide in an event of an exception, to either roll-back or still commit the transaction).

Hence, you can know for sure, that the transaction completed successfully if within your interceptor call, after the proceed has been invoked and returned, there is no exception thrown, with a potential of transaction rollback.

So in your scenario:

@AroundInvoke
public Object logMethodEntry(InvocationContext ctx) throws Exception {
    Object proceed = null;
    try {
        proceed = ctx.proceed();
        // is the transacction finished/commited already?
        // The transaction is successful, but afaik, it is not yet committed, until this method returns successfully
        // is it still open ? More or less. You can still grab the Ejbtransaction and commit it manually or rollback if some other conditions have not been met yet
        return proceed;
    } catch (Exception e) {
        //If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
        throw e;
    }
}

Edit: for you to actually commit the transaction in an interceptor, you will need to be running application-managed transaction, otherwise, it is prohibited by the EJB specs to call commit on a container managed transaction, you can of course call setOnrollback method of the EJBContext. Edit If you truly want to do some DB changes, i would recommend:

  1. user ApplicationManaged transaction, from which you manually start and commit the transaction within the interceptor
  2. Use the concept of the observer, and listen for @Observes(AFTER_SUCCESS) event which will be invoked when the transaction is successfully committed and complete, and hence you can be guaranteed to do a db call, and the new updates will be available.
  3. If you can ignore the BCE pattern, and spin off a new transaction to do the update, so that after it returns successfully, you will be guaranteed of commit, and then continue normally

```

@Stateless
public class TransactionService {

   @TransactionAttribute(REQUIRES_NEW)
   public Object executeTransaction(final Callable<Object> task) {
       return task.call();
   }
}

@Interceptor
public class MyInterceptor {

  @EJB
  private TransactionService service;

   @AroundInvoke
    public Object logMethodEntry(InvocationContext ctx) throws Exception {
        Object proceed = null;
        try {
            proceed = service.executeTransactional(()->ctx.proceed());
            //If you reach here, you will be guaranteed of commit and then you can do the elastic search update
            return proceed;
        } catch (Exception e) {
            //If this happens, and you propagate it, then for sure the transaction will be rolledback, and never get committed. Since all db calls were being done within this transaction, then no DB commit will be done.
            throw e;
        }
    }
}

Question:

I want to implement high level resource filtering on URLs with a servlet filter and lower level action filtering on methods with an interceptor but my interceptor does not get fired on the EJB method called from the servlet filter.

Interceptor annotation Interface:

@Inherited
@InterceptorBinding
@Retention (RUNTIME)
@Target({TYPE, METHOD})
public @interface Permit {
    @Nonbinding
    String[] actions() default "N/A";
}

The Interceptor:

@Permit
@Interceptor
public class PermitInterceptor {

    @AroundInvoke
    public Object verifyPermission(InvocationContext context) throws Exception {
        Method method = context.getMethod();
        if(method.isAnnotationPresent(Permit.class)) {
            Permit permitAnnotation = method.getAnnotation(Permit.class);
            List<String> permittedActions = Arrays.asList(permitAnnotation.actions());
            List<String> userActions = SecurityContextHandler.getActiveUser().getActions();
            if(!Collections.disjoint(permittedActions, userActions)){
                return context.proceed();
            }
        }
        throw new PermissionException("You do NOT have the required permissions to perform this action");
    }
}

Servlet Filter:

@WebFilter(urlPatterns = {"/*"})
public class AccessFilter implements Filter {

    @EJB
    private RulesBean rules;

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        try{
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;

            String url = request.getRequestURI();
            if(rules.isAllowed(url)){
                chain.doFilter(request, response);
            }else{
                //handle as necessary
            }
        }catch(Exception ex){
            //handle as necessary
        }
    }
}

And finally here's what the EJB RulesBean that I want to use to manage routing/interception for all my servlets looks like;

Rules:

@Stateless
@LocalBean
public class RulesBean {
    private static final String CUSTOMERS = "/customers"

    public boolean isAllowed(String url) throws PermissionException {
        switch(url){
            case CUSTOMERS: return canViewAllCustomers();
            default: return true;
        }
    }

    /*This should trigger PermitInterceptor before entering method and 
    should throw my custom PermissionException if permission fails*/
    @Permit(actions={"ViewCustomers"}) 
    private boolean canViewAllCustomers(){
        return true;
    }
    ...
    //Other tests carried out here ...
}

Unfortunately PermitInterceptor doesn't get called before entering canViewAllCustomers() method.

Amazingly however, PermitInterceptor gets triggered when canViewAllCustomers() is made public and called directly as rules.canViewAllCustomers() instead of through the helper method rules.isAllowed(String url). But this isn't helpful in my case, as it gives me no single entry point for my URL checks which essentially means I have to do all the checks in the Servlet Filter.

QUESTION: Please can anybody shed more light on the reason why things are occurring in this manner?... and suggestions about the best way to implement this scenario is highly welcome. Thanks.


NOTE: (To give more perspective) You may be wondering why I want to do this OR more specifically why the RuleBean even exists at all... The reason is simply because a good number of my Servlets aren't doing much except route response to a view that triggers a server-side DataTables ajax call which populates the tables, hence I really need to ensure that the request for the view doesn't even get through to the if...else condition that fetches the view unless the permission checks in the interceptor is satisfied.

See sample servlet below;

@WebServlet ("/customers/*")
public class CustomerServlet extends VelocityViewServlet {
    private static final String CUSTOMERS = "/customers"

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uri = request.getRequestURI();
        if(uri.equals(CUSTOMERS)){
            String view = Path.getTemplate("/customers/index.vm");
            request.setAttribute("headerTitle", "Customers");
            request.getRequestDispatcher(view).forward(request, response);
        }else if(...){
            ...
        }
    }
}

Answer:

You invoke canViewAllCustomers() within isAllowed() directly, which gives the Application Server no chance to intercept the call.

Interception works with proxy classes. When you inject the EJB into your servlet, like you did with:

@EJB
private RulesBean rules;

what actually gets injected is not an EJB instance, but a proxy class, that the application server created at runtime (you can see this with the debugger). Invocations on that proxy class will be intercepted for transactions, custom interceptors, etc. and then delegated to the actual class instance.

So what you need to do is either put canViewAllCustomers() into a new EJB, that you can let the application server inject into your RulesBean class, or you can retrieve a reference of your RulesBean class from inside isAllowed() like so:

@Stateless
@LocalBean
public class RulesBean {
    private static final String CUSTOMERS = "/customers"

    @Resource
    SessionContext ctx;

    public boolean isAllowed(String url) throws PermissionException {
        switch(url){
            case CUSTOMERS: return self().canViewAllCustomers(); 
            default: return true;
        }
    }

    private RulesBean self() {
        return ctx.getBusinessObject(RulesBean.class);
    }

    /*This should trigger PermitInterceptor before entering method and 
    should throw my custom PermissionException if permission fails*/
    @Permit(actions={"ViewCustomers"}) 
    public boolean canViewAllCustomers(){
        return true;
    }
}

Question:

Below is a simple web app configured as a Rest service running on glassfish4. The app itself works, can access the single resource.

Interceptor doesn't work for pong(), but magically works for helloW(). When i had activated for helloW(), i could modify and overwirte the parameter, could throw Exception, etc... But none of this works for pong(). Elsewhere i tried with stateless ejb - same result - not workding - even with ejb-jar assembly-binding deployment descriptor. Why?

Rest:

package main;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/service")
@javax.ejb.Stateless
public class Service {

    @GET
    @Produces("text/plain")
//    @javax.interceptor.Interceptors({Intercept.class})
    public String helloW(String ss){

        String msg = pong("QQ");

        return msg;
        //return ss;
    }

    @javax.interceptor.Interceptors({Intercept.class})
    public String pong(String m){
        String temp = m;
        return temp;
    }
}

The interceptor itself:

package main;

@javax.interceptor.Interceptor
public class Intercept {

    @javax.interceptor.AroundInvoke
    Object qwe(javax.interceptor.InvocationContext ctx) throws Exception{

        ctx.setParameters(new Object[]{"intercepted attribute"});
        throw new Exception();
//        return ctx.proceed();
    }
}

And yes, i did try with beans.xml:

<interceptors><class>main.Intercept</class></interceptors>

No joy.


Answer:

Disclaimer: this is a guess as I didn't found any supporting documentation about this and it might also depend on the server's/JRE implementation.

It's not working because the @GET annotated method get's invoked by using java reflection/introspection techniques. This avoids the framework to intercept that invocation as it's done directly inside the JRE.

To demonstrate this instead of invoking "pong" directly as you're doing try the following:

try {
   String msg = (String) this.getClass().getDeclaredMethod("pong", String.class).invoke(this, ss);
  } catch (IllegalAccessException e) {
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   e.printStackTrace();
  } catch (NoSuchMethodException e) {
   e.printStackTrace();
  }

Just replace the String msg = pong("QQ"); for that code in your sample.

You should see that the pong method is now NOT being intercepted just as happens with helloW.

The only workarround for this I can think of is what you already did: extracting the logic inside another non annotated method.

Question:

I'm trying to autowire beans to EJBs with Spring using the @Interceptors(SpringBeanAutowiringInterceptor.class) annotation. The problem is that I keep on getting the NoSuchBeanDefinitionException for the autowired classes when the EJBs are instantiated. (The Filter class in my example).

I'm using Java EE 6 application (EJB 3.0), Spring 4.2.2 managed with Maven and running in a WebSphere 7 AS.

I have the following Spring dependencies:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.ldap</groupId>
    <artifactId>spring-ldap-core</artifactId>
    <version>2.0.4.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-tx</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>

Implementation:

@Stateless
@Remote(ServiceRemote.class)
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class Service implements ServiceRemote {

@Autowired
private Filter filter;

...

}

Class to be autowired

@Component
public class FilterImpl implements Filter { ... }

The beanRefContext.xml which SpringBeanAutowiringInterceptor will look for:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="businessBeanFactory"
        class="org.springframework.context.support.ClassPathXmlApplicationContext">
        <constructor-arg value="classpath*:applicaion-context.xml" />
    </bean>
</beans>

The application-context.xml with the bean definitions:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">


    <bean class="com.FilterImpl" />

</beans>

Both XMLs are in the classpath, as they are in src/main/resources/.

I've tried some debugging with the following code, but couldn't find something useful.

BeanFactoryLocator factoryLocator = ContextSingletonBeanFactoryLocator.getInstance("classpath:beanRefContext.xml");

BeanFactoryReference ref = factoryLocator.useBeanFactory("businessBeanFactory");
BeanFactory factory = ref.getFactory();

FilterImpl instance = factory.getBean(FilterImpl.class);

Both contexts appear to be loaded, as I get logs like the following, when the EJB is instantiated for the first time.

org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7730773; root of context hierarchy
org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions Loading XML bean definitions from URL [file:/C:/project/service-module/target/classes/beanRefContext.xml]
org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5580558: root of context hierarchy

But after that Spring can't find the bean I defined in the application-context.xml.

In application-context.xml I've tried using <context:component-scan base-package="com"/> instead of the <bean class="com.FilterImpl" />, but I got the same result.

Am I missing some configuration?

Edits: Added suggestion by Steve C and logs of context loading.


Answer:

Move your xml configuration files from src/main/java to src/main/resources.

Files in src/main/java are only processed by the maven-compiler-plugin.

Additional resource files are processed by the maven-resources-plugin which copies files from src/main/resources.