Esta semana saiu nas bancas a edição 31 da revista Mundo Java com o título "Futuro do Java Corporativo", com um artigo meu sobre o mesmo tema.
Para quem quer saber o que vai rolar na próxima especificação do Java EE 6, está edição está um prato cheio, portanto compre uma edição e aproveite...
Não vou entrar em muitos detalhes da matéria, mas vou descrever aqui melhor a demo que está na revista, onde apresento uma aplicação utilizando Session Bean da especificação de EJB 3.1 no GlassFish V3.
Instalando o GlassFish V3 e o conteiner EJB
No site do GlassFish baixe o arquivo zip e descompacte em um diretório da sua escolha, a partir deste momento vamos chamar apenas de GLASSFISH_HOME\bin e execute o updatetool (Figura 1).
Na opção available addons, selecione glassfishv3-ejb e clique em Install, esta ação irá instalar o container EJB no Glassfish.
Instalando o plugin GlassFish V3 no NetBeans
Agora inicie o NetBeans (de preferência a versão 6.1 ou 6.5) e instale o plugin do GlassFish V3 para o NetBeans em Ferramentas | Plugins, selecione em plugins disponíveis "Glassfish JavaEE Integration" e clique em instalar.
Após instaldo o plugin, na aba Serviços, adicione um novo servidor, selecione a opção GlassFish V3 e siga os passos apontando o diretório GLASSFISH_HOME de instalação e finalizar.
Vamos explorar agora uma funcionalidade no NetBeans para a criação de CRUD, crie uma nova Aplicação Web na categoria Web e clique em Próximo.
Neste passo digite "DEMO_EJB31" e clique em Próximo, na opção servidores selecione o servidor GlassFish V3 T2 recém instalado e clique em Próximo. No último passo selecione o framework JavaServer Faces e clique em Finalizar.
Agora vamos criar uma aplicação CRUD completa baseada em uma entidade de banco de dados, para isso clique em Arquivo | Novo Arquivo, na categoria Persistence selecione a opção "Classes de entidade do banco de dados" e clique em Próximo.
OBS: Antes de fazer o passo abaixo, inicie o banco de dados Derby na guia Serviços do NetBeans, clique com o botão direito do mouse em Java DB e clique em "Iniciar Servidor".
Neste segundo passo, na lista Fonte de Dados selecione a opção "Nova Fonte de Dados", no campo "Nome JNDI" digite jndi/TesteEJB e na lista "Conexão de banco de dados" selecione o banco "jdbc:derby://localhost:1527/vir" já existente e clique em OK.
Se tudo ocorreu de maneira correta será apresentada uma lista de tabelas disponíveis. Selecione a tabela Employee, clique em Adicionar e em seguida clique em Próximo.
No campo nome do pacote digite br.com.netfeijao.entities e por fim clique no botão "Criar unidade de persistência". Mantenha os valores default clique em Criar e depois clique em Finalizar. Esta ação irá criar a classe persistente Employee no pacote informado.
Agora vamos utilizar um recurso no NetBeans para a criação de um CRUD com páginas JSF baseado em entidades JPA, no caso a classe Employee que acabamos de criar. Acesse o menu Arquivo | Novo Arquivo, na pasta categoria Persistence selecione "Páginas JSF de classes de entidade" e clique em próximo. Adicione a única classe persistente (Employee) existente (ver figura 1), clique em Próximo e a seguir clique em Finalizar.
Execute a aplicação pressionando o botão F6 e faça testes, perceba que o NetBeans criou uma aplicação completa "a lá Ruby on Rails" com apenas alguns cliques.
public class EmployeeController { private Employee employee = null; private List employees = null; @Resource private UserTransaction utx = null; @PersistenceUnit(unitName = "DEMO_EJB31PU") private EntityManagerFactory emf = null; public EntityManager getEntityManager() { return emf.createEntityManager(); } public int batchSize = 5; private int firstItem = 0; private int itemCount = -1; public SelectItem[] getEmployeesAvailableSelectMany() { return getEmployeesAvailable(false); } public SelectItem[] getEmployeesAvailableSelectOne() { return getEmployeesAvailable(true); } private SelectItem[] getEmployeesAvailable(boolean one) { List allEmployees = getEmployees(true); int size = one ? allEmployees.size() + 1 : allEmployees.size(); SelectItem[] items = new SelectItem[size]; int i = 0; if (one) { items[0] = new SelectItem("", "---"); i++; } for (Employee x : allEmployees) { items[i++] = new SelectItem(x, x.toString()); } return items; } public Employee getEmployee() { if (employee == null) { employee = getEmployeeFromRequest(); } if (employee == null) { employee = new Employee(); } return employee; } public String listSetup() { reset(true); return "employee_list"; } public String createSetup() { reset(false); employee = new Employee(); return "employee_create"; } public String create() { EntityManager em = getEntityManager(); try { utx.begin(); em.persist(employee); utx.commit(); addSuccessMessage("Employee was successfully created."); } catch (Exception ex) { try { if (findEmployee(employee.getId()) != null) { addErrorMessage("Employee " + employee + " already exists."); } else { ensureAddErrorMessage(ex, "A persistence error occurred."); } utx.rollback(); } catch (Exception e) { ensureAddErrorMessage(e, "An error occurred attempting to roll back the transaction."); } return null; } finally { em.close(); } return listSetup(); } public String detailSetup() { return scalarSetup("employee_detail"); } public String editSetup() { return scalarSetup("employee_edit"); } .. }
Perceba que o NetBeans gerou o código de persistencia em uma classe controller, vamos melhorar um pouco isto, tirando o código de acesso aos dados e isolando isto em uma classe DAO, depois vamos transformar esta classe em um EJB sem interface,
OBS> Cuidado, o uso de um EJB DAO deve ser restrito, pois dependendo do seu uso, isso pode ser um Anti-Pattern
Vamos criar uma nova classe Java acessando o menu Arquivo | Novo Arquivo. Nomeie a classe como EmployeeDAO no pacote br.com.mundojava.dao. Nesta classe vamos colocar todos os métodos de acesso ao banco utilizados na classe EmployeeController e inclui-los na classe recém criada. Veja parte do código na Listagem 18.
Vamos criar uma nova classe Java acessando o menu Arquivo | Novo Arquivo. Nomeie a classe como EmployeeDAO no pacote br.com.mundojava.dao.
Adicionalmente vamos criar uma classe para tratamento de Exceptions, crie uma classe e nomeie de DatabaseException, na classe DAO vamos encapsular os erros de acesso a banco nesta classe.
Criada a classe DAO, vamos colocar todos os métodos de acesso ao banco utilizados na classe EmployeeController. Feito isto, transforme esta classe EmployeeDAO em um EJB colocando a anotação Stateless em cima da declaração da classe. Veja parte da classe criada na Listagem 2.
@Stateless public class EmployeeDAO { public static final int PROCESSADO = 1; public static final int JA_EXISTE = 2; public static final int ERRO = 3; @PersistenceUnit(unitName = "DEMO_EJB31PU") private EntityManagerFactory emf; private EntityManager getEntityManager() { return emf.createEntityManager(); } public Employee getEmployeeFromRequestParam(Object employee) { EntityManager em = getEntityManager(); try{ Employee o = em.merge((Employee) employee); return o; } finally { em.close(); } } public int create(Employee employee) throws DatabaseException{ EntityManager em = getEntityManager(); try { em.getTransaction().begin(); em.persist(employee); em.getTransaction().commit(); return EmployeeDAO.PROCESSADO; } catch (Exception ex) { try{ int opt = 0; if (getEmployee(employee.getId()) != null) { opt = EmployeeDAO.JA_EXISTE; } else { opt = EmployeeDAO.ERRO; throw new DatabaseException("A persistence error occurred."); } em.getTransaction().rollback(); return opt; }catch(Exception sup){ throw new DatabaseException("An error occurred attempting to roll back the transaction."); } finally { em.close(); } } } public void edit(Employee employee) throws DatabaseException { EntityManager em = getEntityManager(); try { em.getTransaction().begin(); em.merge(employee); em.getTransaction().commit(); } catch (Exception ex) { try { em.getTransaction().rollback(); throw new DatabaseException(ex.getLocalizedMessage()); } catch (Exception e) { throw new DatabaseException("An error occurred attempting to roll back the transaction."); } } finally { em.close(); } } ... }
Fica um desafio para o leitor do blog, criar os métodos
public void destroy(Employee employee); public List getEmployees(boolean all, int batchSize, int firstItem); public List getEmployees(boolean all, int batchSize, int firstItem); public Employee getEmployee(Integer id); public int getItemCount();Por fim, na classe EmployeeController vamos fazer algumas alterações para consumir o EJB sem interface. Primeiro declare uma variável do tipo EmployeeDAO, e vamos injetar com a referência do EJB, e troque todas as referências ao código de acesso ao banco para apontar para o nosso EJB DAO, veja como ficou na classe EmployeeController na Listagem 3.
public class EmployeeController { private Employee employee = null; private List employees = null; public int batchSize = 5; private int firstItem = 0; private int itemCount = -1; @EJB EmployeeDAO dao; public SelectItem[] getEmployeesAvailableSelectMany() { return getEmployeesAvailable(false); } public SelectItem[] getEmployeesAvailableSelectOne() { return getEmployeesAvailable(true); } private SelectItem[] getEmployeesAvailable(boolean one) { List allEmployees = getEmployees(true); int size = one ? allEmployees.size() + 1 : allEmployees.size(); SelectItem[] items = new SelectItem[size]; int i = 0; if (one) { items[0] = new SelectItem("", "---"); i++; } for (Employee x : allEmployees) { items[i++] = new SelectItem(x, x.toString()); } return items; } public Employee getEmployee() { if (employee == null) { employee = getEmployeeFromRequest(); } if (employee == null) { employee = new Employee(); } return employee; } public String listSetup() { reset(true); return "employee_list"; } public String createSetup() { reset(false); employee = new Employee(); return "employee_create"; } public String create() { int resultado = dao.PROCESSADO; try { resultado = dao.create(employee); if (resultado == dao.PROCESSADO) { addSuccessMessage("Employee was successfully created."); } else if (resultado == dao.JA_EXISTE) { addErrorMessage("Employee " + employee + " already exists."); } } catch (Exception ex) { ensureAddErrorMessage(ex, ex.getLocalizedMessage()); } return listSetup(); } public String detailSetup() { return scalarSetup("employee_detail"); } public String editSetup() { return scalarSetup("employee_edit"); } private String scalarSetup(String destination) { reset(false); employee = getEmployeeFromRequest(); if (employee == null) { String requestEmployeeString = getRequestParameter("jsfcrud.currentEmployee"); addErrorMessage("The employee with id " + requestEmployeeString + " no longer exists."); String relatedControllerOutcome = relatedControllerOutcome(); if (relatedControllerOutcome != null) { return relatedControllerOutcome; } return listSetup(); } return destination; } public String edit() { EmployeeConverter converter = new EmployeeConverter(); String employeeString = converter.getAsString(FacesContext.getCurrentInstance(), null, employee); String currentEmployeeString = getRequestParameter("jsfcrud.currentEmployee"); if (employeeString == null || employeeString.length() == 0 || !employeeString.equals(currentEmployeeString)) { String outcome = editSetup(); if ("employee_edit".equals(outcome)) { addErrorMessage("Could not edit employee. Try again."); } return outcome; } try { dao.edit(employee); addSuccessMessage("Employee was successfully updated."); } catch (DatabaseException ex) { String msg = ex.getLocalizedMessage(); if (msg != null && msg.length() > 0) { addErrorMessage(msg); }else if (getEmployeeFromRequest() == null) { addErrorMessage("The employee with id " + currentEmployeeString + " no longer exists."); return listSetup(); } else { addErrorMessage("A persistence error occurred."); } return null; } return detailSetup(); } public String destroy() { employee = getEmployeeFromRequest(); if (employee == null) { String currentEmployeeString = getRequestParameter("jsfcrud.currentEmployee"); addErrorMessage("The employee with id " + currentEmployeeString + " no longer exists."); String relatedControllerOutcome = relatedControllerOutcome(); if (relatedControllerOutcome != null) { return relatedControllerOutcome; } return listSetup(); } try { dao.destroy(employee); addSuccessMessage("Employee was successfully deleted."); } catch (DatabaseException ex) { ensureAddErrorMessage(ex, ex.getLocalizedMessage()); return null; } String relatedControllerOutcome = relatedControllerOutcome(); if (relatedControllerOutcome != null) { return relatedControllerOutcome; } return listSetup(); } ... }
Listagem 3 - Classe EmployeeController refatorada para utilizar o EJB DAO sem interface
Perceba que na expressão "@EJB EmployeeDAO dao" estamos referenciando ao EJB, sem interface para atrapalhar, claro que não é possível criar uma instância utilizando a palavra reservada new ainda, na verdade estamos trabalhando com um proxy, mas se quisermos podemos fazer um lookup utilizando JNDI também.
Vou deixar o restante dos métodos para o leitor resolver, é apenas trocar a referencia pela classe dao conforme os métodos acima.
Compile e faça o deploy, rode a aplicação, se tudo deu certo você irá ver a sua aplicação rodando (ver figura 2) com um EJB sem interface, e o melhor, o EJB está dentro de um arquivo .war, já estamos implementando o empacotamento simplificado.
Veja como ficou o empacotamento do nosso projeto na Figura 3.
Para maiores informações sobre o futuro do Java Corporativo, leia a edição 31 da Mundo Java, que ainda traz ótimos artigos como:
- EJB 3.1:Conheça as Novidades do Futuro do Java Corporativo.
- Autor:Wagner Roberto dos Santos
- Grizzly e Comet - Ajax Reverso com Escalabilidade.
- Autor: Pedro Cavalero
- Usando o Mavem para melhorar a Qualidade dos seus Projetos.
- Autor:Márcio Varchavsky
- Criando Software mais próximo do Cliente com Domain-Drivgen Design.
- Autor:Sérgio Lopes
- Setembro: Mês de Java.
- Autor:Mauricio Leal
- Testes de unidades Avançadas com JMock 2
- Autor:Eduardo Guerra
- Gerenciamento de Conteúdo Web com OpemCMS -Customização de Sites.
- Autor:Rodrigo Cunha de Paiva
- Tirando o Máximo dos Interceptors no Struts2.
- Autor: José Yoshiriro Ajisaka Ramos
- Tendências em Foco:Ganhando com Open Source
- Autor:Cezar Taurion
- Jogo Rápido
- Autor:Charbel Symanski e Rodrigo Barbosa Cesar
- Mundo OO: Requisitos Executáveis com FIT
- Autor:Rodrigo Yoshima
- SOA na Pratica:Iniciando Projetos SOA.
- Autor:Ricardo Ferreira
Diversão Garantida !!!
2 comentários:
Muito bom o artigo, mas fiquei com uma dúvida. Por que, depois que se criou a classe DAO, não mais se usou JTA?
Olá Simeão,
Na época que fiz esta demo, foi bem quando foi lançado o suporte a EJB 3.1 no Glassfish V3,
E nesta época estava dando muito pau utilizar JTA, na época conversei até com o Reza Rahman e Arun Gupta sobre isto, acredito que hoje este problema já tenha sido resolvido,
Se você quiser testar, fica aí o desafio =)
é só incluir
@Resource UserTransaction usr;
e trocar o código de transação do código por usr.begin(), usr.commit() e usr.rollback();
Qualquer coisa é só avisar !!!
Abraço,
Postar um comentário