7

I am trying to achieve the following :

EJB3 Singleton

@Singleton
@Startup
public class SomeSingleton implements SomeSingletonLocal  {

    // Entity Manager injection
    private EntityManager _entity_manager;

    @Override
    @Asynchronous
    public void createScenario(){
        method1();
        method2();
        // ...
    }

    public void method1(){
        // Persist an Event in a Database.
    }

    public void method2(){
        // Persist an Event in a Database.
    }

}

Managed Bean

@ManagedBean
@RequestScoped
public class SomeManagedBean{

    // Entity Manager injection
    private EntityManager _entity_manager;

    @EJB
    private SomeSingletonRemote _singleton;

    public void createScenario(){
        _singleton.createScenario();
    }

    public List<Event> getEventList(){
        // Retrieve events from database
    } 
}

JSF view

<h:form>
    <p:commandButton value="Start Long Stuff" 
        actionListener="#{SomeManagedBean.createScenario}" />
    <h:outputText id="count" value="#{SomeManagedBean.getEventList.size()}" />
            <p:poll interval="1" update="count" />
</h:form>

log

->SomeManagedBean.getEventList()
<-SomeManagedBean.getEventList() // Size = 0

// Buton clicked
->SomeManagedBean.createScenario()
      ->SomeSingleton.createScenario()
<-SomeManagedBean.createScenario()

->SomeManagedBean.getEventList() // will end at the end of SomeSingleton.createScenario
->SomeSingleton.method1()
<-SomeSingleton.method1() // persist
...
->SomeSingleton.methodN()
<-SomeSingleton.methodN() // persist

<-SomeSingleton.createScenario()

<-SomeManagedBean.getEventList() // size = N

I expected at least one call to getEventList between two methodI() call (ie. each second). When it enters in SomeSingleton.createScenario(), I dont know why getEventList is paused.

It looks like there is a lock with the entity manager or the transaction inside createScenario. Is that a reentrance problem ?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Charles Follet
  • 827
  • 1
  • 10
  • 28

1 Answers1

8

A @Singleton is indeed by default read/write locked. This is not strictly related to transactions, but to concurrency. See also a.o. Java EE 7 tutorial on the subject.

One way to solve it is to set the @ConcurrencyManagement to BEAN. This way you basically tell the container to not worry about concurrency at all and that you take all responsibility on your own.

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class SomeSingleton {}

Another way is to explicitly set @Lock to READ on the class or the read-only methods so that they can concurrently be invoked. Only when a method with an explicit @Lock(LockType.WRITE) is invoked on the same instance, then the lock will occur.

@Singleton
@Lock(LockType.READ)
public class SomeSingleton {}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Why did they [mention](https://docs.oracle.com/javaee/7/tutorial/ejb-basicexamples002.htm), "*If no `@Lock` annotation is present on the singleton class, the default lock type, `@Lock(LockType.WRITE)`, is applied to all business and timeout methods*"? – Tiny Dec 03 '15 at 14:54
  • @Tiny: can you please clarify what part exactly is unclear to you? It basically says that when there's no `@Lock` annotation on the class, then all methods default to `@Lock(LockType.WRITE)`. This is also covered by the answer (but perhaps in a bit unclear way and I'd like to improve that part then, depending on your feedback). – BalusC Dec 03 '15 at 14:57
  • These bold letters here : "*A `@Singleton` is indeed by default **read/write locked***". (Sorry, I initially got an incorrect impression, a singleton EJB has both "read" and "write" locks as default perhaps, this way, methods changing states should be "write" locked by default and the rest, if any, which do not change states should be "read" locked by default). – Tiny Dec 03 '15 at 15:09
  • 2
    @Tiny: The `@Lock(READ)` is indeed kind of misleading name as it basically means that the method becomes unsynchronized instead of synchronized (i.e. there's no means of any internal lock when the method is invoked). However, when a `@Lock(WRITE)` method is invoked, then the entire instance becomes locked, including all access to `@Lock(READ)` methods. – BalusC Dec 03 '15 at 15:13