Trabalhando com o SVN no Eclipse

Introdução

Neste pequeno tutorial vou apresentar uma descrição básica de como se trabalhar com o Subversion (SVN) no Eclipse.
Embora outros servidores de controle de versão estejam sendo utilizados cada vez mais pelo mercado, como o Git, por exemplo, o SVN ainda é, sem dúvida, utilizado por uma grande quantidade de usuários nas empresas.
Apesar da grande utilização, nem sempre as melhores práticas são adotadas quando se trabalha com este tipo de ferramenta, nem mesmo recursos como merging e branching são utilizados na maioria das vezes (ou pela maioria dos usuários), o que acaba por provocar a  subutilização da ferramenta, afetando consequentemente a produtividade das equipes.

Objetivos

Apresentar orientações básicas de como trabalhar com o SVN no Eclipse, que é um dos ambientes de desenvolvimento Java mais utilizados do mercado.
Serão apresentados exemplos de como criar um branch, realizar um merge entre o branch e o trunk, e resolver problemas de conflito durante o merge.
Os conceitos apresentados são válidos para outros ambientes de desenvolvimento, mas os procedimentos podem mudar de acordo com os plugins utilizados em cada um.

Pré-Requisitos

Como não é o foco do tutorial apresentar a instalação do servidor SVN, é necessário ter um repositório disponível para os testes. Uma dica, para se testar em uma máquina local, é instalar o VisualSVN Server Manager.
É necessário também ter o plugin do SVN instalado no Eclipse. Um exemplo de como a instalação do plugin pode ser feita pode ser encontrado no seguinte endereço: https://luizgustavoss.wordpress.com/2009/12/13/preparando-um-ambiente-de-desenvolvimento-java-ee-baseado-em-eclipse/

Mão na Massa!

Configurando o repositório no Eclipse

Para começar a utilizar um repositório do SVN é necessário configurá-lo n o Eclipse. Para isso é preciso ter a URL do repositório, o que pode ser conseguido com o administrador do repositório.
Com a URL em mãos, abra a perspectiva do repositório SVN no Eclipse:

Clicando com o botão auxiliar do mouse, selecione a opção para adicionar um novo local:

Adicione a URL do repositório de testes no campo indicado:

Pode ser necessário informar o usuário e senha para acesso. Esta informação pode ser conseguida com o administrador do servidor:

Após estes passos, o repositório será apresentado na perspectiva, conforme abaixo:

A estrutura sugerida pelo SVN possui três diretórios: branches, tags e trunk.

/trunk –  o diretório onde o desenvolvimento principal do projeto ocorre, ou seja, onde sempre está a versão mais recente e estável dos artefatos oficiais;
/branches – o diretório onde pode-se criar ramificações da linha principal de desenvolvimento (trunk);
/tags – o diretório onde se pode armazenar ramificações da linha principal que são criadas, e talvez destruídas, mas nunca modificadas.

Criando uma aplicação Java

Crie, no C:\, um diretório chamado workspace, e nele crie dois diretórios chamados work1 e work2. Estes serão os workspaces usados para os testes.

Abra o Eclipse apontando para o workspace work1. Após configurar o ambiente (adicionar um JDK apropriado) crie um projeto Java simples, chamado Calculadora. Neste projeto crie um pacote chamado testes.svn, e nele crie uma classe chamada Calculadora.java, com os seguintes métodos:


public class Calculadora {

public BigDecimal somar(BigDecimal param1, BigDecimal param2) throws Exception{
return null;
}

public BigDecimal subtrair(BigDecimal param1, BigDecimal param2) throws Exception{
return null;
}

public BigDecimal multiplicar(BigDecimal param1, BigDecimal param2) throws Exception{
return null;
}

public BigDecimal dividir(BigDecimal param1, BigDecimal param2) throws Exception{
return null;
}
}

Esta é a estrutura do projeto após a criação da classe:

Vamos agora colocar o projeto no repositório que configuramos anteriormente. Clique com o botão auxiliar do mouse sobre o projeto, vá até a opção de menu “Team” e escolha a opção “Share Project”:

Na próxima tela, escolha a opção SVN e prossiga. Na tela seguinte, escolha o repositório configurado anteriormente e prossiga:

Na próxima tela, temos a opção de configurar o nome sobre o qual o projeto será armazenado no repositório, e sua localização. Marque a segunda opção, e clicando no botão “Select…” informe o diretório trunk:

Ao retornar da tela, esta deverá ser a configuração da tela:

Prossiga, e na tela final digite uma mensagem inicial para o versionamento e confirme:

O seguinte comando será executado contra o servidor, adicionando o projeto ao repositório:

mkdir –parents -m “Versionamento do projeto Calculadora.” https://votti/svn/calculadora/trunk/Calculadora
checkout https://votti/svn/calculadora/trunk/Calculadora -r HEAD –depth=immediates –force

Logo em seguida, será apresentada a perspectiva de sincronização, onde poderemos “commitar” o projeto no repositório, ou seja, enviar o conteúdo do projeto para o repositório, literalmente.
As setas cinza sobre os arquivos e diretórios indicam que estes arquivos devem ser comitados, então selecione os mesmos e, clicando com o botão auxiliar do mouse sobre os itens selecionados, escolha a opção “commit”:

Na tela seguinte, inclua um comentário inicial e confirme:

Os seguintes comandos serão executados, indicando que os artefatos foram “commitados” com sucesso:

add -N C:\workspace\work1\Calculadora\.settings
A         C:/workspace/work1/Calculadora/.settings
add -N C:\workspace\work1\Calculadora\src
A         C:/workspace/work1/Calculadora/src
add -N C:\workspace\work1\Calculadora\src\testes
A         C:/workspace/work1/Calculadora/src/testes
add -N C:\workspace\work1\Calculadora\src\testes\svn
A         C:/workspace/work1/Calculadora/src/testes/svn
add -N C:\workspace\work1\Calculadora\.settings\org.eclipse.jdt.core.prefs
A         C:/workspace/work1/Calculadora/.settings/org.eclipse.jdt.core.prefs
add -N C:\workspace\work1\Calculadora\.project
A         C:/workspace/work1/Calculadora/.project
add -N C:\workspace\work1\Calculadora\src\testes\svn\Calculadora.java
A         C:/workspace/work1/Calculadora/src/testes/svn/Calculadora.java
add -N C:\workspace\work1\Calculadora\.classpath
A         C:/workspace/work1/Calculadora/.classpath
commit -m “Versionamento inicial dos artefatos.” (8 paths specified)
Adding         C:/workspace/work1/Calculadora/.classpath
Adding         C:/workspace/work1/Calculadora/.project
Adding         C:/workspace/work1/Calculadora/.settings
Adding         C:/workspace/work1/Calculadora/.settings/org.eclipse.jdt.core.prefs
Adding         C:/workspace/work1/Calculadora/src
Adding         C:/workspace/work1/Calculadora/src/testes
Adding         C:/workspace/work1/Calculadora/src/testes/svn
Adding         C:/workspace/work1/Calculadora/src/testes/svn/Calculadora.java
Transmitting file data …

Voltando à perspectiva Java, podemos perceber que o projeto está no repositório, pela notação à frente do nome do projeto, e podemos ver ainda qual usuário realizou o “commit” da classe Calculadora:

Pois bem, temos o projeto versionado no trunk. Como dito anteriormente, no trunk deveriam ser mantidos os códigos estáveis de um projeto. Para a realização de testes ou implementações que podem ser instáveis por um longo período, pode ser preferível criar um branch. No branch colocamos uma cópia do projeto que está no trunk, mas sem perder a referência. A equipe que trabalha no código do branch pode ainda manter uma sincronização com o trunk, trazendo deste as atualizações pertinentes, evitando que o código do branch fique desatualizado com relação ao trunk,  mas evitando que o código instável do branch influencie negativamente o código do trunk.
Para nosso tutorial, vamos supor que a implementação dos métodos da classe Calculadora serão  realizados no branch. No dia-a-dia tal implementação poderia ser feita no trunk, mas como nosso objetivo é aprender a criar branches, vamos supor que esta seja uma necessidade real.

Criando um Branch

Para criar um branch vá até a perspectiva do repositório SVN e, expandindo os diretórios, clique com o botão auxiliar do mouse sobre o projeto Calculadora, e no menu de contexto escolha a opção “Branch/Tag…”:

Na tela seguinte precisamos informar o diretório destino, no repositório, para o projeto. Como estamos criando um branch, o diretório destino será o branch. Porém, como podem existir vários branches de um mesmo projeto ao mesmo tempo, é conveniente criar um subdiretório com um nome que identifique o objetivo do branch.
Para fazer isso, selecione o branch através do botão “Select…”:

Ao voltar para a tela anterior, dê um nome ao subdiretório. Isso pode ser feito adicionando o nome à URL resultante do processo anterior. Para este exemplo, o diretório será “metodos_calculadora”. Eis a URL final:

https://votti/svn/calculadora/branches/metodos_calculadora

Lembre-se de marcar na tela a opção que permite que o diretório adicional seja criado (Create any intermediate folders that are missing.):

Na próxima tela podemos escolhar qual revisão queremos para o branch. No nosso caso vamos escolhar a opção HEAD, que indica que será enviada para o branch a versão mais atual existente no trunk. Porém, se necessário, uma versão específica poderá ser selecionada:

Na tela seguinte adicione um comentário pertinente, e faça a confirmação:

Se formos agora à perspectiva do repositório, poderemos ver que o conteúdo do trunk foi copiado para o branch configurado:

Veja que até o momento copiamos o conteúdo do projeto para o trunk e a partir do trunk criamos um branch para realizar a implementação, mas no nosso workspace temos apenas o projeto que está “ligado” ao trunk. Isso significa que se fizermos qualquer modificação, e fizermos um commit, estas modificações serão enviadas ao trunk.

Realizando modificações no branch

Se quisermos realizar modificações no branch criado, teremos que configurar um projeto que esteja “ligado” a ele.
Para fazer isso, vamos reiniciar o Eclipse e como workspace vamos selecionar o diretório work2, criado no início do tutorial. Assim que tiver aberto o Eclipse no novo workspace faça as configurações necessárias (configuração de uma JDK padrão, por exemplo).
Abra a perspectiva do SVN e, assim como no início do tutorial, insira uma novo local de repositório, informando a URL do repositório de exemplo.
Ao terminar o processo de configuração do repositório, abra o diretório branch. Clique com o botão auxiliar do mouse sobre o diretório “metodos_calculadora”, e no menu de contexto escolha a opção “Checkout…”:

Com isso vamos copiar o conteúdo do branch para o repositório, para trabalhar nele.
A próxima tela apresenta os dados para o projeto qe será criado:

Repare que o nome do projeto foi recuperado e dado como sugestão. Também há a possibilidade de escolher uma revisão específica, ou então a última (HEAD). Vamos manter as configurações sugeridas.
Na próxima tela será sugerido como workspace o workspace atual (work2). Mantenha o valor sugerido e prossiga, clicando em “Finish”.
Ao voltar para a perspectiva Java, é possível verificar que o projeto foi configurado, e que referencia o branch criado:

Agora vamos simular a situação real de ter que alterar o código em ambos os locais, trunk (work1) e branch (work2) e ter de mantê-los sincronizados.
Vamos começar codificando a classe Calculadora no workspace work2, que referencia o branch, no qual estamos:


public class Calculadora {

    public BigDecimal somar(BigDecimal param1, BigDecimal param2) throws Exception{
        return param1.add(param2);
    }

    public BigDecimal subtrair(BigDecimal param1, BigDecimal param2) throws Exception{
        return param1.subtract(param2);
    }

    public BigDecimal multiplicar(BigDecimal param1, BigDecimal param2) throws Exception{
        return param1.multiply(param2);
    }

    public BigDecimal dividir(BigDecimal param1, BigDecimal param2) throws Exception{
        return param1.divide(param2);
    }
}

Depois de codificar a classe, faça o commit das modificações no branch, descrevendo as modificações:

É extremamente importante realizar o commit de todas as alterações do workspace no brach/trunk antes de realizar qualquer operação de merge. Aliás, é uma exigência!
Vamos agora realizar uma modificação no projeto do trunk, para ver como recuperar esta modificação, depois, no branch. Mude de workspace, voltando para a workspace work1.
Você verá que, como esperado, os métodos na classe Calculadora do workspace work1 estão como antes, sem implementação.
Adicione então, à classe Calculadora, um método a mais:

public BigDecimal potencial(BigDecimal param1, BigDecimal param2) throws Exception{
return null;
}

Faça o commit da modificação, adicionando um comentário pertinente:

Retorne para o workspace work2.

Realizando um merge do trunk para o branch

Agora, no workspace work2, vamos realizar um merge para receber as atualizações do trunk, ou seja, o novo método adicionado à classe Calculadora.
Este procedimento deveria ser, como comentado anteriormente, uma prática realizada frequentemente para que o branch não fique muito tempo desatualizado em relação ao trunk. Isto minimiza problemas de conflito na hora de mover para o trunk as modificações do branch, procedimento este que realizaremos depois.

Selecione a classe Calculadora no projeto, e clicando com o botão auxiliar do mouse sobre ela escolha a opção de “Merge…” no menu “Team”:

A tela seguinte nos apresenta algumas opções para a operação de merge:

Para este tutorial as duas primeiras opções são as que interessam.
Para esta operação que realizaremos agora, mantenha selecionada a primeira opção. Esta opção é usada para capturar mudanças que foram realizadas no trunk ou outro branch, e trazê-las ao branch no qual estamos trabalhando. Confirme a operação, clicando em “Next”:

A próxima tela apresenta uma sugestão do artefato a partir do qual desejamos realizar o merge, ou seja, a classe Calculadora  presente no projeto do trunk. Neste ponto você poderia apontar para a mesma classe presente em algum outro branch, caso existisse, ou seja, não precisa ser necessariamente um merge a partir do trunk. Mas no nosso caso manteremos a sugestão, pois realmente precisamos realizar o merge com o trunk.
Na próxima tela são apresentadas opções para a operação. Basicamente, as sugestões marcadas por padrão são melhores, pois indicam que a cada problema encontrado, o usuário deverá ser consultado sobre o que fazer. Mantenha as sugestões apresentadas por padrão:

Ao confirmar, a seguinte tela é apresentada, indicando que uma atualização foi encontrada:

Após a conclusão da operação, você verá que o método adicionado na classe Calculadora do trunk apareceu na classe Calculadora do branch:

Vale lembrar que, apesar de termos realizado esta operação para apenas uma classe, ela pode ser realizada para vários artefatos ao mesmo tempo.
Agora que a classe foi modificada, faça o commit das alterações no branch, informando um comentário pertinente para a mudança:

Realizando um merge do branch para o trunk

Depois do trabalho realizado no branch, precisamos reintegrá-lo ao trunk, uma hora ou outra.
Muitas vezes o trabalho em um branch pode ser tão rápido quanto algumas horas, ou longo a ponto de levar vários dias ou meses. O tamanho do trabalho a ser realizado é que irá determinar isso.
Independente de quanto tempo ele leve, o fato de realizar periodicamente o merge do trunk com o branch, como feito anteriormente, deve minimizar os problemas de conflitos quando chegar a hora de trazer o branch para o trunk, uma vez que, idealmente, os conflitos existentes já terão sido resolvidos no momento em que se levou as mudanças do trunk para o branch.
Vamos agora trazer o que foi feito no branch para o trunk. Mude para o workspace work1.
Selecione a classe Calculadora no projeto, e clicando com o botão auxiliar do mouse sobre ela escolha a opção de “Merge…” no meu “Team”

Na tela que aparece em seguida, mantenha selecionada a segunda opção, que nos permitirá trazer as mudanças do branch para o trunk:

Na tela seguinte, na qual informamos a partir de onde será feito o merge, selecione a classe Calculadora do branch, como indicado:

Na próxima tela confirme as opções oferecidas como padrão, e ao finalizar a tela seguinte será apresentada, indicando que uma atualização será realizada:

Confirme clicando no botão “OK”. Ao término da operação as atualizações do branch serão aplicadas ao trunk.

Lidando com conflitos entre o trunk e o branch

Até agora realizamos o merge entre o trunk e o branch sem nenhum problema de conflito, mas isto está longe de ser a realidade do dia-a-dia de trabalho das equipes de desenvolvimento. Na verdade os conflitos são muito comuns. As boas práticas procuram melhorar a produtividade das equipes, manter a estabilidade dos repositórios (principalmente do trunk) e minizar os conflitos, mas eliminar os conflitos, principalmente em um ambiente onde muitos desenvolvedores atualizam os mesmos arquivos, é algo muito difícil. Por esta razão é bom se acostumar com estes conflitos, e se acostumar a resolvê-los também.
Para simular este cenário, vamos modificar o último método adicionado à classe Calculadora. Perceba que ele ainda não foi implementado, e que recebe dois parâmetros. Na verdade precisamos modificar um de seus parâmetros, e também precisamos implementá-lo.
Ainda no workspace work1 modifique o método como abaixo:

public BigDecimal potencial(BigDecimal param1, int param2) throws Exception{
return param1.pow(param2);
}

Realize o commit das modificações, e sim, coloque um comentário! (não é à toa que estou batendo nesta tecla):

Agora mude para o workspace work2 e também faça estas modificações no métod, exatamente como no trunk, realizando o commit ao final.
Depois de realizado o commit, faça um merge, trazendo do trunk as modificações para o branch.
Apesar de termos alterado o mesmo método em ambos os locais, nenhuma atualização é encontrada, uma vez que as modificações foram exatamente as mesmas, ou seja, nenhum conflito é encontrado.
Vamos agora simular um conflito real, quando o mesmo local é modificado, porém com valores diferentes.

Mude para o workspace work1 e altere o método dividir conforme apresentado abaixo, realizando o commit ao final:


public BigDecimal dividir(BigDecimal param1, BigDecimal param2) throws Exception{
return param1.divide(param2, BigDecimal.ROUND_UP);
}

Agora mude para o workspace work2 e altere o método dividir conforme apresentado abaixo, realizando o commit no final:

public BigDecimal dividir(BigDecimal param1, BigDecimal param2) throws Exception{
return param1.divide(param2, BigDecimal.ROUND_HALF_EVEN);
}

Agora tente novamente trazer do trunk para o branch as modificações na classe Calculadora e veja o resultado.

Desta vez há um conflito, pois o método foi modificado em mais de um local (repositório), e no mesmo ponto.
As opções apresentadas na tela que é apresentada são:

– Marcar como em conflito. Resolverei mais tarde.
– Resolver o conflito usando a minha versão do arquivo.
– Resolver o conflito usando a versão do arquivo que está chegando.
– Me deixe editar o arquivo com os marcadores de conflito inseridos.
– Abrir um editor gráfico de solução de conflitos.

Particularmente recomendo lidar o quando antes com os conflitos, portanto, escolha a última opção e confirme. O Editor apresenta os conflitos a serem resolvidos:

Suponhamos que nossa versão do branch está correta. Então feche o editor, e uma mensagem será apresentada, perguntando se o conflito foi resolvido:

Marque que o conflito foi resolvido (primeira opção) e confirme:

Na tela que indica que uma atualização será feita faça a confirmação. Depois realize o commit da classe Calculadora no branch.
Desta forma, cada implementação do método, no trunk e no branch, ficou de uma forma. Se quiséssemos deixá-las iguais, bastaria ter aceito a diferença vinda do trunk (caso esta fosse a correta) ou então, a partir do trunk, puxar as alterações do branch e aceitá-las.

 

Conclusão

Neste tutorial foram apresentados os passos necessários para se configurar um repositório SVN no Eclipse e para se adicionar um projeto ao repositório.
Foram apresentados também os passos necessários para se criar um branch, como realizar o merge entre o branch e o trunk (em ambas as direções) e como resolver conflitos de versionamento.
Com isso concluímos este tutorial!
Espero que tenha sido de bom proveito, e que com o que foi apresentado aqui você possa melhorar sua rotina de trabalho aplicando mais algumas boas práticas no seu dia-a-dia de utilização do SVN.

Anúncios

Autenticação de Usuários em uma Aplicação JSF (no JBoss)

Já faz algum tempo que estou devendo este tutorial a alguns dos meus ex-alunos do curso de pós-graduação da TNT de Campo Grande. Então, antes de tudo, ele representa o pagamento de uma dívida =). Peço desculpas pela demora, e espero que valha a pena a espera.

Vou mostrar com este tutorial como publicar uma aplicação JavaServer Faces no JBoss, e configurar a autenticação programática de usuários, baseada em banco de dados.

Apesar de ser apresentada a configuração básica de uma aplicação JSF baseada no Richfaces, o foco do tutorial não é explicar a fundo como usar este framework.

A intenção principal com o tutorial é mostrar como configurar a autenticação de usuários baseada em banco de dados, e utilizar recursos do Richfaces que tiram proveito da autenticação programática do container para ocultar determinados componentes. Ao final deixarei dicas de referências para saber mais sobre as tecnologias.

Vou utilizar como ambiente de desenvolvimento o Eclipse Galileo com o JBoss Tools. Há algum tempo eu publiquei um tutorial mostrando como configurar este ambiente.

A versão do JBoss que utilizarei é a 5.1.0. Há pouco publiquei um how-to mostrando como configurá-lo no Eclipse.

O termo JBOSS_HOME usado ao longo do tutorial refere-se ao diretório de instalação do JBoss.

Mão na Massa!

Considerando que seu Eclipse já está configurado de acordo com o tutorial citado acima, e que o JBoss já foi adicionado, vamos criar uma aplicação JavaServer Faces através da opção oferecida pelo JBoss Tools.

Configure a aplicação como nas próximas imagens:

Faça o download de uma versão estável do RichFaces no link http://www.jboss.org/richfaces/download/stable.html.

Para este tutorial utilizei a versão 3.3.3 (3.3.3.Final Binary (zip)).

Copie os jars do RichFaces para o diretório WEB-INF/lib do projeto que acabamos de criar, como mostra a imagem abaixo:

Agora vamos configurar o projeto para que dê suporte ao RichFaces. Edite o arquivo web.xml do projeto para que fique como apresentado abaixo:


<?xml version="1.0"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 <display-name>WebAut</display-name>

 <context-param>
 <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
 <param-value>server</param-value>
 </context-param>

 <context-param>
 <param-name>org.richfaces.SKIN</param-name>
 <param-value>blueSky</param-value>
 </context-param>

 <context-param>
 <param-name>org.richfaces.CONTROL_SKINNING</param-name>
 <param-value>enable</param-value>
 </context-param>

 <filter>
 <display-name>RichFaces Filter</display-name>
 <filter-name>richfaces</filter-name>
 <filter-class>org.ajax4jsf.Filter</filter-class>
 </filter>

 <filter-mapping>
 <filter-name>richfaces</filter-name>
 <servlet-name>Faces Servlet</servlet-name>
 <dispatcher>REQUEST</dispatcher>
 <dispatcher>FORWARD</dispatcher>
 <dispatcher>INCLUDE</dispatcher>
 </filter-mapping>

 <listener>
 <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
 </listener>

 <!-- Faces Servlet -->
 <servlet>
 <servlet-name>Faces Servlet</servlet-name>
 <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
 <load-on-startup>1</load-on-startup>
 </servlet>

 <!-- Faces Servlet Mapping -->
 <servlet-mapping>
 <servlet-name>Faces Servlet</servlet-name>
 <url-pattern>*.jsf</url-pattern>
 </servlet-mapping>

 <welcome-file-list>
 <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

Agora crie uma página chamada index.jsp na raiz do diretório web, como na imagem abaixo:

Edite a página como apresentado abaixo:


<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head></head>
<body>
<jsp:forward page="/paginas/inicial.jsf" />
</body>
</html>

Agora crie um diretório chamado “paginas” na raiz do diretório web, e dentro dele um arquivo chamado inicial.jsp, com o conteúdo abaixo:

<%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
 <f:view>
 <head>
 <title></title>
 </head>
 <body>
 <div style="width: 300px;">
 <h:form id="formExemplo">
 <rich:panel header="Painel Administrativo">
 Coisas que somente administradores podem ver!
 </rich:panel>
 <rich:spacer height="20"/>
 <rich:panel header="Painel Comum">
 Coisas que qualquer usuário pode ver!
 </rich:panel>
 </h:form>
 </div>
 </body>
 </f:view>
</html>

O projeto deve estar como na imagem abaixo:

Agora vamos testar o que fizemos até agora.

Exporte o projeto como um arquivo war, e salve-o no diretório deploy do servidor, conforme a imagem abaixo:

Inicialize o servidor através da opção de menu disponível no painel Servers:

Após iniciar o servidor, acesse o endereço da aplicação no navegador:

http://localhost:8080/WebAut/

A aplicação deverá ser apresentada, como na imagem a seguir:

Ok! Até agora o que fizemos foi criar uma aplicação simples de exemplo. Vamos agora criar uma base de dados para armazenar os dados do usuário. Para isso vou utilizar o MySQL 5.0.

Crie uma base de dados chamada “credencial” com a estrutura de tabelas como a apresentada abaixo:

create table users (
  user_name         varchar(15) not null primary key,
  user_pass         varchar(15) not null
);

create table user_roles (
  user_name         varchar(15) not null,
  role_name         varchar(15) not null,
  primary key (user_name, role_name)
);

Insira os seguintes dados de exemplo:


insert into users (user_name, user_pass) values ('chuck', 'norris');
insert into users (user_name, user_pass) values ('bond', 'james');

insert into user_roles (user_name, role_name) values ('chuck', 'MASTER');
insert into user_roles (user_name, role_name) values ('bond', 'COMMON');

Agora vamos criar os arquivos de configuração necessários no JBoss. Para isso, pare o servidor.

O primeiro dos arquivos é o de configuração de datasource.

Crie um arquivo chamado webaut-ds.xml dentro do diretório deploy do JBoss (JBOSS_HOME\server\default\deploy). É importante que ele tenha o sufixo -ds, como sugerido. O arquivo deve conter o seguinte conteúdo (alterando-se, é claro, os dados de conexão):


<datasources>
 <local-tx-datasource>
 <jndi-name>WebAutDS</jndi-name>
 <connection-url>jdbc:mysql://localhost:3306/credencial</connection-url>
 <driver-class>com.mysql.jdbc.Driver</driver-class>
 <user-name>root</user-name>
 <password>mysql</password>
 <metadata>
 <type-mapping>mySQL</type-mapping>
 </metadata>
 </local-tx-datasource>
</datasources>

Esta configuração é o datasource da aplicação. Ela poderia ser usada pela nossa aplicação para obter conexões com o banco de dados, mas no caso do nosso exemplo, ela será usada pelo módulo que fará a autenticação dos usuários.

A seguir, procure o arquivo login-config.xml,  dentro do diretório conf (JBOSS_HOME\server\default\deploy\conf), e adicione a ele o seguinte conteúdo:


<application-policy name = "WebAutRealm">
 <authentication>
 <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule"
 flag = "required">
 <module-option name = "dsJndiName">java:/WebAutDS</module-option>
 <module-option name = "principalsQuery">select user_pass from users where user_name=?</module-option>
 <module-option name = "rolesQuery">select role_name, 'Roles' from user_roles where user_name=?</module-option>
 </login-module>
 </authentication>
 </application-policy>

Esta configuração é a responsável por autenticar os usuários da aplicação. Ela utiliza o datasource que foi criado anteriormente para conseguir uma conexão com o banco, e realiza as consultas das credenciais dos usuários de acordo com as sql’s informadas.

Esta configuração será referenciada na aplicação, através de seu nome (WebAutRealm), nas configurações que faremos a seguir.

Antes de fazermos as alterações na aplicação, copie um driver jdbc para o MySQL no diretório lib (JBOSS_HOME\server\default\deploy\lib).

Vamos agora ajustar a aplicação!

Crie um arquivo chamado jboss-web.xml dentro do diretório WEB-INF, e configure seu conteúdo como mostrado a seguir:


<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/WebAutRealm</security-domain>
</jboss-web>

Repare que estamos usando o nome JNDI da configuração de login que fizemos há pouco.

Agora, adicione os seguintes conteúdos no arquivo web.xml (conteúdo é intercalado com a explicação do que cada trecho significa).

  • Aqui identifico quais recursos estão protegidos, e quais perfis têm acesso aos recursos. Neste caso estou informando que todo o conteúdo (/*) está seguro, e que somente os perfis MASTER e COMMON terão acesso:

<security-constraint>
 <web-resource-collection>
 <web-resource-name>Area Restrita</web-resource-name>
 <url-pattern>/*</url-pattern>
 <http-method>POST</http-method>
 <http-method>GET</http-method>
 </web-resource-collection>
 <auth-constraint>
 <role-name>MASTER</role-name>
 <role-name>COMMON</role-name>
 </auth-constraint>
 </security-constraint>
  • Aqui estou informando que o método de login será através de um formulário web:
 <!-- METODO DE LOGIN PARA VALIDACAO DE ACESSO -->
 <login-config>
 <auth-method>FORM</auth-method>
 <form-login-config>
 <form-login-page>/login.jsp</form-login-page>
 <form-error-page>/login.jsp</form-error-page>
 </form-login-config>
 </login-config>

  • Aqui estou informando quais os possíveis perfis de acesso:
<!-- ROLES -->
 <security-role>
 <role-name>MASTER</role-name>
 </security-role>
 <security-role>
 <role-name>COMMON</role-name>
 </security-role>

Agora crie um arquivo chamado login.jsp na base do diretório web, com o seguinte conteúdo:

<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 </head>
 <body>

 <form method="post" action="j_security_check">
 <fieldset>
 <legend>Login</legend>
 <p>
 <label for="form-login">Login:</label>
 <input type="text" name="j_username" id="form-login" />
 </p>
 <p>
 <label for="form-senha">Senha:</label>
 <input type="password" name="j_password" id="form-senha" />
 </p>
 <p>
 <input type="submit" value="Enviar" />
 </p>
 </fieldset>
 </form>
 </body>
</html>

Por fim, faça a seguinte alteração na página inicial.jsp:

...
<h:form id="formExemplo">
 <rich:panel header="Painel Administrativo"
 rendered="#{rich:isUserInRole('MASTER')}">
 Coisas que somente administradores podem ver!
 </rich:panel>
 <rich:spacer height="20"/>
 <rich:panel header="Painel Comum"
 rendered="#{rich:isUserInRole('MASTER, COMMON')}">
 Coisas que qualquer usuário pode ver!
 </rich:panel>
 </h:form>
...

A propriedade rendered na tag indica se a mesma deve ou não ser apresentada. A instrução rich:isUserInRole verifica se o usuário logado possui a autorização informada.
Em resumo, o componente somente será mostrado se o usuário possuir a permissão informada.

No momento a estrutura de arquivos de seu projeto deve estar como a da imagem a seguir:

Gere novamente um war da aplicação, e publique-a no diretório deploy (JBOSS_HOME\server\default\deploy). Apague os diretórios work e temp que estão no diretório default (JBOSS_HOME\server\default).

Inicie o servidor, e assim que ele estiver no ar acesse a aplicação. Uma tela de login deverá aparecer:

Para o Chuck Norris, como era de se esperar, os dois painéis aparecem:

Agora reinicie o servidor, e faça o login como James Bond:

Como era de se esperar, somente o painel comum é apresentado:

Obviamente que há muitas maneira de melhorar o exemplo, como colocar uma opção para logoff na página, adicionar mais propriedades nos arquivos de configuração, mas isso vai ficar como um exercício adicional para o leitor 😉

A seguir listo algumas referências para pesquisa, e ao final coloco um link para o download dos arquivos usados no post.

Referências:

http://java.sun.com/javaee/javaserverfaces/

http://www.jboss.org/richfaces

http://livedemo.exadel.com/richfaces-demo/index.jsp

http://community.jboss.org/wiki/DatabaseServerLoginModule

http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.2/doc/Server_Configuration_Guide/Using_JBoss_Login_Modules-DatabaseServerLoginModule.html

http://community.jboss.org/wiki/configdatasources

http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.2/doc/Server_Configuration_Guide/Connectors_on_JBoss-Configuring_JDBC_DataSources.html

http://docs.sun.com/app/docs/doc/819-3201/6n5eht3o4?a=view

http://www.cafesoft.com/products/cams/tomcat-security.html

Download dos arquivos do tutorial

Preparando um Ambiente de Desenvolvimento Java EE Baseado em Eclipse

Objetivo do Documento

Auxiliar desenvolvedores iniciantes em Java na configuração de um ambiente de desenvolvimento, baseado na IDE Eclipse e em ferramentas open-source, que permita o desenvolvimento de aplicações Java EE profissionais.

Motivação

A plataforma Java, ao contrário da maioria das plataformas e linguagens de programação disponíveis no mercado, apresenta uma infinidade de possibilidades para o desenvolvimento de aplicações. Esta grande variedade não é referente, apenas, à que tipos de aplicações podem ser desenvolvidas, mas também refere-se às tecnologias que podem ser usadas na composição de uma aplicação.

Além da vasta quantidade de tecnologias que fazem parte da especificação da plataforma, há ainda uma diversidade de tecnologias que são adequadas ao desenvolvimento em Java, embora não façam parte das especificações oficiais.

Para um desenvolvedor que pretende iniciar com o desenvolvimento em Java, escolher entre ferramentas de desenvolvimento e frameworks pode ser uma tarefa assustadora. Como reunir “coisas” como Eclipse, NetBeans, Struts, JavaServer Faces, Spring, AOP, JUnit, EJB, Servelts,  JBoss, RichFaces, Facelets, etc… em um ambiente, em uma aplicação? Essa é uma pergunta que certamente todo desenvolvedor iniciante se faz.

Pensando nesta dificuldade, ou necessidade (provavelmente as duas coisas) é que este pequeno tutorial foi desenvolvido. Não existe, porém, a pretensão de se explicar todo o universo de possibilidades de configurações de um ambiente de desenvolvimento, abrangendo todas as tecnologias possíveis. Vamos configurar um ambiente de desenvolvimento que permita a criação de aplicações Java EE baseadas nas principais tecnologias de mercado. A criação das aplicações ficará para uma outra ocasião, mas ter um ambiente pronto para isso já é meio caminho andado. Espero que este tutorial lhe seja útil!

As Ferramentas

O ambiente de desenvolvimento proposto é baseado na IDE Eclipse e em plug-ins gratuitos que permitem o desenvolvimento de aplicações com os principais frameworks do mercado. Abaixo são apresentada as descrições de tais plug-ins:


Plataforma Eclipse

Mais do que uma IDE, o Eclipse é uma plataforma que permite o desenvolvimento de aplicações em diversas linguagens. Esta plataforma apresenta em seu estado mais básico, os recursos mínimos para o desenvolvimento de aplicações. A instalação de plug-ins adicionais é que permite a personalização da ferramenta para uma determinada linguagem.

No site do projeto [1] é possível encontrar instaladores da ferramenta já configurada para diversos propósitos, inclusive para o desenvolvimento Java EE. No entanto, a configuração básica para o desenvolvimento Java EE pode ser melhorada com a configuração de plug-ins adicionais, habilitando a ferramenta para o desenvolvimento com os principais frameworks do mercado.

Durante este tutorial utilizarei a penúltima versão da ferramenta, o Eclipse 3.4 (Ganymede), ao invés de utilizar a última versão, o Eclipse 3.5 (Galileo). O motivo de utilizar a penúltima versão é devido ao fato de que, no momento em que escrevo este tutorial, ainda não há uma versão final (de produção) do plug-in do JBoss Tools para o Galileo, mas a liberação de uma versão final está próxima. De qualquer forma apresentarei uma explicação, ao final, de como reproduzir a configuração na última versão do Eclipse.


Eclipse Web Tools Platform (WTP)

O projeto Eclipse Web Tools Platform (WTP) estende a plataforma do Eclipse com ferramentas para o desenvolvimento de aplicações WEB e Java EE. Estão disponíveis editores visuais e de código para uma grande variedade de linguagens, wizards e aplicações pré-configuradas para simplificar o desenvolvimento, e ainda ferramentas e APIs para dar suporte à implantação, execução e teste de aplicações.


JBoss Tools

Plugin que apresenta um conjunto de ferramentas da JBoss especializados para o desenvolvimento de aplicações WEB e corporativas em Java, dentre as quais pode-se citar algumas:

  • Hibernate Tools (ferramentas para se trabalhar com o framework ORM Hibernate)
  • Seam Dev Tools (ferramentas para se trabalhar com framework Seam)
  • Visual Web Tools  (editor visual que permite trabalhar com qualquer tecnologia web, tais como JSF (suporte a Richfaces), Seam, Struts, JSP, HTML e outros)
  • JBoss Server Manager (ferramentas para gerenciamento do servidor de aplicações JBoss)
  • JSF Tools (ferramentas para desenvolvimento JSF)
  • Struts Tools (ferramentas para desenvolvimento Struts)

Citamos aqui apenas os recursos mais comuns no desenvolvimento WEB, mas muitos outros estão disponíveis, como ferramentas para desenvolvimento de aplicações baseadas em BPM (Business Process Management), Portais, Relatórios, BRMS (Business Rule Management System), etc. Para mais informações, inclusive de como criar projetos baseados em cada uma destas tecnologias, o site do projeto [3] pode ser consultado.


Subclipse

Plugin que permite a integração e gerenciamento de projetos em repositórios Subversion.


EclEmma Java Code Coverage

O EclEmma é uma ferramenta de verificação de cobertura de testes unitários. Com esta ferramenta é possível verificar a porcentagem de código que foi efetivamente testada pelos testes unitários desenvolvidos para a aplicação.

Existem hoje vários frameworks para testes unitários na plataforma Java, e o mais famoso deles é o JUnit, um projeto open-source que atualmente está em sua versão 4.8. A configuração do Eclipse para Java já dá suporte à criação de testes unitários com o JUnit, não sendo necessária a instalação de um plug-in para essa finalidade. Na página do projeto do JUnit [4] você poderá encontrar mais informações a respeito da criação de testes unitários.

Para mais informações sobre o EclEmma, o site do projeto [5] pode ser consultado.

Algumas boas ferramentas de modelagem e desenvolvimento, que não estão disponíveis para a plataforma Eclipse, também podem ser usadas no ambiente proposto, dentre elas eis duas sugestões:


Jude Community/ Astah Community

A ferramenta Jude Community é uma boa ferramenta de modelagem UML gratuita. Por ser uma versão Community possui algumas limitações. Há uma versão Professional disponível, mas os recursos presentes na versão Community podem suprir a necessidade de grande parte dos artefatos necessários no dia-a-dia.

A partir de 2010, a ferramenta será descontinuada, e a Change Vision, empresa responsável pelo Jude, recomenda desde já a utilização da ferramenta que substituirá o Jude, ferramenta esta batizada de Astah. Assim como o Jude, esta ferramenta possui versões Community e Professional. Dentre os recursos da ferramenta estão:

  • Suporte a UML 2.1
  • Diagramas de Classe, Caso de Uso, Sequência, Atividade, Comunicação, Máquina de Estado, Componentes, Implantação, Estrutura de Composição, Objetos e Pacotes.
  • Ajustes de alinhamento e tamanho dos diagramas
  • Impressão dos diagramas (com a marca d’água da ferramenta)
  • Exportação das imagens dos diagramas (com a marca d’água da ferramenta)

Mais informações podem ser encontradas nos sites [6] [7] das ferramentas.


IReports e JasperReports

Durante muito tempo o framework JasperReports foi a principal alternativa para a produção de relatórios para aplicações Java. Hoje é possível encontrar no mercado alternativas a este framework, como o BIRT [8], uma engine de relatórios baseada no Eclipse, inclusive com um editor visual já integrado. Ainda assim, é conveniente ter este ambiente de criação de relatórios devido à sua grande utilização, ainda hoje, por grande parte da comunidade.

O IReports [9] é uma ferramenta visual para a edição de relatórios baseados em JasperReports. Atualmente possui um plug-in para a IDE NetBeans, porém é possível realizar a instalação da ferramenta como uma aplicação a parte.

Instalação e Configuração


JDK

Para a instalação e configuração do ambiente precisamos ter, primeiramente, o Java Development Kit (JDK) instalado. Cada projeto pode ter um requisito diferente quando à versão da JDK, mas é adequado termos a última versão instalada. No momento em que escrevo este tutorial, a versão 6 update 17 é a mais recente. Vamos instalá-la.

Para obter o instalador do JDK acesse o site da Sun [10] e a área de downloads do Java para desenvolvedores. Inicialmente há um menu com as opções de downloads de várias tecnologias. Ao se clicar sobre a opção Java, um menu específico para a tecnologia é apresentado, onde é possível encontrar um link para os downloads para desenvolvedores, conforme imagens abaixo:


Na página que se abre, escolha a opção de download da versão sem bundles, conforme a imagem abaixo:

Na próxima tela escolha a versão mais adequada para seu ambiente e prossiga com o download:

Depois de feito o download, o processo de instalação do JDK é bem intuitivo, bastando seguir as dicas do instalador. Atente-se apenas para os locais de instalação do JDK e do JRE durante o processo de instalação, alterando-os caso julgue necessário:


Após a instalação, configure uma variável de ambiente chamada JAVA_HOME apontando para o diretório de instalação do JDK. O processo de criação da variável de ambiente é específico para cada sistema operacional. Adicione também o diretório “\bin” do diretório de instalação à variável de ambiente “path“.


Após a instalação do JDK, faça o download do Eclipse para a versão de seu sistema operacional, no site de downloads [11] do projeto.

Ao acessar a página de downloads, as versões disponíveis serão referentes a última versão (Galileo). Para encontrar as versões anteriores (nosso caso, para este tutorial) acesso a opção de versões mais antigas da ferramenta:



Ao selecionar a versão do Eclipse, (em destaque a verão mais recente do Ganymede) você será direcionado a uma página com os pacotes disponíveis. Escolha o pacote para Desenvolvedores Java EE. Caso tenha curiosidade de saber a diferença entre cada pacote, acesso o link em destaque na imagem abaixo:


Escolha a versão adequada para seu sistema operacional, nos links à direita.

Depois de fazer o download do Eclipse, o processo de instalação é bem simples. Basta descompactar o arquivo em um diretório de sua preferência. Para este tutorial, farei a instalação em C:\eclipse.

Depois de descompactar o pacote, execute o arquivo executável presente no diretório. A ferramenta será iniciada, e solicitará para que seja informado um workspace (diretório de trabalho). Informe um diretório de sua preferência.



Assim que o ambiente for inicializado por completo, vamos realizar a primeira configuração. Acesse o menu Window > Preferences e na janela que se abre escolha Java > Installed JREs:


Clique no botão “Add…” e na próxima janela escolha a opção Standard VM e prossiga:



Na tela seguinte selecione o diretório de instalação do JDK e finalize:



Ao retornar para a tela de preferências, remova a linha referente ao JRE instalado anteriormente, mantendo somente a linha referente à JDK adicionada:



Instalando os Plug-ins

Até agora realizamos a configuração básica do Eclipse IDE. Vamos a partir de agora, configurar os plug-ins das ferramentas mencionadas.

É importante dizer que para que este processo seja completado, é necessário ter acesso à internet, de preferência com uma boa banda, pois para a instalação dos plug-ins é necessário realizar o download dos mesmos durante a configuração.

Muitos plug-ins oferecem a opção de se fazer o download de arquivos para a posterior instalação offline. Para este tutorial, porém, adotaremos a outra opção, que é fazer a configuração através de “Sites”. Este tipo de instalação é feita através de URLs informadas ao Eclipse, através do qual são feitos os downloads dos plug-ins.

A seguir estão listados os plug-ins que iremos configurar, suas URLs e informações referentes às versões do Eclipse que são suportadas:


JBoss Tools 3.1 (Eclipse 3.5.0 – Galileo)
Site URL: http://download.jboss.org/jbosstools/updates/development/

JBoss Tools 2.1 (Eclipse 3.3.2 – Europa)
Site URL: http://download.jboss.org/jbosstools/updates/JBossTools-2.1.2.GA/

JBoss Tools 3.0 (Eclipse 3.4.2 – Ganymede)
Site URL: http://download.jboss.org/jbosstools/updates/stable/

Subclipse (Versões suportadas do Eclipse: 3.2,3.3,3.4,3.5)
Site URL: http://subclipse.tigris.org/update_1.6.x

EclEmma Java Code Coverage (Versões suportadas do Eclipse: 3.1, 3.2, 3.3, 3.4, 3.5)
Site URL: http://update.eclemma.org/


Antes de iniciarmos, apenas para efeito de comparação, acesse o menu File > New > Other… e veja as opções disponíveis neste momento.  Após instalarmos os plug-ins, volte a este mesmo painel para visualizar as mudanças:


Para iniciarmos a instalação dos plug-ins acesso o menu Help > Software Updates…

Na janela que se abre, há duas abas: Installed Software e Available Software. Na primeira aba estão listados os recursos já instalados na IDE. Clique na segunda aba, e em seguida no botão Add Site…

Vamos instalar primeiramente o plug-in do JBoss Tools para esta versão do Eclipse. Inclua no campo Location a seguinte URL: http://download.jboss.org/jbosstools/updates/stable/


Depois que a ferramenta identificar os recursos do plug-in que estão disponíveis, selecione os mesmos conforme a imagem abaixo:


Após isto clique no botão Install…

Nas próximas telas siga confirmando as operações e aceitando as licenças apresentadas. Aguarde o término da instalação, e ao final será solicitado que você reinicie o IDE.


Após a IDE ter sido reiniciada, repita o processo de instalação de plug-in para os plug-ins do Subclipse (http://subclipse.tigris.org/update_1.6.x) e do EclEmma (http://update.eclemma.org/):


Vale lembrar que o suporte ao WTP já está habilitado por padrão na configuração do Eclipse para desenvolvedores Java EE.

Após estes passos já teremos um ambiente com todos os plug-ins instalados. Acesse novamente o menu File > New > Other… e veja a quantidade de opções disponíveis agora:



A criação de um projeto de exemplo de utilização dos plug-ins está fora do escopo deste tutorial, mas  informações de como criar projetos específicos podem ser encontradas principalmente no site do JBoss Tools.


Instalando as Outras Ferramentas

Ainda falta instalar o IReports e o Astah Community. Vamos então seguir com estas instalações.

Para baixar o IReports acesse o site do projeto [9] e procure pela página de downloads. Muito provavelmente haverá uma opção de download já na página inicial. No momento de criação deste tutorial a última versão disponível é a 3.6.2. Faça o download da versão adequada para seu ambiente:


Após escolher a versão adequada, você poderá se cadastrar ou acessar diretamente o download. Caso queira se cadastrar, prossiga com o formulário indicado, caso contrário acesse a opção apresentada na imagem abaixo para acessar o download do arquivo:


Em instantes o download do arquivo deverá ser iniciado.

Assim que o download terminar, o processo de instalação é bem direto. Algumas opções de download disponibilizam um arquivo compactado, multiplataforma, e neste caso seria necessário apenas descompactá-lo. Para este tutorial baixei o instalador para o Windows, e para este caso o processo de instalação é guiado por um wizard.

Após a instalação você poderá executar a ferramenta, e navegar entre suas opções para conhecê-la.


Um exemplo de relatório está além do escopo deste tutorial, mas não é difícil encontrar exemplos nos sites da comunidade da ferramenta.

Agora vamos partir para a instalação da Astah Community, a ferramenta de modelagem UML. Acesse o site da ferramenta [6] , e procure pela opção de downloads.

Para efetuar o download será necessário se cadastrar no site:

Após realizar o processo de cadastro, será possível ter acesso às opções de download. Faça o download para seu ambiente.

Para este tutorial foi realizado o download do instalador para Windows. Assim como o instalador do IReport, o instalador do Astah é bem intuitivo, bastando apenas seguir as sugestões do wizard, alterando as configurações sugeridas quando necessário.

Após a instalação, execute a ferramenta e navegue em suas opções para conhecê-la.

Um exemplo de projeto UML está alem do escopo deste tutorial, mas com alguns minutos de uso, é possível, para aqueles que conhecem a UML, criar diagramas com a ferramenta.

Com isso encerramos o processo de configuração de um ambiente de desenvolvimento Java EE baseado em Eclipse e plug-ins open-source.

Instalação e Configuração no Eclipse Galileo

O processo de instalação e configuração utilizando o Eclipse Galileo é praticamente o mesmo. As únicas coisas que devem ser observadas são as versões dos plug-ins (somente no caso do JBoss Tools o URL Site é diferente) e as diferenças de telas no momento de informar o Eclipse Site, que no Eclipse Galileo é como a imagem a seguir apresenta:

Conclusões

Como dito inicialmente, o ambiente proposto não atende a todas as possíveis combinações de frameworks do mercado para desenvolvimento Java. Frameworks como Wicket ou Spring, por exemplo, não são contemplados por esta configuração.

O ambiente, no entanto, atende a maioria das necessidades de desenvolvimento para a plataforma Java EE, com frameworks como JavaServer Faces, Struts, Hibernate, entre vários outros presentes na configuração. Com um ambiente destes é possível criar aplicações profissionais em Java, tanto aplicações WEB quanto aplicações corporativas.

Para conhecer outros plug-ins disponíveis para a plataforma Eclipse, acesse a página de plug-ins do projeto [12]. Atente sempre para as versões suportadas pelos plug-ins, assim como para as licenças que regem a sua utilização.

Referências

[1] Plataforma Eclipse: http://www.eclipse.org

[2] Eclipse Web Tools Platform (WTP): http://www.eclipse.org/webtools/

[3] JBoss Tools: http://jboss.org/tools.html

[4] JUnit: http://www.junit.org/

[5] EclEmma: http://www.eclemma.org/

[6] Astah Community: http://astah.change-vision.com/en/product/astah-community.html

[7] Jude Community: http://jude.change-vision.com/jude-web/product/community.html

[8] BIRT Project: http://eclipse.org/birt/phoenix/

[9] IReports: http://jasperforge.org/projects/ireport

[10] Sun Microsystems: http://www.sun.com/

[11] Eclipse Downloads: http://eclipse.org/downloads/

[12] Plug-ins para o Eclipse: http://www.eclipseplugincentral.com/

Configurando um Datasource para o Microsoft SQL Server 2005 no WebSphere 6.1

por: Luiz Gustavo Stábile de Souza

versão PDF

Este tutorial tem o objetivo de ajudar na configuração de um datasource (origem de dados) para o SQL Server 2005 no WebSphere 6.1.
A idéia de criar este tutorial surgiu de uma necessidade pessoal, de encontrar um passo-a-passo na internet, que fosse específico para esta versão do banco no WebSphere 6.1.
Para a elaboração deste tutorial, usei uma versão trial do servidor, que pode ser encontrada no site da IBM, que consta das referências no final do artigo.
Ao longo do artigo, os termos datasource e origem de dados serão usados para indicar a mesma coisa.

Configurando as credenciais de usuário para a conexão com banco através da JNDI

O WebSphere armazena todas as informações de usuário e senha de forma centralizada e faz isso em uma área diferente do console administrativo. Uma vez que a conexão JNDI requer um nome de usuário e senha, teremos que configurá-los antecipadamente.
A partir do menu esquerdo, selecione a opção Segurança > Administração, Aplicativos e Infra-estrutura Seguros.
Na tela que aparece, expanda o item Java Authentication and Authorization Service à direita, e selecione o item Dados de autenticação J2C:

Dados de autenticação J2C

Dados de autenticação J2C

A próxima tela apresenta a lista de identidades já cadastradas. Selecione a opção para incluir uma nova identidade, e após o cadastro clique em Salvar, para confirmar a operação:

Tela de cadastro de identidades

Tela de cadastro de identidades

Configurando um Provedor JDBC

No WebSphere é preciso configurar um provedor para um driver JDBC que pretendemos usar para um datasource. Segundo a IBM: “O objeto provedor JDBC encapsula a classe de implementação específica do driver JDBC para o datasource definido, e o associa ao provedor.”
Para configurar o provedor JDBC, escolha o item Recursos no menu à esquerda do painel administrativo, e então o item Provedores de JDBC.
Na tela apresentada, escolha o escopo adequado para a criação do recurso, no combo box apresentado, e escolha a opção para cadastrar um novo provedor JDBC.
Você pode cadastrar um provedor JDBC para o driver já oferecido pelo servidor por padrão. Para isso escolha na lista de tipo de provedor a opção WebSphere embedded ConnectJDBC:

Cadastro de provedor JDBC

Cadastro de provedor JDBC

Resumo dos dados do provedor JDBC cadastrado

Resumo dos dados do provedor JDBC cadastrado

Criando o Datasource

Agora, voltando à lista de provedores JDBC, selecione o provedor que acabamos de configurar, clicando sobre o mesmo. Na tela que aparece selecione o link Origem de Dados, à direita. Na tela que aparece em seguida, escolha a opção para cadastrar um novo datasource. A tela de cadastro aparece parcialmente preenchida, com os dados do provedor de conexão:

Tela de cadastro do datasource (origem de dados)

Tela de cadastro do datasource (origem de dados)

Preencha o campo de nome JNDI e escolha o alias de autenticação a ser usado, de acordo com o que foi cadastrado anteriormente.

Na próxima tela serão solicitadas informações adicionais para a configuração do datasource:

Dados adicionais para o datasource

Dados adicionais para o datasource

Após a configuração e confirmação, você poderá realizar o teste da conexão do datasource:

Teste do datasource criado executado com sucesso

Teste do datasource criado executado com sucesso

Instâncias Nomeadas do SQL Server

O Microsoft SQL Server suporta múltiplas instâncias de um banco de dados executando em um mesmo servidor. Uma instância é identificada por um nome de instância.
Para se conectar a uma instância nomeada utilizando uma URL de conexão, use o seguinte formato de URL:

jdbc:sqlserver://server_name\\instance_name

Observação: a primeira barra invertida (\) em \\instance_name é um caractere de escape.

onde:

server_name é o endereço ip ou hostname do servidor.
instance_name é o nome da instância com a qual se deseja uma conexão no servidor.

Seguindo este padrão de nomenclatura, caso quiséssemos uma conexão com uma instância chamada  openserver2005 em um servidor chamado openserver, a URL de conexão seria a seguinte:

jdbc:sqlserver://openserver\\openserver2005

Para o caso da configuração de um datasource para um banco cuja instância é nomeada, colocaríamos o seguinte, na identificação do servidor:


openserver\openserver2005

Configuração para uma instância nomeada do banco de dados

Configuração para uma instância nomeada do banco de dados

Configurando um Driver JDBC alternativo

Muitas vezes, o driver JDBC fornecido por padrão pelo servidor de aplicações pode não ser adequado, sendo necessário utilizar uma outra versão, baixada à parte.
O download do driver JDBC para o SQL Server 2005 pode ser feito a partir da página de downloads da Microsoft, que consta nas referências no final do artigo.
Tendo o driver do SQL Server em mãos, acesse o console administrativo.
No meu esquerdo do console administrativo, escolha a opção Ambiente > Variáveis do WebSphere:

sqlserver_was_img9

Ao escolher esta opção, um painel se abrirá, onde é possível visualizar as variáveis de ambiente do WebSphere. Dentre as variáveis encontradas neste painel, procure pela variável WAS_INSTALL_ROOT . O valor dela indicará um diretório, no qual existe um diretório lib, e dentro deste um diretório chamado ext. É para este diretório (ext) que deverá ser copiado o driver JDBC do SQL Server.

Variáveis de ambiente do WebSphere

Variáveis de ambiente do WebSphere

No mesmo painel, crie uma variável de ambiente, digamos MSSQLSERVER2005_JDBC_DRIVER_PATH, para apontar para o diretório onde estará o driver JDBC do SQL Server:

Criação de variável de ambiente

Criação de variável de ambiente

Na próxima tela apesentada, confirme clicando em Salvar:

sqlserver_was_img12

Criando o Provedor JDBC

Agora precisamos criar um provedor JDBC para o driver JDBC alternativo.
No menu do console administrativo selecione Recursos > JDBC > Provedores de JDBC. Selecione a opção para cadastrar um novo provedor.
No campo Tipo do Banco de Dados informe “Definido pelo usuário”. Em Nome da Classe de Implementação informe o nome da classe que implementa o datasource,  fornecido pelo driver. No caso do driver que estou usando essa classe é “com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource”.

Criação de um provedor JDBC para o driver alternativo

Criação de um provedor JDBC para o driver alternativo

Na próxima tela, informe o caminho até o driver JDBC:

Configuração do caminho para o driver alternativo

Configuração do caminho para o driver alternativo

Após confirmar as configurações, salve as alterações realizadas.

Criando o datasource para o Driver JDBC alternativo

Agora, voltando à lista de provedores JDBC, selecione o provedor que acabamos de configurar, clicando sobre o mesmo. Na tela que aparece, caso necessário, modifique as informações do driver jdbc, como o nome do arquivo .jar ou a classe de implementação:

Propriedades do provedor JDBC

Propriedades do provedor JDBC

Depois das devidas alterações feitas (caso necessário), selecione o link Origem de Dados, à direita. Na tela que aparece, escolha a opção para cadastrar uma nova origem de dados. A tela de cadastro aparece parcialmente preenchida, com os dados do provedor de conexão:

Tela de cadastro do datasource (origem de dados)

Tela de cadastro do datasource (origem de dados)

Preencha o campo de nome da origem de dados, de nome JNDI e escolha o alias de autenticação a ser usado, de acordo com o que foi cadastrado anteriormente.
Na próxima tela, no campo Nome da Classe Auxiliar do Data Store  informe “com.ibm.websphere.rsadapter.GenericDataStoreHelper” (padrão):

Informações do banco de dados para o datasource

Informações do banco de dados para o datasource

Ainda precisamos configurar o nome do banco de dados e o nome do servidor. Na tela de propriedades do datasource, clique na opção Propriedades Personalizadas, localizada à direita:

Propriedades do datasource

Propriedades do datasource

Na tela que se abre, cadastre duas propriedades, conforme a imagem a seguir:

Propriedades adicionais para o datasource (referentes ao banco de dados)

Propriedades adicionais para o datasource (referentes ao banco de dados)

O número da porta é opcional, portanto não o configurei. Para ser mais exato, ao tentar configurar o número da porta, obtive um erro, que informava não ser possível encontrar a versão correta do servidor:

Erro ao testar o datasource quando se informa o número da porta

Erro ao testar o datasource quando se informa o número da porta

Mas ao remover a propriedade, o teste de conexão do datasource funcionou normalmente, com apenas um aviso no log do servidor:

[19/08/09 16:25:30:375 BRT] 0000002e DSConfigurati W   DSRA0174W: Aviso: GenericDataStoreHelper está sendo utilizado.

É isso! Espero que este tutorial seja útil para quem precisa executar esta tarefa, muitas vezes tediosa.

Referências

Named Instances
http://e-docs.bea.com/wls/docs81/jdbc_drivers/mssqlserver.html#1074583

Trial: IBM WebSphere Application Server
http://www.ibm.com/developerworks/downloads/ws/was/

Microsoft SQL Server JDBC Driver
http://msdn.microsoft.com/en-us/data/aa937724.aspx

Setting up a JNDI data source in WebSphere 6.0/6.1
http://www.enavigo.com/2007/05/14/setting-up-a-jndi-data-source-in-websphere-6061/

SQL SERVER 2005 Support in Websphere Application Server V6.0.1:
http://www.theserverside.com/discussions/thread.tss?thread_id=41808

Testes Unitários com JUnit

Versão PDF

Motivação

Apesar de já fazer um certo tempo que escrevi a primeira versão deste tutorial, o assunto de testes unitários, e a utilização do JUnit ainda são para alguns uma novidade.
Acredito que tanto a utilização de testes unitários quanto a utilização do JUnit deverão ser, cada vez mais, constantes no dia-a-dia dos profissionais.
Portanto, eis aqui a minha contribuição para quem está começando!

O que são Testes Unitários?

Falando de forma simples e direta, um teste unitário é um teste realizado para verificar a funcionalidade de um determinado trecho de código, verificar se ele realmente faz o que se propõe a fazer.
O objetivo de testes unitários não é testar toda a funcionalidade do sistema, ou a integração de várias partes do sistema de uma única vez, mas realizar testes isolados, testando blocos específicos do sistema, mais comumente os métodos das classes.
Para entender melhor esse conceito, vamos a exemplos práticos, primeiramente vendo como seria executar um teste sem a utilização de um framework de testes.

Testes sem o JUnit – Um exemplo prático

Para a realização dos exemplos deste tutorial, vou utilizar o IDE Netbeans. Aconselho que você também siga este tutorial executando os exemplos no Netbeans, pois alguns passos são específicos para este IDE, a exemplo da última parte. Mas saiba que a criação de testes unitários não é de forma alguma dependente de uma IDE, e você também poderá criar testes unitários para suas classes, por exemplo, no Eclipse. A escolha aqui é meramente “didática”.

Vamos começar criando um projeto Java simples com o nome de Calculadora. Neste projeto crie uma classe chamada Calculadora, e inclua nesta classe o código abaixo:

public class Calculadora{

        // atributo
        private int resultado = 0;

        // método somar
        public double somar( int n1, int n2 ){

            resultado = n1 + n2;
            return resultado;
        }

        // método subtrair
        public double subtrair( int n1, int n2 ){

            resultado = n1 - n2;
            return resultado;
        }        

        // método multiplicar
        public double multiplicar( int n1, int n2 ){

            resultado = n1 * n2;
            return resultado;
        }

        // método dividir
        public double dividir( int n1, int n2 ){

            resultado = n1 / n2;
            return resultado;
        }
}

Repare que, no método dividir, propositadamente retiramos o teste que verifica a possibilidade de uma divisão por zero.

Crie agora uma classe chamada PrincipalCalculadora, que utilizará a primeira classe:

import javax.swing.JOptionPane;

public class PrincipalCalculadora{

    public static void main( String args[] ){

        int x, y;
        String sX, sY;

        sX = JOptionPane.showInputDialog( null, "Digite o primeiro número:",
        "Primeiro número", JOptionPane.QUESTION_MESSAGE );

        x = Integer.parseInt( sX );

        sY = JOptionPane.showInputDialog( null, "Digite o segundo número:",
        "Segundo número", JOptionPane.QUESTION_MESSAGE );

        y = Integer.parseInt( sY );    

        // instanciação de um objeto da classe calculadora
        Calculadora calc = new Calculadora();

        JOptionPane.showMessageDialog(null, "somar: " +  calc.somar( x, y ) );
        JOptionPane.showMessageDialog(null, "subtrair: " +  calc.subtrair( x, y ) );
        JOptionPane.showMessageDialog(null, "multiplicar: " +  calc.multiplicar( x, y ) );
        JOptionPane.showMessageDialog(null, "dividir: " +  calc.dividir( x, y ) );    

        System.exit( 0 );
    }
}

junit_img_1

Vamos agora criar uma classe chamada TesteCalculadora para testar nossa classe Calculadora. Adicione a ela o seguinte trecho de código:

public class TesteCalculadora {

    public static void main(String[] a){

        Calculadora c = new Calculadora();

        int valorUm = 5;
        int valorDois = 5;

        double valorTotal = c.somar(valorUm, valorDois);

        if(valorTotal == 10){
            System.out.println("valor correto!");
        }
        else{
            System.out.println("valor errado!");
        }
    }

}

Clique sobre a classe TesteCalculadora com o botão direito e escolha a opção Executar Arquivo (Run File). A execução deve apresentar o resultado esperado, ou seja, o teste foi executado e o método se comportou como esperado:

junit_img_2

Nossa classe de teste é funcional, mas para a boa prática de programação, erros devem ser tratados com exceções (blocos try-catch), e não com avaliações condicionais. Criar testes desta maneira, com controle de exceções, além de poluir o código com blocos try-catch, é muito trabalhoso.
É nesse momento que se mostra útil a utilização de um framework de testes.

O Framework de Testes JUnit

Existem vários frameworks para testes unitários na plataforma Java, e o mais famoso deles é o JUnit, um projeto open-source que atualmente está em sua versão 4.5. Na página do projeto [1] você poderá encontrar mais informações, além de tutoriais.
O Netbeans 6 dá suporte à versão mais recente do JUnit, que incorpora o uso de anotações para facilitar a criação de testes. A integração do Netbeans com o JUnit nos permite criar classes de teste rapidamente.

Testes com JUnit – Um exemplo prático

Agora que já vimos como criar uma classe de teste sem o JUnit, e vimos alguns dos inconvenientes dessa abordagem, vamos criar uma classe de teste para a nossa classe Calculadora, utilizando os recursos de integração do Netbeans ao JUnit.
Clique com o botão direito do mouse sobre a classe Calculadora. No menu que aparece, escolha a opção Ferramentas > Criar testes JUnit (Tools > Create JUnit Tests). Na próxima janela que aparece, escolha a opção JUnit 4.x e clique em Select. A seguinte tela será apresentada:

junit_img_3

Nesta tela encontramos alguns recursos a serem configurados para a classe de teste, como a presença de métodos de inicialização e finalização de casos de testes, o nível de acesso dos métodos a serem testados, e a possibilidade de geração de comentários nos códigos. Repare também que é sugerido um nome para a classe de teste (CalculadoraTest). Podemos aceitar as opções sugeridas, clicando em OK. Após este procedimento, será criada a classe de teste no diretório Pacote de Testes (Test Packages):

junit_img_4

Além dos casos de teste simples, criados para cada método da classe Calculadora, foram criados outros 4 métodos que merecem comentário, são eles:

@BeforeClass
setUpClass()

Neste método devem ser colocados códigos que precisam ser executados antes da criação de um objeto da classe de teste, ou seja, um código do qual todos os métodos de teste podem tirar algum proveito. Pode ser a criação de uma conexão com o banco de dados, por exemplo, ou a leitura de um arquivo no sistema de arquivos.
A anotação que acompanha o método (@BeforeClass) pode ser adicionada a qualquer método, e nesse caso, todos os métodos que tiverem essa anotação serão executados na ordem em que aparecem declarados, e antes de qualquer caso de teste específico.

@AfterClass
tearDownClass()

Neste método deverão ser colocados códigos que precisam ser executados assim que todos os casos de teste tiverem sido executados. Tais códigos podem ser referentes a liberação de recursos adquiridos no método setUpClass(), como o fechamento de conexões com o banco de dados, ou à liberação de arquivos.
Assim como acontece com a anotação @BeforeClass, a anotação @AfterClass pode acompanhar qualquer método, e nesses casos todos os métodos serão executados para a liberação de recursos, na ordem em que aparecem declarados.

@Before
setUp()

O método setUp() pode ser utilizado para a inicialização de recursos antes da execução de cada método de teste. É o local ideal para obter e inicializar recursos que precisam ser reiniciados a cada teste.
Assim como as outras anotações, @Before pode ser adicionado a outros métodos.

@After
tearDown()

O método tearDown() é utilizado para a liberação de recursos ao final de cada método de teste. Estes recursos geralmente são os que foram obtidos no método setUp().
A anotação @After pode, assim como as demais, ser utilizada com outros métodos.

Agora vamos observar o método de teste criado para o método somar da classe Calculadora:

junit_img_5

A anotação @Test indica que este método de teste deve ser executado pelo framework. Quando não quisermos que um método de teste seja executado basta remover a anotação.
O que o método de teste faz é criar uma instância da classe Calculadora, e passar dois parâmetros para o método somar, verificando em seguida o resultado.
Como você pode observar, os valores iniciais das variáveis de teste não são nada otimizados, isto porque o framework não tem como conhecer as regras pertinentes ao nosso sistema, colocando então valores padrões. Cabe a nós a “lapidação” do caso de teste, passando valores mais adequados para o teste.
Repare também que a verificação do resultado é feita através de um método de asserção, chamado assertEquals(), que recebe dois parâmetros. O que este método faz é verificar a igualdade entre os parâmetros, e caso estes valores não sejam iguais, é retornada uma exceção, e o teste falha. Veja que não precisamos utilizar blocos condicionais, nem estruturas de controle de exceções, o que torna nosso código de teste mais limpo e direto.
O comando fail() força a falha do teste, e no geral deve ser removido (a não ser que se queira realmente forçar a falha de um teste).
Vamos refinar nosso caso de teste:

junit_img_6

Repare que melhoramos os valores das variáveis utilizadas no teste do método testSomar, e no método assertEquals, passamos um terceiro parâmetro, que especifica uma variação decimal aceitável para a comparação de números de ponto-flutuante.
Para rodar o teste, clique com o botão direito do mouse sobre o ícone que representa a classe CalculadoraTest, na árvore de recursos de projeto (à esquerda), e escolha a opção Executar Arquivo (Run File). Você verá que todos os métodos de teste anotados com @Test serão executados, e como retiramos o comando fail somente da classe de teste testSomar, todos os outros testes falharão:

junit_img_7

Vamos ajustar os demais testes. O código final da classe de testes, até agora, deverá ser o seguinte:

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class CalculadoraTest {

    public CalculadoraTest() {
    }

    @BeforeClass
    public static void setUpClass() throws Exception {
    }

    @AfterClass
    public static void tearDownClass() throws Exception {
    }

    @Before
    public void setUp() {
    }

    @After
    public void tearDown() {
    }

    /**
     * Test of somar method, of class Calculadora.
     */
    @Test
    public void testSomar() {
        System.out.println("somar");
        int n1 = 5;
        int n2 = 5;
        Calculadora instance = new Calculadora();
        double expResult = 10.0;
        double result = instance.somar(n1, n2);
        assertEquals(expResult, result, 0);
    }

    /**
     * Test of subtrair method, of class Calculadora.
     */
    @Test
    public void testSubtrair() {
        System.out.println("subtrair");
        int n1 = 5;
        int n2 = 3;
        Calculadora instance = new Calculadora();
        double expResult = 2;
        double result = instance.subtrair(n1, n2);
        assertEquals(expResult, result, 0);
    }

    /**
     * Test of multiplicar method, of class Calculadora.
     */
    @Test
    public void testMultiplicar() {
        System.out.println("multiplicar");
        int n1 = 2;
        int n2 = 3;
        Calculadora instance = new Calculadora();
        double expResult = 6;
        double result = instance.multiplicar(n1, n2);
        assertEquals(expResult, result, 0);
    }

    /**
     * Test of dividir method, of class Calculadora.
     */
    @Test
    public void testDividir() {
        System.out.println("dividir");
        int n1 = 5;
        int n2 = 2;
        Calculadora instance = new Calculadora();
        double expResult = 2.5;
        double result = instance.dividir(n1, n2);
        assertEquals(expResult, result, 0);
    }

}

Substitua o código da classe de testes pelo código acima e execute o teste. O último teste, do método dividir falha. Vamos analisar o que aconteceu:

junit_img_8

O método de teste para o método dividir determina que a divisão de 5 por 2 deve resultar em 2.5. Isto está correto, e devemos então verificar o método dividir na nossa classe Calculadora, para encontrar a causa do problema.
Ao analisar o código do método dividir, da classe Calculadora, vemos que ele retorna como resultado o valor da divisão direta de dois números inteiros. Qualquer operação que envolva um número inteiro retornará um número inteiro, e quando fazemos a divisão de 5 por 2, ele retorna somente o valor inteiro 2, é esse o problema de nosso método. Vamos alterar o código de nosso método dividir, na classe Calculadora para o seguinte:

public double dividir( int n1, int n2 ){

    double d1 = Double.valueOf(n1);
    double d2 = Double.valueOf(n2);

    double r = d1 / d2;
    return r;
}

Agora, sem nenhuma alteração em nossa classe de teste, execute os testes. Agora todos os testes passam:

junit_img_9

Conforme vimos até agora, temos um caso de teste para cada método da nossa classe Calculadora. Isso é bom, mas não o bastante. Precisamos criar casos de teste para outras situações. Por exemplo, vamos testar o comportamento de nosso método multiplicar para o caso de parâmetros com valores negativos:

junit_img_10

Como se pode ver, passamos dois valores negativos ao método multiplicar, e o mesmo nos retornou um valor positivo.

Consulte a API do framework JUnit na web e conheça os diversos métodos assertivos disponíveis. Há métodos assertivos para comparação de Strings, para garantir que um valor não é nulo, etc…

Gerando Relatórios de Teste com JunitReport

Podemos aplicar algumas modificações em um script de configuração do Netbeans para que sejam gerados relatórios dos testes realizados.
Na tela de arquivos (Files) procure pelo arquivo build-imp.xml. Trata-se de um arquivo de script ant, que o Netbeans utiliza para algumas configurações internas:

junit_img_11

Clique duas vezes sobre o arquivo, para abri-lo no editor. Procure pela tag de fechamento </junit> e abaixo dela digite o seguinte código:

<junitreport todir=”${test.src.dir}”>
<fileset dir=”${build.test.results.dir}”>
<include name=”TEST-*.xml”/>
</fileset>
<report todir=”${test.src.dir}/html”/>
</junitreport>

junit_img_12

Depois dessa modificação, clique sobre o projeto com o botão direito e escolha a opção Teste. Você perceberá que será criada uma estrutura a mais junto com as classes de teste. Esta estrutura passa a armazenar as páginas que apresentam os testes executados.

junit_img_13

Clique com o botão direito do mouse sobre o arquivo index.html e escolha a opção View:

junit_img_14

É isso pessoal! Espero que este pequeno tutorial seja útil.

Até o próximo!

Referências

[1] – http://www.junit.org/

Jogo da Memória em Java

Neste post irei publicar os fontes de um jogo que fiz há um tempo atrás, quando ainda estava na faculdade.

Este jogo foi um pequeno desafio pessoal que me propus quando comecei a aprender Java e que depois acabou virando um trabalho de sala.

Na faculdade havíamos feito um jogo da memória em C, para console.Nesta época eu estava começando a aprender Java, e estava encantado com a possibilidade de criar interfaces gráficas sem a necessidade de uma IDE. Por esta razão, nenhuma IDE será necessária para criar este jogo.

Na ocasião eu utilizei o JCreator como editor de código, mas você poderá digitar as classes no bloco de notas e compilar no console se preferir, funciona também 😉

Basicamente é um jogo que utiliza pacotes do core de Java, e não é necessária a utilização de nenhuma biblioteca adicional às já encontradas na JRE para seu funcionamento.

São quatro classes e algumas imagens, que você deverá dispor da seguinte forma:

– Jogador.java
– MyButton.java
– TelaUm.java
– TelaJogo.java
– – imagens (diretório)
– – – duke (diretório)
– – – tux (diretório)
– – – jogo (diretório)

As imagens usadas, mais especificamente dos diretórios tux e duke, foram encontradas na internet. As demais, do diretório jogo, foram encontradas também na internet, e algumas são montagens que fiz.

Cada diretório de imagem, com exceção do diretório “jogo”, representam os possíveis jogos presentes para se escolher. Quando eu fiz esse jogo pela primeira vez, ele tinha 20 diretórios, ou seja, era possível escolher vários desenhos diferentes para os jogos.

Bom, depois de tanto falar, vamos aos fontes. Uma vez criados os arquivos .java citados acima, copie os conteúdos das classes abaixo para seus devidos arquivos:

Jogador.java


public class Jogador{

    private String nome;
    private int pontos;

    public Jogador(String nomeJogador){

        nome = (nomeJogador.equals("")? "Jogador sem nome...coitado! ": nomeJogador );
        pontos = 0;
    }

    public String obterNome(){

        return nome;
    }

    public int obterPontos(){

        return pontos;
    }

    public void incrementarPontos(){

        pontos += 5;
    }

    public void decrementarPontos(){

        pontos--;
    }

} // fim da classe pessoa

MyButton.java


import javax.swing.*;

public class MyButton extends JButton{

    private Icon imagemPadrao;
    private Icon imagemBotao;

    public MyButton(Icon imagemPadrao, Icon imagemBotao){

        super();

        this.imagemBotao = imagemBotao;
        this.imagemPadrao = imagemPadrao;

        setImagemPadrao();
    }

    public void setImagemPadrao(){

        this.setIcon(imagemPadrao);
    }

    public void setImagemBotao(){

        this.setIcon(imagemBotao);
    }    

    public Icon getImagemBotao(){

        return this.imagemBotao;
    }
}

TelaUm.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TelaUm extends JFrame implements ActionListener, ItemListener{    

    private Container container;
    private JButton botaoSair, botaoIniciarJogo;
    private JPanel painelBotoes, painelDados, painelCabecalho;
    private JPanel painelCentro, painelRodape, painelImagem;
    private Icon imagemPainel, imagemRodape, imagemJogar, titulo;
    private Icon imagemJogarRoll, imagemSair, imagemSairRoll;
    private JLabel labelImagemPainel, nomeJogador, temaJogada;
    private JLabel texto1, desenvolvimento, autor;
    private GridLayout gridBotoes, gridDados, gridCabecalho, gridRodape;
    private JComboBox temasJogada;
    private int indiceTema = 1;
    private Jogador objJogador;
    private TelaJogo objTelaJogo;
    private JTextField fieldNomeJogador;
    private Font fonte18, fonte10;
    private JTextArea telaSaida = new JTextArea();
    private boolean jogoFacil = false;
    private ButtonGroup dificuldadeGroup;
    private JMenu menuDificuldade;
    private JMenuBar barraMenu;
    private JRadioButtonMenuItem facil, dificil;
    private JLabel nivelDificuldade;

    String nomeObjJogador;

    // os temas para os jogos
    private String nomesTemas[] =
    { "Duke", "Tux"};    

    // índices dos temas para os jogos
    private int indiceTemas[] =
    { 1, 2 };    

    public TelaUm(){                   

        super( "Bem-vindo ao Jogo da Memória!" );                

        // adiciona um listener à janela
        addWindowListener(            

            // classe interna anônima
            new WindowAdapter(){

                // trata o evento de quando o usuário fecha a janela
                public void windowClosing( WindowEvent windowEvent ){

                    saidaPrograma();
                }
            }// fim da classe interna anônima    

        );

        nivelDificuldade = new JLabel("Nível de dificuldade: DIFÍCIL");
        nivelDificuldade.setForeground(Color.red);

        // gerenciadores de layout
        gridBotoes = new GridLayout( 1, 2, 5, 5 );
        gridDados = new GridLayout( 6, 1, 10, 10 );
        gridCabecalho = new GridLayout( 1, 1, 10, 10 );
        gridRodape = new GridLayout( 1, 1, 10, 10 );

        // painel da imagem (esquerda)
        imagemPainel = new ImageIcon(getClass().getResource("/imagens/jogo/painel.gif"));
        labelImagemPainel = new JLabel( imagemPainel );
        painelImagem = new JPanel();
        painelImagem.add( labelImagemPainel );
        painelImagem.setBackground( Color.white );

        fonte10 = new Font( "Verdana", Font.ITALIC, 10 );

        // imagens dos botões
        imagemJogar = new ImageIcon(getClass().getResource("/imagens/jogo/jogar.gif"));
        imagemJogarRoll = new ImageIcon(getClass().getResource("/imagens/jogo/jogarRoll.gif")); // imagem Rollover
        imagemSair = new ImageIcon(getClass().getResource("/imagens/jogo/sair.gif"));
        imagemSairRoll = new ImageIcon(getClass().getResource("/imagens/jogo/sairRoll.gif")); // imagem Rollover        

        // botão Iniciar Jogo
        botaoIniciarJogo = new JButton( "Iniciar Jogo", imagemJogar );
        botaoIniciarJogo.setRolloverIcon( imagemJogarRoll );
        botaoIniciarJogo.addActionListener( this );
        botaoIniciarJogo.setSize( 250, 15 );
        botaoIniciarJogo.setHorizontalAlignment( SwingConstants.LEFT );

        // botão Sair
        botaoSair = new JButton( "Abandonar o Jogo", imagemSair );
        botaoSair.setRolloverIcon( imagemSairRoll );
        botaoSair.addActionListener( this );
        botaoSair.setSize( 250, 15 );
        botaoSair.setHorizontalAlignment( SwingConstants.RIGHT );
        botaoSair.setHorizontalTextPosition( SwingConstants.LEFT );

        // painel de botões (inferior)
        painelBotoes = new JPanel();
        painelBotoes.setBackground( Color.lightGray );
        painelBotoes.setLayout( gridBotoes );
        painelBotoes.add( botaoSair );
        painelBotoes.add( botaoIniciarJogo );        

        //cabeçalho
        titulo = new ImageIcon(getClass().getResource("/imagens/jogo/titulo.gif"));
        texto1 = new JLabel( titulo );
        texto1.setHorizontalAlignment( SwingConstants.CENTER );        

        // painel de cabeçalho
        painelCabecalho = new JPanel();
        painelCabecalho.setLayout( gridCabecalho );
        painelCabecalho.setBackground( Color.lightGray );
        painelCabecalho.add( texto1 );

        // label do nome do jogador
        nomeJogador = new JLabel("Informe seu nome:");
        nomeJogador.setHorizontalAlignment( SwingConstants.LEFT );

        // campo do nome do jogador
        fieldNomeJogador = new JTextField( 15 );
        fieldNomeJogador.setHorizontalAlignment( SwingConstants.LEFT );

        // label do tema da jogada
        temaJogada = new JLabel( "Escolha o tema:" );
        temaJogada.setHorizontalAlignment( SwingConstants.LEFT );

        // ComboBox do tema da jogada
        temasJogada = new JComboBox( nomesTemas );
        temasJogada.addItemListener( this );
        temasJogada.setMaximumRowCount( 4 );

        // configuração do painel central
        painelCentro = new JPanel();
        painelCentro.setLayout( gridDados );
        painelCentro.setBackground( Color.lightGray );
        painelCentro.add( temaJogada );
        painelCentro.add( temasJogada );
        painelCentro.add( nomeJogador );
        painelCentro.add( fieldNomeJogador );
        //painelCentro.add( som );
        painelCentro.add(nivelDificuldade);

        menuDificuldade = new JMenu("Nível de Dificuldade");
        menuDificuldade.setBackground( Color.lightGray );

        dificuldadeGroup = new ButtonGroup();

        dificil = new JRadioButtonMenuItem("Difícil (6X6)");
        dificil.setSelected(true);

        dificil.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent evt){
                jogoFacil = false;
                nivelDificuldade.setText("Nível de dificuldade: DIFÍCIL");
                nivelDificuldade.setForeground(Color.red);
            }
        });

        facil = new JRadioButtonMenuItem("Fácil (4X4)");

        facil.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent evt){
                jogoFacil = true;
                nivelDificuldade.setText("Nível de dificuldade: FÁCIL");
                nivelDificuldade.setForeground(Color.blue);
            }
        });

        dificuldadeGroup.add(dificil);
        dificuldadeGroup.add(facil);

        menuDificuldade.add(dificil);
        menuDificuldade.add(facil);

        barraMenu = new JMenuBar();
        setJMenuBar(barraMenu);
        barraMenu.setBackground( Color.lightGray );

        barraMenu.add(menuDificuldade);        

        // imagem do rodapé
        imagemRodape = new ImageIcon(getClass().getResource("/imagens/jogo/java.gif"));

        // texto do rodapé
        desenvolvimento = new JLabel(
        "Desenvolvido em Java",
        imagemRodape, SwingConstants.CENTER );
        desenvolvimento.setFont( fonte10 );        

        // configuração do painel de rodapé
        painelRodape = new JPanel();
        painelRodape.setLayout( gridRodape );
        painelRodape.setBackground( Color.white );
        painelRodape.add( desenvolvimento );        

        // configuração do painel de dados (que possui o painel de cabeçalho,
        // o painel central e o painel de botões
        painelDados = new JPanel();
        painelDados.setLayout( new BorderLayout( 10, 10 ));
        painelDados.setBackground( Color.lightGray );
        painelDados.add( painelCabecalho, BorderLayout.NORTH);
        painelDados.add( painelCentro );
        painelDados.add( painelBotoes, BorderLayout.SOUTH );        

        // configuração do container (que tem toda essa bagunça aí!!! =)
        container = getContentPane();
        container.setLayout( new BorderLayout( 5, 5 ) );
        container.setBackground( Color.lightGray );
        container.add( painelRodape, BorderLayout.SOUTH );
        container.add( painelImagem, BorderLayout.WEST );
        container.add( painelDados );        

        setSize( 550, 380 );
        setVisible( true );
        setResizable( false );

    }

    private void saidaPrograma(){

        telaSaida.setText(

            "tJogo da Memória - 2005nn"+
            "tDesenvolvido por: Luiz Gustavo Stábile de Souzan"+
            "tGraduando em Análise de Sistemas pela "+
            "Faculdade de Ciências e Tecnologia de Birigui  n"+
            "tContato: luizgustavoss@gmail.comnn"+
            "tLinguagem de Desenvolvimento: Java (TM) 2 Standard Edition nn"+
            "tRequisitos: J2RE - Java(TM) 2 Runtime Environmentnn"+
            "tEste jogo foi desenvolvido com fins educacionais, portanto sem nenhum tipo de garantia.nn"

        );

        JOptionPane.showMessageDialog(null, telaSaida,
        "Informações sobre o Jogo", JOptionPane.PLAIN_MESSAGE);
        System.exit( 0 );
    }

    public void actionPerformed( ActionEvent event ){

            // se o evento for do botão sair
            if ( event.getSource() == botaoSair ){

                saidaPrograma();

            }

            // senão é do botão iniciar jogo
            else if ( event.getSource() == botaoIniciarJogo ){                

                nomeObjJogador = fieldNomeJogador.getText();
                objJogador = new Jogador( nomeObjJogador );

                if(jogoFacil)
                    objTelaJogo = new TelaJogo( objJogador, indiceTema, 4 );
                else
                    objTelaJogo = new TelaJogo( objJogador, indiceTema, 6 );
            }
    }

    public void itemStateChanged( ItemEvent event ){

        if ( event.getSource() == temasJogada ){

            if ( event.getStateChange() == ItemEvent.SELECTED )

                indiceTema = indiceTemas[ temasJogada.getSelectedIndex() ];
        }
    }

    public static void main( String args[] ){

        TelaUm application = new TelaUm();

        application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        application.setLocationRelativeTo( null );
    }

} // fim da classe TelaUm

TelaJogo.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TelaJogo extends JFrame implements ActionListener{

    /**
     * Construtor da classe
     * @Param jog - Objeto jogador, com os dados do jogador
     * @Param codJog - O tema escolhido
     * @Param fatorJogo - Inteiro que determina se o jogo é 4X4 ou 6X6
     */    

         //ClassLoader cl = this.getClass().getClassLoader();
         public TelaJogo( Jogador jog, int codJog, int fatorJogo ){        

        super( "Jogo da Memória" );    

        setLocation( 200, 10 );            

        addWindowListener(new WindowAdapter(){

                public void windowClosing( WindowEvent windowEvent ){

                    mostrarResumoJogo();
                }
        });        

        fator = fatorJogo;

        objJogadorJogada = jog;
        temaJogada( codJog );
        preparaArrayImagens();
        container = getContentPane();

        labelPontosJogador = new JLabel( "Jogador: " + objJogadorJogada.obterNome() + " >> Pontos: " );

        pontosJogador = new JLabel( ""+objJogadorJogada.obterPontos() );

        imagemSair = new ImageIcon(getClass().getResource("/imagens/jogo/sair.gif"));
        imagemSairRoll = new ImageIcon(getClass().getResource("/imagens/jogo/sairRoll.gif"));        

        // configura o botão sair
        botaoSair = new JButton(" Sair do Jogo ", imagemSair);
        botaoSair.setRolloverIcon( imagemSairRoll );
        botaoSair.addActionListener( this );        

        painelImagens = new JPanel();
        painelPontos = new JPanel();        

        // configurando o painel superior
        painelPontos.setBackground( Color.white );
        painelPontos.add( labelPontosJogador );
        painelPontos.add( pontosJogador );        

        grid = new GridLayout( fator, fator, 5, 5 );

        painelImagens.setLayout( grid );                

        imagemPadrao = new ImageIcon(getClass().getResource("/imagens/jogo/standard.gif"));

        posicoesFiguras = new MyButton[( fator * fator )];                

        for ( int cont = 0; cont < ( fator * fator ); cont++ ){

            posicoesFiguras[ cont ] = new MyButton(imagemPadrao, imagens[cont]);
            posicoesFiguras[ cont ].addActionListener( this );
            painelImagens.add( posicoesFiguras[ cont ] );
        }

        container.add( painelPontos, BorderLayout.NORTH);
        container.add( botaoSair, BorderLayout.SOUTH );
        container.add( painelImagens );

        double d_largura = (fator == 4?82.5:80);

        d_largura *= fator;

        int i_largura = (int) d_largura;

        setSize( i_largura, i_largura+70 );
        setVisible( true );
        setResizable( false );
    }

    /**
     * Prepara o Array de Imagens
     *
     */
    private void preparaArrayImagens(){

        int posicaoNoArray, x, y;

        this.imagens8 = new Icon[(fator * fator)/2];

        for ( x = 0; x < ((fator*fator)/2); x++ ){

            this.imagens8[ x ] = new ImageIcon(getClass().getResource(tema + ( x + 1 ) + ".GIF"));
        }

        this.imagens = new Icon[ (fator*fator) ];

        for ( x = 0; x < 2; x++ ){

            for ( y = 0; y < ((fator*fator)/2); y++ ){

                do{
                    posicaoNoArray = ( int ) ( Math.random() * (fator*fator) );        

                }while( this.imagens[ posicaoNoArray ] != null );

                this.imagens[ posicaoNoArray ] = imagens8[ y ];

            }
        }
    }

    /**
     * Descobre o tema escolhido
     * @Param codTema - Código do tema escolhido
     */
    private void temaJogada( int codTema ){

        switch( codTema ){

                case 1: this.tema = "/imagens/duke/";
            break;

            case 2: this.tema = "/imagens/tux/";
            break;

        }
    } 

    /**
     * Mostra o resumo do jogo ao sair
     *
     */
    private void mostrarResumoJogo(){

        resumoJogo = "Jogador: " + objJogadorJogada.obterNome() + "nn" +
        "Pontos: " + objJogadorJogada.obterPontos() + "nn" +
        "Quantidade de tentativas: " + qtdeTentativas;

        JOptionPane.showMessageDialog( null, ""+resumoJogo, "Resumo do Jogo",
        JOptionPane.INFORMATION_MESSAGE ); 

        setVisible( false );
        System.gc();
    }

    public void actionPerformed( ActionEvent event ){        

        // Se o evento for do botão Sair...
        if ( event.getSource() == botaoSair ){

            mostrarResumoJogo();
        }

        // Caso contrário é evento de botões de imagem
        else{

            // testa se é o primeiro clique
            if( primeiroClique ){

                qtdeTentativas++; 

                // testa se o jogador errou na jogada anterior
                if ( !acertou ){

                    botaoUm = (MyButton) botaoClicadoUm;
                    botaoDois = (MyButton) botaoClicadoDois;

                    botaoUm.setImagemPadrao();
                    botaoDois.setImagemPadrao();
                }

                botaoClicadoUm = event.getSource();

                botaoUm = (MyButton) botaoClicadoUm;
                botaoUm.setImagemBotao();                

                // indica que este foi o primeiro clique
                primeiroClique = !primeiroClique;
            }

            // se não for o primeiro clique
            else{                

                botaoClicadoDois = event.getSource();
                botaoDois = (MyButton) botaoClicadoDois;                                

                // verifica se o jogador clicou no mesmo botão
                if ( botaoClicadoDois == botaoClicadoUm ){

                    acertou = false;

                    JOptionPane.showMessageDialog( null, "Ação não permitida!",
                    "Ação Não Permitida", JOptionPane.WARNING_MESSAGE );
                }

                // se não for o mesmo botão...
                else{            

                    // coloca a imagem no botão
                    botaoDois.setImagemBotao();

                    // compara com o primeiro botão clicado
                    if ( botaoUm.getImagemBotao().equals( botaoDois.getImagemBotao())){

                        acertou = true;
                        qtdeAcerto++;                            

                        // Desabilita os botões
                        botaoUm.setEnabled( false );
                        botaoDois.setEnabled( false );                            

                        // incrementar pontos
                        objJogadorJogada.incrementarPontos();

                        pontosJogador.setText( ""+objJogadorJogada.obterPontos() );                            

                        // se foi o último par encontrado, apresenta resumo do jogo e fecha a janela
                        if ( qtdeAcerto == ((fator*fator)/2) ){

                            mostrarResumoJogo();
                            setVisible( false );
                            System.gc();
                        }
                    }

                    // se não for o mesmo botão
                    else{

                        acertou = false;                            

                        //decrementar pontos
                        objJogadorJogada.decrementarPontos();

                        pontosJogador.setText( ""+objJogadorJogada.obterPontos() );
                    }                    

                    // indica que este foi o segundo clique
                    primeiroClique = !primeiroClique;
                }
            }
        }
    } 

    private Object botaoClicadoUm = new Object();
    private Object botaoClicadoDois = new Object();
    private JLabel pontosJogador, labelPontosJogador;
    private JPanel painelImagens, painelPontos;
    private JButton botaoSair;
    private Icon imagemSair, imagemSairRoll, imagens[], imagemPadrao, imagens8[];
    private GridLayout grid;
    private Container container;
    private String tema, resumoJogo = "";
    private Jogador objJogadorJogada;
    private int fator, qtdeAcerto, qtdeTentativas = 0;
    private boolean primeiroClique = true;
    private boolean acertou = true;
    private MyButton botaoUm, botaoDois, posicoesFiguras[];    

} // fim TelaJogo

Os arquivos de imagem necessários estarão disponíveis para download ao final do post.

Não vou explicar o jogo passo-a-passo (ahhhhhhh) mas se você está começando a aprender Java, esse pequeno jogo é uma boa oportunidade para aprender a usar componentes de tela básicos, assim como aprender a usar tratamento de eventos desses componentes.

Depois de compiladas as classes (com o comando javac [nome-da-classe.java], pelo console), basta executar a classe TelaUm.java (java TelaUm, pelo console).

Jogo em execução

Jogo em execução

Imagens para o Jogo

Queria colocar todas as imagens compactadas para download, mas não é possível (até onde eu sei). Portanto, colocarei aqui as imagens referentes a cada diretório. No caso das imagens dos diretórios “tux” e “duke”, que eram muitas, eu dei um printscreen na tela e postarei uma imagem só, com todas (separadas, uma única imagem para o diretório “tux” e uma única imagem para o diretório “duke”). Vocês terão o trabalho de recortar cada uma, no tamanho de 70×70 pixels. Aproveitem para renomear cada uma de acordo com o que se vê nas imagens.

No caso das imagens do jogo (imagens de tela, botões…), por apresentarem tamanhos diversos optei por colocar uma a uma. Vamos a elas:

(importante 1: clique sobre as imagens para que elas sejam abertas no tamanho original, para só depois salvarem as mesmas)

Imagens do diretório “duke”

imagens-duke

Imagens do diretório “tux”

imagens-tux

Imagens do diretório “jogo”

titulo standard sairroll sair painel jogarroll jogar java

Se tiverem dúvidas, fique à vontade para postar comentários!

Exemplo de JInternalFrame

Segue um exemplo, com alguma adaptação, que postei há um tempo no GUJ. Trata-se de um exemplo de como usar o JInternalFrame.

Por ser uma dica rápida, não vou entrar em detalhes sobre os componentes em questão, isso pode ser encontrado em diversos lugares em maiores detalhes, incluindo o site da própria documentação do Swing. No final há alguns links de referência.

Espero que seja útil.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Principal extends JFrame{

    private JDesktopPane desktopPane;
    private JMenuItem menuItem1, menuItem2;
    private JMenuBar menuBar;
    private JMenu menu;
    private InternalFrameUm frameUm;
    private InternalFrameDois frameDois;

    public Principal(){

        super("Exemplo de JDesktopPane");

        int inset = 50;

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                   screenSize.width  - inset*2,
                  screenSize.height - inset*2);

        desktopPane = new JDesktopPane();
        menuItem1 = new JMenuItem("InternalFrameUm");
        menuItem1.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent evt){

                if(frameUm == null){
                    frameUm = new InternalFrameUm();
                    frameUm.setVisible(true);
                    desktopPane.add(frameUm);
                }
                else if(!frameUm.isVisible()){
                    frameUm.setVisible(true);
                    desktopPane.add(frameUm);
                }
            }
        });

        menuItem2 = new JMenuItem("InternalFrameDois");
        menuItem2.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent evt){

                if(frameDois == null){
                    frameDois = new InternalFrameDois();
                    frameDois.setVisible(true);
                    desktopPane.add(frameDois);
                }
                else if(!frameDois.isVisible()){
                    frameDois.setVisible(true);
                    desktopPane.add(frameDois);
                }
            }
        });

        menuBar = new JMenuBar();
        menu = new JMenu("Opções");

        setContentPane(desktopPane);

        menu.add(menuItem1);
        menu.add(menuItem2);

        menuBar.add(menu);

        setJMenuBar(menuBar);

        setVisible(true);
        setResizable(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String args[]){

        Principal p = new Principal();
    }

}

Nos JPanel criados abaixo, apenas coloquei uma cor de fundo, para exemplificar. O importante aqui é mostrar como fixar os JPanel ao JInternalFrame. Nesses JPanel poderiam ser colocados diversos outros componentes de tela, como botões, campos de entrada, etc…


import javax.swing.JInternalFrame;
import java.awt.event.*;
import java.awt.*;
import javax.swing.JPanel;

public class InternalFrameUm extends JInternalFrame {  

    public InternalFrameUm() {
        super("Internal Frame Um",
              true, //resizable
               true, //closable
              true, //maximizable
               true);//iconifiable  

         setSize(300,300);  

         setSize(300,300);  

        JPanel panel = new JPanel();
         panel.setBackground(Color.GREEN);  

         Container container = getContentPane();
         container.add(panel);
     }
 }


import javax.swing.JInternalFrame;
import java.awt.event.*;
import java.awt.*;
import javax.swing.JPanel;

public class InternalFrameDois extends JInternalFrame {  

     public InternalFrameDois() {
         super("Internal Frame Um",
               true, //resizable
               true, //closable
                true, //maximizable
               true);//iconifiable  

         setSize(300,300);  

        JPanel panel = new JPanel();
         panel.setBackground(Color.YELLOW);  

         Container container = getContentPane();
         container.add(panel);

     }
}

É isto!

Procurei criar um exemplo bem simples, que vá direto ao ponto.

Qualquer dúvida ou sugestão ou crítica é bem vinda!

Até o próximo post!

Referências:

http://java.sun.com/docs/books/tutorial/uiswing/components/internalframe.html