
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");
}
..
}
Listagem 1 - Parte da Classe EmployeeController gerado pelo NetBeans
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();
}
}
...
}
Listagem 2 - Parte da classe EmployeeDAO recém criada.
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 !!!