JSF Exibindo Objeto e Mensagens após Redirect

Olá, tudo bem?

Vamos falar hoje sobre como enviar uma mensagem utilizando JSF após um redirect. O redirect acaba por eliminar os dados da requisição original impossibilitando a exibição de alguma informação.

Essa situação é muito comum quando se utiliza Ajax para navegar entre páginas, e uma alternativa para forçar a troca de páginas, é utilizar o redirect.

Outros posts de JSF/Web você poderá encontrar aqui: Validação de Login de Usuário com JSF e JAASJSF: Converter and Bean AutoCompleteJSF – Hello World, AutoCompleteTratando Exceções em uma Aplicação WebAutenticação de Usuários (Filter/Servlet)Criando um WebServer.

Ao final do post você poderá fazer o download do código do post de hoje.

Vou abordar dois modos para envio de mensagem e/ou objeto após um redirect. Utilizando o Flash (API do JSF 2.0) ou então criando uma classe para controlar o os ciclos (JSF 1.2 para cima).

Abaixo, as imagens mostrando a navegação e a classe do nosso projeto.

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		return "greeting";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

Agora, faremos uma pequena alteração para que a ação de ir para página final seja através de um redirect. O código abaixo demonstra como ficará nosso faces-config.xml e o resultado dessa alteração (note a linha 14).

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

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


Onde o Peter foi parar? É isso que acontece quando utilizamos um redirect e o objeto não está na sessão. Para consertar esse “problema” vamos utilizar o não tão famoso Flash Scope.

Veja como o código da classe User ficará (repare nas linhas 38,39):

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}
}

E para acessar o objeto usuário (user), basta alterar a página:

<!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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User</ui:define>
			<ui:define name="pageHeader">Greeting Page</ui:define>
			<ui:define name="body">
				#{msgs.greeting} #{flash.user.name}!
			</ui:define>
	</ui:composition>
</html>

E o Peter voltará a aparecer.

O escopo Flash nada mais faz do que guarda o objeto como se fosse sessão. Atenção para o fato de que após a sua utilização o objeto “user” será removido do escopo. Como manter o objeto no Flash Scope mesmo após um redirect? Fácil, basta você trocar o comando

      #{flash.user.name}

por

      #{flash.keep.user.name}

Desse modo o objeto usuário irá continuar dentro do Flash Scope.

Para JSF 1.2: Infelizmente para se manter um objeto em uma requisição após um redirect no JSF 1.2 (até o dia de hoje), será necessário fazer o controle do objeto na sessão. =/ MAS, caso você queira enviar apenas um alerta para a próxima tela existe outra solução.

Vou criar uma nova página para exibir uma mensagem em forma de alerta “<h:messages/>“. Veja como nosso código ficará:

<!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:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ez="http://java.sun.com/jsf/composite/demo">

	<ui:composition template="/templates/common.xhtml">

		<ui:define name="pageTitle">Input User Name</ui:define>

		<ui:define name="pageHeader">JSF 2 Hello Application</ui:define>

		<ui:define name="body">
			<h:message showSummary="true" showDetail="false" style="color: red; font-weight: bold;" for="inputname" />
			<ez:input id="inputname" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHello}" submitlabel="Say Hello"/>
			<ez:input id="inputname2" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHelloWithAlert}" submitlabel="Say Hello With Alert"/>
		</ui:define>
	</ui:composition>
</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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User With alert</ui:define>
			<ui:define name="pageHeader">Greeting Page With alert</ui:define>
			<ui:define name="body">
				<h:messages />
			</ui:define>
	</ui:composition>
</html>
/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}

	public String sayHelloWithAlert() {
		String message = "Hello : " + name;
		FacesContext context = FacesContext.getCurrentInstance();

		context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));

		return "greetingWithAlert";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


E caso utilizemos o redirect a mensagem nossa mensagem não será exibida. Veja o que acontece:

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

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


A solução abaixo eu encontrei no site: “ocpsoft.com/java/persist-and-pass-facesmessages-over-page-redirects/”.

O que a classe faz é adicionar sua mensagem ao contexto de sessão, exibe e depois remove a mensagem da sessão. Uma solução bem útil quando se quer prolongar as mensagens.

package demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * Enables messages to be rendered on different pages from which they were set.
 *
 * After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 *
 * Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 *
 * Only global messages, not associated with a particular component, are
 * moved. Component messages cannot be rendered on pages other than the one on
 * which they were added.
 *
 * To enable multi-page messages support, add a <code>lifecycle</code> block to your
 * faces-config.xml file. That block should contain a single
 * <code>phase-listener</code> block containing the fully-qualified classname
 * of this file.
 *
 * @author Jesse Wilson jesse[AT]odel.on.ca
 * @secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com
 */
public class MultiPageMessagesSupport implements PhaseListener
{

    private static final long serialVersionUID = 1250469273857785274L;
    private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

    public PhaseId getPhaseId()
    {
        return PhaseId.ANY_PHASE;
    }

    /*
     * Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
     * have arrived here and the response is already complete, then the page is
     * not going to show up: don't display messages yet.
     */
    // TODO: Blog this (MultiPageMessagesSupport)
    public void beforePhase(final PhaseEvent event)
    {
        FacesContext facesContext = event.getFacesContext();
        this.saveMessages(facesContext);

        if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            if (!facesContext.getResponseComplete())
            {
                this.restoreMessages(facesContext);
            }
        }
    }

    /*
     * Save messages into the session after every phase.
     */
    public void afterPhase(final PhaseEvent event)
    {
        if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            FacesContext facesContext = event.getFacesContext();
            this.saveMessages(facesContext);
        }
    }

    @SuppressWarnings("unchecked")
    private int saveMessages(final FacesContext facesContext)
    {
        List<FacesMessage> messages = new ArrayList<FacesMessage>();
        for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
        {
            messages.add(iter.next());
            iter.remove();
        }

        if (messages.size() == 0)
        {
            return 0;
        }

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
        if (existingMessages != null)
        {
            existingMessages.addAll(messages);
        }
        else
        {
            sessionMap.put(sessionToken, messages);
        }
        return messages.size();
    }

    @SuppressWarnings("unchecked")
    private int restoreMessages(final FacesContext facesContext)
    {
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);

        if (messages == null)
        {
            return 0;
        }

        int restoredCount = messages.size();
        for (Object element : messages)
        {
            facesContext.addMessage(null, (FacesMessage) element);
        }
        return restoredCount;
    }
}

E por último precisamos alterar o “faces-config.xml”:

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

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>
	<lifecycle>
		<phase-listener>demo.MultiPageMessagesSupport</phase-listener>
	</lifecycle>	

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

E ao executar novamente nosso sistema, veja quem voltou… Peter! o/

Para realizar o download do código fonte do post, clique aqui. Para executar o código você precisará ter o JBoss 6 configurado em seu Eclipse, para isso, terá que instalar o JBoss Tools. Nesse post, a instalação e configuração são abordadas: User Login Validation with JAAS and JSF.

Espero que o post de hoje possa te ajudar.

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

Esse post foi baseado:

  • Livro: “Core JavaServer Faces” Capítulo: “Redirection and Flash”
  • Código gerado pela ferramenta JBoss Tools
  • Código da classe de mensagem persistida “ocpsoft.com/”

Até a próxima! o_

JSF Persisting Objects and Messages after a Redirect

Hello, how are you?

Let us talk today about how to send messages with JSF after a redirect action. The redirect action will not carry any data from the first request; sometimes you will not have the value to be displayed.

We often see this happen in an application with a heavy use of Ajax; sometimes to navigate between the pages you will need to use the redirect command.

The other posts of JSF/Web you can find in here: User Login Validation with JAAS and JSFJSF: Converter and Bean AutoComplete, JSF – Hello World, Auto Complete, Handling Exceptions on a WebApp, User Authentication (Filter/Servlet), Creating a WebServer.

In the end of the post you will find the link to download the source code of today’s post.

Today we will see two ways to send an object and/or a message after a redirect. We will use the Flash Scope (JSF 2.0 API) or creating a class to control the JSF phases (works on JSF 1.2 or a higher version).

Bellow you will see some images of the project screen shots and a few codes:

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		return "greeting";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

I will edit now the “faces-config.xml”; we will use the redirect to navigate between the pages. The code bellow will show how our file will be (take a look at the line 14):

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

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


Where is Peter? That is what happens when we use the redirect and the object is not in the session scope. To fix this “problem” we will use the not so popular Flash Scope.

Let us edit the User class (take a look at the lines 38, 39):

/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}
}

And to access the user object in the Flash Scope, you just have to edit your page:

<!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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User</ui:define>
			<ui:define name="pageHeader">Greeting Page</ui:define>
			<ui:define name="body">
				#{msgs.greeting} #{flash.user.name}!
			</ui:define>
	</ui:composition>
</html>

Now we will be able to meet Peter again:

The Flash scope keeps the object just like if it was a session scope. Pay attention to the fact that after you use the object “user” that is stored inside the flash scope, the object will be removed. How can you keep an object inside the Flash Scope after we use it? Piece of cake, just edit the invocation:

      #{flash.user.name}

For

      #{flash.keep.user.name}

If you use the last line your object will remain inside the Flash Scope.

To JSF 1.2: Unfortunately the only way to keep an object / message after a redirect (until today) is controlling it inside the session. =/ BUT, if you want just to send an alert (message) to the next screen there is another solution.

I will create another page to display the welcome message as an alert “<h:messages/>”. Take a look at our new code:

<!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:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ez="http://java.sun.com/jsf/composite/demo">

	<ui:composition template="/templates/common.xhtml">

		<ui:define name="pageTitle">Input User Name</ui:define>

		<ui:define name="pageHeader">JSF 2 Hello Application</ui:define>

		<ui:define name="body">
			<h:message showSummary="true" showDetail="false" style="color: red; font-weight: bold;" for="inputname" />
			<ez:input id="inputname" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHello}" submitlabel="Say Hello"/>
			<ez:input id="inputname2" label="${msgs.prompt}" value="#{user.name}" action="#{user.sayHelloWithAlert}" submitlabel="Say Hello With Alert"/>
		</ui:define>
	</ui:composition>
</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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

	<ui:composition template="/templates/common.xhtml">
			<ui:define name="pageTitle">Greeting to User With alert</ui:define>
			<ui:define name="pageHeader">Greeting Page With alert</ui:define>
			<ui:define name="body">
				<h:messages />
			</ui:define>
	</ui:composition>
</html>
/*******************************************************************************
 * Copyright (c) 2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package demo;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

import com.sun.faces.context.flash.ELFlash;

/**
 * Created by JBoss Tools
 */
@ManagedBean(name="user")
@RequestScoped
public class User {
	private String name;

	public User() {
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String sayHello() {
		// Put the object inside the flash scope
		ELFlash.getFlash().put("user", this);
		return "greeting";
	}

	public String sayHelloWithAlert() {
		String message = "Hello : " + name;
		FacesContext context = FacesContext.getCurrentInstance();

		context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, message));

		return "greetingWithAlert";
	}
}
<?xml version="1.0" encoding="UTF-8"?>

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


But, if we use the redirect action our message will not be displayed:

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

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>


The solution bellow I found in the site: “ocpsoft.com/java/persist-and-pass-facesmessages-over-page-redirects/”.

The class bellow will get your message and store it in the session scope. Once the message was displayed it will be removed from the session scope. This is a very useful solution to prolong the scope of a message:

package demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * Enables messages to be rendered on different pages from which they were set.
 *
 * After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 *
 * Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 *
 * Only global messages, not associated with a particular component, are
 * moved. Component messages cannot be rendered on pages other than the one on
 * which they were added.
 *
 * To enable multi-page messages support, add a <code>lifecycle</code> block to your
 * faces-config.xml file. That block should contain a single
 * <code>phase-listener</code> block containing the fully-qualified classname
 * of this file.
 *
 * @author Jesse Wilson jesse[AT]odel.on.ca
 * @secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com
 */
public class MultiPageMessagesSupport implements PhaseListener
{

    private static final long serialVersionUID = 1250469273857785274L;
    private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

    public PhaseId getPhaseId()
    {
        return PhaseId.ANY_PHASE;
    }

    /*
     * Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
     * have arrived here and the response is already complete, then the page is
     * not going to show up: don't display messages yet.
     */
    // TODO: Blog this (MultiPageMessagesSupport)
    public void beforePhase(final PhaseEvent event)
    {
        FacesContext facesContext = event.getFacesContext();
        this.saveMessages(facesContext);

        if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            if (!facesContext.getResponseComplete())
            {
                this.restoreMessages(facesContext);
            }
        }
    }

    /*
     * Save messages into the session after every phase.
     */
    public void afterPhase(final PhaseEvent event)
    {
        if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            FacesContext facesContext = event.getFacesContext();
            this.saveMessages(facesContext);
        }
    }

    @SuppressWarnings("unchecked")
    private int saveMessages(final FacesContext facesContext)
    {
        List<FacesMessage> messages = new ArrayList<FacesMessage>();
        for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
        {
            messages.add(iter.next());
            iter.remove();
        }

        if (messages.size() == 0)
        {
            return 0;
        }

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
        if (existingMessages != null)
        {
            existingMessages.addAll(messages);
        }
        else
        {
            sessionMap.put(sessionToken, messages);
        }
        return messages.size();
    }

    @SuppressWarnings("unchecked")
    private int restoreMessages(final FacesContext facesContext)
    {
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);

        if (messages == null)
        {
            return 0;
        }

        int restoredCount = messages.size();
        for (Object element : messages)
        {
            facesContext.addMessage(null, (FacesMessage) element);
        }
        return restoredCount;
    }
}

And as our last action, let us edit the “faces-config.xml”:

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

<faces-config
    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-facesconfig_2_0.xsd"
	version="2.0">

	<navigation-rule>
		<from-view-id>/pages/inputname.xhtml</from-view-id>
		<navigation-case>
			<from-outcome>greeting</from-outcome>
			<to-view-id>/pages/greeting.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
		<navigation-case>
			<from-outcome>greetingWithAlert</from-outcome>
			<to-view-id>/pages/greetingWithAlert.xhtml</to-view-id>
			<redirect/>
		</navigation-case>
	</navigation-rule>
	<lifecycle>
		<phase-listener>demo.MultiPageMessagesSupport</phase-listener>
	</lifecycle>	

    <application>
        <resource-bundle>
            <base-name>resources</base-name>
            <var>msgs</var>
        </resource-bundle>
    </application>
</faces-config>

Deploy the application again and guess who is back? Peter! o/

If you want to download the source code of this post, click here. To run this code, you will need to have the JBoss 6 configured in your Eclipse. But to set up the JBoss 6 in you Eclipse you will need the JBoss tools. If you need some help to install the JBoss Tools check this post: User Login Validation with JAAS and JSF.

I hope this post might help you.

If you have any question or comment just post it.

This post was based on:

  • Book: “Core JavaServer Faces” Chapter: “Redirection and Flash”
  • JSF source code generated by the JBoss tools
  • The class to persist the message through the redirect: “ocpsoft.com/”

See you soon! o_

JPA Single Table per Class Hierarchy

THERE IS A NEW VERSION OF THIS POST.
CLICK HERE: http://uaihebert.com/?p=1674&page=13

Hello, how are you?

Let us talk today about how to persist in the database table classes with hierarchy. We will start with the “Single Table per Class Hierarchy” mode.

The others posts about JPA you can find in here: @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.

In the end of this post you will find the source code of this post.

We will use a car project where we will have a Main class do compile and other two class extending a super class. Let us see the initial code:

package com.model;

public abstract class Car {
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}

}
package com.model;

public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

We have a simple model with three classes only; we will need to persist our class’s data. Today I will use the Postgres database as database, just to display how our data will be stored in the database.

Let us edit our model classes and add the JPA annotations:

package com.model;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FROM_CLASS", discriminatorType=DiscriminatorType.STRING)
public abstract class Car {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

I will talk a little bit about the code above:

  • The “@Inheritance” indicates to the JPA which model of hierarchy persistence we will use.
  • The “@DiscriminatorColumn” it is used to store a column name that will describe to which column that data line belongs to.

With the class bellow we will insert the data in the database. Run it and let us take a look in the result:

package com;

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

import com.model.Beetle;
import com.model.Ferrari;

public class Main {

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

		em.getTransaction().begin();

		try {
			Ferrari ferrari = new Ferrari();
			ferrari.setModel(455);
			ferrari.setName("WWJD");

			Beetle beetle = new Beetle();
			beetle.setGasCapacity(46);
			beetle.setName("Heeerbie");

			em.persist(ferrari);
			em.persist(beetle);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			em.getTransaction().commit();
			emf.close();
		}

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

}


Notice that in our table appeared a column named “from_clas” that indicates to which class that line belongs to.

To finish our post, what could we do to change that column value? What if we want to change how the name of our class will be persisted? What if we want CAR_FERRARI instead of Ferrari?

Let us edit our class and leave it like the code below:

package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_BEETLE")
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_FERRARI")
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Run the Main class again and check the result:

Take a closer look and notice that the line 1 and 2 hold the older values; lines 3 and 4 already has the new values that we defined in the annotation “@DiscriminatorValue”.

Let us talk now about the Advantages and Disadvantages:

  • Advantages: The system will have a better performance; the JPA will not need to do join in all classes tables to gather the information. To those developers that are not familiar with this kind of persistence it will be easy to check the data in the tables.
  • Disadvantages: Your subclasses can not have any “not null” attributes. If the Beetle class has a not null attribute and you call the persist method on the Ferrari object, the database would raise an Exception.

Click here to download the code from this post. To run the code you will need the Postgres database running with a schema named SingleTable. Do not forget to change the user/password. ^o^

I hope this post can be useful to you.

If you have any question/comment just post it.

See you soon. o_

JPA Uma tabela por herança

EXISTE UMA NOVA VERSÃO DESSE POST.
CLICK AQUI: http://uaihebert.com/?p=1622&page=13

Olá, tudo bem?

Vamos falar hoje sobre como salvar nas tabelas que envolvem herança. Vamos começar pelo modo “Single Table per Class Hierarchy” (Uma tabela para toda herança).

Os outros assuntos já tratados sobre JPA: Ordernando listas e utilizando Map como atributo mapeado, @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.

Ao final do post de hoje você irá encontrar o código fonte para download.

Vamos utilizar um sistema de carros onde teremos a classe principal e outras duas classes herdando da classe pai. Vejamos nosso código inicial:

package com.model;

public abstract class Car {
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}

}
package com.model;

public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Temos um modelo bem simples onde queremos persistir todas as informações em uma tabela. No exemplo de hoje irei utilizar o Postgres como banco de dados para exibir no final como os dados ficarão gravados no banco de dados.

Coloquemos as anotações JPA para que a herança seja persistida:

package com.model;

import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name="CAR")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="FROM_CLASS", discriminatorType=DiscriminatorType.STRING)
public abstract class Car {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.Entity;

@Entity
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Algumas explicações sobre o código acima:

  • A anotação “@Inheritance” indica ao JPA qual o tipo de herança iremos utilizar ao persistir as informações.
  • A anotação “@DiscriminatorColumn” é uma coluna onde ficará armazenado um valor descrevendo a qual classe pertence aquela linha.

Com a classe abaixo, vamos inserir dados no banco de dados e ver como ficou.

package com;

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

import com.model.Beetle;
import com.model.Ferrari;

public class Main {

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

		em.getTransaction().begin();

		try {
			Ferrari ferrari = new Ferrari();
			ferrari.setModel(455);
			ferrari.setName("WWJD");

			Beetle beetle = new Beetle();
			beetle.setGasCapacity(46);
			beetle.setName("Heeerbie");

			em.persist(ferrari);
			em.persist(beetle);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			em.getTransaction().commit();
			emf.close();
		}

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

}


Repare que na tabela apareceu uma coluna chamada “from_class” que indica à qual classe pertence à respectiva linha.

E para finalizar, como faríamos para alterar o valor salvo na no coluna? E se quisemos mudar o nome de como o valor da classe será salvo? Ao invés de Ferrari for CAR_FERRARI, como faríamos?

Basta editar a classe e deixar conforme o código abaixo:

package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_BEETLE")
public class Beetle extends Car {
	private int gasCapacity;

	public void talk(){
		System.out.println("Herbie!");
	}

	public int getGasCapacity() {
		return gasCapacity;
	}

	public void setGasCapacity(int gasCapacity) {
		this.gasCapacity = gasCapacity;
	}
}
package com.model;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value="CAR_FERRARI")
public class Ferrari extends Car {
	private int model;

	// The newest new models will fly... some day! =P
	public void fly() {

	}

	public int getModel() {
		return model;
	}

	public void setModel(int model) {
		this.model = model;
	}
}

Execute a classe Main novamente e veja o resultado:


Note que as linhas 1 e 2 estão com os valores antigos. As linhas 3 e 4 já constam os valores da nova anotação inserida “@DiscriminatorValue”.

Vamos falar agora dos contras e prós dessa abordagem:

  • Prós: O desempenho é maior e melhor, pois o JPA não irá necessitar fazer diversos Joins para ter as informações de todas as classes. Para quem está começando a trabalhar com esse tipo de persistência é mais fácil de adaptar e coletar informações no banco de dados.
  • Contras: As subclasses não poderão conter campos “não nullos”. Se a classe Beetle tivesse um atributo não nulo ao se salvar um objeto Ferrari o banco de dados levantaria uma Exception.

Clique aqui para fazer o download do código de hoje. Esteja atento ao fato que para o código rodar você precisará ter o Postgres instalado e com um “database” chamado “SingleTable”. E não esqueça também de configurar o usuário e senha. ^o^

Espero que o post de hoje possa te ajudar.

Qualquer dúvida/colocação basta postar.

Até mais! o_

Cobrindo seus testes com Cobertura, JUnit, HSQLDB, JPA

Olá tudo bem?

Vamos falar hoje sobre uma ferramenta muito importante chamada Cobertura. Esse framework tem a mesma função de cobertura de testes que o framework Emma, já mostrado aqui.

A diferença é que a tela inicial do framework Cobertura nos mostra os dados de um modo mais gráfico.

Caso você queira ver os outros posts sobre esse assunto,basta clicar nos links a seguir: Cobertura de testes com JUnit Ant e Emma, JUnit com HSQLDB, JPA e Hibernate, Primeiros passos.

Vou utilizar exatamente o código do post JUnit com HSQLDB, JPA e Hibernate. Caso você queira montar o ambiente corretamente, basta fazer o passo a passo que se encontra lá (no fim do post você irá encontrar o código fonte para download).
Eu não sou um usuário habilidoso do ant, você poderá ver algum código e pensar: “ele poderia ter usado o comando xyk”. Fique a vontade para sugerir. ;)

Vamos fazer o download da biblioteca do framework CoberturaDownload1.9.4.1Faça o download e coloque todos os arquivos jars (/cobertura.jar, /lib/* que tem os arquivos asm, jakarta, log4j) dentro na pasta lib do nosso projeto.

Na raiz do seu projeto crie um arquivo chamado “build.xml”com o seguinte código (tem que estar exatamente na raiz do projeto para funcionar):

<project name="Cobertura Coverage" basedir=".">

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

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

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

</project>

No código acima, estamos apenas declarando os caminhos para o código fonte e as bibliotecas.

Agora vamos criar uma tarefa que irá apagar arquivos criados pelo framework Cobertura e pela compilação do nosso projeto.

<!-- Clears the paths -->
<target name="01-CleannUp" description="Remove all generated files.">
    <delete dir="${build.dir}" />
    <delete file="cobertura.ser" />
    <delete dir="${report.dir}" />

    <mkdir dir="${build.dir}" />
    <mkdir dir="${report.dir}" />
</target>

E para compilar seu código Java, adicione o código abaixo e depois execute para ter a certeza de que está compilando:

<!-- Compiles the Java code -->
<target name="02-Compile" depends="01-CleannUp" description="invoke compiler">
    <javac debug="true" debuglevel="vars,lines,source" srcdir="${src.dir}" destdir="${build.dir}">
        <classpath refid="project.classpath" />
    </javac>
    <copy file="${src.dir}/META-INF/persistence.xml" todir="${build.dir}/META-INF" />
</target>

Vamos configurar o framework Cobertura para que ele possa instrumentar as classes de testes e já preparar o ambiente:

<!-- Cobertura configs -->
<property name="cobertura.instrumented-classes.dir" value="${report.dir}/instrumented-classes" />
<property name="cobertura.data.file" value="cobertura.ser" />
<path id="cobertura.classpath">
    <fileset dir="${lib.dir}" includes="/*.jar" />
</path>

<!-- Points to the cobertura jar -->
<taskdef classpath="${lib.dir}/cobertura.jar" resource="tasks.properties" classpathref="cobertura.classpath" />

<!-- Instruments the classes -->
<target name="03-Instrument" depends="02-Compile">
    <delete quiet="false" failonerror="false">
        <fileset dir="${cobertura.instrumented-classes.dir}" />
    </delete>
    <delete file="${cobertura.data.file}" />
    <cobertura-instrument todir="${cobertura.instrumented-classes.dir}">
        <fileset dir="${build.dir}">
            <include name="**/*.class" />
            <exclude name="**/*Test.class" />
        </fileset>
    </cobertura-instrument>
    <copy todir="${cobertura.instrumented-classes.dir}">
        <fileset dir="${src.dir}" casesensitive="yes">
            <patternset id="resources.ps" />
        </fileset>
    </copy>
</target>

Adicione o código abaixo e vamos executar o JUnit pelo “ant.xml”:

<!-- Set up the instrumented classes path -->
<path id="cover-test.classpath">
    <fileset dir="${lib.dir}" includes="**/*.jar" />
    <pathelement location="${cobertura.instrumented-classes.dir}" />
    <pathelement location="${build.dir}" />
</path>

<!-- Run the JUnit test -->
<target name="04-RunTest" depends="03-Instrument" >
    <junit printsummary="yes" haltonerror="no" haltonfailure="no"  fork="yes">
        <batchtest>
            <fileset dir="${build.dir}" includes="**/*Test.class" />
        </batchtest>
        <classpath refid="cover-test.classpath" />
    </junit>
    <delete file="transaction.log" />
</target>

E por último, vamos executar gerar nosso relatório. Adicione o código abaixo ao “build.xml” e execute a task.

<!-- Creates the Cobertura report -->
<target name="00-CreateReport" depends="04-RunTest">
    <cobertura-report srcdir="${cobertura.data.file}" destdir="${report.dir}">
        <fileset dir="${src.dir}">
            <include name="**/*.java" />
        </fileset>
    </cobertura-report>
    <delete dir="${report.dir}/instrumented-classes" />
    <delete file="cobertura.ser"  />
</target>

Atualize seu projeto no Eclipse (F5 no projeto) e você verá que os arquivos do relatório foram criados com sucesso. Abra o arquivo “index.html” que estará dentro da pasta cobertura.




O framework Cobertura inclusive mostra quantas vezes você tem que testar um método. Como desafio, escreva o método equals da classe Dog,gere o relatório só para você conferir que não está sendo coberto. E crie seus testes e olhe depois a cobertura do seu método como ficou.

Você poderá fazer o download do código do post de hoje.

Qualquer dúvida/comentário basta postar.

Até a próxima! o_

Covering your tests with Cobertura, JUnit, HSQLDB, JPA

Hello, how are you?

Let us talk today about a very useful tool named “Cobertura”. This framework has the same functions of the Emma framework that we saw in another post.

The major difference between Cobertura and Emma is that Cobertura displays the resume page with graphics.

If you want to see the other topics about this subject click on the links: Coverage of tests with JUnit Ant and Emma // JUnit with HSQLDB, JPA and Hibernate // TDD – First Steps.

I will use the same code from this post (JUnit with HSQLDB, JPA and Hibernate); if you want to set up an environment to run the code you can follow the steps that you will find there (you will find the source code to download at the end of this article).
I am not a skilful Ant user and you may see some code and think “this script is not good”. Fell free to give me ideas of how to “upgrade” the ant code. ;)

Let us do the download of the Cobertura libraries – Download1.9.4.1

After the download finishes put the jar files (/cobertura.jar, /lib/* that includes asm, jakarta, log4j) inside the lib folder of our project.

In the root source of our project create a file named “build.xml” with the following code (the created file needs to be in the root of your project or your project will not work):

<project name="Cobertura Coverage" basedir=".">

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

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

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

</project>

In the code above we are creating the paths to the source codes and to the libraries. Let us create a task to delete the files generated by the Cobertura and the compilated Java source code.

<!-- Clears the paths -->
<target name="01-CleannUp" description="Remove all generated files.">
    <delete dir="${build.dir}" />
    <delete file="cobertura.ser" />
    <delete dir="${report.dir}" />

    <mkdir dir="${build.dir}" />
    <mkdir dir="${report.dir}" />
</target>

To compile our source code, add the code bellow and run the task (be sure that the code will compile):

<!-- Compiles the Java code -->
<target name="02-Compile" depends="01-CleannUp" description="invoke compiler">
    <javac debug="true" debuglevel="vars,lines,source" srcdir="${src.dir}" destdir="${build.dir}">
        <classpath refid="project.classpath" />
    </javac>
    <copy file="${src.dir}/META-INF/persistence.xml" todir="${build.dir}/META-INF" />
</target>

Let us setup the Cobertura so it may instrument the tests class and get the environment ready:

<!-- Cobertura configs -->
<property name="cobertura.instrumented-classes.dir" value="${report.dir}/instrumented-classes" />
<property name="cobertura.data.file" value="cobertura.ser" />
<path id="cobertura.classpath">
    <fileset dir="${lib.dir}" includes="/*.jar" />
</path>

<!-- Points to the cobertura jar -->
<taskdef classpath="${lib.dir}/cobertura.jar" resource="tasks.properties" classpathref="cobertura.classpath" />

<!-- Instruments the classes -->
<target name="03-Instrument" depends="02-Compile">
    <delete quiet="false" failonerror="false">
        <fileset dir="${cobertura.instrumented-classes.dir}" />
    </delete>
    <delete file="${cobertura.data.file}" />
    <cobertura-instrument todir="${cobertura.instrumented-classes.dir}">
        <fileset dir="${build.dir}">
            <include name="**/*.class" />
            <exclude name="**/*Test.class" />
        </fileset>
    </cobertura-instrument>
    <copy todir="${cobertura.instrumented-classes.dir}">
        <fileset dir="${src.dir}" casesensitive="yes">
            <patternset id="resources.ps" />
        </fileset>
    </copy>
</target>

Add the code bellow and you will be able to execute the tests with the JUnit through the ant:

<!-- Set up the instrumented classes path -->
<path id="cover-test.classpath">
    <fileset dir="${lib.dir}" includes="**/*.jar" />
    <pathelement location="${cobertura.instrumented-classes.dir}" />
    <pathelement location="${build.dir}" />
</path>

<!-- Run the JUnit test -->
<target name="04-RunTest" depends="03-Instrument" >
    <junit printsummary="yes" haltonerror="no" haltonfailure="no"  fork="yes">
        <batchtest>
            <fileset dir="${build.dir}" includes="**/*Test.class" />
        </batchtest>
        <classpath refid="cover-test.classpath" />
    </junit>
    <delete file="transaction.log" />
</target>

As our last action, let us create the report using the Cobertura. Add the code bellow to your “build.xml” and execute the task:

<!-- Creates the Cobertura report -->
<target name="00-CreateReport" depends="04-RunTest">
    <cobertura-report srcdir="${cobertura.data.file}" destdir="${report.dir}">
        <fileset dir="${src.dir}">
            <include name="**/*.java" />
        </fileset>
    </cobertura-report>

    <delete dir="${report.dir}/instrumented-classes" />
    <delete file="cobertura.ser"  />
</target>

Update your Eclipse project (press F5 in the project) and you will see that the reports were created with success. Open the “index.html” file that it is inside the “cobertura” folder.



The Cobertura framework helps us calculating how many times we should test a method. As homework write the Dog equals method, create the report so you may see that your equals is not covered. Create your tests and run the report again.

You can download the source code from our project in here.

If you have any doubts or questions, just write it bellow.

See you soon. o_

Lei de Demeter

Olá pessoal, tudo bem?

Hoje vamos falar sobre a Lei de Demeter. É um pattern para orientação objeto que nos ajuda e muito a diminuir o acoplamento, aumentar a manutenibilidade e adaptabilidade de nossos sistemas.

Onde essas palavras difíceis nos ajuda? Ao dar manutenção,sua aplicação não irá sofrer tanto impacto, suas classes irão conhecer apenas a quem deve e com isso as alterações serão mais rápidas e com menos impacto no sistema.

Tudo de bom não é mesmo? Calma aí que depois nós vamos chegar à desvantagem dessa abordagem! (:

Para ver outro post sobre OO basta clicar no link: “Diga, não pergunte!“.

Olhe o código abaixo, em que ele pode ser melhorado?

package com;

public class Main {

    public static void main(String[] args) {
        IAddress address = new Address();
        address.setName("01");
        address.setZipCode("000001");

        IHouse house = new House();
        house.setAddress(address);

        IPerson person = new Person();
        person.setHouse(house);

        // Print the person zip code
        System.out.println(person.getHouse().getAddress().getZipCode());
    }
}

O código acima executa tranquilamente, estamos programando para Interface, nosso código está bem identado e formatado. O que mais poderia melhorar em nosso código?

A Lei de Demeter prega que uma classe não deve conhecer mais do que a quem está sendo invocado. HEIN?! Vamos por partes; repare nossa classe que nossa classe Main quer imprimir o CEP da pessoa, mas para fazer isso a classe Main toma conhecimento de outras duas classes. Caso você não tenha percebido, aí está seu acoplamento!

Repare a classe Main está passando por dentro da classe Person, House e finalmente Address.
Qual a parte ruim dessa história? Imagine que por uma determinação da empresa, a classe Address deixa de existir e a classe House será a responsável por conter o ZipCode.

Em nosso exemplo, será muito fácil fazer essa alteração.Imagine um sistema enorme onde o comando para buscar o ZipCode fosse utilizado quase 100 vezes. Você teria que alterar em 100 lugares diferentes por causa dessa alteração.

A lei de Demeter veio para nos ajudar nesse ponto, com uma pequena alteração em nossa classe Person e House, nós poderemos evitar esse enorme impacto na hora da remoção da classe Address. Veja como nosso código ficará:

package com;

public class Main {

    public static void main(String[] args) {
        IAddress address = new Address();
        address.setName("01");
        address.setZipCode("000001");

        IHouse house = new House();
        house.setAddress(address);

        IPerson person = new Person();
        person.setHouse(house);

        // Print the person zip code
        System.out.println(person.getZipCode());
    }
}
package com;

public interface IPerson {

    void setHouse(IHouse house);

    IHouse getHouse();

    String getZipCode();

}
package com;

public class Person implements IPerson {
    
    private IHouse house;

    @Override
    public void setHouse(IHouse house) {
        this.house = house;
    }

    @Override
    public IHouse getHouse() {
        return house;
    }

    @Override
    public String getZipCode() {
        return house.getZipCode();
    }
}
package com;

public interface IHouse {

    void setAddress(IAddress address);

    IAddress getAddress();

    String getZipCode();

}
package com;

public class House implements IHouse {

    private IAddress address;

    @Override
    public void setAddress(IAddress address) {
        this.address = address;
    }

    @Override
    public IAddress getAddress() {
        return address;
    }

    @Override
    public String getZipCode() {
        return address.getZipCode();
    }
}
package com;

public interface IAddress {

    void setName(String string);

    void setZipCode(String string);

    String getZipCode();

    public abstract String getName();

}
package com;

public class Address implements IAddress {

    private String name;
    private String zipCode;

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getZipCode() {
        return zipCode;
    }
}

Olhando pelo novo código, imagine qual seria o impacto em nosso código para remover a classe Address. Apenas a classe Home seria alterada, o restante de nosso sistema permaneceria intacto.

Essa é a grande vantagem da Lei de Demeter. Na hora da manutenção seu projeto inteiro terá o menor impacto possível. As adaptações solicitadas pelos responsáveis serão mais simples, menos custosas. No exemplo de hoje, apenas uma classe seria afetada. As demais classes do seu sistema continuariam não sendo afetada se seu sistema continuará com baixo acoplamento.

A desvantagem dessa abordagem é o impacto sobre o desempenho. Você poderia ter uma queda de desempenho caso utilize essa abordagem em um loop como “While, For, …”. Nesse caso, você terá que ver em quais partes do sistema não haveria impacto ao aplicar a lei de Demeter.

Creio que mesma com essa desvantagem sobre o desempenho em alguns lugares localizados, essa abordagem vale a pena ser aplicada em nossos sistemas; a lei de Demeter pode ser utilizada em quase todo o sistema, sendo deixada de lado apenas em locais críticos.

Espero que este post possa te ajudar.

Qualquer dúvida/colocação basta postar.

Até a próxima! o_

Demeter Law

Hello, how are you?

Let us talk today about the Demeter Law. It is a pattern of Object Orientation that helps us to lower our coupling, decrease our maintenance impact and the raise adaptability of our systems.

What is utility for those “weird” words? If you have to do any maintenance in your application, it will have a lesser impact; your classes will only know the classes that it should know, and your code changes will be quicker and with less impact in your system.

Just advantages in the Demeter law, right? Let us take it easy; in the end of this post we will see the disadvantage of this approach.

If you want to see another post about OO just click in the link: “Tell, do not ask!

Take a look in the code bellow, what could we do to make it better?

package com;

public class Main {

    public static void main(String[] args) {
        IAddress address = new Address();
        address.setName("01");
        address.setZipCode("000001");

        IHouse house = new House();
        house.setAddress(address);

        IPerson person = new Person();
        person.setHouse(house);

        // Print the person zip code
        System.out.println(person.getHouse().getAddress().getZipCode());
    }
}

The code above will run as expected; we are coding to interface, our code is wellindented and well formatted. What could we do to “upgrade” our code?

The Demeter law says that a class may not know more then one friendly class. WHAT? Let us take small steps and analyze the code above; notice that our Main class wants to print the Person ZipCode, but to do this the Main class get to know two more classes. If you did not noticed, there is a coupling there.

To print de ZipCode our class Main is going through the Person, House and finally Address class. What is this a bad approaching? Imagine if out Annalist decide to remove our Address class from the system and the House class will be responsible to keep the ZipCode.

In our cod ewill be very easy to change; but imagine now if we had a huge system with the ZipCode printed for more than 100 code lines. You would have to change 100 lines of codes at your system.

The Demeter law came to help us with this kind of situation, with a little change in our Person and House classes; we can avoid this huge impact when we remove the Address class. Take a look in our new code.

package com;

public class Main {

    public static void main(String[] args) {
        IAddress address = new Address();
        address.setName("01");
        address.setZipCode("000001");

        IHouse house = new House();
        house.setAddress(address);

        IPerson person = new Person();
        person.setHouse(house);

        // Print the person zip code
        System.out.println(person.getZipCode());
    }
}
package com;

public interface IPerson {

    void setHouse(IHouse house);

    IHouse getHouse();

    String getZipCode();

}
package com;

public class Person implements IPerson {
    
    private IHouse house;

    @Override
    public void setHouse(IHouse house) {
        this.house = house;
    }

    @Override
    public IHouse getHouse() {
        return house;
    }

    @Override
    public String getZipCode() {
        return house.getZipCode();
    }
}
package com;

public interface IHouse {

    void setAddress(IAddress address);

    IAddress getAddress();

    String getZipCode();

}
package com;

public class House implements IHouse {

    private IAddress address;

    @Override
    public void setAddress(IAddress address) {
        this.address = address;
    }

    @Override
    public IAddress getAddress() {
        return address;
    }

    @Override
    public String getZipCode() {
        return address.getZipCode();
    }
}
package com;

public interface IAddress {

    void setName(String string);

    void setZipCode(String string);

    String getZipCode();

    public abstract String getName();

}
package com;

public class Address implements IAddress {

    private String name;
    private String zipCode;

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getZipCode() {
        return zipCode;
    }
}

Look at our new code, think now where you will need to change if you need to remove the Address class. Only the Home class will be edited, the rest of our code will remain the same.

This is the greatest advantage of the Demeter law. When you have to do maintenance your project will have a small impact. The new features will be easily adapted, simpler code editions, and with a small cost. In code that we saw today only one class would be impacted. The other classes of your system would remain the same and your system will have a small coupling.

The disadvantage of this approach is an impact on the system performance. You may have a low performance if you use this approach in loops like “While, For, …” .In this case you will have to see which code of your system will not have the performance impacted with the Demeter law.

I believe that even with this disadvantage in the performance in some code pieces this approach is useful and worth of use it in our systems; Demeter law could be used in almost all code of our system.

I hope this post might help you

If you have any doubt or question just post it.

See you soon! o_