0

So I read from this post from BalusC about how to stop a stateless session bean from continually thrashing a data store (e.g. DB) when accessed by JSF (which may/will make multiple calls) and so I've implemented my code in what I'd like to think is in the spirit of what was posted by BalusC (and other forums posts from "best practices" I've seen concerning this issue).

My stateless session bean looks like this:

@Stateless
@Named("productsService")
public class ProductService {

    private static boolean changed = true;

    private List<Product> products;

    private long count;

    @PersistenceContext(name = "myPU")
    private EntityManager em;

    @Inject
    private Product product;

    public ProductService() {
    }

    private void productRecordsFromDB() {
        products = em.createNamedQuery("Product.getAll", Product.class).getResultList();
        Object o = em.createNamedQuery("Product.getCount", Product.class).getSingleResult();
        count = ((Long) o).longValue();
    }

    public void addProduct() {
        synchronized (ProductService.class) {
            changed = true;
            em.persist(product);
        }
    }

    public long countProducts() {
        return count;
    }

    public void removeProduct(Product p) {
        synchronized (ProductService.class) {
            changed = true;
            em.remove(em.merge(p));
        }
    }

    public int removeAllProducts() {
        synchronized (ProductService.class) {
            changed = true;
            return em.createNamedQuery("Product.deleteAll").executeUpdate();
        }
    }

    public List<Product> getProducts() {
        synchronized (ProductService.class) {
            if (changed) {
                productRecordsFromDB();
                changed = false;
            }
        return products;
        }
    }

    public Product getProduct() {
        return product;
    }

    public void setProduct(Product p) {
        product = p;
    }
}

EDIT: I've added the synchronized block to the relevant portions to ensure serial access although this is now starting to feel more like a singleton. I'm still curious to know how others have dealt with this issue pertaining to multiple calls to a data store.

Specifically, I've created a 'dirty' flag that is checked and if the DB has been updated then the dirty flag is set to true (via updates to the DB). After detection of the dirty flag, it's set back to false and so only one call to the DB is made. Hence, no DB thrashing ensues.

My question is: what I've done, is this an appropriate "best practice" to solve the solution or is there a more clever way that I am not aware of? I'm kind of thinking in terms of the old 'J2EE' blue print design patterns as well as a possible annotation that I may be missing within the context of Java EE 6.

Community
  • 1
  • 1

4 Answers4

5

is this an appropriate "best practice" to solve the solution or is there a more clever way that I am not aware of?

The way in which you've build your session bean is unfortunately not a best practice. In fact, as Mikko explained, it's completely anti to how a session bean should normally work. So, despite your obvious efforts I'm afraid what you created is a prime example of a bad practice. It almost does everything one should not do in a stateless session bean.

In order to solve the problem BalusC outlined, you can use a separate backing bean to which your view binds instead of letting it directly bind to a service. This backing then is the bean that caches a result during a request or for the duration of a view scope.

E.g.

The Service:

@Stateless
public class ProductService {

    @PersistenceContext(name = "myPU")
    private EntityManager em;

    public List<Product> getProducts() {
        em.createNamedQuery("Product.getAll", Product.class).getResultList();
    }

    // ...
}

Then the backing bean (if using CDI, add @ViewScoped annotation via e.g. CODI):

@ViewScoped
@ManagedBean
public class SomeBacking {

    private List<Product> products;

    @EJB
    ProductService productService;

    @PostConstruct
    public void init() {
        products = productService.getProducts();
    }

    public List<Product> getProducts() {
        return products;
    }
}

I've placed the call to the service is a @PostConstruct here, but depending on your exact requirements this could of course also be in an action method or via the lazy-loaded pattern in the getter.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
  • Unfortunately what you've posted does not address the issue which is to stop thrashing of the DB when a JSF component calls it. You've simply created another class and moved the problem into there. –  May 01 '12 at 15:38
  • 2
    Why do think that? It's not just another class. The DB is consulted only once per request or per view scope, and every time JSF calls the getter the cached `products` is returned. This is a very cheap call and involves no DB access. What I effectively did was to move this kind of cache from a global location to a local one. – Arjan Tijms May 01 '12 at 16:02
  • But if a JSF component is calling this up multiple times (which it will due to the JSF life cycle) then the thrashing issue hasn't been resolved. Somewhere in one of the classes a Boolean variable will be required to keep track of whether or not the DB has been updated. This should be handled by JPA an in theory is. The problem is that each provider has it's own way of handling caching (L1 and L2) based on either properties in the persistence.xml file or using @QueryHint. Problem is most of these are hints and nothing more. –  May 01 '12 at 17:18
  • 2
    @CodesNChaos that is simply not what BalusC meant. Whether the DB is updated is not relevant here. What is the matter is that during a single request JSF would normally consult a property many times. Without the pattern I outlined, every such consultation will result in a DB call. This is not what you want. What you also don't want is that data changes at an arbitrary point during life-cycle processing. The data thus actually *should* stay constant during the entire life-cycle (and actually when processing a post-back as well). – Arjan Tijms May 01 '12 at 17:36
  • @CodesNChaos >The problem is that each provider has it's own way of handling caching (L1 and L2) - offtopic here, but this is also not entirely correct. L1 caching has always been standard (it's the persistence context), L2 caching has been standardized in JPA 2 (2009). – Arjan Tijms May 01 '12 at 17:38
  • Thanks for your replies. Whether or not I use a proper design pattern does not fix the problem of thrashing the DB. Using a RequestScoped bean (BTW, ManagedBean is superfluous with Java EE 6 CDI via Weld) to call the stateless session bean appears to be the proper way to use these features but my original question was within the context of solving the problem of DB thrashing. So far, I've not yet seen a solution to solving the problem of DB requests thrashing the DB server. –  May 01 '12 at 18:12
  • 1
    The `ManagedBean` in my example is the `javax.faces.bean.ManagedBean` and thus a JSF managed bean. I used this here because CDI does not have the view scope by default. I think the DB "thrashing", with "trashing" meaning "many redundant calls" is solved by the solution I handed out. Of course, it's your own decision what to do with it. Good luck anyway! – Arjan Tijms May 01 '12 at 19:02
2

You have two main problems:

  1. state is shared between threads without guarding with lock
  2. stateless beans have state visible to client.

You share state between all instances of ProductService via static changed variable. However, reads and writes to this variable are not synchronized. As result there is no guarantee that instances share same value for changed. You have to control access via lock (synchronize with same lock) or in minimum make it volatile (just guarantees that you will see last written value).

Next problem is that stateless beans should not have state visible to client. That's why they are called stateless. It should make no difference to client which instance handles call, because client cannot control which instances is called. Think about for example following scenario (instances s1 and s2, assuming both are in fresh state):

  1. s1.getProducts => changed = false
  2. s2.getProducts => because changed is false, it will return null (no one set value to instance variable products for s2)

Answer to edited question and comment: You still have same problem with stateless vs. state. Stateless is used where solution without state does not fit in. As you see from following, it still matters which instance will handle business method call:

  1. s1.getProducts => changed = false;
  2. s2.getProducts => because changed is false, null is returned

Please follow approach Arjan Tijms offered, or something in same spirit at least.

Mikko Maunu
  • 41,366
  • 10
  • 132
  • 135
  • Changing the 'changed' variable to instance would be bad I think since if there are pooled beans one could update but the others wouldn't see the update unless they too made an update. So, a static variable that is synchronized should take care of the problem, no? –  Apr 30 '12 at 07:41
  • It does not matter which instance handles it because the access to the DB calls is synchronized at the class level. I'm truly puzzled that everyone that has replied seems to be focusing on misuse of design patterns rather than solving the problem of DB thrashing. I can go with a request scoped bean that calls a stateless session bean and that **still doesn't** solve the problem of DB thrashing. –  May 01 '12 at 18:19
  • 1
    If other instance returning null (as in example you do not have control to is s2.products populated) and other one returning populated list, and selection of which instance handles call being out of your hands is not problem in this context, then I do not understand your problem well enough to provide further answers. I hope you find solution. – Mikko Maunu May 01 '12 at 18:31
1

This is by far the worst abuse of stateless session beans I've seen in a long time.

You have created a kind of Singleton, but in a bean that is absolutely not suited for this, with mixed concerns of global and local operations and the risks of dead locks by synchronizing on the class literal.

If you need a Singleton, you should use the dedicated @Singleton bean, but if you want to prevent the repeated calls from JSF during a request, use a backing bean like is shown in the other answer.

Mike Braun
  • 3,729
  • 17
  • 15
  • And yet you offer no solution. I don't have SO "street cred" and so I can't down vote your answer. All I can say is that I find your answer completely useless. –  May 01 '12 at 17:25
  • You asked if what you did is a best-practice mate, and I answered it wasn't. Sorry if you don't like that answer. – Mike Braun May 01 '12 at 22:21
  • Unfortunately you failed to provide a solution to your accompanying weak trolling. Otherwise I would have overlooked that. –  May 02 '12 at 01:36
0

There's a concrete application for EJBs as singletons, examples Here. The EJB 3.0 Standard allows for timeshared, synchronized access to a single session bean. That being said you have the following options to prevent/control session caching.

1)A singleton EJB may maintain a local cache copy of database results that is refreshed on a schedule using an EJB 3.0 Timer bean, a ridiculously plain example Here. This singleton you could then inject into whatever context you please JSF managed bean, CDI bean etc

2)Alternatively, your specific usecase could cache it's own local copy of required data, which is read once at bean construction @PostConstruct although this paradigm is more applicable to a longer lived bean than a @RequestScoped bean.

3)Related to 2), have a @ApplicationScoped bean that functions similarly to 1) above but that now maintains a (huge)cache of various Lists

kolossus
  • 20,559
  • 3
  • 52
  • 104