Você está na página 1de 119

Editores

Osvaldo Doederlein (osvaldo@javamagazine.com.br)


Eduardo Spínola (eduspinola@gmail.com)

Arte
Diagramação - Romulo Araujo
Capa - Antonio Xavier

Produção
Gerência de Marketing - Kaline Dolabella

Atendimento ao leitor
A DevMedia possui uma Central de Atendimento on-line, onde você pode tirar suas dúvidas sobre
serviços, enviar críticas e sugestões e falar com um de nossos atendentes. Através da nossa central
também é possível alterar dados cadastrais, consultar o status de assinaturas e conferir a data de envio
de suas revistas. Acesse www.devmedia.com.br/central, ou se preferir entre em contato conosco através
do telefone 21 3382-5025.

Edições anteriores
Adquira as edições anteriores da revista Java Magazine ou de qualquer outra publicação do Grupo
DevMedia de forma prática e segura, em
www.devmedia.com.br/anteriores.

Publicidade
publicidade@devmedia.com.br
21 3382-5025
Anúncios - Anunciando nas publicações e nos sites do Grupo DevMedia, você divulga sua marca ou
produto para mais de 100 mil desenvolvedores de todo o Brasil, em mais de 200 cidades. Solicite nossos
Media Kits, com detalhes sobre preços e formatos de anúncios.

Java, o logotipo da xícara de café Java e todas as marcas e logotipos baseados em ou referentes a Java
são marcas comerciais ou marcas registradas da Sun Microsystems, Inc. nos Estados Unidos e em outros
países.
ÍNDICE

[Easy Java, Core]


Um pouco de Groovy
Uma linguagem de scripts simples e 100% compatível com Java
Marcelo Castellani

[Core, Web]
JBoss Application Server 5
As principais novidades do JBoss AS 5
Bruno Rossetto Machado e
João Paulo Viragine

[Web, Tutorial]
Spring Security
Segurança simples, poderosa e portável - à moda do SpringFramework
Michel Zanini

[Core, Web, Mobile]


JavaFX Script
Uma referência didática da linguagem JavaFX Script, uma das principais peças da plataforma JavaFX
Osvaldo Pinali Doederlein

[Vanguarda, Core]
Novos tempos: javax.time
A nova API de Data e Hora, que facilita a programação e corrige falhas históricas das suas antecessoras
Daniel Cicero Amadei e Michael Nascimento Santos

[Mobile]
Java ME Platform SDK 3
As novidades do novo SDK para Java ME, que substitui o antigo Wireless Toolkit
Ernandes Mourão Júnior

[Boas Práticas]
Estratégias de Integração de Aplicações Java EE
Melhores práticas de interoperabilidade Java para o desenvolvimento de aplicações robustas
Marco Aurélio de Souza Mendes

[Core, Expert]
Estratégias de Integração de Aplicações Java EE
Desenvolvendo aplicações concorrentes estáveis e escaláveis com a java.util.concurrent
Ronaldo Blanch Rocha
Um pouco de Groovy

Marcelo Castellani

Marcelo Castellani (castellani@itautec.com) é analista de sistemas sênior na Itautec S/A e atua na área
de desenvolvimento desde 1996, passando por linguagens como Visual Basic, C, C++ e Java. Mantém o
blog hype quino (www.hypequino.com) aonde fala de novas tecnologias em matérias curtas.

De que se trata o artigo:

Apresenta uma introdução a linguagem dinâmica Groovy, que possibilita ter uma produtividade de
linguagens dinâmicas como Python e Ruby dentro da máquina virtual Java, sem emendas.

Para que serve:

Tornar o desenvolvimento em Java mais rápido e produtivo, além de apresentar novos conceitos, como
closures, não disponíveis na linguagem Java, que podem facilitar a vida do desenvolvedor em tarefas
corriqueiras.
Em que situação o tema é útil: Para agilizar o desenvolvimento de aplicações Java, otimizando situações
onde a mesma é muito verbosa, como manipulação de XML por exemplo. Groovy possibilita, através de
mecanismos como closures, uma sintaxe mais limpa e produtividade em alto nível.

Um pouco de Groovy:

É possível programar para a plataforma Java sem usar a linguagem Java, e ainda por cima ser
extremamente produtivo. Groovy é uma linguagem dinâmica, flexível e que se integra com Java sem
emendas, além de fornecer recursos como closures e atribute accessors, não disponíveis na linguagem
padrão da JVM. Groovy, ao contrario de outras linguagens dinâmicas, possibilita gerar bytecodes Java e
possibilita o uso de toda a infra-estrutura já desenvolvida para suas aplicações.

Introdução

Linguagens dinâmicas estão na moda já há algum tempo. Desde o advento de frameworks de alta
produtividade como o Ruby on Rails e o Django, linguagens como Ruby e Python saíram de seus nichos e
passaram a fazer parte das rodinhas de conversa de desenvolvedores Java, outrora um tanto quanto
seletivos. Esses, então, descobriram um admirável novo mundo, com closures, tipos de dados complexos
e facilidades que não existem na sua linguagem preferida. Foi mais ou menos nesse meio que surgiu o
embrião do que viria a ser o Groovy.

Como tudo começou

No dia 29 de agosto de 2003 James Strachan publicou em seu blog o primeiro artigo sobre aquilo que
viria a ser o Groovy (veja em Links o endereço do post). Ele deixava bem claro as suas intenções na
época: “minha idéia inicial é fazer uma pequena linguagem dinâmica, que seja compilada diretamente em
classes Java e que tenha toda a produtividade elegante encontrada em Ruby e Python, mas que permita
reusar, estender, implementar e testar código Java já existente”. James procurava uma linguagem
dinâmica para desenvolver em plataforma Java, e em seu post ele deixava claro que as opções da época
não eram interessantes. Ele não queria apenas uma linguagem dinâmica, mas sim algo que pudesse ser
integrado ao que ele já tinha pronto em Java, algo que acelerasse seu desenvolvimento e que não o
obrigasse a jogar tudo o que tinha de código Java já pronto e testado (e em produção) no lixo. Enfim, ele
queria algo que não existia na época. Mas como querer é poder, James uniu-se a Bob McWhirter e juntos
fundaram o projeto Groovy em 2003.

Logo, com um grupo de pessoas que compartilhavam da mesma idéia, iniciaram o desenvolvimento da
linguagem. Foi em 2004, com a fundação do GroovyOne e a entrada de outros desenvolvedores (entre
eles Guillaume Laforge – hoje o mantenedor do projeto) que a coisa decolou. Foi criada a Groovy
Language Specification (GLS) e o kit para testes de compatibilidade (o TCK), além do parser básico da
linguagem. O embrião do projeto estava pronto e a partir daí não teria mais como voltar atrás.Groovy
evoluiu desconhecido por algum tempo, e até dezembro de 2007 várias versões foram lançadas sob o
número 1.1.x. Em 7 de dezembro de 2007 a versão final da família 1.1 foi lançada, e então nomeada
Groovy 1.5 devido às diversas modificações realizadas na mesma. Hoje a linguagem é uma especificação
do JCP (JSR 241) e é considerada a segunda linguagem oficial da plataforma.Ao contrario do que alguns
pensam, Groovy não é um concorrente do Java, mas uma ferramenta de apoio, como veremos neste
artigo.

O que é Groovy?

O web site oficial da linguagem possui uma das melhores definições sobre a linguagem, a qual reproduzo
a seguir: “Groovy é uma linguagem ágil e dinâmica para a Plataforma Java com recursos que são
inspirados em linguagens como Python, Ruby e Smalltalk, tornando-os disponíveis aos programadores
Java, usando uma sintaxe mais próxima do Java”. Ou seja, Groovy possui uma sintaxe semelhante ao
Java, mas com o poder de linguagens dinamicamente tipadas. Mas Groovy não é apenas uma linguagem
de script. Groovy pode ser compilada, gerando bytecodes Java, ou seja, um arquivo de código fonte
Groovy pode virar um arquivo .class (e esse recurso você não encontra por exemplo no JRuby). Isso
garante que a única coisa que você precisa para rodar seus códigos Groovy no ambiente de produção é a
máquina virtual Java (ou seja, nada mais do que usualmente já precisaria) e o jar com o runtime e API`s
do Groovy. É comum dizer que Groovy roda integrado com o Java sem emendas, o que não acontece com
seus “concorrentes”, por assim dizer.

Mais do que isso, o Groovy é totalmente integrado ao Java no mais baixo nível. Por exemplo, se você
instanciar um objeto do tipo Date em Groovy esse nada mais é do que uma instância de java.util.Date. E
tudo funciona de maneira transparente por que, por debaixo dos panos, tudo é bytecode Java.
Aí você me pergunta “ok, então pra que eu preciso disso?”, e a resposta é simples: para facilitar sua vida.
Groovy possui uma sintaxe mais simples e enxuta do que o Java, apesar de ainda parecer Java, e possui
recursos poderosos que não são encontrados na linguagem Java, como closures.E Groovy possui um
grande aliado, o Grails, um framework de produtividade baseado em Spring e Hibernate que permite o
desenvolvimento de aplicações Java EE com a mesma agilidade do pessoal do Ruby on Rails (e sem
configurar centenas de arquivos XML!).

Outro ponto importante é que ambos, Groovy e Grails, são hoje propriedades da SpringSource, empresa
que mantém o framework Spring. A empresa adquiriu a G2One, que cuidava de ambos, em 11 de
novembro de 2008 e em seu site (novamente na seção Links) é possível ver uma nota interessante sobre
o assunto.

O que preciso para desenvolver em Groovy?

Como qualquer outra linguagem, para desenvolver em Groovy você precisa de um editor de textos, de
um interpretador/compilador e outras ferramentas. Felizmente o NetBeans nos fornece tudo o que
precisamos (e até mais) num único pacote.O NetBeans é uma excelente IDE mantida pela Sun e
desenvolvida por uma grande comunidade ao redor do mundo. Ela possui suporte nativo a Java, C/C++,
PHP, JavaScript, Ruby e Groovy.

Para este artigo vou usar a versão 6.5 da IDE rodando no Mac OS X versão 10.5.6. Você pode baixar
gratuitamente o NetBeans de seu site (veja o endereço no quadro Links). Caso deseje usar o Eclipse (que
também suporta Groovy via plugins) ou outra IDE para testar os códigos deste artigo, ou mesmo um
editor de textos simples como o GEdit do Gnome ou o Notepad do Windows, você deverá configurar o
ambiente. Para isso recomendo que acesse a página do projeto na internet. Porém, caso ainda não use o
NetBeans, dê uma chance a ele. Essa é a oportunidade de conhecer duas ferramentas novas e de alta
qualidade ao mesmo tempo.

Alô Mundo

Vamos começar nossa viagem pelo admirável mundo novo do Groovy com um exemplo extremamente
simples. Sou fã do tradicional Alô mundo, apesar de ser um exemplo simples para iniciar numa
linguagem. No nosso caso ele será suficiente para apresentar muitos recursos, como attribute accessors,
como usar Groovy dentro do Java e muito mais.

Abra o NetBeans (ou o ambiente de sua escolha) e crie uma nova aplicação Java. Você verá uma
categoria Groovy na janela de Novo Projeto, mas ela tem apenas um template para a criação de um
projeto Grails. Como este não é o escopo deste artigo vamos ignorar essa categoria de projetos. Para isso
use a categoria Java, como na Figura 1. Clique no botão Próximo e então informe um nome para seu
aplicativo (eu usei o singelo nome “AloMundoGroovy”) e selecione onde o deseja salvar. Clique em
Finalizar e seu aplicativo será criado. Até agora não colocamos nem um pouco de Groovy no projeto, ele
é um tradicional e simples projeto de aplicação Java que você já conhece.
Vamos adicionar então o Groovy ao projeto. Para isso clique com o botão de atalho do mouse sobre o
projeto e selecione o menu Propriedades. Na janela que será aberta selecione o item Groovy na lista de
categorias e marque a opção Ativar Groovy, como na Figura 2.

Figura 1: Criação de um novo projeto Java.

Figura 2: Adicionando Groovy ao projeto


Agora nosso projeto já possui Groovy em sua estrutura. Perceba, na Figura 3, que, no grupo Categorias,
no navegador do projeto, foi adicionado o groovy-all.jar, que é quem faz a “mágica” para nós.
Figura 3: Nosso projeto agora tem Groovy.

Por padrão o NetBeans criará um arquivo .java no nosso projeto. Remova-o pois não o usaremos, e então
crie um pacote chamado org.groovy.maonamassa para adicionarmos os códigos-fonte de nosso projeto.
Dentro desse pacote adicione um novo JFrame e, na janela de design do NetBeans, adicione uma caixa
de texto ao JFrame, como na Figura 4.

Figura 4: Um JFrame com uma caixa de texto dentro.

Agora vamos adicionar um arquivo .groovy em nosso projeto. Para isso clique sobre o pacote criado e
clique com o botão de atalho do mouse. Selecione então a opção Novo>Outro, e na janela que será
aberta, na categoria Groovy, selecione Classe do Groovy. Nomeie-a como considerar mais interessante,
sem a necessidade de ser o nome da classe principal, pois Groovy não faz essa diferenciação como o
Java.

Vamos adicionar o código à nossa classe Groovy. Para isso, digite o código da Listagem 1.

Listagem 1. Nossa classe Groovy

package org.groovy.maonamassa

class AloMundoGroovy {
def saudacao = "Alô mundo, em Groovy !!!!"
}

Perceba que não usamos ponto-e-vírgula no final de nossa declaração, e que usamos a palavra reservada
def na criação de nosso objeto (sim, objeto, da classe Object). O restante deve ser conhecido para
programadores Java. Nossa classe Groovy está pronta para uso. Basta editarmos o código de nosso
JFrame para colocar tudo em funcionamento. O código do JFrame deverá ficar como apresentado na
Listagem 2.

Listagem 2. O código de nosso JFrame

package org.groovy.maonamassa;

public class MostrarJFrame extends javax.swing.JFrame


{
AloMundoGroovy meuAloMundo =
new AloMundoGroovy();

/** Creates new form MostrarJFrame */


public MostrarJFrame() {
initComponents();
String aloMundo = meuAloMundo.
getSaudacao().toString();
jTextField1.setText(aloMundo);
}

/** This method is called from within the


constructor to
* initialize the form.
* WARNING: Do NOT modify this code.
The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
//
private void initComponents() {

jTextField1 =
new javax.swing.JTextField();

setDefaultCloseOperation(javax.swing.
WindowConstants.EXIT_ON_CLOSE);
getContentPane().add(jTextField1,
java.awt.BorderLayout.CENTER);
pack();
}//

/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater
(new Runnable() {
public void run() {
new MostrarJFrame().setVisible(true);
}
});
}

// Variables declaration -do not modify


private javax.swing.JTextField jTextField1;
// End of variables declaration

Repare na linha em negrito, é nela que chamamos o método getSaudacao() de nossa classe Groovy. Se
você olhou o código da Listagem 1 e não achou esse método não se preocupe, ele é criado através de um
recurso comum no Groovy e no Ruby, que é chamado attribute accessor. Esse recurso cria os getters e
setters para propriedades de uma maneira simples e eficaz, sem excesso de código. Isso acontecerá
sempre que você definir um membro de sua classe sem especificar a visibilidade (protected, public ou
private): o Groovy o considera como uma propriedade e define automaticamente os getters e setters.

A mesma classe da Listagem 1, escrita em Java, teria o dobro do tamanho. Veja na Listagem 3.

Listagem 3. Nossa classe AloMundoGroovy em Java - um tanto quanto “


prolixo”
, não acha?
package org.groovy.maonamassa;

public class AloMundoJava {

String saudacao = new String();

public String getSaudacao() {

return saudacao;
}

public void setSaudacao(String saudacao) {

this.saudacao = saudacao;

}
}

Note que parte de nosso projeto foi feito em Java (para tirar proveito do editor visual do IDE), mas uma
parte foi feita em Groovy (e isso deixou as coisas mais simples - uma linha ao invés de sete), e ambos
funcionam bem juntos, como é possível ver na Figura 5.

Figura 5: Nosso projeto em execução.

O leitor mais atento deve ter reparado no .toString() junto ao nosso método getSaudacao(), mostrado na
Listagem 2, o que mostra que tanto o getter como o setter de saudacao trabalham com um Object.

Isso se deve ao fato de que não definimos o tipo de nosso objeto no momento da sua criação com o def.
Listagem 4 e remova o .toString() da Listagem 2. Verifique que agora ambos os métodos getter e setter
trabalham com String e não com Object. É possível ser menos verboso ainda e deixar de utilizar o def,
pois ele pode ser omitido quando especificamos o tipo do objeto.

Listagem 4. Nossa classe Groovy modificada

package org.groovy.maonamassa
class AloMundoGroovy {
// aqui você pode omitir o def
def String saudacao = "Alô mundo,
em Groovy !!!!"
}
GDK

Assim como o Java possui a JDK, o Groovy possui a GDK. É fácil confundir-se quando pensamos nisso
pois todo objeto Java pode ser usado dentro do Groovy, mas o contrario não é verdade. A GDK estende a
JDK adicionando métodos que não existem originalmente nos objetos Java.

Um exemplo seria como obter o tamanho de uma determinada instancia de uma classe.

Em Java isso é bem confuso pois existem objetos que usam o método length() (como os que são
instancias de arrays e Strings), outros usam o método size() (como os que são instancias de Collections e
Maps). Temos até um getLength()(para java.lang.reflect.Array) e o método groupCount(), para instancias
da classe Matcher.

Groovy, através de um dispositivo chamado MetaClass, possibilita que todos esses objetos utilizem um
mesmo método para obter o tamanho de um objeto: o size(). Isso possibilita termos o código da
Listagem 5, onde temos tanto um ArrayList quanto uma String usando o mesmo método, o size().

Listagem 5. O poder de fogo do GDK

class TestaSize {

public def imprime() {

String teste = "isso é um teste"

ArrayList meuArray = new ArrayList()

meuArray.add(teste)

println teste.size()

println meuArray.size()

Outra vantagem do GDK sobre a biblioteca padrão do Java é que Groovy não diferencia entre tipos
primitivos e objetos. Em Groovy a expressão valor01 + valor02, considerando valor01 e valor02 do tipo
java.lang.Integer, retorna a soma dos dois inteiros. Mas o Groovy vai além, definindo uma equivalência
entre operadores e métodos: no caso, a expressão acima será interpretada como valor01.plus(valor02).

Se você quiser criar novas classes com suporte a operadores, basta definir nela o método que será
chamado para cada operação (para + o método é o plus(), para - o método é o minus(), para ++ é
next() e por aí vai - consulte a documentação do Groovy para a lista de métodos e operações
correspondentes).

Pense na utilidade prática disso. Quantas vezes você já não teve uma classe em que seria conveniente
usar o operador + para somar uma instância com outra e teve que apelar para métodos mirabolantes
como somaClasse() ou append()? Em Groovy basta definir o método correto e, ao avaliar a operação, o
Groovy procura o método correspondente.

Isso só é possível por que o Groovy usa o conceito de MetaClass apresentado acima. Quando o
compilador encontra uma expressão como valor01 + valor02 num código Groovy, ele gera um bytecode
diferente do que gera quando encontra essa mesma expressão num arquivo de código Java, pois
chamadas de métodos são redirecionadas pelo MetaClass do objeto, o que possibilita ao Groovy
interceptar, adicionar, redirecionar e remover código em tempo de execução. É esse truque que possibilita
valor01 + valor02 tornar-se valor01.plus(valor02) em tempo de execução.

Claro que todo esse poder tem um preço: um código Groovy é ligeiramente mais lento do que um código
Java durante a execução, mas nada que torne proibitivo seu uso. Fazendo uma analogia, num
comparativo com o desenvolvimento para sistemas operacionais, a JVM seria nosso sistema operacional,
e ao desenvolver optaríamos por Java quando precisamos de velocidade, e por Groovy quando
precisamos de facilidades e performance não é um dos requisitos principais. Pode parecer, com todo esse
parágrafo, que Groovy é um elefante pesado e lento, mas Groovy é apenas um pouco mais lento que
Java.

Na Listagem 6 é possível encontrar um exemplo de como sobrescrever o operador + através do uso do


método plus().

Criei uma classe chamada SomaODobro que, quando tem duas instâncias somadas, realiza a adição e
depois a multiplicação para retornar o resultado. Veja o código.

Listagem 6. Usando o plus para aumentar o poder do +

package org.groovy.maonamassa

class SomaODobro {

def Integer valorInicial

public def plus(SomaODobro outro) {


return (valorInicial +
outro.valorInicial) * 2
}

class AloMundoGroovy {

public def testa() {


SomaODobro valor01 = new SomaODobro()
SomaODobro valor02 = new SomaODobro()

valor01.valorInicial = 10
valor02.valorInicial = 20

println valor01 + valor02


}
}

É importante ressaltar que sim, é possível, em Groovy, ter mais de uma classe dentro de um arquivo de
código fonte como visto acima. As classes podem, inclusive, ser públicas, ao contrário do Java, onde
podemos ter apenas uma classe pública por arquivo. Veja nas Listagens 7 e 8 um exemplo simples.

Listagem 7. Um arquivo Groovy com duas classes públicas

package groovytest

public class Carro {

public def imprime() {

println "Sou um carro"

public class Moto {

public def imprime() {

println "Sou uma moto"

}
}

Listagem 8. Instanciando as classes Groovy

package groovytest;

public class Main {

public static void main(String[] args) {

Carro meuCarro = new Carro();

Moto minhaMoto = new Moto();

meuCarro.imprime();

minhaMoto.imprime();

Closures

Um dos recursos mais úteis e apaixonantes de linguagens dinâmicas são os closures, ou fechamentos, ou
blocos, que dão um poder de fogo enorme no desenvolvimento. Closures nada mais são do que pedaços
de código tratados como objetos, e como tal podem receber parâmetros e retornar valores. E como a JVM
não sabe se o código que está rodando é Groovy ou Java, é perfeitamente possível dizer que um closure
é apenas mais objeto para a JVM, tal qual uma String ou um Integer. Uma aplicação robusta e poderosa
de closure é na classe File da GDK. O Groovy estende o objeto File padrão do Java e adiciona um método
eachLine(), que recebe como parâmetro um closure:

new File(“arquivo.txt”).eachLine { println it }

O closure nada mais é do que o bloco { println linha }, que será executado para cada iteração de
eachLine(), ou seja, essa única linha abrirá um arquivo de texto chamado arquivo.txt, lerá linha por linha
até o final e passará cada linha ao closure, que imprimirá o que recebeu na saída padrão usando o
println() (o it representa a linha recebida). Tudo isso em apenas uma linha de código.

Outro exemplo bem interessante é apresentado a seguir, onde temos um tipo de dado do Groovy (o
Range, que é um intervalo representado com dois números separados por dois pontos simples, como em
1..100, que inclui os números de 1 a 100); e o método each(), que varre cada membro do Range, e
depois executa a closure que o imprime.

(1..10).each { println it }

Ok, mas o que é esse tal de it no meu código? Ele é uma variável conhecida como “Magic variable”, ou “
variável mágica”, algo que não precisa ser declarado pra existir. it pode ser visto como um membro da
classe que define os closures do Groovy, é ele quem recebe o parâmetro padrão quando esse não é
declarado. Veja como fica a opção de uso de uma variável explícita no lugar da variável mágica.

(1..10).each { valorAtual -> println valorAtual }

É o sinal de -> que separa a declaração de variáveis do corpo do closure. Mas closures não são úteis
apenas para imprimir um valor na saída padrão, eles podem ser usados para operações complexas, sua
imaginação é o limite.

O poder dos closures

Vamos criar um exemplo pra demonstrar um pouco do poder e da elegância do uso de closures. Crie um
novo projeto Java e adicione Groovy a ele como visto anteriormente. Adicione uma nova classe Groovy
chamada Aluno e então insira o código da Listagem 9.

Listagem 9. A classe Aluno.groovy


package groovy2

class Aluno {

// definimos as propriedades

String nomeAluno

Integer matriculaAluno

// variavel para a matriculo

static int matricula = 0

// Construtor

Aluno(String nome) {

matricula = matricula + 1

this.setNomeAluno(nome)

this.setMatriculaAluno(matricula)

public String toString() {

return "Matricula: " +


this.getMatriculaAluno().toString() +

" Nome: " + this.getNomeAluno()

Nessa classe temos duas propriedades, o nome do aluno e o número de sua matrícula. O nome deverá
ser informado no momento da criação do objeto, através de nosso construtor e o número da matrícula é
sugerido através do uso de um membro estático que é incrementado a cada nova instancia criada
(perceba que esse recurso não deve ser usado em situações reais, mas encaixa como uma luva nesse
exemplo).

Redefinimos também o método toString(), para possibilitar uma impressão simplificada de nossos
valores, quando necessário. Perceba que faço uso dos getters e setters sem os declarar pois, como foi
visto antes, eles são criados automaticamente quando definimos membros de classe sem especificar sua
visibilidade.

Na Listagem 10 instanciamos, em um arquivo main.java três objetos de nossa classe e imprimimos seus
dados, usando o toString().

Listagem 10. A classe Main.java, instanciando três objetos aluno

package groovy2;

public class Main {

public static void main(String[] args) {


Aluno aluno01 = new Aluno("Marcelo");

Aluno aluno02 = new Aluno("Giuliana");

Aluno aluno03 = new Aluno("Ana Beatriz");

System.out.println(aluno01.toString());

System.out.println(aluno02.toString());

System.out.println(aluno03.toString());

O resultado será algo como:

Matricula: 1 Nome: Marcelo


Matricula: 2 Nome: Giuliana
Matricula: 3 Nome: Ana Beatriz

Vamos agora criar, na Listagem 11, uma nova classe Groovy chamada Aula. Vamos utilizá-la para agrupar
nossos alunos em aulas.

Listagem 11. A classe Aula.groovy

package groovy2

class Aula {

ArrayList alunos = new ArrayList()

def adicionaAluno(Aluno nomeAluno) {

alunos.add(nomeAluno)

def removeAluno(Aluno nomeAluno) {

alunos.remove(nomeAluno)

def imprimeListaAlunos() {

alunos.each() { alunoAtual ->


println alunoAtual.toString() }

Modifique o arquivo Main.java para que fique como na Listagem 12.

Listagem 12. A classe Main.java, modificada

package groovy2;

public class Main {

public static void main(String[] args) {


Aluno aluno01 = new Aluno("Marcelo");

Aluno aluno02 = new Aluno("Giuliana");

Aluno aluno03 = new Aluno("Ana Beatriz");

Aula historia = new Aula();

historia.adicionaAluno(aluno01);

historia.adicionaAluno(aluno02);

historia.adicionaAluno(aluno03);

historia.imprimeListaAlunos();

Ao chamarmos o método imprimeListaAlunos() faremos uso de um closure para percorrer todos os


membros do ArrayList da classe Aula (através do each()). Agora faça um teste e escreva as classes Aluno
e Aula em Java e veja quantas linha a mais de código seriam necessárias para o mesmo resultado.

A maior parte das linguagens e recursos que se propõe a enxugar o código o faz às custas de clareza.
Isso não acontece em Groovy: o código continua perfeitamente legível, mesmo com linhas a menos. Em
alguns casos, como no exemplo acima, é até mais fácil ler um código Groovy do que um código Java.

Para onde ir agora?

Groovy é extremamente poderosa e pode lhe ajudar muito em seus projetos Java. Ela é particularmente
útil para processamento de texto graças a um poderoso suporte a expressões regulares, para
processamento de listas e de arquivos graças ao uso de closures e principalmente para o processamento
de XML, onde o Java é deveras confuso e prolixo.

Além disso Groovy é a base do Grails, framework de produtividade para o desenvolvimento de aplicações
web que une a poderosa infra-estrutura do Spring e Hibernate à simplicidade das idéias do Ruby on Rails.
Grails possibilita o desenvolvimento de aplicações complexas de maneira rápida e indolor, gerando uma
aplicação web totalmente compatível com Java EE em um arquivo .war.

Para aprender mais sobre Groovy dê uma olhada na página do projeto na web. Existem dúzias de
exemplos e informação relevante para que você torne-se produtivo rapidamente.

Além disso, é interessante dar uma olhada no livro Groovy em ação, escrito por Dierk König junto a
Andrew Glover, Paul King, Guillaume Laforge (o mantenedor do projeto) e Jon Skeet, publicado pela
editora AltaBooks. (O original, Groovy in action, é publicado pela Manning, caso inglês não seja um
problema). Groovy é simples e poderoso e merece sua atenção. O tempo que irá ganhar quando começar
a usá-lo compensará e muito seu aprendizado.

Links
http://radio.weblogs.com/0112098/2003/08/29.html
Post de James Strachan, onde surgiu a idéia sobre o Groovy

http://www.springsource.com/node/836
Página da SpringSource comentando a compra da G2One

http://www.netbeans.org
Site do NetBeans, a única IDE que você precisa

http://grails.codehaus.org
Site do Grails, framework para aplicações web que usa o Groovy como base

http://groovy.codehaus.org
Site do Groovy

Saiba Mais
www.devmedia.com.br/articles/viewcomp.asp?comp=11412
Auditório Virtual DevMedia - Groovy na web com Struts 2

www.devmedia.com.br/cursos/listcurso.asp?curso=55
Curso Online - Introdução ao Groovy

www.devmedia.com.br/articles/viewcomp.asp?comp=8982
Java Magazine 32 - Groovy: Java Através de Scripts
JBoss Application Server 5

João Paulo Viragine


é bacharel em Ciência da Computação pela Unesp. Trabalha como Arquiteto de Soluções na JBoss, a
division of Red Hat, onde uma de suas atribuições é ser o evangelista oficial de Seam no Brasil. Acumula
cerca de 7 anos de experiência na tecnologia Java, tendo atuado em projetos da área de Governo e
Finanças. Possui as certificações: SCJP e SCWCD.

Bruno Rosseto
É bacharel em Sistemas de Informacão pela Universidade Mackenzie e atua com desenvolvimento de
software com a plataforma Java há quatro anos. Possui experiênciaem projetos Java EE nas áreas
automotivas, e-commerce,e outros projetos de missão crítica. Participou do projeto ganhador do Duke’
s
Choice Awards de 2005 - SIGA Saúde. Atualmente é consultor da Summa Technologies do Brasil.

De que se trata o artigo:


Após três anos de desenvolvimento, o JBoss Application Server 5 está pronto e traz uma arquitetura
totalmente redesenhada. Este artigo introduzirá a nova arquitetura baseada no JBoss Microcontainer,
suas vantagens e principais diferenças em relação à sua antecessora baseada no JBoss Microkernel. Além
disso, detalhará as principais novidades em mensageria, clustering, balanceamento de carga, cache,
transações e monitoração.

Para que serve:


O JBoss Application Server 5 serve para disponibilizar os recursos necessários à execução de aplicações
Java EE 5. Sua arquitetura flexível permite total controle, customização e tuning, conforme as
necessidades do desenvolvedor. Além disso, sua instalação é simples e rápida.

Em que situação o tema é útil:


O artigo atualiza o leitor em relação às mudanças mais expressivas da nova versão do JBoss Application
Server, o servidor de aplicações mais utilizado do mundo.
JBoss Application Server 5:

O JBoss Application Server 5 é resultado de três anos de pesquisas e desenvolvimento, que culminou em
um total redesenho da arquitetura interna do servidor de aplicações.

Além da compatibilidade total à especificação Java EE 5 e do suporte ao JDK 6, a principal característica


dessa nova versão é a substituição do JBoss Microkernel pelo JBoss Microcontainer. Com o JBoss
Microcontainer, é possível desenvolver serviços baseados em POJOs, não sendo mais necessário
implementar MBeans. Com isso, a integração de novos módulos torna-se ainda mais dinâmica e rápida, o
que facilita customizar, excluir ou acoplar novos serviços. É possível também integrar componentes
baseados nos antigos MBeans, módulos OSGi, entre outros.

Houve também mudanças significativas no mecanismo de classloader para a utilização do Virtual


Deployment Framework (VDF), o qual garante que toda dependência do deploy seja satisfeita antes da
disponibilização do serviço. Os principais módulos também sofreram evoluções, como: clustering,
mensageria, cache, binding de portas, etc.

A interface gráfica do novo JBossAS também foi atualizada, e foi criado o projeto Jopr - uma interface
web para monitoração e controle de toda a infraestrutura JBoss, o que possibilita, entre outras coisas: a
criação de novos datasources, deploy de pacotes, monitoração de pools de conexão, mantendo um
histórico de ações e gráficos para futuras consultas. Dessa forma, ficou muito mais fácil cuidar dos seus
JBosses.

O lançamento do JBoss AS 5 é apenas o começo de uma nova era para os projetos JBoss, uma vez que
outras soluções, como a Plataforma SOA, Portais, etc., poderão usufruir desse robusto e performático
servidor de aplicações.

A versão 5 do JBoss Application Server, que a partir desse momento chamaremos simplesmente AS5, foi
resultado de uma maratona de três anos de pesquisas e desenvolvimento que culminou em um total
redesenho da arquitetura interna do servidor de aplicações.

Essa versão marca o início de uma nova era para o servidor de aplicações mais popular, querido e
utilizado do mundo. Não se trata apenas de uma nova versão do servidor de aplicações, mas de todo um
ambiente em estado da arte para execução da próxima geração de projetos desenvolvidos pela JBoss.

Sobre a JBoss

JBoss, apesar de ser sinônimo de servidor de aplicações, não está restrito apenas a isso. A comunidade
JBoss (www.jboss.org) possui hoje mais de 35 projetos. Entre eles, podemos destacar:

Hibernate - o framework de persistência ORM (Object Relational Mapping) mais utilizado do mundo e que
muito influenciou a especificação de EJB 3.0.

JBoss Seam - o framework que combina o que há de melhor em desenvolvimento Web 2.0 com o novo
modelo de componentes de negócio EJB 3.0, aumentando a produtividade e disponibilizando inúmeros
componentes para facilitar e acelerar o desenvolvimento de aplicações corporativas em Java. O
reconhecimento do poder do JBoss Seam por parte da comunidade deu origem à JSR 299 - Web Beans.
Ver Edição 58.

Além de influenciar o futuro da plataforma Java EE, seja com participações nas JSRs, seja com a
implementação de referência de JSRs, a JBoss conta hoje com a participação de brasileiros, funcionários
da Red Hat Brasil, como principais desenvolvedores em diversos projetos da comunidade JBoss,
dedicando-se em tempo integral a atividades de desenvolvimento e suporte a clientes. Dentre esses
projetos, podemos destacar:

o próprio JBoss AS, o JBoss Rules - motor de regras e BRMS (business rule management system),
JBoss AOP - framework para AOP (programação orientada a aspectos), JBoss SX (framework de
segurança da JBoss), JBoss Profiler (ferramenta de profiling baseada em log), JBoss Messaging (JMS
provider), JBoss ESB (para integração SOA, ver Edição 59) e JBoss Portal (solução para portais
corporativos).

Mais detalhes sobre esses e outros projetos JBoss (como o JBoss jBPM, JBoss Tools, Teiid) podem ser
encontrados no site: www.jboss.org.

Apesar de os projetos JBoss serem conduzidos, em sua maior parte, por funcionários da Red Hat/JBoss, é
inegável a contribuição da comunidade durante todos esses anos de existência da jboss.org. Essa
contribuição é de extrema importância para a sobrevivência e qualidade dos projetos.

A contribuição não é feita apenas com desenvolvimento de código fonte, mas também com participação
em fóruns de discussão, relato de bugs, pedido de novas funcionalidades, elaboração/tradução de
documentação, blogs pessoais, eventos organizados pela comunidade, etc.

Veja os links de referência no final do artigo para saber mais informações sobre como contribuir com a
comunidade JBoss

Novidades

Uma das novidades da versão 5 do JBoss AS é a compatibilidade total à especificação Java EE 5.


Apesar de a versão 4.x já suportar grande parte da especificação Java EE 5 (EJB 3.0, JPA, JAX-WS, etc.)
e ser um dos primeiros servidores de aplicações a suportar a especificação de EJB 3.0, o suporte à Java
EE 5 não era completo nem certificado. A certificação era uma característica bastante requisitada por
clientes corporativos da Red Hat, principalmente em relação à garantia de compatibilidade das aplicações
desenvolvidas.

O suporte ao JDK 6 é também uma novidade dessa versão. Apesar de suportar o Java 6 desde a versão
4.2, é na versão 5 que esse suporte foi aprimorado e tornou-se padrão para execução do servidor de
aplicações.

Além da certificação Java EE 5 e do suporte ao Java 6, o destaque dessa versão, sem dúvida nenhuma,
fica a cargo do JBoss Microcontainer. O AS5 faz parte de uma nova geração do servidor de aplicações,
construído com base no novo JBoss Microcontainer.

O JBoss Microcontainer é resultado de uma completa reescrita do JBoss JMX Microkernel (utilizado nas
versões das séries 3.x e 4.x do JBoss AS) e o substitui completamente para suportar a utilização direta
de POJOs e o uso como um projeto independente do servidor de aplicações JBoss, seguindo a tendência e
evolução do desenvolvimento Java EE com a utilização de novos paradigmas como AOP, injeção de
dependência e inversão de controle, e o foco na utilização de POJOs (como EJB 3.0, JPA, Spring, Guice,
entre outros).

O AS5 utiliza o JBoss Microcontainer para fazer a integração dos serviços disponibilizados pelo servidor
de aplicações, entre eles: container Servlet/JSP; container EJB; gerenciador de deploy, entre outros,
disponibilizando, assim, um ambiente Java EE padrão. O JBoss AS não é um servidor de aplicações
monolítico - com um único kernel fornecendo os serviços requeridos pela especificação Java EE - mas
sim, uma coleção de componentes independentes e interconectados, cada um deles com foco em uma
funcionalidade específica requerida pela especificação Java EE. Essa arquitetura flexível possibilita que
novos serviços possam ser facilmente adicionados e os serviços desnecessários possam ser removidos.
Se houver necessidade de um serviço adicional, simplesmente fazemos o deploy do serviço desejado. De
maneira análoga, se não precisamos de um serviço, podemos simplesmente removê-lo. A Figura 1
mostra uma visão geral de como os serviços são associados e disponibilizados pelo Microcontainer. Como
resultado, temos um servidor de aplicações totalmente customizado às nossas necessidades, com o uso
eficaz de recursos do servidor físico (CPU, memória, disco).

A flexibilidade é tamanha, que os serviços construídos com base no JBoss Microcontainer podem ser
utilizados de maneira independente em outros ambientes/servidores de aplicações não JBoss, como o
GlassFish, ou até mesmo o Tomcat. Como exemplo, podemos citar o suporte total ao EJB 3.0 no Tomcat
(bastando para isso, fazermos o deploy do JBoss EJB 3.0 container no Tomcat).

Como o JBoss Microcontainer é extremamente leve, pode ser utilizado para disponibilizar serviços até
mesmo em um ambiente Java ME. Desse modo, abre-se um novo horizonte de possibilidades para
aplicações móveis que passam a usufruir dos serviços “enterprise”, sem a necessidade de utilização de
um ambiente/servidor Java EE completo. O JBoss Microcontainer utiliza o conceito de injeção de
dependências (IoD) para interconectar e disponibilizar os serviços do servidor de aplicações. Além disso,
pode ser utilizado como container de injeção de dependências de propósito geral ao estilo Pico Container
e Spring. Como amplamente consolidada no Java 5, toda configuração do JBoss Microcontainer pode ser
realizada através de anotações ou XML, dependendo do local onde a informação de configuração está
localizada (classes Java ou arquivos de configuração).

Além de tudo isso, o JBoss Microcontainer possui classes de testes utilitárias que estendem o JUnit e
tornam a configuração e a execução de testes unitários uma tarefa extremamente simples, permitindo
que o desenvolvedor acesse POJOs e serviços nas classes de testes com apenas algumas linhas de
código.

Todo o mecanismo de classloader do JBoss AS também foi modificado para utilizar o avançado conceito
do Virtual Deployment Framework (VDF). O VDF foi desenvolvido para abstrair, simplificar e unificar a
manipulação de arquivos pelo servidor de aplicações.

Denominado Virtual File System (VFS) Class Loader, esse novo mecanismo de classloader, além do uso do
VDF para localizar bibliotecas e classes, faz uso extensivo de AOP para “aspectizar” o deploy de
aplicações/serviços no servidor de aplicações.

O VFS faz a análise dos deploys produzindo meta-informações que serão utilizadas pelo JBoss
Microcontainer para instanciar, interconectar e controlar o ciclo de vida e dependência dos deploys. O
JBoss Microcontainer utiliza uma máquina de estados para garantir que toda e qualquer dependência do
deploy seja satisfeita antes de disponibilizar o serviço.

Modularização dos serviços - Maior possibilidade de escolha e flexibilidade

Um grande avanço do AS5 foi a modularização dos serviços internos do servidor de aplicações em
projetos independentes.

Essa modularização não traz impactos diretos para o usuário final, mas faz parte de uma importante
estratégia da JBoss em disponibilizar os vários serviços Java EE como projetos independentes. Assim,
esses serviços podem ser consumidos a la carte em diferentes ambientes, e não apenas no próprio
servidor de aplicações, o que permite grande flexibilidade e liberdade de escolha. Remover serviços é tão
simples quanto adicionar novos.

Muitas das principais funcionalidades do AS5 são providas da integração de vários outros projetos JBoss,
entre eles: JBoss EJB 3.0 - Implementação da última versão da especificação EJB 3.0 - A especificação
de EJB 3.0 faz parte de uma total reestruturação da especificação EJB. E tem por objetivo a simplificação
do desenvolvimento;

JBoss Messaging - Reescrita completa do antigo JBossMQ (que é o provedor JMS padrão no JBoss AS das
séries 4.x).

É uma implementação de JMS de alta performance compatível com a JSR-914, com suporte out-of-the-
box a cluster de filas e tópicos, com tolerância a falhas transparente e um sistema de redistribuição de
mensagens inteligente. É hoje o provedor de mensageria padrão do AS5, além de ser parte integrante da
infraestrutura do JBoss ESB;

JBoss Cache - É a implementação de cache utilizada no AS5. É utilizado principalmente em conjunto com
o JGroups para fornecer uma solução completa de cluster. Entre as diversas características está o buddy
replication: permite que o cache seja replicado apenas para um “buddy” (companheiro) no cluster, evita a
sobrecarga de outros nós no cluster sem necessidade, realiza uma espécie de backup do estado do nó;

JBoss WS - Implementação da pilha de web services compatível com a especificação JAX-WS 2.0/JAX-
RPC.

Além de implementar toda a especificação de Web Services, foi criada uma camada de abstração que
possibilita que se pluguem outras implementações. Por exemplo: podemos utilizar a implementação
nativa do JBoss WS, o Metro (implementação de web services da Sun), ou ainda o CXF (implementação
da Apache), sem qualquer tipo de impacto sobre o JBoss AS. Assim, o usuário tem total liberdade para
escolher a implementação que melhor se adapta às suas necessidades.

No final do mês de março, foi anunciado que os esforços serão focados em uma única implementação:
JBossWS-CXF, ou seja, as próximas versões do JBoss AS virão com o CXF nativo. Aqueles que usam o
JBoss WS nativo não precisam se preocupar, pois a transição será realizada gradativamente e trará
grandes benefícios, principalmente no que diz respeito a soluções SOA;

JBoss Transactions - é o gerenciador de transações padrão no AS5. O JBoss Transactions foi adquirido em
2005 da Arjuna/Hewlett-Packard - um gerenciador de transações extremamente rápido e robusto.
Resultado de mais de 18 anos de experiência dos seus criadores em gerenciamento de transações, foi a
primeira implementação de JTA e JTS do mercado;

JBoss Web - É o container Web/Servlet do AS5, comumente conhecido como "Tomcat on stereoids". Tem
sua implementação baseada no Apache Tomcat 6.0 e inclui suporte a conectores baseados no Apache
Portable Runtime (APR) para alcançar maior performance e escalabilidade, podendo, em alguns casos,
equiparar-se ao Apache HTTP Server ou até superá-lo;

JBoss Security - Além do suporte a JAAS, foi atualizado para suportar mecanismos de autorização
plugáveis: SAML, XACML e SSO.

A Tabela 1 compara os principais serviços utilizados no AS5 e no seu sucessor (JBossAS 4.2.3), para
facilitar a visualização da evolução de versões entre os dois.

Tabela 1: Matriz de comparação de versão entre o JBossAS 4.2.3 e o JBossAS 5

O AS5 suporta nativamente POJOs, MBeans e OSGi bundles (com ajuda do Apache Felix). Suportar um
novo modelo de componentes é tão simples quanto implementar uma nova fachada para o JBoss
Microcontainer. Isso permite que o JBoss AS suporte qualquer outro modelo de componentes existente ou
que ainda está por vir; já está preparado, portanto, para o sistema de módulos que será implementado
no Java 7.

Novidades em Cluster

Uma das melhorias no AS5 foi a criação de um novo mecanismo de integração entre o JBoss Cache e o
Hibernate/JPA para a utilização de Second Cache Level (ou cache L2, introduzido no Hibernate 3.3).
Existem basicamente quatro tipos de objetos que podem participar de um Second Cache Level:
entidades, collections, resultados de query e timestamps. Nas versões 4.x, apenas um único cache podia
ser utilizado para armazenar todos os tipos de objetos, causando dificuldades na consulta. Com o AS5
existe um cache diferenciado para cada tipo de objeto, evitando assim sobrecarga e lentidão na busca de
objetos na árvore de cache. Essa versão também permite buddy replication de Stateful Session Beans.
Com buddy replication, é possível diminuir a carga de memória e o tráfego na rede. A Figura 2 mostra
uma arquitetura com seis nós, onde cada nó possui os seus dados e um cache do nó anterior.

Quando ocorre queda de um dos nós, automaticamente o nó que possuía o cache do nó em queda
assume o comando, guardando também o cache do nó anterior, fechando o círculo. A Figura 3 mostra a
queda do servidor nó 1 e o servidor nó 2 assumindo o cache do nó 1 (em queda) e 6.

Nas versões 4.2.x, buddy replication era configurada no arquivo $JBOSS_HOME/server/all/deploy/jboss-


web-cluster.sar/META-INF/jboss-service.xml (apenas camada web). Já o AS5 centraliza toda a
configuração de cache no serviço chamado Cache Manager, que está localizado em
$JBOSS_HOME/server/all/deploy/cluster/jboss-cache-manager.sar.

Com a utilização do JBoss Messaging em vez do JBossMQ, a clusterização do provider de mensageria


também mudou. O antigo JBossMQ era executado em modo HA-Singleton, ou seja, apenas uma instância
de JBoss executava o provider de mensageria por vez. Com o JBoss Messaging, é possível ter todos os
nós ativos para o serviço de mensageria, proporcionando um balanceamento de carga entre os diversos
nós, caso ocorra uma sobrecarga no servidor. Para que as filas sejam consideradas “clusterizadas”, é
necessário adicionar o atributo Clustered com valor True na declaração da Queue ou Topic, conforme
mostra a Listagem 1.

Listagem 1.

$JBOSS_HOME/server/all/deploy/cluster/jboss-cache-manager.sar
<mbean code="org.jboss.jms.server.destination.
QueueService"
name="jboss.messaging.destination:service=Queue,
name=testDistributedQueue"
xmbean-dd="xmdesc/Queue-xmbean.xml">
<depends optional-attribute-name=
"ServerPeer">jboss.messaging:service=
ServerPeer</depends>
<depends>jboss.messaging:service=PostOffice
</depends>
<attribute name="Clustered">true</attribute>
</mbean>

A configuração do serviço de portas também mudou. Com a utilização de POJOs e AOP no Microcontainer,
as portas agora são injetadas conforme a necessidade da configuração.

O serviço ServiceBindingManager é o responsável por fazer a associação entre as portas e cada um dos
serviços, por exemplo, HTTP na porta 8080, AJP na porta 8009, etc. Na versão 5.0, ocorre uma chamada
ao arquivo bootstrap.xml (Listagem 2), que é o responsável por carregar algumas configurações em
tempo de inicialização. O bootstrap.xml está configurado para utilizar o arquivo de configuração de portas
bindings.xml, o qual já possui algumas configurações pré-definidas que podem ser utilizadas e
customizadas conforme a necessidade do usuário.

Listagem 2.

$JBOSS_HOME/server/$PROFILE/conf/bootstrap.xml
<bootstrap xmlns="urn:jboss:bootstrap:1.0">
<url>bootstrap/vfs.xml</url>
<url>bootstrap/classloader.xml</url>
<url>bootstrap/aop.xml</url>
<url>bootstrap/jmx.xml</url>
<url>bootstrap/deployers.xml</url>
<url>bootstrap/bindings.xml</url>
<url>bootstrap/profile-repository.xml</url>
</bootstrap>

A Listagem 3 mostra a injeção das configurações de portas disponíveis. No caso, estão sendo injetadas as
configurações de PortsDefaultBindings, Ports01Bindings, Ports02Bindings e Ports03Bindings.

Listagem 3. Trecho do arquivo bindings.xml - definição de alias

<bean name="ServiceBindingStore"
class="org.jboss.services.binding.impl.
PojoServiceBindingStore">

<!-- Base bindings that are used to create


bindings for each set -->
<property name="standardBindings">
<inject bean="StandardBindings"/>
</property>

<!-- The sets of bindings -->


<property name="serviceBindingSets">
<set>
<inject bean="PortsDefaultBindings"/>
<inject bean="Ports01Bindings"/>
<inject bean="Ports02Bindings"/>
<inject bean="Ports03Bindings"/>
</set>
</property>
</bean>

A definição das configurações PortsDefaultBindings e Ports01Bindings é realizada com a injeção de


beans do tipo ServiceBindingSet (Listagens 4 e 5).

Listagem 4. Trecho do arquivo bindings.xml - definição de PortsDefaultBindings

<bean name="PortsDefaultBindings"
class="org.jboss.services.binding.
impl.ServiceBindingSet">
<constructor>
<!-- The name of the set -->
<parameter>ports-default</parameter>
<!-- Default host name -->
<parameter>${jboss.bind.address}</parameter>
<!-- The port offset -->
<parameter>0</parameter>
<!-- Set of bindings to which the
"offset by X" approach cant be applied -->
<parameter><null/></parameter>
</constructor>
</bean>

Listagem 5. Trecho do arquivo bindings.xml - definição de Ports01Bindings

<bean name="Ports01Bindings"
class="org.jboss.services.binding.impl.
ServiceBindingSet">
<constructor>
<!-- The name of the set -->
<parameter>ports-01</parameter>
<!-- Default host name -->
<parameter>${jboss.bind.address}</parameter>
<!-- The port offset -->
<parameter>100</parameter>
<!-- Set of bindings to which the
"offset by X" approach cant be applied -->
<parameter><null/></parameter>
</constructor>
</bean>

O trecho referente a port offset atua diretamente na configuração de StandardBindings (Listagem 6),
realizando uma somatória no valor de cada porta. No caso, o PortsDefaultBindings irá manter as
configurações conforme estão no arquivo, pois seu port offset está definido com valor 0, porém a
configuração Ports01Bindings irá somar 100 em cada uma das portas. Portanto, para o Ports01Bindings,
a porta 1099 será 1199, a porta 1098 será1198, a porta 8080 será 8180, e assim por diante.

Listagem 6. Trecho do arquivo bindings.xml - definição de StandardBindings - portas padrão

<bean name="StandardBindings"
class="java.util.HashSet"
elementClass="org.jboss.
services.binding.ServiceBindingMetadata">
<constructor>
<parameter>
<set>
<!-- *********************
conf/jboss-service.xml
****************** -->
<!-- Naming Service -->
<bean class="org.jboss.services.
binding.ServiceBindingMetadata">
<property name="serviceName">
jboss:service=Naming</property>
<property name="bindingName">
Port</property>
<property name="port">1099</property>
</bean>

<bean class="org.jboss.services.
binding.ServiceBindingMetadata">
<property name="serviceName">
jboss:service=Naming</property>
<property name="bindingName">
RmiPort</property>
<property name="port">1098</property>
</bean>
.
.
.

</set>
</parameter>
</constructor>
</bean>

$JBOSS_HOME é o diretório de instalação do JBoss AS5.

$PROFILE é o diretório referente ao profile/configuração, por exemplo: default, all, web, etc.

As portas padrão do JBoss Application Server são definidas no bean StandardBindings, o qual realiza a
associação de cada porta a cada serviço específico. A Listagem 6 mostra um trecho da declaração do
StandardBindings, em que o serviço Naming (JNDI) está sendo associado à porta 1099 e à porta RMI
1098.

Com a utilização do ServiceBindingManager, torna-se mais fácil a utilização de várias instâncias de JBoss
AS no mesmo servidor físico. Desse modo, a configuração de portas é mantida em um único lugar, o que
facilita a manutenção.

Injeção de POJOs utilizando o Microcontainer

Os arquivos que terminam com jboss-beans.xml são utilizados pelo Microcontainer para realizar a injeção
de POJOs. São semelhantes aos -service.xml que eram utilizados nas versões 4.x. A maioria dos serviços
do AS5 já foi convertida para POJOs.

Porém, ainda restam alguns MBeans das versões antigas que utilizam os -service.xml, os quais serão
substituídos em versões futuras.

Para facilitar o entendimento da injeção de POJOs realizada pelo Microcontainer, criamos a classe
PojoServiceExample (Listagem 7).

Listagem 7. Classe PojoServiceExample

package jm.exemplos.pojo;

public class PojoServiceExample {

public PojoServiceExample() {

System.out.println("Construtor de PojoServiceExample()");

É uma classe muito simples, com apenas um método construtor que imprime uma mensagem na tela. A
Listagem 8 mostra o arquivo jboss-beans.xml, o qual possui a declaração do POJO com o nome
PojoServiceExample.

Listagem 8. Arquivo jboss-beans.xml


<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns="urn:jboss:bean-deployer:2.0">

<bean name="PojoServiceExample"
class="jm.exemplos.pojo.PojoServiceExample"/>
</deployment>

Ao realizar o deploy, deverá ser exibida a mensagem conforme mostra a Listagem 9.

Listagem 9.

$JBOSS_HOME/server/$PROFILE/log/server.log

2009-03-04 17:10:16,060 DEBUG [org.jboss.deployers.structure.

spi.helpers.AbstractDeploymentContext] (main) Added component

PojoServiceExample to vfszip:/opt/downloads/jboss-5.0.1.GA/server

/default/deploy/jboss5-servico-exemplo.beans

2009-03-04 17:10:16,063 INFO [STDOUT]

(main) Construtor de PojoServiceExample()

Esse exemplo mostra a forma mais simples de disponibilizar um POJO utilizando o Microcontainer.
Basicamente todos os serviços, módulos e suas dependências são injetados como POJOs e manipulados
dessa forma, sem a necessidade de ter de implementar uma interface ou estender uma classe abstrata.

Instalação

Para a instalação, utilizaremos a versão binária da distribuição do JBoss AS. Para fazer o download do
AS5, basta acessar a página de download (ver Links) e selecionar o arquivo jboss-5.0.1.GA.zip. O site
JBoss.org disponibiliza também versões anteriores e um Release Notes de cada uma das versões.

Recomendamos a utilização da versão GA (General Availability), que é a versão estável da distribuição.

O único pré-requisito para execução do JBoss AS é uma máquina virtual Java (JRE) 1.5 ou superior
instalada. Na versão 5, não há mais a necessidade de instalação do JDK, pois o AS5 já vem com o ECJ
(compilador do Eclipse JDT), necessário para compilar JSPs.

O AS5 pode ser instalado e executado utilizando uma máquina virtual Java 5 ou 6 em qualquer Sistema
Operacional. Os binários compilados com as duas JDKs estão disponíveis no site www.jboss.org. Apesar
de o número de downloads do binário compilado com Java 6 ser superior e ser considerado uma versão
experimental, a versão com Java 5 é a chamada versão primária e é recomendada para utilização em
produção. Caso queira rodar o AS5 compilado com JDK 5 utilizando JRE 6, quatro bibliotecas devem ser
copiadas do diretório /client para o diretório /lib/endorsed.

São elas:
• jbossws-native-saaj.jar;
• jbossws-native-jaxrpc.jar;
• jbossws-native-jaxws.jar;
• jbossws-native-jaxws-ext.jar.

Porém, utilizando o binário compilado com JDK 6 em uma JRE 6, nenhuma alteração é necessária.O que
muda em geral é o script de inicialização de cada ambiente. Por exemplo: no Windows utilizamos run.bat,
no Unix/Linux, run.sh (para simplificar, omitiremos a extensão a partir deste ponto). O processo de
instalação básica do JBoss AS é extremamente simples: basta descompactá-lo em algum diretório de sua
escolha.

Todo o JBoss AS e sua configuração estão contidos em uma única estrutura de diretório. Então para
desinstalar o JBoss AS basta removermos todo o diretório.
Inicializando o servidor

Após a descompactação do arquivo, iremos iniciar o Application Server. Os scripts de inicialização estão
localizados no diretório /bin; execute o script run. Caso esteja utilizando o Windows, execute o arquivo
run.bat. O prompt de comando deverá exibir algo semelhante à Figura 4.

Figura 4: Prompt de inicialização do AS5

Com isso, seu AS5 está pronto para ser utilizado.

Os arquivos de inicialização utilizarão por padrão o profile default. Veja o quadro “Explorando a nova
estrutura de diretórios” para entender melhor os profiles existentes no AS5. Com o parâmetro -c, é
possível inicializar diferentes profiles:

# ./run -c allP

Por questões de segurança, o JBoss AS, quando executado, atende às requisições apenas na interface
local (127.0.0.1). Esse comportamento faz com que o servidor não seja acessível remotamente. Para
habilitar o acesso remoto ao servidor de aplicações, basta utilizarmos a opção -b no script de inicialização
do servidor. O comando abaixo fará o JBoss AS executar os serviços em todas as interfaces de rede do
servidor onde está sendo executado.

# run.sh -b 0.0.0.0

De qualquer maneira, esteja ciente da necessidade da correta configuração de segurança do servidor


conforme a política de segurança local.
Explorando a nova estrutura de diretórios

A estrutura de diretórios do AS5 é muito semelhante à dos seus antecessores (Figura 5). Iremos detalhar
as principais diferenças entre a estrutura de diretórios de uma versão da série 4.x para a versão 5.

Ao instalar o AS5, percebe-se o novo diretório


common na raiz da instalação. Nas versões
anteriores, o diretório lib de cada profile
(configuração) possuía praticamente as mesmas
bibliotecas. Em um ambiente com diversos
profiles sendo executados em paralelo, o
tamanho em disco do JBoss AS aumenta
consideravelmente. No AS5 as bibliotecas que
são obrigatórias para o funcionamento de todos
os profiles foram movidas para o diretório
$JBOSS_HOME/common/lib. Esse diretório
funciona como uma extensão do diretório
$JBOSS_HOME/server/$PROFILE/lib, deixando-o
mais limpo e de fácil manutenção, especialmente
para gerenciar seus drivers jdbc ou bibliotecas
específicas das aplicações.

Os outros diretórios da raiz da instalação


permanecem com as mesmas funções:
•
bin - possui todos os scripts e arquivos binários
necessários para iniciar e parar o AS;
•
client - contém bibliotecas necessárias à
comunicação de uma aplicação cliente com o
JBoss AS. Essas bibliotecas não são carregadas
pelo JBoss AS diretamente, mas por aplicações
clientes rodando em JVMs diferentes;
•
docs - diretório com exemplos de configuração
de Datasources, DTDs e XML schemas, licenças
de bibliotecas e resultados de testes unitários.
Para documentação completa do JBoss AS,
acesse o site da comunidade JBoss
www.jboss.org;
•
lib - possui as bibliotecas necessárias para
executar o “core” do AS;
•
server - é um dos diretórios mais importantes e
contém os profiles e as configurações para a
execução de aplicações Java EE.

Além dos profiles clássicos das versões


anteriores (minimum, default, all), dois novos
profiles foram adicionados ao diretório server:
web e standard. O profile web foi criado com o
Figura 5: Estrutura de diretórios da raiz do AS5 objetivo de executar apenas aplicações web, ou
seja, JSPs e Servlets (sem a utilização de EJBs).
Além disso, também estão disponíveis JTA, JCA e JPA; porém a maioria dos outros serviços não está
disponível, como os serviços de e-mail, mensageria, Quartz, gerador de chaves, etc. Esses serviços só
estão disponíveis a partir do profiler default. O profile standard foi criado para a execução dos testes de
compatibilidade com Java EE 5. O profile default é um “superset” do standard e possui algumas poucas
configurações e aplicações adicionais.

Muitos usuários se perguntam qual profile devem utilizar para executar suas aplicações. A Tabela 2
mostra as principais diferenças entre os cinco profiles.
Tabela 2: Serviços disponíveis em cada profile.

Os profiles mais utilizados nas versões anteriores eram o default e o all. No AS5, além destes, o profile
web deverá ser utilizado em grande escala, substituindo a utilização do Tomcat e jboss-web stand-alone.
Se a aplicação necessita de alta-disponibilidade (HA), utiliza-se o profile all. A maioria dos outros casos
será atendida com o profile default.

Cada profile possui um diretório conf. Assim como nas versões anteriores do JBoss AS, possui as
configurações gerais do servidor de aplicação, como controle transacional, log4j, diretórios de deploy, etc.

O diretório deploy é o local utilizado para publicar aplicações e disponibilizar serviços no JBoss AS.

No AS5, houve a separação dos serviços “deployáveis” com a criação do diretório deployers (Figura 6).
Figura 6: Serviços disponíveis em cada profile.

Assim, não ficam mais misturados no diretório deploy, sendo armazenados no diretório deployers(a
exemplo do jbossweb.deployer).

O diretório lib contém bibliotecas que serão compartilhadas por todos os serviços e aplicações disponíveis
para o profile. Caso queira compartilhar um driver JDBC entre várias aplicações no mesmo profile,
adicione a biblioteca do drive no diretório lib.

Para o profile all, foi criado o diretório cluster, o qual armazena uma total reestruturação dos arquivos de
configuração de cluster. O arquivo cluster-service.xml não existe mais, e seu equivalente é o
$PROFILE/deploy/cluster/jgroups-channelfactory.sar/META-INF/jgroups-channelfactory-stacks.xml.
JBoss AS de cara nova

O design do JMX Console e Web Console também foi atualizado para facilitar a busca de serviços. Agora,
há um menu lateral esquerdo com filtro para facilitar a identificação dos serviços. Ao clicar no link,
apenas os serviços relacionados serão exibidos (Figura 7).

Figura 7: Novo design do JMX Console

O Web Console também foi modificado, incorporando o design do JMX Console (Figura 8).

Figura 8: Novo design do Web Console


Mitos

Acabando com o mito: O JBoss AS não tem um console unificado de administração, monitoração e
controle, tudo tem de ser feito “à mão”.

Não basta ser um servidor moderno, robusto e performático se a administração não for algo simples e
intuitivo. Pensando nisso, foi criado o projeto Jopr (Figura 9). O nome Jopr (pronuncia-se jopper) foi
escolhido baseado no filme Jogos de Guerra (WarGames), em que havia um super computador chamado
WOPR (Whopper) - War Operation Plan Response - o qual era responsável por responder a todo e
qualquer ataque nuclear inimigo.

Figura 9: Jopr

O propósito desse projeto é disponibilizar ao administrador uma interface Web integrada e intuitiva para a
administração, monitoração, configuração e controle da Plataforma JBoss.

Através do Jopr é possível, entre várias outras coisas:

Saber o consumo de CPU, memória, disco e rede do servidor;


Saber o consumo de memória da JVM utilizada pelo servidor de aplicações;
Visualizar e monitorar métricas do servidor de aplicações e das aplicações. (Ex: saber quantas
sessões estão abertas em uma determinada aplicação);
Realizar start/stop/restart do servidor de aplicações;
Monitorar métricas e criar/deletar/configurar datasources, connection factories, JMS topics;
Gerar alertas através de e-mail e/ou traps SNMP quando da ocorrência de uma condição pré-
configurada (Ex: utilização de CPU ultrapassar 80%; memória da JVM ultrapassar 90%).
Além do Jopr Server - com foco em várias instâncias de JBoss AS, existe também uma versão com foco
em apenas uma instância de JBoss AS chamado Embedded Jopr, o qual é disponibilizado no próprio
servidor. Com o Embedded Jopr (Figura 10), é possível realizar deploys de EARs e WARs, criar novos
Datasources, criar novas filas JMS, monitorar o pool de conexões, executar scripts, etc. Tudo isso
utilizando uma interface Web intuitiva.

Figura 10: Embedded Jopr.

Para completar seu estudo sobre o JBoss AS 5, veja a Apresentação “Mergulhando fundo no JBoss AS 5 e
Jopr”.

Conclusão

Após três anos de pesquisas e desenvolvimento, o JBoss Application Server 5 está pronto para sua
grande estréia.

Com um novo kernel construído a partir do zero, substitui a utilização de MBeans por POJOs, utiliza
extensamente AOP, unifica a manipulação de metadados, altera o mecanismo de classloading, “aspectiza”
os deployers, tudo isso mantendo a compatibilidade com a maioria dos serviços existentes. Essas são
algumas das novidades dessa nova versão.

O lançamento do AS5 é apenas o começo de uma nova era para os projetos JBoss.

A flexibilidade proporcionada pelo Microcontainer permitirá à Red Hat/JBoss, além de inovar, estar up-to-
date com as especificações do JCP.

O objetivo da Red Hat/ JBoss é ter o mais inovador, robusto e performático servidor de aplicações do
mercado.

As possibilidades são infinitas e a Red Hat/JBoss precisa da participação da comunidade para ajudar na
definição do futuro e no desenvolvimento do JBoss AS e de todos os outros projetos. Essa contribuição é
essencial para manter os projetos JBoss como os mais populares, queridos e utilizados do planeta. A
comunidade é o poder do open source. Não seja apenas usuário: participe, teste, reclame, sugira,
contribua.
Links
www.jboss.org
Página principal da JBoss

http://www.jboss.org/community/docs/DOC-9938
Como contribuir com a comunidade JBoss

http://java.sun.com/javaee/overview/compatibility.jsp
Listagem de Servidores de Aplicação compatíveis com Java EE 5.0

http://jcp.org/en/jsr/detail?id=244
Página da especificação Java EE 5.0

http://www.jboss.com/products/platforms/application/standards
Página de padrões suportados pelo JBoss AS

http://www.jboss.org/jbossas/downloads/
Página de download do JBoss AS

http://www.jboss.org/jopr/
Página principal do Jopr

http://felix.apache.org
Página principal do Apache Felix

http://magazine.redhat.com/2008/10/31/interview-chris-morgan-on-jopr/
Entrevista com Chris Morgan sobre Jopr

Livros
JBoss in Action, Javid Jamae e Peter Johnson, Manning Publications, 2009
Nova versão do livro JBoss in Action a qual foca nas principais configurações do JBoss Application Server
5

Saiba Mais
www.devmedia.com.br/articles/viewcomp.asp?comp=10118
Java Magazine 46 - JBoss: Instalação, Arquitetura , Configuração, Tuning e Administração

www.devmedia.com.br/articles/viewcomp.asp?comp=8453
Java Magazine 51 - Portlets com JBoss Portal

www.devmedia.com.br/articles/viewcomp.asp?comp=8495
Java Bagazine 53 - Drools: Regras na Prática

www.devmedia.com.br/articles/viewcomp.asp?comp=9488
Java Magazine 58 - JBoss Seam

www.devmedia.com.br/articles/viewcomp.asp?comp=10202
Java Magazine 59 - JBoss ESB
Spring Security

Michel Zanini

Formado em Ciências da Computação pela Universidade Federal de Santa Catarina (UFSC) e possui as
certificações SCJP, SCWCD, SCBCD e SCDJWS.

De que se trata o artigo:

O artigo apresenta o projeto Spring Security como uma alternativa na área de segurança à tradicional
especificação Java EE, através de um exemplo prático e realista.

Para que serve:

Com o Spring Security é possível criar um mecanismo de autenticação e autorização para sua aplicação
web em questão de minutos. O framework foca em facilitar a implementação dos casos de uso mais
freqüentes, porém oferece valiosos pontos de extensão para requisitos mais complexos. Por fim,
disponibiliza suporte a inúmeros diferentes tipos de autenticação e integração com as mais usadas
tecnologias na área de segurança.

Em que situação o tema é útil:

Para qualquer aplicação web que necessite restringir seus recursos para diferentes tipos de usuário,
bem como assegurar que se autentiquem de forma prática e segura.

Spring Security:

O Spring Security surgiu da necessidade de melhorar o suporte à segurança oferecido pela especificação
Java EE. O framework centraliza a configuração em um único XML, dispensando configurações do
container e tornando a aplicação web um arquivo WAR auto contido.

Para começar a utilizá-lo basta adicionar seus JARs ao classpath, configurar um filtro e um listener no
web.xml e criar um application context (XML de configuração). O XML centraliza todas as configurações
de autenticação e autorização. As tags definem quais roles podem acessar cada grupo de URLs. A tag
define a fonte de dados para as informações de usuários (banco de dados, arquivo de propriedades,
LDAP, etc.).

Quando necessário, é possível utilizar os eventos publicados pelo framework a cada sucesso ou falha na
autenticação ou autorização. Ouvir os eventos permite criar complexos casos de gerenciamento de
usuários. O Spring Security ainda oferece integrações com a API de Servlets, taglibs para facilitar a
codificação de JSPs, suporte à HTTPS, segurança em métodos com uso de anotações e suporte a
autenticação com LDAP ou certificados X509.

Segurança é um requisito importante presente na grande maioria dos sistemas desenvolvidos. Na


plataforma Java EE temos uma solução oferecida pela especificação que determina como uma aplicação
pode definir regras de controle de acesso e autenticação. Entretanto, ainda é comum nos depararmos
com soluções “caseiras” para cumprir tal requisito. Em parte, tais soluções são criadas pela falta de
experiência dos desenvolvedores, ou por outro lado, porque a especificação não é flexível o suficiente
para comportar os requisitos.

Com o objetivo de preencher a lacuna deixada pela especificação, em 2003 surgiu o Acegi Security
System for Spring. O Acegi Security é conhecido por ser extremamente configurável e poderoso, porém
difícil de utilizar devido à enorme quantidade de configuração XML necessária. Em 2007 o projeto Acegi
foi incorporado dentro do guarda-chuva de projetos do Spring Framework Portifolio, e então, renomeado
como Spring Security.
Em abril de 2008 a versão 2.0.0 do Spring Security foi lançada tomando como proveito a configuração
baseada em namespaces do Spring 2.0. Hoje, o Spring Security é extremamente fácil de configurar, sem
perder a flexibilidade e o poder do antigo Acegi.

O Spring Security depende de alguns JARs do Spring Framework “core”. Entretanto, não é necessário que
sua aplicação seja construída com o modelo de programação do Spring Framework. Ou seja, uma
aplicação pré-existente que não usa Spring pode passar a utilizar o Spring Security sem grandes
modificações. Para aprender mais sobre o Spring Framework consulte o artigo de capa da Edição 65.

Assim como o Java EE o Spring Security possui uma abordagem declarativa para segurança, baseada em
roles (papéis). A abordagem é declarativa, pois a aplicação não precisa chamar nenhum método para
realizar autenticação ou autorização, tudo é feito através de configuração XML.

Para configurar o Spring Security de forma declarativa, assim como no Java EE, é necessário declarar
quais serão os roles envolvidos, quais os recursos que serão protegidos, e quais roles podem acessar
cada recurso. Além disso, declara-se como a autenticação será feita (basic, digest, form login, LDAP,
etc.).

Este artigo apresenta as características do Spring Security, mostrando alguns recursos importantes não
presentes na especificação Java EE. Exemplos práticos serão construídos, abordando cenários frequentes
que são requisitos de grande parte das aplicações web.

Os conceitos básicos sobre segurança não são abordados no artigo. Entretanto, o artigo de capa da
Edição 22 descreve tais conceitos e demonstra exemplos práticos utilizando a especificação Java EE.

Primeiro exemplo

Como primeiro exemplo vamos criar uma aplicação web simples com duas áreas de acesso restrito: uma
permitida para qualquer usuário autenticado (/usuarios/index.jsp) e outra apenas para usuários
administradores (/admin/index.jsp). As páginas restritas apenas exibem uma mensagem e possuem um
link de retorno à página principal. Espera-se que um login seja solicitado ao acessar qualquer uma destas
áreas. A página inicial da aplicação (/index.jsp), ilustrada na Figura 1, tem acesso livre e possui links
para as duas áreas restritas.

Figura 1: Página inicial da aplicação.


O código fonte dos três JSPs são HTML simples e portanto não serão exibidos nas listagens (estão
disponíveis para download no site da Java Magazine).

Configurando o web.xml

O Spring Security utiliza-se de um filtro HTTP, declarado no web.xml (Listagem 1), para interceptar todas
as URLs acessadas e conferir suas permissões de acesso.

Por isso, o filtro é aplicado com o url-pattern “barra asterisco”. No Java EE tal filtro não é necessário, pois
o controle de acesso é realizado pelo próprio container.

É importante notar que o nome do filtro é ‘springSecurityFilterChain´ e não deve ser alterado, pois o
Spring Security já espera que o filtro esteja com este nome, por convenção.

No Java EE as configurações de autenticação e autorização são feitas no web.xml. No Spring Security são
feitas em um application context padrão do Spring Framework. Dessa forma, precisamos do listener
ContextLoaderListener declarado no web.xml para carregar o application context na inicialização da
aplicação web.

O atributo contextConfigLocation do listener indica a localização do application context, neste caso, na


raiz do classpath com o nome spring-security-config.xml. O web.xml da Listagem 1 aplica essas
configurações.

Listagem 1. web.xml - Configuração do Spring Security no web.xml

<web-app>

<filter>
<filter-name>springSecurityFilterChain
</filter-name>
<filter-class>org.springframework.web.filter.
DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain
</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
<listener-class>org.springframework.web.
context.ContextLoaderListener
</listener-class>
</listener>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:
spring-security-config.xml
</param-value>
</context-param>

</web-app>

Application context é o nome dado aos XMLs de configuração do Spring Framework. Esses arquivos são
genéricos o suficiente para configurar qualquer tipo de aplicação. Em casos específicos, como do Spring
Security, namespaces são utilizados para reduzir a quantidade de XML necessária.

Controle de acesso e autenticação no spring-security-config.xml

Agora é necessário criarmos o arquivo spring-security-config.xml, conforme a Listagem 2. Este arquivo,


carregado pelo listener do web.xml, utiliza o namespace do Spring Security
(http://www.springframework.org/schema/security) para declarar regras de autenticação e autorização.
Declaramos este namespace como default para o XML (sem prefixo) e o prefixo “beans” para o
namespace normal do Spring Framework.

Listagem 2. spring-security-config.xml - Arquivo de configuração do Spring Security

<beans:beans xmlns="http://www.springframework.org/
schema/security"
xmlns:beans="http://www.springframework.org
/schema/beans"
xmlns:xsi="http://www.w3.org/2001/
XMLSchema-instance" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/
beans/spring-beans-2.5.xsd
http://www.springframework.org/
schema/security
http://www.springframework.org/
schema/security/
spring-security-2.0.2.xsd">
<http auto-config="true">
<intercept-url pattern="/usuarios/**"
access="ROLE_USUARIO,ROLE_ADMIN" />
<intercept-url pattern="/admin/**"
access="ROLE_ADMIN" />
</http>
<authentication-provider>
<user-service>
<user name="joao" password="123"
authorities="ROLE_USUARIO" />
<user name="admin" password="123"
authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</beans:beans>

Se o leitor utiliza a IDE Eclipse, aconselhamos a instalação do plugin Spring IDE (veja seção de links)
para facilidades na edição de application contexts. Outras IDEs como NetBeans e IntelliJ IDEA também
possuem excelentes plugins de integração com o Spring, vale à pena conferir.

O controle de acesso é definido pela tag interna à tag . No atributo pattern definimos uma expressão que
atenderá as URLs acessadas e no atributo access definimos os roles de usuários que poderão acessar as
URLs, separados por vírgulas. Definimos que todas as URLs com prefixo /usuarios serão acessadas por
usuários normais e administradores e URLs com prefixo /admin apenas por administradores.

Por default a sintaxe das expressões utilizadas no atributo pattern é a mesma utilizada no Ant. Porém, se
desejado, pode-se alterá-la para seguir a sintaxe de expressões regulares. Para tal, basta adicionar o
atributo path-type="regex" na tag . Dessa forma é possível criar expressões tão complexas quanto
necessário para atender às URLs, de acordo com os requisitos do desenvolvedor.

As tags serão interpretadas em ordem de definição e a primeira a atender será usada. Dessa forma, os
patterns mais específicos devem vir primeiro. Por exemplo, a expressão ‘/usuarios/vip/**´ deve ser
declarada acima da expressão ‘/usuarios/**´, caso contrário a expressão ‘vip´ nunca será avaliada, pois
a expressão ‘/usuarios/**´ também atende a URLs do tipo ‘/usuarios/vip/**´.

O atributo auto-config=“true” da tag configura automaticamente a aplicação para utilizar login baseado
em formulário. O JSP do formulário nem mesmo precisa ser codificado, o Spring Security irá gerá-lo
dinamicamente conforme a Figura 2. Para mais informações sobre o auto-config=“true” veja o quadro
“Entendendo o auto-config”.

Figura 2: Login gerado automaticamente pelo


Spring Security.

Entendendo o auto-config

O atributo auto-config da tag na configuração do Spring Security ativa as opções mais usadas do
framework, ajudando a diminuir a quantidade de XML necessário. Quando setamos seu valor para “true”
o Spring Security considera o XML como a seguir:
<http>
<form-login />
<anonymous />
<http-basic />
<logout />
<remember-me />
</http>

Isso configura o framework para utilizar autenticação por formulário e http-basic, bem como tratamento
de usuários anônimos e previamente autenticados (remember-me).
Cada uma das tags possui atributos. Para modificá-los é necessário apenas escrever a tag específica, e o
auto-config=true substituirá esta parte da configuração.

As tags omitidas continuam sendo consideradas. Por exemplo, o código abaixo muda o JSP do formulário
de login e o restante das configurações automáticas continuam aplicadas.

Isso configura o framework para utilizar autenticação por formulário e http-basic, bem como tratamento
de usuários anônimos e previamente autenticados (remember-me).

Cada uma das tags possui atributos. Para modificá-los é necessário apenas escrever a tag específica, e o
auto-config=true substituirá esta parte da configuração. As tags omitidas continuam sendo consideradas.
Por exemplo, o código abaixo muda o JSP do formulário de login e o restante das configurações
automáticas continuam aplicadas.

<http auto-config=true>
<form-login login-page=/login.jsp/>
</http>

Desta forma, a última coisa que nos resta é definir os usuários possíveis e seus papéis. No primeiro
exemplo, para facilitar, iremos utilizar a tag que permite definir usuários, senhas e roles no próprio
arquivo XML (também é possível referenciar um arquivo de propriedades). Normalmente, em uma
aplicação real, não é viável definir os usuários em arquivo. Nos próximos exemplos iremos apresentar
alternativas.

Últimos passos

O esqueleto do projeto, juntamente com os JARs necessários para rodar os exemplos, podem ser vistos
da Figura 3. Para ajudar na depuração da aplicação, através de log, criamos o arquivo log4j.properties e
adicionamos o log4j.jar ao classpath. O JAR do banco de dados HSQL-DB está presente pois será utilizado
em breve, nos próximos exemplos. O resto das bibliotecas são dependências do Spring Security. Os JARs
estão disponíveis no download dessa edição no site da Java Magazine.

Com os três JSPs, o web.xml, o spring-security-config.xml e os


JARs necessários, podemos executar a aplicação. O deploy pode
ser realizado em qualquer servidor da preferência do leitor, pois
o Spring Security não requer configurações adicionais
dependentes de container.

Executando o exemplo

Ao acessar o index da aplicação (Figura 1) dois links serão


apresentados. Ao clicar em algum deles, por exemplo
/usuarios/index.jsp, o filtro do Spring Security irá detectar como
uma página protegida e irá gerar automaticamente o HTML para
o login (Figura 2). Um usuário normal, ao logar-se, terá acesso à
página de usuários, mas não à de administradores. Já o
administrador tem acesso a ambas as páginas. Note que o
serviço de “remeber-me” para o login já está funcionando. O
exemplo é bastante simples, mas já cobre alguns dos principais
conceitos e casos de uso.

Utilizando algoritmos de hash para senhas

Figura 3: Arquivos do primeiro


exemplo.
No primeiro exemplo as senhas dos usuários estão visíveis aos administradores da aplicação. Isto
apresenta um risco de segurança. Um algoritmo de hash é normalmente usado em uma aplicação real,
impossibilitando o acesso à senha original. Para tal, o Spring Security oferece a tag . São oferecidos os
principais algoritmos de hash e também é possível criar uma implementação customizada. A Listagem 3
aplica o algoritmo “md5” como password-encoder.

Listagem 3. spring-security-config.xml - Utilizando um password-encoder

<beans:beans (...) >


(...)
<authentication-provider>
<password-encoder hash="md5" />
<user-service>
<user name="joao"
password="202cb962ac59075b964b07152d234b70"
authorities="ROLE_USUARIO" />
<user name="admin"
password="202cb962ac59075b964b07152d234b70"
authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
(...)
</beans:beans>
<beans:beans (...) >
(...)
<authentication-provider>
<password-encoder hash="md5" />
<user-service>
<user name="joao"
password="202cb962ac59075b964b07152d234b70"
authorities="ROLE_USUARIO" />
<user name="admin"
password="202cb962ac59075b964b07152d234b70"
authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
(...)
</beans:beans>

Para gerar rapidamente um hash md5 para utilizar nos exemplos o leitor pode implementar uma classe
Java simples de utilidade ou acessar um gerador online (veja Links).

Formulário personalizado

A geração automática de formulário é útil apenas para testes. Uma aplicação real necessita de uma
página de formulário customizada. Iremos criar um JSP (/login.jsp) com um formulário para o nosso
exemplo. Para isso acrescentamos este trecho dentro da tag no spring-security-config.xml:

<form-login login-page="/login.jsp" authentication-failure-url= "/login.jsp?login_error=true" />

O atributo authentication-failure-url configura o JSP que será apresentado caso o login falhe. Neste caso
configuramos o mesmo JSP do formulário de login com um parâmetro login_error=true. Esse parâmetro
é utilizado pelo JSP de login da Listagem 4.

Listagem 4. login.jsp - Página de login

<html>
<head>
<title>Spring Security</title>
</head>
<body>
<h1>Spring Security</h1><hr/>
<p>
<% if (request.getParameter
("login_error") != null) { %>
<font color="red">
Não foi possível se autenticar.<br/>
Motivo:
${SPRING_SECURITY_LAST_
EXCEPTION.message}.
</font>
<% } %>
</p>
<form action=
"j_spring_security_check"
method="POST">
Login: <input type=text name=j_username
value="${not empty login_error ?
SPRING_SECURITY_LAST_USERNAME : }" />
Senha: <input type=password name=j_password>
<input type="checkbox"
name="_spring_security_remember_me" />
Salvar as minhas
informações neste computador?
<input name="submit"
type="submit" value=”Login” />
<input name="reset"
type="reset" value=”Limpar” />
</form>
<a href="index.jsp">Voltar...</a><br>
</body>
</html>

Os exemplos demonstrados utilizam Scriplets (código Java no JSP) apenas por questões didáticas. Uma
aplicação real estaria utilizando a taglib JSTL.

No topo do JSP, abaixo do título, testamos se o parâmetro login_error é igual a true. Caso verdade (o
login falhou) então mostramos uma mensagem de erro. A expressão $
{SPRING_SECURITY_LAST_EXCEPTION.message} recupera a última mensagem de erro gerada pelo
framework. Essa mensagem por default está em inglês, mas pode ser internacionalizada.

Em seguida codificamos o formulário. Por default a action do formulário de login deve ser
j_spring_security_check e o “name” dos inputs de usuário e senha j_username e j_password,
respectivamente. No nosso exemplo, para habilitar o remember-me, adicionamos um checkbox com o
“name” _spring_security_remember_me. Esse JSP gera a imagem da Figura 4.

Figura 4: Página de login customizada


Logout

O atributo auto-config=true da tag <http> já configura uma URL default para realizar o logout,
/j_spring_security_logout. Ao clicar em um link apontando para esta URL o logout é realizado. Caso
necessário, é possível modificar a URL acrescentado a tag <logout> dentro da tag <http>:

Quando um usuário normal acessa a página protegida dos administradores o container apresenta uma
mensagem padrão para o erro 403 (access denied). Essa página pode ser personalizada pelo Spring
Security acrescentando o atributo access-denied-page à tag :

<http access-denied-page="/accessDenied.jsp" (...) >

E então, criamos o accessDenied.jsp conforme a Listagem 5.

Listagem 5. accessDenied.jsp - Página de erro

<html>
<body>
<h1>Spring Security</h1><hr/>
<p><font color="red">Acesso negado.
O usuário não tem permissão para acessar
essa página.</font><p>
<p>Remote user....:
<%= request.getRemoteUser() %></p>
<p>User principal....:
<%= request.getUserPrincipal() %></p>
<a href="../">Voltar...</a><br>
<a href="../j_spring_security_logout">
Logout</a><br>
</body>
</html>

Formulário de login embutido em outras páginas

Quem tem experiência com a segurança tradicional Java EE sabe que não é possível acessar o JSP de
login diretamente pela aplicação. O container deve apresentar este JSP quando uma página protegida é
acessada. Essa limitação cria outros problemas, por exemplo, quando um portal deseja que o formulário
de login esteja contido em todas as áreas do site. Nesse caso, os desenvolvedores se obrigam a criar
algum mecanismo utilizando JavaScript para contornar a situação.

No Spring Security tais limitações simplesmente não existem. Quando o usuário acessa diretamente o
JSP de login, ou quando um formulário de login embutido é utilizado, o Spring Security utiliza uma URL
default para redirecionar o usuário. A tag <form-login> possui um atributo default-target-url que indica
esta URL (se não informada o default é a raiz da aplicação). Por exemplo, se o formulário for configurado
desta forma:

<form-login default-target-url="/index.jsp" (...) />

Sempre que o formulário de login for acessado diretamente, após logar-se, o /index.jsp será carregado.
Caso o usuário acessar uma página protegida, após logar-se, esta será a página exibida e não o
/index.jsp. Se o atributo always-use-default-target for true o usuário sempre será redirecionado para o
/index.jsp.

Autenticação utilizando banco de dados

A última modificação necessária para tornar o exemplo realístico é adicionar um banco de dados,
substituindo a configuração dos usuários e roles do XML. Um modelo de dados típico para uma aplicação
web, de maneira simplificada, é algo parecido com as tabelas da Figura 5.

Figura 5: Modelo de dados para armazenar os usuários, senhas e perfis.


Apesar de não ser necessário no nosso exemplo, criamos uma tabela de junção entre as tabelas de
usuário e perfil para termos uma relação NxN. Este cenário é muito comum em aplicações enterprise.

O campo chamado ativo na tabela de usuários serve para impedir que usuários bloqueados autentiquem-
se. Caso este campo for false o usuário está com o acesso bloqueado. O campo tentativas_login
armazena o número de vezes que o usuário errou a senha consecutivamente. Esse campo será utilizado
apenas em exemplos posteriores.

O download do artigo traz dois scripts para serem executados no banco de dados HSQL. Um script cria as
tabelas e outro cria alguns usuários para teste. Para rodar o HSQL basta executar este comando no
prompt do sistema operacional (considerando que o jar do HSQL está no diretório corrente):

java -cp hsqldb-1.8.0.7.jar org.hsqldb.Server

Logo após, abra outro prompt e execute o comando para abrir o console SQL com interface gráfica:

java -cp hsqldb-1.8.0.7.jar org.hsqldb.util.DatabaseManagerSwing

Selecione a opção “HSQL Database Engine Server” e pressione OK. Ao abrir o console execute os dois
scripts para criar e popular as tabelas. Feche o console, mas mantenha sempre o prompt do servidor
HSQL rodando.

Alterar a configuração do Spring Security para considerar as tabelas é algo muito simples, pressupondo
que o banco de dados está corretamente configurado. Duas modificações são necessárias: substituir a
tag por uma tag e acrescentar um spring bean para a configuração do banco de dados (data-source). A
Listagem 6 demonstra o novo spring-security-config.xml.

Listagem 6. spring-security-config.xml - Adicionando autenticação com banco de dados

<beans:beans (...)>
<http auto-config="true" access-denied-page=
"/accessDenied.jsp">
<intercept-url pattern="/usuarios/**"
access="ROLE_USUARIO,ROLE_ADMIN" />
<intercept-url pattern="/admin/**"
access="ROLE_ADMIN" />
<form-login login-page="/login.jsp"
authentication-failure-url=
"/login.jsp?login_error=true"
default-target-url="/index.jsp" />
</http>
<authentication-provider>
<password-encoder hash="md5" />
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select login as username,
senha as password, ativo as enabled
from usuario where login = ?"
authorities-by-username-query="select u.login as username,
p.descricao as authority from usuario u join usuario_perfil
up on u.login = up.login join perfil p on up.id_perfil =
p.id_perfil where u.login = ?" />
</authentication-provider>
<beans:bean id="dataSource"
class="org.springframework.jdbc.
datasource.DriverManagerDataSource">
<beans:property name="url"
value="jdbc:hsqldb:hsql://localhost" />
<beans:property name="driverClassName"
value="org.hsqldb.jdbcDriver" />
<beans:property name="username" value="sa" />
<beans:property name="password" value="" />
</beans:bean>
</beans:beans>
Precisamos de três atributos para configurar a tag . O primeiro atributo é uma referência para a
configuração do banco de dados que será utilizado (data-source-ref). O segundo é a query que será
utilizada para buscar os usuários dado um username (users-by-username-query). Essa query necessita
retornar três colunas esperadas pelo Spring Security: username, password e enabled. Mapeamos as
colunas do nosso modelo para as esperadas pela query. Por último, uma query para buscar os roles
(authorities) do usuário, dado um username (authorities-by-username-query). Essa query retorna um
username e a authority (role). Isso permite ao framework realizar as queries no banco e utilizar as
informações retornadas para autenticação.

No final do arquivo de configuração adicionamos um bean “dataSource” e o configuramos para acesso ao


banco de dados HSQL.

Repare que utilizamos a classe DriverManagerDataSource que serve apenas para propósitos de teste. Em
uma aplicação real configuraríamos um pool de conexões como data-source, normalmente oferecido pelo
container através de um nome JNDI.

Eventos de autenticação e autorização

O Spring Security utiliza a infra-estrutura do application context do Spring para publicar eventos
referentes a momentos importantes em seu fluxo de execução. Existem duas categorias de eventos:
eventos de autenticação e eventos de autorização. Para cada situação existe um evento correspondente.
Por exemplo, quando uma autenticação ocorre com sucesso, um evento AuthenticationSuccessEvent é
publicado; quando a autenticação falha porque a senha está errada, um evento
AuthenticationFailureBadCredentialsEvent publicado; se a autenticação falha porque o usuário está
inativo, um evento AuthenticationFailureDisabledEvent ocorre; e assim por diante. O Spring Security
utiliza a infra-estrutura do application context do Spring para publicar eventos referentes a momentos
importantes em seu fluxo de execução. Existem duas categorias de eventos: eventos de autenticação e
eventos de autorização. Para cada situação existe um evento correspondente. Por exemplo, quando uma
autenticação ocorre com sucesso, um evento AuthenticationSuccessEvent é publicado; quando a
autenticação falha porque a senha está errada, um evento AuthenticationFailureBadCredentialsEvent é
publicado; se a autenticação falha porque o usuário está inativo, um evento
AuthenticationFailureDisabledEvent ocorre; e assim por diante.

Quando uma URL é acessada e o filtro do Spring Security verifica se o usuário tem autorização para
acessar a URL, eventos de sucesso ou falha também são publicados. Veja na Figura 6 uma hierarquia de
eventos de autenticação e autorização. Consulte o Javadoc de cada classe para entender o momento em
que cada evento é publicado.

Figura 6: Eventos de autenticação e autorização publicados pelo Spring Security.


Para “ouvirmos” aos eventos acima é necessário implementar a interface ApplicationListener e registrar a
classe no application context. A Listagem 7 demonstra um exemplo simples de como ouvir a eventos de
sucesso ou falha na autenticação.

Listagem 7. TestEventListener.java - Ouve eventos de autenticação publicados pelo Spring


Security

public class TestEventListener implements


ApplicationListener {
public void onApplicationEvent
(ApplicationEvent event) {
if (event instanceof
AuthenticationSuccessEvent) {
System.out.println
("Usuário autenticado com sucesso");
}
if (event instanceof
AbstractAuthenticationFailureEvent) {
System.out.println
("Usuário não autenticado");
}
}

Lembre-se de adicionar o novo bean no application context:

<beans:bean class="br.com.jm.security.
TestEventListener" />

Essa funcionalidade permite implementar complexos casos de uso, com baixo acoplamento entre a
aplicação e o Spring Security, como veremos em um exemplo adiante. Para facilitar o aprendizado de
quando os eventos ocorrem, registre dois listeners que vêm juntos com a distribuição do Spring Security,
como a seguir:

<beans:bean class="org.springframework.security.event.
authorization.LoggerListener" />
<beans:bean class="org.springframework.security.event.
authentication.LoggerListener" />

Estes listeners efetuam log de todos os eventos que ocorrem, de autenticação e autorização. Uma ótima
forma de aprender o funcionamento do framework é a análise do log gerado por estes listeners.

Explorando os pontos de extensão

Escolhemos um caso de uso para demonstrar como os eventos de autenticação são úteis e como é fácil
customizar o framework. Cada vez que um usuário errar a senha por três vezes consecutivas, a sua conta
será bloqueada. Essa funcionalidade é muito comum em sites que exigem altos padrões de segurança,
como bancos por exemplo. A cada erro de autenticação por informar a senha incorreta (evento
AuthenticationFailureBadCredentialsEvent) a coluna tentativas_login será incrementada. A cada
autenticação com sucesso (evento AuthenticationSuccessEvent) a coluna tentativas_login será zerada. A
cada tentativa de login, caso a coluna tentativas_login for igual ou maior que três, a autenticação será
bloqueada. Veja na Listagem 8 o listener que atualiza a coluna tentativas_login. Escolhemos um caso de
uso para demonstrar como os eventos de autenticação são úteis e como é fácil customizar o framework.

Cada vez que um usuário errar a senha por três vezes consecutivas, a sua conta será bloqueada. Essa
funcionalidade é muito comum em sites que exigem altos padrões de segurança, como bancos por
exemplo. A cada erro de autenticação por informar a senha incorreta (evento
AuthenticationFailureBadCredentialsEvent) a coluna tentativas_login será incrementada. A cada
autenticação com sucesso (evento AuthenticationSuccessEvent) a coluna tentativas_login será zerada. A
cada tentativa de login, caso a coluna tentativas_login for igual ou maior que três, a autenticação será
bloqueada. Veja na Listagem 8 o listener que atualiza a coluna tentativas_login.

Listagem 8. IncorrectPasswordEventListener.java - Listener que atualiza a coluna


tentativas_login de acordo com o resultado da autenticação

public class IncorrectPasswordEventListener extends


JdbcDaoSupport implements ApplicationListener {
public void onApplicationEvent
(ApplicationEvent event) {
if (event instanceof AuthenticationFailureBadCredentialsEvent) {
AuthenticationFailureBadCredentialsEvent
badCredentialsEvent =
(AuthenticationFailureBadCredentialsEvent)
event;
String sql =
"update Usuario set tentativas_login =
tentativas_login + 1 where login = ?";
this.executeSql(badCredentialsEvent, sql);
}
if (event instanceof AuthenticationSuccessEvent) {
AuthenticationSuccessEvent successEvent =
(AuthenticationSuccessEvent) event;
String sql = "update Usuario set
tentativas_login =
0 where login = ?";
this.executeSql(successEvent, sql);
}
}
private void executeSql(AbstractAuthenticationEvent
event, String sql) {
getJdbcTemplate().update(sql, new Object[]
{event.getAuthentication().getName()});
}
}
public class IncorrectPasswordEventListener
extends JdbcDaoSupport implements
ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof
AuthenticationFailureBadCredentialsEvent) {
AuthenticationFailureBadCredentialsEvent
badCredentialsEvent =
(AuthenticationFailureBadCredentialsEvent)
event;
String sql = "update Usuario set
tentativas_login =
tentativas_login + 1 where login = ?";
this.executeSql(badCredentialsEvent, sql);
}
if (event instanceof AuthenticationSuccessEvent) {
AuthenticationSuccessEvent
successEvent =
(AuthenticationSuccessEvent) event;
String sql = "update Usuario set
tentativas_login = 0 where login = ?";
this.executeSql(successEvent, sql);
}
}
private void executeSql
(AbstractAuthenticationEvent event, String sql) {
getJdbcTemplate().update(sql,
new Object[]{event.getAuthentication().getName()});
}
}

Para completar o exemplo ainda é necessário bloquear o login caso a coluna seja maior ou igual a três.
Para isso iremos explorar um ponto de extensão comumente utilizado no Spring Security, a interface
UserDetailsService. Essa interface possui o método loadUserByUsername() que recupera os dados de um
usuário dado seu username. Veja a Listagem 9.

Listagem 9. Interface UserDetailsService e o método loadUserByUsername() a ser


implementado

public class CustomUserDetailsService implements UserDetailsService {

public UserDetails loadUserByUsername(String username)

throws UsernameNotFoundException, DataAccessException {

//retorna um objeto representado o usuário a ser autenticado


return null;

Uma implementação dessa interface substitui as tags (usuários em XML ou arquivo de propriedades) ou
(usuários em banco de dados) que vimos até agora. Sendo assim, através dessa implementação, o
usuário do framework tem liberdade para buscar as informações de onde necessitar e da forma que
quiser. Entretanto, o motivo mais comum para implementar a interface não é a escolha de uma nova
fonte de dados e sim, a personalização do objeto UserDetails de retorno.
A interface UserDetails é implementada por objetos que representam o usuário no seu modelo de
domínio. Ela possui métodos para retornar o username, o password, as authorities e quatro booleans
representando diferentes motivos para bloqueio do login: enabled, accountNonLocked,
accountNonExpired e credentialsNonExpired. Sendo assim, criamos uma classe Usuario para implementar
a interface, veja a Listagem 10.

Listagem 10. Usuario.java - Objeto do modelo de domínio que implementa a interface


UserDetails

public class Usuario implements UserDetails {

private String login;

private String senha;

private String email;

private boolean ativo;

private Integer tentativasLogin;

private GrantedAuthority[] authorities;

public String getUsername() {

return this.login;

public String getPassword() {

return this.senha;

public GrantedAuthority[] getAuthorities() {

return this.authorities;

public boolean isEnabled() {

return this.ativo;

public boolean isAccountNonLocked() {

return this.tentativasLogin < 3;

}
public boolean isAccountNonExpired() {

return true;

public boolean isCredentialsNonExpired() {

return true;

// --- restante dos getters and setters omitidos ---

Caso qualquer um dos métodos que retornam boolean retornar false o login será bloqueado e uma
mensagem adequada será apresentada. Nesse caso não iremos utilizar as propriedades de conta ou
senha expirada, então retornarmos sempre true nos métodos isAccountNonExpired() e
isCredentialsNonExpired(). Para o método isEnable() utilizamos o valor da coluna ativo no banco de
dados e para o método isAccountNonLocked() utilizamos nossa regra de negócios: bloquear o login caso
três ou mais tentativas de login tenham sido feitas com a senha incorreta.

Para conectar as implementações precisamos de uma classe que implemente a interface


UserDetailsService. Entretanto, queremos continuar utilizando um banco de dados para armazenar os
usuários e apenas retornar um objeto Usuario com informações adicionais, as colunas email e
tentativas_login.

Para não duplicarmos código sem necessidade, iremos estender uma classe do Spring Security que já nos
oferece um ponto de extensão justamente para estes casos. A classe JdbcDaoImpl é a classe por trás da
tag . Essa classe já implementa o método loadUserByUsername() e nos deixa uma extensão valiosa, o
método createUserDetails(). Esse método é um gancho para retornar um UserDetails customizado. Veja a
Listagem 11.

Listagem 11. CustomUserDetailsService.java - Estende a classe JdbcDaoImpl para modificar o


UserDetails de retorno

public class CustomUserDetailsService


extends JdbcDaoImpl {
protected UserDetails createUserDetails
(String username, UserDetails userFromUserQuery,
GrantedAuthority[] combinedAuthorities) {
Usuario usuario = new Usuario();
usuario.setLogin
(userFromUserQuery.getUsername());
usuario.setSenha
(userFromUserQuery.getPassword());
usuario.setAtivo
(userFromUserQuery.isEnabled());
usuario.setAuthorities
(combinedAuthorities);
this.carregarInformacoesAdicionais
(usuario);
return usuario;
}

private void carregarInformacoesAdicionais


(final Usuario usuario) {
String sql = "select email, tentativas_login
from Usuario where login = ?";
getJdbcTemplate().query(sql, new Object[]
{usuario.getUsername()}, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException {
usuario.setEmail(rs.getString("email"));
usuario.setTentativasLogin(rs.getInt
("tentativas_login"));
return null;
}
});
}
}

O método createUserDetails() apenas copia propriedades que já estão disponíveis e chama o método
carregarInformacoesAdicionais() que faz uma query para preencher as propriedades restantes: email e
tentativas_login. A implementação do caso de uso está completa. A classe CustomUserDetailsService é
utilizada para buscar as informações de um usuário e retornar um UserDetails do nosso domínio. Após a
execução do método loadUserByUsername() as propriedades booleanas do Usuario são testadas. Nesse
caso, o getter da propriedade accountNonLocked testa se a coluna tentativas_login é igual ou maior que
três. Por fim, para controlar o incremento da coluna, os eventos de sucesso e falha na autenticação são
tratados pela classe IncorrectPasswordEventListener. O novo spring-security-config.xml é demonstrado na
Listagem 12.

Listagem 12. spring-security-config.xml - Versão final do XML de configuração refletindo o


exemplo de bloqueio de login

<beans:beans (...) >


<http auto-config="true"
access-denied-page="/accessDenied.jsp">
<intercept-url pattern="/usuarios/**"
access="ROLE_USUARIO,ROLE_ADMIN" />
<intercept-url pattern="/admin/**"
access="ROLE_ADMIN" />
<form-login login-page="/login.jsp"
authentication-failure-url="/login.jsp?
login_error=true"
default-target-url="/index.jsp" />
</http>
<authentication-provider
user-service-ref="customUserService">
<password-encoder hash="md5" />
</authentication-provider>
<beans:bean id="customUserService"
class="br.com.jm.security.
CustomUserDetailsService">
<beans:property name="dataSource"
ref="dataSource" />
<beans:property name="usersByUsernameQuery"
value="select login as username,
senha as password,
ativo as enabled from
usuario where login = ?" />
<beans:property name=
"authoritiesByUsernameQuery"
value="select u.login as username,
p.descricao as
authority from usuario u join
usuario_perfil up on u.
login = up.login join perfil
p on up.id_perfil =
p.id_perfil where u.login = ?" />
</beans:bean>
<beans:bean id="dataSource"
class="org.springframework.jdbc.
datasource.DriverManagerDataSource">
<beans:property name="url"
value="jdbc:hsqldb:hsql://localhost" />
<beans:property name="driverClassName"
value="org.hsqldb.jdbcDriver" />
<beans:property name=
"username" value="sa" />
<beans:property name=
"password" value="" />
</beans:bean>
<beans:bean class="br.com.jm.security.
IncorrectPasswordEventListener">
<beans:property name="dataSource"
ref="dataSource" />
</beans:bean>
<beans:bean class=
"org.springframework.security.
event.authorization.LoggerListener" />
<beans:bean class=
"org.springframework.security.
event.authentication.LoggerListener" />
</beans:beans>

A principal diferença do novo XML é a tag . Retiramos o e fizemos uma referência para o bean
“customUserService”. Como a classe CustomUserServiceDetails estende JdbcDaoImpl as propriedades
das queries continuam existindo. Sendo assim, copiamos as propriedades da antiga para o novo bean.
Outra diferença importante é a adição do IncorrectPasswordEventListener no final do arquivo.

Integração com a API de servlets

O filtro do Spring Security substitui a implementação original da interface ServletRequest por um wrapper
(extensão da classe ServletRequestWrapper). Este wrapper implementa os métodos relacionados a
segurança na interface ServletRequest: getRemoteUser(), isUserInRole() e getUserPrincipal(). Dessa
forma, é possível utilizar os métodos tradicionais do Java EE como de costume, sem diferenças. Por
exemplo, veja o trecho de código a seguir:

String role = request.getRemoteUser();


System.out.println("Role: " + role);

System.out.println
(request.isUserInRole
("ROLE_USUARIO"));
System.out.println
(request.isUserInRole
("ROLE_ADMIN"));

Este código funciona perfeitamente com o Spring Security. Se o usuário “admin” (com perfil de
administrador) estiver logado, então a seqüência a ser impressa no console será:

Role: admin
false
true

O método getUserPrincipal() retorna um objeto que implementa a interface org.springframework.


security.Authentication. Através desse objeto é possível retornar o usuário logado, como a seguir:

public Usuario getUsuarioLogado(HttpServletRequest request) {


Authentication authentication = (Authentication) request.getUserPrincipal();
if (authentication == null) return null;
return (Usuario) authentication.getPrincipal();
}

Outra forma de se obter o usuário logado, porém sem necessitar do HttpServletRequest, é através da
classe SecurityContextHolder. Essa classe mantém o objeto Authentication em uma variável thread-local.
O método a seguir pode ser implementado em uma classe de utilidade e pode ser chamado em qualquer
ponto da aplicação:

public static Usuario getUsuarioLogado() {

Authentication authentication = (Authentication) SecurityContextHolder.getContext().getAuthentication();


if (authentication instanceof Usuario) {
return (Usuario) authentication.getPrincipal();
}
return null;
}

Utilizando as taglibs

O Spring Security fornece duas tags úteis para codificação de JSPs: e . Ambas podem ser importadas
com a declaração a seguir:

<%@ taglib prefix="sec" uri="http://www.spring


framework.org/security/tags" %>

A tag <authorize> é utilizada para mostrar/esconder informações de acordo com as permissões (roles)
do usuário. Veja o código a seguir:

<sec:authorize ifAllGranted="ROLE_ADMIN">
<p>Você é um administrador e também pode acessar esta <a
href="../admin/index.jsp">página</a>.<p>
</sec:authorize>

A tag possui três atributos exclusivos: ifAllGranted, ifAnyGranted e ifNotGranted. Todos os atributos
suportam um ou mais roles separados por vírgula. O atributo ifAllGranted informa que o usuário tem que
<Authentication>permite acessar as propriedades do objeto Authentication do usuário logado no JSP. Por
exemplo, o trecho de código a seguir, codificado no index.jsp, irá exibir as informações do usuário
utilizando a tag <Authentication>:

<sec:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USUARIO">
<b>Informações do usuário logado:</b><br>
Login: <sec:authentication property="principal.login" /><br>
Senha: <sec:authentication property="principal.senha" /><br>
E-mail: <sec:authentication property="principal.email" /><br>
<a href="j_spring_security_logout">Logout</a><br>
</sec:authorize>

O vídeo “Spring Security” apresenta como realizar o controle de sessões, trabalhar com HTTPS e
autenticação através de certificados digitais.

Conclusões

Com os exemplos demonstrados podemos ver alguns dos principais casos de uso de segurança em uma
aplicação web. O Spring Security centraliza as configurações em apenas um arquivo que será
empacotado juntamente com o war da aplicação, dispensando configurações externas do container. A
aplicação não fica apenas mais enxuta, como também mais extensível, como vimos através da interface
UserDetailsService e dos eventos de autenticação e autorização. Dessa forma é possível implementar
casos complexos de gerenciamento de usuários, de forma desacoplada do domínio de negócios da
aplicação.

Alguns assuntos não foram abordados no artigo por motivo de espaço, porém o leitor pode consultar a
documentação no site do framework.

Algumas funcionalidades oferecidas pelo Spring Security e não apresentadas são: segurança a nível de
métodos (utilizando anotações e AOP), suporte a HTTPS, controle de sessões concorrentes e diferentes
formas de autenticação, como certificados X509, LDAP, etc. Para mais informações sobre single sign on
consulte o quadro “Single sign on com CAS e OpenID”.

Single sign on com CAS e OpenID

Autenticação single sign on significa logar-se em uma aplicação e permanecer logado ao acessar outra
aplicação do mesmo grupo. Essa funcionalidade é comumente oferecida por containers para as aplicações
as quais ele gerencia. O Spring Security oferece suporte a single sign on para aplicações intranet através
do Central Authentication Service (CAS) e aplicações internet através do OpenID.
O CAS é uma aplicação web (arquivo WAR) que é responsável por apresentar as telas de login das
aplicações do mesmo grupo. Basicamente, todas as aplicações do grupo irão redirecionar o browser para
a aplicação central na hora da autenticação, e após o login, o CAS irá retornar a URL para a aplicação que
solicitou o login. Dessa forma, as configurações de autenticação ficam no WAR do CAS e nos demais
WARs o Spring Security é usado para realizar a integração.

Uma vantagem do CAS é o fato de existirem clientes para diversas linguagens como Java, .Net, PHP, Perl,
etc. permitindo integração entre aplicações heterogêneas.

O OpenID, por sua vez, elimina a necessidade de ter vários logins em diferentes sites na internet. Ao
criar um login em um dos providers de OpenID (veja Links) é possível utilizar o username e senha para
acessar todos os sites que o suportam, sem necessitar logar-se múltiplas vezes. Grandes empresas já
suportam o padrão como Sun, Google, IBM, Microsoft, etc. O Spring Security facilita o processo de
habilitar sua aplicação para suportar logins provenientes de providers OpenID.

Nota Devman - Wrapper

Um objeto wrapper é uma implementação do padrão de projeto Decorator. O objetivo deste desing
pattern é adicionar ou substituir o comportamento de classes existentes de forma dinâmica. Isto é feito
criando uma classe base, normalmente com o sufixo Wrapper ou Decorator, que implementa a interface a
ser decorada e também recebe como parâmetro no construtor um objeto da mesma interface. Em
seguida, todos os métodos da classe são implementados delegando para o objeto recebido no construtor.
Por fim, quando se deseja adicionar ou mudar o comportamento da interface, basta estender o Wrapper e
sobre-escrever os métodos desejados.

Links
www.springsource.org
Portal do Spring Framework e sub-projetos

static.springframework.org/spring-security/site/index.html
Acesso direto à home page do Spring Security

springide.org/blog
Download do Spring IDE (plugin para o Eclipse)

www.jasig.org/cas
Homepage do projeto CAS

openid.net
Homepage do OpenID

www.myopenid.com
Provider de logins OpenID

md5-hash-online.waraxe.us
Gerador online de hash md5

Saiba Mais
www.devmedia.com.br/cursos/listcurso.asp?curso=46
Curso Online - Trabalhando com Struts 2 em conjunto com Sitemesh, Spring e JPA

www.devmedia.com.br/cursos/listcurso.asp?curso=113
Curso Online - Java Web: Saiba como Desenvolver Aplicações utilizando Spring, Hibernate e JSF na
Prática

www.devmedia.com.br/articles/viewcomp.asp?comp=10225
Java Magazine 40 - Começando com Spring

www.devmedia.com.br/articles/viewcomp.asp?comp=8573
Java Magazine 45 - Indo além com Spring

www.devmedia.com.br/articles/viewcomp.asp?comp=10170
Java Magazine 46 - Integrando Spring com JSF e JDBC

www.devmedia.com.br/articles/viewcomp.asp?comp=8369
Java Magazine 50 - Spring com Struts e JPA

www.devmedia.com.br/articles/viewcomp.asp?comp=8647
Java Magazine 53 - Spring Remoting
www.devmedia.com.br/articles/viewcomp.asp?comp=10210
Java Magazine 60 - Enviando e-mails com Spring 2.5

www.devmedia.com.br/articles/viewcomp.asp?comp=11678
Java Magazine 65 - Spring e Java EE 6 estão na mesma direção?

www.devmedia.com.br/articles/viewcomp.asp?comp=11677
Java Magazine 65 - Criando uma aplicação web com Spring Framework
JavaFX Script

Osvaldo Pinali Doederlein

é Mestre em Engenharia de Software Orientado a Objetos e Arquiteto de Tecnologia da Visionnaire


Informática, trabalhando em projetos de software e prospecção tecnológica.

De que se trata o artigo:

Apresentamos uma referência da linguagem JavaFX Script, que faz parte da plataforma JavaFX.

Para que serve:

JavaFX é a nova plataforma RIA da Sun, compatível com Java SE e Java ME, já apresentadas em artigos
anteriores desta série. Alguns leitores podem encarar a exigência de aprender uma nova linguagem de
programação como um obstáculo, mas este aprendizado é facilitado pela semelhança entre JavaFX Script
e Java - tiramos proveito desta semelhança para explicar a nova linguagem de forma concisa. Mesmo
para quem já aprendeu JavaFX Script, o artigo serve como uma referência bem organizada e pragmática
(ao contrário da referência oficial, que tem uma estrutura mais formal, certamente mais detalhada, mas
não adequada à facilidade de consulta e sem pretensões didáticas).

Em que situação o tema é útil:

A linguagem JavaFX Script é um pré-requisito para desenvolver para a plataforma JavaFX, a qual
promete grande produtividade, mas (com qualquer plataforma) somente após vencer uma curva inicial
de aprendizado. Mesmo para quem não tiver grande interesse na JavaFX, o artigo apresenta muitas
inovações da JavaFX Script que são um aprendizado valioso para qualquer interessado em linguagens de
programação.

Na Edição 67 apresentamos a plataforma JavaFX, introduzindo-a de forma conceitual e dando alguns


“aperitivos” das suas funcionalidades e programação. Na Edição anterior (68), seguimos com um primeiro
tutorial de programação JavaFX, ensinando o básico da linguagem e framework de uma forma prática,
mas bastante informal.

Agora, encerrando esta cobertura inicial da JavaFX, vamos nos focar apenas na linguagem JavaFX Script,
cobrindo-a de uma forma mais formal e abrangente. (Para o leitor ainda pouco motivado a aprender
JavaFX Script, seria boa idéia começar pelo quadro “Por que uma nova linguagem?”.)

Não é meu objetivo repetir o documento de Referência da Linguagem de Programação JavaFX Script que
está disponível em openjfx.dev.java.net/langref. Ao invés disso, a idéia é explicar a linguagem de uma
forma concisa e pragmática, tendo como alvo não alguém que vai escrever um compilador ou outra
ferramenta que exige conhecimento formal da linguagem, mas um desenvolvedor que deseja vencer a
curva de aprendizado da sua sintaxe ou então ter uma referência de fácil consulta.

Ainda mais especificamente, escrevo pensando no programador que já conhece bem a linguagem Java, o
que permitirá resumir ou omitir explicações dos pontos onde ambas as linguagens forem iguais ou muito
parecidas (e são muitos pontos). Assim, podemos concentrar nosso tempo e neurônios nas partes que
são diferentes.

O artigo é organizado como uma referência da linguagem, agrupando suas funcionalidades em seções
como Valores, Operadores, Classes etc., de forma similar a uma referência formal. No entanto, fiz este
agrupamento de uma forma “relaxada” segundo critérios conceituais e não de gramática formal. Por
exemplo, a seção de Operadores não possui todos os operadores, pois alguns deles são cobertos em
outras seções (ex.: os diversos operadores específicos para sequences são mostrados na seção
Sequences).

Também não me preocupei em documentar minimamente alguns aspectos mais elementares da


linguagem que são exatamente idênticos a Java, por exemplo a sintaxe para números literais. A intenção
é ter um texto o mais compacto e didático possível, mas ainda assim, suficientemente organizado e
formal para servir como referência da linguagem.

No decorrer do artigo, uso o termo “JavaFX” no lugar de “JavaFX Script”, por simplicidade.

Tipos e Valores

O sistema de tipos da JavaFX é inspirado no Java, mas com melhorias importantes. Na Tabela 1,
classifiquei os tipos da JavaFX em cinco categorias e comparei-os aos tipos equivalentes em Java. Vamos
comentar apenas as novidades desta tabela.

Tabela 1: Classificações de tipos da JavaFX, comparativamente com Java.

Valores

A maior novidade é que o typesystem de JavaFX é OO-puro: não existem tipos primitivos. Por outro lado,
existem “tipos de valor”, que são aqueles cuja igualdade de referência é equivalente à igualdade de valor
(em outras palavras: x == y se e somente se x.equals(y)). Os tipos primitivos do Java, como int, são
tipos de valor, por isso não há dois valores 17 distintos, por exemplo. O mesmo vale para o Integer da
JavaFX. Além disso, os valores são imutáveis, e não admitem null.

A diferença entre esses tipos da JavaFX e os tipos primitivos do Java é que os tipos de valor são objetos.
Em JavaFX, todos os tipos, sem exceção, são derivados de Object.

Em termos de implementação, os valores de JavaFX são equivalentes aos primitivos de Java, evitando o
custo de desempenho de usar objetos. Por exemplo, se você olhar o arquivo .class gerado pelo javafxc
para uma função que recebe um parâmetro Integer, verá que o bytecode compilado irá conter um
método que recebe um int primitivo. Somente quando um valor é utilizado em alguma operação que
exige sua faceta OO, a JavaFX faz uma conversão automática para um objeto. Você pode considerar isso
uma versão melhorada do autoboxing de Java 5. As melhorias incluem a simplificação (não há tipos
paralelos como int/Integer) e desempenho (o compilador, runtime e frameworks usam várias técnicas
para representar valores na forma primitiva, e reduzir ao mínimo as conversões de/para a forma OO).
Strings

Menção honrosa vai para o tipo String da JavaFX Script, que ao contrário de Java, é definido como um
value type. Além disso, carrega um lote de funcionalidades extra:
•
O valor default de uma String não inicializada é "" (string vazia). É impossível ter uma string null; se você
fizer esta atribuição, fica com "". Nunca acontecerá uma NullPointerException envolvendo strings (ou
qualquer value type);
•
O operador == equivale a equals(), não há distinção entre igualdade e identidade (novamente, como
todos value types);
•
Pode-se mesclar strings com expressões, por exemplo: "Alô, {nome}!" será expandido conforme o valor
dinâmico da variável nome. Ou então, "Temperatura: {%2.2f temp}", a variável temp será formatada no
estilo de System.printf() (ex.: %2.2f 32.257 → 32,25);
•
Pode ser delimitada por aspas simples ou duplas, ex.: String com "aspas duplas" dentro;

Internacionalização é simples: def msg = ##"ERRO" irá atribuir a msg o valor da string associada à
chave ERRO no resource bundle da aplicação; se não existir este resource, o valor da string será "ERRO"
mesmo. Para separar a chave do valor default, use ##[ERRO]"Deu zebra!";
•
É o único tipo de JavaFX Script para manipular caracteres. Não existe um tipo específico para um
caractere único, como o char do Java. Conversões automáticas são feitas ao invocar classes/APIs de Java
que utilizam char; ex.: "Osvaldo".charAt(0) = "O".

Duration

O tipo de valor Duration representa uma quantidade de tempo. Exemplos de literais: 15ms (15
milissegundos), 2.3s (2.3 segundos), 55m (55 minutos), 10h (10 horas), 10h + 5m (10 horas e 5
minutos). Este tipo não passa de um açúcar sintático para um long contendo o tempo normalizado para
milissegundos, mas ao usar o framework de animação da JavaFX, você verá que é muito conveniente ter
uma sintaxe especial para isso.

Void

O Void do Java FX significa o mesmo que o do Java, mas é mais útil, ver seção Controle.

Referência

Os reference types da JavaFX também são familiares. Ver seções sobre funções e sequences.

Funções

JavaFX Script possui funções de primeira ordem. Exemplo:

var validador: function (o:Object): Boolean O tipo da variável validador é uma função que possui um
parâmetro do tipo Object e retorna um Boolean. Portanto, funções podem ser atribuídas a variáveis,
passadas como parâmetro, etc. como quaisquer outros tipos. A linguagem Java possui uma aproximação
desta facilidade, combinando interfaces com inner classes - mas o resultado é uma sintaxe confusa. Com
a versão muito melhorada de JavaFX, podemos escrever por exemplo um tratador de eventos assim:

Stage {
onClose: function() { FX.exit() }
}

Funções suportam os seguintes modificadores (antes do function):


•
Visibilidade (public, protected, package, e por default script-private);
abstract, igual a Java;
•
override, igual ao @Override do Java 5 (mas é mandatório);
bound, que examinaremos em Binding;
Não há nada equivalente ao final dos métodos Java.

Sequences

As sequences da JavaFX Script possuem sintaxe similar, mas significado e capacidades diferentes dos
arrays do Java. Em comum com os arrays, as sequences são coleções de objetos de um tipo homogêneo
e com tamanho fixo, indexadas a partir da base 0.

As diferenças começam com algumas sintaxes facilitadas (syntax sugar), mas vão além. A Tabela 2
exemplifica todas as capacidades e sintaxes específicas para sequences. Algumas coisas são simples - por
exemplo, o operador sizeof equivale ao pseudo-atributo length dos arrays do Java; e com o ‘..´ você
pode especificar ranges (intervalos), que podem ser usados tanto para determinar quais elementos da
sequence serão manipulados (como em delete dias[1 .. 2]), quanto para criar uma sequence contendo
todos os números inteiros do range (como em [1 .. 100]).
Tabela 2: Sintaxes de uso de sequences.

Uma característica importante das sequences é que elas não admitem aninhamento; qualquer tentativa
de colocar uma sequence dentro de outra irá gerar uma sequence única com todos os elementos.

Por exemplo, o resultado de [ dias, ["Qua, "Qui"] ] não é [ "Dom", "Seg", "Ter", ["Qua, "Qui"] ], e sim
[ "Dom", "Seg", "Ter", "Qua, "Qui" ]. Esta transformação é conhecida como flattening (achatamento), e
embora possa parecer uma limitação, é uma característica importante do estilo de programação da
linguagem. Este estilo incentiva o uso de expressões “fluentes” - onde o output de cada expressão é
usado como input da seguinte, algo similar ao uso de pipes em shell scripts. Para isso funcionar, é preciso
que todas as operações utilizem uma estrutura de dados padronizada; no Unix essa estrutura é texto
ASCII, em Java FX Script (e outras linguagens) é a lista ou sequence.

O leitor expert em outras linguagens que fazem uso pesado de listas/sequences, como as da família LISP,
sabe que tais linguagens permitem o uso de estruturas aninhadas. Mas JavaFX Script não pretende ser
uma linguagem de programação funcional sofisticada, para quem curte técnicas do tipo “map/reduce”; ao
invés disso, foi feita a opção por sequences com flattening automático, o que simplifica os cenários de
uso mais importantes para JavaFX, por exemplo a inicialização de estruturas complexas como o Scene
Graph.

Observe a tentativa da JavaFX Script de usar uma sintaxe SQL-like para a manipulação de dados:
sintaxes como insert..into, delete..from, o in e o where do for.

Num detalhe importante, o leitor pode estar confuso com minha afirmação que as sequences são
imutáveis, seguida por uma tabela que indica várias sintaxes para sua alteração. Explico: todas essas
sintaxes criam uma nova sequence. Por exemplo:

var dias = [ "Dom", "Seg", "Ter" ];


var diasUteis = dias;
delete "Dom" from diasUteis;
println(diasUteis); // ["Seg", "Ter" ]
println(dias); // [ "Dom", "Seg", "Ter" ]

No exemplo acima, após a atribuição diasUteis = dias, ambas variáveis apontam para a mesma
sequence. Mas em seguida, o delete cria uma nova sequence e a atribui à variável diasUteis; a sequence
original continua existindo pois ainda é referenciada por dias. Este truque sintático - operadores como
delete e insert, que executam uma atribuição ‘escondida´ - mantêm a sintaxe familiar para quem não
está acostumado ao estilo funcional (no qual nunca, ou raramente, se altera o valor de variáveis
preexistentes). Para mais detalhes, veja o quadro “Imperativo ou Funcional?”.

Declarações

Nesta seção veremos como JavaFX Script permite declarar variáveis e funções.

Na Tabela 3, podemos ver que as variáveis de JavaFX podem ser declaradas com var ou def. A diferença
é que o def é usado para constantes (similar às variáveis final do Java), enquanto var indica variáveis
propriamente ditas (i.e., cujo valor pode variar).
Tabela 3: Declarações de variáveis e funções.
As declarações estáticas de tipo são opcionais, mas isso não significa que JavaFX Script seja uma
linguagem dinamicamente tipada. Ao contrário, JavaFX é tão estaticamente tipada quanto Java; a
diferença é que contamos com inferência de tipos, ou seja, a capacidade do compilador de determinar
automaticamente o tipo. No exemplo var x = 5, o tipo de x é estabelecido como Integer, pois este é o
tipo do valor 5 com o qual a variável é inicializada. No caso específico das declarações def, como o valor
inicial é mandatório, a declaração de tipo jamais é necessária.

Também salta aos olhos que JavaFX Script utiliza o “estilo Pascal” de declarações de tipos, ou seja nome :
Tipo ao invés de Tipo nome como em Java. Apesar de parecer uma diferença gratuita e desagradável de
Java, você se acostumará e verá que existem bons motivos para isso (o mesmo vale para Scala, que -
não coincidentemente - também usa inferência de tipos).

A mesma sintaxe de declarações pode ser utilizada em três escopos distintos:


var x = 5;

function x1 () { return x; } // Retorna 5

class Y {

var x = 6;

function x2 () { return x; } // Retorna 6

function z () {

var x = 7;

def f = function x3 () { return x; } // Retorna 7

JavaFX suporta variáveis e funções locais, de instância, e globais. Esta última opção parece ser uma
novidade, mas de fato não é, pois ocupa o lugar dos static do Java. Se num arquivo Y.fx você tiver um
elemento global x, este será acessível externamente com a sintaxe Y.x, ou seja, igual às statics. Em
termos de compilação e desempenho, a equivalência também permanece: variáveis e funções globais são
transformadas pelo javafxc em variáveis e métodos static.

A inferência de tipos também funciona para funções, especialmente o tipo de retorno: raramente é
preciso especificá-lo explicitamente, pois ao encontrar por exemplo uma operação return a + b no corpo
da função, o javafxc determina que o tipo desta expressão é o tipo estático de retorno a função. Mas
alguns programadores podem preferir especificar o tipo de retorno, para ter certeza que este será um
tipo específico (neste caso, um return e onde e não possui exatamente o mesmo tipo declarado para
retorno, sofrerá uma conversão implícita se possível).

O compilador javafxc irá transformar as funções em métodos idênticos aos do Java, com uma
“assinatura” no bytecode que inclui os tipos dos parâmetros e do retorno, sendo que esta assinatura influi
na compilação e na compatibilidade binária de outras classes compiladas com invocações ao método
(função) em questão. Assim, as funções de JavaFX Script podem ser também invocadas a partir de
classes Java, de maneira direta e tipicamente sem nenhum overhead.

Os tipos dos parâmetros, surpreendentemente, também podem ser inferidos. Isso é possível se houver
pelo menos uma invocação à função no mesmo script, ou em atribuições ou inicializações para uma
propriedade tipada como função:

function teste (a, b) { a + b }


println(teste(10, 15));
class A { var b : function (a:String); }
A { b: function (x) { println(x.charAt(0)) } }

No primeiro exemplo, o compilador determina que os parâmetros a e b de teste() são do tipo Number,
devido à presença da invocação teste(10, 15). No segundo exemplo, ao compilar a última linha onde
instanciamos um objeto do tipo A, a function (x) {...} não exige declarar que x é uma String pois esta
função está sendo atribuída à propriedade A.b, e para esta atribuição ser válida, a função no lado direito
deve ter a mesma assinatura da propriedade no lado esquerdo.

Visibilidade

JavaFX suporta um conjunto completo de modificadores de visibilidade, de fato, mais completo que Java.
Veja a relação completa na Tabela 4. Mais explicações (sobre a necessidade dos novos níveis) na próxima
seção. Mas antes de chegar lá, podemos destacar um fato interessante: JavaFX não possui um nível de
visibilidade private. Você logo entenderá o motivo.
Propriedades

JavaFX Script utiliza o termo “propriedade” ao invés de atributo. Propriedades são atributos de classes,
mas com alguns recursos adicionais em comparação com os atributos do Java.

Tabela 4: Níveis de visibilidade.


A Tabela 5 resume a sintaxe, que complementamos com as seguintes explicações:
•
Não se usa getters ou setters. Propriedades são declaradas com visibilidade pública. Para
propriedades que você não deseja que possam ser modificadas arbitrariamente, basta usar os níveis
public-init (equivalente a um atributo do Java que só pode ser inicializado por um construtor) ou public-
read (equivalente a um atributo do Java que possui getter, mas não setter);
•
Não há construtores. Objetos são instanciados com uma sintaxe de objetos literais;
•
Triggers. As propriedades podem possuir triggers, que são funções invocadas automaticamente quando
o valor da função for modificado. Isso substitui a necessidade de setters para alterações não-triviais de
propriedades;
•
Binding. O recurso de binding (ver seção Binding) permite manter diversas propriedades associadas
entre si de forma automática.

Para quem vem de uma linguagem OO tradicional como C++ ou Java, poderão parecer “heréticas” idéias
como não encapsular atributos com getters e setters, ou não dispor de um nível de visibilidade private.
Mas analisando a linguagem como um todo, podemos entender seu design. A soma de recursos como
visibilidade mais refinada (especialmente public-init e public-read), instanciação “estilo JSON”, triggers e
binding, substitui 99% das necessidades de getters, setters e construtores. Ou seja, seus atributos (opa,
propriedades!) continuarão tão bem encapsulados quanto antes, só que com um código bem mais
simples - e, de quebra, algumas novas facilidades.

Quanto à visibilidade private, esta não existe de fato nem em Java - um membro private de uma classe
pode ser acessado por suas classes aninhadas ou inner classes. Classes aninhadas/inner costumam ser
fortemente acopladas à sua enclosing class, justificando o acesso privilegiado. Em JavaFX a lógica é
igual: todo o código contido por um script .fx é fortemente acoplado (se não for, divida-o em vários
scripts). JavaFX só torna explícito e homogêneo um design que, em Java, é acidental e imperfeito.
Acidental, pois não era assim no Java 1.0 - mudou no 1.1 quando as classes aninhadas/inner foram
criadas. Imperfeito, pois Java permite definir várias classes no mesmo arquivo .java (desde que só uma
seja pública), mas não há privilégios de acesso a membros privados entre estas classes - embora o
mesmo argumento de alto acoplamento seja válido.

Ilustrando de forma mais aplicada as sintaxes de propriedades e construção de objetos, apresento


novamente o “Alô Mundo” da JavaFX:
Stage {

title: "Aplicação Alô Mundo"

width: 250 height: 80

scene: Scene {

content: Text {

font: Font { size: 24 }

x: 10, y: 30

content: "Alô, JavaFX!"

onClose: function() { FX.exit() }

O código acima constrói a aplicação completa (ok, faltando só as declarações package e import). Este
código instancia (e retorna - ver seção Controle) um objeto Stage. Este objeto é inicializado com o valor
250 para a propriedade width, etc. No caso da propriedade scene, seu valor é um objeto Scene que é
inicializado da mesma forma, idem para a propriedade content de Scene. Finalmente, a propriedade
onClose tem o tipo function (): Void, sendo inicializada com uma função deste tipo.

Teoricamente, você poderia fazer o mesmo em Java, com expressões new aninhadas. Mas há três
problemas. Primeiro, os construtores do Java são rígidos - se um objeto com os atributos a, b, ca,b),
inicializar apenas a exige passar explicitamente o valor default de b, e inicializar c exige invocar setC()
após a construção (saindo além da inicialização hierárquica).

O segundo problema é que os frameworks Java não costumam trabalhar com “atributos funcionais” como
onClose, preferindo design patterns como Template Method (um método onClose() que pode ser
redefinido) ou Observer (um método como addCloseListener()); ambos suportam uma expressão única
de inicialização, mas ao custo de código bem mais confuso devido à horrível sintaxe das inner classes.
Terceiro, a invocação de construtores não indica o nome dos argumentos, de forma que expressões de
construção longas e/ou aninhadas tendem a ficar difíceis de ler se você não souber de cor qual
argumento que vai em cada posição (ou se isso não for evidente pelos valores passados).

Alguns frameworks Java modernos adotam o pattern de “APIs fluentes” no qual métodos terminam com
um return this, o que permite encadear expressões como: new X(a).setB(b).setC(c), etc. Um método
addXxxListener() também poderia seguir este design. Mas o resultado, mais uma vez, é bem pouco
elegante. Na minha opinião, é uma tentativa pobre de imitar a linguagem Smalltak, na qual o estilo
“fluente” é natural devido à sua sintaxe de mensagens (não há parâmetros posicionais) e ao fato de
todos os métodos retornarem this por default (não existe o tipo void). E como, na definição do Java SE 7,
os conservadores venceram a briga e ficaremos sem closures, o Java continua condenado a gambiarras
pavorosas (ainda que sejam melhor-que-nada) como “APIs fluentes”.

Por que tudo isso é importante - não é muito oba-oba em cima de uma economia de algumas linhas de
código para inicializar objetos complexos? Acontece que esta facilidade sintática possui outros “efeitos
colaterais” positivos. Por exemplo, você pode usar a sintaxe de notação de objetos literais do JavaFX para
muitas tarefas onde tipicamente usaria XML, resultando num código que é mais enxuto e legível, fácil de
emitir e interpretar (como sabe qualquer programador JavaScript que prefere JSON a XML). Um exemplo
concreto disso é o formato gráfico FXD (ver Edição 67), nada mais que código JavaFX com inicializações
de classes gráficas. Outro exemplo é a construção da árvore de componentes e eventos da GUI, ilustrada
acima: este código, além de enxuto e legível, é ideal para manipulação de ferramentas. Por tudo isso,
JavaFX dispensa a necessidade de linguagens de descrição de GUI como XAML (Silverlight), MXML (Flex),
XUL (Mozilla), “layouts” do Android, etc.

A falta de algo equivalente (uma DSL para GUI & gráficos) não é uma lacuna de JavaFX, pelo contrário,
sua presença em plataformas competidoras revela inadequações das linguagens de programação
principais destas plataformas. Na JavaFX, o FXD é inclusive bom o suficiente para substituir o formato
SVG de gráficos vetoriais.

Binding

Binding é uma das características mais “famosas” de JavaFX Script, pois além de ser um poderoso
recurso de programação em geral, é uma peça essencial para os frameworks e o estilo de programação
da plataforma JavaFX.

Se você (como eu) começou aprendendo sobre binding examinando os fontes de diversos demos que só
utilizam as duas variantes mais simples deste recurso (bind x = y e criação de objetos com binding
igualmente simples em propriedades), poderá se surpreender com a Tabela 6, que mostra uma
sofisticação extraordinária. Vamos demonstrar esta capacidade com um exemplo que explora uma das
sintaxes complexas, combinado com triggers para logar o que acontece:

var dias = [ "Dom", "Seg", "Ter" ]


on replace d[i..j] = nd { println("{d[i..j]} = {nd}") }

def iniciaisDias = bind for (d in dias) d.charAt(0);

dias[1..2] = [ "TER", "QUA" ];

Executando o código acima, o output será o seguinte:


-> DomSegTer

-> DST

SegTer -> TERQUA

DST -> DTT

DTT -> DTQ

As duas primeiras linhas são efeito das atribuições dos valores iniciais a dias, e por conseqüência do
binding, a iniciaisDias - pois a trigger é disparada inclusive para a atribuição inicial. (Note que ao exibir
sequences com o recurso de mesclagem de strings, os elementos são concatenados.) Na terceira linha,
que é o que mais nos interessa, “SegTer -> TERQUA” vemos que o binding de iniciaisDias foi acionado
pela atribuição a dias; sendo que esta atribuição modificou apenas dois dos seus elementos. Na quarta e
quinta linhas, vemos que o for (d in dias) é reexecutado somente para estes elementos (dias[1..2]), e as
alterações de iniciaisDias também são feitas de maneira incremental: repare que são geradas duas
alterações independentes para os elementos de índice 1 ("S"®"T") e 2 ("T"®"Q"), ao invés de uma
alteração única que causaria um log "DST -> DTQ".

Podemos concluir várias coisas. Primeiro, como já disse, o binding do Java vai bem além daqueles casos
simplórios que você viu em javafx.com/samples; é um recurso que pode ser explorado de inúmeras
formas. Segundo, as capacidades das sequences do Java são ainda mais incrementadas pelo tratamento
especial de outros recursos da linguagem, como triggers e binding - de uma forma geral, a linguagem
tenta otimizar o esforço destas operações tornando-as o mais incrementais possíveis (se você tem uma
sequence de 1000 elementos e altera apenas um elemento, isso não irá gerar execuções de triggers ou
bindings considerando todos os 1000 elementos).

Como nada é perfeito, um alerta: binding é pesado, especialmente devido às capacidades de avaliação
incremental de sequences e expressões complexas.

Para cada variável sujeita a binding e cada expressão bind, o runtime é obrigado a manter na memória
estruturas de dados relativamente grandes. É por isso que você pode encontrar na internet algumas
pessoas comentando que ficaram horrorizadas ao ver que uma variável Boolean ocupava 100 bytes ou
mais. Use este recurso apenas quando necessário (especialmente na JavaFX Mobile).

Operadores

Já apresentamos alguns operadores da JavaFX especializados em sequences; veremos agora os


operadores mais convencionais para manipulação de valores em geral.

A Tabela 7 revela algumas das minhas poucas discordâncias do design de JavaFX Script. Alguns
operadores desviam de Java, a meu ver gratuitamente: por que, por exemplo, and ao invés de &&? O
argumento é que operadores nomeados são mais “simples” que símbolos misteriosos como &&, mas não
engulo isso, lembrando que linguagens RIA competidoras, como JavaScript e ActionScript, utilizam os
operadores simbólicos tradicionais da família C.

Os operadores simbólicos são mais legíveis pois se destacam de nomes de variáveis e funções; ao ler um
código como aa || bb && cc, a distinção entre operadores/operandos é imediatamente clara, o que não
ocorre para aa or bb and cc - que exige um “parsing mental” para reconhecer or e and como operadores
e as demais palavras como identificadores de variáveis.

Pior que isso, vejo algumas inconsistências: a exclamação ! foi substituída por not como operador de
negação, mas no operador diferente-de !=, continuamos vendo a exclamação indicando negação. Mais: o
‘|´ de [a | a > 5] e o where de for (a in b where a > 5) têm exatamente o mesmo significado... nada é
perfeito, e na minha opinião, a sintaxe de operadores não-simbólicos é um lugar onde o design de JavaFX
Script derrapou.

Outras divergências me parecem OK. O operador de typecast as é mais elegante que o do Java, pois
sendo posfixado, é coerente com a ordem de avaliação, ex.: dVar * 2.0 as Integer, temos primeiro uma
multiplicação entre variáveis Double, depois sua conversão para Integer, e observe também que não
precisamos usar parênteses - (dVar * 2.0) as Integer - pois a precedência do as é mais fraca (i.e., o
typecast é avaliado depois da maioria dos outros operadores). E o sizeof é um conceito bastante
interessante, coerente com o design orientado a expressões de JavaFX Script.

JavaFX não possui operadores de manipulação de bits, como |, &, ^, ~, <<, >> e >>> do Java. O
veterano de C/C++/Java dentro de mim também não gostou disso à primeira vista, mas acabei
entendendo, pois estes operadores são de uso muito raro na enorme maioria das aplicações, e JavaFX
Script é uma linguagem dedicada à camada de aplicação - não se supõe que alguém vá usá-la para
“escovações de bits” como algoritmos de compressão ou criptografia.

Principalmente lembrando que JavaFX é uma linguagem “parceira” de Java: é trivial invocar métodos de
casses Java a partir de JavaFX e vice-versa, misturar ambas classes no mesmo projeto/IDE etc.

Além disso, JavaFX reserva os tokens << e >> para outro propósito: escape de literais externas. Por
exemplo, se você tiver que invocar um método bind() de uma classe Java, pode fazê-lo com
obj.<>(argumentos). Sem o escape isso seria ilegal por que bind é uma palavra-chave em JavaFX. Java
7 também terá um mecanismo de escape similar.
Controle

JavaFX Script, como qualquer linguagem de programação, precisa de estruturas de controle como
decisões e loops. Você poderia imaginar que pelo menos nesta área, a linguagem seria praticamente igual
a Java. Engano, também aqui JavaFX apresenta inovações poderosas, muito embora mantenha uma boa
similaridade sintática com Java. Veja a Tabela 8.

Tabela 5: Estruturas de controle.


Começaremos pela novidade conceitual: JavaFX Script é uma linguagem orientada a expressões. Não
existe a dualidade entre “statements” (que não geram nenhum valor, como o if do Java) e “expressões”
(que retornam um valor, como x + y). Porém, nos casos onde não há nenhum valor que faça sentido
associar a determinada expressão, seu tipo é definido como Void.

Isso pode parecer malandragem, qual é a diferença entre um statement e uma expressão Void? A maior
diferença é técnica: a gramática da linguagem fica mais simples e unificada, o único efeito do Void é não
permitir o uso de determinada expressão no lado direito de lugares que exijam um valor, como uma
atribuição. Porém, podemos ver que a linguagem faz um grande esforço para que quase tudo seja uma
expressão normal (não-Void). Notavelmente, as estruturas de controle if e for são expressões que
retornam valores. Vemos também que num bloco de código, o return é geralmente desnecessário para
retornos com valor - basta terminar o bloco com um valor, como:

def function par (n : Number) { n mod 2 == 0 }

O bloco {} em si é uma expressão que possui valor, o qual pode ter tipo Void (se termina por um return
sem valor ou outra expressão de tipo Void) ou outro tipo qualquer.

E não, a Tabela 8 não está incompleta: Veja o quadro “Cadê o switch?”.

Cadê o switch?

Se o leitor viu a Tabela 8 com as estruturas de controle de JavaFX e estiver perguntando “onde está o
switch?”, a resposta simples é: não tem. Mas a resposta mais longa é... tem algo parecido, ou poderá ter
logo.

Muitos designers de linguagens OO apontam que a estrutura de controle switch é anti-OO pois muitas
vezes pode ser substituída por polimorfismo ou outras técnicas. Em JavaFX, tiveram a coragem de deixar
o switch de fora. Isso significa que você tem que usar ou polirmorfismo, ou cascatas de ifs, onde
normalmente usaria um switch.

Mas esta parte do design da linguagem me parece incipiente pois, para realmente não sentirmos falta do
switch, precisamos de outros mecanismos (o polimorfismo nem sempre é adequado). JavaFX já possui
parte da solução (funções de primeira ordem) - é melhor ilustrar com um exemplo prático:

[
function() { println("Case 0"); }
function() { println("Case 1"); }
function() { println("Case 2"); }
function() { println("Case 3"); }
][valor]();

Ao invés de case, usamos uma sequence contendo uma função com o código correspondente para o case
do seu índice. Ao invés de switch(valor), usamos o valor para indexar a sequence, obtendo e executando
a função que contém o código deste “caso”. Note que os “casos” poderiam ter parâmetros e retornar
valores (desde que todos tenham a mesma assinatura: isso será verificado pelo compilador!), o que já
tornaria esta técnica mais poderosa que o switch/case do Java.

Observe mais uma vez a inferência de tipos de JavaFX: no exemplo, o tipo da sequence é function()
[].Digamos que você edite apenas um dos “casos” e coloque um parâmetro Integer na função; então o
tipo inferido será o tipo mais elementar compatível com todos os casos - no caso, um tipo Function
genérico (herdado por todos os tipos de função). Mas se você utilizar a sequence como no exemplo,
indexando-a e executando a invocação com (), o compilador exige que todas as funções possuam
assinatura compatível com a da invocação - no caso, nenhum parâmetro - e reclamará de qualquer
“caso” que seja diferente disso.

O maior problema é que nem todos os switch/case possuem um domínio de valores como 0, 1, 2..., que
se prestem ao mapeamento para índices de sequence. O ideal seria então usar mapas (sequences
associativas), o que permitiria criar uma estrutura como [ "Ouros": function() { println("Ouros"} ], etc.;
isso também permitiria o uso de objetos arbitrários, não só números, como chave. Infelizmente JavaFX
ainda não possui nenhum suporte nativo a estruturas de dado deste tipo - pode-se usar as do Java como
java.util.HashMap, mas isso não teria facilidades como uma sintaxe especializada de inicialização e
indexação.

Uma versão futura da linguagem possuirá este suporte a mapas, provavelmente a JavaFX 2.0 (ver JFXC-
642 no JIRA). Mas também seria bom permitir a declaração de funções triviais ainda mais simples (sem o
function()), e mesmo assim, faltaria o default.

Há linguagens, como Smalltalk, que sempre se viraram muito bem sem o switch. Na pior hipótese você
pode simplesmente usar ifs em cascata, o que em JavaFX talvez não seja ruim, lembrando que if é uma
expressão e você pode escrever código como:

def sobrenome =
if (nome == "Osvaldo")
"Doederlein"
else if (nome == "Eduardo")
"Spínola"
else
"Silva";

… que é mais enxuto que, com um switch, ter que fazer uma atribuição separada para sobrenome em
o
cada case. (Para um exemplo tão trivial o Java permitiria usar o ternário ?:, mas não se os cases
tivessem que executar algum statement antes do valor retornado.)

Mas esta solução ainda incomoda um pouco: esteticamente, o primeiro ”caso” (if) não tem um cabeçalho
idêntico aos demais (else if); e a repetição de "nome ==" também me incomoda um pouco, ainda que
seja mais legível e genérica que os cases. Eficiência não é um problema pois o compilador poderia
reconhecer uma cascata de ifs com estrutura similar a um switch, e gerar código idêntico (com bytecodes
tableswitch ou lookupswitch).

Generators

Uma vantagem do design orientado a expressões de JavaFX, e seu uso de tipos de dados de alto nível
como sequences e estruturas de controle avançadas como generators (for), é que o javafxc tem
oportunidade para fazer otimizações importantes. Por exemplo, ao conhecer o for da JavaFX Script, você
talvez tenha se horrorizado ao imaginar que este sempre irá gerar uma sequence com os valores
produzidos por cada iteração do loop. Mas não é exatamente assim; o compilador só faz isso se
necessário. Assim, no código

def x = [1, 2, 3]
def y = for (n in x) n * 2

a sequence será de fato gerada, no caso [2, 4, 6], e atribuída a y. Porém, neste outro código:
…

def x = [1, 2, 3]
for (n in x) n * 2

…for só irá avaliar a expressão n * 2 para cada n in x, mas nenhuma sequence será criada, pois o “valor
o
de retorno” do for não está sendo utilizado de qualquer forma (como termo de outra expressão, lado-
direito de atribuição, etc.).

Classes

JavaFX é uma linguagem OO baseada em classes, com sintaxe básica parecida com Java, mas com pelo
menos um grande desvio do design de Java.

Como JavaFX faz isso? Se você observar os arquivos gerados pelo javafxc, verá que uma classe C da
JavaFX gera um par de .class, que se você descompilar gerando arquivos .java (ou examinar com o
javap), verá que correspondem a uma interface C.Intf e uma classe C que implementa esta interface.
Junto com alguns outros truques de compilação, isso permite integrar o paradigma de herança múltipla
generalizada da JavaFX Script ao modelo OO do bytecode / JVM, que suporta herança múltipla apenas
para interfaces com métodos abstratos.

Para saber mais sobre a linguagem acesse a Apresentação “JavaFX Script - Referência Rápida”.

A Tabela 9 resume a sintaxe das classes de JavaFX. Não existem interfaces, apenas classes, e a herança
múltipla é suportada para tudo (funções abstratas e concretas, e até mesmo propriedades). Classes da
JavaFX podem inclusive herdar qualquer coisa de Java (tanto classes quanto interfaces). Ou seja, JavaFX
Script unifica nossas class e interface numa única entidade, também não exigindo distinguir entre
extends e implements.
Tabela 6: Classes
Por que uma nova linguagem?

Ao serem apresentados à JavaFX, muitos desenvolvedores têm a seguinte reação: aprovam as


funcionalidades, o framework, mas... isso precisava de uma nova linguagem? Não daria para suportar
todos os recursos da JavaFX com APIs orientadas à linguagem Java, ou talvez, uma sintaxe “Java
estendida” (mantendo compatibilidade retroativa) ao invés de inventar outra linguagem separada?
A resposta curta: Sim, seria possível fazer tudo com Java ou com uma extensão de Java... porém, não
seria uma boa idéia. Vamos explicar os porquês - a “resposta longa”.

Produtividade Competitiva

Todas as plataformas RIA competidoras utilizam alguma linguagem de programação considerada de “alta
produtividade” - pelo menos segundo alguns critérios, como alta densidade de código (mais
funcionalidade com menos linhas de código) e facilidade de prototipação. As competidoras incluem
JavaScript, ActionScript, e linguagens XML como XAML e MXML (para estas últimas, a vantagem é o uso
de ferramentas visuais).

JavaFX Script possui diversas características de alta produtividade, como: inferência de tipos, binding,
sequences, inicialização declarativa de estruturas hierárquicas de objetos, sintaxes de alto nível para
diversas necessidades de programação geral (desde o simples relaxamento na pontuação até o for
“turbinado”), e sintaxes especiais para algumas APIs.

Agilidade

Quando se fala em evolução da linguagem Java, não podemos nos esquecer que a Sun não é dona do
Java. A especificação da linguagem, bem como de todas APIs formais, é controlada pelo Java Community
Process (JCP). Embora a Sun mantenha certas prerrogativas e um alto grau de influência no JCP, isso não
inclui carta branca para fazer o que quiser com a plataforma. Em especial, a evolução de especificações
preexistentes é sempre “torturante” pois precisa levar em conta os interesses de diversos players com
voz no JCP, e possivelmente grande investimento na tecnologia em questão. A quebra de compatibilidade
retroativa é virtualmente impossível.

A Sun chegou atrasada à briga do RIA. Se fosse seguir o caminho de estender a linguagem Java (mesmo
que isso fosse tecnicamente uma boa idéia), ou criar a JavaFX sob os auspícios do JCP, o lançamento da
JavaFX 1.0 levaria pelo menos o dobro do tempo, e a sua evolução até a JavaFX 2.0 levaria 3-4 anos ao
invés de um ano. Isso liquidaria qualquer chance da JavaFX de competir. Órgãos de padrões como o JCP
são excelentes para tecnologias já razoavelmente maduras, mas são quase sempre péssimos para criar
coisas completamente novas - a inovação raramente acontece em comitês.

Imperativo ou Funcional?

Na minha mania de aprender novas linguagens mexendo em código dos outros, cheguei ao seguinte
trecho de um demo (javafx.com/samples/SmokeParticles/):

var parts: Particle[];


…
function update() : Void {
insert Particle {
x : 84 y : 164 timer : 100 acc : bind acc
vx : 0.3 * random.nextGaussian()
vy : 0.3 * random.nextGaussian() - 1
} into parts;
var i = sizeof parts - 1;
while( i >= 0 ) {
parts[i].update();
if( parts[i].isdead())
delete parts[i.intValue()];
i--;
}
}

Seu funcionamento é o seguinte: primeiro um novo objeto Particle é criado e inserido ao final da
sequence parts; depois, um loop varre todos os elementos que já estavam na sequence, invocando os
métodos update() e isdead(), e deletando da sequence aqueles que estão “mortos”. Mas achei esse
código feio, confuso, então resolvi reescrevê-lo:

function update() : Void {

parts = [

for (p in parts where not p.update()) p

Particle {

x : 84 y : 164 timer : 100 acc : bind acc

vx : 0.3 * random.nextGaussian()

vy : 0.3 * random.nextGaussian() - 1
}

Esta nova versão faz uma substituição única da sequence antiga pela nova, a qual é criada da seguinte
forma: primeiro temos um forisdead() a update()), e depois temos a nova Particle.

O ‘[...]´ mais externo serve concatena a sequence gerada pelo for com o objeto final, sendo que graças
ao flattening automático, o resultado será uma sequence simples com todos estes elementos.

A nova versão é característica do estilo funcional, evitando alterações repetitivas como insert e delete.
Sobrou uma alteração de variável (parts = ...), mas é uma só, e mesmo esta atribuição só restou por que
eu não quis reescrever o programa todo. É um código bem mais “limpo”, menor (10 linhas ao invés de
14) e mais simples (sua complexidade ciclomática - quantidade e aninhamento de estruturas de controle
- é menor.)

Mais importante, a versão funcional é idêntica a uma descrição formal / matemática do algoritmo, que
podemos enunciar como: “o novo conjunto de partículas é formado pelas partículas preexistentes que
após o update() continuem vivas, mais uma partícula nova”. É por isso que tanta gente gosta de
programação funcional: é o estilo de programação que permite traduzir, de forma direta, algoritmos
especificados de forma matematicamente rigorosa (que é quase sempre a descrição mais enxuta e
elegante possível).

Bem, agora as más notícias. Testando meu novo código, este funcionou, mas com um desempenho muito
pior. O problema é que a sequence parts é amarrada, via binding, ao scene graph da animação do
programa; porém, o binding de sequences não é otimizado para o cenário de substituição total de uma
sequence (com uma atribuição explícita), apesar de ser otimizado para manipulações pontuais como
insert e delete

A otimização em questão evita que o scene graph inteiro seja reconstruído em cada frame da animação,
o que resulta em péssimo desempenho. Ou seja, não se trata de um bug/limitação de desempenho das
sequences propriamente ditas, mas somente da combinação entre sequences e binding, ou talvez, do
runtime do scene graph. Registrei um novo bug descrevendo o problema (javafx-
jira.kenai.com/browse/JFXC-2911). É o tipo de coisa que, infelizmente, podemos esperar de um software
complexo como a JavaFX que ainda é praticamente recém-lançado no momento em que escrevo. Mas não
tira o mérito do estilo de programação funcional ou do design de sequences (o JIRA da JavaFX registra
uma grande quantidade de bugs similares, a maioria corrigidos antes mesmo do release 1.0 - mas o
trabalho obviamente ainda não terminou).

Conclusões

Este artigo encerra uma primeira “trilogia” de JavaFX, na qual cobrimos os aspectos principais desta nova
plataforma: tecnologia RIA, mobilidade, linguagem de programação, pontos principais do framework. A
partir deste ponto, minha expectativa é que um leitor que sabe programar Java e tem interesse pela
plataforma JavaFX possa andar com seus próprios pés, chegando ao ponto de desenvolver aplicações RIA
sofisticadas para desktop e dispositivos móveis.

Há duas partes da JavaFX que cobrimos de forma superficial: os recursos de mídia (ver Edição 67) e as
APIs javafx.* (ver Edição 68). Estes temas renderiam, seguramente, pelo menos mais dois capítulos da
série. Mesmo na linguagem JavaFX Script, coberta neste artigo, existem tópicos específicos que
poderíamos explorar, como a integração com Java, ou o desempenho.

Mas preferi parar por aqui, por três motivos. Primeiro, a plataforma JavaFX ainda é muito recente e
ainda é difícil avaliar o interesse dos leitores por uma cobertura tão contínua e abrangente. Segundo, o
mundo não parou por causa da JavaFX, e há outros temas que pretendo cobrir nas próximas edições.
Terceiro, a JavaFX ainda está em rápida evolução: no caso específico dos frameworks, não vou gastar
nosso tempo (meu e dos leitores) com um “artigão” sobre os frameworks da JavaFX 1.1, quando sei que
já em junho deste ano (quando nossa próxima Edição já estaria nas bancas) a Sun terá lançado a JavaFX
1.5, com muitas novidades especialmente nas APIs. Resumindo, espero que o leitor tenha apreciado
estes primeiros artigos, tanto quanto eu apreciei escrevê-los... chegou a hora de fazer uma pausa, mas
com planos de voltar à JavaFX daqui a algumas edições, com as baterias recarregadas.
Saiba Mais

http://www.devmedia.com.br/resumo/default.asp?idrev=123#1515
Java Magazine 67 - JavaFX: RIA a todo vapor

http://www.devmedia.com.br/resumo/default.asp?idrev=130#1590
Java Magazine 68 - JavaFX: Tutorial
Novos tempos: javax.time

Daniel Cicero Amadei

Bacharel em Sistemas de Informação pelo Mackenzie e pós-graduado pela Fundação Vanzolini. Trabalha
com Java desde 1999 e possui as certificações SCJP, SCWCD, SCBCD, SCDJWS, SCEA, BEA Certified
Developer: Integration Solutions e BEA Certified SOA Architect. Já atuou como Desenvolvedor, Analista e
Arquiteto de Sistemas e atualmente é Consultor em Arquitetura Oracle | BEA. Blog:
http://www.amadei.com.br

Michael Nascimento Santos

É um dos spec-leads da JSR-310 e expert em outras cinco JSRs. Ganhou um JavaOne Rock Star Speaker
Award pela sua palestra sobre a JSR-310 em 2008 no JavaOne. Atua como Senior Technical Consultant na
Summa Technologies do Brasil.

De que se trata o artigo:

O artigo aborda o resultado da JSR-310, a JSR que está definindo a nova API de data e hora que será
incorporada na plataforma Java. A arquitetura e as principais classes e interfaces da API são discutidas e
exemplificadas. Além disso, são abordadas as lacunas presentes nas classes atuais (Date e Calendar) e
como a javax.time endereça tais problemas.

Para que serve:

Apresentar a nova API de datas e horas que será incorporada na plataforma Java, provendo discussões
sobre a sua arquitetura e exemplos de uso da API, além de comparações com as classes atuais.

Em que situação o tema é útil:

O tema é importante para qualquer um que deseje estar atualizado sobre as novas tendências da
plataforma Java. A manipulação de datas e horas é peça-chave em qualquer implementação e a criação
de uma nova API tende a facilitar muito a vida do desenvolvedor.

Novos tempos: javax.time:

O artigo aborda a nova API para representação de datas e horas da plataforma Java e como ela facilita a
vida do desenvolvedor Java, corrigindo bugs e endereçando problemas das classes atuais Date e
Calendar. A nova API possui representações distintas para datas que serão utilizadas para cálculos
computacionais e datas que serão utilizadas por seres humanos. Além disso, temos representações
padrão para tipos de dados que atualmente precisamos criar como durações, períodos e intervalos.

Neste artigo conheceremos a nova API de data e hora que está sendo elaborada para a plataforma Java
SE 7. O foco da nova API é resolver vários problemas que afetam os desenvolvedores Java há anos,
presentes nas classes java.util.Date e java.util.Calendar.
Por que uma nova API?

A arquitetura elaborada para as classes Date e Calendar, ambas do pacote java.util é bastante
questionável. Veremos algumas dessas decisões arquiteturais dúbias, além de “bugs” da API atual, no
decorrer do artigo.

Outro grande problema é a falta de tipos para representar unidades comuns no dia-a-dia de qualquer
desenvolvedor como, por exemplo, períodos, instantes, durações, entre outros. Sem classes que
representem essas unidades do “mundo real”, é necessária a criação de soluções paliativas por conta e
risco dos desenvolvedores. Isso resulta em mais codificação e código legado para manutenção.

Esses pontos claramente abrem espaço a uma nova abordagem para o tratamento de datas e horas
dentro da plataforma Java.

Alguns dos problemas apresentados por Date e Calendar

Um dos principais problemas das classes atuais é a inconsistência. A classe java.util.Date não representa
uma data, mas sim um instante na linha do tempo. Desta forma, não temos como representar somente
uma data ou somente o tempo. Outro exemplo de inconsistência é o fato da classe Date utilizar anos com
base em 1900, enquanto a Calendar requerer os anos completos, incluindo o século. O código
apresentado a seguir, apesar do uso de um construtor descontinuado (deprecated), ilustra a criação de
uma data cujo ano será 3909, ao contrário do que parece ao ler o código.

Date date = new Date(2009, 2, 1);

O início dos meses em 0 (Zero) na classe Calendar também é algo que tem atrapalhado a vida dos
desenvolvedores Java de forma considerável. Por exemplo, para criar uma instância de Calendar que
represente a data 01/01/2009, temos que escrever o seguinte código:

Calendar calendar = new GregorianCalendar(2009, 0, 1);

Além disso, temos uma série de outros problemas arquiteturais ou de implementação:

Os objetos são mutáveis, inseguros em ambiente multi-thread, requerendo sincronização;


Não há como formatar uma instância de Calendar usando as classes do pacote java.text;
Não existe opção para representar períodos ou durações de forma padronizada;

As APIs atuais exigem muito código para tarefas rotineiras de manipulação de datas e horas. Sem
mencionar ainda que muitas dessas “tarefas rotineiras” constituem casos de uso comuns e poderiam ser
implementados pelas próprias APIs.

Esses são apenas alguns dos problemas que a JSR 310 irá resolver. Os já velhos conhecidos problemas
devido às alterações no horário de verão também estão sendo endereçados. Além de tudo isso, você verá
que a API é muito mais intuitiva e irá lhe poupar muitas e muitas linhas de codificação.

Princípios de Design da JSR 310

A JSR 310 tem sido elaborada com alguns princípios para facilitar seu uso e entendimento e tornar seu
código mais robusto e de fácil leitura.

Baseada em padrões

A nova API está sendo construída totalmente alinhada com o ISO-8601, um padrão internacional para
representação de datas e horas.

Imutável

Os principais objetos da nova API serão imutáveis. Não será possível alterá-los após a construção.

Também serão thread-safe, podendo até mesmo ser singletons.


A seguir, um primeiro exemplo, da criação de uma data (sem a hora!):

LocalDate date = LocalDate.date(2009, 1, 1);

Nesse caso, criamos uma data com a classe javax.time.calendar.LocalDate. Essa classe representa uma
data sem fuso horário (time zone) no padrão ISO-8601, resultando em 2009-01-01. Caso desejemos
alterar o ano dessa data, por exemplo, para 2010 não iremos utilizar um método set(). Neste caso,
devemos utilizar um método with(), que cria uma nova instância da classe com o ano desejado. O código
ficaria conforme apresentado a seguir:

date = date.withYear(2010);

Repare na atribuição ao objeto date com o resultado da invocação do método.


Isso é necessário, pois é criado um novo objeto, mantendo o original inalterado, pois os
objetos envolvidos são imutáveis.

Interfaces Fluentes

As classes e interfaces seguem o padrão de fluência para permitir uma melhor legibilidade do código. Isso
transforma as invocações a mais do que um método em sentenças de fácil leitura. O código apresentado
a seguir, além do ano, altera o mês e o dia.

date = date.withYear(2010).

withMonthOfYear(3).withDayOfMonth(2);

Clareza

Os métodos são bem definidos e indicam claramente seu propósito. Por exemplo, para subtrair dois anos
de uma data qualquer, utilizamos o seguinte código:

date = date.withYear(2010).

withMonthOfYear(3).

withDayOfMonth(2);

date = date.minusYears(2);

Isso irá resultar na data 2008-03-02. Muito mais claro do que o método roll(int field, int amount) da
classe Calendar.

Extensível

Através de pontos de extensão, utilizando conceitos do design pattern Strategy, é possível controlar e
customizar como a API se comporta em relação às datas e horas. Mesmo assim, não é necessário ser um
especialista para usar a API. São fornecidas implementações padrão para a maioria dos cenários.

Um exemplo desses “ganchos” para extensibilidade é a interface javax.time.calendar.DateAdjuster, capaz


de realizar ajustes em uma data de forma padronizada. Você simplesmente implementa essa interface e
a utiliza para ajustar suas datas de forma padronizada e bem definida.

Além disso, a javax.time traz consigo uma classe utilitária, a javax.time.calendar.DateAdjusters, contendo
implementações dos casos de uso mais comuns para o ajuste de datas. Essas implementações devem
satisfazer a maioria das necessidades de desenvolvimento. O código apresentado a seguir ilustra o ajuste
sendo realizado em uma data:

LocalDate date = Clock.systemDefaultZone().today();

date = date.with(DateAdjusters.

next(DayOfWeek.MONDAY));

O código que apresentamos, além do ajuste da data, apresenta uma nova classe:
javax.time.calendar.Clock. Essa classe é um façade para o acesso à data e hora correntes. O método
today(), que invocamos na classe Clock, retorna a data corrente como uma instância de LocalDate.

Voltando a falar sobre o ajuste da data, ele ocorre na invocação ao método with(). Informamos
DateAdjusters.next(DayOfWeek.MONDAY) como parâmetro do método. O objeto retornado pelo método
next() é responsável por ajustar a data para a segunda-feira subsequente à data informada. Avaliando as
datas, caso executemos tal código no dia 05/02/2009, após o ajuste, a nova data (lembre-se da
imutabilidade!) será dia 09/02/2009.
Ainda no âmbito dos Adjusters, outro exemplo muito interessante de sua aplicabilidade seria o ajuste das
datas para dias úteis. Você pode, ainda, implementar seu próprio adjuster - digamos, para saltar feriados
obtidos de um cadastro de feriados do seu sistema.

Duas escalas para lidar com tempo

A javax.time apresenta duas formas distintas para lidar com o tempo. Uma escala voltada para
máquinas, denominada Continuous e a outra com foco em datas para seres humanos, denominada
Human.

Continuous

Essa abordagem da javax.time é voltada para máquinas e representa o tempo na forma de um número
incremental, sem muito significado para seres humanos, porém com grande valor para uso em
processamentos que requerem cálculos envolvendo timestamps.

Instant

A classe javax.time.Instant representa um ponto instantâneo na linha do tempo, um instante de tempo,


conhecido também como timestamp. Possui precisão de nanossegundos e 96 bits para armazenamento.
Através dessa abordagem, é possível armazenar até algumas centenas de vezes uma data equivalente ao
tempo de existência do universo. (Para os curiosos, o universo tem em torno de 13,8 bilhões de anos.) A
seguir, um exemplo onde validamos se um instante é superior a outro utilizando o método isAfter().

Instant instante1 = Clock.systemDefaultZone().instant();

//qualquer código aqui

Instant instante2 = Clock.systemDefaultZone().instant();

boolean avaliacao = instante1.isAfter(instante2);

Duration

A classe javax.time.Duration representa uma duração de tempo. Dentro da javax.time, ela representa a
duração entre dois instantes. O instante inicial que forma uma instância de Duration é inclusivo e o final é
exclusivo. Apesar de armazenar dados provenientes de lá, a classe Duration é desconectada e
independente da linha do tempo. O código apresentado a seguir ilustra o uso da classe Duration:

Instant agora = Clock.systemDefaultZone().instant();

Instant umMinutoMais = agora.plusSeconds(60);

Duration duration = Duration.durationBetween(agora, umMinutoMais);

System.out.println(duration);

No trecho de código apresentado, vemos primeiramente a criação de dois instantes. O primeiro


representa o momento exato da execução, enquanto que o segundo derivamos a partir do primeiro com a
soma de 60 segundos. Repare no método plusSeconds(). Ele é conciso, bem definido e indica exatamente
seu propósito e a unidade manipulada. Após a criação dos instantes, criamos uma duração entre eles,
que deverá conter exatamente 60 segundos. Ao imprimir a duração no console, temos a impressão da
duração utilizando o padrão ISO-8601.

InstantInterval

A classe javax.time.InstantInterval representa um intervalo de instantes na linha do tempo. A classe


pode possuir intervalos inclusivos, exclusivos ou algum dos intervalos pode não estar associado, isso quer
dizer que podemos criar o intervalo com apenas o instante de início e posteriormente associar o instante
final. O código apresentado a seguir ilustra a criação de um intervalo a partir de dois instantes e a
posterior verificação se um terceiro intervalo está contido entre eles:
Instant agora = Clock.systemDefaultZone().instant();

Instant umMinutoMais = agora.plusSeconds(60);

InstantInterval intervalo = InstantInterval.

intervalBetween(agora, umMinutoMais);

boolean contido = intervalo.contains

(Clock.systemDefaultZone().instant());

No exemplo, por utilizarmos o método “padrão”, o intervalo inicial é inclusivo e o final é exclusivo. Existe
outro método que recebe valores booleanos indicando se desejamos que cada um dos intervalos seja
inclusivo (true) ou exclusivo (false). Além disso, podemos construir a instância de InstantInterval a partir
dos métodos “builder”: intervalFrom() e intervalTo().

Human

Essa abordagem da javax.time é voltada para seres humanos. Ela representa os valores de datas e horas
utilizando campos com classes específicas para representar cada um dos dados do calendário: ano, mês,
dia, hora, minuto e segundo. Além desses campos mais comuns, temos algumas outras classes ou
enumerações para representar, por exemplo, o dia do ano, a semana do ano, os nanossegundos de cada
segundo, entre outras.

Através da abordagem para seres humanos, temos formas (classes!) para representar datas e horas,
datas sem hora, horas sem data, offsets e time zones.

Datas e Horas locais (sem time zone ou offset)

Os tipos mais simples presentes dentro da javax.time são os tipos chamados de “locais”. Esses tipos
podem representar data ou hora de forma isolada ou as duas em conjunto. São chamadas de locais por
não estarem associados a um offset ou time zone.

LocalDate

A primeira classe que mereceria nossa atenção seria a javax.time.


calendar.LocalDate. Como já estamos cansados de ver exemplos envolvendo essa classe, pois é a que
estamos acompanhando no decorrer do artigo, veremos algo a mais.

A representação dos campos de datas e horas dentro das classes é efetuada através de classes com este
propósito. Com isso, essas classes contém métodos implementando ações comuns, requeridas de cada
um desses campos que compõem nossas datas e horas. A javax.time utiliza como padrão os métodos
get() retornando valores numéricos para tais campos, por exemplo, getYear() retorna o ano como um
inteiro e os métodos to() retornam as classes específicas, no caso do ano, seria a classe
javax.time.calendar.field.Year. O exemplo apresentado a seguir ilustra o uso do método toYear().

LocalDate hoje = Clock.systemDefaultZone().today();

Year ano = hoje.toYear();

boolean bissexto = ano.isLeap();

No exemplo, obtemos o ano e, a partir do objeto criado, verificamos se ele é bissexto. Repare como isso
seria trabalhoso caso o ano fosse representado somente pelo tipo primitivo (int).

Teríamos que fazer esse cálculo manualmente, cada um em seu projeto, testar esse código e mantê-lo.

No exemplo apresentado a seguir, temos a obtenção do mês, representado pela enumeração


javax.time.calendar.field.MonthOfYear e, a partir dela, obtemos o último dia do mês representado pela
classe javax.time.calendar.field.DayOfMonth.
LocalDate hoje = Clock.systemDefaultZone().today();

MonthOfYear mes = hoje.toMonthOfYear();

DayOfMonth ultimoDiaMes = mes.getLastDayOfMonth(hoje.toYear());

Vemos aqui também, com a obtenção do último dia do mês, o grande valor no uso de classes específicas
em comparação com tipos primitivos. O cálculo do último dia do mês é resolvido automaticamente pela
API, poupando você, desenvolvedor, desse (grande) trabalho.

LocalTime

A classe javax.time.calendar.LocalTime representa uma hora sem time zone ou offset. O trecho de código
apresentado a seguir imprime a hora corrente e, posteriormente, subtrai uma hora e imprime o valor
atualizado.

LocalTime agora = Clock.systemDefaultZone().currentTime();

System.out.println(agora);

agora = agora.minusHours(1);

System.out.println(agora);

LocalDateTime

A classe javax.time.calendar.LocalDateTime representa uma data e hora sem time zone ou offset. O
trecho de exemplo apresentado a seguir imprime a hora corrente e posteriormente subtrai 36 horas e
imprime o valor atualizado.

LocalDateTime agora = Clock.systemDefaultZone().currentDateTime();

System.out.println(agora);

agora = agora.minusHours(36);

System.out.println(agora);

No exemplo, caso a primeira data e hora fosse representada por “2009-02-07T11:50:08.093”, após a
subtração de 36 horas, teríamos “2009-02-05T23:50:08.093”. Repare que o cálculo ocorreu
corretamente e alterou até mesmo a data.

Datas e Horas com offset


As datas e horas com offset representam um valor relativo ao UTC (Coordinated Universal Time ou Tempo
Universal Coordenado). Esses valores relativos, ou offsets, estão geralmente casados com as áreas de
fuso horário (time zones), porém a javax.time separa os dois conceitos: offsets e time zones em classes
distintas. O objetivo dessa separação é tratar trocas de offset por uma mesma time zone devido a
horários de inverno ou verão.

OffsetDate

A classe javax.time.calendar.OffsetDate representa uma data com um offset em relação ao UTC. Por
exemplo, ao criar um objeto OffsetDate aqui no Brasil, na região que segue o horário de Brasília, temos
como offset o valor -03h00min, ou seja, se em Brasília são 16h, o horário UTC estará marcando 13h.

O trecho de código apresentado a seguir ilustra a obtenção da data atual considerando o offset:

OffsetDate hoje = Clock.systemDefaultZone().offsetDateTime().toOffsetDate();

Caso esteja sob o offset de Brasília, ao imprimir o objeto, a string “2009-02-07-03:00” teria sido
impressa no console, seguindo o padrão ISO-8601 para datas e horas com offset.
Nota Devman - Thread-safe

Thread-safe ou, melhor dizendo, thread-safety é o conceito que usamos para nomear a situação em que
o acesso de múltiplas threads a um elemento no mesmo momento não apresenta resultados
inconsistentes, sem que uma thread tenha interferência sobre os dados manipulados por outra thread.

A classe javax.time.calendar.OffsetTime representa um horário com um offset em relação ao UTC,


seguindo o mesmo padrão da classe OffsetDate.

O trecho de código apresentado a seguir ilustra a obtenção do horário atual considerando o offset. Após
isso, a criação de uma nova instância de OffsetTime contendo o mesmo horário, porém com o offset
encontrado no Japão, que é +09h00min. Após isso, fazemos uma comparação dos dois horários com o
intuito de ilustrar que a comparação levará em conta o offset.

OffsetTime hojeBrasil = Clock.systemDefaultZone().offsetDateTime().toOffsetTime();

OffsetTime hojeJapao = hojeBrasil.withOffset(ZoneOffset.zoneOffset(9));

boolean horarioJapaoPosterior = hojeJapao.isAfter(hojeBrasil);

Após a execução do código apresentado, a variável horarioJapaoPosterior irá armazenar o valor true
indicando que a hora no Japão é posterior.

OffsetDateTime

A classe javax.time.calendar.OffsetDateTime representa uma data e um horário com um offset em


relação ao UTC, seguindo o padrão dos dois exemplos vistos até o momento. O código a seguir ilustra a
obtenção de uma OffsetDateTime.

OffsetDateTime hojeBrasil = Clock.systemDefaultZone().offsetDateTime();

Ao imprimir essa variável no console, temos uma string representando a data, horário e o offset seguindo
o padrão ISO: “2009-02-09T23:20:54.171-03:00”.

Time Zones

Além de objetos capazes de armazenar o offset, temos a classe javax.time.calendar.ZonedDateTime que


armazena também o time zone. O time zone é representado pela classe javax.time.calendar.TimeZone
que tem o propósito de tratar regras e exceções das alterações nos offsets das regiões mundiais.

Essas alterações geralmente se devem a mudanças no horário de verão ou inverno.

Dentro da javax.time, é possível instanciar a classe TimeZone a partir de um identificador da base de


time zones zoneinfo ou a partir do offset em relação à UTC.

O código apresentado a seguir ilustra a obtenção de duas instâncias de ZonedDateTime, a primeira com o
time zone padrão da JVM, configurado como “América/Sao_Paulo” e o segundo forçando o uso do time
zone de Paris.

ZonedDateTime agora = Clock.systemDefaultZone().


zonedDateTime();
System.out.println(agora);

TimeZone timeZone = TimeZone.timeZone


("Europe/Paris");
agora = Clock.system(timeZone).
zonedDateTime();
System.out.println(agora);

Ao executar o código apresentado, as datas e horas correspondentes ao time zone informado são
impressas no console:
2009-02-09T23:37:18.968-03:00 UTC-03:00
2009-02-10T03:37:18.968+01:00 Europe/Paris

Repare que a primeira string impressa apresenta o time zone como UTC-03:00, isso por que não há um
identificador correspondente para este time zone, ao contrário do que ocorreu com o time zone de Paris,
que criamos a partir do identificador, que é apresentado na impressão do objeto.

Uma forma equivalente de obter a instância de ZonedDateTime à que utilizamos é a apresentada a seguir,
uma vez que o time zone de Paris corresponde ao offset de uma hora em relação ao UTC:

ZonedDateTime agora = Clock.


systemDefaultZone().zonedDateTime();
System.out.println(agora);

TimeZone timeZone = TimeZone.


timeZone(ZoneOffset.zoneOffset(1));
agora = Clock.system(timeZone).
zonedDateTime();
System.out.println(agora);

Matchers e Resolvers

Como já dissemos, a javax.time recorre a conceitos do design pattern Strategy para que você possa
customizar pontos de seu comportamento. Um exemplo pelo qual já passamos foi o dos Adjusters que
permitem a realização de ajustes em datas e horas de forma bastante flexível.

Além dos Adjusters, a javax.time nos provê o conceito de Matchers e Resolvers, que veremos agora.

Matchers

Os Matchers possuem a responsabilidade de realizar consultas em datas e horas de forma muito simples
e flexível. Eles reduzem drasticamente a quantidade de código e a lógica empregada neste tipo de
operação.

O código apresentado a seguir ilustra a consulta à data (e hora) atual. Nesse caso consultamos se o ano
é 2009, valorizando uma variável booleana denominada ano2009. Como estamos realmente em 2009,
essa variável será valorizada com true.

LocalDateTime agora = Clock.systemDefaultZone().dateTime();


boolean ano2009 = agora.matches(Year.isoYear(2009));

A consulta envolvendo o horário é praticamente idêntica, conforme pode ser visto a seguir:

LocalDateTime agora = Clock.systemDefaultZone().dateTime();

boolean vinteUmaHoras = agora.matches(HourOfDay.hourOfDay(21));

É possível realizar tais operações, pois cada uma dessas classes (Year, HourOfDay e as outras classes
representando os elementos do calendário) implementa as interfaces necessárias para executar o método
matches().

A interface que representa o Matcher para a consulta em datas é a javax.time.calendar.DateMatcher, que


possui um único método boolean matchesDate(LocalDate input). Já a interface para a consulta em
horários é a javax.time.calendar.TimeMatcher e ela segue o mesmo padrão da outra que vimos,
possuindo um único método boolean matchesTime(LocalTime time).

A Listagem 1 apresenta um exemplo de implementação de DateMatcher para verificar se a data possui


um dia ímpar ou não.

Listagem 1. Exemplo do uso de um DateMatcher customizado para realizar a consulta de dias


ímpares

package br.com.jm.javax.time.human.matchers;
//imports...

public class ExemploMatcherDiaImpar {

public static void main(String[] args) throws Exception {

LocalDateTime agora = Clock.systemDefaultZone().dateTime();

boolean diaImpar = agora.matches(new DiaImparMatcher());

System.out.println(diaImpar);

class DiaImparMatcher implements DateMatcher {

@Override

public boolean matchesDate(LocalDate data) {

if (data.getDayOfMonth() % 2 == 0) {

return false;

} else {

return true;

A classe javax.time.calendar.DateMatchers possui alguns matchers pré-configurados para você utilizar.


Entre os matchers que já vêm por padrão temos, por exemplo, um para verificar se estamos durante a
semana ou em um final de semana, se é o primeiro ou último dia do mês, além de alguns outros.

O código apresentado a seguir ilustra o uso da classe DateMatchers para a verificação se a data corrente
é um final de semana.

LocalDateTime agora = Clock.systemDefaultZone().dateTime();


boolean finalDeSemana = agora.matches(DateMatchers.weekendDay());

Como visto, a classe DateMatcher pode nos auxiliar com a verificação de datas e nos auxiliar muito em
tarefas do mundo real como, por exemplo, verificar se determinada data está dentro do domingo de
páscoa ou na sexta-feira santa.

Resolvers

Assim como os matchers, os Resolvers são pontos de extensibilidade disponíveis na javax.time. Através
deles você pode indicar como deseja que uma data inválida seja tratada, por exemplo, ao criar uma data
no dia 29 de fevereiro em um ano que não seja bissexto. A classe javax.time.calendar.DateResolvers
possui algumas implementações dos casos de uso mais comuns onde possa vir a ser necessário o uso de
um Resolver.
O exemplo apresentado a seguir ilustra a criação de uma data utilizando um resolver provido pela classe
DateResolvers que resolve uma data inválida como a próxima data válida.

LocalDate data = DateResolvers.nextValid().resolveDate(Year.isoYear(2009),


MonthOfYear.monthOfYear(2),DayOfMonth.dayOfMonth(29));

Ao imprimir tal data no console, temos como data apresentada 2009-03-01. Caso você tenha achado o
código verboso demais, é possível utilizar o recurso de static imports para reduzir a verbosidade. A
Listagem 2 apresenta tal opção.

Listagem 2. Uso de static imports para minimizar a verbosidade do código no uso de Resolvers

package br.com.jm.javax.time.human.resolvers;

import static javax.time.calendar.field.DayOfMonth.dayOfMonth;

import static javax.time.calendar.field.MonthOfYear.monthOfYear;

import static javax.time.calendar.field.Year.isoYear;

import javax.time.calendar.DateResolvers;

import javax.time.calendar.LocalDate;

public class ExemploResolverSI {

public static void main


(String[] args) throws Exception {

LocalDate data = DateResolvers.nextValid().

resolveDate(isoYear(2009),

monthOfYear(2), dayOfMonth(29));

System.out.println(data);

Períodos

Os períodos, dentro da javax.time, são cidadãos de primeira-classe. Isso quer dizer que existem classes
capazes de representá-los, ao contrário do que você encontra nas APIs atuais, onde não há forma padrão
para representar períodos.

Através da representação dos períodos, podemos expressar durações de tempo da maneira tratada pelos
seres humanos como, por exemplo, a duração de uma reunião ou de suas férias.
Como dito, existe uma classe para cada parte do período:

javax.time.period.field.Days
•
javax.time.period.field.Hours
•
javax.time.period.field.Minutes
•
javax.time.period.field.Months
•
javax.time.period.field.Seconds
•
javax.time.period.field.Weeks
•
javax.time.period.field.Years

E uma classe para representar o período como um todo: javax.time.period.Period.

O código apresentado a seguir representa a criação de um período de duas horas:

Period period = Period.hours(2);

A partir desse momento, o objeto representa o determinado período para o qual foi criado. Ao imprimir
tal período no console, é impressa sua representação de acordo com o padrão ISO-8106. Essa
representação para o período de duas horas é “PT2H”. Uma das grandes vantagens no uso de objetos
especializados para os períodos é permitir que você execute operações nesse objeto. Isso resultará na
criação de outro objeto para representar o novo período resultante da operação. O objeto que representa
o período também é imutável. A seguir, uma subtração de quarenta minutos de nosso período de duas
horas:

period = period.minusMinutes(40);

Ao executar tal operação, o período ainda permanece com duas horas e menos quarenta minutos e
necessita ser normalizado para representar o período real, que seria de uma hora e vinte minutos.

Normalização

O método normalize() retorna uma cópia do período normalizado para os limites padrão dos campos de
data e hora, levando em conta as seguintes regras:

12 meses em um ano;
60 minutos em uma hora;
60 segundos em um minuto;
1.000.000.000 de nanossegundos em um segundo.

Por exemplo, um período de 13 meses é normalizado para um ano e um mês e a criação de um período
de 5000 minutos com o código Period.minutes(5000) resulta em um período normalizado de 83 horas e
20 minutos.

Caso você necessite ainda realizar a normalização por dias, quebrando cada 24 horas em um dia, você
deve utilizar o método normalizedWith24HourDays(), que neste nosso exemplo resultaria em um período
de três dias, onze horas e vinte minutos.

Para revisar o que foi visto e por em prática o conteúdo apresentado, veja a Apresentação “Javax.time”.
Conclusão

Você conheceu um pouco sobre a nova especificação para representar datas e horas dentro da plataforma
Java. Essa nova API tenta trazer várias facilidades e corrigir problemas conhecidos das opções atuais,
buscando transformar seu código em algo mais legível e simples. Passamos por todos os pontos mais
importantes da nova API, fornecendo exemplos pontuais e focados, tentando ambientar você com os
novos conceitos.

A JSR 310, que está definindo a nova API, é aberta e você pode contribuir para seu desenvolvimento.
Acesse o site da JSR (veja seção de Links) e faça parte da definição do futuro da linguagem. O código
apresentado neste artigo está disponível para download no site da revista, contando com um JAR da
javax.time compilado durante a escrita do artigo.

Como a API ainda está sendo concebida, é possível que quando o artigo chegar a você, leitor, algo possa
ter sido alterado. Devido a isso, deixamos aqui a sugestão de baixar os fontes da última versão da API e
adequar os exemplos às possíveis alterações. Acesse também a lista de discussões para entender o rumo
que está sendo dado à API.

Links

jsr-310.dev.java.net
Site de desenvolvimento da JSR 310, onde você pode obter acesso a todos os artefatos relacionados à
nova API, além de participar de seu desenvolvimento, cadastrando-se nas listas de discussão.

jcp.org/en/jsr/detail?id=310
Site oficial da JSR 310, parte do JCP.

joda-time.sourceforge.net
Site da Joda Time, API que está servindo de base para o desenvolvimento da JSR 310.
Java ME Platform SDK 3

Ernandes Mourão Júnior

é Bacharel em Informática pela Universidade de Fortaleza e desenvolvedor certificado Java (SCWCD,


SCBCD e SCMAD). Atualmente exerce o cargo de Analista de Sistemas e Líder Técnico Java ME no
Instituto Atlântico.

De que se trata o artigo:


Apresentação da nova ferramenta da Sun para a plataforma Java ME: o Java ME Platform SDK, o
sucessor do Wireless Toolkit. Neste artigo, as principais novidades trazidas por esta ferramenta são
apresentadas de uma forma clara e objetiva para o leitor.

Para que serve:


Proporcionar uma apresentação objetiva das principais funcionalidades da nova ferramenta de
desenvolvimento da plataforma Java ME. Esta apresentação concede ao leitor uma visão geral, mas
completa, do que de novo ele pode vir a utilizar daqui para frente no desenvolvimento de suas
aplicações.

Em que situação o tema é útil:

No momento em que uma ferramenta que usamos com frequência surge com uma versão nova, é
sempre válido dar uma olhada nas principais novidades que ela vem trazendo. Estas novidades,
geralmente, vêm para melhorar nossa produtividade, além de nos proporcionar mais subsídios, a fim de
desenvolvermos aplicações ainda melhores.

Java ME Platform SDK 3:

Assim como os seres vivos, os softwares de computador também evoluem a partir do momento em que
se inicia um novo ciclo. No caso da principal ferramenta de desenvolvimento da plataforma Java ME, o
Wireless Toolkit, este seu novo ciclo trouxe-lhe evoluções bastante expressivas. Mudanças que vão desde
o nome, passando pelo novo conceito da ferramenta, até chegar às suas novas funcionalidades.

A ferramenta que antes era considerada um simples toolkit (caixa de ferramenta), agora passa a ser um
completo SDK, contando com um ambiente integrado de desenvolvimento. Editor de código, emuladores,
nova máquina virtual e APIs são somente algumas das muitas novidades trazidas por esta nova
ferramenta. Sem falar que até o seu nome é novidade, pois agora a ferramenta é chamada de Java ME
Platform SDK 3.

Com todas essas novidades é possível constatar o grande passo que a plataforma Java ME dá em direção
a uma maior popularização da plataforma. Pois com o ambiente de desenvolvimento integrado baseado
no NetBeans, novos desenvolvedores terão mais facilidades de criar as suas primeiras aplicações, o que
garante uma maior chance de crescimento da comunidade Java ME.

Segundo a Biologia, o processo permanente de mudança que vem transformando a vida na Terra, desde
o seu princípio mais simples até a sua atual diversidade, é conhecido como Evolução. A evolução ocorre
quando um ser vivo se reproduz, e pequenas mudanças aleatórias nos seus genes fazem com que o seu
descendente seja diferente dele próprio. Essas mudanças, se benéficas, fazem com que o indivíduo
sobreviva o tempo necessário para se reproduzir, dando início a um novo ciclo de transformações, o que
resulta no surgimento de novas espécies.

Num paralelo entre o processo de evolução dos seres vivos com o de um software de computador,
verificamos como ambos são parecidos. O software, geralmente, inicia o seu clico de vida de forma bem
simples, com poucas funcionalidades, somente com o intuito de atender certa demanda do momento.

À medida que vai sendo utilizado, o software, se for bom, acaba aguçando a criatividade dos usuários,
que solicitam novas versões mais aperfeiçoadas e com mais funcionalidades. Estas solicitações induzem o
software a entrar no clico de evoluções, resultando em novos releases.
Neste artigo, falaremos sobre como este processo evolutivo dos softwares atingiu o Wireless Toolkit
(WTK). Apresentaremos algumas das principais novidades que a Sun Microsystems preparou, justificando
a mudança do seu nome para Java ME Platform SDK 3.

De Toolkit para SDK

O fato de a Sun ter mudando o nome do WTK não deve causar muito espanto aos desenvolvedores,
tendo em vista que esta não é a primeira vez. Desde o surgimento da plataforma Java ME, por volta de
1999, esta já é a segunda vez que a Sun muda o nome da ferramenta.

O nome original, Java 2 Platform Micro Edition Wireless Toolkit, foi substituído em 2007, devido à nova
versão 1.5 (ou 5) da plataforma Java. Desde então, a Sun retirou o famigerado “2” de todos os nomes de
suas plataformas e ferramentas. Com isto, o que era J2ME virou Java ME, o que também acabou
refletindo na primeira mudança de nome do WTK para Sun Java Wireless Toolkit.

A mudança do nome na nova versão, que agora insere o termo SDK (Standard Development Kit), se deve
ao fato da ferramenta proporcionar um ambiente de desenvolvimento completo, e não somente os
emuladores, ferramentas de monitoração (e.g. monitor de memória), códigos de exemplo,
documentação, etc. O pacote agora também vem com editor de código com assistência de codificação
(veja a seção “Netbeans “Lite”). Além disso, a plataforma Java ME é suportada de forma muito mais
completa (o antigo WTK suportava apenas a configuração CLDC, exigindo toolkits separados para CDC e
outras variantes). Um arsenal completo que eleva a categoria da ferramenta, justificando o acréscimo do
termo SDK.

O nome original, Java 2 Platform Micro Edition Wireless Toolkit, foi substituído em 2007, devido à nova
versão 1.5 (ou 5) da plataforma Java. Desde então, a Sun retirou o famigerado “2” de todos os nomes de
suas plataformas e ferramentas. Com isto, o que era J2ME virou Java ME, o que também acabou
refletindo na primeira mudança de nome do WTK para Sun Java Wireless Toolkit.

A mudança do nome na nova versão, que agora insere o termo SDK (Standard Development Kit), se deve
ao fato da ferramenta proporcionar um ambiente de desenvolvimento completo, e não somente os
emuladores, ferramentas de monitoração (e.g. monitor de memória), códigos de exemplo,
documentação, etc. O pacote agora também vem com editor de código com assistência de codificação
(veja a seção “Netbeans “Lite”). Além disso, a plataforma Java ME é suportada de forma muito mais
completa (o antigo WTK suportava apenas a configuração CLDC, exigindo toolkits separados para CDC e
outras variantes). Um arsenal completo que eleva a categoria da ferramenta, justificando o acréscimo do
termo SDK.

NetBeans “
Lite”

De longe a mais importante e impactante novidade do Java ME Platform SDK 3 é o ambiente de


desenvolvimento integrado (veja Figura 1), baseado na plataforma NetBeans. Nas versões anteriores da
ferramenta, todo o desenvolvimento das aplicações (e.g. escrita de código) tinha que ser feito numa
ferramenta (IDE) à parte, como por exemplo, Eclipse, NetBeans, VistaMax, etc. O trabalho do WTK era
executar os emuladores, alterar configurações de ambiente da plataforma, monitoramento de memória,
dentre outras funcionalidades auxiliares. IDEs como Eclipse e NetBeans, por exemplo, permitiam que o
WTK pudesse ser integrado, possibilitando ao desenvolvedor uma transparência do seu uso.

As principais ferramentas deste ambiente de desenvolvimento integrado são o editor de código,


gerenciador de projetos e arquivos, integração com o Ant, configurações do projeto (e.g. propriedades do
arquivo JAD), ofuscador de código, além de outras de cunho mais geral, encontradas em qualquer
distribuição do NetBeans.
Quem já trabalha com o Netbeans Mobility Pack vai se sentir em casa neste novo ambiente, pois ambos
são praticamente o mesmo. Além disso, aqueles que, porventura, decidirem migrar para o NetBeans
Mobility Pack futuramente, já que este dispõe de mais funcionalidades, não encontrarão barreiras. Pois o
arquivo de projeto do Java ME Platform SDK 3 é totalmente compatível com o Mobility Pack, o que facilita
muito a migração entre as ferramentas.
Figura 1: Ambiente integrado de desenvolvimento do Java ME Platform SDK 3 baseado no
NetBeans.
A KVM se Despede

Após quase dez anos desde sua chegada, a máquina virtual da Java ME, a Kilo Virtual Machine (KVM),
finalmente encerra o seu ciclo de vida. Este fim já era esperado, já que há alguns anos, a KVM já não
estava mais presente nos dispositivos móveis disponíveis no mercado, sendo esta substituída pela CLDC
HotSpot Virtual Machine. Um projeto mais moderno e robusto, que já conseguia atender melhor as atuais
demandas por desempenho.

O que prolongou um pouco mais a vida da KVM foi o fato da Sun continuar utilizando-a em seus
emuladores. Até a última versão do WTK, a 2.5.2, todos os emuladores ainda eram baseados na KVM. No
entanto, com a chegada do Java ME Platform SDK 3 a Sun finalmente “aposentou” a KVM, trazendo de
uma vez por todas, a CLDC HotSpot Virtual Machine também para os seus emuladores, inclusive para a
configuração CDC. Com esta mudança, o comportamento das aplicações no emulador ficou mais parecido
ao encontrado nos dispositivos reais.

Dentre as principais vantagens da CLDC HotSpot Virtual Machine, comparada à KVM, estão a compilação
dinâmica das instruções de bytecode em instruções nativas (veja o quadro “Compilação Just-in-time
(JIT)”), menor consumo e fragmentação de memória, maior economia da bateria, dentre outras. Em
termos de números, a execução de uma instrução compilada dinamicamente, chega a ser cinquenta
vezes mais rápida do que uma instrução interpretada.

Para conhecer um pouco mais sobre a CLDC HotSpot Virtual Machine e a sua chegada aos dispositivos
móveis, veja a seção Links, além do artigo “Java: Uma perspectiva”, da Edição 65.

Descobrindo as Diferenças

Existe na Internet uma iniciativa chamada de Wireless Universal Resource File (WURFL), aonde
desenvolvedores e entusiastas vêm tentando catalogar todos os dispositivos móveis disponíveis no
mercado, assim como suas características. O objetivo é fornecer uma base de dados centralizada para os
desenvolvedores. Neste caso, um arquivo XML, afim de que eles possam conhecer, antecipadamente, as
principais diferenças que existem de um dispositivo para outro, além de ter uma idéia da fatia de
mercado que suas aplicações estão abrangendo.

Com o objetivo de facilitar o acesso às informações providas pelo WURFL (para saber mais sobre esta
iniciativa, veja a seção Links), a Sun desenvolveu uma nova funcionalidade, no Java ME Platform SDK 3,
chamada Device Database Search, que acessa essa base de dados de uma forma fácil e rápida. O usuário
pode tanto solicitar para ver a lista completa de todos os dispositivos disponíveis, como ele também pode
aplicar alguns filtros (e.g. fabricante, modelo, JSRs suportadas, tamanho de tela, etc.), a fim de facilitar a
busca pelo dispositivo desejado. O resultado é apresentado numa lista, onde modelo e fabricante são
identificados. Para ver os detalhes de um determinado dispositivo, basta selecioná-lo na lista, que todas
as suas informações serão exibidas numa outra lista ao lado (veja Figura 2).

LWUIT Ganha Força

A equipe do projeto Lightweight User Interface Toolkit (LWUIT) deve estar orgulhosa com esta conquista.
Tendo em vista a grande popularização deste projeto entre os desenvolvedores Java ME, a Sun não
perdeu tempo em tentar transformar este projeto numa espécie de padrão dentro da comunidade de
desenvolvedores. Os engenheiros da Sun devem ter percebido a quantidade de projetos que existem na
Internet, que visam melhorar a vida dos desenvolvedores, no tocante do desenvolvimento de interfaces
gráficas mais sofisticadas. Com isso, a Sun não hesitou e incorporou o LWUIT como uma de suas
bibliotecas padrão, disponibilizou uma aplicação exemplo e integrou uma de suas ferramentas utilitárias,
o Resource Manager, dentro do Java ME Platform SDK 3. Alavancando de vez o nome LWUIT dentro da
comunidade Java ME.

Figura 2: Ferramenta ‘
Device Database Search’
retornando todos os dispositivos da Nokia que
suportam MIDP 2.0.
O LWUIT, para quem ainda não conhece, é um framework de componentes gráficos inspirado no Swing
da plataforma Java Standard Edition (Java SE), especificamente modelado e construído para ambientes
restritos em poder de processamento e memória, como o dos dispositivos móveis. O LWUIT traz para o
mundo móvel, algumas das funcionalidades para o desenvolvimento de interfaces gráficas já conhecidas
no desktop e que são bem características do Swing, como por exemplo, layouts (e.g. FlowLayout),
renders (componentes que especificam a forma como um objeto é desenhado na tela), manipuladores de
evento (e.g. ActionListener), etc.

Com toda esta integração, para um desenvolvedor acrescentar o suporte ao LWUIT à sua aplicação
dentro do Java ME Platform SDK 3, basta somente alguns passos: acessar as propriedades do projeto,
selecionar “Libraries & Resources”, clicar no botão “Add Library” e selecionar a biblioteca LWUIT, que já
aparece na lista, junto com as demais bibliotecas disponíveis na ferramenta. Além disso, a aplicação
exemplo é bem completa, o que serve como uma boa referência inicial para os desenvolvedores que
estão começando no LWUIT (para saber mais sobre LWUIT, veja a Edição 60 e a seção Links). Ela
apresenta diversos exemplos de várias partes do framework.

Encontrando o “
Gargalo”

Como se os problema de lógica não fossem o bastante, os problemas de desempenho também tem sido
uma constante nas aplicações atuais, principalmente nas móveis, devido às exigentes demandas por mais
funcionalidades, sem falar das restrições de processamento, inerentes aos dispositivos móveis.

A fim de também facilitar mais esta tarefa dos desenvolvedores, já que por muito tempo esta também foi
executada com o auxilio de APIs como System.out.println() e System.currentTimeMillis(), foram criados
os monitores de processamento (profiler). São ferramentas que monitoram toda a execução da aplicação,
registrando os tempos de execução e a quantidade de vezes que cada método foi executado. Informações
muito valiosas quando se está à procura do “gargalo” de um determinado processo. No Java ME Platform
SDK 3, esta ferramenta de monitoração é a mesma encontrada em outras distribuições do NetBeans,
também usada para testes em aplicações Java SE e Java EE.

Para ativar o profiler para determinada aplicação no Java ME Platform SDK 3, basta habilitar a opção
Enable profiler nas propriedades do emulador utilizado. Durante a execução, o profiler registra num
arquivo (e.g. data.prof), todas as informações sobre o código que está sendo executado. Ao final da
execução, é preciso informar ao SDK, através da opção Profile>Import Java ME SDK Snapshot, o caminho
do arquivo gerado pelo profiler, a fim de que a informação coletada seja apresentada. Nesta nova tela
que é mostrada (veja Figura 3), o desenvolvedor pode ver o tempo gasto para executar cada método e a
quantidade de vezes em que ele foi executado, além de ainda poder agrupar todas estas informações por
classe ou por pacote. Dando uma visão mais macro dos tempos de execução de cada componente da
aplicação.

Figura 3: Tela do profiler que mostra os tempos de execução dos métodos da aplicação.
E mais JSRs

A cada dia novas APIs são finalmente especificadas, tornando-se aptas a serem implementadas por
algum fabricante, que suporte a plataforma Java ME em seus dispositivos móveis. O que normalmente
acontece é a implementação dessas APIs chegarem primeiro às ferramentas de desenvolvimento, tendo
em vista uma maior facilidade de implementá-las em plataformas Windows ou Linux, por exemplo, para
depois chegarem aos dispositivos.

No Java ME Platform SDK 3, três novas APIs estão finalmente disponíveis para os desenvolvedores:
Mobile Sensor API (JSR 256) (veja o artigo da Edição 55), XML API for Java ME (JSR 280) e a Java
Binding for the OpenGL ES API (JSR 239). A primeira é responsável por fornecer acesso a alguns
sensores (e.g. acelerômetro) disponíveis em certos dispositivos móveis (veja o quadro “Trabalhando com
Sensores no Emulador”).

A JSR 280, por sua vez, define uma API exclusiva para manipular arquivos XML com parsers SAX2 e
DOM. A idéia é acabar com a fragmentação que existe atualmente, onde cada API que precisa manipular
XML define o seu próprio mecanismo para desempenhar este trabalho (e.g. Web Services Specification
(JSR 172)). E finalmente, a JSR 239 vem para prover o suporte ao desenvolvimento de gráficos 3D
através da biblioteca OpenGL ES, a qual é um subconjunto da OpenGL 1.3.

Para fechar o pacote das novas API, o Java ME Platform SDK 3 também disponibiliza aplicações exemplo
para cada nova API suportada, inclusive as que já eram suportadas. Um ótimo “pontapé inicial” para
quem está começando a trabalhar com as APIs mais recentes.

Quanto mais Emuladores, Melhor

É inquestionável a qualidade dos emuladores disponibilizados pelo Java ME Platform SDK 3, assim como
os de suas versões anteriores. Todos eles seguem à risca cada ponto das especificações das APIs, dando
maior segurança ao desenvolvedor sobre a corretude do seu código. Se nenhuma exceção for lançada,
alertando sobre alguma operação indevida, é porque ele está no caminho certo.

No entanto, por mais que as especificações sirvam para definir uma base comum, além de diminuir as
chances de incompatibilidade entre as diferentes implementações, no mundo real as coisas não são tão
perfeitas assim. É comum encontrar problemas de incompatibilidade entre uma implementação feita para
o Windows, que não funciona no Linux, e vice-versa. No mundo móvel então, é ainda pior. Aplicações
feitas para rodar num dispositivo da Nokia podem precisar de ajustes para rodar perfeitamente num
Motorola ou Sony Ericsson, por exemplo.

Por mais que sigam a risca o que está na especificação, sempre é possível ter algo na implementação
(inclusive bugs) que gera um comportamento diferente. A complexidade e as restrições dessas
plataformas móveis potencializam ainda mais este problema.

Tendo em vista essas possíveis diferenças, os testes em outras plataformas se tornam muito importantes
para uma aplicação Java ME, que deseja rodar em dispositivos de mais de um fabricante. Nesta versão do
Java ME Platform SDK 3 é possível importar outros emuladores desenvolvidos, por exemplo, pela Nokia,
Samsung, Motorola, Sony Ericsson, dentre outros, para dentro da ferramenta e usá-los para testar suas
aplicações. Uma funcionalidade já encontrada no NetBeans Mobility Pack.

Para importar novos emuladores, é preciso informar o caminho do SDK do fabricante, o qual os
emuladores pertencem. Para isto, deve-se acessar Tools>Java Platforms, selecionar o tipo de plataforma
J2ME, clicar em Add Plaform, selecionar Custom Java ME MIDP Platform Emulador e informar os caminhos
solicitados pelo restante do wizard. Após ter configurado a nova plataforma, basta acessar as
propriedades do projeto e selecionar a nova plataforma e o emulador a ser utilizado.

Testes em emuladores é realmente uma “mão na roda”, pois facilitam muito o desenvolvimento, tendo
em vista a rapidez que é por em execução a aplicação no emulador. Entretanto, eles nunca vão substituir
o teste no dispositivo real, pois, se de um emulador para outro já existem diferenças, imagine do
emulador para o hardware.

Suporte Oficial

Com exceção do sistema operacional Symbian, outros sistemas como Palm OS e Windows Mobile,
também bem populares no mercado, nunca foram referência pelo seu suporte à plataforma Java ME, pelo
contrário. Esta questão, acredito, deve-se ao fato que ambos possuem, desde muito cedo, suas próprias
plataformas nativas (baseadas em C) de desenvolvimento de aplicações. Ambas muito bem difundidas e
poderosas, e que acabou gerando uma comunidade de desenvolvedores duradoura.
Com relação ao Palm OS, a Sun até que se esforçou na época do lançamento da primeira versão do
Wireless Toolkit (ainda MIDP 1.0), disponibilizando uma máquina virtual que podia ser instalada nos
dispositivos da empresa Palm.

No entanto, a investida não vingou, talvez pelo desinteresse da própria Palm, e o projeto ficou pelo
caminho. A IBM também tentou, lançando uma máquina virtual chamada Websphere Everyplace Micro
Environment (WME), com suporte ao MIDP 2.0. A Palm dessa vez até esboçou um incentivo, publicando a
VM da IBM no seu site como um produto homologado. Porém, em 2008, o incentivo acabou e a Palm
retirou o apoio. Hoje este projeto está parado no tempo, assim como o próprio Palm OS, que vem
sumindo do mercado aos poucos.

A história da plataforma Java ME no sistema operacional da Microsoft não é muito diferente da vivida pela
Palm. Esta também é marcada pela dificuldade de encontrar uma máquina virtual robusta e sólida, o que
ajudaria a difundir melhor a Java ME nos populares PDAs da empresa de Bill Gates.

No entanto, por mais que o suporte seja fraco, o Windows Mobile chega ainda a ser melhor que o Palm
OS, com relação a Java ME. Nas versões para smartphones do Windows Mobile, por exemplo, até existe
uma máquina virtual que já acompanha o dispositivo. Por mais que seja restrito em recursos, ainda serve
para rodar aplicações mais simples. A IBM também investiu na Java ME para o Windows Mobile,
disponibilizando uma versão paga da sua máquina virtual, WME, voltada para versões anteriores deste
sistema.

Mas agora, as coisas parecem que vão melhorar para os desenvolvedores Java ME que querem rodar
suas aplicações no Windows Mobile.

A Sun está disponibilizando, no Java ME Platform SDK 3, uma máquina virtual para o Windows Mobile 6,
que pode tanto ser instalado em um emulador do próprio sistema operacional no PC, quanto num
dispositivo real.

Tudo o que o desenvolvedor precisa fazer é instalar a maquina virtual (veja Figura 4), através do arquivo
‘sun-java-cldc-emu.cab´, que se encontra disponível no SDK na pasta ‘JavaMESdkHome\on-
device\winmobile-arm´, no emulador ou no dispositivo. Além de executar as aplicações, o desenvolvedor
ainda tem a possibilidade de depurar suas aplicações tanto no emulador quanto no dispositivo real.

Com mais esta plataforma dando suporte à Java ME, agora com a assinatura da Sun, a plataforma Java
dá mais um passo muito importante na consolidação do seu nome nas principais plataformas móveis do
mercado.

Conclusão

A Sun, com certeza, acertou com o Java ME Platform SDK, pois a falta do ambiente de codificação do
Wireless Toolkit atrapalhava, de certa forma, os novos desenvolvedores. O problema era que estes não
conseguiam, rapidamente, codificar um “Hello, World!”. Haja vista que precisavam usar um editor de
código externo, importar o projeto para o toolkit, etc.

Com o Java ME Platform SDK tudo ficou mais integrado. A facilidade de criar um projeto, codificar,
escolher o emulador e colocar para executar são características muito importantes, que todo iniciante,
em qualquer plataforma, gosta de ter.

Entretanto, o Java ME Platform SDK é indicado somente para aplicações de teste ou pequenas, pois
algumas funcionalidades importantes no desenvolvimento de aplicações mais complexas, como
depuração e controle de versão, não estão disponíveis. Nesses casos, o recomendado mesmo é o
NetBeans Mobility Pack, que oferece o ambiente completo do NetBeans mais uma série de ferramentas
específicas para Java ME

QUADRO INFORMATIVO

Compilação Just-in-time (JIT)

A característica da plataforma Java que permite que suas aplicações sejam possíveis de serem
executadas em diferentes plataformas (e.g. Windows e Linux), sem necessidade de recompilação, é o
fato do seu código-fonte ser compilado para uma representação intermediária, conhecida como bytecode.
Mas sua execução depende de outra aplicação, esta sim dependente de plataforma: a máquina virtual,
que interpreta o bytecode.

Os ganhos em portabilidade com este código intermediário, por outro lado, gera problemas de
desempenho, pois a interpretação é bem menos eficiente que a execução de código nativo. Entretanto, o
problema pode ser resolvido com ajuda da técnica de compilação Just-in-time (JIT), que consiste na
compilação do bytecode para código nativo durante a execução da aplicação.

No momento em que um método vai ser executado, este é convertido numa representação de código de
máquina. Por isso o termo Just-in-time que, em português, significa “no momento exato”. Este código
compilado fica salvo na memória, pronto para execuções futuras.

Além da plataforma Java, a .NET também utiliza esta técnica. Neste caso, o código intermediário
convertido pela máquina virtual .NET é o Microsoft Intermediate Language (MIL). Para saber mais sobre a
JIT, veja a seção Links.

Trabalhando com Sensores no Emulador

Existem algumas APIs Java ME que interagem com alguns tipos de hardware ou serviço, que vem
integrado a algumas linhas de dispositivos móveis. Por exemplo, a Mobile Sensor API (JSR 256), que
interage com sensores (e.g. acelerômetro); a Location API (JSR 179), que captura os dados de um GPS;
e a Payment API (JSR 229), que fornece serviço de pagamentos em geral.

Imaginar como se testa uma aplicação desenvolvida com estas APIs, no dispositivo real, não é difícil. Pois
neste caso, o hardware ou serviço está presente no dispositivo e a API vai acessá-lo para poder retornar
suas informações. Mas o que acontece quando não se tem o dispositivo real em mãos, para testar a
aplicação? A resposta é simples: para cada API que interage com um hardware ou serviço, os emuladores
do Java ME Platform SDK executam tais papéis, simulando-os.

No caso da JSR 256, para a qual a emulação de sensores é mais uma novidade do Java ME Platform SDK,
o desenvolvedor pode alterar as informações que o acelerômetro retorna. Desta forma, o desenvolvedor
pode informar novos valores para as coordenadas x, y e z, que representam a leitura tridimensional deste
tipo de hardware.

Para acessar as ferramentas de simulação dos emuladores, o desenvolvedor precisa acessar a opção
View>External Events Generator, disponível no próprio emulador. Depois disto, uma nova tela será
apresentada, com uma série abas nela. Cada aba, por sua vez, representa um tipo de hardware ou
serviço que pode ser simulado. No caso do acelerômetro, a aba Sensors é a que fornece os meios para
simulá-lo. Nesta mesma aba, o Java ME Platform SDK ainda fornece a simulação de um sensor de
temperatura.

Links

http://java.sun.com/javame/downloads/sdk30ea.jsp
Página de download do Java ME Platform SDK 3

http://java.sun.com/j2me/docs/pdf/CLDC-HI_whitepaper-February_2005.pdf
CLDC HotSpot™ Implementation Virtual Machine

http://wurfl.sourceforge.net/
WURFL: Wireless Universal Resource File

https://lwuit.dev.java.net/
LWUIT: Lightweight User Interface Toolkit

http://pt.wikipedia.org/wiki/JIT
JIT: Just-in-time

Saiba Mais

www.devmedia.com.br/articles/viewcomp.asp?comp=10574
Auditório Virtual DevMedia - Empreendedorismo na era dos Celulares

www.devmedia.com.br/cursos/listcurso.asp?curso=17
Curso Online - Introdução ao desenvolvimento para celulares com Java ME

www.devmedia.com.br/cursos/listcurso.asp?curso=96
Curso Online - Introdução ao uso de Web Services em Java ME

www.devmedia.com.br/cursos/listcurso.asp?curso=95
Curso Online - Criação de Interfaces Gráficas em Java ME

www.devmedia.com.br/cursos/listcurso.asp?curso=94
Curso Online - Java ME e Banco de Dados

www.devmedia.com.br/cursos/listcurso.asp?curso=93
Curso Online - Trabalhando com SMS e MMS em JavaME

www.devmedia.com.br/cursos/listcurso.asp?curso=92
Curso Online - Conectividade em Java ME

www.devmedia.com.br/cursos/listcurso.asp?curso=59
Curso Online - Desenvolvendo um Software Acadêmico para Celular

www.devmedia.com.br/articles/viewcomp.asp?comp=8585
Java Magazine 44 - A Plataforma Java ME: Parte 1

www.devmedia.com.br/articles/viewcomp.asp?comp=8569
Java Magazine 45 - Mini-curso: programação Java ME: Parte 2

www.devmedia.com.br/articles/viewcomp.asp?comp=10119
Java Magazine 46 - Programação Java ME: Parte 3

www.devmedia.com.br/articles/viewcomp.asp?comp=8547
Java Magazine 47 - Programação Java ME: Parte 4

www.devmedia.com.br/articles/viewcomp.asp?comp=8523
Java Magazine 48 - Programação Java ME: Parte 5

www.devmedia.com.br/articles/viewcomp.asp?comp=8303
Java Magazine 49 - Mini-curso de java ME: Parte 6

www.devmedia.com.br/articles/viewcomp.asp?comp=8379
Java Magazine 56 - Opinião: Android versus Java ME

www.devmedia.com.br/articles/viewcomp.asp?comp=8459
Java Magazine 56 - JSR 257: Contactless Communication API

www.devmedia.com.br/resumo/default.asp?idrev=123#1517
Java Magazine 57 - Web Feed Reader ME

www.devmedia.com.br/articles/viewcomp.asp?comp=10214
Java Magazine 60 - LWUIT: “Swing” para Java ME

www.devmedia.com.br/articles/viewcomp.asp?comp=11396
Java Magazine 64 - Google Maps em aplicações móvies

www.devmedia.com.br/articles/viewcomp.asp?comp=4895
WebMobile 11 - JavaME usando GPS

www.devmedia.com.br/articles/viewcomp.asp?comp=8057
WebMobile 15 - J2ME Polish - Desenvolvendo interfaces gráficas para aplicações JavaME

www.devmedia.com.br/articles/viewcomp.asp?comp=8275
webmobile 17 - Desenvolvimento ponta-a-ponta: cliente JavaME com servidor JavaEE

www.devmedia.com.br/articles/viewcomp.asp?comp=8274
WebMobile 17 - Criando jogos de ação em 2D com a API de jogos em JavaME

www.devmedia.com.br/articles/viewcomp.asp?comp=9353
WebMobile 18 - Desenvolvimento ponta-a-ponta: Cliente JavaME com servidor JavaEE

www.devmedia.com.br/articles/viewcomp.asp?comp=10333
WebMobile 20 - Escrevendo Jogos para Celular em JavaME

www.devmedia.com.br/articles/viewcomp.asp?comp=11440
WebMobile 22 - Construindo Interfaces Gráficas na Prática

www.devmedia.com.br/articles/viewcomp.asp?comp=9509
Vídeo - Introdução do Desenvolvimento de Jogos em Java ME - Parte 1

www.devmedia.com.br/articles/viewcomp.asp?comp=9749
Vídeo - Introdução do Desenvolvimento de Jogos em Java ME - Parte 2

www.devmedia.com.br/articles/viewcomp.asp?comp=9750
Vídeo - Introdução do Desenvolvimento de Jogos em Java ME - Parte 3
Estratégias de Integração de Aplicações Java EE

Marco Aurélio de Souza Mendes

É consultor independente de Arquitetura de Software e Engenharia de Software com mais de 16 anos de


experiência em projetos de TI. É também professor de pós-graduação dos cursos de Estratégias de
Arquitetura de Sistemas do IGTI e Engenharia de Software do IEC/PUC-Minas. Ele mantém artigos
diversos sobre Arquitetura, Java e Engenharia de Software no seu blog http://blog.marcomendes.com

De que se trata o artigo:

O artigo apresenta técnicas e práticas provadas de integração de aplicações Java com outras aplicações,
sistemas e bancos de dados. Projetos de integração apresentam muitas complexidades técnicas e muitas
possibilidades técnicas em Java tais como JMS, JCA, JDBC, RMI, HTTP e SOAP. O artigo mostra como
escolher estas tecnologias e como utilizá-las adequadamente através de padrões de integração de
aplicações (EIP).

Para que serve:

O artigo é útil para que desenvolvedores possam conhecer os riscos associados à integração de sistemas
Java com outros sistemas e aplicar as melhores práticas de mercado e padrões de integração para
mitigar e eliminar estes riscos. Projetos com riscos técnicos reduzidos possuem maior garantia de
sucesso, maior qualidade, estabilidade e manutenções mais simples e facilitadas.

Em que situação o tema é útil:

Em projetos de TI que requerem integrações de todo tipo, como bancos de dados relacionais, bases de
dados em outros formatos, sistemas de filas de mensagens, aplicações legadas em plataformas baixas e
altas ou aplicativos como CRM (Customer Relationship Management) e ERP (Enteprise Resource
Planning).

Estratégias de Integração de Aplicações Java EE:

Integrar aplicações Java com outros sistemas não é uma tarefa trivial. Para reduzir riscos e manter
projetos sob controle, devemos usar práticas provadas de integração de sistemas.

As melhores práticas de integração são denominadas padrões EAI (EAI Patterns) e foram popularizadas
por Gregor Hohpe através do seu livro Enterprise Integration Patterns. Estas práticas permitem que um
arquiteto ou desenvolvedor escolha as estratégias mais eficientes de integração de sistemas Java com
outros sistemas e produza soluções mais perenes e econômicas.

Estas práticas de integração também são fundamentais para empresas que estejam buscando iniciativas
SOA e BPM, pois permitem gerir adequadamente a criação e evolução de serviços (ativos) em
repositórios de serviços (ESB).

Um processo simples para que você possa aprender a integrar sistemas em projetos envolve os seguintes
passos: (1) Coletar os requisitos arquiteturais de interoperabilidade; (2) Desenvolver soluções
independentes de tecnologia para cada requisito arquitetural coletado; (3) Estudar os exemplos
disponibilizados na referência [1]; (4) Estudar as tecnologias Java mais adequadas para cada requisito
(ex: WebServices ou RMI); (5) Implementar os cenários com as tecnologias Java escolhidas; (6) Testar
as soluções em ambiente de homologação.

Em alguma empresa do Brasil, uma história comum...

Integrar aplicações não é uma tarefa trivial. Protocolos diversos, APIs exóticas, tecnologias e ambientes
complexos e cronogramas agressivos são alguns aspectos que desafiam diariamente analistas
desenvolvedores Java EE.
Conto aqui uma história que resume estes desafios. Uma equipe necessitava enviar emails de sua
aplicação Java. Uma tarefa aparentemente trivial, que muitos desenvolvedores vivem todos os dias. A
equipe não possuía experiência com a API JavaMail, mas com a ajuda de um recurso externo a equipe se
capacitou e desenvolveu o código necessário para suportar a integração, que neste caso ocorreria com o
servidor Microsoft Exchange. Código implementado, testes internos realizados com sucesso e aprovação
gerencial. Aparentemente o problema foi resolvido.

A vida real, entretanto, guarda surpresas. Na véspera da semana de implementação, o código foi
implantado na empresa do cliente (vamos chamá-la de empresa ACME), mas os casos de uso que
requeriam a integração com o Microsoft Exchange não funcionavam. Todos os olhos e atenções (e culpas)
foram lançados para o servidor de email da Microsoft. Após a descoberta que o servidor de email do
cliente (v2007) estava em uma diferente versão do ambiente interno da desenvolvedora (v2003),
modificações no ambiente de desenvolvimento foram realizadas para adequar o servidor. Nenhum
resultado positivo foi alcançado. O pânico se instalou!

Mais investigações foram realizadas e a equipe descobriu que o servidor de email de produção não residia
na mesma rede do servidor de aplicação do cliente, mas em um distante local remoto (em outra cidade)
em um provedor com um acesso controlado por um firewall. O analista de infra-estrutura do cliente não
foi questionado o bastante para informar este fato. Para encurtar a história, a equipe descobriu que o
envio de emails neste ambiente requeria o uso de certificados e protocolos com garantia de transporte
seguro (SMTP sobre SSL). Noites mal-dormidas, um atraso de algumas semanas no cronograma, um
cliente relativamente estressado e o desafio foi finalmente vencido.

Que lições podemos aprender com esta história? Enumero algumas abaixo:

• Certificar que as versões dos ambientes a serem integrados sejam exatamente iguais;
• Analisar cuidadosamente a topologia, isto é, a organização física do ambiente de produção do
cliente.

Diagramas UML de implantação são uma ferramenta útil neste aspecto;

• Analisar os protocolos e requisitos não-funcionais que possam interferir na interoperabilidade;


• Antecipar testes realizados na plataforma real do cliente.

O leitor mais experiente pode questionar as lições aprendidas neste exemplo. “Estas lições são óbvias”.
Mas devemos lembrar que senso comum não é prática comum.

Dica: Seja um “Indiana Jones” de aplicações Java na sua empresa. Faça uma arqueologia de software nas
aplicações Java da sua empresa e colete os erros e lições aprendidas. Os erros do passado são uma
excelente fonte de aprendizado para projetos futuros.

O objetivo deste artigo é mostrar a você como tornar senso comum em prática comum, isto é, evitar
erros comuns de integrações de sistemas em projetos e evitar estresses desnecessários. Vamos abordar
estratégias e técnicas para organizar o seu trabalho, organizados da seguinte forma nas seções seguintes
deste artigo:

• Estilos, Níveis e Topologias de Integração. Iremos conhecer aqui fundamentos da integração de


sistemas;
• Tecnologias para Interoperabilidade Java. Aqui iremos citar as principais tecnologias e padrões
Java para a integração de aplicações;
• Padrões de Integração (EIP). Iremos conhecer aqui as melhores práticas de mercado para
integrar aplicações;
• Processo para Integrar Aplicações. Vamos abordar aqui as principais tarefas necessárias durante
um projeto para garantir uma boa interoperabilidade de suas aplicações Java.

Estilos, Níveis e Topologias de Integração

“Se você se conhece, mas não o inimigo, para cada vitória sofrerá uma derrota”, Sun Tzu, A Arte da
Guerra.

Como conhecer o inimigo? Primeiramente devemos compreender que interoperabilidade é um mecanismo


arquitetural, ou seja, uma preocupação importante de um projeto e que, portanto, requer a atenção
técnica ainda no começo do projeto. Devemos investigar as interoperabilidades requeridas no nosso
projeto através do estilo, nível e topologia associados, pois para cada um haverá uma abordagem técnica
diferenciada e tecnologias distintas em Java.
Dica: Colete os requisitos arquiteturais diretamente com representantes do seu cliente em reuniões
conjuntas com o analista de requisitos. Normalmente analistas de requisitos têm dificuldade de capturar
e expressar informações técnicas, o que implica em informações truncadas e novas descobertas
indesejadas no final do projeto.

A interoperabilidade de aplicações pode se apresentar em quatro estilos, conforme Gregor Hohpe [1].

1. O primeiro estilo é a transferência de arquivos, onde duas aplicações compartilham as


informações através de um arquivo texto ou binário. O protocolo FTP é um exemplo popular de
tecnologia que suporta este estilo;
2. O segundo estilo, talvez o mais popular, é através de dados compartilhados em bancos de dados;
3. O próximo estilo é através de procedimentos remotos (RPC), onde um programa cliente invoca
uma funcionalidade em outra máquina. As tecnologias de WebServices são exemplos
normalmente associados a RPC;
4. O último estilo é através de passagem de mensagens. Programas com suporte a filas de
mensagens, como a especificação Java JMS, são exemplo deste estilo.

Pare um minuto e responda: Qual o estilo requerido no exemplo citado de interoperabilidade com o
Microsoft Exchange? (Resposta no final deste artigo).

Além dos estilos, podemos classificar a interoperabilidade em níveis, listados a seguir:

1. Nível de dados. Como integrar fontes de dados relacionais ou em outros formatos e como reduzir
a sua redundância? A integração em nível de dados foca na movimentação de dados entre
aplicações com o objetivo de compartilhar o mesmo dado entre aplicações diferentes;
2. Nível de Interfaces de Aplicação (API). Como integrar APIs de aplicação em outras tecnologias
que não sejam Java? A integração via APIs passa pela chamada de funções através de protocolos
síncronos (RPC) ou assíncronos (mensagens);
3. Nível de Processos de Negócio. Domínio do mundo BPM/SOA, a integração em nível de processos
de negócio foca no desenvolvimento de componentes de alto nível que irão fornecer interfaces de
alto nível que podem ser considerados serviços;
4. Nível de Apresentação. Popularmente conhecidos como mash-ups, integram aplicações através de
um conjunto de vários portlets (janelas visuais independentes) que residem em um determinado
portal. A Figura 1 mostra um exemplo de integração neste nível.

Figura 1: Mashup do internet google

Nota Devman - Requisitos Funcionais e Requisitos Não-Funcionais

Requisitos funcionais expressam os desejos dos interessados do projeto na perspectiva da funcionalidade.


Exemplos incluem cadastros, relatórios e fluxos de trabalho. Requisitos não-funcionais expressam
atributos de qualidade de um sistema tais como desempenho, usabilidade, disponibilidade ou
portabilidade.

Requisitos funcionais e não-funcionais importantes para o negócio (prioritários) e complexos formam o


conjunto de requisitos arquiteturais que devem ser foco inicial de qualquer projeto de TI.

Requisitos funcionais e não-funcionais coletados podem responder a perguntas sobre escolhas de


estratégias e tecnologias sobre interoperabilidade. Por exemplo, restrições sobre desempenho podem
eventualmente influenciar na escolha de uma integração com soquetes ao invés de XML e Web Services.

Um novo teste: Qual o nível associado ao exemplo citado de interoperabilidade com o Microsoft
Exchange? (Resposta no final deste artigo).

Finalmente, devemos considerar também a questão da topologia da aplicação. Dois modelos clássicos
existem:

1. Comunicação Ponto a Ponto. A integração ponto a ponto é também chamada de integração


oportunista e ocorre diretamente entre dois sistemas que queiram conversar. Quando conectamos
uma TV diretamente a um DVD, usamos intuitivamente esta topologia de integração;
2. Comunicação Hub and Spoke. Topologia popularizada pelos ESB (um tipo particular de Hub and
Spoke), esta topologia usa um mediador central para comunicar cada aplicação (Spoke) que
deseje conversar. Nenhuma aplicação conversa diretamente com outra aplicação. Isto pode
parecer confuso e complexo, mas considere (na sua sala de TV dos sonhos) como você poderia
integrar adequadamente uma televisão digital, um computador, um sistema de som 7.1, um DVD,
um vídeo-cassete (para as fitas de formatura da sua família), um projetor e um vídeo-game de
última geração. É claro neste exemplo que usar um aparelho central para gerir todas as conexões
é mais vantajoso. O Home-Theater é um hub na sua sala de TV dos sonhos e pode ligar todos os
fios dos aparelhos (Figura 2).

Figura 2: A topologia de comunicação ponto a ponto liga duas


aplicações diretamente. A topologia Hub and Spoke usa um barramento
central (Hub) para ligar múltiplos clientes.
Mais um desafio: Qual a topologia associada ao exemplo citado de interoperabilidade com o Microsoft
Exchange? (Resposta no final deste artigo).

Dica: Colete os requisitos arquiteturais de interoperabilidade na sua aplicação. Para cada requisito
arquitetural, defina o estilo, nível e topologia associada.

Tecnologias de Interoperabilidade Java

“Um chefe deve empregar táticas variadas de acordo com os tipos de terreno”, Sun Tzu.

A plataforma Java foi desenhada e evoluída nos últimos anos com um grande suporte para
interoperabilidade. Considere como exemplo simples a especificação JDBC. Projetada de forma elegante
para suportar a portabilidade de sistemas operacionais e bancos de dados, ela é um exemplo que suporta
a integração do estilo (ver Tabela 1) banco de dados compartilhados, em nível (ver Tabela 2) de dados.

Diversas outras tecnologias foram projetadas para suportar outros estilos e níveis. Apresentamos alguns
exemplos nas Tabelas 1, 2 e 3, sem nos ater aos detalhes da API, que podem ser encontrados em artigos
anteriores da revista Java Magazine.

As Tabelas 1, 2 e 3 mostram que é fundamental escolher adequadamente a tática (tecnologia) conforme


o tipo de terreno (estilo, nível ou topologia).

Tabela 1: Tecnologias Java para suportar estilos de interoperabilidade

Tabela 2: Tecnologias Java para suportar níveis de interoperabilidade

Tabela 3: Tecnologias Java para suportar topologias de interoperabilidade

Dica: Não use as tecnologias Java no primeiro combate a um problema de interoperabilidade. A


tecnologia é secundária e deve ser considerada apenas após a correta coleta e desenvolvimento dos
requisitos arquiteturais e definição dos estilos, níveis e topologia.

Padrões de Integração de Aplicações (EIP)

“Um soberano iluminado estuda deliberadamente a situação e um bom general lida cuidadosamente com
ela. Se não é vantajoso, nunca envie suas tropas; se não lhe rende ganhos, nunca utilize seus homens;
se não é uma situação perigosa, nunca lute uma batalha precipitada.”, Sun Tzu.

Conhecemos o inimigo. Conhecemos as ferramentas. Entretanto, a grande chave na interoperabilidade de


aplicações é saber como aplicar as tecnologias Java a nosso favor. Neste aspecto, introduzimos o conceito
de um padrão de integração de aplicações.

Um padrão EIP é uma solução provada aplicável ao contexto de integração de aplicações corporativas.

O livro Enterprise Integration Patterns [1] apresenta 65 padrões de integração de sistemas. Cada padrão
resolve um problema particular no contexto de integração de aplicações corporativas e pode ser usado
junto com outros padrões EIP para solucionar um problema do mundo real.
Consideremos, para ilustrar o conceito, um problema similar ao citado no começo deste artigo.
Poderíamos formalizar os requisitos arquiteturais da seguinte forma:

R1. A aplicação ACME deve interoperar com o servidor Microsoft Exchange 2007 através de
protocolos SMTP para envio de emails.

R2. O transporte para interoperabilidade com o servidor de emails deve garantir confidencialidade
e integridade das informações enviadas através do uso do protocolo SMTP sobre SSL. Ao lermos
o livro EAI Patterns, capturamos alguns padrões candidatos para o problema acima. Estes
padrões são documentados na Tabela 4.

Tabela 4: Padrões EAI usados para resolver a interoperabilidade com o Microsoft Exchange
O primeiro padrão da Tabela 4 nos diz que a solução provavelmente será resolvida com um mecanismo
assíncrono. Dadas as restrições de protocolos no requisito R1, vemos que a especificação Java nos sugere
a especificação JavaMail.

O segundo e terceiro padrões nos diz que teremos um canal para o envio e recebimento de informações e
este canal deve se conectar a um sistema de email como o Microsoft Exchange. Ao examinarmos a classe
Transport, da API do JavaMail, veremos que ela possui esta função. Naturalmente, um Message Endpoint
requer uma implementação da especificação JavaMail. Assumamos neste exemplo que usamos o JBoss
AS para esta solução.
O quarto padrão nos diz que devemos enviar um documento textual. Ao observamos novamente a classe
Transport, notamos que ela possui um método send() que espera como argumento um objeto do tipo
Message, que modela um Document Message.

O quinto padrão endereça os requisitos de segurança. Um enriquecedor de conteúdo permite adicionar à


mensagem original informações adicionais. No nosso caso, estas informações adicionais serão de
segurança, que nos permite que a mensagem seja entregue em servidores que operem sobre protocolo
SSL. Ao examinarmos a hierarquia da classe Transport, vemos a classe SMTPSSLTransport. Esta classe
fornece a funcionalidade de envio de emails sobre canal seguro (confidencialidade e integridade).

O último padrão está fora do escopo Java, mas ele indica que alguém deve configurar o servidor
Exchange para não aceitar mensagens SMTP, mas somente mensagens SMTP sobre SSL. O padrão
Consumidor Seletivo representa este mecanismo.

Se conectarmos os padrões EIP teremos o desenho da nossa solução apresentado na Figura 3.

Figura 3: Esquema da solução de integração com o MS Exchange Server

Nota Devman - Diagramas de Casos de Uso

Diagramas de casos de uso são visualizações de negócio usadas por analistas de requisitos para o auxílio
na coleta e entendimento de requisitos. Estes diagramas possuem atualmente grande popularidade na
comunidade de analistas e podem ser ferramentas poderosas para a coleta de interoperabilidades. No
nosso exemplo, poderíamos representar a interoperabilidade do envio do e-mail com o seguinte
diagrama.

Note que neste diagrama o Microsoft Exchange suporta a operação de envio de email e por isso é
chamado de Ator Secundário.

O trecho de código abaixo mostra estas peças conectadas.

Listagem 1. Fragmento de Código para Envio de Email.

Security.addProvider(new com. sun.net.ssl.internal.ssl.Provider());


Properties props = new Properties();
props.put("mail.smtp.host", SMTP_HOST_NAME); // Servidor de email
props.put("mail.smtp.auth", "true"); // Requer autenticacao
props.put("mail.debug", "true");
props.put("mail.smtp.port", SMTP_PORT); // Porto do servidor de email
props.put("mail.smtp.socketFactory.port", SMTP_PORT);
props.put("mail.smtp.socketFactory.class", // Enriquecedor da mensagem com SSL
"javax.net.ssl.SSLSocketFactory" );
props.put("mail.smtp.socketFactory. fallback", "false");

Session session = Session.getDefaultInstance (props, new javax.mail.Authenticator() {


protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication ("username", "password");
}
});

Message message = new MimeMessage(session);


InternetAddress addressFrom = new InternetAddress(from);
message.setFrom(addressFrom);
message.setSubject(“OLÁ EIP”);
message.setText(“Padrões EIP organizam e comunicam as soluções Java!”);
Transport.send(message);
...

Este fragmento de código pode ser entendido como a realização do esquemático da Figura 3.
Para um problema simples, naturalmente, podemos até ir intuitivamente para uma tecnologia ou um
código, mas isso é como começar a correr uma maratona sem preparação física adequada. Os padrões
representam esta preparação física. Eles permitem que você pense nos aspectos da solução sem se ater
aos detalhes da tecnologia. Ao escolheremos uma tecnologia Java determinada, ela será a conseqüência
dos padrões, que naturalmente devem suportar os requisitos arquiteturais especificados.

Para problemas complexos de integração do mundo real, entretanto, devemos conhecer e aplicar os
padrões EIP. Outra excelente fonte de padrões é o livro POSA [2], que contém mais de uma centena de
padrões arquiteturais, entre eles diversos padrões EIP.

Como exemplo de padrões EIP usados para modelar aplicações com integração complexa, dita aplicações
EAI, poderíamos citar os padrões descritos na Tabela 5.

Tabela 5: Padrões EAI tipicamente usados em aplicações BPM/SOA/ESB. .


Um Processo para Interoperar Aplicativos

“Se um general sabe o lugar e a hora de uma batalha, ele pode conduzir as suas tropas para até mil
milhas, mesmo para uma batalha decisiva. Se ele não sabe nem o lugar nem a hora de uma batalha,
então o seu lado esquerdo não pode ajudar a sua direita e a ala direita não pode salvar a esquerda; a
tropa da frente não pode auxiliar a tropa da retaguarda, nem a tropa da retaguarda pode ajudar a tropa
da frente.”, Sun Tzu
1. Colete os requisitos arquiteturais de interoperabilidade. Uma boa fonte para isso são atores
secundários em diagramas de casos de uso. Eles normalmente revelam sistemas e aplicações que
devem ser integrados. Especifique detalhadamente estes requisitos para que não existam dúvidas
sobre as versões de servidores, topologias e protocolos exatos a serem utilizados;
2. Desenvolva soluções independentes de tecnologia para cada requisito arquitetural de
interoperabilidade. Para isso, identifique os estilos, níveis e topologias de cada requisito
arquitetural de interoperabilidade. Expresse então a sua solução através dos padrões EIP
encontrados em [1] e [2];
3. Estude os exemplos (mais complexos e além do escopo deste artigo) disponíveis em [1];
4. Para cada solução independente de tecnologia, escolha as tecnologias Java mais adequadas para
resolver o seu problema. Use as Tabelas 1, 2 e 3 como ponto de partida e então estude as
especificações de cada tecnologia apresentada e produtos Java que suportem estas
especificações;
5. Prove a sua solução. Talvez a dica mais valiosa, a sua solução deve ser provada com um código
real e que possa capturar um cenário de utilização do seu sistema;
6. Teste a sua solução em ambiente idêntico ao de produção. Envolva o usuário e faça testes reais e
significativos, pois este é o passo mais complexo. Aplicações não testadas adiam riscos e podem
levar a problemas graves no fim do projeto. Em cenários mais complexos onde você não tem
acesso ao aplicativo sendo interoperado, considere o uso de ferramentas como o jMOCK ou
EasyMock. Recomendo, neste particular, o excelente artigo Mocks Aren´t Stubs, de Martin Fowler,
e o artigo “Testando com Mocks de forma fácil”, Edição 62;
7. Garanta que você realizou os passos 1-5 no começo do projeto. Requisitos de interoperabilidade
são complexos e devem ser endereçados no INÍCIO do projeto. Se você usa um processo
derivado do UP (Unified Process), os itens 1-5 para todos os requisitos arquiteturais devem estar
finalizados até o 3/10 temporal do seu projeto. Se você usa processos ágeis, enderece estes
requisitos nos primeiros sprints/iterações.

Conclusões

Este artigo apresentou, de forma introdutória, princípios que devem nortear aplicações que requerem
integrações de sistemas. Coloco abaixo um guia de estudo para os mais interessados no assunto.

1. Certifique-se de conhecer os fundamentos de integração de sistemas. Estilos, topologias e


especificações são fundamentais e livros como [1] e [2] são fontes fundamentais;
2. Estude antecipadamente as ferramentas e APIs Java para suportar os estilos e topologias acima;
3. Nunca acredite em tecnologias que não foram provadas no seu projeto. Negocie tempo do seu
gerente de projeto para provar conceitos e realizar testes de integração. Se você não realizar
esta negociação no começo do projeto, espere estresses, noites mal dormidas e problemas na
entrega do seu código;
4. Lembre-se que o mundo não é centrado em Java. Você precisará conhecer peças e protocolos
além da linguagem Java para se tornar um bom arquiteto de integrações;
5. Teste o seu código de integração. Já observei diversos problemas em projetos devido a processos
de testes de integração falhos e que aconteceram literalmente na entrega do projeto.

O leitor curioso deve ter notado o uso de ícones diferenciados para representar padrões EIP. Estes ícones
estão disponíveis gratuitamente em formato Visio no próprio portal de Gregor Hohpe (EAI Patterns).

Para o leitor ainda mais curioso que busca aqui as respostas para as perguntas feitas ao longo do artigo,
o problema tem como solução: o estilo Passagem de Mensagens, integração em Nível de Aplicação e
topologia Ponto a Ponto.

Boas integrações!

Links

martinfowler.com/articles/mocksArentStubs.html
Descreve o conceito de Mocks e que ele pode ser usado para simular aplicações externas no contexto de
projetos de integração de aplicações.

eaipatterns.com/
Site central dos padrões EIP e que resume muito material interessante sobre integração de aplicações.

eaipatterns.com/downloads.html
Recursos sobre EIP e em particular o stencil (plugin) Microsoft Visio para desenhar EIP.

epf.eclipse.org/wikis/openup/
Open UP. Processo de desenvolvimento gratuito baseado nos conceitos do UP.

Livros

[1] Enterprise Integration Patterns, Gregor Hohpe, Addison Wesley, 2003


Referência primária sobre a integração de aplicações corporativas, escrito por Gregor Hohpe, arquiteto de
soluções do Google.

[2] Pattern Oriented Software Architecture - vol 05, Buschmann e al., Wiley and sons, 2007
Continuação do clássico livro POSA - vol 01, de 1996. O primeiro livro sobre padrões arquiteturais de IT e
referência obrigatória para sistemas que requeiram integrações complexas.

[3] A Arte da Guerra - Sun Tzu, Século IV A.C.


Clássico livro sobre estratégias e táticas de guerra. Ganhou popularidade recentemente no mundo dos
negócios ao ser adaptado por vários autores para situações “militares” vividas em projetos de TI por
gerentes, analistas e desenvolvedores.

Saiba Mais

www.devmedia.com.br/articles/viewcomp.asp?comp=8984
Java Magazine 32 - Integração com JBI

www.devmedia.com.br/articles/viewcomp.asp?comp=8375
Java Magazine 50 - Integração Simples com XML-RPC

www.devmedia.com.br/articles/viewcomp.asp?comp=8380
Java Magazine 56 - Web Service REST

www.devmedia.com.br/articles/viewcomp.asp?comp=9422
Java Magazine 57 - Oracle BPEL: orquestra afinada de sistemas

www.devmedia.com.br/articles/viewcomp.asp?comp=10203
Java Magazine 59 - EAI na prática

www.devmedia.com.br/articles/viewcomp.asp?comp=10202
Java Magazine 59 - JBoss ESB

www.devmedia.com.br/articles/viewcomp.asp?comp=10816
Java Magazine 62 - SOA na prática - Parte 1

www.devmedia.com.br/articles/viewcomp.asp?comp=11057
Java Magazine 63 - SOA na prática - Parte 2
Aplicações Concorrentes em Java

RONALDO BLANC ROCHA

Consultor Java EE pela Summa Technologies do Brasil, atuando há 9 anos com Java/Java EE.

De que se trata o artigo:


Desenvolvimento de software com processamento concorrente usando a API java.util.concurrent. E a
comparação entre o esforço utilizado para desenvolver um software concorrente utilizando as primitivas
de concorrência e utilizando a API.

Para que serve:


Aumentar a qualidade, o desempenho e a escalabilidade de software com processamento concorrente. As
classes da API são bem testadas e confiáveis, e tornam o código mais compreensivo, facilitando
atividades de manutenção.

Em que situação o tema é útil:


Com a evolução dos computadores de múltiplos núcleos, e a crescente demanda por processamento,
desenvolver softwares concorrentes com eficiência, facilidade, escalabilidade e boa manutenibilidade são
alguns dos objetivos mais comuns para empresas de desenvolvimento de software.

Aplicações concorrentes em Java:


O Java, assim como outras linguagens, escolheu o modelo de threads para tratar tarefas que podem ser
executadas simultaneamente. Esse modelo tem suas vantagens e desvantagens (assim como qualquer
modelo). O Java sempre trouxe a concorrência como um recurso da linguagem, não algo externo. Mesmo
assim, desenvolver aplicações concorrentes sempre foi uma tarefa muito complexa. Com a introdução da
java.util.concurrent, a complexidade para desenvolver tais aplicações diminuiu muito. Utilizando essa
ferramenta, mesmo programadores menos experientes podem escrever aplicações concorrentes com
qualidade e segurança, obtendo ótimos resultados.

A evolução dos processadores com um único núcleo baseada em clocks com maior freqüência atingiu o
limite do aquecimento suportado e um custo/beneficio inviável. Esses processadores enfrentavam ainda
outros problemas, como alto consumo de energia e gargalo no acesso à memória. Portanto, novas
alternativas para a melhoria de desempenho e consumo ganharam destaque, entre elas o paralelismo de
threads e cores. Os processadores de múltiplos núcleos (MC) e a tecnologia SMT (Simultaneous
Multithreading) permitem que tarefas sejam executadas em paralelo melhorando o aproveitamento dos
recursos. Para tirar-se maior proveito desses avanços, é necessário mudar o modo de pensar e escrever
aplicações, substituindo-se o paradigma seqüencial pelo concorrente. Dentre os desafios que esse
paradigma apresenta, alguns merecem destaque: identificar o que pode ser executado em paralelo
(vamos chamar de thread cada parte do código com essa característica) e a sincronização dos threads.

O que são Threads?

Os sistemas operacionais, em sua maioria, suportam processos, isto é, programas independentes que
rodam com algum grau de isolamento. Thread é um recurso que permite múltiplas atividades ocorrendo
ao mesmo tempo em um mesmo processo. O Java foi uma das primeiras linguagens, com boa aceitação
no mercado, que trouxe as threads como parte da linguagem, não as tratando apenas como um recurso
controlado pelo sistema operacional.

As threads assim como os processos, são independentes e concorrentes, tendo variáveis locais e pilha
(stack) próprias, mas compartilham a memória e estado do processo em que participam. Dessa forma,
elas têm acesso aos mesmos objetos alocados no heap. Apesar de facilitar a troca de dados, deve-se
tomar cuidado para garantir que isto não resulte em erros de concorrência (data races).

Todos os programas escritos em Java rodam threads, pelo menos uma (main). A JVM cria outras threads
que geralmente não são percebidas, como: Garbage Collection (GC), threads da AWT/Swing; em
servidores de aplicação conectores de HTTP, EJB etc.

A API do Java para threads é muito simples. Entretanto, escrever programas que as utilizem com
eficiência é um trabalho complexo. Então, porque utilizá-las se são tão complicadas? Algumas das
principais razões são:

Aproveitar as vantagens dos computadores multiprocessados;


Executar tarefas assíncronas ou em background;
Tornar a interface com o usuário mais fluida;
Simplificar a modelagem.

Antes da API java.util.concurrent, trabalhar com threads em Java exigia um grande esforço, pois os
recursos disponíveis são muito simples e de baixo nível.

No exemplo da (Listagens 1 a 4), apresenta-se a implementação de um problema clássico de produtor e


consumidor, utilizando os recursos primitivos de concorrência.

Listagem 1. Produtor/Consumidor com Semáforo

package br.com.jm.concurrent;

public class Queue {

private static final Integer MAX_ITENS = 5000;

private Integer itens = new Integer(0);

private boolean firstThousand = false;

private synchronized boolean isFull() {

if (this.itens >= MAX_ITENS) {

if (!this.firstThousand)

this.firstThousand = true;

return true;

else {

return false;

private synchronized boolean isEmpty() {

if (this.itens <= 0) {

return true;

else {

return false;
}

public synchronized void add() {

while (isFull()) {

try {

synchronized (this) {

wait();

System.out.println(Thread.currentThread()

.getName() + " waiting");

catch (InterruptedException e) {

e.printStackTrace();

itens++;

System.out.println(Thread.currentThread()

.getName()

+ " produziu um valor. Total [" + this.itens + "] ");

synchronized (this) {

notifyAll();

public synchronized void remove() {

while (isEmpty()) {

try {

synchronized (this) {

wait();

System.out.println(Thread.currentThread().

getName() + " waiting");

}
catch (InterruptedException e) {

e.printStackTrace();

itens--;

System.out.println(Thread.currentThread().

getName()

+ " consumiu um valor. Total

[" + this.itens + "]");

synchronized (this) {

notifyAll();

public synchronized boolean isRemovable() {

return this.firstThousand;

Listagem 2. Produtor/Consumidor com Semáforo

package br.com.jm.concurrent;

public class Producer extends Thread {

private Queue queue;

public Producer(Queue queue, String string) {

this.queue = queue;

this.setName(string);

public void run() {

while (true) {
try {

sleep(1);

catch (InterruptedException e) {

e.printStackTrace();

queue.add();

Listagem 3. Produtor/Consumidor com Semáforo

package br.com.jm.concurrent;

public class Consumer extends Thread {

private Queue queue;

public Consumer(Queue queue, String string) {

this.queue = queue;

this.setName(string);

public void run() {

while (true) {

try {

sleep(random());

catch (InterruptedException e) {

e.printStackTrace();

if (queue.isRemovable())

queue.remove();

}
private long random() {

Double random = Math.random() * 100;

return (long) (random.intValue() % 10);

Listagem 4. Produtor/Consumidor com Semáforo

package br.com.jm.concurrent;

public class ProducerConsumer {

private static final int MAX_PRODUCER = 2;

private static final int MAX_CONSUMER = 6;

public static void main(String args[]) {

Queue queue = new Queue();

// Producers

Producer producer = null;

for (int i = 0; i < MAX_PRODUCER; i++) {

producer = new Producer(queue, "Producer " + i);

producer.start();

Consumer consumer = null;

// Consumers

for (int i = 0; i < MAX_CONSUMER; i++) {

consumer = new Consumer(queue, "Consumer " + i);

consumer.start();

A classe principal ProducerConsumer instancia alguns Producers, que geram dados, e Consumers, que os
utilizam. Ambos se comunicam por intermédio da fila customizada (representada na classe Queue),
implementada utilizando recursos primitivos do Java como synchronized, além dos algoritmos específicos
a este tipo de fila. Por exemplo, o método add() não pode inserir elementos se a fila já estiver “cheia”,
neste caso o recurso de bloqueio seguido de retry (ver o loop while(isFull()) com um wait()) garante a
integridade; assim, se a fila estiver cheia, a thread-produtor que está tentando inserir ficará em wait()
até que alguma thread-consumidor remova um elemento com remove(), que ao final faz um notifyAll(),
acordando o produtor para o próximo retry. Todo este código é bastante trabalhoso e complexo, além de
limitado, em escalabilidade e outras qualidades.

O conceito Thread-safe

Como é difícil definir se uma classe é thread-safe , alguns pontos devem ficar claros:

Para uma classe ser considerada thread-safe, primeiro ela deve funcionar bem em um ambiente não
concorrente, ou seja, deve ser implementada de acordo com as especificações e qualquer operação
(leitura ou alteração de valores utilizando membros ou métodos públicos) realizada em objetos dessas
classes não devem colocá-los em estado inválido. Além disso, a classe deve funcionar bem em ambientes
multi-thread. Nesse cenário, a estratégia de agendamento e tempo de execução, ou sincronizações
adicionais necessárias ao código, não devem influenciar o bom funcionamento da classe. Para que as
operações executadas por um objeto thread-safe atendam esses critérios, elas devem estar bem
encapsuladas e a integridade dos dados deve ser mantida de forma transparente.

Antes do Java 5, o único mecanismo para garantir que as classes atendiam o conceito thread-safe era a
primitiva synchronized. Assim, as variáveis compartilhadas entre múltiplas threads precisavam ser
sincronizadas para que as operações de leitura e alteração de valores fossem coordenadas.

Mesmo que não exista nenhuma linha de código que indique explicitamente o uso de threads em sua
aplicação, o uso de frameworks e alguns recursos podem exigir que as classes que os utilizam sejam
thread-safe. Mas desenvolver classes thread-safe exige muito mais atenção e análise do que classes não
thread-safe.

Servlets containers, por exemplo, criam muitas threads e uma determinada Servlet pode ter acessos
simultâneos para atender múltiplas requisições, por isso, uma classe Servlet deve ser thread-safe.

A Listagem 5 mostra o exemplo de uma Servlet não thread-safe (a princípio o código parece correto) que
salva o endereço das máquinas que acessam o servidor. Entretanto, a Servlet não é thread-safe, pois a
classe HashSet também não é. Nessas condições, poderíamos perder algum dado ou corromper o estado
da coleção. O modo simples de corrigir esse problema, utilizando a nova API, seria usar uma das coleções
seguras, criando o objeto ipAdressSet da seguinte forma:
private Set<String> ipAddressSet = Collections.synchronizedSet(new HashSet<String>());

É muito interessante observar que a nova API simplifica o trabalho do desenvolvedor, ao mesmo tempo
em que exige um maior conhecimento sobre concorrência, mesmo para um exemplo tão simples quanto
esse.

Listagem 5. Servlet não thread-safe

public class ServletNaoSeguro extends HttpServlet{

private Set<String> ipAddressSet = new HashSet<String>();

protected void doGet(HttpServletRequest httpServletRequest,

HttpServletResponse httpServletResponse) throws ServletException,

IOException {

String ipAddress = httpServletRequest.getRemoteAddr();

if (ipAddressSet != null)

ipAddressSet.add(ipAddress);

Coleções Seguras

Na plataforma Java (desde a JDK 1.0) existem coleções thread-safe como Hashtable e Vector. A API de
Collections (introduzida no JDK 1.2) introduz muitas coleções sem esta característica (como ArrayList,
HashSet etc.), mas podemos transformá-las em thread-safe, com o uso das factories:
Collections.synchronizedSet;
Collections.synchronizedMap;
Collections.synchronizedList.

Essas coleções geralmente têm baixa performance e alguns problemas, principalmente quando o
processamento depende de dados processados anteriormente. Chamadas aos métodos Iterator.hasNext()
e Iterator.next() podem lançar uma ConcurrentModificationException, pois alguma thread pode ter
alterado a coleção. Analogamente, testar se um objeto existe na lista antes de inseri-lo, ou recuperar o
tamanho da coleção (por exemplo um List.size()), pode provocar “data races”. Esses cenários são
conhecidos pelo nome "conditionally thread-safe".

Além desses recursos, a java.util.concurrent traz novas coleções como: ConcurrentHashMap,


CopyOnWriteArrayList e CopyOnWriteArraySet. Os propósitos dessas classes são alta performance e alta
escalabilidade para classes thread-safe. O uso dessas classes cumprirá seus objetivos em todas as
aplicações que não tenham a necessidade de bloquear toda a coleção para evitar alterações. A
ConcurrentHashMap, por exemplo, é feita para cenários de alta concorrência e leva tão a serio essa idéia
que muito raramente bloqueia em locks, mesmo por pouco tempo. As coleções da nova API retornam
“weakly consistent iterators” ao contrario das coleções da java.util que retornam "fast-fail iterators".

Desde a introdução da java.util.concurrent, você deve encarar estes recursos como meros quebra-galhos
- talvez para código legado que você não tenha tempo de revisar mais profundamente com a nova API.

As classes CopyOnWriteArrayList e CopyOnWriteArraySet sempre criam uma cópia da lista quando ela é
alterada (add(), set() ou remove()), assim as threads que estão iterando sobre a lista continuarão
trabalhando sobre a mesma lista que começaram. Mesmo com o custo de copiar a lista, na maioria dos
casos, o numero de iterações é bem maior que o de modificações. E o custo de tais cópias é menor do
que o normal devido à otimização copy-on-write: grosso modo, cada thread só copia de forma
incremental os elementos que alterou, não a coleção inteira. Para esses cenários as novas classes
oferecem melhor performance e permitem maior concorrência que as outras.

Os “fast-fail iterators” lançam uma ConcurrentModificationException (como explicado anteriormente) pois


consideram que a lista não pode ser alterada, seja por outra thread ou pelo mesmo, enquanto está sendo
percorrida. Já para os “weakly consistent iterators” se um objeto foi removido, ele não será retornado e
se um objeto foi adicionado, ele pode ou não ser retornado. Existe, porém, uma garantia de que o
mesmo objeto não será retornado mais de uma vez em uma mesma iteração. Para coleções com ordem
estável, os objetos retornados virão na ordem correta, não importa como a coleção foi alterada.

As listas baseadas em arrays (como Vector) ou criadas com uma das factories disponíveis na nova API
(como Collections.synchronizedList()) retornam “fast-fail iterators” e para evitar o cenário “conditionally
thread-safe” a thread que está iterando sobre a lista teria que copiar toda a lista ou bloqueá-la, sendo
qualquer uma dessas soluções muito custosa.

A classe ConcurrentHashMap permite maior concorrência que as outras alternativas de mapas thread-
safe. O modo utilizado pelas outras implementações é sincronizar todos os métodos, o que nos leva a
dois problemas:

1. Falta de escalabilidade, pois uma única thread pode acessar a coleção durante o mesmo período;
2. A possibilidade de falha na segurança do acesso (“conditionally thread-safe”) que exige
sincronização externa às classes.

Com a técnica de sincronizar tudo, independente das operações que outras threads queiram realizar na
coleção, estas precisam esperar pela primeira thread terminar sua execução. Já a ConcurrentHashMap
permite várias operações de leitura quase concomitantes, operações de leitura e gravação simultâneas
(pode-se ler e gravar dados ao mesmo tempo) e múltiplas gravações concorrentes (várias threads
gravando ao mesmo tempo).

A JDK 5 incluiu uma nova estrutura de dados, a Queue, que é uma interface bem simples.

Simplesmente bloquear a thread também permite maior controle sobre o fluxo de dados (se os
produtores estão colocando objetos na fila muito mais rápido que os consumidores, bloquear os
produtores tornará o consumo mais rápido). As implementações dessa interface são:
LinkedBlockingQueue - Fila FIFO com ou sem limites baseada em nós ligados (como se fosse uma
•
LinkedList);
PriorityBlockingQueue - Fila sem limites e com prioridades(não FIFO);
ArrayBlockingQueue - Fila FIFO com limites baseada num array;
SynchronousQueue - Uma fila “degenerada” que não comporta nem um único elemento, mas
facilita a sincronização entre threads (permite o hand-off, ou transferência direta, de elementos
diretamente do produtor para o consumidor).

No exemplo da Listagens 6 a 8 apresenta-se a implementação de um produtor/consumidor utilizando


BlockingQueue.

Listagem 6. Producer/Consumer utilizando BlockingQueue

package br.com.jm.concurrent.bq;

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class ProducerConsumer {

public static void main(String... args) throws Exception {

BlockingQueue<String> q = new ArrayBlockingQueue<String>(10);

ExecutorService executor = Executors.newCachedThreadPool();

//2 produtores

executor.execute(new Producer(q));

executor.execute(new Producer(q));

//6 consumidores

executor.execute(new Consumer(q));

executor.execute(new Consumer(q));

executor.execute(new Consumer(q));

executor.execute(new Consumer(q));

executor.execute(new Consumer(q));

executor.execute(new Consumer(q));

Listagem 7. Producer/Consumer utilizando BlockingQueue

package br.com.jm.concurrent.bq;

import java.util.concurrent.BlockingQueue;

public class Producer implements Runnable {

private final BlockingQueue<String> queue;

public Producer(BlockingQueue<String> q) {
queue = q;

public void run() {

while (true) {

try {

queue.put("Java Magazine");

catch (InterruptedException e) {

e.printStackTrace();

Listagem 8. Producer/Consumer utilizando BlockingQueue

package br.com.jm.concurrent.bq;

import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable {

private final BlockingQueue<String> queue;

public Consumer(BlockingQueue<String> q) {

queue = q;

public void run() {

while (true) {

try {

System.out.println(queue.take());

catch (InterruptedException e) {

e.printStackTrace();
}

Esse exemplo tem praticamente o mesmo nível de complexidade que o das Listagens 1 a 4 , mas o
código em si é muito mais simples. Na classe principal ProducerConsumer temos uma
ArrayBlockingQueue com tamanho fixo que controla os números máximos e mínimos de elementos na fila
(assim como a fila da Listagem 1), temos ainda um Executor (CachedTheadPool) que lida com a
concorrência, utilizando um pool de threads que podem ser reutilizadas, maximizando assim a
performance, pois alocar recursos para uma thread não é um processo simples. A fila comporta no
máximo 10 elementos e o produtor só pode inserir dados se existir espaço livre (assim como o primeiro
exemplo), a própria BlockingQueue controla esses limites. O consumidor utilizando o método take() não
corre riscos de exceções por falta de objetos na fila, pois esse método espera até que exista um elemento
na fila para retornar.

Todo o processo ocorre sem a utilização de qualquer primitiva de concorrência, muito utilizadas em todas
as classes das Listagens 1 a 4, deixando claro como a nova API pode facilitar o nosso trabalho como
desenvolvedores.

FIFO - Define a ordem de entrada e saída dos elementos da fila, nesse caso o primeiro a entrar é o
primeiro a sair (first in, first out).

O framework Executor e Thread Pools

As implementações de thread pools disponíveis na API são bem flexíveis e existe um framework para
controlar a execução de tasks que implementam a interface Runnable (ou uma nova variante, Callable).
No framework Executor existe a interface Executor, que é bem simples. O contrato dessa interface define
um método para executar implementações de Runnable. Em qual thread a task será executada não faz
parte do contrato, dependerá da implementação de Executor sendo utilizada, ou seja, a forma de
execução é desacoplada da forma de chamada. Como a interface Executor se preocupa com o modo da
chamada, fica muito mais fácil ajustar os parâmetros de execução (alterando valores, como: tamanho do
pool, prioridade, timeouts, etc.) com pequenas alterações no código.

Dica: Thread pool é uma boa solução para os problemas de alocação e liberação de recursos enfrentados
em aplicações multi-thread. Como nos thread pools as threads são reutilizadas, o custo de alocação de
cada thread é dividido entre as tarefas que ela executará. Como a thread já está criada a aplicação pode
responder uma requisição (ou seja, iniciar uma tarefa) imediatamente.

Existem várias implementações de Executors, cada uma com sua estratégia de execução. Elas são
criadas por factories disponíveis em métodos estáticos da classe Executors, os principais são:

• Executors.newCachedThreadPool(): Um pool sem limite de tamanho, mas que reutiliza threads


quando disponíveis. Se não existem threads disponíveis, cria uma nova e adiciona ao pool. Se
uma thread não for utilizada durante um minuto, será finalizada e removida do pool;
• Executors.newFixedThreadPoll(int n): Um pool que reutiliza um certo grupo de threads com
tamanho definido. Se o numero de threads ultrapassar o número definido, as threads esperam
até um espaço ser liberado;
• Executors.newSingleThreadExecutor(): Um pool que utiliza uma única thread para executar as
tarefas. Dessa forma, as tarefas são executadas de forma seqüencial. A thread de eventos do
Swing, por exemplo, seria um SingleTheadExecutor.

A estratégia de execução define em qual thread uma task será executada. Define o modo de execução,
ou seja, como os recursos serão utilizados (ex.: memória) e como se comportar em casos de sobrecarga.

Dica: Uma pergunta muito comum é: Qual tamanho deve ter o Thread pool?

A resposta depende, principalmente, do hardware e o tipo de tarefa (task) que será executada. Existe
uma lei que pode ajudar bastante: A lei de Amdahl. Para aplicá-la, precisa-se do número de
processadores (N) existentes na máquina que executará a tarefa.
Precisa-se ainda de aproximações dos tempos médio de processamento (TP) e de espera (TE) para a
tarefa.O TP é o tempo gasto para a conclusão da tarefa e o TE é o tempo gasto pela thread esperando
para iniciar a execução. Assim, o tamanho do Thread pool deve ser algo em torno de:

N * (1 + TE / TP)

A interface Future

Essa interface pode representar uma task que completou a execução, que está em execução, ou que
ainda não começou. Pode-se ainda cancelar uma task que ainda não terminou, descobrir quando a task
terminou ou foi cancelada e receber ou esperar pelos valores retornados.

Uma implementação dessa interface é a FutureTask, que encapsula Runnable ou Callable obedecendo aos
contratos das interfaces Future e Runnable, assim, pode ser executada facilmente por um Executor.
Alguns métodos do Executor (como ExecutorService.submit()) retornam Future além de executar a task.

Para recuperar os resultados de uma task, usamos Future.get(), que lança uma ExecutionException caso
a execução da task tenha provocado uma exceção; caso a task ainda não tenha terminado o método
ficará esperando o retorno do resultado, se a execução já acabou retorna o valor imediatamente.

As diferenças entre as interfaces Runnable e Callable são: Na interface Callable o método T call(), além
de devolver um resultado do tipo definido T, lança exceção. O método void run() da interface Runnable
não retorna valores e não lança exceção.

A interface CompletionService

Permite que o processamento dos resultados seja desacoplado da execução da task. Sua interface define
o método submit() para indicar as tasks que serão executadas e define o método take() para recuperar
as tasks terminadas. Os dois métodos retornam uma implementação de Future, o que facilita o processo
de recuperação de resultados. Usando a interface CompletionService a aplicação pode ser estruturada
facilmente com o pattern Produtor/Consumidor (utilizado como exemplo nas Listagens 1 a 4 e 6 a 8),
onde o produtor cria e submete as tasks e o consumidor recupera os resultados para processá-los.

Uma implementação dessa interface é a ExecutorCompletionService, que usa um Executor para processar
as tasks. No exemplo das Listagens 9 a 11 apresenta-se a implementação de um produtor/consumidor
utilizando essa interface.

Esse exemplo usa muitos recursos da nova API para resolver praticamente o mesmo problema que os
outros exemplos, mas nele as interfaces ExecutorCompletionService, Future e Callable deixam bem claro
como o código pode ser bem simples e executar tarefas complexas, como submeter tarefas e recuperar
os valores ao final da execução das mesmas. Para esse exemplo foi escolhido o Executor
FixedThreadPool, com tamanho máximo de 10 threads. O produtor submete uma tarefa para a
ExecutorCompletionService chamando o método addTask(), enquanto o consumidor espera para obter o
resultado com o método getResult(). O getResult() retorna uma instância da interface Future, onde o
método get() retorna o resultado imediatamente, se ele existir, ou espera até o final da execução tarefa
para retornar.

Listagem 9. Producer/Consumer utilizando CompletionService

package br.com.jm.concurrent.cs;

public class ProducerConsumer {

private static AtomicInteger count = new AtomicInteger(0);

public static void main(String args[]) throws InterruptedException,

ExecutionException

CompletionService completionService = new ExecutorCompletionService(

Executors.newFixedThreadPool(10));
Producer producer = new Producer(completionService);

Consumer consumer = new Consumer(completionService);

while (true) {

producer.addTask("Java Magazine [" + safeIncrement() + "]");

System.out.println(consumer.getResult().get());

private static int safeIncrement() {

if (count.equals(Integer.MAX_VALUE)) {

return 0;

else {

return count.addAndGet(1);

Listagem 10. Producer/Consumer utilizando CompletionService

package br.com.jm.concurrent.cs;

public class Producer {

private CompletionService<String> completionService;

public Producer(CompletionService<String> completionService) {

this.completionService = completionService;

public void addTask(final String value) {

completionService.submit(new Callable<String>() {

public String call() throws Exception {

return value;

});
}

Listagem 11. Producer/Consumer utilizando CompletionService

package br.com.jm.concurrent.cs;

public class Consumer {

private CompletionService<String> completionService;

public Consumer(CompletionService<String> completionService) {

this.completionService = completionService;

public Future<String> getResult() throws InterruptedException {

return completionService.take();

Conclusões

Pode-se perceber que desenvolver com a nova API é muito mais simples, apesar de exigir ainda
conhecimentos sólidos sobre programação concorrente e a necessidade de códigos específicos que
indiquem para a JVM como lidar com essas situações. A mesma segurança que existe em um código não
concorrente em Java (ou seja, que a memória será alocada e desalocada de modo correto e eficiente, as
melhorias que o JIT Compiler proporciona, entre outros) é o que a java.util.concurrent proporciona para
os códigos concorrentes. Claro que seria melhor não existir nenhuma preocupação em deixar explícitos os
códigos para o controle da sincronização e concorrência e deixar para a JVM resolver, assim como ela
resolve os problemas de alocação de memória, mas não é tão simples (como alocação de memória),
controlar a sincronização e concorrência de um processo. Esse é um dos motivos para o assunto ser um
dos mais discutidos hoje em dia. Com essa API podemos substituir a maior parte das primitivas de
concorrência por classes de alto nível, bem testadas e de bom desempenho.

Livros

Concurrent Programming in Java, Doug Lea, Addison-Wesley, 1999


Livro do professor Doug Lea, que escreveu a API que serviu de base para a java.util.concurrent.

Java Concurrency In Practice, Brian Goetz, Addison-Wesley, 2006


Livro do engenheiro da Sun Brian Goetz, muito conhecido e comentado.

Links

http://www.jcp.org/jsr/detail/166.jsp
JSR 166: Concurrency Utilities

http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html
Tutorial da Sun sobre concorrência em Java

http://gee.cs.oswego.edu/dl/concurrency-interest/
Site do Doug Lea sobre concorrência

http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=practice:
Coluna do Brian Goetz no developerWorks

Saiba mais

www.devmedia.com.br/articles/viewcomp.asp?comp=10017
Vídeo - Novidades do NetBeans 6.5 - Parte 1

www.devmedia.com.br/articles/viewcomp.asp?comp=8471
Java Magazine 53 - Perspectivas para um Mundo Paralelo

www.devmedia.com.br/articles/viewcomp.asp?comp=9420
Java Magazine 57 - Programando com Pools

www.devmedia.com.br/articles/viewcomp.asp?comp=10197
Java Magazine 59 - Quick Update: A Crise dos Threads

Você também pode gostar