A mágica por trás das interceptações em Java

Tinha escrito esse no LinkedIn há algum tempo. Só estou postando aqui para manter tudo junto.

Qualquer programador Java deve estar mais habituado a injetar dependências, definir métodos de callback ou, entre outras, fazer lazy load de suas entidades. Mas você já parou para pensar como isso acontece por trás dos panos? Como, por exemplo, o Hibernate permite que você defina métodos para executar antes e depois da sua entidade ser sincronizada com o banco de dados subjascente, como um container EJB/CDI consegue interceptar as chamadas de métodos para iniciar transações, executar rotinas de segurança e encapsular exceções?

Como acontece em qualquer profissão, o profissional trabalha melhor quando conhece bem a ferramenta que utiliza. E não seria diferente com os profissionais de TI. Tomar conhecimento dos mecanismos que permitem com que a mágica por trás das tecnologias aconteçam não só te tornará um profissional melhor, mas te permitirá antecipar situações inconvenientes e utilizar seguramente um componente qualquer.

Neste caso, vamos falar dos proxies.

Proxy é o nome do design pattern designado a permitir fornecer um substituto para um objeto qualquer. Esse substituto deve ser capaz de interceptar as chamadas ao objeto substituído permitindo que operações extras sejam executadas em complemento à função do objeto real.

Suponha que você tenha a classe a seguir:

public class Greetings {
   
    public String sayHello(String to) {         
        return "Olá " + to + ".";     
    } 
} 

E você tenha uma factory para os objetos dela:

public class GreetingsFactory {

    public static Greetings createGreetings() {  
       return new Greetings();
    }
} 

Quando você precisar gerar uma saudação, você pode facilmente invocar:

GreetingsFactory.createGreetings().sayHello("Jaumzera");

Que vai te retornar um belo:

Olá Jaumzera.

Agora suponha que em dado momento seu cliente decida que a saudação deva ser retornada entre tags <h1> e </h1>. É claro que no mundo real simplesmente alteraríamos a classe Greetings para fazer o desejado. Entretanto, aqui estamos para exemplificar o padrão proxy.

public class GreetingsHtmlProxy extends Greetings {
   
    @Override
    public String sayHello(String to) {         
        return "<h1> " + new Greetings().sayHello(to) + ".</h1>";     
    } 
} 

A nova classe GreetingsHtmlProxy implementa a mesma interface do proxy (que neste caso é a própria classe), delega a chamada para um objeto da classe Greetings e altera seu resultado para se adequar ao novo requisito. E então a fábrica é alterada para retornar para retornar o novo objeto saudador:

public class GreetingsFactory {

    public static Greetings createGreetings() {  
       return new GreetingsHtmlProxy();
    }
} 

O fato da classe proxy estender a classe original, fará com que não tenhamos problemas de compatibilidade em quaisquer que sejam os pontos onde a classe Greetings original estava sendo usada.

E onde antes exibia uma suadação simples, passará a exibir:

<h1>Olá Jaumzera.</h1>

Observe que utilizando a classe proxy poderíamos, por exemplo, executar ações antes ou depois do método orignal ser executado, como executar rotinas de log, iniciar e parar transações, criar callbacks para pré e pós execução, entre outras.

Concordamos no fato de que é um exemplo medíocre, mas serve bem para mostrar o papel de um objeto substituto, ou seja, um proxy.

Com a importância que esse tipo de abordagem foi ganhando, foi inserida na plataforma Java, a partir da versão 1.3, a possibilidade de criar-se versões dinâmicas do pattern proxy. Essas versões nada mais são do que implementações de runtime de interfaces Java. Pelo fato de se basearem em interfaces, vamos extrair uma interface do exemplo anterior.

public interface IGreetings {
    
    String sayHello(String to);
    
}

E alterar o objeto Greetings para implementá-la:

public class Greetings implements IGreetings {
    
    public String sayHello(String to) {
        return "Olá " + to;
    }        
}

Para criar um proxy dinâmico, neste caso, vamos implementar a interface InvocationHandler, do pacote java.lang.reflect:

public class GreetingsInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {

        return "<h1>" + new Greetings().sayHello((String) args[0]) + "</h1>";
    }
}

A interface faz exatamente o mesmo trabalho do proxy que implementamos manualmente, ou seja, envolve o retorno do objeto original em tags <h1> e </h1>.

Para executar o exemplo, criamos uma instância do proxy a partir da classe Proxy, também do pacote `java.lang.reflect`:

IGreetings greet = (IGreetings) Proxy.newProxyInstance(
                Greetings.class.getClassLoader(),
                new Class[]{IGreetings.class},
                new GreetingsInvocationHandler());

System.out.println(greet.sayHello("Jaumzera"));

E, ao executar, podemos observar o resultado desejado.

<h1>Olá Jaumzera.</h1>

O fato é que utilizar implementações de runtime baseadas em interfaces limita um pouco a utilização da estratégia de proxy. Portanto, frameworks como o próprio Hibernate utilizam APIs mais sofisticada que permite a criação de proxies baseadas em classes concretas, como por exemplo o CGLib (https://github.com/cglib/cglib), uma vez que seus objetos de trabalho, ou seja, as entidades, são baseadas em POJOs frequentemente criados sem interface de negócio.

Para implementar o mesmo proxy anterior utilizando a API CGLib, faríamos da seguinte forma.

 Greetings greetCGLib = (Greetings) Enhancer.create(
                Greetings.class, new MethodInterceptor() {

            @Override
            public Object intercept(Object o, Method method,
                    Object[] os, MethodProxy mp) throws Throwable {
                
                return "<h1>" + mp.invokeSuper(o, os) + "</h1>";
            }
        });
        
        System.out.println(greetCGLib.sayHello("Jaumzera"));

E, ao executar, verás que o resultado é o mesmo do exemplo anterior. Também é evidente que a CGLib simplifica muito a criação dos proxies dinâmicos, uma vez que podem ser utilizadas a partir de classes concretas, em detrimento dos proxies Java, que são baseados em interfaces.

Enfim, resta dizer que a CGLib não possui uma documentação muito legal, portanto, você tem que confiar no tato e no JavaDoc da API. Além do fato de que, a menos que você esteja projetando frameworks ou qualquer coisa do tipo, sempre haverá uma solução baseada em modelos de objetos mais elegante do que apelar para introspecção de objetos ou qualquer coisa mais “baixo nível” dessas.

Até o próximo.

Similaridade entre palavras

Há um bom tempo (quase uma década) eu tive um problema para normalizar os nomes das ruas de uma base de dados, que eram digitadas manualmente pelos usuários, com os nomes da base de dados fornecida pelos Correios. Na época, um formulário de venda era preenchido manualmente e gravado no banco de dados. Quando houve a necessidade e extrair relatórios analíticos, com base nas informações de venda, nos deparamos com o problema da grafia dos nomes das ruas que era um verdadeiro pesadelo. Só para exemplificar, a rua “Juscelino Kubitschek” era escrita das formas mais criativas possíveis:

  • Jusselino Kubicheque
  • Jucelino Kubicheck
  • Jucelino Kubicheki
  • JK

E por aí vai. O desafio era encontrar uma forma de normalizar essa base automaticamente, sem ter que verificar cada uma das alternativas criativas inseridas pelos usuários.

Depois de alguma busca, me deparei com um artigo denominado How to Strike A Match que propunha uma solução que consistia em criar uma série de pares de caracteres de cada uma das duas palavras, encontrar a interseção entre os  dois grupos de pares, multiplicá-la por 2 e dividi-la pela soma da quantidade de pares.

strike_a_match

O resultado da comparação é o percentual de aproximação entre as duas entradas. Daí bastou estabelecer um nível de corte com base na similaridade e executar as atualizações.

Esses dias, levantando um material sobre NLP para um sistema em que estou trabalhando, acabei me lembrando desse fato, justamente por causa a dificuldade de se fazer análise de linguagem natural para buscas em uma base de dados. Então resolvi postar aqui, já que faz uma era que não vinha escrevendo.

Essa versão do Gist já está atualizada para o Java 8. Já a saída fica como na imagem a seguir:

strikeamatch_output

Sendo que quanto mais próximo de 1, mais parecidas são as duas entradas.

Eu não consegui encontrar o artigo e nem o site original, porém se você quiser conhecer o processo na íntegra, ele está disponível aqui.

Valeu!

Empacotamento Java

Há tempo sem escrever aqui por causa da correria. Dia desses, estava conversando com um amigo de trabalho e entramos no mérito do empacotamento dos sitemas em que estávamos trabalhando. Na ocasião, o assunto era DDD, mas acabamos caindo no assunto packages. A maioria dos sistemas em que eu trabalhei usa aquela divisão de responsabilidades do tipo DAOs, services e controllers na qual, teoricamente, a DAO toma conta das suas entidades (entidades ORM), os serviços encapsulam a lógica de negócio e os controllers atuam como – rufem os tambores – controladores de fluxo.

Minha opinião é que esse tipo de divisão é assim: DAO: tabelas relacionais representadas em objetos Java; Services: lógica estruturada usando classes e objetos; e contollers: onde vai todo o resto. Mas isso é assunto para outro dia.

É muito comum, nesse tipo de divisão – que também é chamada de package-by-layer – utilizar um pacote com.acme.model (ou entity) para colocar as entidades, com.acme.service para colocar os serviços e com.acme.controller para colocar os controllers, sob a justificativa de que isso mantém os artefatos bem separados caso o sistema venha a escalar.

Não!

Gosto de pensar em pacotes como pequenos módulos – package-by-feature – se você tem um módulo de gerenciamento de usuários, nada mais justo do que manter os respectivos artefatos (entities, DAOs, services e controllers) dentro do pacote com.acme.usuario. Não é?

Pense bem, isso cria uma boa alternativa pra você utilizar o modificador package-private ou default (só contextualizando que estamos falando sobre Java). Dessa forma, você pode garantir, por exemplo, que objetos do seu módulo Usuarios só sejam instanciados dentro dos limites do seu próprio pacote. E isso é só um dos exemplos.

Para deixar mais claro, quero dar uma “introspectada” no empacotamento de uma das APIs do Java: o java.util, responsável por prover, dentre outras, as implementações de coleções (famigerada Collections API). Se você reparar, não há divisões internas do pacote no que tange lists ou sets nem mesmo para a própria raízes das coleções, como as interfaces Collection e Map. Está tudo imediatamente abaixo de java.util:

java.util.packagingE aí, pra reforçar o que eu disse há pouco, a prória implementação da interface Entry da classe HashMap faz uso do package-private, que eu comentei há pouco:

static class Entry implements Map.Entry { ... }

Vantagens do empacotamento da perpectiva de features

Então resta enumerar as benesses de se empacotar seu software do prisma package-by-feature. Essas são só as que eu julguei legais de elencar aqui, as demais vou deixar como links no final do post.

Coesão

Vai te fazer pensar melhor sobre os limites do seu software – isso no DDD é chamado de Bounded Contexts – e acredite, vai diminuir a quantidade de artefatos que você terá que escrever. Na divisão por camadas, é muito comum que, ao codificar suas camadas em pacotes diferentes, você automaticamente comece a escrever artefatos desnecessários como por exemplo, de uma relação Usuario @OneMany Conta, você acabe escrevendo um UsuarioDAO e um ContaDAO. É um exemplo miserável, eu sei, mas já vi muito. Conta, de uma perpectiva de empacotamento por feature, poderia ser transparentemente e  automaticamente internalizada pelo módulo de usuários.

Navegação

No que tange a navegação, empacotar por feature também te deixa mais confortável para encontrar os artefatos que você deve alterar. É comum, por exemplo, que alterações nas entidades propaguem alterações por todas as outras camadas. Manter tudo junto vai aliviar seu estresse quando você tiver que sair procurando onde mais estão os fragmentos do seu módulo.

Testes

Eu duvido que você já não tenha se deparado com uma situação em que teve que modificar a visibilidade de algum aterfato só para conseguir testá-lo – eu conheço o dilema sobre “testar ou não testar privados”, mas acontece que você vai cedo ou tarde se deparar com isso . Então, empacotar por feature também vai te beneficiar nesse sentido, uma vez que ao invés de publicar os atributos ou comportamentos da sua classe, você pode simplesmente deixá-los privados ao nível do pacote.

Nomenclatura

Aumenta significativamente o escopo dos nomes que você pode empregar em seu código. Pense assim, uma vez que o pacote compõe o nome (unique name) das suas classes, você poderia simplificá-los:

  • com.acme.usuario.Usuario.java
  • com.acme.usuario.Dao.java
  • com.acme.usuario.Service.java
  • com.acme.usuario.Controller.java
  • com.acme.usuario.Exception.java

Sei que tem gente que vai infartar ao ler isso, mas não deixa de ser uma possibilidade.

E você, como empacota?

Fontes:

Regex sem medo: de uma vez por todas (parte 2)

Prosseguindo com o post anterior, vamos falar sobre o operador “ou”, grupos e substituição. A propósito: eu tinha pego um texto do tipo Lorem Ipsum para utilizar de alvo nos exemplos anteriores, mas confesso que ficou bem bosta. Portanto, a partir de agora, vou utilizar um texto que se adapte melhor ao contexto do problema.

Operador “|”: um ou outro

Imagine uma lista de URLs entre as quais você queira encontrar as que utilizam http e https. Você poderia simplesmente utilizar a seguinte regex:

/https?:\/\/.+/

Screenshot from 2017-08-04 21-24-54

Que diz ao interpretador:

um “http”, seguido ou não de um “s”, seguido de uma “/”, seguido de outra “/”, de um ou mais qualquer coisa.

Neste caso, as barras estão escapadas (cada uma com uma contra barra) para evitar que que elas sejam interpretadas como o final da regex, já que elas são representadas sempre entre duas barras.

Agora imagine que você precisa casar também as URLs que utilizam o protocolo FTP. Utilizando os coringas e as listas de opções que foram apresentadas até agora, isso não seria possível (pelo menos não de um jeito fácil). É para isso que serve o operador “|” (ou); separar duas ou mais opções de busca. A regex ficaria assim:

(http|https|ftp):\/\/.+

Screenshot from 2017-08-04 21-51-22

Que diria:

um “http”, ou um “https”, ou um “ftp” seguido de uma “/”, seguido de outra “/”, de um ou mais qualquer coisa.

Repare que além do operador “|” foram inseridos os caracteres de agrupamento “(” e “)” para delimitar o grupo de opções.

Sem os parênteses:

/http|https|ftp:\/\/.+/

O resultado seria:

Screenshot from 2017-08-04 22-21-19
Porque o interpretador entenderia que você procura cada um dos blocos isoladamente, como se fosse 3 regexes:

/http//https/ ou /ftp:\/\/.+/

Outra alternativa seria você definir as 3 regexes separadas por “|”:

/http:\/\/.+|https:\/\/.+|ftp:\/\/.+/

Screenshot from 2017-08-04 22-35-35

(…) que, na minha opinião, não fica nem bonito nem muito inteligente. Mas funciona! 

Outra vantagem do agrupamento (ou grupo) é que ele cria referências para cada match chamadas de retrovisores (ou back reference) que podem ser utilizadas na em casos de substituição. Funciona assim: cada item de grupo casado é armazenado em uma variável para ser utilizada em uma operação de substituição. Por exemplo, o primeiro match do grupo fica em \1, o segundo em \2 e o eNésimo no \N. Exemplos explicam mais: vamos mascarar alguns CEPs utilizando a ferramenta de substituição do regex101.com bem na parte inferior da tela:

Screenshot from 2017-08-04 22-56-03

É só clicar sobre o SUBSTITUTION que ele a tela expande com o preview das substituições.

Substituindo

O CEP é algo mais ou menos assim XX.YYY-ZZZ, em que Z, Y e Z são inteiros entre 0 e 9. Portanto a regex fica:

/([0-9]{2})([0-9]{3})([0-9]{3})/

Ou seja:

Dois caracteres entre 0 e 9 (primeiro grupo: \1), seguidos de mais três caracteres entre 0 e 9 (segundo grupo: \2), seguido por mais três caracteres entre 0 e 9 (terceiro grupo: \3).

Vale acrescentar aqui cada um dos “0-9” da regex anterior poderia ser substituído por um “\d” – de dígito – que funciona exatamente da mesma forma. Falaremos sobre esses meta caracteres em outra ocasião, mas tenta lá.

E vou separar cada um dos grupos com o caractere adequado da máscara:  ou um “.” ou um “-“:

\1.\2-\3

Que é interpretado:

Primeiro match seguido por um ponto, seguido pelo segundo match seguido por um hífen, seguido pelo terceiro match. 

E o resultado:

Screenshot from 2017-08-04 23-09-42

Encerrando…

Ficou um pouco mais longo do que eu tinha imaginado inicialmente, portanto, vou encerrar por aqui. Continuamos na parte 3. Sugestões e críticas são mais bem-vindas que boletos e dívidas.

Obrigado por ler (ou “por ter lido”?) e até o próximo.

Regex sem medo: de uma vez por todas (parte 1)

Eu aprendi expressões regulares com o livro do Aurélio Jargas, que além de me ensinar, me motivou aprender a explicar as coisas de forma objetiva, clara e rápida. Foi um dos melhores livros de TI que eu já li e recomendo veementemente que você tenha uma cópia.

Expressões regulares, ou regex, é uma ferramenta de manipulação de texto que vai lhe permitir simplificar suas tarefas do dia-a-dia. Mesmo que você não seja um programador, certamente já deve ter encarado situações em que tinha que localizar ou até mesmo substituir um texto em um documento.

Para desenvolver e testar as expressões, eu costumo utilizar algumas ferramentas online, entre elas o Regexpal e o Regex101. As interfaces são bem simples: basicamente um campo para você inserir a regex, outro para inserir um texto de teste e um bloco para inserir a expressão de substituição.

Geralmente as regex são representadas entre duas barras (/regex/) seguida por alguns extras de configuração (/regex/ig), os quais vamos discutir mais adiante.

Pau na mula!

O exemplo mais básico é você procurar por um texto literal:

/et/

Vai buscar pelo texto “et”.

Screenshot from 2017-07-21 22:08:46

Até aí nada demais, qualquer editor de textos com uma ferramenta de busca faz isso. Você também deve ter percebido que qualquer ocorrência de “et” foi casada, seja ela isolada ou compondo outra palavra, como no caso de “amet” e “consectetur”. Se você você quisesse optar por encontrar somente a palavra “et” isolada, você poderia usar o delimitador de bordas: “\b”. A regex ficaria assim:

/\bet\b/

Screenshot from 2017-07-29 00-32-21

Os “\b” envolvendo a palavra “et” diz ao interpretador que a ocorrência deve ser uma palavra isolada.

Coringa ponto

O “.” (ponto) é o coringa mais básico. Ele quer dizer “qualquer coisa”. Por exemplo, se você quisesse buscar por todos os “e’s” seguidos por qualquer coisa:

/e./

Screenshot from 2017-07-23 17-24-39

O ponto diz ao interpretador:

um “e” seguido por qualquer coisa.

Repare que o “qualquer coisa” é qualquer coisa mesmo, inclusive um espaço em branco.

Lista de opções

As listas de opções entram em ação quando o ponto é demais, ou seja, você precisa delimitar as opções que deseja casar. Veja o exemplo:

/e[tx]/

Screenshot from 2017-07-21 22:14:47

Os colchetes ([ e ]) delimitam as listas de opções. O interpretador vai ler assim:

um “e” seguido por um “t” ou “x”.

Veja como fica se adicionarmos, por exemplo, um “a” na lista:

/e[txa]/

Screenshot from 2017-07-21 22:17:39

Neste caso, o interpretador passa a ler:

um “e” seguido de um “t”, um “x” ou um “a”.

As listas também permitem definir intervalos como “de a à z” ou “de 0 à 9”:

/e[a-z0-9]/

Screenshot from 2017-07-23 16-42-30

Como não temos nenhum caractere numérico no texto, o resultado destaca somente as letras “e” seguidas por qualquer caractere que esteja entre “a” e “z”, como especificado pela lista.

Lista negada

Ao invés de especificar o que você quer casar, você pode especificar o que você não quer.

/e[^\s\n,.-]/

usando um “^” no ínicio, a lista passa a funcionar com uma lista de opções que não serão aceitas:

Screenshot from 2017-07-23 22-43-46

qualquer “e” que não seja seguido por um espaço em branco (“\s”), quebra de linha (“\n”), uma vírgula (“,”), um ponto “.” ou um hífen (“-“).

Tanto faz usar um ” ” (espaço em branco) ou um “\s”. O critério é seu. Gosto do \s porque é mais fácil de visualizar. Já quanto ao hífen, o final da lista é o único lugar onde o ele pode aparecer sem estar escapado, ou seja, sem estar precedido por uma contra-barra. Em qualquer outra posição, ele seria considerado um operador de intervalo entre os caracteres que o precedessem e sucedessem. Já o ponto dentro da lista é um ponto mesmo; deixa de ter o efeito de um coringa.

Quantificadores

Os quantificadores servem para delimitar a quantidade de matches que você quer. São 4 tipos de quantificadores:

  • ? (interrogação): que quer dizer “tem ou não”  ou “zero ou um”;
  • * (asterisco): que quer dizer “zero ou mais“;
  • + (mais): que quer dizer “um ou mais“;
  • {x} (chaves): que quer dizer “exatamente x“, sendo x um número inteiro positivo ou um intervalo válido ex: {1,5} (de um a cinco), {0,9} (de zero a nove), etc.

Vamos modificar uma das regex anteriores para utilizar o quantificador “?”:

Quantificador “?”: tem ou não

/e[txa]?/

Screenshot from 2017-07-23 16-59-46

A expressão /e[txa]?/ diz para o interpretador:

um “e” seguido por zero ou um “t”, zero ou um “x”, ou zero ou um “a”, inclusive nenhum.

O “inclusive nenhum” é o que faz com que o e, do Lorem, seja marcado como match, já que ele não é sucedido por nenhuma das opções [txa]. Vale lembrar também que quantificadores são fixados imediatamente após ao valor que eles vão quantificar. Portanto, a interrogação na regex anterior vale somente para lista “[txa]” e não para o “e”.

Quantificador “*”: zero ou mais

O “*” funciona de forma similar ao “?”, porém, ao invés de zero ou um ele quantifica zero ou mais:

/e[txa]*/

Screenshot from 2017-07-23 16-50-47

Essa regex diz ao interpretador:

um “e” seguido de um ou mais “t”, um “x” ou um “a”, inclusive um “e” seguido de nada. 

Se trocarmos o “*” pelo “+”, o interpretador vai deixar de casar “e’s” que não estejam sucedidos por um “t”, “x” ou “a”.

Quantificador “+”: um ou mais

/e[txa]+/

Screenshot from 2017-07-23 16-55-23

Justamente por dizer “um ou mais” o quantificador “+” deixa de casar os casos de “e’s” seguidos por nenhum “t”, “x” ou “a”, como acontecia com o “*”.

Quantificador {x}

Quando você precisa quantificar número específico de matches, você usa as chaves. Por exemplo, vamos criar uma regex para casar todos os “e’s” seguidos por duas vogais:

/e[a-z]{2}/

Screenshot from 2017-07-23 22-25-45

O interpretador lerá:

um “e” seguido por um “a”, ou “e”, ou “i”, ou “o”, ou “u” exatamente duas vezes.

E como foi dito, você pode especificar intervalos:

/e[a-z]{3,5}/

Screenshot from 2017-07-23 22-30-31

qualquer “e” seguido por 3, 4, ou 5 caractere entre “a” e “z”.

Vale ressaltar que “{3,}” também é um intervalo válido. Neste caso “{3,}” quer dizer “três ou mais”, mas vou deixar esse teste para você fazer.

Encerrando…

Tendo sempre em mente escrever posts de 5 minutos, encerramos por aqui a primeira parte do tutorial das regex. Nos posts seguintes pretendo apresentar os blocos, os operadores de gulodice e algumas regex que utilizo no meu dia-a-dia.

Por hora é só.
Até o próximo.

Java EE Exceptions

Em complemento ao meu post anterior sobre tratamento de exceções, quero neste post focar em Java EE Exceptions.

As exceções Java EE se dividem em duas categorias básicas: System Exceptions e Applicatipon Exceptions. A grosso modo, uma System Exception (SE) é qualquer exceção que estenda a classe java.lang.RuntimeException enquanto que Application Exception (AE) é qualquer derivada de java.lang.Exception. Cada uma possui um conjunto de peculiaridades que dizem respeito à forma com que elas serão capturadas, encapsuladas e eventualmente repassadas para o cliente.

Application Exceptions

Uma AE pode ser qualquer tipo de exceção que estenda a classe java.lang.Exception como o exemplo a seguir:

public class MyAppException extends Exception {}

Para lançá-la, o método deve explicitamente declarar que o faz, utilizando a cláusula throws.

public void someMethod() throws MyAppException {
    // ...
}

A principal característica desse tipo de exceção é que ela será entregue ao cliente exatamente como ela foi lançada e, em contrapartida, fará com que o cliente explicitamente utilize um bloco try-catch para executar a chamada do método.

System Exceptions

As SE são as exceções que derivam da classe java.lang.RuntimeException (RTE).

public MySysException extends RuntimeException {}

Por se tratar de uma RTE, o método que a lança não precisa explicitamente declarar que o faz e o cliente também não necessariamente precisa executar a chamada dentro de um bloco try-catch. Quando um método lança uma SE, o container deve tomar algumas precauções importantes em relação ao EJB que a lança: se for um session bean (stateless ou stateful) ou um MDB, a instância que a lançou é destruída e a transação subjacente (se houver) será revertida (rolled-back). Essa é a diferença principal entre AEs e SEs. Enquanto que as AE são entregues para o cliente em seu estado original e sem forçar com que o container tome quaisquer medidas em relação à instâncias ou transações, as SEs sim o fazem.

Ainda quanto as SEs, o container irá encapsular a exceção original (a que foi lançada) em um javax.ejb.EJBException antes de entregá-la para o cliente.

A anotação @ApplicationException

A anotação @javax.ejb.ApplicationException serve para misturar os dois mundos. Por exemplo: ao utilizá-la em uma exceção do tipo RTE, faz com que ela seja tratada com exceção de aplicação:

@ApplicationException
public MySysException extends RuntimeException {}

Desta forma, sempre que um método lançar uma MySysException, o container irá se comportar da mesma forma com que se comporta em relação as AE convencionais  subclasses diretas de Exception  entregando a exceção original para o cliente (sem encapsulá-la em EJBException) e não tomando qualquer medida no sentido de destruir instância do EJB que a lançou ou reverter a transação subjacente.

A anotação ainda especifica um atributo boolean denominado rollback. Este atributo serve para indicar ao container se a transação deve ou não ser revertida.

@ApplicationException(rollback=true)
public MyAEException extends RuntimeException {}

A qualquer momento que uma instância da exceção MyAEException for lançada, ela fará com que a transação envolvida seja revertida.

Resumidamente é isso.
Até o próximo.