Pergunta

Eu tenho uma sessão de escopo de classe que contém um objeto para gerenciar estatísticas do usuário.Quando um usuário faz logon(através de SSO) uma aplicação de escopo do método verifica a tabela de sessões ativas - se nenhum for encontrado, a sessão é invalidado usando o id da sessão na tabela.

Uma linha é adicionada a uma userStats tabela na sessão de escopo de classe:

/**
     * get all the info needed for collecting user stats, add userStats to the users session and save to userStats table
     * this happens after session is created
     * @param request
     */
    private void createUserStats(HttpServletRequest request){
        if (!this.sessionExists) {
            this.userStats = new UserStats(this.user, request.getSession(true)
                    .getId(), System.getProperty("atcots_host_name"));
            request.getSession().setAttribute("userstats", this.userStats);
            Events.instance().raiseEvent("userBoundToSession", this.userStats);
            this.sessionExists = true;
            log.info("user " + this.user + " is now logged on");

            // add this to the db
            try {
                this.saveUserStatsToDb();
            } catch (Exception e) {
                log.error("could not save " + this.userStats.getId().getPeoplesoftId() + " information to db");
                e.printStackTrace();
            }           

        }

    }

Quando a sessão do usuário for destruída esta linha é atualizada com um registo de tempo.

Por razões que eu não posso explicar, nem duplicados 2 usuários nas últimas 2 semanas foram registados e bloqueado a linha.Quando isso acontece a qualquer banco de dados de chamadas por usuário que não são mais possíveis e a aplicação é efetivamente utilizável para este usuário.

 [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8180-3) SQL Error: 0, SQLState: null
2012-07-26 18:45:53,427 ERROR [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8180-3) Transaction is not active: tx=TransactionImple < ac, BasicAction: -75805e7d:3300:5011c807:6a status: ActionStatus.ABORT_ONLY >; - nested throwable: (javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: -75805e7d:3300:5011c807:6a status: ActionStatus.ABORT_ONLY >)

O encontro dessas estatísticas é importante, mas não a vida e a morte, se eu não puder obter a informação que eu gostaria de dar um up e mantê-lo em movimento.Mas isso não está acontecendo.O que está acontecendo é que o entityManager é marcar a transação para reversão e qualquer db chamada depois que retorna o erro acima.Originalmente, eu salvo os usuários de estatísticas no âmbito de aplicação - portanto, quando a linha bloqueado bloqueado o entityManager para o APLICATIVO INTEIRO (isso não vá mais além).Quando me mudei para o método de escopo de sessão, ele só bloqueia o usuário infrator.

I tried setting the entityManger to a lesser scope(I tried EVENT and METHOD):
        ((EntityManager) Component.getInstance("entityManager", ScopeType.EVENT)).persist(this.userStats);
        ((EntityManager) Component.getInstance("entityManager", ScopeType.EVENT)).flush();

Isso não faz chamadas de db em tudo.Eu tentei manualmente reverter a transação, mas não a alegria.

Quando eu bloqueio de uma linha em uma tabela que contém dados que é utilizado na conversação nível de escopo, os resultados não são tão catastróficas, não há dados é salvo, mas ele se recupera.

ETA:

Eu tentei levantar uma AsynchronousEvent - que funciona localmente, mas implantado para o nosso teste remoto do servidor e isso é estranho - eu recebo:

    DEBUG [org.quartz.core.JobRunShell] (qtz_Worker-1) Calling execute on job DEFAULT.2d0badb3:139030aec6e:-7f34
    INFO  [com.mypkg.myapp.criteria.SessionCriteria] (qtz_Worker-1) observing predestroy for seam
    DEBUG [com.mypkg.myapp.criteria.SessionCriteria] (qtz_Worker-1) destroy destroy destroy sessionCriteria
    ERROR [org.jboss.seam.async.AsynchronousExceptionHandler] (qtz_Worker-1) Exeception thrown whilst executing asynchronous call
    java.lang.IllegalArgumentException: attempt to create create event with null entity
            at org.hibernate.event.PersistEvent.<init>(PersistEvent.java:45)
            at org.hibernate.event.PersistEvent.<init>(PersistEvent.java:38)
            at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
            at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
...

O pouco estranho é que ele parece estar passando o Quartzo manipulador.

ETA novamente:

Assim, não é tão estranho, eu tinha o Quartzo como o async manipulador - eu pensei que era apenas para agendamento de tarefas.Também métodos assíncronos não tem acesso para o contexto de sessão, então eu tive que adicionar um parâmetro ao meu observando método para realmente ter um objeto a persistir:

@Observer("saveUserStatsEvent")
@Transactional
public void saveUserStatsToDb(UserStats userstats) throws Exception {
    if(userstats != null){
        log.debug("persisting userstats to db");
        this.getEntityManager().persist(userstats);
        this.getEntityManager().flush();
    }
}

Como faço para recuperar esse?

Foi útil?

Solução

Primeiro de tudo, especificando o âmbito de aplicação no Component.getInstance() não tem como resultado a criação do componente no escopo especificado. EntityManager instâncias de viver sempre com a conversa de contexto (seja ele temporário ou de longa duração).O scope parâmetro de getInstance() serve o propósito exclusivo de dicas do contexto em que o componente deve ser, a fim de evitar um caro de pesquisa em todos os contextos (que é o que acontece se você não especificar um contexto ou especificar o contexto errado).

A transação está sendo marcado para reversão porque do erro anterior.Se o entityManager cometer independentemente, não seria transacional (uma transação de fato garante que, se um erro acontecer, nada persiste).Se você quiser isolar o login transação a partir de estatísticas de coleta, a solução mais simples é realizar o saveUserStatsToDb método dentro de um evento assíncrono (transações são vinculados para o segmento, então, usando um thread diferente, garante que o evento é tratado em uma transação separada).

Algo como isto:

@Observer("saveUserStatsEvent")
@Transactional
public void saveUserStatsToDb(UserStats stats) {
    ((EntityManager)Component.getInstance("entityManager")).persist(stats);
}

E na sua createUserStats método:

Events.instance().raiseAsynchronousEvent("saveUserStatsEvent", this.userStats);

No entanto, esta apenas contorna o problema dividindo-se a transacções em dois.O que você realmente quero resolver é o fecho condição na base do problema.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top