2

I am trying to inject a single SFSB into a ViewScoped JSF backing bean, in Glassfish 3.1.1.

My log shows this, which is that TWO SFSBs are created, although only the second gets injected:

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@f48cde0
INFO: constructed a new sfsb: com.exmaple.test.service.impl._TestSFSBImpl_Serializable@13dbf5ce
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@4de6b191

Later, when I navigate away and the ViewScoped bean goes out of scope, I invoke remove() on the SFSB, and so I see this in the log:

INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@4de6b191
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@13dbf5ce
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@13dbf5ce

But the superfluous first one ending in @f48cde0 never got injected, so I don't have a handle to it, and it never gets removed. Only later, when I shut down the server, I see it get removed.

Here is my code:

The backing bean:

package com.example.test.ui;

import java.io.Serializable;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.AjaxBehaviorEvent;

import com.example.test.service.api.TestSFSB;

@ManagedBean(name = "testViewScopedSFSB")
@ViewScoped
public class TestViewScopedSFSB implements Serializable {

  private static final long serialVersionUID = 1L;
  private static final Logger LOGGER = Logger.getLogger(TestViewScopedSFSB.class.getCanonicalName());

  @EJB
  private TestSFSB testSFSB;

  @PostConstruct
  public void postConstruct() {
    LOGGER.info("constructed a new view scoped bean: " + this);
  }

  public int getNumClicks() {
    return testSFSB.getNumClicks();
  }

  public void clicked(AjaxBehaviorEvent event) {
    testSFSB.clicked();
  }

  @PreDestroy
  public void preDestroy() {
    LOGGER.info("destroying view scoped bean: " + this);
    testSFSB.remove();
  }

}

The SFSB interface:

package com.example.test.service.api;

public interface TestSFSB {

  void clicked();

  int getNumClicks();

  void remove();

}

The SFSB implementation:

package com.example.test.service.impl;

import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.Remove;
import javax.ejb.Stateful;

import com.example.test.service.api.TestSFSB;

@Stateful
@Local(TestSFSB.class)
public class TestSFSBImpl implements TestSFSB {

  private static final long serialVersionUID = 1L;
  private static final Logger LOGGER = Logger.getLogger(TestSFSBImpl.class.getCanonicalName());

  int numClicks = 0;

  @PostConstruct
  public void postConstruct() {
    LOGGER.info("constructed a new sfsb: " + this);
  }

  @Override
  public void clicked() {
    numClicks++;
  }

  @Override
  public int getNumClicks() {
    return numClicks;
  }

  @Override
  @Remove
  public void remove() {
    LOGGER.info("removing sfsb: " + this);
  }

  @PreDestroy
  public void preDestroy() {
    LOGGER.info("destroying sfsb: " + this);
  }

}

And finally, the jsf page:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<h:html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

<h:head>

</h:head>

<h:body>
    <h:form>
        <h:commandLink action="/public/publicResource.jsf" value="Home" />
        <h:panelGrid id="panel1">
            <h:commandButton value="Click me">
                <f:ajax event="click" listener="#{testViewScopedSFSB.clicked}"
                    render="panel1" />
            </h:commandButton>
            <h:outputText value="#{testViewScopedSFSB.numClicks}" />
        </h:panelGrid>
    </h:form>

</h:body>
</h:html>

This is such a simple setup... what the heck could it be? A bug in Glassfish perhaps?

EDIT: In order to allay any "doubts" about the veracity of what I am reporting, here is the log when I load the page 10 times. Note 20 SFSBs created, 2 each time, but only 1 cleaned up when I navigate away.

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@561b0019
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@2767c7d9
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@7b239469
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@7b239469
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@2767c7d9
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@2767c7d9

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5c8608b9
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@63a6d923
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@5ef9bbf1
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@5ef9bbf1
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@63a6d923
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@63a6d923

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@31a4ef37
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@43551d57
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@8f95d4a
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@8f95d4a
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@43551d57
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@43551d57

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@69e3f60e
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5f91e550
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@63661834
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@63661834
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5f91e550
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5f91e550

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@266c4c10
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@ff5c225
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@4f9d1352
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@4f9d1352
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@ff5c225
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@ff5c225

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@42650c3b
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@7806178a
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@57f3a295
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@57f3a295
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@7806178a
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@7806178a

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@70879d38
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5763013f
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@24e6fbeb
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@24e6fbeb
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5763013f
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5763013f

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@49649260
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@552ee43b
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@798a092d
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@798a092d
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@552ee43b
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@552ee43b

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@1a722605
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@3c1e1fd3
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@5dbb747a
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@5dbb747a
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@3c1e1fd3
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@3c1e1fd3

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@21da38c0
INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@7dc3d69c
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@443edc45
INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@443edc45
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@7dc3d69c
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@7dc3d69c

And then finally, when I unload the app, note the 10 superfluous SFSBs finally get destroyed:

INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@561b0019
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5c8608b9
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@31a4ef37
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@69e3f60e
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@266c4c10
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@42650c3b
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@70879d38
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@49649260
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@1a722605
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@21da38c0

Take my word for it based on my reputation that the behavior holds for 100 hits causing 200 beans in the same pattern.

Kevin Pauli
  • 8,577
  • 15
  • 49
  • 70

1 Answers1

0

It is not a bug, it is a feature. And an important one. In Java EE the container creates a few instances of different things (such as stateless beans, message-driven beans, entities and sfsbs) and pools and chaches them, and what not. Letting the container do and handle this is one of the reasons for using Java EE. You can configure this behaviour in most containers.

From the Glassfish documentation:

In addition, the GlassFish Server supports a number of tunable parameters that can control the number of “stateful” instances (stateful session beans and entity beans) cached as well as the duration they are cached.

[..]

One of the most important parameters for GlassFish Server pooling is steady-pool-size. When steady-pool-size is set to a value greater than 0, the container not only pre-populates the bean pool with the specified number of beans, but also attempts to ensure that this number of beans is always available in the free pool. This ensures that there are enough beans in the ready-to-serve state to process user requests.

( http://docs.oracle.com/cd/E18930_01/html/821-2418/beahn.html#beahq )

If you don't want this behaviour, you can configure it away: Read and follow appropriate links from the link above.

esej
  • 3,059
  • 1
  • 18
  • 22
  • I don't think this is right. A bounded cache (for stateful beans) is different than a pool (for stateless beans). Stateful beans can't be reused. The behavior I am seeing is 2 SFSB's created each time I load the page. If I load the page 100 times, I see 200 beans created. The 100 "superfluous" beans are never used again; it's not like they were added to a pool. This is clearly not desirable behavior. – Kevin Pauli Mar 28 '12 at 19:17
  • It is true they are not pooled in the same way, or for the same reason as stateless. They are however in this case constructed (object initialization is "expensive" in java) to be available for a second+ concurrent need. If it really is true that 200 is created after 100 request we do really have a problem (with default configuration of glassfish). I'm sorry but I do doubt that is the case. – esej Mar 28 '12 at 22:35
  • I'll look more closely at it - hopefully tonight (CET), sorry if I have sounded harsh. – esej Apr 03 '12 at 08:47
  • Apology accepted! Thanks for taking the time to look. In all likelihood it's something wrong with my configuration; I have worked with SLSB's before but I am a SFSB (and JEE6) newbie. – Kevin Pauli Apr 04 '12 at 20:05