De volta do Devoxx! O evento foi muito bom. De longe o maior, mais alto nível, melhor organizado evento que já fui.
Minha palestra aconteceu na quarta e qual foi minha surpresa ao ver o Pete Muir (Seam/Weld/Arquillian), Dan Allen (Seam/Weld/Arquillian) e Aslak Knutsen (Arquillian lead) sentados na primeira fileira!
O mais legal foi ter conversado com eles e eles terem me mostrado que a proposta que apresentei já está no road map do Arquillian. Legal!
O evento foi uma ótima oportunidade para fazer networking. Além do convite para um brainstorm regado a cerveja belga do time do Arquillian, encontrei os brazucas Yara e o Vinícus Senger lá também.
Outra pessoa com quem estava querendo trocar umas idéias é o Mathieu Ancelin, criador do weld-osgi, idéia que eu há muito queria implementar e ele fez eximiamente.
Deve estar saindo nos proximos dias um artigo que escrevi para a InfoQ BR sobre a cobertura do Devoxx. Quando sair post o link aqui. Também disponibilizei os slides da palestra no Slideshare.
Esse mês e o próximo estarei palestrando no TDC e no Devoxx.
A edição Goiânia do TDC vai acontecer dias 29 e 30, e de novo estarei lá palestrando.
Sábado, dia 29/10, às 17:40 estarei falando sobre o Weld/CDI. Será uma palestra introdutória onde quero mostrar alguns recursos que temos e aplicações interessantes deles. Será uma versão mais básica da palestra que farei no Devoxx, e será focada em testes.
No domingo às 14:10 foi mostrar o Drools, a apresentação será similar à do TDC São Paulo, mas vou tentar me organizar melhor para mostrar todas as demos que tinha planejado mostrar Apareçam, prometo que vai ser interessante!
Em Novembro estarei na Bélgica para o Devoxx, que será uma semana inteira dedicada a tecnologia com representantes da Red Hat, Oracle, Adobe, Google, HP, IBM, SrpingSource e outros.
Lá vou fazer uma rápida palestra na quinta, dia 17/11, às 13:15. Vou falar como TDD combina com CDI: quais são os problemas enfrentados e quais alternativas temos. Terá um pouco de Weld, portable extensions e Arquillian. Aos brazucas que estiverem por lá, vamos combinar uma cerveja depois!
Estava para escrever um post mas ia entrar numa discussão de maven haters e lovers então resolvi esclarecer as coisas antes. Não entendo esse pessoal que odeia maven.
Reclamam: maven baixa toda a internet
Acho que quem reclama não entende muito como funciona o sistema de dependências do maven. O maven não faz (muita) mágica. O dono do artefato tem que definir as dependências do artefato.
Ou seja, de duas uma: ou o maven está baixando a internet porque os mantenedores doas artefatos foram relapsos ou realmente todas aquelas dependências eram necessárias.
Se foi preguiça do desenvolvedor, não é culpa do maven. Todos conhecemos sistemas de empacotamento (port, portage, agt, yum) e sabemos que tem muito mantenedor preguiçoso que, em vez de deixar as dependências enxutas, põe tudo como dependência para poupar dor de cabeça. Onde está o problema do maven aqui? Maven é uma ferramenta, ele faz o que for mandado.
Se é porque realmente existiam muitas dependências, ai não tem o que falar mesmo. Provavelmente esse pessoal que reclama nunca entrou site por site, procurando os binários, lendo documentação de que versão é compatível com que versão. Só de me poupar esse trabalho, mesmo com pom’s totalmente bagunçados, que baixam o repositório inteiro, para mim vale a pena usar maven. Dica: você não precisa ficar olhando o terminal esperando tudo ser baixado.
E ele baixa cada pacote uma única vez, é muito choro por pouca coisa. Além do disso, coisas que parar mim salvam muito tempo e o pessoal não pensa..
Não pensam: baixa dependências (transitivas!)
Agora com o Ivy isso já não é tão extraordinário, mas o Ivy foi provocado pelo Maven. Aliás, o Ivy usa os repositórios do Maven! Nunca usei o Ivy, mas para mim isso significa que ele também irá baixar toda a internet.
Não pensam: analisar source de pacotes
Já teve que decompilar classes com o jad (ou qualquer outra ferramenta) para debugar código? Quem já fez isso percebeu que é um pé no saco ter que decompilar todas as classes que formam a stack (e provavelmente mais) e ainda ter os números de linha todos bagunçados impedindo de usar o source na IDE para setar break points. Ou isso ou você procurava o source para baixar (de novo, procurando site em site) quando não tinha que fazer checkout de SCM e tendo que achar a tag certinha!
Não pensam: tooling
Já usou o m2eclipse (o antigo, antes da Sonar doar pra Eclipse)? Quando trabalhava em fábrica lembro o terror e pânico quando alguém commitava o .project, era ficar ajustando classpath um bom tempo. E tem algo mais porco que versionar JARs? Busca JARs, adiciona projetos dependentes ao classpath sem precisar dar build, mostra a árvore de dependência, o pom efetivo. Ele tem bugs sim, mas nem de longe vale a pena deixar de usá-lo por causa disso.
Para os que reclamam: Everythings Amazing & Nobodys Happy
Tente parar de usá-lo para ver o quanto vão dar valor a ele.
Havia escrito há muito tempo atrás como fazer o deploy de uma aplicação Seam no Tomcat. Daquele tempo para cá muita coisa mudou: o Seam junto com outros frameworks de IoC gerou a JSR-299 de Contexts and Dependency Injection (CDI), a JBoss criou o Weld (implementação de referência CDI), o Seam 3 foi lançado baseado na nova especificação.
Com a padronização numa JSR, agora implementações CDI são obrigadas a suportar uma variedade maior de ambientes de forma vendor neutral. O Weld faz isso e entre os ambientes suportados está o Tomcat. Portanto não há necessidade de configurações extras e gambiarras! O próprio projeto Weld provê archetypes maven com várias configuração, para criar uma aplicação para rodar em um servlet container:
E um projeto pronto para ser executado no Tomcat/Jetty será criado. O problema é que, devido a um bug, no Tomcat 7 pode ser que você veja a seguinte exceção:
SEVERE:Exception sending context initialized event to listener instance of class org.jboss.weld.environment.servlet.Listener
java.lang.NoClassDefFoundError: org/apache/AnnotationProcessor
at java.lang.ClassLoader.defineClass1(NativeMethod)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2820)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1143)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1638)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
at java.lang.ClassLoader.defineClass1(NativeMethod)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2820)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1143)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1638)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
at org.jboss.weld.environment.tomcat.Tomcat6Container.initialize(Tomcat6Container.java:47)
at org.jboss.weld.environment.servlet.Listener.contextInitialized(Listener.java:184)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4544)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5016)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1035)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:738)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1035)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:289)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:442)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:674)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
at org.apache.catalina.startup.Catalina.start(Catalina.java:596)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:303)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:431)
Caused by: java.lang.ClassNotFoundException: org.apache.AnnotationProcessor
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1671)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
... 36 more
O problema é que o Weld identifica erroneamente o Tomcat 7 como Tomcat 6. Para resolver o problema podemos definir explicitamente que o container que estamos usando é o Tomcat 7. Isso é feito criando-se um arquivo META-INF/services/org.jboss.weld.environment.Container no classpath com o conteúdo:
Foram abertas as incrições para o JBoss In Bossa 2010 que acontecerá em São Paulo dias 7 e 8 de maio. O evento é o maior da América Latina relacionado a JBoss e contará com a presença de palestrantes internacionais como o Peter Muir (project lead do Seam e do Weld), Benjamin Mestrallet (CEO da eXo Platform) e Mauricio Salatino (CTO da PlugTree).
Veja alguns dos principais assuntos previstos na agenda:
Ok, estou convencido que usar mock objects é uma boa idéia mas como faço isso? A princípio não é qualquer código que está pronto para ser testado usando-se mocks, ele precisa ser escrito de uma forma a possibilitar isso.
No geral escrever código de tal forma que seja testável é uma boa prática e indica que ele segue alguns princípios de projeto que o tornam fracamente acoplado, aumentam sua manutenibilidades, etc.
Vejamos um exemplo de código que deva ser alterado para se tornar testável:
Como já devo ter dito, não sou muito bom de exemplos.. Mas acho que esse ai está bem ilustrativo, embora para alguns DAO possa parecer meio arcaico. Deixei as reticências para a imaginação de vocês.
Existem 3 formas de se alterar esse código, as duas primeiras se usam de técnicas de bom projeto e melhoram a qualidade do software:
1. Dependency Injection (DI)
O mais simples e limpo. Foi a forma usada para injetar os mocks no exemplo do post passado:
Notem que o createCustomerDao() é protected, isso porque na hora de usá-lo nos testes vamos estender essa classe e sobrescrever o createCustomerDao() para retornar um mock:
publicclass CustomerFacadeTest {private Mockery context =new Mockery();public CustomerFacadeTest(){
context =new Mockery();}
@Test
publicvoid testHire()throwsException{// set up mockfinal CustomerDao customerDao = context.mock(CustomerDao.class);// test state
CustomerFacade customerFacade =new CustomerFacade(){
@Override
protected CustomerDao createCustomerDao(){// return the mock instead!!return customerDao;}};// expectations
context.checking(new Expectations(){{
oneOf(customerDao).save(12);}});// execute
customerFacade.hireCustomer(new Custumer(12));// verify
context.assertIsSatisfied();}}
3. Aspect Oriented Programming
Essa é uma técnica workaround, e é para quando realmente não há jeito de usar-se as outras técnicas. Com ela não é preciso alterar código algum, apenas criar um point cut que intercepte a instanciação do CustomerDaoImpl. O test case é o seguinte:
publicclass CustomerFacadeTest {private Mockery context =new Mockery();private CustomerDaoImpl customerDao;public CustomerFacadeTest(){
context =new Mockery();// so we can mock a concrete class
context.setImposteriser(ClassImposteriser.INSTANCE);
customerDao = context.mock(CustomerDaoImpl.class);}// returns the mock instance in test contextpublic CustomerDaoImpl getCustomerDao(){return customerDao;}
@Test
publicvoid testHire()throwsException{// test state
CustomerFacade customerFacade =new CustomerFacade();// expectations
context.checking(new Expectations(){{
oneOf(customerDao).save(12);}});// execute
customerFacade.hireCustomer(new Custumer(12));// verify
context.assertIsSatisfied();}publicString print(){return("adasda");}}
E criando-se o aspecto em AspectJ:
public aspect InstanciatingAspect{// intercept only from CustomerFacadeTest
pointcut testing(Object test):this(test)&&
execution(publicvoid CustomerFacadeTest.*());// intercept CustomerDaoImpl instantiations
pointcut instanciateCustomerDao(Object test):
cflow(testing(test))&&
call(CustomerDaoImpl.new(..));Object around(Object test): instanciateCustomerDao(test){// get a hold of the mock instance made public by our test case
CustomerDaoImpl customerDaoImpl =((CustomerFacadeTest) test).getCustomerDao();// return the mock reference insteadreturn customerDaoImpl;}}
Como visto, a única dificuldade é que o aspecto deve retornar uma instância do mock que tenha sido criada no contexto do test case.Para tanto instanciamos o mock no test case e o tornamos acessível através do getCustomerDao(). Feito isso, ao interceptarmos no aspecto a criação do CustomerDaoImpl() nós a substituímos pela referência obtida do test case.
O seam-gen é um gerador de scaffolding muito útil para quem programa em Seam. O seam-gen em si é uma ferramenta CLI, mas o JBoss Tools nos dá uma GUI para facilitar nossa vida. Bom, então o primeiro passo é instalar o plugin do JBoss Tools. É só baixar do site deles e descompactar no diretório do Eclipse, nada de mais.
Você notará que serão adicionadas várias perspectivas, entre elas, a Seam. Vamos selecionar essa perspectiva e criar um novo Seam Project. Com isso já temos um projeto funcional com segurança, apresentação de erros, e conexão com banco de dados. Também é criado um projeto <projeto>-Test para testes. Vamos entender um pouco do que nos foi gerado.
classe usada pelo Seam para autenticação
arquivo chave. Indica ao Seam que ele deve procurar nesse diretório por componentes
persistence.xml do JPA
arquivo com algumas propriedados a serem substituidas pelo Ant no components.xml
import.sql do Hibernate
mensagens para internacionalização
arquivo Drools com regras de autorização
datasource gerado para o JBoss
arquivo de configuração do Seam (componentes, integração)
arquivo de configuração WAR do JBoss
arquivo de configuração do Seam (regras de fluxo, segurança, controle de conversação)
similar ao arquivo pages.xml, mas específico para o login.xhtml
arquivo de configuração do plugin Hibernate Console
Vamos ver por alto algumas configurações interessantes. Na parte de segurança temos no components.xml:
O Seam possui o conceito de eventos. Eventos são mensagens que podem ser capturadas ou lançadas, no estilo broadcast. O Seam em si lança vários eventos e podemos capturar esses eventos através de XML, como no exemplo acima. O trecho acima está invocando métodos do componente built-in #{redirect} quando eventos de segurança forem lançados. Ele especifica que a view id JSF deve ser salva quando o usuário não-logado tentar acessar uma página restrita e que essa view id deve ser restaurada uma vez que ele se autentique.
Um outro trecho especifica que componente é responsável pela autenticação:
E no pages.xml dizemos que página deve ser utilizada para login:
<exceptionclass="org.jboss.seam.security.NotLoggedInException"><redirectview-id="/login.xhtml"><message>Please log in first</message></redirect></exception>
Aqui temos um tratamento de exceção bem parecido com o de servlets que definimos no web.xml. O usuário será redirecionado para a página login.xhtml e uma mensagem JSF será adicionada.
Se olharmos no login.pages.xml vemos ainda:
O que nos lembra bastante as navigation rules JSF. De fato parecem muito, mas as extendem em funcionalidade, podendo não só enviar o usuário para outra página como também lançar um evento, executar um método para avaliar qual será a próxima página, e algumas coisas mais avançadas.
O objetivo desse post é apenas nos familiarizar mais com o Seam e dar uma visão sobre o scaffold que nos foi gerado de base e que iremos usar para desenvolver nossa aplicação. Ainda vamos falar com mais detalhes de eventos, componentes e configurações.
Como recebo vários hints no postJBoss Seam no Tomcat, resolvi escrever mais sobre Seam. Esse post abre uma série de tutoriais de Seam.
Seam é um frameworkmuito amplo. Pegue alguns dos frameworks mais representativos, ponha algumas práticas de desenvolvimento e misture: assim que vejo o Seam. O Seam em sua plenitude usa os frameworks: EJB3, JSF, Facelets, Hibernate, Hibernate Validations, Richfaces, Ajax4JSF, jBPM, Drools (qualquer associação ao nome JBoss é mera coincidência). É muita coisa. Mas ele não só usa esses frameworks, ele também provê uma integração entre eles, disponibiliza componentes built-in (à la Spring, cujo qual inclusive pode-se integrar) e introduz algumas melhorias sobre eles. Além disso ele apregoa algumas práticas como desenvolvimento orientado a componentes e desencoraja outras, como desenvolvimento em camadas.
Vou deixar de lado o que cada framework faz, pois não entra no mérito do que o Seam tem a oferecer. Dando um enfoque bem prático, o Seam:
Reduz plumbing code do JSF. Quem já programou com JSF sabe do que estou falando (Não? FacesContext? faces-config.xml?).
Resolve o infame problema do “back button” em JSF. E reload, e bookmarking, e …
Facilita o uso de JPA. Tem um código cheio de merges()? Usa uma long-running transaction num page flow?
Possibilita page flows usando o jBPM.
Permite aplicações multi-windows. E finalmente poder clicar “Abrir em nova janela” nos links!
Permite o uso de workspaces. Como no Linux.
Faz uso extensivo de anotações. Mas também permiti o uso de XML.
Possui uma penca de tags JSF. Uns muito úteis, uns bem exóticos…
E muito mais!
Dada essa introdução, vou detalhar nos próximos posts cada um desses itens dizendo como realizamos eles com o uso so Seam. Esse blog não é sobre Seam, então esperem outros posts no meio. Até mais!
EDIT: esse post é para o Seam 2. Utilizar o Weld (com Seam 3) no Tomcat ficou bem mais fácil, veja um post mais recente sobre o assunto.
Quem está começando a aprender JBoss Seam pode ter a impressão que ele roda apenas no JBoss AS, o que não é verdade. Devido à maioria da documentação ser provida pelo pessoal do JBoss é claro que tudo é feito tendo-se em mente o AS deles. Mesmo quem sabe ser possível rodar sobre outros ASs, muitas vezes não tem idéia de como fazê-lo.
O Seam em si não é um serviço no JBoss, mas sim um framework, o que possibilita sua utilização em vários ASs. Caso não seja usada nenhuma facilidade EE (ou mesmo algumas que o Seam cobre), é possível fazer o deploy de uma aplicação Seam até mesmo sobre um simples container web como o Tomcat e esse post sobre esse caso.
Sendo o Seam uma biblioteca, dificuldades em fazer deploy em containers se limitam a conflitos e dependências. Mas quem já usou Seam sabe da grande mão na roda que é o seam-gen para geração de scaffold (inclusa no JBoss Tools). O problema é que o scaffold gerado é feito para o JBoss.
Então nossos esforços serão basicamente mudar configurações do JBoss para o Tomcat e passar para o Seam o tratamento de features enterprise. As instruções são para o seguinte ambiente:
Eclipse 3.3.2 com JBoss Tools 2.1.1.GA
JBoss Seam 2.0.3.CR1
Apache Tomcat 6.0.16
Vamos lá:
Já que estamos rodando num container web não temos o controle transacional provido pelo AS, então devemos passá-lo para o Seam.
No persistence.xml alteramos o transaction-type da persistence-unit para RESOURCE_LOCAL e removemos a property hibernate.transaction.manager_lookup_class. Vamos mudar também a linha do data source, para utilizar ENC. Ela deve refletir:
Onde #{entityManager} é o nome da managed-persistence-context, que é o persistence context que será usado pelo Seam nas conversações. A definição do namespace:
Para finalizarmos com essa parte de persistência falta apenas criar o próprio datasource. Quem usa o Sysdeo deve ir nas propriedades do Tomcat Project fazer isso, mas quem usa o WTP (usem!) não tem essa opção. Ao invés disso podemos usar o esquema de deploy de contexto do Tomcat. Crie um arquivo context.xml em seu WEB-INF/META-INF contendo seu data source. No meu caso:
A maioria delas pode ser achada na pasta lib do Seam. No meu caso criei uma User Libraries e as marquei como dependência para a Web Library, assim organizo meus jars e torno mais fácil a adição desses frameworks em projetos futuros.
Assumi que se sabe somo criar um projeto Seam. Deixei a idéia de um tutorial básico de lado, quero ainda falar do uso do JBoss Tools para geração de código e um pouco da integração com o Drools e com o jBPM (muito interessante!!).
Dêem o feedback sobre o que querem ler! Que se houver muita manifestação de interesse faço um post, até se for o caso, sobre o gorado tutorial ou o passo-a-passo para a criação de um projeto Seam.
O padrão push é o de mais tradição, nele os dados são previamente carregados e disponibilizados para a camada de apresentação. Na prática: por exemplo o Struts com sua Action carregando os dados no request para serem usados na JSP.
No padrão pull a camada de apresentação é quem faz as chamadas aos componentes que irão lhes retornar os dados. Na prática: pro exemplo a página JSF invocando um getter no backing bean.
Resultado: no padrão do Struts muitas vezes os dados são gerados de forma que não podem ser facilmente reutilizados. Por outro lado JSF torna a reutilizacão muito mais clara e direta, através de componentes, no melhor estilo OO. Pessoalmente vejo a relação página-objetos muito mais intuitiva do que página-ação.