= Anotação @AccessTimeout
:index-group: EJB
:jbake-type: page
:jbake-status: published

Antes de darmos uma olhada no `@AccessTimeout`, pode ajudar entender quando uma chamada precisa "esperar".

== Esperando...

=== Stateful Bean

[note]

NOTE: Por padrão, é permitido que clientes façam chamadas concorrentes para um stateful session object e o container é requerido para serializar cada requisição concorrente. O container nunca permite acesso multi-thread a instancia de um componente stateful. Por essa razão, métodos de leitura/escrita bloqueiam metadados assim como componentes que gerenciam a própria concorrência (bean-managed concurrency) não são aplicáveis a um stateful session bean e não devem ser usados.

Isso significa que quando um método `foo()` de uma instancia stateful esta sendo executado e uma chega uma segunda requisição para esse ou outro método, o container EJB serializa a segunda requisição. Isso não permite com que o método seja executado concorrentemente mas espere ate o primeiro método ser processado.

O cliente também tem de esperar quando o bean `@Stateful` esta em uma transação e o cliente o invocar de fora dessa transação.

=== Stateless Bean

Se existir 20 instancias de um bean no pool e todas elas estiverem ocupadas, quando uma nova requisição chegar, o processo *tem de esperar* ate algum bean estar disponível no pool. (Nota: a semântica de pool, se houver, não é coberta pela especificação. O server vendor pode ou não envolver uma condição de espera)

=== Singleton Bean

O container força um acesso single-thread por padrão para um componente singleton. Isso é parecido com o que a anotação `@Lock(WRITE)` faz. Quando um método anotado com `@Lock(WRITE)` é executado, todos os outros métodos `@Lock(WRITE)` e `@Lock(READ)` que são chamados tem de esperar até que ele termine sua execução.

=== Resumindo

 - `@Singleton` - Um método `@Lock(WRITE)` esta sendo invocado e o gerenciamento de concorrência pelo container esta sendo usado. Todos os métodos sao `@Lock(WRITE)` por padrão.
 - `@Stateful` - Qualquer método de uma instancia pode estar sendo utilizado e uma segunda chamada pode acontecer. Ou o bean `@Stateful` esta em uma transação e o cliente o chama de fora dessa transação.
 - `@Stateless` - Sem instancias disponíveis no pool. Como observado, a semântica de pool (se houver) não é coberta pela especificação. Caso exista uma semântica no server vendor que envolva uma condição de espera, a anotação `@AccessTimeout` deveria ser aplicada.

== `@AccessTimeout`

A anotação `@AccessTimeout` é simplesmente uma conveniência em torno da tupla `long` e `TimeUnit` comumente usadas na `java.util.concurrent` API.

[source,java,numbered]
----
import java.util.concurrent.TimeUnit;
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface AccessTimeout {
    long value();
    TimeUnit unit() default TimeUnit.MILLISECONDS;
}
----

== Uso

Um método ou uma classe pode ser anotada com `@AccessTimeout` para especificar o temo máximo que uma chamada deve esperar para acessar o bean quando acontecer uma condição de espera.

A semântica para o `value` é a seguinte:

 - `value` > 0 indica um tempo de espera que é especificado pelo elemento `unit`.
 - `value` de 0 significa que acesso concorrente não é permitido.
 - `value` de -1 indica que a chamada do cliente vai ficar bloqueada por tempo indeterminado ate que possa proceder.

Simples!

NOTE: Quando acontecer um timeout, qual exceção o cliente recebe?
Citando a especificação: "if a client-invoked business method is in progress on an instance when another client-invoked call, from the same or different client, arrives at the same instance of a stateful session bean, if the second client is a client of the bean's business interface or no-interface view, the concurrent invocation must result in the second client receiving a javax.ejb.ConcurrentAccessException[15]. If the EJB 2.1 client view is used, the container must throw a java.rmi.RemoteException if the second client is a remote client, or a javax.ejb.EJBException if the second client is a local client"
Ou seja pode receber `javax.ejb.ConcurrentAccessException`. Ou no caso de EJB 2.1 estar sendo utilizado pode receber `java.rmi.RemoteException` se for um cliente externo ou `javax.ejb.EJBException` se for local.

=== Sem padrão

Note que o atributo `value` não tem um valor padrão. Isso foi intencional, tendo a intenção de informar que se o `@AccessTimeout` não for explicitamente usado, o comportamento sera o do server vendor.

Alguns servidores vão esperar por um tempo pre determinado e lançar a exceção `javax.ejb.ConcurrentAccessException`, outros podem lançar de imediato.

== Exemplo

Aqui nos temos um simples `@Singleton` bean que possui tres métodos síncronos e um método anotado com `@Asynchronous`. O componente esta anotado com `@Lock(WRITE)` então apenas uma thread pode acessar o `@Singleton` por vez. Este é o comportamento padrão de um componente `@Singleton`, então usar a anotação `@Lock(WRITE)` não é necessário mas é importante para deixar claro que o componente tem um comportamento single-thread.

[source,java,numbered]
----
@Singleton
@Lock(WRITE)
public class BusyBee {

    @Asynchronous
    public Future stayBusy(CountDownLatch ready) {
        ready.countDown();

        try {
            new CountDownLatch(1).await();
        } catch (InterruptedException e) {
            Thread.interrupted();
        }

        return null;
    }

    @AccessTimeout(0)
    public void doItNow() {
        // faz alguma coisa
    }

    @AccessTimeout(value = 5, unit = TimeUnit.SECONDS)
    public void doItSoon() {
        // faz alguma coisa
    }

    @AccessTimeout(-1)
    public void justDoIt() {
        // faz alguma coisa
    }
}
----

O método `@Asynchronous` não tem uma relação direta com o `@AccessTimeout`, mas serve como uma forma simple de travar ("lockar") o bean para realizarmos o teste. Ele nos permite testar o comportamento concorrente do componente. 

[source,java,numbered]
----
public class BusyBeeTest extends TestCase {

    public void test() throws Exception {

        final Context context = EJBContainer.createEJBContainer().getContext();

        final CountDownLatch ready = new CountDownLatch(1);

        final BusyBee busyBee = (BusyBee) context.lookup("java:global/access-timeout/BusyBee");

        // Esse método assíncrono nunca termina.
        busyBee.stayBusy(ready);

        // Você ainda esta trabalhando abelhinha?
        ready.await();


        // Beleza, a abelha esta ocupada.


        { // Timeout imediato
            final long start = System.nanoTime();

            try {
                busyBee.doItNow();

                fail("A abelha continua ocupada");
            } catch (Exception e) {
                // A abelha continua muito ocupada como esperado.
            }

            assertEquals(0, seconds(start));
        }

        { // Timeout em 5 segundos
            final long start = System.nanoTime();

            try {
                busyBee.doItSoon();

                fail("A abelha deve estar ocupada");
            } catch (Exception e) {
                // A abelha continua ocupada como esperado.
            }

            assertEquals(5, seconds(start));
        }

        // Esse método vai te fazer esperar para sempre, apenas teste se estiver com bastante tempo :D
        // busyBee.justDoIt();
    }

    private long seconds(long start) {
        return TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
    }
}
----

== Executando

[source,bash]
----
mvn clean test
----

=== Saida
[source,bash]
----
-------------------------------------------------------
    T E S T S
-------------------------------------------------------
Running org.superbiz.accesstimeout.BusyBeeTest
Apache OpenEJB 4.0.0-beta-1    build: 20111002-04:06
http://tomee.apache.org/
INFO - openejb.home = /Users/dblevins/examples/access-timeout
INFO - openejb.base = /Users/dblevins/examples/access-timeout
INFO - Using 'javax.ejb.embeddable.EJBContainer=true'
INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
INFO - Found EjbModule in classpath: /Users/dblevins/examples/access-timeout/target/classes
INFO - Beginning load: /Users/dblevins/examples/access-timeout/target/classes
INFO - Configuring enterprise application: /Users/dblevins/examples/access-timeout
INFO - Configuring Service(id=Default Singleton Container, type=Container, provider-id=Default Singleton Container)
INFO - Auto-creating a container for bean BusyBee: Container(type=SINGLETON, id=Default Singleton Container)
INFO - Configuring Service(id=Default Managed Container, type=Container, provider-id=Default Managed Container)
INFO - Auto-creating a container for bean org.superbiz.accesstimeout.BusyBeeTest: Container(type=MANAGED, id=Default Managed Container)
INFO - Enterprise application "/Users/dblevins/examples/access-timeout" loaded.
INFO - Assembling app: /Users/dblevins/examples/access-timeout
INFO - Jndi(name="java:global/access-timeout/BusyBee!org.superbiz.accesstimeout.BusyBee")
INFO - Jndi(name="java:global/access-timeout/BusyBee")
INFO - Jndi(name="java:global/EjbModule748454644/org.superbiz.accesstimeout.BusyBeeTest!org.superbiz.accesstimeout.BusyBeeTest")
INFO - Jndi(name="java:global/EjbModule748454644/org.superbiz.accesstimeout.BusyBeeTest")
INFO - Created Ejb(deployment-id=org.superbiz.accesstimeout.BusyBeeTest, ejb-name=org.superbiz.accesstimeout.BusyBeeTest, container=Default Managed Container)
INFO - Created Ejb(deployment-id=BusyBee, ejb-name=BusyBee, container=Default Singleton Container)
INFO - Started Ejb(deployment-id=org.superbiz.accesstimeout.BusyBeeTest, ejb-name=org.superbiz.accesstimeout.BusyBeeTest, container=Default Managed Container)
INFO - Started Ejb(deployment-id=BusyBee, ejb-name=BusyBee, container=Default Singleton Container)
INFO - Deployed Application(path=/Users/dblevins/examples/access-timeout)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.071 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
----
