Começando do começo então:
Eu tenho uma classe JPAHelper que é responsável por criar e gerenciar o EntityManagerFactory:
public class JPAHelper implements ServletContextListener {
private static EntityManagerFactory emf;
public void contextInitialized(ServletContextEvent sce) {
emf = Persistence.createEntityManagerFactory("persistenceWeb");
}
public void contextDestroyed(ServletContextEvent sce) {
if (emf != null) {
System.out.println("Fechando EntityManagerFactory (" + getClass().getName() + ".contextDestroyed() )");
emf.close();
}
}
public static EntityManagerFactory getEntityManagerFactory() {
if (emf == null) {
if (JSFHelper.isJSFContext()) {
emf = Persistence.createEntityManagerFactory("persistenceWeb");
} else {
emf = Persistence.createEntityManagerFactory("persistenceSE");
}
}
return emf;
}
}
Esta classe está registrada como um listener no web.xml.
Todas as regras de acesso a este entity estão de uma classe chamada ArrecadacaoManager:
public class ArrecadacaoManager {
private EntityManagerFactory emf;
private EntityManager em;
...
public ArrecadacaoManager() {
this.emf = JPAHelper.getEntityManagerFactory();
this.em = getEM();
}
public ArrecadacaoManager(EntityManagerFactory emf) {
this.emf = emf;
this.getEM();
}
public ArrecadacaoManager(EntityManagerFactory emf, EntityManager em) {
this.emf = emf;
this.em = em;
}
public EntityManager getEM() {
if (em == null) {
em = emf.createEntityManager();
}
return em;
}
public void closeEM() {
if ((em != null) && (em.isOpen())) {
em.clear();
em.close();
}
em = null;
}
@Override
protected void finalize() throws Throwable {
closeEM();
}
...
public MesCompetenciaPessoa find(int mes, int ano, Pessoa pessoa) {
StringBuffer sql = new StringBuffer();
sql.append("select mp ");
sql.append(" from MesCompetenciaPessoa mp");
sql.append(" where mp.pessoa = :pPessoa ");
sql.append(" and mp.mesCompetencia.mes = :pMes ");
sql.append(" and mp.mesCompetencia.ano = :pAno");
Query qry = em.createQuery(sql.toString());
qry.setParameter("pPessoa", pessoa);
qry.setParameter("pAno", ano);
qry.setParameter("pMes", mes);
List result = qry.getResultList();
if (result.size() > 0) {
return (MesCompetenciaPessoa) result.get(0);
} else {
return null;
}
}
...
}
O método find() que eu copiei acima é um dos que eu identifiquei onde ocorre o lock.
Este método retorna um objeto MesCompetenciaPessoa, que por sua vez contém um MesCompetencia como atributo:
@Entity
public class MesCompetenciaPessoa implements Serializable {
private static final long serialVersionUID = -3012120036014653363L;
@Id
@SequenceGenerator(name = "MES_COMP_PES_SEQ", allocationSize = 1, sequenceName = "mescompetenciapessoa_id_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MES_COMP_PES_SEQ")
private long id;
@Temporal(TemporalType.TIMESTAMP)
private Date dataHoraEncerramento;
@ManyToOne
private MesCompetencia mesCompetencia;
...
}
Por que eu acho que o lock está no MesCompetencia e não no MesCompetenciaPessoa?
Por que o objeto MesCompetenciaPessoa é um para cada usuário, enquanto que o MesCompetencia é único para todos os usuários, então é mais provavel que o lock esteja ocorrendo na instância do MesCompetencia do que na instância do MesCompetenciaPessoa.
Por fim, a classe ArrecadacaoManager é usada diretamente nos ManagedBeans do JSF, por exemplo:
public class ArrecadacaoMB {
...
public String loadLancamentosDoMes() {
ResumoGeralArrecadacao linha = (ResumoGeralArrecadacao) listaConsulta.getRowData();
ArrecadacaoManager am = new ArrecadacaoManager();
setMesPessoaConsulta(am.find(linha.getMes(), linha.getAno(), contribuinte));
verificarSeMesAberto(am, linha.getMes(), linha.getAno());
return FORM_LANCAMENTOS;
}
...
Tudo isso funciona bem, rodando localmente, mas quando vai para produção e começa a ter varios usuários usando ao mesmo tempo, o sistema simplesmente para de responder em algumas operações (sempre as que envolvem esta entity). E nestas situações, ao tirar um dump, verifica-se que existem várias threads paradas mais ou menos no mesmo ponto:
"httpSSLWorkerThread-8181-24" - Thread t@113161
java.lang.Thread.State: WAITING
at java.lang.Object.wait(Native Method)
- waiting on <5220e2c2> (a oracle.toplink.essentials.internal.helper.ConcurrencyManager)
at java.lang.Object.wait(Object.java:485)
at oracle.toplink.essentials.internal.helper.WriteLockManager.acquireLocksForClone(WriteLockManager.java:99)
at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.cloneAndRegisterObject(UnitOfWorkImpl.java:669)
at oracle.toplink.essentials.internal.sessions.UnitOfWorkIdentityMapAccessor.getAndCloneCacheKeyFromParent(UnitOfWorkIdentityMapAccessor.java:167)
at oracle.toplink.essentials.internal.sessions.UnitOfWorkIdentityMapAccessor.getFromIdentityMap(UnitOfWorkIdentityMapAccessor.java:105)
at oracle.toplink.essentials.internal.sessions.IdentityMapAccessor.getFromIdentityMap(IdentityMapAccessor.java:310)
at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.registerExistingObject(UnitOfWorkImpl.java:3087)
- locked <3eeeb41d> (a oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork)
at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.registerExistingObject(UnitOfWorkImpl.java:3049)
- locked <3eeeb41d> (a oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork)
at oracle.toplink.essentials.queryframework.ObjectBuildingQuery.registerIndividualResult(ObjectBuildingQuery.java:339)
at oracle.toplink.essentials.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneNormally(ObjectBuilder.java:456)
at oracle.toplink.essentials.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:421)
at oracle.toplink.essentials.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:387)
at oracle.toplink.essentials.queryframework.ReportQueryResult.processItem(ReportQueryResult.java:220)
at oracle.toplink.essentials.queryframework.ReportQueryResult.buildResult(ReportQueryResult.java:182)
at oracle.toplink.essentials.queryframework.ReportQueryResult.<init>(ReportQueryResult.java:98)
at oracle.toplink.essentials.queryframework.ReportQuery.buildObject(ReportQuery.java:594)
at oracle.toplink.essentials.queryframework.ReportQuery.buildObjects(ReportQuery.java:643)
at oracle.toplink.essentials.queryframework.ReportQuery.executeDatabaseQuery(ReportQuery.java:804)
at oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:628)
at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:692)
at oracle.toplink.essentials.queryframework.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:746)
at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2244)
at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:952)
at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:924)
at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.executeReadQuery(EJBQueryImpl.java:367)
at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.getResultList(EJBQueryImpl.java:478)