quinta-feira, 5 de outubro de 2017

WildFly: Configurar Oracle XA Datasource

standalone.xml

  <xa-datasource jndi-name="java:jboss/datasources/dataBaseXA" pool-name="dataBaseXA" enabled="true">
                    <xa-datasource-property name="URL">
                        jdbc:oracle:thin:@127.0.0.1:1521:XE
                    </xa-datasource-property>
                    <driver>oracleXA</driver>
                    <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
                    <xa-pool>
                        <is-same-rm-override>false</is-same-rm-override>
                        <no-tx-separate-pools>true</no-tx-separate-pools>
                    </xa-pool>
                    <security>
                        <user-name>****</user-name>
                        <password>*****</password>
                    </security>
                </xa-datasource>
      </driver>
            <driver name="oracleXA" module="com.oracle.ojdbc6">
            <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
      </driver>


An XA transaction, in the most general terms, is a "global transaction" that may span multiple resources. A non-XA transaction always involves just one resource. 

An XA transaction involves a coordinating transaction manager, with one or more databases (or other resources, like JMS) all involved in a single global transaction. Non-XA transactions have no transaction coordinator, and a single resource is doing all its transaction work itself (this is sometimes called local transactions). 

XA transactions come from the X/Open group specification on distributed, global transactions. JTA includes the X/Open XA spec, in modified form. 

Most stuff in the world is non-XA - a Servlet or EJB or plain old JDBC in a Java application talking to a single database. XA gets involved when you want to work with multiple resources - 2 or more databases, a database and a JMS connection, all of those plus maybe a JCA resource - all in a single transaction. In this scenario, you'll have an app server like Websphere or Weblogic or JBoss acting as the Transaction Manager, and your various resources (Oracle, Sybase, IBM MQ JMS, SAP, whatever) acting as transaction resources. Your code can then update/delete/publish/whatever across the many resources. When you say "commit", the results are commited across all of the resources. When you say "rollback", _everything_ is rolled back across all resources. 

The Transaction Manager coordinates all of this through a protocol called Two Phase Commit (2PC). This protocol also has to be supported by the individual resources. 

In terms of datasources, an XA datasource is a data source that can participate in an XA global transaction. A non-XA datasource generally can't participate in a global transaction (sort of - some people implement what's called a "last participant" optimization that can let you do this for exactly one non-XA item). 

For more details - see the JTA pages on java.sun.com. Look at the XAResource and Xid interfaces in JTA. See the X/Open XA Distributed Transaction specification. Do a google source on "Java JTA XA transaction". 

Fonte: http://www.theserverside.com/discussions/thread/21385.html

ARJUNA012140: Adding multiple last resources is disallowed



<server xmlns="urn:jboss:domain:2.2">

    <extensions>
    ....
    </extensions>
    // Adicione as linhas abaixo para resolver o problema
    <system-properties>
        <property name="java.util.Arrays.useLegacyMergeSort" value="true"/>
    </system-properties>
</server>

segunda-feira, 25 de setembro de 2017

Gerenciamento de Transações com JTA

Fonte: http://www.portalarquiteto.com.br/gerenciamento-de-transacoes-com-jta/

Segue abaixo algumas considerações acerca da API JTA e de como EJB3 suporta transações declarativamente.
Normalmente as transações em aplicações são controladas em nível de métodos de negócio. Para se declarar que se está sendo utilizada alguma transação deve se utilizar a anotação @TransactionAttribute.
Por padrão a transação utiliza a estratégia REQUIRED, que significa que se o método do serviço não possuir alguma transação, uma será criada, e se já possuir uma transação esta será aproveitada. Esta é a estratégia que deve ser utilizada na maior parte dos casos uma vez que é bem lógica.
O que esta estratégia quer dizer resumidamente? Quando você estiver em um contexto de transação e caso ocorra alguma exceção do tipo Runtime ou exceção com a anotação @ApplicationException(rollback=true), deve ser dado o rollback na base de dados. Com esta estratégia padrão sempre será dado o rollback da transação mais externa, o que na maioria dos casos faz todo sentido.
Outras estratégias são:
  • SUPPORTS – Não cria nenhuma transação, mas se já existir alguma ela será utilizada.
  • MANDATORY – Requer que quem chamou o método tenha criado uma transação.
  • NEVER – Proíbe que quem chamou o método tenha iniciado uma transação.
  • REQUIRESNEW – Sempre começa uma nova transação, suspendendo a antiga existente.
  • NOTSUPPORTED – Suspende qualquer transação ativa.
Como padrão, pode-se anotar toda a classe com a anotação @TransactionAttribute, o que determina que todos os métodos da classe utilizam transação e com a estratégia REQUIRED. Caso exista algum método que deva usar outra estratégia basta sobrescrever a estratégia usando acima do método alguma das anotações abaixo:
  • @TransactionAttribute(TransactionAttributeType.NEVER)
  • @TransactionAttribute(TransactionAttributeType.MANDATORY)
  • @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
  • @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  • @TransactionAttribute(TransactionAttributeType.SUPPORTS)
Agora é definir para os métodos qual o melhor tipo de estratégia de transação e sobreescrever. É isto aí pessoal. Qualquer problema me avisem.

terça-feira, 15 de agosto de 2017

7zip: backup com data e hora no nome do arquivo | 7zip with date and time in backup file name




@ECHO OFF

REM DATA E HORA SEPARADA POR PONTO(.)
REM        DIA(DD)     MES(MM)     ANO(AAAA)   HORA(HH)    MINUTO(MM)  SEGUNDO(SS)   
Set TODAY="%DATE:~0,2%.%DATE:~3,2%.%DATE:~-4%" %time:~0,2%.%time:~3,2%.%time:~6,2%
 
ECHO.

REM -mx9 = MAXIMA COMPRESSAO
7za a -tzip "C:\TESTE_%TODAY%.zip" "C:\TESTE" -mx9

ECHO.
 
PAUSE

Postgresql: Backup automático com data e hora no nome do arquivo | Pgdump with date and time in backup file name

Windows!

Crie o arquivo e salve com a extensão .BAT, em seguida faça o agendamento da tarefa no Windows.

// Base local
@ECHO OFF

SET PGPASSWORD=123456

pg_dump.exe -h localhost -U postgres -F c -b -v -f "c:\BackupPostGre_%date:/=%_%time:~0,2%-%time:~3,2%-%time:~6,2%.backup" minhabase

// Base na Amazon
@ECHO OFF

SET PGPASSWORD=123456

pg_dump.exe -h minhabase.xxxx.sa-east-1.rds.amazonaws.com -p 5432 -U postgres -F c -b -v -f "c:\BackupPostGre_%date:/=%_%time:~0,2%-%time:~3,2%-%time:~6,2%.backup" minhabase 

sábado, 12 de agosto de 2017

JPA: JPQL count + distinct



Query q = em.createQuery("select "
            + " new com.apalmeira.modelo.followup.Gf( count( distinct p.nome ), u.nome)"
            + " from " + FollowUp.class.getName() + " o"
            + " left join o.pessoa p"
            + " left join o.usuarioResponsavel u"
            + " where"
            + " o.modulo = :modulo and "
            + " o.atividade in (:atividade1, :atividade2, :atividade3) and"
            + " o.mesVigencia between :dataInicio and :dataFim "
            + " group by 2"
            + " order by u.nome");

sexta-feira, 28 de julho de 2017

ejb3.timerService: Failed to start service

Pare o WildFly, localize o diretório standalone\data\timer-service-data, delete o conteúdo do diretório. Reinicie o WildFly, deve funcionar.

quinta-feira, 27 de julho de 2017

Executar Ajax ao selecionar data no Calendar do PrimeFaces



<p:ajax event="dateSelect" update="comboUsuario dataTablePesquisa"}"/>

quarta-feira, 12 de julho de 2017

Oralce 11g: Senha expirada / Desloquear usuário | password expired / Unlock user

1) Faça login com SYSDBA

2) VERIFICAR O PROFILE UTILIZADO PELO USUARIO

SELECT username, profile FROM dba_users
WHERE username in ('SYSTEM', 'SEUUSUARIO');

3) VERIFICAR O LIMITE DE TEMPO DA SENHA
select resource_type, limit
from dba_profiles
where profile='DEFAULT'
  and resource_name = 'PASSWORD_LIFE_TIME';
 
4) REMOVER O LIMITE DE TEMPO DA SENHA
ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

5) DESBLOQUEAR USUARIO
ALTER USER SEUUSUARIO IDENTIFIED BY SENHA ACCOUNT UNLOCK;

sexta-feira, 26 de maio de 2017

JPA: Como criar índices no JPA 2x


@Table(indexes = {@Index(name = "nomeIndice1", columnList="cpf", unique = true),
                  @Index(name = "nomeIndice2", columnList="dataNascimento",unique = false)})
public class Pessoa{
}

terça-feira, 23 de maio de 2017

Oracle: APAGAR TABELAS COM RELACIONAMENTOS.

DROP TABLE USUARIO CASCADE CONSTRAINTS

Deleta a tabela USUARIO e as chaves estrangerias das tabelas que utilizam a tabela USUARIO. Só assim é possível deletar a tabela USUARIO.

Se usar apenas DROP TABLE USUARIO, corre o seguinte erro:

ORA-02449: chaves primárias/exclusivas na tabela referenciadas por chaves externas

sexta-feira, 12 de maio de 2017

Instalar Wildfly como serviço

WildFly 8

1) Abrir Prompt de Comando
2) Navegar até o diretório do WildFly\bin\Service
3) Digitar o comando: service install
4) O serviço deverá ser configurado manualmente para inicio automático (services.msc)

WildFly 10

1) Abra o diretóro WildFly\docs\contrib\scripts\service.
2) Copie os arquivos para o diretório WildFly\bin.
3) Digitar o comando: service install
4) O serviço deverá ser configurado manualmente para inicio automático (services.msc)

JSF: Definir locale para Português do Brasil

Sistema exibindo mês como: April (Desejado Abril)

Adicionar o seguinte código no faces.config

<application> 
  <locale-config>
     <default-locale>pt_BR</default-locale>
  </locale-config>
 </application>

Resultado: Abril.

Obs: Setando o locale no faces.config vale para toda toda a aplicação, ou seja, tudo será em Português do Brasil. (Datas, Valore etc).

sexta-feira, 5 de maio de 2017

JSF1091: Nenhum tipo de mime pôde ser encontrado para o arquivo /restrict/home.jsp. Para resolver isso, adicione um mapeamento de mime-type ao web.xml do aplicativo.

Adicione as linhas abaixo no web.xml

<mime-mapping>
   <extension>jsp</extension>
   <mime-type>application/jsp</mime-type>
</mime-mapping>    

quinta-feira, 4 de maio de 2017

TIBCO/IREPORT: Instruções IF


Código dentro de um TextField
$F{pessoa} instanceof com.apalmeira.modelo.cadastro.Cliente ?
(
    $F{modulo}.name().equals("CONTABIL") ? ((com.apalmeira.modelo.cadastro.Cliente) $F{pessoa}).getUsuarioResponsavelContabil().getNome() :
    ($F{modulo}.name().equals("DPTOPESSOAL") ? ((com.apalmeira.modelo.cadastro.Cliente) $F{pessoa}).getUsuarioResponsavelDp().getNome() :
    ($F{modulo}.name().equals("FISCAL") ? ((com.apalmeira.modelo.cadastro.Cliente) $F{pessoa}).getUsuarioResponsavelFiscal().getNome() : "")
    )
) 

: 

$F{pessoa} instanceof com.apalmeira.modelo.cadastro.Domestica ?
(
$F{modulo}.name().equals("DOMESTICA") ? ((com.apalmeira.modelo.cadastro.Domestica) $F{pessoa}).getUsuarioResponsavel().getNome() : ""    
)  : "";

quinta-feira, 27 de abril de 2017

JPA: ORA-01791: não é uma expressão de SELECT (Order By)

Problema: não é uma expressão de SELECT

TypedQuery<CheckList> q = em.createQuery("select distinct"  
                       + " checkList "  
                       + " from " + CheckList.class.getName() + " checkList"  
                       + " where"  
                       + " checkList.dataCadastro = :dataPesquisa "  
                       + " order by checkList.usuarioResponsavel.nome", CheckList.class);  

Cause:
There is an incorrect ORDER BY item. The query is a SELECT DISTINCT query with an ORDER BY clause. In this context, all ORDER BY items must be constants, SELECT list expressions, or expressions whose operands are constants or SELECT list expressions.


Solução: adicionar um join e fazer a ordem através dele.
TypedQuery<CheckList> q = em.createQuery("select distinct"  
           + " checkList "  
           + " from " + CheckList.class.getName() + " checkList"  
           + " left join fetch checkList.usuarioResponsavel usuarioResponsavel"  
           + " where"  
           + " checkList.dataCadastro = :dataPesquisa "  
           + " order by usuarioResponsavel.nome", CheckList.class);  


sexta-feira, 24 de março de 2017

Exemplo JPQL x Criteria

JPQL
public List<Empresa> buscarPorEmpresa(Usuario usuario, String nomeEmpresa, Calendar dataInicio, Calendar dataFim){
    if(usuario != null && usuario.getContabilidade() != null){
        
        String queryEmpresa = "";
        if( (nomeEmpresa == null || nomeEmpresa.isEmpty()) && dataInicio != null && dataFim != null){
            queryEmpresa = " and o.dataInicio between '" + dataInicio.getTime() + "' and '" + dataFim.getTime() + "'";
        }else if( (nomeEmpresa != null && !nomeEmpresa.isEmpty()) && dataInicio != null && dataFim != null){
            queryEmpresa = " and lower(o.nomeEmpresa) like '" + nomeEmpresa.toLowerCase() + "%' and "
                         + "o.dataInicio between '" + dataInicio.getTime() + "' and '" + dataFim.getTime() + "'";
        }else {
            if(nomeEmpresa == null || nomeEmpresa.isEmpty()){
                return null;
            }
            queryEmpresa = " and lower(o.nomeEmpresa) like '" + nomeEmpresa.toLowerCase() + "%'";
        }
                
        TypedQuery<Empresa> q = em.createQuery("select o"
                               + " from " + Empresa.class.getName() + " o"
                               + " where "
                               + " o.contabilidade = " + usuario.getContabilidade().getId()
                               + queryEmpresa, Empresa.class);
        
        return q.getResultList();
    }else {
        return null;
    }
}

Criteria

public List<Empresa> buscarPorEmpresa(Usuario usuario, String nomeEmpresa, Calendar dataInicio, Calendar dataFim){
    if(usuario != null && usuario.getContabilidade() != null){

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Empresa> cq = cb.createQuery(Empresa.class);
        Root<Empresa> root = cq.from(Empresa.class);
        cq.select(root);
                    
        if( (nomeEmpresa == null || nomeEmpresa.isEmpty()) && dataInicio != null && dataFim != null){
            cq.where(cb.between(root.get("dataInicio"), dataInicio, dataFim));
        }else if( (nomeEmpresa != null && !nomeEmpresa.isEmpty()) && dataInicio != null && dataFim != null){
            cq.where(cb.between(root.get("dataInicio"), dataInicio, dataFim));
            cq.where(cb.like(cb.lower( root.get("nomeEmpresa") ), nomeEmpresa.toLowerCase() + "%"));
        }else {
            if(nomeEmpresa == null || nomeEmpresa.isEmpty()){
                return null;
            }                
            cq.where(cb.like(cb.lower( root.get("nomeEmpresa") ), nomeEmpresa.toLowerCase() + "%"));                
        }
                    
        TypedQuery<Empresa> query = em.createQuery(cq);
        
        return query.getResultList();
        
    }else {
 return null;
    }
}




quinta-feira, 9 de março de 2017

WiildFly: Muitos dados no p:dataTable não permitem a edição de linhas e colunas | Too much data in p:dataTable results in broken row/cell edit submits

Solução

Aumentar o max-parameters de 1000 (padrão) para um valor mais adequado.

<server name="default-server">
    <http-listener name="default" socket-binding="http" max-parameters="10000"/>
    <host name="default-host" alias="localhost">
        <location name="/" handler="welcome-content"/>
        <filter-ref name="server-header"/>
        <filter-ref name="x-powered-by-header"/>
    </host>
</server>

sexta-feira, 24 de fevereiro de 2017

JPA: Selecionar mês / Select Month

JPA: Selecionar mês / Select Month


Query q = em.createQuery("select"
    + " u from "+ Usuario.class.getName() + " u"
    + " left join fetch u.unidadeOperacional o"
    + " where u.tfAtivo is true and"
    + " to_char(u.dataNascimento,'Month') like '" + 
          LocalDate.now().getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault()) + "%'"
    + " order by u.dataNascimento");

quinta-feira, 16 de fevereiro de 2017

WieldFly / JBoss ARJUNA012117: TransactionReaper::check timeout for TX

A exceção ocorre durante a manipulação de uma grande quantidade de informações, que acaba consumindo muito tempo e excede o default-timeout de 5 minutos.

Solução:

Adicionar a linha abaixo no standalone.xml do WildFly/JBoss,

<coordinator-environment default-timeout="Segundos"/>

<subsystem xmlns="urn:jboss:domain:transactions:2.0">
    <core-environment>
        <process-id>
            <uuid/>
        </process-id>
    </core-environment>
    <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
    <coordinator-environment default-timeout="3600"/> <!-- 1h -->
</subsystem>

Atenção: Fique atento ao consumo de memória e processamento!

Exemplo de formatação de data com String.format



Calendar data = Calendar.getInstance();

System.out.println("Mes: " + String.format(Locale.getDefault(),"%1$tA, %1$te. %1$tB %1$tY",data) );
            
System.out.println("Dia da semana: " + String.format(Locale.getDefault(),"%1$tA",data) );

System.out.println("Dia do mês: " + String.format(Locale.getDefault(),"%1$te",data) );
            
System.out.println("Mês: " + String.format(Locale.getDefault(),"%1$tB",data) );
            
System.out.println("Ano: " + String.format(Locale.getDefault(),"%1$tY",data) );


Saída

Mes: Quinta-feira, 16. Fevereiro 2017
Dia da semana: Quinta-feira
Dia do mês: 16
Mês: Fevereiro
Ano: 2017

quinta-feira, 9 de fevereiro de 2017

@Asynchronous Methods

Fonte: http://tomee.apache.org/examples-trunk/async-methods/README.html

The @Asynchronous annotation was introduced in EJB 3.1 as a simple way of creating asynchronous processing.
Every time a method annotated @Asynchronous is invoked by anyone it will immediately return regardless of how long the method actually takes. Each invocation returns a Future object that essentially starts out empty and will later have its value filled in by the container when the related method call actually completes. Returning a Future object is not required and @Asynchronous methods can of course return void.

Example

Here, in JobProcessorTest,
final Future<String> red = processor.addJob("red"); proceeds to the next statement,
final Future<String> orange = processor.addJob("orange");
without waiting for the addJob() method to complete. And later we could ask for the result using the Future<?>.get() method like
assertEquals("blue", blue.get());
It waits for the processing to complete (if its not completed already) and gets the result. If you did not care about the result, you could simply have your asynchronous method as a void method.
Future Object from docs,
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled. If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future and return null as a result of the underlying task

The code

@Singleton
public class JobProcessor {
@Asynchronous
@Lock(READ)
@AccessTimeout(-1)
public Future<String> addJob(String jobName) {

    // Pretend this job takes a while
    doSomeHeavyLifting();

    // Return our result
    return new AsyncResult<String>(jobName);
}

private void doSomeHeavyLifting() {
    try {
        Thread.sleep(SECONDS.toMillis(10));
    } catch (InterruptedException e) {
        Thread.interrupted();
        throw new IllegalStateException(e);
    }
  }
}

Test

public class JobProcessorTest extends TestCase {

public void test() throws Exception {

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

    final JobProcessor processor = (JobProcessor) context.lookup("java:global/async-methods/JobProcessor");

    final long start = System.nanoTime();

    // Queue up a bunch of work
    final Future<String> red = processor.addJob("red");
    final Future<String> orange = processor.addJob("orange");
    final Future<String> yellow = processor.addJob("yellow");
    final Future<String> green = processor.addJob("green");
    final Future<String> blue = processor.addJob("blue");
    final Future<String> violet = processor.addJob("violet");

    // Wait for the result -- 1 minute worth of work
    assertEquals("blue", blue.get());
    assertEquals("orange", orange.get());
    assertEquals("green", green.get());
    assertEquals("red", red.get());
    assertEquals("yellow", yellow.get());
    assertEquals("violet", violet.get());

    // How long did it take?
    final long total = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);

    // Execution should be around 9 - 21 seconds
    // The execution time depends on the number of threads available for asynchronous execution.
    // In the best case it is 10s plus some minimal processing time. 
    assertTrue("Expected > 9 but was: " + total, total > 9);
    assertTrue("Expected < 21 but was: " + total, total < 21);

  }
}

Running

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.superbiz.async.JobProcessorTest
Apache OpenEJB 4.0.0-SNAPSHOT    build: 20110801-04:02
http://tomee.apache.org/
INFO - openejb.home = G:\Workspace\fullproject\openejb3\examples\async-methods
INFO - openejb.base = G:\Workspace\fullproject\openejb3\examples\async-methods
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: g:\Workspace\fullproject\openejb3\examples\async-methods\target\classes
INFO - Beginning load: g:\Workspace\fullproject\openejb3\examples\async-methods\target\classes
INFO - Configuring enterprise application: g:\Workspace\fullproject\openejb3\examples\async-methods
INFO - Configuring Service(id=Default Singleton Container, type=Container, provider-id=Default Singleton Container)
INFO - Auto-creating a container for bean JobProcessor: 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.async.JobProcessorTest: Container(type=MANAGED, id=Default Managed Container)
INFO - Enterprise application "g:\Workspace\fullproject\openejb3\examples\async-methods" loaded.
INFO - Assembling app: g:\Workspace\fullproject\openejb3\examples\async-methods
INFO - Jndi(name="java:global/async-methods/JobProcessor!org.superbiz.async.JobProcessor")
INFO - Jndi(name="java:global/async-methods/JobProcessor")
INFO - Jndi(name="java:global/EjbModule100568296/org.superbiz.async.JobProcessorTest!org.superbiz.async.JobProcessorTest")
INFO - Jndi(name="java:global/EjbModule100568296/org.superbiz.async.JobProcessorTest")
INFO - Created Ejb(deployment-id=org.superbiz.async.JobProcessorTest, ejb-name=org.superbiz.async.JobProcessorTest, container=Default Managed Container)
INFO - Created Ejb(deployment-id=JobProcessor, ejb-name=JobProcessor, container=Default Singleton Container)
INFO - Started Ejb(deployment-id=org.superbiz.async.JobProcessorTest, ejb-name=org.superbiz.async.JobProcessorTest, container=Default Managed Container)
INFO - Started Ejb(deployment-id=JobProcessor, ejb-name=JobProcessor, container=Default Singleton Container)
INFO - Deployed Application(path=g:\Workspace\fullproject\openejb3\examples\async-methods)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 13.305 sec

Results :

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

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 21.097s
[INFO] Finished at: Wed Aug 03 22:48:26 IST 2011
[INFO] Final Memory: 13M/145M
[INFO] ------------------------------------------------------------------------

How it works under the covers

Under the covers what makes this work is:
  • The JobProcessor the caller sees is not actually an instance of JobProcessor. Rather it's a subclass or proxy that has all the methods overridden. Methods that are supposed to be asynchronous are handled differently.
  • Calls to an asynchronous method simply result in a Runnable being created that wraps the method and parameters you gave. This runnable is given to an Executor which is simply a work queue attached to a thread pool.
  • After adding the work to the queue, the proxied version of the method returns an implementation of Future that is linked to the Runnable which is now waiting on the queue.
  • When the Runnable finally executes the method on the real JobProcessor instance, it will take the return value and set it into the Future making it available to the caller.
Important to note that the AsyncResult object the JobProcessor returns is not the same Future object the caller is holding. It would have been neat if the real JobProcessor could just return String and the caller's version of JobProcessor could return Future<String>, but we didn't see any way to do that without adding more complexity. So the AsyncResult is a simple wrapper object. The container will pull the String out, throw the AsyncResult away, then put the String in the real Future that the caller is holding.
To get progress along the way, simply pass a thread-safe object like AtomicInteger to the @Asynchronous method and have the bean code periodically update it with the percent complete.

Related Examples

For complex asynchronous processing, JavaEE's answer is @MessageDrivenBean. Have a look at the simple-mdb example

APIs Used

Source

svn co http://svn.apache.org/repos/asf/tomee/tomee/trunk/examples/async-methods
cd async-methods
mvn clean install
Copyright © 1999-2016 The Apache Software Foundation, Licensed under the Apache License, Version 2.0. Apache TomEE, TomEE, Apache, the Apache feather logo, and the Apache TomEE project logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners.