TDD Cobertura de testes com JUnit Ant e Emma

Olá pessoal, bom dia.

Quando escrevemos testes temos sempre a preocupação de saber se estamos testando toda nossa classe. Imagine só, testar 4 métodos e esquecer de simular um caso de teste que caia em uma Exception do Try/Catch?

Para solucionar esse problema existem ferramentas para cobrir seu teste, que irão informar por onde seu teste passou ou deixou de passar.

Vou falar sobre duas ferramentas para cobertura do seu teste Emma (no post de hoje) e Cobertura (no próximo post sobre TDD).

Vou usar exatamente o código deste post (TDD com HSQLDB, JPA e Hibernate). Se você quiser montar um ambiente, siga o que for descrito no post anterior e tudo irá funcionar 100%. Outro post sobre o assunto é: TDD – Primeiros passos.

Hoje iremos utilizar o framework Emma com Ant. Não sou um bom conhecedor ainda do Ant, então os códigos que “garimpei” pela internet talvez tenha alguma redundância ou coisa assim. Um dia irei melhorá-lo.

Vamos começar com os downloads:

O JUnit é utilizado apenas pelo o framework do Emma possa executar os testes.

Copie as bibliotecas do Emma e do JUnit para a pasta lib; não será necessário adicioná-los ao “Build Path” do projeto.

Crie um arquivo chamado “build.xml” na raiz do seu projeto (
o arquivo tem que ficar exatamente na raiz do projeto, ou então o script irá falhar)
.

Altera altere o arquivo para ficar com o seguinte código:

<project name="Emma Reports" basedir=".">

    <!--  Project Source  Code -->
    <property name="src.dir" value="src" />
    <property name="bin.dir" value="bin" />
    <property name="teste.dir" value="src/test" />
    <property name="lib.dir" value="lib" />

    <!-- Emma source code -->
    <property name="emma.bin.dir" value="emma/bin" />
    <property name="emma.metadate.dir" value="emma/metadate" />
    <property name="emma.report.dir" value="emma/report" />

    <!-- Tested Class -->
    <property name="DogFacadeTest" value="test.com.facade.DogFacadeTest"/>

    <!-- Project classpath -->
    <path id="project.classpath">
        <pathelement location="${bin.dir}" />
        <fileset dir="${lib.dir}">
            <include name="*.jar" />
        </fileset>
    </path>

    <!-- Emma task definitions that you will find inside the jar -->
    <taskdef resource="emma_ant.properties">
        <classpath refid="project.classpath" />
    </taskdef>

    <!-- JUnit task definition -->
    <taskdef name="junit" classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask" />

</project>

No arquivo build.xml estamos definindo as variáveis do script, os caminhos dos arquivos e o JUnit a ser executado.

Agora vá ao menu Window > Show View > Other. Digite Ante aperte Ok:


Arraste o arquivo para o dentro do da view do Ant. Duplo click no arquivo para executá-lo. A seguinte mensagem irá aparecer:

Caso a mensagem “Could not load definitions from resource emma_ant.properties. It could not be found.” É por que o caminho da sua lib está errado. Verifique novamente se o seu arquivo“build.xml” está na raiz do projeto. Não prossiga sem resolver esse problema de path.

Vamos alterar nosso “build.xml” e fazer com que ele a partir de agora compile nosso código java. Ele tem dois passos (ações chamadas de “target”).

    <!-- Clean UP Your Code -->
    <target name="01-CleanUp">
        <delete dir="${bin.dir}" />
        <mkdir dir="${bin.dir}" />
    </target>

    <!-- Compile Your Code -->
    <target name="02-CompileSourceCode" depends="01-CleanUp">
        <javac debug="on" srcdir="${src.dir}" destdir="${bin.dir}">
            <classpath refid="project.classpath" />
        </javac>
        <copy file="${src.dir}/META-INF/persistence.xml" todir="${bin.dir}/META-INF" />
    </target>

Perceba que temos as ações “01-CleanUp” e “02-CompileSourceCode”onde a ação de compilar “depends” da ação de limpar o código, com isso, toda vez que você quiser compilar o projeto o Ant irá executar a ação de limpar automaticamente.

Vamos gerar agora a task com uma palavra que você irá ouvir muito, Instrumentação. Falando vagamente, o Emma ele é bytecode instrumentado,ou seja, ele não necessita de adaptações ou alterações na JVM para mensurar o seu código. Ele analisa o seu bytecode e pronto.

Vamos alterar o build.xml e adicionar a task:

    <!-- Generate the Emma  -->
    <target name="03-Instrumentation" depends="02-CompileSourceCode">
        <emma>
            <instr instrpath="${bin.dir}" destdir="${emma.bin.dir}" metadatafile="${emma.metadate.dir}/metadate.emma" merge="false" mode="fullcopy" />
        </emma>
    </target>

Vamos criar agora, a chave que irá compilar e executar o teste JUnit:

    <!-- Runs JUnit Tests -->
    <target name="04-RunTests" depends="03-Instrumentation">
        <junit haltonfailure="false" haltonerror="false" fork="true">
            <classpath>
                <pathelement location="${emma.bin.dir}/classes" />
                <pathelement location="${emma.bin.dir}/lib" />
                <path refid="project.classpath" />
            </classpath>
            <formatter type="plain" usefile="false" />
            <test name="${DogFacadeTest}" />
            <jvmarg value="-Demma.coverage.out.file=${emma.metadado.dir}/cobertura.emma" />
            <jvmarg value="-Demma.coverage.out.merge=false" />
        </junit>
    </target>

Execute o teste e veja que o nosso teste não retornou erro:

Vamos criar agora o nosso gerador de relatório:

    <!-- Creates the report -->
    <target name="00-GenerateReport" depends="04-RunTests">
        <delete dir="${emma.report.dir}" />
        <emma enabled="true">
            <report sourcepath="${src.dir}" sort="+block,+name,+method,+class" metrics="method:70,block:80,line:80,class:100">
                <fileset dir="${emma.metadate.dir}">
                    <include name="*.emma" />
                </fileset>
                <html outfile="${emma.report.dir}/report.html" depth="method" columns="name,class,method,block,line" />
            </report>
        </emma>
    </target>

Execute a tarefa de Gerar relatório e abra o arquivo que irá aparecer em “
/emma/report/report.html
”.


E por ultimo vamos apagar os arquivos que foram gerados para que o relatório fosse extraído.

Altere a task para gerar relatório e vamos criar uma task que irá ser chamada automaticamente pelo Ant após gerar um relatório:

    <!-- Creates the report -->
    <target name="00-GenerateReport" depends="04-RunTests">
        <delete dir="${emma.report.dir}" />
        <emma enabled="true">
            <report sourcepath="${src.dir}" sort="+block,+name,+method,+class" metrics="method:70,block:80,line:80,class:100">
                <fileset dir="${emma.metadate.dir}">
                    <include name="*.emma" />
                </fileset>
                <html outfile="${emma.report.dir}/report.html" depth="method" columns="name,class,method,block,line" />
            </report>
        </emma>

        <antcall target="05-DeleteOldReportData" />
    </target>

    <!-- Delete Old Report Data -->
    <target name="05-DeleteOldReportData">
        <delete dir="${emma.bin.dir}" />
        <delete dir="${emma.metadate.dir}" />
    </target>

Click aqui para fazer o download do tutorial de hoje.
Espero que esse post possa te ajudar.

Qualquer dúvida ou comentário, basta colocar.

Inté+! o_

OBS.: Fonte de pesquisa:

TDD Coverage of tests with JUnit Ant and Emma

Hello, good morning.

When we write out TDD tests we always care to write the right tests, and to be sure if we are testing all classes that we have. Imagine if you test 4 methods and forget to test a Catch of an Exception. How sad is that?

To help us with this problems there are some tools to cover your test; it will let you know where your test passed or missed.

I will talk about two coverage tests tools: Emma (our post today), Cobertura (the next post about TDD).

I will use the same code you can find in here (TDD with HSQLDB, JPA and Hibernate). If you want to set up your environment, follow that how-to and everything will work 100% for sure. Another post about TDD is: TDD – First Steps.

Today we will use the Emma framework with Ant. I do not have a good knowledge about Ant yet, so all the codes that I found on the internet maybe have some redundancies or something like that. Some day I will improve it.

Let us start downloading the libraries:

The JUnit library it is only used by the Emma Framework; it will not be necessary to add them in your project “Build Path”.

Create a file named “build.xml” at the root folder of your project (pay attention to the fact that your file must be in the root source of your project, or this script will fail):

Edit your file and add the code bellow:

<project name="Emma Reports" basedir=".">

    <!--  Project Source  Code -->
    <property name="src.dir" value="src" />
    <property name="bin.dir" value="bin" />
    <property name="teste.dir" value="src/test" />
    <property name="lib.dir" value="lib" />

    <!-- Emma source code -->
    <property name="emma.bin.dir" value="emma/bin" />
    <property name="emma.metadate.dir" value="emma/metadate" />
    <property name="emma.report.dir" value="emma/report" />

    <!-- Tested Class -->
    <property name="DogFacadeTest" value="test.com.facade.DogFacadeTest"/>

    <!-- Project classpath -->
    <path id="project.classpath">
        <pathelement location="${bin.dir}" />
        <fileset dir="${lib.dir}">
            <include name="*.jar" />
        </fileset>
    </path>

    <!-- Emma task definitions that you will find inside the jar -->
    <taskdef resource="emma_ant.properties">
        <classpath refid="project.classpath" />
    </taskdef>

    <!-- JUnit task definition -->
    <taskdef name="junit" classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask" />

</project>

In the “build.xml” file we are defining the script variables, the files path and the JUnit to be executed.

Go to the menu Window > Show View > Other. Type Ant and press Ok:


Drag the file “build.xml” and drop it into the Ant view. Double click in the file to execute it. You will see this message:

If you see the message “
Could not load definitions from resource emma_ant.properties. It could not be found.
” It is because your lib path is wrong. Check again your build.xml file is in the root of your project.

Do not go forward without solving this path issue.

Let us edit our “build.xml” and add the code to compile our java code. It has two actions (an action is defined as target):

    <!-- Clean UP Your Code -->
    <target name="01-CleanUp">
        <delete dir="${bin.dir}" />
        <mkdir dir="${bin.dir}" />
    </target>

    <!-- Compile Your Code -->
    <target name="02-CompileSourceCode" depends="01-CleanUp">
        <javac debug="on" srcdir="${src.dir}" destdir="${bin.dir}">
            <classpath refid="project.classpath" />
        </javac>
        <copy file="${src.dir}/META-INF/persistence.xml" todir="${bin.dir}/META-INF" />
    </target>

Notice that we have two actions named “01-CleanUp” and “02-CompileSourceCode”; to compile our code the target “depends” of the action that cleans the bin path; every time that you compile the source code of your project, the ant will run the action of cleaning the old code.

Let us create a task now with a word that you will hear a lot, Instrumentation. Just to comment about it, the Emma framework it is bytecode instrumented; Emma will not need adaptations or switches in the JVM to watch over your code. It will analyze your code through the bytecode only.

Edit your “build.xml” file and add the following task:

    <!-- Generate the Emma  -->
    <target name="03-Instrumentation" depends="02-CompileSourceCode">
        <emma>
            <instr instrpath="${bin.dir}" destdir="${emma.bin.dir}" metadatafile="${emma.metadate.dir}/metadate.emma" merge="false" mode="fullcopy" />
        </emma>
    </target>

Let us now add the target that will allow us to run the JUnit tests:

    <!-- Runs JUnit Tests -->
    <target name="04-RunTests" depends="03-Instrumentation">
        <junit haltonfailure="false" haltonerror="false" fork="true">
            <classpath>
                <pathelement location="${emma.bin.dir}/classes" />
                <pathelement location="${emma.bin.dir}/lib" />
                <path refid="project.classpath" />
            </classpath>
            <formatter type="plain" usefile="false" />
            <test name="${DogFacadeTest}" />
            <jvmarg value="-Demma.coverage.out.file=${emma.metadado.dir}/cobertura.emma" />
            <jvmarg value="-Demma.coverage.out.merge=false" />
        </junit>
    </target>

Run the test and you will see that in our code there is no error:

Let us create the task that will create the report:

    <!-- Creates the report -->
    <target name="00-GenerateReport" depends="04-RunTests">
        <delete dir="${emma.report.dir}" />
        <emma enabled="true">
            <report sourcepath="${src.dir}" sort="+block,+name,+method,+class" metrics="method:70,block:80,line:80,class:100">
                <fileset dir="${emma.metadate.dir}">
                    <include name="*.emma" />
                </fileset>
                <html outfile="${emma.report.dir}/report.html" depth="method" columns="name,class,method,block,line" />
            </report>
        </emma>
    </target>

Execute the task and then open the report file that you will find in: “
/emma/report/report.html
”.

As our last action, let us delete the files that were generated only to build the report and now they are not needed anymore. Edit the task that creates the report and add the line that will call the deleting files task:

    <!-- Creates the report -->
    <target name="00-GenerateReport" depends="04-RunTests">
        <delete dir="${emma.report.dir}" />
        <emma enabled="true">
            <report sourcepath="${src.dir}" sort="+block,+name,+method,+class" metrics="method:70,block:80,line:80,class:100">
                <fileset dir="${emma.metadate.dir}">
                    <include name="*.emma" />
                </fileset>
                <html outfile="${emma.report.dir}/report.html" depth="method" columns="name,class,method,block,line" />
            </report>
        </emma>

        <antcall target="05-DeleteOldReportData" />
    </target>

    <!-- Delete Old Report Data -->
    <target name="05-DeleteOldReportData">
        <delete dir="${emma.bin.dir}" />
        <delete dir="${emma.metadate.dir}" />
    </target>

Click here to download the source code of this tutorial.

I hope this post might help you.

If you have any doubt or comments to do, just post it.

See you later! o_

PS.: Researched from:

JUnit com HSQLDB, JPA e Hibernate

Olá, bom dia.

Vamos falar hoje em como integrar seus testes unitários utilizando um banco de dados? Uma das melhores soluções do mercado atualmente é utilizarum banco de dados em memória.

O banco de dados HSQLDB faz todo esse trabalho de, criar aestrutura de tabelas, relacionamento entre as chaves e permitir que o JPAconsiga trabalhar sem problemas.

Nesse tutorial vamos ver como criar teste unitário (TDD) comJPA e HSQLDB.

Para ver o outro post sobre TDD você pode clicar aqui: TDD – Primeiros passos.

Você irá precisar fazer o download do JAR do HSQLDB aqui (hsqldb.org/Versão2.25 – Última versão).

Vou utilizar o Hibernate como provider do JPA. Nessetutorial (Tutorial Hibernate 3 com JPA 2) você irá encontrar os links necessários para download.

Crie um projeto Java em File > New Project > JavaProject.

Crie uma pasta lib e dentro dela coloque todas asbibliotecas necessárias dentro dessa pasta. Os arquivos são as bibliotecas do HSQLB e do Hibernate.

Vou resumir daqui para frente, mas no tutorial (Tutorial Hibernate 3 com JPA 2) mostracomo colocar seu projeto para executar com sucesso o Hibernate.

Clique com o botão direito do mouse sobre o Projeto >Properties. Vá a Java Build Path > aba Libraries > Add Jars.Selecione as bibliotecas que lá estão e aperte Ok.

Crie uma pasta chamada “src/META-INF” e dentro dela coloqueo seu persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="HsqldbWithTDD" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:." />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="javax.persistence.jdbc.password" value="" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
            <property name="hibernate.connection.shutdown" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
</persistence>

Vamos criar a classe Dog que será persistida.

package com.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "dog")
@NamedQuery(name="listALL", query="select d from Dog d")
public class Dog {

    public static final String LIST_ALL = "listALL";

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    private double weight;

    // Getters and Setters
}

Vamos criar agora a classe DAO que irá ter um CRUD básico da classe Dog (Na classe Dog você irá a anotação @NamedQuery, ainda não vimos aqui no blog mas basicamente é um SQL que irá realizar a consulta):

package com.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.model.Dog;

public class DogDAO {

    private EntityManagerFactory emf;
    private EntityManager em;

    public void startConnection(){
        emf = Persistence.createEntityManagerFactory("HsqldbWithTDD");
        em = emf.createEntityManager();
        em.getTransaction().begin();
    }

    public void closeConnection(){
        em.getTransaction().commit();
        emf.close();
    }

    public void save(Dog dog){
        em.persist(dog);
    }

    public void edit(Dog dog){
        em.merge(dog);
    }

    public Dog find(int dogId){
        return em.find(Dog.class, dogId);
    }

    public void remove(Dog dog){
        em.remove(dog);
    }

    public List listALL(){
        return em.createNamedQuery(Dog.LIST_ALL, Dog.class).getResultList();
    }
}

Vamos testar se nossa informação está sendo persistida? Vamos criar uma classe Main e executar um teste, o resultado da classe Main deve ser como a figura abaixo:

package com;

import com.dao.DogDAO;
import com.model.Dog;

public class Main {

    public static void main(String[] args) {
        DogDAO dogDAO = new DogDAO();

        dogDAO.startConnection();

        try {
            Dog dog = new Dog();
            dog.setName("Beethoven");
            dog.setWeight(45);

            dogDAO.save(dog);

            // It was the first saved dog, so its id is 1
            Dog persistedDog = dogDAO.find(1);

            System.out.println("Name: " + persistedDog.getName());
            System.out.println("Weight: " + persistedDog.getWeight());
        } catch (Exception e) {
            System.out.println("Ops, something happen: " + e.getMessage());
            e.printStackTrace();
        }finally{
            dogDAO.closeConnection();
        }
    }
}


Agora, vamos utilizar a metodologia de TDD para criar um método/classe? Vamos simular que, precisamos de um método que calcule a média de peso de todos os cachorros cadastrados no sistema.

Vou criar a classe DogFacade que irá fazer esse trabalho, einclusive será ela quem irá trabalhar com o DAO.

Vamos configurar a biblioteca do JUnit em nosso sistema, namesma tela em que adicionamos as bibliotecas do Hibernate e do HSQLDB. Cliqueem Add Library.

Selecione JUnit 4 e pronto.

Seguindo boas práticas do TDD, vamos criar nossa classe deteste sem ter a classe DogFacade criada.

package test.com.facade;

import static junit.framework.Assert.assertEquals;

import org.junit.Test;

import com.model.Dog;

public class DogFacadeTest {

    @Test
    public void isWeightOfAllDogsCorret(){
        DogFacade dogFacade = new DogFacade();
        dogFacade.startConnection();

        createData(dogFacade);
        assertEquals(30, dogFacade.getAverageDogsWeight());

        dogFacade.closeConnection();
    }

    private void createData(){
        Dog dogA = new Dog();
        dogA.setName("Big Dog");
        dogA.setWeight(45);

        Dog dogB = new Dog();
        dogB.setName("Medium Dog");
        dogB.setWeight(30);

        Dog dogC = new Dog();
        dogC.setName("Small Dog");
        dogC.setWeight(15);
    }
}

Ao criar essa classe vários erros serão exibidos, masseguindo o princípio do TDD, “Vermelho, Verde, Refatorar”!  Para mais detalhes veja aqui (TDD – Primeiros passos).

Como nosso exemplo é simples, estou deixando o Facadegerenciando a conexão. Em uma aplicação web uma boa saída seria utilizar ainjeção de recursos, para continuar utilizando essa abordagem você pode adotaro que for melhor para você (algumas sugestões minhas, mas você poderá achar outraspela internet):

  • Passar o EntityManager por construtores. Sua classe de teste criaria o EntityManager através do EntityManagerFactory e o enviaria através de um construtor sobrecarregado. Ficaria assim: new DogFacade(entityManager); e dentro do Facade você iria fazer new DogDao(entityManager).
  • Criar o DAO na classe de teste com a conexão ativa e passar o DAO através de construtores sobrecarregados nos Facades.

Será necessário adotar alguma estratégia ou então vai choverNullPointer em seus testes. A parte ruim dessa estratégia é que sua classeFacade vai ficar conhecendo o EntityManager, ou ter uma dependência em umconstrutor de DAO. Mas esses construtores serão utilizados apenas em testes,não irão/devem interferir na aplicação em tempo real.

Vamos ver como ficou nossa classe DogFacade:

package com.facade;

import java.util.List;

import com.dao.DogDAO;
import com.model.Dog;

public class DogFacade {

    private DogDAO dogDAO;

    public DogFacade() {
        dogDAO = new DogDAO();
    }

    public void startConnection() {
        dogDAO.startConnection();
    }

    public double getAverageDogsWeight() {
        double totalWeight = 0;

        List dogs = dogDAO.listALL();

        for(Dog dog : dogs){
            totalWeight += dog.getWeight();
        }

        return totalWeight / dogs.size();
    }

    public void save(Dog dog){
        dogDAO.save(dog);
    }

    public void closeConnection() {
        dogDAO.closeConnection();
    }
}

Vamos executar nosso teste novamente?


Considerações Finais:

  • Eu prefiro essa abordagem a realizar Mocks. Um objeto “mocado” não irá se comportar como você não espera, esse objeto irá apenas se comportar do modo que ele foi programado. Em nosso caso o DAO não teve nenhum método “mocado”. Utilizamos o Facade de modo natural e não alteramos nenhum dos seus métodos.
  • Vejo o mock sendo realmente útil em casos onde são necessárias conexões com outras aplicações ou em aplicações de código legado onde o grau de complexidade é alta e utilizar essa abordagem seria muito custosa.
  • Com esse tipo de abordagem poderemos inclusive criar testes para as classes de view: JSF, Struts, Seam, etc. Próximo post que vou escrever sobre esse assunto! ;)

Espero que esse post possa te ajudar.

Qualquer dúvida ou colocação, basta postar.

Até +! o_

JUnit with HSQLDB, JPA and Hibernate

Hello, good morning.

Today we will talk about integration between your database and unit testing. One of the best solutions that we can find, it is to run a runtime database.

The HSQLDB database will do the work of creating the table structures, the primary key relationships and will allow the JPA to work without problems.

In this How-To we will see how to create a Unit Test (TDD) with JPA and HSQLDB.

To see the other post of TDD you can check it here: TDD – First Steps.
You will need to download the HSQLDB jar that you will find here (http://hsqldb.org/- Version2.25 – Last version so far).

I will use the Hibernate as our JPA provider. In this tutorial (Tutorial Hibernate 3 with JPA 2) you will find the link to download it.

Create a Java project at File > New Project > Java Project.

Let us create a folder named lib and put all the libraries inside the folder. The files will be those inside the HSQLDB and Hibernate zips:

I will not get in the details of how to setup the application; I will give only a few hints. To see amore detailed post about JPA/Hibernate you can check this links: (Tutorial Hibernate 3 with JPA 2).

Click with the right mouse button in the Project > Properties. Then go to JavaBuild Path > Libraries tab > Add jars. Select the files that you will find in the lib folder.

You will need to create a folder named “src/META-INF” and inside it create a file named persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="HsqldbWithTDD" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:." />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="javax.persistence.jdbc.password" value="" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
            <property name="hibernate.connection.shutdown" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
</persistence>

Let us create an entity to be persisted, a Dog class:

package com.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "dog")
@NamedQuery(name="listALL", query="select d from Dog d")
public class Dog {

    public static final String LIST_ALL = "listALL";

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    private double weight;

    // Getters and Setters
}

Let us create a DAOclass that will have a basic CRUD of the Dog class (In the Dog class you will find the annotation “NamedQuery”, we have not studied about this annotation here in the blog but this annotations “it is like” a raw SQL):

package com.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.model.Dog;

public class DogDAO {

    private EntityManagerFactory emf;
    private EntityManager em;

    public void startConnection(){
        emf = Persistence.createEntityManagerFactory("HsqldbWithTDD");
        em = emf.createEntityManager();
        em.getTransaction().begin();
    }

    public void closeConnection(){
        em.getTransaction().commit();
        emf.close();
    }

    public void save(Dog dog){
        em.persist(dog);
    }

    public void edit(Dog dog){
        em.merge(dog);
    }

    public Dog find(int dogId){
        return em.find(Dog.class, dogId);
    }

    public void remove(Dog dog){
        em.remove(dog);
    }

    public List listALL(){
        return em.createNamedQuery(Dog.LIST_ALL, Dog.class).getResultList();
    }
}

What if we do a test now? Let us see if the dog is being persisted. We will check this information through a class that we will name “Main”, run this class and you will have a message displayed like the picture bellow:

package com;

import com.dao.DogDAO;
import com.model.Dog;

public class Main {

    public static void main(String[] args) {
        DogDAO dogDAO = new DogDAO();

        dogDAO.startConnection();

        try {
            Dog dog = new Dog();
            dog.setName("Beethoven");
            dog.setWeight(45);

            dogDAO.save(dog);

            // It was the first saved dog, so its id is 1
            Dog persistedDog = dogDAO.find(1);

            System.out.println("Name: " + persistedDog.getName());
            System.out.println("Weight: " + persistedDog.getWeight());
        } catch (Exception e) {
            System.out.println("Ops, something happen: " + e.getMessage());
            e.printStackTrace();
        }finally{
            dogDAO.closeConnection();
        }
    }
}


Let us apply now the TDD methodology to create a method/class. We will create a method to give us the weight average of all dogs in our database.

I will create a DogFacade class that will do all the work, even with the DAO.

We need to configure the JUnit library. We will start from the same screen that we added the HSQLDB and Hibernate; Click in the button add Library:

Select JUnit 4 and finish.

Following the TDD good practices, let us create our test class before the DogFacade class.

package test.com.facade;

import static junit.framework.Assert.assertEquals;

import org.junit.Test;

import com.model.Dog;

public class DogFacadeTest {

    @Test
    public void isWeightOfAllDogsCorret(){
        DogFacade dogFacade = new DogFacade();
        dogFacade.startConnection();

        createData(dogFacade);
        assertEquals(30, dogFacade.getAverageDogsWeight());

        dogFacade.closeConnection();
    }

    private void createData(){
        Dog dogA = new Dog();
        dogA.setName("Big Dog");
        dogA.setWeight(45);

        Dog dogB = new Dog();
        dogB.setName("Medium Dog");
        dogB.setWeight(30);

        Dog dogC = new Dog();
        dogC.setName("Small Dog");
        dogC.setWeight(15);
    }
}

After you create detest class you will see tons of errors, but following the TDD principles, “Red, Green, and Refactor”. To more details about this concept, check this link: TDD – First Steps.

The code of this post is not a complex code, so the Façade by itself handles the connection. When we work with web applications it is a god idea to use the resource injection, so if you want to keep using this kind of resources it is a good idea to use the best pattern for you (I will list bellow some ideas I had, but you can find tons of others ideas on the internet):

  • Pass the EntityManager in the constructors. Your test class would create the EntityManager and it would send it to the DAO through the Façade. It would be like this: new DogFacade(entityManager); and inside the DogFacade class you would do new DogDao(entityManager);
  • You can create a DAO in your Test class (with an active entityManager) and pass it through overloaded constructors in the Façades.

You will need to adopt some strategy to pass the EntityManager from the test class to your DAO or it will “rain” NullPointerException at your console.

When you use any of the strategies above you have the down side that your Façade will now the EntityManager or will have a constructor with a DAO on it. The overloaded constructor should be used only by tests classes and should not interact with the production code.

Let us see how the class DogFacade will look like:

package com.facade;

import java.util.List;

import com.dao.DogDAO;
import com.model.Dog;

public class DogFacade {

    private DogDAO dogDAO;

    public DogFacade() {
        dogDAO = new DogDAO();
    }

    public void startConnection() {
        dogDAO.startConnection();
    }

    public double getAverageDogsWeight() {
        double totalWeight = 0;

        List dogs = dogDAO.listALL();

        for(Dog dog : dogs){
            totalWeight += dog.getWeight();
        }

        return totalWeight / dogs.size();
    }

    public void save(Dog dog){
        dogDAO.save(dog);
    }

    public void closeConnection() {
        dogDAO.closeConnection();
    }
}

Let us run our test again:


Final thoughts:

  • I think this approach it is a better then using mock objects. A mocked object will not behave like you do not want to; it will only act in the way it was programmed to. In our case the DAO was behaving like it will behave with the production code, and not like a mocked object. The Façade was not mocked also; it was behaving without any change at runtime in our tests.
  • I see mock strategy very useful when you need to do tests with classes that integrate with other systems; mock is a good strategy also when you need to work with legacy codes that will cost more to refactor than to mock.
  • With this approach you will be able to create tests to your view classes like: JSF, Struts, Seam, etc. In a future post I will write about this.

I hope this post might help you.

If you have any doubt or want to share an opinion just post it.

Bye o_

Validação de Login de Usuário com JSF e JAAS

Olá pessoal, tudo bem?

Em outro post eu mostrei como fazer uma validação via Servlet e Filter. Vamos fazer de um modo diferente hoje, vamos validar usuário utilizando JSF e JAAS.

A validação de usuário via JAAS faz com que o próprio servidor valide o usuário e sua senha juntamente a qual perfil de acesso ele tem. Sem a necessidade de a todo o momento estar (programaticamente) defendendo nossa aplicação. Deixemos o JBoss fazer isso por nós.

A seguir todos os links sobre web que já postei aqui no Blog: JSF: Converter e Bean Auto Complete, JSF – Hello World, Auto Complete, Autenticação de Usuários (Filter/Servlet), Tratando Exceções em uma Aplicação Web, Criando um WebServer.

Nós iremos utilizar JSF, Postgres, Eclipse e o JBoss 6:

O JBoss Tools é apenas para adiantar a parte JSF e poder utilizar o JBoss 6 por dentro do Eclipse. Você poderá criar todo o código passado aqui, e depois colocar apenas o war dentro da pasta do JBoss e executá-lo por fora do Eclipse. E mais acima tem um link para o post de como fazer de como fazer uma aplicação JSF.

Não irei mostrar como instalar o Postgres, pois irá fugir do tema do post, mas você poderá utilizar qualquer banco como MySQL, SqlServer, Oracle e assim vai.

Para instalar o JBoss Server descompacte o arquivo e pronto.

Descompacte o Eclipse e instale o JBoss Tools. É uma ferramenta muito boa quando se trabalha com JBoss e JSF.
Para instalar o JBoss Tools vá ao menu Help >Install New Software digite o endereço http://download.jboss.org/jbosstools/updates/stable/helios/na barra “Work with” e aperte a tecla “enter”.

Para ficar mais fácil, selecionei essas duas opções:

Depois avance e finalize a instalação.
E para instalar o servidor é fácil, botão direito na aba “Servers” > New > Server
Selecione JBoss 6 > Next

Selecione o path onde está seu JBoss descompactado.

E agora clique em Finish.

Vamos criar um novo projeto JSF (File > New > Project). Estou utilizando já um modelo pronto fornecido pelo JBoss Tools. Caso você queria criar um projeto JSF do zero, basta olhar mais acima onde eu postei outros links sobre a parte web.

Configure sua aplicação, eu a estou chamando de LoginJSF, selecione JSF 2.0 e depois clique em Finish.

O Eclipse irá perguntar se você quer mudar o modo de visualização web, eu marquei que não. Você pode marcar o sim depois e ver como que fica.

Ele irá criar uma aplicação onde já tem uma navegação básica. Note que ele já adicionou seu projeto dentro do servidor, caso não o tenha adicionado clique com o botão direito em cima do projeto >Run As > Run on Server. E selecione o JBoss.


Rode o servidor e acesse o seguinte link: http://localhost:8080/LoginJSF/

Pare o JBoss pois vamos criar três pastas:

  • Uma pública
  • Uma com acesso apenas para admin
  • Uma com acesso parar admin e user

Abaixo, como ficará nossa estrutura de arquivos e os arquivos listados.

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <h:head>
        I am public! [=
    </h:head>
    <h:body>
        Everyone can see me. o/
    </h:body>
</html>
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <h:head>
        Yes master admin! Welcome!
    </h:head>
    <h:body>
        Let us run this business!
    </h:body>
</html>
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <h:head>
        Welcome mister user!
    </h:head>
    <h:body>
        <br/>
        Please, do not create any bugs, ok?! >=)
    </h:body>
</html>

Agora vamos criar as configurações que interessam. Primeiramente, vamos alterar o “web.xml” para que ele proteja as nossas páginas:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
    <display-name>LoginJSF</display-name>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
        <url-pattern>*.jsf</url-pattern>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <!-- Protected Areas -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Only admins</web-resource-name>
            <url-pattern>/pages/protected/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Users and admins</web-resource-name>
            <url-pattern>/pages/protected/user/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
            <role-name>USER</role-name>
        </auth-constraint>
    </security-constraint>

    <!-- Validation By Form -->
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/pages/public/login.xhtml</form-login-page>
            <form-error-page>/pages/public/loginError.xhtml</form-error-page>
        </form-login-config>
    </login-config>

    <!-- Allowed Roles -->
    <security-role>
        <role-name>ADMIN</role-name>
    </security-role>
    <security-role>
        <role-name>USER</role-name>
    </security-role>

    <!-- Filter to get the user name and work with it -->
    <filter>
        <filter-name>LoginFilter</filter-name>
        <filter-class>com.filters.LoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>LoginFilter</filter-name>
        <url-pattern>/pages/protected/*</url-pattern>
    </filter-mapping>
</web-app>

Nós iremos precisar de uma página para login juntamente com uma página para erro. Observe que essas páginas estão descritas no “web.xml”.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    JSF 2 Login
</h:head>
<h:body>
    <p>Login to access secure pages:</p>
    <form method="post" action="j_security_check">
        <h:panelGrid columns="2">
            <h:outputLabel for="j_username" value="Username" />
            <input type="text" id="j_username" name="j_username" />
            <h:outputLabel for="j_password" value="Password" />
            <input type="password" id="j_password" name="j_password" />
            <input type="submit" name="submit" value="Login" />
        </h:panelGrid>
        <br />
    </form>
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    JSF 2 Login
</h:head>
<h:body>
    <p>Ops! Check your user and password</p>
</h:body>
</html>

Vamos criar um Filtro java que irá capturar o nome do login que foi digitado onde você poderá utilizar o login para buscar no banco de dados o usuário e colocá-lo na sessão.

package com.filters;

import java.io.IOException;

import javax.servlet.*;

import org.jboss.security.SecurityAssociation;

public class LoginFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException,
            ServletException {

        String userName = SecurityAssociation.getPrincipal().getName();

        System.out.println("Yeeey! Get me here and find me in the database: " + userName);

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

Vamos criar agora nossas tabelas no banco de dados?

Estou utilizando um exemplo mais simples possível, duas tabelas apenas e cada uma com o mínimo de campos necessários.
Crie um database chamado LoginJSF e em seguida execute o script abaixo:

CREATE TABLE "users"
(
  "name" character varying(50),
  pass character varying(50)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE "users" OWNER TO postgres;

CREATE TABLE user_roles
(
  user_name character varying(50),
  role_name character varying
)
WITH (
  OIDS=FALSE
);
ALTER TABLE user_roles OWNER TO postgres;

Vamos inserir dois usuários, um com perfil de USER e outro com perfil de ADMIN:

INSERT INTO users("name", pass) VALUES ('user', 'user');
INSERT INTO users("name", pass) VALUES ('admin', 'admin');
INSERT INTO user_roles(user_name, role_name) VALUES ('user', 'USER');
INSERT INTO user_roles(user_name, role_name) VALUES ('admin', 'ADMIN');

Estamos quase lá, seja um pouquinho mais paciente! [=

Agora precisamos configurar o arquivo “login-config.xml” do próprio JBoss. Ele fica no diretório /JBOSS_QUE_VC_DESCOMPACTOU/server/default/conf/ . Adicione as seguintes linhas nele junto com as outras tags declaradas como “application-policy”:

<!-- Others application-policy -->

<!-- ..................... -->

<!-- Security Profile Of our Software-->
<application-policy name="LoginJSFRealm">
    <authentication>
        <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
            <module-option name="dsJndiName">java:/LoginJSFRealmDS</module-option>
            <module-option name="principalsQuery">select pass from users where name=?</module-option>
            <module-option name="rolesQuery">select role_name, 'Roles' from user_roles where user_name = ?</module-option>
        </login-module>
    </authentication>
</application-policy>

Acabamos de informar o JBoss que iremos utilizar o Realm:LoginJSFRealm e precisamos informar ao JBoss agora que nossa aplicação irá utilizar esse Realm. Vamos criar um arquivo chamado “jboss-web.xml” dentro da pasta WEB-INF com o seguinte código:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <!-- Links with JBoss the Realm to use -->
    <security-domain>java:/jaas/LoginJSFRealm</security-domain>
</jboss-web>

No arquivo “login-config.xhml” informamos ao JBoss para utilizar o seguinte datasource LoginJSFRealmDS. Através desse datasource ele irá se conectar ao banco de dados. Crie um arquivo chamado “LoginJSFRealm-ds.xml”dentro da pasta /JBOSS_QUE_VC_DESCOMPACTOU/server/ default/deploy/:

<?xml version="1.0" encoding="UTF-8"?>

<datasources>
    <local-tx-datasource>
        <jndi-name>LoginJSFRealmDS</jndi-name>
        <connection-url>jdbc:postgresql://localhost:5432/LoginJSF</connection-url>
        <driver-class>org.postgresql.Driver</driver-class>
        <user-name>postgres</user-name>
        <password>YOUR_PASSWORD</password>
        <!-- sql to call when connection is created. Can be anything, select 1 is valid for PostgreSQL <new-connection-sql>select 1</new-connection-sql> -->

        <!-- sql to call on an existing pooled connection when it is obtained from pool. Can be anything, select 1 is valid for PostgreSQL <check-valid-connection-sql>select
            1</check-valid-connection-sql> -->

        <!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
        <metadata>
            <type-mapping>PostgreSQL 7.2</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

E por último, copie o Driver JDBC 4 do Postgres que você fez o download para o diretório /JBOSS_QUE_VC_DESCOMPACTOU/server/ default/lib/.

Vamos agora rodar nossa aplicação e fazer o teste final? o/
Acesse ao diretório publico primeiro: http://localhost:8080/LoginJSF/pages/public/public.html


Vamos ao diretório que apenas Admin pode acessar: http://localhost:8080/LoginJSF/pages/protected/admin/admin.html

Digite a senha e o usuário correto e pronto. Você irá logar.


E se um usuário tentasse acessar o link do admin? Faça o teste e veja o que acontece:

Tente o mesmo com o USER acessando a URL: http://localhost:8080/LoginJSF/pages/protected/user/user.html
Bem, por último e não menos importante, note no console quem apareceu por lá:

Através do filtro você irá conseguir fazer a consulta no seu banco de dados utilizando o usuário digitado e colocá-lo na sessão.

Importante 1: Você só irá passar pelo filtro após o login com sucesso feito pelo usuário. O trabalho de validar se o usuário digitou a senha correta fica por conta do JBoss! ;)

Importante 2: Caso você digite a senha correta e ele te envie para a tela de erro confira novamente os dados do JBoss como datasource,se o nome do datasource declarado no login-config é igual ao nome declarado dentro do arquivo de final “–ds.xml”, se as configurações descritas no datasource estão ok como senha, database, url de conexão.

Importante 3: Só após terminar o post percebi que eu dei o nome às páginas como html e não xhtml (user.html, admin.html, public.html). Você pode alterar e acessar novamente vendo o JSF entrar em ação (não se esqueça de acertar trocando o link e colocando xhtml no final!). Foi mals! =/

Espero que o post possa ter lhe ajudado.

Qualquer dúvida, pergunta ou sugestão basta falar.

Até a próxima pessoal! o_

User Login Validation with JAAS and JSF

Hello, how are you?

In another post I have talked about a User validation using Servlet / Filter. Today we will see another way of doing it, we will use JSF and JAAS.

The JAAS validation uses the server as tools to validate the user, his password and the role that the user has. You will not need to validate the user (programmatically) at every http request.

He are the others posts about web in my blog: JSF: Converter and Bean AutoComplete, JSF – Hello World, Auto Complete, Handling Exceptions on a WebApp, User Authentication (Filter/Servlet), Creating a WebServer.

We will use today JSF, Postgres, Eclipse and JBoss AS 6:

I will use the JBoss Tools just to help us with the JSF and allow us to use the JBoss 6 inside the Eclipse. You can create all the code posted here and put the WAR inside the JBoss folder and run it without the need of the Eclipse. In the beginning of the post I have listed a link with a “how-to” about a JSF application.

I will not show how to install the Postgres database because it is out of the scope of this post, but you can use any database that you want like MySQL, SqlServer, Oracle, etc.

To install the JBoss you just have to unzip it and it will be ready to be used.

Unzip the Eclipse and install the JBoss Tools. It is a really helpful tool when we work with JBoss and JSF.

  •   To make it easier I have selected this two options:

  • Then Finish the installation.

To install the server just need to click in the tab “Servers” > New >Server

Select JBoss 6 > Next

Select the path where you unzipped your JBoss:

And click in the “Finish” button.

Let us create a new JSF project (File > New > Project). I will use a project created by the JBoss Tools. If you want to create a JSF project by yourself, check the link in the begging of this post about JSF.

I am naming the application “LoginJSF”, select JSF 2.0 and click in Finish.

The Eclipse will ask you if you want to change to the web view, I clicked on the “No” button. You can click Yes later to see this view.

The Eclipse will create an application with a basic navigation. Notice that the Eclipse already added your application to the server, if it did not added, click with the right button of the mouse on the project name > Run As > Run on Server. Select JBoss.


Run the server and access by the link: http://localhost:8080/LoginJSF/pages/inputname.jsf

Stop the JBoss and let us create 3 folders:

  • A folder with public access
  • A folder only to admin access
  • A folder to user and admin access

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <h:head>
        I am public! [=
    </h:head>
    <h:body>
        Everyone can see me. o/
    </h:body>
</html>
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <h:head>
        Yes master admin! Welcome!
    </h:head>
    <h:body>
        Let us run this business!
    </h:body>
</html>
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <h:head>
        Welcome mister user!
    </h:head>
    <h:body>
        <br/>
        Please, do not create any bugs, ok?! >=)
    </h:body>
</html>

Now let us start the JAAS configuration. Let us start editing the web.xml file indicating which folders are protected:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
    <display-name>LoginJSF</display-name>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
        <url-pattern>*.jsf</url-pattern>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <!-- Protected Areas -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Only admins</web-resource-name>
            <url-pattern>/pages/protected/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Users and admins</web-resource-name>
            <url-pattern>/pages/protected/user/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
            <role-name>USER</role-name>
        </auth-constraint>
    </security-constraint>

    <!-- Validation By Form -->
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/pages/public/login.xhtml</form-login-page>
            <form-error-page>/pages/public/loginError.xhtml</form-error-page>
        </form-login-config>
    </login-config>

    <!-- Allowed Roles -->
    <security-role>
        <role-name>ADMIN</role-name>
    </security-role>
    <security-role>
        <role-name>USER</role-name>
    </security-role>

    <!-- Filter to get the user name and work with it -->
    <filter>
        <filter-name>LoginFilter</filter-name>
        <filter-class>com.filters.LoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>LoginFilter</filter-name>
        <url-pattern>/pages/protected/*</url-pattern>
    </filter-mapping>
</web-app>

We will need a page to login and another page to be displayed if any error happens. Take a closer look at web.xml and you will see these pages described there.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    JSF 2 Login
</h:head>
<h:body>
    <p>Login to access secure pages:</p>
    <form method="post" action="j_security_check">
        <h:panelGrid columns="2">
            <h:outputLabel for="j_username" value="Username" />
            <input type="text" id="j_username" name="j_username" />
            <h:outputLabel for="j_password" value="Password" />
            <input type="password" id="j_password" name="j_password" />
            <input type="submit" name="submit" value="Login" />
        </h:panelGrid>
        <br />
    </form>
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    JSF 2 Login
</h:head>
<h:body>
    <p>Ops! Check your user and password</p>
</h:body>
</html>

Let us create a java Filter that will catch the login (user name) that was typed. With this login (user name) you will be able to search for the user in the database and put it in the session.

package com.filters;

import java.io.IOException;

import javax.servlet.*;

import org.jboss.security.SecurityAssociation;

public class LoginFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException,
            ServletException {

        String userName = SecurityAssociation.getPrincipal().getName();

        System.out.println("Yeeey! Get me here and find me in the database: " + userName);

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

Let us create now our database tables.

I will use the simplest database possible, with only two tables and a few columns.
Create a database named LoginJSF and run the script bellow:

CREATE TABLE "users"
(
  "name" character varying(50),
  pass character varying(50)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE "users" OWNER TO postgres;

CREATE TABLE user_roles
(
  user_name character varying(50),
  role_name character varying
)
WITH (
  OIDS=FALSE
);
ALTER TABLE user_roles OWNER TO postgres;

Let us insert two users, one with the USER role and another with the ADMIN role:

INSERT INTO users("name", pass) VALUES ('user', 'user');
INSERT INTO users("name", pass) VALUES ('admin', 'admin');
INSERT INTO user_roles(user_name, role_name) VALUES ('user', 'USER');
INSERT INTO user_roles(user_name, role_name) VALUES ('admin', 'ADMIN');

We are almost there, have a little bit more of patient. [=

We need to edit the file “login-config.xml” of the JBoss. You will find this file at “/JBOSS_THAT_YOU_UNZIPPED/server/default/conf/“. Add the lines bellow near the other tags named “application-policy”:

<!-- Others application-policy -->

<!-- ..................... -->

<!-- Security Profile Of our Software-->
<application-policy name="LoginJSFRealm">
    <authentication>
        <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
            <module-option name="dsJndiName">java:/LoginJSFRealmDS</module-option>
            <module-option name="principalsQuery">select pass from users where name=?</module-option>
            <module-option name="rolesQuery">select role_name, 'Roles' from user_roles where user_name = ?</module-option>
        </login-module>
    </authentication>
</application-policy>

We just gave to the JBoss a Realm to use: LoginJSFRealm. Now we need to let the JBoss know that our app will use this Realm. Let us create a file named “jboss-web.xml” inside the folder WEB-INF with the code bellow:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <!-- Links with JBoss the Realm to use -->
    <security-domain>java:/jaas/LoginJSFRealm</security-domain>
</jboss-web>

In the file “login-config.xml” we informed the JBoss to use a datasource named “LoginJSFRealmDS”. Through this datasource the JBoss will connect to the database. Let us create a file named “LoginJSFRealm-ds.xml” inside the folder “/JBOSS_THAT_YOU_UNZIPPED/server/default/deploy/“:

<?xml version="1.0" encoding="UTF-8"?>

<datasources>
    <local-tx-datasource>
        <jndi-name>LoginJSFRealmDS</jndi-name>
        <connection-url>jdbc:postgresql://localhost:5432/LoginJSF</connection-url>
        <driver-class>org.postgresql.Driver</driver-class>
        <user-name>postgres</user-name>
        <password>YOUR_PASSWORD</password>
        <!-- sql to call when connection is created. Can be anything, select 1 is valid for PostgreSQL <new-connection-sql>select 1</new-connection-sql> -->

        <!-- sql to call on an existing pooled connection when it is obtained from pool. Can be anything, select 1 is valid for PostgreSQL <check-valid-connection-sql>select
            1</check-valid-connection-sql> -->

        <!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
        <metadata>
            <type-mapping>PostgreSQL 7.2</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

To finish this tutorial, copy the Postgres Driver JDBC 4 that you downloaded into the folder “/JBOSS_THAT_YOU_UNZIPPED/server/default/lib/“.

Let us run our application! o/

Access the public link first: http://localhost:8080/LoginJSF/pages/public/public.html

Let us access the protected area where only the admin may enter: http://localhost:8080/LoginJSF/pages/protected/admin/admin.html

Type and user and the password and it is done. You will log in.

What if a USER accesses the admin URL? What will happen?

Try the same with the USER trough the URL: http://localhost:8080/LoginJSF/pages/protected/user/user.html
We cannot forget about the message that were be displayed in our console:
With the filter you will be able to search in your database for a user with the user name typed in the login page and put the user in the session.

Note 1: Your filter will be invoked only if the user login with success. The job to validate if the password/login is correct we leave to the JBoss.

Note 2: If you type the right login/password and the error page is displayed check again the JBoss datasource data, if the datasource name declared in the login-config.xml is the same declared in the file with the “-ds.xml” in its name, if the configurations inside the datasource are correct like: password, database, connection URL.

Note 3: Just after I finish the post I notice that I named the pages with html and not with xhtml (user.html,admin.html, pubilc.html). You can rename this pages and access again to see the JSF working (do not forget to change the link also before you access, it will finish with xhtml and not html). My bad! =/

I hope this post helps you.

Any doubt, question or suggestions just post it.

See you soon! o_

JPA: Ordernando listas e utilizando Map como atributo mapeado

Olá pessoal, tudo bem?

Como ordenar uma lista em um relacionamento sem utilizar uma query? Ou então como mapear seu relacionamento dentro de um Map? Vamos ver como são simples essas funções.

Vou utilizar códigos antigos dos outros posts. Para montar um ambiente para executar o código de hoje, você poderá olhar os posts antigos: @ManyToMany Unidirecional e Bidirecional, JPA @OneToMany e @ManyToOne Unidirecional e Bidirecional, @OneToOne Unidirecional e Bidirecional, Mapeando Duas Tabelas em uma Classe, Mapeando Datas (Date) e Enum, Chave Primária Composta, SequenceGenerator, TableGenerator – Chave Primária Simples, Auto Create Schema Script com: Ant, Hibernate 3 e JPA 2, Tutorial Hibernate 3 com JPA 2.

Vou postar aqui o código da classe Customer, Dog, e Main:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    public Dog(){

    }

    public Dog(String name){
        this.name = name;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="DOG_SEQUENCE")
    private int id;

    @Column
    private String name;

    // Getters and Setters
}
package com;

import java.util.List;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name="CUSTOMER_HAS_DOGS", joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="DOG_ID", referencedColumnName="id")})
    private List dogs;

    // Getters and Setters
}
package com;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            List dogs = new ArrayList();
            dogs.add(new Dog("Terminator"));
            dogs.add(new Dog("Fluffy"));
            dogs.add(new Dog("Bacon"));

            Customer customer = new Customer();
            customer.setName("Arnold");
            customer.setDogs(dogs);

            em.persist(customer);

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Ao executar o código acima você irá criar dados necessário para poder começar o estudo de hoje.

Após uma pequena alteração na classe Main, vamos exibir os itens da lista Dog de um Customer. Veja a baixo o novo código da classe Main e o resultado impressao:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = em.find(Customer.class, 1);

            for(Dog dog : customer.getDogs()){
                System.out.println(dog.getName());
            }

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}
Unordered List

Note que os objetos da lista não estão sendo impressos em ordem alfabética do nome do cachorro. Mas, caso queiramos ordenar os resultados como faríamos?

Iremos utilizar a anotação @OrderBy que nos permite especificar o nome do campo da tabela ao qual queremos que nossa lista seja ordenada.

Basta adicionar a anotação em nossa classe Customer, e nossa lista será ordenada. Veja como ficará:

package com;

import java.util.List;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @OrderBy("name")
    @JoinTable(name="CUSTOMER_HAS_DOGS", joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="DOG_ID", referencedColumnName="id")})
    private List dogs;

    // Getters and Setters
}

Execute nossa classe Main e a ordenação irá aparecer:

Bem simples. Lógico que é apenas uma solução para caso você queira ordenar sua coleção sem ter que ter uma nativeQuery/namedQuery/Criteria para isso.

E se precisássemos colocar nossa lista dentro de um map, como fazer?

Vamos criar um atributo com o java.util.Map e a anotação @MapKey:

package com;

import java.util.List;
import java.util.Map;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = true, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name = "USER_ID", nullable = true)
    private User user;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @OrderBy("name")
    @JoinColumn(name = "CUSTOMER_ID")
    private List dogs;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "CUSTOMER_ID")
    @MapKey(name = "id")
    private Map dogsInMap;

    // Getters and Setters
}

O campo “id” é o campo que será utilizado para chave do mapa.

E para finalizar executemos a classe Main para exibir a iteração sobre nosso map:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = em.find(Customer.class, 1);

            System.out.println("Ordered List");
            for(Dog dog : customer.getDogs()){
                System.out.println(dog.getName());
            }

            System.out.println("nThe map: ");
            for(int id : customer.getDogsInMap().keySet()){
                Dog dog = customer.getDogsInMap().get(id);
                System.out.println(dog.getName());
            }

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Fiz a iteração dentro do Map buscado pela chave (key) para mostrar que o atributo da chave foi corretamente populado.

Espero que esse post possa te ajudar.

Até a próxima.
o_

JPA: Ordering a List and using a Map as mapped attributes

Hello, how are you?
How can we change the order of a mapped list of our class without a query? Or how can we map our relationship to a Map? Let us see how easy these features are to implement.
I will use the code from the other posts. To set up an environment to run the code you cantake a look from the other posts: @ManyToMany Unidirectional and Bidirectional, @OneToMany and @ManyToOne Unidirectional and Bidirectional, OneToOne Unidirectional and Bidirectional, Mapping two Tables in one Class, Mapping Date and Enum, Composite Primary-Key, SequenceGenerator, TableGenerator – Simple Primay Key, Auto Create Schema Script with: Ant, Hibernate 3 and JPA 2, Tutorial Hibernate 3 with JPA 2.

I will post here the code of the 3 classes that we will use today.Main,Dog and Customer:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    public Dog(){

    }

    public Dog(String name){
        this.name = name;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="DOG_SEQUENCE")
    private int id;

    @Column
    private String name;

    // Getters and Setters
}
package com;

import java.util.List;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name="CUSTOMER_HAS_DOGS", joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="DOG_ID", referencedColumnName="id")})
    private List dogs;

    // Getters and Setters
}
package com;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            List dogs = new ArrayList();
            dogs.add(new Dog("Terminator"));
            dogs.add(new Dog("Fluffy"));
            dogs.add(new Dog("Bacon"));

            Customer customer = new Customer();
            customer.setName("Arnold");
            customer.setDogs(dogs);

            em.persist(customer);

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

The code of the Main.java will create a relationship for you between Customer and Dog.

After asmall refactoring in the Main class we will print the items of the Dog listinside Customer class:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = em.find(Customer.class, 1);

            for(Dog dog : customer.getDogs()){
                System.out.println(dog.getName());
            }

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

 

Unordered List

Notice thatthe Dog objects are not ordered by its name. What if we wanted that list ordered by the Dog name? What should we do?

We will use the @OrderBy annotation that allows us to specify which table column from the database we will use.

Let us refactor our Customer class and add the annotation:

package com;

import java.util.List;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @OrderBy("name")
    @JoinTable(name="CUSTOMER_HAS_DOGS", joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="DOG_ID", referencedColumnName="id")})
    private List dogs;

    // Getters and Setters
}

Run theMain class again and you will see the result:


It is very easy, right? This is a solution when you want to give a order to a List without a namedQuery/nativeQuery/Criteria.What if we need our class to be mapped in a map? How can we do that?

Let us create an attribute java.util.Map inside the Customer class with the @MapKey annotation:

package com;

import java.util.List;
import java.util.Map;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = true, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name = "USER_ID", nullable = true)
    private User user;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @OrderBy("name")
    @JoinColumn(name = "CUSTOMER_ID")
    private List dogs;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "CUSTOMER_ID")
    @MapKey(name = "id")
    private Map dogsInMap;

    // Getters and Setters
}

The field“id” will be used as the map key.
Let us refactor and run the Main class to see the result:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = em.find(Customer.class, 1);

            System.out.println("Ordered List");
            for(Dog dog : customer.getDogs()){
                System.out.println(dog.getName());
            }

            System.out.println("nThe map: ");
            for(int id : customer.getDogsInMap().keySet()){
                Dog dog = customer.getDogsInMap().get(id);
                System.out.println(dog.getName());
            }

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

I used the iteration through the keys just to show that our map was with all the values.
I hope this post might help you.
See you soon.
o_