Jsf+controle de zona crítica

Olá,

Estou trabalhando em uma agenda de marcações. Diversas pessoas abrem ao mesmo tempo a agenda para verificar se tem horário disponível. Se não estiver com o indicador “ocupado”, elas clicam no horário e o horário é reservado para elas.

O problema é que algumas pessoas estão conseguindo marcar o mesmo horário (com diferenças pequenas, de segundos entre as marcações). A parte de apresentação é toda em jsf e o código que controla a aplicação está em um EJB que é importado como biblioteca para a aplicação.

Existe alguma forma de conseguir impedir que as pessoas marquem o mesmo horário, de preferencia, sem ser por multithread?

Sugiro que você crie um método sincronizado para marcar o horário. Antes de marcar o horário verifique se já não está marcado. Caso esteja, mostre uma mensagem informando que não foi possível marcar o horário, pois esse está sendo utilizado por outra pessoa.

Também gosto de fazer esse tipo de restrição no banco de dados. Não sei como você modelou, mas seria interessante ter uma tabela com as marcações onde o campo do horário tenha a restrição única (unique).

Essa verificação já é feita.

O problema é que enquanto está retornando a resposta do banco para a aplicação, alguém já foi lá e fez a inserção. Tenho diferença entre as marcações de 2 segundos.

Não fui eu quem modelou a tabela, vou verificar se existe a restrição unique para o timestamp de horário =)

Mas se o método da inserção for sincronizado, outro usuário não conseguirá marcar o mesmo horário.

[quote]11.3. Optimistic concurrency control

The only approach that is consistent with high concurrency and high scalability, is optimistic concurrency control with versioning. Version checking uses version numbers, or timestamps, to detect conflicting updates and to prevent lost updates. Hibernate provides three possible approaches to writing application code that uses optimistic concurrency. The use cases we discuss are in the context of long conversations, but version checking also has the benefit of preventing lost updates in single database transactions.

11.3.1. Application version checking

In an implementation without much help from Hibernate, each interaction with the database occurs in a new Session and the developer is responsible for reloading all persistent instances from the database before manipulating them. The application is forced to carry out its own version checking to ensure conversation transaction isolation. This approach is the least efficient in terms of database access. It is the approach most similar to entity EJBs.

// foo is an instance loaded by a previous Session
session = factory.openSession();
Transaction t = session.beginTransaction();

int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion != foo.getVersion() ) throw new StaleObjectStateException();
foo.setProperty(“bar”);

t.commit();
session.close();
The version property is mapped using <version>, and Hibernate will automatically increment it during flush if the entity is dirty.

If you are operating in a low-data-concurrency environment, and do not require version checking, you can use this approach and skip the version check. In this case, last commit wins is the default strategy for long conversations. Be aware that this might confuse the users of the application, as they might experience lost updates without error messages or a chance to merge conflicting changes.

Manual version checking is only feasible in trivial circumstances and not practical for most applications. Often not only single instances, but complete graphs of modified objects, have to be checked. Hibernate offers automatic version checking with either an extended Session or detached instances as the design paradigm.

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html
[/quote]

Isso ajuda?

Felipe, ainda não tinha tentado isso.

O que estava fazendo é o seguinte:

conn.setAutoCommit(false);
 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Fazia os selects de verificação e o insert

depois

 stmt.close();
 conn.commit();

Muito obrigada pelas respostas. Vou testar aqui e retorno o resultado =D

  1. Ao clicar no botão para nova inserção verifique se existe vaga disponível
    1.1. Caso exista
    a) Insira um novo registro vazio e relacione apenas com o horário. Servira para se alguém clicar no btn (passo 1) a verificação falhar.
    b) Redirecionar para a tela do usuário preencher os dados
    c) Atualizar o registro incluído (passo a) com o dados informados (passo b)
    1.2. Caso não exista
    a) Avise ao usuário

Vinicius, obrigada pela sugestão, mas o usuário não preenche nada. Ele só tem que clicar em um horário =)

Felipe, sinceramente, não sei como implementar a sua sugestão, pois o método responsável pela marcação está num EJB e ele não cuida de sessão.

É correto colocar esse controle de sessão no EJB??

Quanto a tentativa que fiz, citada acima, esqueci de falar o erro que retorna.

Quando faço

            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Acontece o seguinte erro:

Uso JBOSS