Sumário a v a J y s a E
Easy Java, Core
Olá, eu sou o DevMan! Desta página em diante, eu estarei lhe ajudando a compreender com ainda mais facilidade o conteúdo desta edição. Será um prazer contar com sua companhia! Confira abaixo o que teremos nesta revista:
08 – Um pouco de Groovy
Uma linguagem de scripts simples e 100% compatível com Java [ Marcelo Castel lani ]
16 – JBoss Application Server 5 Core, Web
As principais novidades do JBoss AS 5 [ Bruno Rossetto Machado e João Paulo Viragine ]
28 – Spring Security Web, Tutorial
a v a J
Segurança simples, poderosa e portável - à moda do SpringFramework [ Michel Zanini ]
40– JavaFX Script Core, Web, Mobile
Uma referência didática da linguagem JavaFX Script, uma das principais peças da plataforma JavaFX [ Osvaldo Pinali Doederlein ]
52 – Novos tempos: javax.time Vanguarda, Core
Mobile
s a c i t á r P s a o B
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 ]
60 – Java ME Platform SDK 3
As novidades do novo SDK para Java ME, que substitui o antigo Wireless Toolkit [ Ernandes Mourão Júnior ]
68 – Estratégias de Integração de Aplicações Java EE Boas Práticas
Melhores práticas de interoperabilidade Java para o desenvolvimento de aplicações robustas [ Marco Auréli o de Souza Mendes ]
Você percebeu os ícones ao lado de cada matéria? Eles indicam o que você vai encontrar no artigo – dessa forma, você também pode ter uma idéia geral do que vai encontrar nesta edição como um todo! Os editores trabalham sempre no sentido de fechar a revista seguindo esta definição, para oferecer a você o melhor conteúdo didático! Confira abaixo a lista com a definição dos tipos de artigo encontrados nesta edição:
[Easy Java] Artigos introdutórios sobre a linguagem Java, APIs
[Tutorial] Artigo no estilo tutorial passo-a-passo.
[Boas práticas] Um dos objetivos da revista é levar para o lei-
ou ferramentas.
[Mobile] Artigo sobre as plataformas Java ME, Android, JavaCard,
tor não somente as melhores técnicas, mas também as melhores práticas de desenvolvimento, orientadas ao aumento da qualidade e produtividade.
[Core] Técnicas Server-side, ferramentas, IDEs, e outros assuntos que fogem às demais categorias.
[Web] Artigos sobre ou que envolvam técnicas de desenvolvimento para WEB.
ou outras móveis/embutidas/wireless.
[Vanguarda] Artigos sobre tecnologias de ponta, que ainda não fazem parte do dia-a-dia do desenvolv desenvolvedor edor mas que prometem ser a próxima febre.
Carta ao Leitor
Edição Editores Osvaldo Doederlein (
[email protected] ) Eduardo Spínola (
[email protected])
Arte Diagramação Romulo Araujo Capa Antonio Xavier
Produção Gerência de Marketing Kaline Dolabella Coordenação Geral DaniellaCosta (
[email protected])
Distribuição Fernando Chinaglia Distribuidora S.A. Rua Teodoro da Silva, 907, Grajaú - RJ CEP 20563-900, (21) 3879-7766 - (21) 2577-6362
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
[email protected] – 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. Reprints Editoriais – Se foi publicado na Java Magazine um artigo que possa alavancar as suas vendas, multiplique essa oportunidade! Solicite a reimpressão da matéria junto com a capa da edição em que saiu, e distribua esse reprint personalizado entre seus clientes. Encarte de CDs – Faça como nossos maiores anunciantes. Encarte um CD com uma amostra de seus produtos na Java Magazine e atinja um público segmentado e formador de opinião. 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. Fale com o Editor!
É muito importante para a equipe saber o que você está achando da revista: que tipo de artigo você gostaria de ler,que artigo você mais gostou e qual artigo você menos gostou. Fique a vontade para entrar em contato com os editores e dar a sua sugestão! Se você estiver interessado em publicar um
artigo na revista ou no site Java Magazine, entre em contato com os editores, informando o título e mini-resumo do tema que você gostaria de publicar: Eduardo Spínola - Editor da Revista
[email protected]
A
capa desta edição é dedicada ao ser vidor de aplicações JBoss, o que aconteceu pela últ ima vez há dois anos (Edição 46). Nesse ínteri m cobrimos vários softwares da “família JBoss” (Hibernate, Seam, ESB...), mas o JBoss AS andou longe dos holofotes, que em geral destacam novidades. Como o novo JBoss AS 5 , fina lmente liberado. Esta nova versão tem grande importânc ia devido ao suporte certificado à plataform a Java EE 5 e também à renovação da arquitetura interna do servidor – como explica nosso artigo, numa cobertu ra aprofundada que faz jus ao release. Continuamos também acompanhando o lado Spring da força, dessa vez com foco no Spring Security , que oferec e facil idades de controle de acesso e autenticação poderosas e simples de integrar à sua aplicação. A palavra “integração”, por sinal, provavelmente pode ser encontrada em qualquer edição da Java Magazine. O site eaipatterns.com e o livro que o acompanha são a mais importante linguagem de design patterns disponível para o desenvolvedor da geração SOA. O artigo sobre Integração de Aplicações Java EE apresenta esta linguagem, de forma orientada à plataforma Java. Dois artigos tratam de linguagens de programação alternativas para a JVM. No pri meir o, term ina mos a cober tu ra i nic ial da pl ata form a Java FX com um texto dedicado à linguagem JavaFX Script. No segundo, voltamos à linguagem Groovy – já apresentada anteriormente, mas cujo i nteresse vem sido renovado recentemente em razão de grande progres so na maturidade da sua implementação e de seus frameworks. A plataforma Java ME é representada pelo artigo sobre o novo Java ME SDK 3 , que nesses tempos de competição aci rrada no espaço mobile, é outra renovação crítica do arsenal do Java, aposentando o velho e enferrujado Wireless Toolkit. E na mesma linha de “novos tempos”, cobrimos a nova API javafx.time , que promete corrig ir de uma vez por to das u ma á rea de funcionalidade da Java SE que vem sendo criticada desde o JDK 1.0.
Boa leitura, Osvaldo Doederlein
[email protected]
Conheça nosso novo web site.
Conheça nossos frameworks open-source
genesis O framework genesis garante uma maior produtividade no desenvolvimento de aplicações desktop, permitindo a utilização de Swing, SWT e Thinlet de forma declarativa e com suporte a anotações como forma de evitar as complexidades e particularidades de cada API gráfica. Seu modelo de integração entre interface e camada de negócios possibilita que aplicações locais, implementadas de forma simples e facilmente testável, possam ser posteriormente disponibilizadas em arquitetura rich-client distribuída, simplesmente reconfigurando-se sua lógica de negócios para execução remota em servidores Java EE. Além de open-source e gratuito, o genesis possui extensa documentação (em inglês e português) e uma ativa comunidade de desenvolvedores.
DevWare O DevWare é um ambiente virtual completo de desenvolvimento já instalado e configurado, composto exclusivamente por produtos open source. Inclui controle de issues e de versão, automação de buid, fórum, wiki corporativa, servidores web, email e ftp, repositório maven2, servidor ldap para controle de usuários, console administrativo, entre outros. Com baixo consumo de recursos e baseado em tecnologia de virtualização, o DevWare foi projetado especialmente para facilitar tarefas administrativas como backup e migração entre máquinas. Consulte-nos para saber como o DevWare, combinado com boas práticas de desenvolvimento, pode aumentar a qualidade e produtividade de seus projetos Java.
Os frameworks genesis e DevWare foram criados pela Summa Technologies, disponibilizados de forma livre e gratuita (licença open source LGPL).
Uma empresa premiada Entre os projetos que já contaram com a consul-
Para maiores informações, downloads e documentações consulte os sites: https://genesis.dev.java.net/ https://devware.dev.java.net/
toria da Summa Technologies, incluem-se os três únicos sistemas brasileiros premiados com o Duke Award, conferido anualmente durante o JavaOne Conference: Cartão Nacional de Saúde do Datasus (2003); IRPF Multiplataforma da Receita Federal e Serpro (2004); e Sistema Integrado de Agendamento de Pacientes da Secretaria de Saúde da Prefeitura de São Paulo (2005).
Summa Technologies do Brasil . +55.11.3055.2060 . www.summa.com.br
Consultoria . Arquitetura . Mentoring . Treinamento . Framework . Componentes
Em busca de novos desafios em sua carreira? Envie seu currículo para:
[email protected]
Informativo Java Magazine Portal Java Magazine
www.devmedia.com.br/java
Brinde na web desta edição
5
Spring, Hibernate e JSF na prática. Para visualizar acesse o link:
Vídeos
http://www.devmedia.com.br/articles/listcomp.asp?keyword=jm69&codigobanca= jb os sa s5 O portal www.devmedia.com.br possui mais de e sobre desenvolvimento de software! Agora você pode comprar as vídeo aulas que preferir e fazer sua própria combinação de vídeos! Saiba mais em www.devmedia.com.br/creditos
Edições Anteriores da Java Magazine Você pode comprar todas as edições anteriores da Java Magazine através do site DevMedia! Para isso basta acessar https://seguro.devmedia.com.br/edicoes_anteriores.asp
u
e s ê
F eedb ac k
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você,
D
s
leitor, acha da revista!
r
Dê seu voto sobre esta edição, artigo por artigo, através do link:
o b
e e s t a
i d ç o ã
e
www.devmedia.com.br/javamagazine/feedback
Para votar, você vai precisar do código de banca desta edição, que é:
SEÇÃO EASY JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS PARA INICIANTES EM JAVA
L
inguagens 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 li nguagem preferida. Foi mais ou menos nesse meio que surgiu o embrião do que viria a ser o Groovy.
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 possibi lita, através de mecanismos como closures, uma sintaxe mais limpa e produtividade em alto nível.
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 t udo 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 uniuse a Bob McWhirter e juntos fundaram o projeto Groovy em 2003. Logo, com um grupo de pessoas que compartilhavam da
8 Java Magazine
Edição 69
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, possi bilita gerar bytecodes Java e possibilita o uso de toda a infra-estrutura já desenvolvida para suas aplicações.
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, de produtividade, como veremos neste artigo.
O que é e pra que serve o Groovy? O web site oficial da li nguagem possui uma das melhores definições sobre a linguagem, a qual reproduzo a seguir: “Groovy é uma linguagem ágil e dinâmica para a Plat afo rm a Java com rec ursos 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.
MARCELO CASTELLANI 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 (nova-
Figura 1. Criação de um novo projeto Java.
mente 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 am biente. 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 s erá
Edição 69 Java Magazine
9
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 Gro ovy , como na Figura 2 . Agora nosso projeto já possui Groovy em sua estrutura. Perceba, na Figura 3 , que, no grupo Categorias , no navegador do projeto, foi adicionado o groov y-all.jar , que é quem faz a “mágica” para nós. 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 . 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 clas -
10 Java Magazine
Edição 69
Figura 2. Adicionando Groovy ao projeto.
Figura 3.
se Groovy. Para isso, digite o código da Listagem 1. 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. 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 tama nho. Veja na Listagem 3.
Figura 4. Um JFrame com uma caixa de texto dentro.
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. 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 . Para corrigir, modifique o código Groovy para ficar como o da 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.
GDK Assim como o Java possui a JDK, o Groovy possui a GDK. É fácil confundir-
Listagem 1. package org.groovy.maonamassa class AloMundoGroovy { def saudacao = “Alô mundo, em Groovy !!!” }
Listagem 2. O código de nosso JFrame package org.groovy.maonamassa;
Figura 5.
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 Collection s e Map s). Temos até um getLength() (para jav a.lang.ref lect .Arr ay) e o método groupCount() , para in stancias 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 ta nto um ArrayList quanto uma String usa ndo o mesmo método, o size(). Outra vantagem do GDK sobre a bi blioteca 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.I nteger , 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).
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 }
Listagem 3. package org.groovy.maonamassa; public class AloMundoJava { String saudacao = new String(); public String getSaudacao() { return saudacao; }
}
public void setSaudacao(String saudacao) { this.saudacao = saudacao; }
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 Edição 69 Java Magazine
11
Listagem 4. package org.groovy.maonamassa class AloMundoGroovy { // aqui você pode omitir o def def String saudacao = “Alô mundo, em Groovy !!!!” }
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()
Closures
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
}
}
somadas, realiza a adição e depois a multiplicação para retornar o resultado. Veja o código. É importante ressaltar que sim, é possível, em Groovy, ter mais de uma class e 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 um a classe pública por arquivo. Veja nas Listagens 7 e 8 um exemplo simples. 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:
println valor01 + valor02
new File(“arquivo.txt”).eachLine { println it }
Groovy basta defini r 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:
12 Java Magazine
Edição 69
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
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 “ Mag ic var iable ”, 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.
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();
}
Listagem 9. A classe Aluno.groovy
O poder dos closures
package groovy2
Vamos criar um exemplo pra demonstrar um pouco do poder e da elegância do uso de closures. Crie um novo pro jeto Java e adicione Groov y a ele como visto anteriormente. Adicione uma nova classe Groovy chamada Aluno e então insira o código da Listagem 9. 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 constr utor 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(). O resultado será algo como:
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() }
Listagem 10. A classe Main.java, instanciando três objetos aluno package groovy2; public class Main { public static void Aluno aluno01 = Aluno aluno02 = Aluno aluno03 =
}
main(String[] args) { new Aluno(“Marcelo”); new Aluno(“Giuliana”); new Aluno(“Ana Beatriz”);
System.out.println(aluno01.toString()); System.out.println(aluno02.toString()); System.out.println(aluno03.toString());
}
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. Edição 69 Java Magazine
13
Listagem 11. A classe Aula.groovy
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.
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() } }
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);
}
}
É diretor de novas tecnologias na mindaslab.com. Desenvolvedor experiente e fã incondicional de linguagens dinâmicas, atua há mais de quatorze anos em projetos de software, seja sistemas embarcados, web ou desktop. É um dos fundadores do grupo de usuários de Ruby do estado de São Paulo e ministra periodicamente treinamentos sobre Groovy, Ruby e Rails pela Tempo Real Eventos.
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
historia.imprimeListaAlunos();
Página da SpringSource comentando a compra da G2One
Modifique o arquivo Main.java para que fique como na Listagem 12 . 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. Iss o 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
14 Java Magazine
Marcelo Castellani
[email protected]
Edição 69
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 possibil ita 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
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
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
u
e
ee
a c
o b e
ã i o
Dê seu voto sobre este artigo, através do link:
www.devmedia.com.br/javamagazine/feedback
t a
Edição 69 Java Magazine
15
SEÇÃO JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS INTERMEDIÁRIOS E AVANÇADOS SOBRE JAVA
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.
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 ar tigo 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 disponibiliz ar 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.
Sobre a JBoss JBoss , apesar de ser sinônimo de servi-
dor 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 JSR’s, seja com a implementação de referência de JSR’s, 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,
16 Java Magazine
Edição 69
JBoss Application Server5: 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 integ rar 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.
BRUNO ROSSETTO MACHADO E JOÃO PAULO VIRAGINE 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 Profile r (ferramenta de profiling baseada em log), JBoss Messag ing (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.
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 servidor de aplicação JBoss 3/4 era basicamente uma série de MBeans agrupados, controlados e disponibilizados pelo Microkernel, o qual já possibilitava grande flexibilidade para retirar e adicionar novos MBeans. Já com o JBoss Microcontainer é possível desenvolver serviços do servidor de aplicações baseados em POJOs. Não é mais necessário desenvolver MBeans.
Veja os links de referência 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 Edição 69 Java Magazine
17
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
18 Java Magazine
Edição 69
Figura 1. Uma visão geral da arquitetura de serviços que são disponibilizados pelo Microcontainer
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 serv idor 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 especif icaçã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 Porta ble 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. 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
JBoss AS 4.2.3.GA
JBoss AS 5.0.1.GA
1.5.6.GA
2.0.1.GA
4.2.3.SP7
4.4.0.GA
2.0.1.GA
v2.1.2.GA
3.0.1-native-2.0.4.GA
3.0.5.GA
JBoss MQ com suporte a JMS 1.1
JBoss Messaging 1.4.1.GA
2.4.1.SP4
2.6.7.GA
1.4.1.SP7
3.0.2.GA
Tabela 1. Matriz de comparação de versão entre o JBossAS 4.2.3 e o JBossAS 5
Figura 2. Arquitetura com buddy replication
Figura 3. Arquitetura com buddy replication e queda do nó 1
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.
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_
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
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. Edição 69 Java Magazine
19
Listagem 1. $JBOSS_HOME/server/all/deploy/cluster/jboss-cache-manager.sar
jboss. messaging:service=ServerPeer jboss.messaging:service=PostOffice true
Listagem 2. $JBOSS_HOME/server/$PROFILE/conf/bootstrap.xml
bootstrap/vfs.xml bootstrap/classloader.xml bootstrap/aop.xml bootstrap/jmx.xml bootstrap/deployers.xml bootstrap/bindings.xml bootstrap/profile-repository.xml
Listagem 3. Trecho do arquivo bindings.xml – definição de alias
Listagem 4. Trecho do arquivo bindings.xml – definição de PortsDefaultBindings
ports-default ${jboss.bind.address} 0
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
20 Java Magazine
Edição 69
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. 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. A definição das configurações PortsDefaultBindings e
Ports01Bindings é realizada com a injeção de beans do tipo ServiceBindingSet (Listagens 4 e 5). 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. $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 jbossbeans.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 algu ns 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 5. Trecho do arquivo bindings.xml – definição de Ports01Bindings
(Listagem 7). É uma classe muito simples, com apenas um método construtor que imprime uma mensagem na tela. A Listagem 8 mostra o arquivo jbossbeans.xml , o qual possui a declaração do POJO com o nome PojoServiceExample. Ao realizar o deploy, deverá ser exi bida a mensagem conforme mostra a
ports-01 ${jboss.bind.address} 100
Listagem 9.
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.
Listagem 6. Trecho do arquivo bindings.xml – definição de StandardBindings – portas padrão
jboss:service=Naming Port 1099
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 bibliote ca s devem ser copiadas do diretório /client para o diretório /l ib/ endorsed . São elas:
jboss:service=Naming RmiPort 1098 . . .
Listagem 7. Classe PojoServiceExample package jm.exemplos.pojo; public class PojoServiceExample {
}
public PojoServiceExample() { System.out.println(“Construtor de PojoServiceExample()”); }
Listagem 8. Arquivo jboss-beans.xml
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/jboss5servico-exemplo. beans 2009-03-04 17:10:16,063 INFO [STDOUT] (main) Construtor de PojoServiceExample()
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: Edição 69 Java Magazine
21
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. 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:
Figura 4. Prompt de inicialização do AS5
# ./run -c all
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.
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
22 Java Magazine
Edição 69
Figura 5.
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 5). O Web Console também foi modificado, incorporando o design do JMX Console (Figura 6).
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 7). O nome Jopr (pronuncia-se jopper) foi escolhido baseado no filme Jogos de Guerra (WarGames), em que havia um super computador chamado
Figura 6.
WOPR (Whopper) – War Operation Plan Response – o qual era responsável por responder a todo e qualquer ataque nuclear inimigo. O propósito desse projeto é disponi bilizar 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 serv idor 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 Em bedded Jopr, o qual é disponibilizado no próprio serv idor. Com o Embedded Jopr (Figura 8), é possível reali zar deploys de EARs e WARs, criar novos Datasources,
Figura 7. Jopr
Figura 8. Embedded Jopr
criar novas filas JMS, monitorar o pool de conexões, executar scripts, etc. Tudo isso utilizando uma interface Web intuitiva.
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. Edição 69 Java Magazine
23
A estrutura de diretórios do AS5 é muito semelhante à dos seus antecessores (). 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; – 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; – 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; – possui as bibliotecas necessárias para executar o “core” do AS; – é 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 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 mostra as principais diferenças entre os cinco profiles. Os profiles mais utilizados nas versões anteriores eram o default e o all. No AS5, além destes, o pro-
file 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 ser vidor 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 (). 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 ser viç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 clusterservice.xml não existe mais, e seu equivalente é o $PROFILE/deploy/cluster/jgroups-channelfactory. sar/META-INF/jgroups-channelfactory-stacks.xml.
minimal
web
standard
default
all
x
x
x
ejb2
x
x
x
ejb3
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
Tabela Q1. Serviços disponíveis em cada profile.
24 Java Magazine
Edição 69
x
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. Bruno Rossetto Machado [email protected]
Figura Q1. Estrutura de diretórios da raiz do AS5
É bacharel em Sistemas de Informação pela Universidade Presbiteriana Mackenzie e atua com desenvolvimento de software com a plataforma Java desde 2003. Possui experiência em projetos Java EE nas áreas automotivas, e-commerce, financeira, telecom, e outros projetos de missão crítica. Participou do projeto ganhador do Duke Choice’s Awards de 2005 - SIGA Saúde. Atualmente é Consultor da JBoss, a division of Red Hat no Brasil com foco em customização, performance e tuning do JBoss Application Server e desenvolvimento de portais com JBoss Portal.
João Paulo Viragine [email protected]
É 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.
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
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
ee e s
a c
o e
t a e o ã i d
Dê seu voto sobre este artigo, através do link:
www.devmedia.com.br/javamagazine/feedback
Nova versão do livro JBoss in Action a qual
Figura Q2.
foca nas principais configurações do JBoss Applic ation S erver 5
Edição 69 Java Magazine
25
SEÇÃO JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS INTERMEDIÁRIOS E AVANÇADOS SOBRE JAVA
S
eguranç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 modif icaçõ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).
28 Java Magazine
Edição 69
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 re cursos 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 supor te à 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.
MICHEL ZANINI 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 exem plos 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. O código fonte dos três JSPs são HTML simples e portanto não serão exibidos nas listagens (estão dis poníveis para download no site da Java Mag azine).
Figura 1. Página inicial da aplicação. Listagem 1. web.xml – Configuração do Spring Security no web.xml springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:spring-security-config.xml
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. Edição 69 Java Magazine
29
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. 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 conf erir.
O controle de acesso é definido pela tag interna à tag . No atri buto 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 /usuar ios 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 pri-
30 Java Magazine
Edição 69
Listagem 2. spring-security-config.xml – Arquivo de configuração do Spring Security
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:
Isso configura o framework para utilizar autenticação por formulário e http-basic, bem como
meiro. 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 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 veja o quadro “Entendendo o auto-config”. Desta forma, a última coisa que nos resta é definir os usuários possíveis e se us papéis. No primeiro exemplo, para facilitar,
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.
Figura 2. Login gerado automaticamente pelo Spring Security.
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. pro per ti es 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 springsecurity-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 /u su ar io s/in dex.js p , o fi ltro do Spr ing 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 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 (...) (...)
Listagem 4. login.jsp – Página de login Spring Security Spring Security
<% if (request.getParameter(“login_error”) != null) { %> Não foi possível se autenticar.
Motivo: ${SPRING_SECURITY_LAST_EXCEPTION.message}. <% } %>
Voltar...
Para gerar rapidamente um hash md5 para utilizar nos exemplos o leitor pode implementar uma classe Java simples de utilidade ou acessar um gerado r 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 ( /l ogi n.jsp) com um formulário para o nosso exemplo. Para isso acrescentamos este trecho dentro da tag no springsecurity-config.xml :
O atributo authentication-failure-url configura o JSP que será apresentado caso o login falhe. Neste caso configuramos o
Figura 3. Arquivos do primeiro exemplo. Edição 69 Java Magazine
31
Listagem 5. accessDenied.jsp – Página de erro
Figura 4. Página de login customizada.
Spring Security
Acesso negado. O usuário não tem permissão para acessar essa página.
Remote user....: <%= request.getRemoteUser() %>
User principal....: <%= request.getUserPrincipal() %>
Voltar...
Logout
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. 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_ secur ity_ 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 . Logout O atributo auto-config=true da tag 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 dentro da tag :
Página de erro Quando um usuário normal acessa a página protegida dos administradores
32 Java Magazine
Edição 69
Figura 5. Modelo de dados para armazenar os usuários, senhas e perfis.
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 :
E então, criamos o accessDenied.jsp conforme a Listagem 5. 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 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:
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 logarse, 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. 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á utili zado 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 hsqld b-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 (datasource). A Listagem 6 demonstra o novo spring-security-config.xml. 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-byusername-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
Listagem 6. spring-security-config.xml – Adicionando autenticação com banco de dados
Figura 6. Eventos de autenticação e autorização publicados pelo Spring Security.
Edição 69 Java Magazine
33
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”); } } }
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(). get Name()}); }
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. 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. Lembre-se de adicionar o novo bean no application context:
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; } }
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 infraestrutura 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.
34 Java Magazine
Edição 69
Essa funcionalidade permite implementar complexos casos de uso, com baixo acoplamento ent re 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:
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. 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. 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. 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 sem-
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 ---
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 Row Mapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException { usuario.setEmail(rs.getString(“email”)); usuario.setTentativasLogin(rs.getInt(“tentativas_login”)); return null; } }); }
pre 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 Edição 69 Java Magazine
35
Listagem 12. spring-security-config.xml – Versão final do XML de configuração refletindo o exemplo de bloqueio de login
Autenticação single sign on significa logarse 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
nos deixa uma extensão valiosa, o método createUserDetails(). Esse método é um gancho para retornar um UserDetails customizado. Veja a Listagem 11. O método createUserDetails() apenas copia propriedades que já estão disponíveis
36 Java Magazine
Edição 69
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 necessida de 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á suport am o padrão com o Sun, Go og le, IBM, Microsoft, etc. O Spring Security facilita o processo de habilitar sua aplicação para suportar logins provenientes de providers OpenID.
e chama o método carregarInformacoes Adicionais() 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
Nota do 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.
é 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 . 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(); 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 getUsuarioLog getUsuarioLogado(HttpS ado(HttpServletRequest ervletRequest request) { Authentication authentication = (Authentication) request. getUserPrincipal(); if (authentication == null) retur n null; return (Usuario) authentication.getPrincipal(); }
> Você é um administrador e também pode acessar esta página.
A tag possui três atributos exclusivos: ifAllGranted , ifAnyGranted e ifNotGranted . Todos os atributos suportam um ou mais ma is roles separados por vírgula. O atributo ifAllGranted informa que o usuário tem que possuir todos os roles declarados para retornar true. O atributo ifAnyGranted necessita de apenas um dos roles para retornar true e o atributo ifNotGranted será true apenas se o usuário não possuir nenhum dos roles. A tag 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 informações do usuário utilizando a tag :
Informações do usuário logado:
Login:
Senha:
E-mail:
Logout
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 juntament junta mentee com o war da ap aplicaç licação ão,, dispen dispen-sando 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
Outra forma de se obter o usuário logado, porém sem necessitar do Httpatravé ravé s da cl clas asse se SecuServletRequest , , é at rityContextHolder. 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() SecurityContextHolder .getContext().getAuthen .getAuthentication(); tication(); 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.springframework. uri=”http://www.springframework. org/security/tags” %>
A tag é utilizada para mostrar/ trar /esconder informações in formações de acordo com as permissões (roles) do usuário. Veja o código a seguir: Edição 69 Java Java Magazine
37
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 certif icados X509, LDAP LDAP,, etc. Para mais informações sobre single sign on consulte o quadro “Single sign on com CAS e OpenID”.
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
e
d
c
o
www.devmedia.com.br/javamagazine/feedback
www.springsource.org Portal do Spring Framework e sub-projetos
static.springframework.org/spring-security/ 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
[email protected] michelzanini.wordpress.com
Homepage do OpenID
www.myopenid.com Provider de logins OpenID
md5-hash-online.waraxe.us Gerador online de hash md5
e
t a e o ã i d
Dê seu voto sobre este artigo, através do link:
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.
u
e s ê
Edição 69 • Java Magazine
39
SEÇÃO JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS INTERMEDIÁRIOS E AVANÇADOS SOBRE JAVA
N
a 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 Ope-
40 Java Magazine
Edição 69
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.
radores 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. 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
OSVALDO PINALI DOEDERLEIN 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).
VALORES (value types) Comentários true ou false Inteiro, 8 bits sem sinal Inteiro, 16 bits com sinal Inteiro, 32 bits com sinal IEEE754, 32 bits IEEE754, 64 bits String Duração temporal
Java boolean byte short int float double (Em Java, é tipo de referência) N/A; costuma-se usar long
VOID Comentários Nenhum valor
JavaFX Void
Java void REFERÊNCIA
Comentários
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); , não O operador == equivale a equals() 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.
JavaFX Boolean Byte Short Integer Number, Float Double String Duration
Java class, interface
Função
JavaFX class function ( parâme tros) : retorno
Seqüência/Array
TipoElemento []
TipoElemento []
Classe
N/D
Tabela 1. Classificações de tipos da JavaFX, comparativamente com Java.
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.
Edição 69 Java Magazine
41
Funções JavaFX Script possui funções de primeira ordem. Exemplo:
SEQUENCES Expressão def dias : String[] = [ “Dom”, “Seg”, “Ter” ] def dias = [ “Dom”, “Seg”, “Ter” ]
var validador: function (o:Object): Boolean
sizeo f dias
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, com binando 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:
dias[1]
Stage { onClose: function() { FX.exit() } }
delete dias
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.
dias[1 .. 2] dias[1 .. <3] dias[d | d.startsWith(“T”)] dias[0] = “DOM” insert “Qua” into dias insert “Qua” before dias[3] insert “Qua” after dias[2] insert [“Qua”, “Qui”] into dias [ dias, [“Qua”, “Qui”] ]
42 Java Magazine
Edição 69
3 “Seg” [“Ter”, “Qua”] [“Ter”] [“DOM”, “Seg”, “Ter”] [“Dom”, “Seg”, “Ter”, “Qua”] [“Dom”, “Seg”, “Ter”, “Qua”, “Qui” ]
delete dias[1] delete “Dom” from dias
[“Seg”, “Ter”]
delete dias[1 .. 2]
[“Seg”] [] [ “D”, “S”, “T” ] (for generator , retorna uma sequence com os elementos
for (d in dias) d.charAt(0)
produzidos por cada iteração) for (d in dias where d.startsWith(“T”)) d.charAt(0)
[ “T” ]
[ 1 .. 100 ]
[ 1, 2, 3, 4, .., 98, 99, 100 ] (É na verdade uma instância de Range, não consome memória com a lista de elementos)
[ 1 .. 100 step 10] dias = “Sáb”
[ 1, 11, 21, 31, 41, 51, 61, 71, 81, 91 ] (Novamente Range) [ “Sáb” ] (Uma sequence de um único elemento pode ser Inicializada/atribuída sem os ‘ [] ’)
reverse dias
Sequences As sequences da JavaFX Script possuem sintaxe similar, mas signif icado 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]).
Resultado [ “Dom”, “Seg”, “Ter” ]
[ 1, null, 2 ]
[ “Ter”, “Seg”, “Dom” ] [ 1, 2 ] (Nulos não são suportados)
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). 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 estat icamente 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
Expressão var x
VARIÁVEIS E ATRIBUTOS Significado Ilegal
var x : Integer
Declara variável mutável, de tipo Integer, inicializada com o valor default para o seu tipo (no caso, 0)
var x = 5
Declara e inicializa variável mutável, de tipo Integer
var x : Integer = 5
Declara e inicializa variável mutável, de tipo Integer
def x = 5
Declara e inicializa constante, de tipo Integer
def x :
Declara e inicializa constante, de tipo Integer
def x
Ilegal, valor é exigido
def x : Integer
Ilegal, valor é exigido
for (d in dias) { d = “Domingo” }
Ilegal: é proibido modificar “variáveis induzidas”, como a variável de iteração de um for
Expressão (a : Integer) : Integer
{ return a * 2; } (a : Integer, b : Integer)
{ return a + b; } (nome : String)
{ println(“Alo, {nome}”); } (nome)
{ println(“Alo, {nome}”); } … alo(“Osvaldo”); (Double) : Boolean var f : function function
FUNÇÕES Significado Declara uma função com um parâmetro retorno também Integer
Integer e tipo de
Declara uma função com dois parâmetros retorno também Integer
Integer e tipo de
Declara uma função com um parâmetro retorno Void
String e tipo de
Declara uma função com um parâmetro retorno Void
String e tipo de
Declara uma variável cujo tipo é uma função com um parâmetro Double e tipo de retorno Boolean Declara uma variável cujo tipo é uma função com um parâmetro Double e tipo de retorno Boolean, e inicializa-a com uma função deste tipo
}
(a : Double) { return a >= 0; } function (a : Double) { a = 5 }
Declara uma variável cujo tipo é uma função com um parâmetro Double e tipo de retorno Boolean, e inicializa-a com uma função deste tipo Ilegal: é proibido modificar parâmetros
Tabela 3. Declarações de variáveis e funções.
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 static s. 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 Edição 69 Java Magazine
43
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.
VISIBILIDADE
Significado
Igual a Java, permite acesso a partir de qualquer código
Igual a Java, permite acesso para a mesma classe ou derivadas, e também outras classes do mesmo package Igual ao package-private do Java (exceto por exigir uma palavra-chave: não é a visibilidade default!), permite acesso para classes do mesmo (Somente para propriedades) Permite inicialização em cláusulas de construção do objeto; depois disso, comporta-se como script-private (Somente para propriedades) Permite inicialização e também leitura de qualquer código, mas não permite outras alterações exceto para código do mesmo script Sem palavra-chave, é a default: permite acesso total para código do mesmo script (seja qual for a classe, ou mesmo código “global” do script). Comparando com Java, é um nível intermediário entre e package- private
script-private
Tabela 4. PROPRIEDADES
Significado
nomeString
Assim como em Java, o modificador de visibilidade (caso exista) vem no início da declaração, antes do Trigger de alteração. Será invocado inclusive para a atribuição inicial à variável; se for necessário discriminar esta situação, basta usar
String
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.
44 Java Magazine
Edição 69
if (not println(“ Novo nome: {nome}”);
} var nome : String
nomeVelho {
Trigger de alteração, com cláusula de acesso ao valor anterior da propriedade
println(“Velho: {nomeVelho}, novo: {nome}”); }
Trigger de alteração, com cláusula de acesso ao valor anterior, para se pVelhos[i .. j] = pNovos { quence. Neste caso temos até quatro variáveis para controle da alteração: println(“Alterando: {pVelhos[i..j]} é a parte da sequence que foi modificada, contém os novos elementos que substituíram aquela parte ( para um ). para: {pNovos}”); var projetos : Pr ojeto[]
} var osvaldo = Pessoa {
nome: “Osvaldo”, cargos: [ Cargo { titulo: “Editor” }, Cargo { titulo: “Desenvolvedor” } ]
Instanciação e inicialização de objetos (ou árvores de objetos), através da enumeração de valores iniciais para suas propriedades públicas. É também a notação de objetos literais de JavaFX Script (não por acaso, idêntica à JSON de JavaScript tipado).
}
Tabela 5. Propriedades.
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. 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);
Se o leitor observar a 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 i lustrar 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:
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 en-
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 us ar 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:
capsular 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 visi bilidade 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
def sobrenome = if (nome == “Osvaldo”) “Doederlein” else if (nome == “Eduardo”) “Spínola” else “Silva”;
…o que é mais enxuto que, com um switch, ter que fazer uma atribuição separada para sobrenome em 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 s imilar a um switch, e gerar código idêntico (com bytecodes tableswitch ou lookupswi tch).
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íc ito 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 Edição 69 Java Magazine
45
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 , c possui apenas um construtor (a,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
46 Java Magazine
Edição 69
BINDING Declaração
Significado
def x = bind y
Sempre que o valor de y mudar, este mesmo valor será copiado para x (x é um alias para y)
def x = bind y + f(10)
Sempre que o valor de y mudar, a expressão y + f(10) é reavaliada e o novo resultado atribuído a x. Note que f(10) não será invocada novamente – o valor retornado anteriormente será reutilizado
def x = bind y + f(z, 10)
Dessa vez, como a invocação a f() tem um parâmetro alimentado por uma variável z; se esta variável mudar, f(z, 10) será reavaliada
bound function f ( x : Double) { Como f é declarada como uma bound function, o bind de z reexecutará return x * y f(a) sempre que qualquer variável utilizada por f() seja alterada. Isso inclui } tanto os parâmetros (como a) quanto outras variáveis externas (atributos def z = bind f(a) ou globais) como y def iniciaisDias = bind Sempre que ocorrer uma modificação na sequence dias, o for será reexefor (d in dias) d.charAt(0) cutado somente para os elementos modificados, resultando em alterações também incrementais na sequence iniciaisDias def posAtual = bind Point { Se uma das variáveis xAtual ou yAtual mudar, será construído um novo x: xAtual, y:yAtual objeto Point, que será atribuído a posAtual }
def posAtual = Point { bind x: xAtual, y:yAtual }
def x = bind y with inverse
Se a variável xAtual mudar, a propriedade posAtual.x será modificada (não será criado um novo objeto Point ). Se yAtual for alterada nada acontecerá, pois não tem bind Se y for alterada, seu valor é copiado para x. Mas se x sofrer uma atribuição, seu valor é copiado para y Nota: with inverse não suporta expressões mais complexas
Tabela 6. Binding. OPERADORES ARITMÉTICOS Operador
Significado
+ - * / ++ -+= -= *= /= mod
Igual a Java Igual a Java: operadores com atribuição combinada Resto de divisão, como o % do Java (não há equivalente a %=)
OPERADORES RELACIONAIS Operador
Significado
not or and == != <= >= < >
Negação, como o ! do Java Disjunção, como o || do Java Conjunção, como o && do Java Igual a Java
OUTROS OPERADORES Operador
Significado
instanceof obj as String sizeof e
Igual a Java Typecast: como (String)obj do Java Retorna o tamanho de uma expressã o. Para uma sequence, é o seu número de elementos. Para objetos de outros tipos (ex.: Integer), é 0 se o objeto for null, 1 se não-nulo.
Tabela 7. Operadores.
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 aofato 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 vetoriais1. 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 1 Isso obviamente não é devido somente à sintaxe de objetos literais, mas também ao poderoso framework de Scene Graph, que define entidades gráficas suficientemente ricas para ser equivalente ao metamodelo SVG.
DECISÃO Declaração
Significado
if (1 != 0) then println(“ok!”) else println(“pane na CPU!”) var msg = if (1 != 0) “ ok ” else “ pane ”
Igual a Java, exceto que a palavra-chave opcional)
Declaração
Significado
while (x++ < y) println(x) for (d in dias) println(d) var nums = for (n in [ 3, 2, 1]) n*2
Igual a Java. A expressão while tem tipo Void
for (n in nums where n >= 0)...
Filtra a iteração, ignorando elementos que não satisfazem ao . Em Java, exigiria um contendo um var retorna o número ordinal do elemento iterado (0, 1, 2, ...). Tecnicamente, é um operador de sequences, pois a iteração é sempre feita sobre sequences Como em Java, sendo que e têm tipo Não há "gotos disfarçados" label ou label
for (d in dias) println ( indexof d) for/while (..) { if (a) break else continue }
then é suportada(porém,
Igual ao operador ternário ‘ ?:’ do Java
msg = “ok” ITERAÇÃO / GENERATORS
Igual ao for estendido do Java 5, só muda ‘ :’ para ‘in’ Gera uma sequence com todos os valores “retornados”
nums = [ 6, 4, 2 ]
EXCEÇÕES Declaração
Significado
try, throw, catch, finally, throws
Como em Java. As expressões e têm tipo
BLOCOS Declaração
Significado
{ fazAlgo() } { fazAlgo(); return } { fazAlgo(); return valor } { fazAlgo(); valor }
Igual a Java: bloco Igual a Java: bloco, retornando valor. Note que o é opcional
INVOCAÇÃO Declaração
new Cliente(“Osvaldo”) m(a, b) obj.m(a, b) Tabela 8. Estruturas de controle.
Significado Igual a Java: instancia objeto e invoca construtor Igual a Java: invocação de método, sendo o receiver default
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 Edição 69 Java Magazine
47
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 exp licar os porquês – a “resposta longa”.
tipaçã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 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 proto -
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
48 Java Magazine
Edição 69
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 in clui carta
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
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 R IA. 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 tem po, 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.
como && , mas não engulo isso, lembrando que linguagens RIA competidoras, como JavaScript e ActionScript, utili zam 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 viceversa, 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 comobj.<>(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. 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
CONTROLE Declaração
Significado
Pessoa {
Declara uma classe
var nome : String; var sobrenome : String; function nomeCompleto () { “{nome}{sobrenome}” } } craque = Pessoa {
Instancia (aloca e inicializa) objeto de uma classe
nome: “Mané” sobrenome: “Garrincha” } Pessoa();
craque.nome = “Mané” craque.sobrenome = “Garrincha” list java.uti l.
ArrayLi st(100); IPessoa {
Instancia (aloca e inicializa) objeto de uma classe (Classes JavaFX não podem ter construtores, mas a sintaxe é suportada por questão de homogeneidade) Instancia (aloca e inicializa) objeto de uma classe Neste caso é uma classe Java, que tem construtores com parâmetros, que podemos invocar de forma normal Classe abstrata pura (substitui interfaces)
abstract function nomeCompleto (); } A extends B , C, D {...}
Herança
C
O bloco init é invocado após a alocação do objeto e a inicialização de todas as propriedades (inclusive de todas as classes-base). Os são invocados depois disso, e se for o caso, depois que todos os init da hierarquia Pode-se redefinir tanto funções quanto propriedades. No caso das propriedades, o serve apenas para modificar o valor default e/ou a cláusula
init { println(“init ok!”); }
{ println(“postinit ok!”); } }
var y = 0; function f () { y + y }
} }
Tabela 9. Classes.
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?”. 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 javaf xc 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 Edição 69 Java Magazine
49
Na minha mania de aprender novas linguagens mexendo em código dos outros, cheguei ao se guinte trecho de um demo (javafx.com/samples/ SmokeParticles/):
Esta nova versão faz uma substituição única da sequence antiga pela nova, a qual é criada da seguinte forma: primeiro temos um for que retorna todos os elementos da sequence anterior que continuam vivos (juntei a funcionalidade de isdead() 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. Regist rei 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)
do loop. Mas não é exatamente assim; o compilador só faz isso se necessário. Assim, no código
for não está sendo utilizado de qualquer
def x = [1, 2, 3] def y = for (n in x) n * 2
Classes
(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. 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
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 } ] }
…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
…o for só irá avaliar a expressão n * 2 para cada n in x , mas nenhuma sequence será criada, pois o “valor de retorno” do
50 Java Magazine
Edição 69
forma (como termo de outra expressão, lado-direito de atribuição, etc.). 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. 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
do bytecode / JVM, que suporta herança múltipla apenas para interfaces com métodos abstratos.
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. Osvaldo Pinali Doederlein [email protected]
É Mestre em Engenharia de Software Orientado a Objetos, membro individual do Java Community Process e trabalha na Visionnaire Virtus como arquiteto e desenvolvedor.
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
eedb e ê
e ç d o ã
s
Dê seu voto sobre este artigo, através do link:
www.devmedia.com.br/javamagazine/feedback
Edição 69 Java Magazine
51
SEÇÃO JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS INTERMEDIÁRIOS E AVANÇADOS SOBRE JAVA
N
este 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?
Para que serve:
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 class es que representem essas unidades do “mundo real”, é necessária a criação de soluções paliativas por conta e ri sco 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.
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.
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.
52 Java Magazine
Edição 69
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.
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.
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 man ipulaçã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 3101 irá resolver. Os já velhos conhecidos problemas devido às 1 A nova especificação para representação de datas e horas dentro da plataforma Java, denominada javax.time neste artigo, está sendo criada pela JSR 310. O link para acesso à JSR pode ser obtido na seção de Links.
DANIEL CICERO AMADEI E MICHAEL NASCIMENTO SANTOS 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 codif icação.
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.
Princípios de Design da JSR 310
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.
A JSR 310 tem sido elaborada com algu ns 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 threadsafe , 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 zone2) 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);
2 Utilizaremos os termos em inglês para a zona de fuso horário, time zone e fuso horário como offset, como veremos no decorrer de todo o artigo para que não precisemos usar os termos em português, muito mais incomuns até mesmo aqui no Brasil.
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);
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.DateAd justers , 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
Isso irá resultar na data 2008-03-02. Muito mais claro do que o método roll(intfield, 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 Edição 69 Java Magazine
53
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 Adjuste rs , outro exemplo muito interessante de sua aplica bilidade 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 3 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. 3 Até a finalização deste artigo, a classe InstantInterval ainda não estava finalizada e funcional na implementação de referência da javax.time e o código apresentado como exemplo ainda nãoestava funcionando corretamente.
54 Java Magazine
Edição 69
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ê, des envolvedor, 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-0207T11: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
Nota do 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.
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-0703:00” teria sido impressa no console, seguindo o padrão ISO-8601 para datas e horas com offset. OffsetTime 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(); OffsetTimehojeJapao = 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.Off setDateTime representa uma data e um horário com um Edição 69 Java Magazine
55
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 zoneinfo4 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 4 A base de dados denominada zoneinfo ou tz é um conjunto das zonas de fuso horário de todo o mundo. Essa base de dados também é chamada de Olson Database devido ao nome de seu criador,Arthur David Olson.
56 Java Magazine
Edição 69
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 quepermitem 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 Matc her 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). Essas i nterfaces estão disponíveis para você, desenvolvedor, implementar sua própria estratégia de consulta nas datas ou nos horários. Com isso, você garante que terá essa lógica implementada em um objeto coeso e reutili zável. A Listagem 1 apresenta um exemplo de implementação de DateMatcher para verificar se a data possui um dia ímpar ou não. A classe javax.time.calendar.DateMatcher s 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
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...
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:
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; } } }
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 static void main(String[] args) throws Exception { LocalDate data = DateResolvers.nextValid().resolveDate(isoYear(2009), monthOfYear(2), dayOfMonth(29));
}
System.out.println(data);
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.nex tValid(). 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.
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 represe ntaçã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);
public class ExemploResolverSI {
}
Period period = Period. hours(2);
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 class e para cada parte do período: javax.time.period.field.Days javax.time.period.field.Hours javax.time.period.field.Minutes javax.time.period.f ield.Months javax.time.period.f ield.Seconds javax.time.period.f ield.Weeks javax.time.period.f ield.Years
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 mi nutos. 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 m inuto; 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. Edição 69 Java Magazine
57
Michael Nascimento Santos
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.
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
[email protected]
É 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.
Daniel Cicero Amadei [email protected]
É Bacharel em Sistemas de Informação pelo Mackenzie e pós-graduado pela Fundação Vanzolini. Trabalha com Java desde 1999 e possui as certifi caçõ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 BEA.
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.
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
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.
Edição Especial
Entenda os principais conceitos sobre Testes e Inspeção de Software
Requisitos
Conheça os principais conceitos envolvidos na Engenharia de Requisitos
Especial
Projeto
Entenda o conceito de Arquitetura de Software e como trabalhar com os Estilos Arquiteturais
Mais de 60 mil downloads na primeira edição!
Processos
Melhore seus processos através da análise de risco e conformidade
Veja como integrar conceitos de Modelos Tradicionais e Ágeis
o
a
e
ã i d o
Faça um up grade em sua carreira.
Engenharia de Software
Ferramentas Open Source e melhores práticas na gestão de defeitos
c
www.devmedia.com.br/javamagazine/feedback
Saibaseusignificadoeparaqueserve
Verificação, Validação & Teste
eed
Dê seu voto sobre este artigo, através do link:
Conhecimento faz diferença! e n i z a g a m
u
e s ê
Veja como integrar o Processo Unificado ao desenvolvimento Web
Faça já sua assinatura digital! | www.devmedia.com.br/es
Em um mercado cada vez mais focado em qualidade ter conhecimentos aprofundados sobre requisitos, metodologia, análises, testes, entre outros, pode ser a diferença entre conquistar ou não uma boa posição profissional. Sabendo disso a DevMedia lança para os desenvolvedores brasileiros sua primeira revista digital totalmente especializada em Engenharia de Software. Todos os meses você irá encontrar artigos sobre Metodologias Ageis; Metodologias tradicionais ( document driven); ALM ( application lifecycle management ); SOA (aplicações orientadas a servicos); Analise de sistemas; modelagem; Métricas; orientação à objetos; UML; testes e muito mais. Assine já!
59
69 Java Magazine Edição
SEÇÃO JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS INTERMEDIÁRIOS E AVANÇADOS SOBRE JAVA
S
egundo 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écies1. 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 Pla tform SDK 3.
De Toolkit para SDK O fato de a Sun ter mudando o nome do WTK não deve causar muito espanto 1 Trecho baseado no artigo ‘Introdução à Evolução’.(fonte Wikipedia)
60 Java Magazine
Edição 69
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 principai s novidades que ela vem trazendo. Estas novid ades, 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.
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 Pla tform Mic ro 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.
ERNANDES MOURÃO JÚNIOR 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 configu raçã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 am biente de desenvolv imento integrado são o editor de código, gerenciador de projetos e arquivos, integração com o
Figura 1.
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 distri buição do NetBeans. Quem já trabalha com o Netbeans Mo-
bility 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 fu ncionalida-
Edição 69 Java Magazine
61
Nota do DevMan Ant: Ant é uma ferramenta utilizada para automatizar a
construção de software. Ela é similar ao make, mas é escrita na linguagem Java e foi desenvolvida inicialmente para ser utilizada em projetos desta linguagem. (fonte Wikipedia)
des, 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.
A KVM se Despede Após quase dez anos desde sua chegada, a máquina virtual da Java ME, a Kilo Virtual Machine (KVM) , fi nalmente 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 conti nuar 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 mai s rápida do que uma instrução interpretada.
62 Java Magazine
Edição 69
Figura 2.
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 perspect iva”, 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 2 Ferramenta utilitária do LWUIT que serve para editar arquivos de recurso do framework,como por exemplo,definição de tema,imagens, textos internacionalizados, etc.
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 Manager2 , dentro do Java ME Platform SDK 3. Alavancando de vez o nome LWUIT dentro da comunidade Java ME. O LWUIT, para quem ainda não conhece, é um fram ework 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 , clicar no botão e selecionar a biblioteca LWUIT, que já aparece na lista, ju nto 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.
Figura 3. Tela do profiler que mostra os tempos de execução dos métodos da aplicação
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 3 Determinado trecho de um processamento que demora muito para ser executado, afetando o tempo final de resposta de um processo.
Edição 69 Java Magazine
63
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 portabili dade 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 re-
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”3 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
64 Java Magazine
Edição 69
solvido 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-ti me 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 .
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.
E mais JSRs A cada dia novas APIs são finalmente especificadas, tornando-se aptas a serem implementadas por algum fa brica nte, 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 Bind ing for the Ope nGL ES AP I (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
Existem algumas APIs Java ME que i nteragem 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
Nota do DevMan OpenGL: OpenGL é uma API aberta utilizada na computação
gráfica para desenvolvimento de aplicativos gráficos, ambientes 3D, jogos,entre outros.(fonte Wikipedia)
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.
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 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
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
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.
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) ,
Edição 69 Java Magazine
65
Figura 4. Ícone que mostra a máquina virtual Java ME no Windows Mobile 6.
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, disponi bilizando 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
66 Java Magazine
Edição 69
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 , que se encontra disponível no SDK na pasta , 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 . 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.
Ernandes Mourão Júnior [email protected]
É Bacharel em Informática pela Universidade de Fortaleza e desenvolvedor certificado Java (SCWCD, SCBCD e SCMAD). Atualmente exerce os cargos de Analista de Desenvolvimento de Sistemas e Líder Técnico Java ME no Instituto Atlântico, em Fortaleza/CE. Recentemente foi premiado como um dos vencedores do Sony Ericsson Content Awards 2008, com a aplicação EcoMate.
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/CLDCHI_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 Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
ee e s
c
o e
t a
o ã i d
Dê seu voto sobre este artigo, através do link:
www.devmedia.com.br/javamagazine/feedback
Edição 69
SEÇÃO JAVA: NESTA SEÇÃO VOCÊ ENCONTRA ARTIGOS INTERMEDIÁRIOS E AVANÇADOS SOBRE JAVA
I
ntegrar 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
68 Java Magazine
Edição 69
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 tecnologi a para cada requisito arquitetural coletado; (3) Estudar os exemplos disponibiliz ados na referência [1]; (4) Estudar as tecnologias Java mais adequadas para cada requis ito (ex: WebServices ou RMI); (5) Implementar os cenários com as tecnologias Java escolhidas; (6) Testar as soluções em ambiente de homologação.
infra-estrutura do cliente não foi questionado o bastante para informar este fato. Para encurtar a história, a equipe desco briu que o envio de emails neste ambiente requeria o uso de certificados e protocolos com garantia de transporte seg uro (SMTP sobre SSL). Noites mal-dormidas, um atra-
so 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:
serem integrados sejam exatamente iguais;
MARCO AURÉLIO DE SOUZA MENDES Nota do 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 interoperabil idade. 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.
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; requisitos nãofuncionais que possam interferir na interoperabilidade;
real do cliente. O leitor mais experiente pode questionar as lições aprendidas neste exemplo. . Mas devemos lembrar que senso comum não é prática comum. 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 in-
tegraçõ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:
Iremos conhecer aqui fundamentos da integração de sistemas;
Java. Aqui iremos citar as principais tecnologias e padrões Java para a integração de aplicações;
conhecer aqui as melhores práticas de mercado 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 inimigo, para , 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.
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?
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.
Edição 69 Java Magazine
69
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.
Figura 1. Mash-up do Internet Google. Diferentes aplicações foram integradas aqui em nível de camada de apresentação. Podemos ver no exemplo acima blogs de diferentes autores, o Google Talk e mesmo uma aplicação de previsão de tempo.
Estilo de Interoperabilidade
APIs/Tecnologias Java
Transferência de Arquivos
Streams I/O; Soquetes
Bancos de Dados Compartilhados
JDBC
Procedimentos Remotos (RPC)
Servlets, JCA, Web Services/SOAP
Passagem de Mensagens
JMS, Web Services/SOAP
Tabela 1. Tecnologias Java para suportar estilos de interoperabilidade. Níveis de Interoperabilidade
APIs/Tecnologias Java
Nível de Dados
JDBC
Nível de Interface de Aplicação
Servlets, EJB/RMI, JCA, Web Services/SOAP, JavaMail
Nível de Processos de Negócio
JBI (Java Business Integration), OpenSOA SCA
Nível de Apresentação
Portlets
Tabela 2. Tecnologias Java para suportar níveis de interoperabilidade. Topologias de Interoperabilidade
APIs/Tecnologias Java
Ponto a ponto
Servlets, WebServices/SOAP, EJB/RMI
Hub and Spoke
JBI, OpenSOA SCA
Tabela 3. Tecnologias Java para suportar topologias de interoperabilidade.
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 desenvol-
70 Java Magazine
Edição 69
vimento 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. 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 tam bé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 an d Spok e. 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ídeocassete (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). Mais um desafio: Qual a topologia associada ao exemplo citado de interoperabilidade com o Microsoft Exchange? (Resposta no final deste artigo). Colete os requisitos arquiteturais de interoperabilidade na sua aplicação. Para cada requisito arquitetural, defina o estilo, nível e topologia associada.
Padrão EAI
Pergunta endereçada Como eu posso integrar múltiplas aplicações de forma que elas possam trocar informações?
Nota do DevMan Diagramas de Casos de Uso:
Como uma aplicação se comunica com outra através de mensagens?
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.
Como uma aplicação se conecta a um canal de mensagens para enviar e receber mensagens?
Como mensagens podem ser usadas para transferir dados entre aplicações?
Como mensagens podem ser enviadas para outro sistema se o originador não tem todas as informações necessárias?
Como um consumidor de mensagens (ex: Microsoft Exchange) pode selecionar que tipos de mensagens ele deseja receber.
Tabela 4. Padrões EAI usados para resolver a interoperabilidade com o Microsoft Exchange.
Tecnologias de Interoperabilidade Java , 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).
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) damente 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 , 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.
Note que neste diagrama o Microsoft Exchange suporta a operação de envio de email e por isso é chamado de Ator Secundário.
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. 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 Java Ma il . 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 Edição 69 Java Magazine
71
Figura 3. Esquema da solução de integração com o MS Exchange Ser ver
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”,“javax.net.ssl. SSLSocketFactory” ); //Enriquecedor da mensagem com SSL 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); ...
o Microsoft Exchange. Ao examinarmos a classe Transport , da API do JavaMai l, 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
72 Java Magazine
Edição 69
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. O trecho de código acima mostra estas peças conectadas. 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.
Um Processo para Interoperar Aplicativos 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 Sun Tzu.
Conhecer um processo para solucionar problemas técnicos é fundamental. Saber a hora e o lugar das ações é fundamental, especialmente nos mecanismos arquiteturais de interoperabilidade. Podemos resumir o processo de aplicação das estratégias e padrões EIP da seguinte forma: 1. Colete os requisitos arquiteturais de interoperabilidade. Uma boa fonte para
Padrão EAI
Comentários Captura a essência das soluções ESB de mercado.Note que o termo ESB, em sua definição original, é um padrão ou estilo arquitetural e não uma tecnologia ou especificação. Produtos como o JBoss ESB ou Apache ServiceMix podem ser vistos como produtos que implementam o padrão EIP Message Broker .
Message Broker
Captura a essência das soluções BPMS de mercado. O padrão Process Manager permite que uma integração com múltiplas fontes de dados e aplicações possam ser integradas através de um fluxo de trabalho arbitrário. Produtos como o JBoss BPM ou IBM WebSphere Process Ser ver são exemplos deste padrão. A linguagem BPEL (Business Process Execution Language) é um exemplo de linguagem usada para descrever os fluxos do padrão Process Manager . Process Manager
Padrão usado para implementar a interoperabilidade com múltiplos protocolos em aplicações ESB. Um produto como o JBoss ESB, por exemplo, suporta FTP, HTTP, SMTP, HTTPS e diversos outros formatos para interoperar aplicações. No interior da sua solução, vemos que o padrão Normalizer foi usado para suportar isso.
Normalizer
Tabela 5. Padrões EAI tipicamente usados em aplicações BPM/SOA/ESB.
Nota do DevMan Processo Unificado (UP):
O UP (Unified Process) é um processo para dese nvolvimento de software baseado no conceito de um projeto centrado em arquiteturas de software. Em termos simples isso significa reduzir os riscos técnicos do projeto nas suas fases iniciais através do ataque aos requisitos mais prioritários e complexos através de provas de conceito que busquem mitigar e eliminar estes riscos. O UP possui diversas variantes como por exemplo o popular IBM Rational Unified Process, o Enterprise Unified Process de Scott Ambler ou o processo ágil OpenUP.
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, identifi-
que 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 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; Edição 69 Java Magazine
73
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.
Marco Aurélio S. Mendes [email protected]
É 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.
martinfowler.com/articles/ mocksArentStubs.html Descreve o conceito de Mocks e que ele pode ser usado para simular aplicações ex ternas no contexto de projetos de integração de aplicações.
eaipatterns.com/
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!
74 Java Magazine
Edição 69
Site central dos p adrõ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.
Referência primária sobre a integração de aplicações corporativas, escrito por Gregor Hohpe, arquiteto de soluções do Google.
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.
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.
Dê seu feedback sobre esta edição! A Java Magazine tem que ser feita ao seu gosto. Para isso, precisamos saber o que você, leitor, acha da revista!
u
e s ê
eed
c
o
a
o ã i d
Dê seu voto sobre este artigo, através do link:
www.devmedia.com.br/javamagazine/feedback
e
Existem momentos onde a estrutura faz toda a diferença. Se o tempo é ruim, você encontra na Alog um porto seguro. Isto porque sua empresa reduz custos e melhora a governança em TI. Entre em contato e descubra porque a Alog é líder em hosting gerenciado.
REDUÇÃO DOS CUSTOS
AUMENTO DA GOVERNANÇA EM TI
0800 282 3330 | alog .com.br HOSTING GERENCIADO | COLOCATION | CONTINGÊNCIA | E-MAIL CORPORATIVO | CONECTIVIDADE | SERVIÇOS PROFISSIONAIS Edição 69 • Java Magazine
75