WebServices RESTful com JAX-RS / Jersey em instantes

JAX-RS (Java API for RESTful Web Services) ou JSR 311 é a especificação da linguagem Java para criar WebServices RESTful.

Existe varias implementações desta especificação tais como Jersey, Apache CXF, JBoss RESTEasy, Restlet…
Neste artigo vou utilizar Jersey a implementação da Sun/Oracle para esta especificação.
Com JAX-RS é possível criar WS através de Annotations de forma bastante simples e rápida, que podem ser distribuídos através de Web Containers como Tomcat, Jetty, JBoss etc…

Neste caso vou utilizar o framework Grizzly que permite criar um web container embedded excluindo assim a necessidade de um web container externo à aplicação. 
Comecemos pelo serviço que permite o calculo de fibonacci

package com.wordpress.magician.jaxrs.ws;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

/**
 * Calcula o resultado a sequencia de fibonacci
 * @author magician
 *
 */
@Path("/fib/{number}")
public class Fibonacci {

	/**
	 * Devolve uma resposta em plain text com o resultado do calculo.
	 * @param number numero a calcular
	 * @return resultado do calculo
	 */
	@GET
	@Produces("text/plain")
	public String fibText(@PathParam("number") int number){
		return String.format("%d",fibonacci(number));
	}

	/**
	 * Calcula a sequência de fibonacci de forma recursiva
	 * @param n numero actual do processo recursivo
	 * @return resultado actual do processo recursivo
	 */
	private long fibonacci(int n){
		if(n <= 1){
			return n;
		}else{
			return fibonacci(n-1) + fibonacci(n-2);
		}
	}
}

Fibonacci.java

Como se pode ver é muito simples, na header da classe utilizamos a annotation @Path que permite definir o caminho para o serviço. Neste caso o path será /fib/{number} onde o {number} é  utilizado como parâmetro para método fibText onde é associado através da annotation @PathParam.

A annotation @GET indica que o serviço deve ser acedido por HTTP GET e a annotation @Produces define o mime type do conteúdo gerado como resposta pelo WS. Em seguida temos o serviço Auth, este serviço recebe um username e uma password sob forma de String e caso o username seja igual à password ele devolve um long representando a session id que o utilizador deve usar. Caso o username seja diferente da password o serviço devolve -1 ou seja sem session id.


package com.wordpress.magician.jaxrs.ws;

import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

/**
 * Simula a autenticação de um user/pass
 * @author magician
 *
 */
@Path("/auth")
public class Auth {

	/**
	 * Devolve uma resposta em plain text com o resultado da autenticação.
	 * @param user String com o username
	 * @param pass String com a password
	 * @return String com a resposta plain text
	 */
	@POST
	@Produces("text/plain")
	public String authText(@FormParam("user") String user, @FormParam("pass") String pass){
		return String.format("%d", session(user, pass));
	}

	/**
	 * Devolve uma resposta em XML com o resultado da autenticação.
	 * @param String com o user username
	 * @param String com a pass password
	 * @return String com a resposta em XML
	 */
	@POST
	@Produces("text/xml")
	public String authXML(@FormParam("user") String user, @FormParam("pass") String pass){
		return String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?><auth username=\"%s\">%d</auth>", user, session(user, pass));
	}

	/**
	 * Caso o user seja igual à pass a autenticação é feita
	 * com sucesso
	 * @param user String com o username
	 * @param pass String com a password
	 * @return -1 se a auth falha com long caso contrario
	 */
	private long session(String user, String pass){
		System.out.println(user);
		System.out.println(pass);
		if(user.equals(pass)){
			return System.nanoTime();
		}
		return -1;
	}
}

Auth.java

Este serviço tem duas coisas distintas do anterior, primeiro ele apenas deve ser acedido por HTTP POST e não por HTTP GET como era o caso anterior. Isto pode ser definido pela annotation @POST que é utilizada nas linhas 22 e 34, e utilizamos a annotation @FormParam para ir buscar os valores user e pass que devem vir no request POST.

A outra diferença está no tipo de conteúdo produzido pelo serviço neste serviço é gerado dois tipos de conteúdo Plain Text e XML. Desta forma quando o request vem limitado a resposta XML é o método authXML que é executado, caso o request aceite qualquer tipo de conteúdo ou plain text então é o método authText que é executado.

O terceiro serviço permite obter imagens através do seu nome, basicamente enviamos o nome da imagem no url e o WS envia como resposta o conteúdo do ficheiro da imagem.

package com.wordpress.magician.jaxrs.ws;

import java.io.File;

import javax.activation.MimetypesFileTypeMap;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

/**
 * Procura a imagem pelo seu nome e devolve como resposta.
 * @author magician
 *
 */
@Path("/images/{image}")
public class GetFile {

	/**
	 * Devolve a imagem com o mime type do ficheiro ou 404 caso
	 * o ficheiro não seja encontrada.
	 * @param image nome da imagem a procurar
	 * @return imagem com o mime type da imagem fonte.
	 */
	@GET
	@Produces("image/*")
	public Response getImage(@PathParam("image") String image){

		String tmp = System.getProperty("java.io.tmpdir");
		File target = new File(tmp + image);
		System.out.println(target.getAbsolutePath());

		if(!target.exists()){
			throw new WebApplicationException(404);
		}
		String mt = new MimetypesFileTypeMap().getContentType(target);
		return Response.ok(target, mt).build();
	}

}

GetFile.java

A novidade neste serviço é a utilização da Classe Response, esta classe permite gerar uma resposta que deve ser devolvida pelo serviço. No serviço GetFile é gerada uma response OK e irá conter o conteúdo do ficheiro com o mime type desse mesmo ficheiro.
A classe Response permite retornar respostas de erro ou redirect com Response.status ou Response.temporaryRedirect.

Por fim temos o servidor que irá export por HTTP o WS com os serviços descritos acima, como referido foi utilizado o framework Grizzly.

package com.wordpress.magician.jaxrs;

import java.util.HashMap;
import java.util.Map;

import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;

/**
 * Servidor HTTP para o WebService utilizando o WebContainer
 * do framework Grizzly
 * @author magician
 *
 */
public class Server {

	public static void main(String[] args) throws Exception {
		Map<String, String> initParams = new HashMap<String, String>();

		initParams.put("com.sun.jersey.config.property.packages","com.wordpress.magician.jaxrs.ws");

		SelectorThread container = GrizzlyWebContainerFactory.create("http://0.0.0.0:8080/", initParams);

		try{
			container.initEndpoint();
			container.startEndpoint();
		}finally{
			if(container.isRunning()){
				container.stopEndpoint();
			}
		}
	}
}

Server.java

Como se pode ver a configuração é muito simples basta definir um URL (linha 22) e indicar o root package onde se encontram as implementações do serviços (linha 20). Por fim ao executar o nosso projecto
passamos a ter o serviço exposto no URI http://localhost:8080/
Acedendo ao URI http://localhost:8080/application.wadl recebemos a seguinte resposta

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<application xmlns="http://research.sun.com/wadl/2006/10"> 
    <doc xmlns:jersey="http://jersey.dev.java.net/" jersey:generatedBy="Jersey: 1.5 01/14/2011 12:36 PM"/> 
    <resources base="http://localhost:8080/"> 
        <resource path="/images/{image}"> 
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="template" name="image"/> 
            <method name="GET" id="getImage"> 
                <response> 
                    <representation mediaType="image/*"/> 
                </response> 
            </method> 
        </resource> 
        <resource path="/fib/{number}"> 
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:int" style="template" name="number"/> 
            <method name="GET" id="fibText"> 
                <response> 
                    <representation mediaType="text/plain"/> 
                </response> 
            </method> 
        </resource> 
        <resource path="/auth"> 
            <method name="POST" id="authText"> 
                <request> 
                    <representation mediaType="application/x-www-form-urlencoded"> 
                        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="user"/> 
                        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="pass"/> 
                    </representation> 
                </request> 
                <response> 
                    <representation mediaType="text/plain"/> 
                </response> 
            </method> 
            <method name="POST" id="authXML"> 
                <request> 
                    <representation mediaType="application/x-www-form-urlencoded"> 
                        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="user"/> 
                        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="pass"/> 
                    </representation> 
                </request> 
                <response> 
                    <representation mediaType="text/xml"/> 
                </response> 
            </method> 
        </resource> 
    </resources> 
</application> 

Este XML é o utilizado para descrever o WS, tem um propósito semelhante ao WSDL dos WS SOAP mas neste caso para RESTful e bastante mais simples que o WSDL. Com ele é possível saber que serviços estão disponíveis, dado os paths, métodos de request, tipo de dados que devem ser passados ou que são devolvidos, etc…

Referencias:

http://en.wikipedia.org/wiki/JAX-RS

http://jersey.java.net/

http://grizzly.java.net/

http://www.suryasuravarapu.com/2009/02/rest-jersey-configuration-on-tomcat.html

Mais Enumeração em Java

De forma a completar um pouco mais o artigo anterior Enumeração em Java vou agora exemplificar o uso de enum em Java para criar enumerações que executam acções encapsuladas em cada uma delas.

Para começar vamos implementar a enumeração, neste caso o nosso objectivo é o de ter vários formatadores de texto e ao passar um determinado texto para um formatador ele deve devolver esse texto formatado consoante a implementação desse formatador.

Esta solução poderia ser implementada de outras formas, mas neste caso vamos recorrer a uma enum

import java.io.InvalidClassException;

/**
 * Conjunto de formatadores de texto.
 * @author magician
 *
 */
public enum Formater {

	/**
	 * Formatação do texto em HTML
	 */
	HTML(1){
		@Override
		public String format(String text) {
			return String.format("<html>\n\t<head>\n\t\t<title>FORMATED TEXT</title>\n\t</head>\n\t<body>\n\t\t%s\n\t</body>\n</html>", text);
		}
	},

	/**
	 * Formatação do texto em XML
	 */
	XML(2){
		@Override
		public String format(String text) {
			return String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<page>\n\t<head>\n\t\t<title>FORMATED TEXT</title>\n\t</head>\n\t<body>\n\t\t%s\n\t</body>\n</page>", text);
		}
	},

	/**
	 * Formatação do texto em JSON
	 */
	JSON(3){
		@Override
		public String format(String text) {
			return String.format("{\n\tpage:{\n\t\thead:{\n\t\t\ttitle:'FORMATED TEXT'\n\t\t},\n\t\tbody:'%s'\n\t}\n}", text);
		}
	};

	/**
	 * Identificar numérico do Formater
	 */
	private final int ID;

	private Formater(int id){
		ID = id;
	}

	/**
	 * Devolve o identificador numérico associado ao formater
	 * @return identificador numérico
	 */
	public int getID() {
		return ID;
	}

	/**
	 * Procura por um Formater especifico através do identificador ao
	 * invés do nome
	 * @param id identificador numérico do formater pretendido
	 * @return Formater que corresponde ao id dado
	 * @throws InvalidClassException Ocorre caso não exista nenhum Formater com o id dado
	 */
	public static Formater getFormater(int id) throws InvalidClassException {
		for(Formater f : values()){
			if(f.ID == id){
				return f;
			}
		}
		throw new InvalidClassException(String.format("Enum with id %d not found.", id));
	}

	/**
	 * Procura por um Formater especifico através do identificador ao
	 * invés do nome, em seguida formata o texto com a implementação
	 * desse formater.
	 * @param id identificador numérico do formater pretendido
	 * @param text String com o texto a ser formatado
	 * @return String com texto formatado
	 * @throws InvalidClassException
	 */
	public static String formatText(int id, String text) throws InvalidClassException {
		return getFormater(id).format(text);
	}

	/**
	 * Formata o texto enviado seguindo a implementação escolhida.
	 * @param text String com o texto a ser formatado
	 * @return String com o texto formatado.
	 */
	public abstract String format(String text);
}

Como podemos ver para além da típica enumeração de um conjunto de nomes (HTML,XML,JSON….) temos também métodos implementados em cada um desses nomes. Podemos pensar como se o Formater fosse uma classe abstract e o HTML, XML e JSON fosse subclasses da Formater mas “dentro” da classe formater.

Para esta implementação basta na base da enum declarar métodos abstract e estes são implementados em cada um dos nomes que existem na enum. Para além disso temos também um construtor que vai ser usado na declaração dos nomes (HTML(1), XML(2), JSON(3)) e os métodos static que permitem fazer operações sobre a enum genérica ao invés de cada um dos nomes.

Vejamos agora como utilizar o nosso Formater implementado com enum.

import java.io.InvalidClassException;

public class Main {

	private static final int HTML_ID = 1;
	private static final int XML_ID = 2;
	private static final int JSON_ID = 3;

	public static void main(String[] args) {
		String text = "Hello World xD";

		String HTML = Formater.HTML.format(text);
		System.out.println("-----------------------HTML----------------------");
		System.out.println(HTML);

		String XML = Formater.XML.format(text);
		System.out.println("-----------------------XML-----------------------");
		System.out.println(XML);

		String JSON = Formater.JSON.format(text);
		System.out.println("-----------------------JSON----------------------");
		System.out.println(JSON);

		try {
			String XML_FROM_ID = Formater.getFormater(XML_ID).format(text);
			System.out.println("------------------XML_FROM_ID--------------------");
			System.out.println(XML_FROM_ID);

			String JSON_FROM_ID = Formater.formatText(JSON_ID, text);
			System.out.println("------------------JSON_FROM_ID--------------------");
			System.out.println(JSON_FROM_ID);

			Formater INVALID_ID = Formater.getFormater(100);
		} catch (InvalidClassException e) {
			System.err.println(e.getMessage());
		}
	}
}

No bloco [12-22] utilizamos o Formater especificando o nome da implementação a ser usada e chamamos o método format da implementação escolhida, neste caso recorremos às três implementações.

Em seguida no bloco [25-27] usamos o método static getFormater para através de um id devolver o formater com esse id ao invés de recorrer ao nome como foi feito antes. Esta forma pode ser útil quando temos identificadores externos à enum ou quando queremos tornar a escolha mais dinâmica ao invés de recorrer a uma cadeia de if-else ou switch.

O método static formatText (bloco [29-31]) recorremos ao método static  formatText que no fundo faz o mesmo que o método getFormater mas a chamada ao método format fica oculta.

Para finalizar na linha 33 enviamos um id que não existe e neste caso é lançada uma exception a informar que não existe nenhum Formater com o id enviado.

Após a execução temos o seguinte output

-----------------------HTML----------------------
<html>
	<head>
		<title>FORMATED TEXT</title>
	</head>
	<body>
		Hello World xD
	</body>
</html>
-----------------------XML-----------------------
<?xml version="1.0" encoding="UTF-8"?>
<page>
	<head>
		<title>FORMATED TEXT</title>
	</head>
	<body>
		Hello World xD
	</body>
</page>
-----------------------JSON----------------------
{
	page:{
		head:{
			title:'FORMATED TEXT'
		},
		body:'Hello World xD'
	}
}
------------------XML_FROM_ID--------------------
<?xml version="1.0" encoding="UTF-8"?>
<page>
	<head>
		<title>FORMATED TEXT</title>
	</head>
	<body>
		Hello World xD
	</body>
</page>
------------------JSON_FROM_ID--------------------
{
	page:{
		head:{
			title:'FORMATED TEXT'
		},
		body:'Hello World xD'
	}
}
Enum with id 100 not found.

Como referi existem outras formas de implementar este mesmo mecanismo esta é apenas mais uma fazendo uso dos recursos da linguagem Java.

Logging em Java

Na maioria das vezes e talvez por habito é comum fazer o logging de uma aplicação através de simples prints para a consola, mas na realidade isto não é aconselhável. Provavelmente pode-se achar mais rápido fazer um System.err.println do que utilizar um mecanismo de logging mas numa aplicação digna desse nome isso já não é bem assim.

Fazer logging de algumas dezenas de classes e métodos através de simples prints pode-se tornar bastante complicado e doloroso não de implementar claro mas de manter e perceber. Para isso o Java dispõe de uma API de logging, existe tem outras externas como a conhecida log4j da Apache mas na minha opinião java.util.logging faz o trabalho.

Vamos começar por um exemplo “ridículo” com logging por prints ao invés de usar o sistema de logging.

import javax.swing.JOptionPane;

/**
 * @author magician
 */
public class HelloLogging {

    public static void printMsg(String msg){
        if(msg == null){
            System.err.println("A mensagem não pode ser null!");
            return;
        }
        else if(msg.isEmpty()){
            System.err.println("A mensagem não deve ser vazia!");
        }
        JOptionPane.showMessageDialog(null, msg);
        System.err.println("A mensagem foi enviada com sucesso!");
    }

    public static void main(String args []){
        System.err.println("Programa a iniciar...");
        printMsg(null);
        printMsg("");
        printMsg("Programa simples sem sistema de logging");
        System.err.println("Programa a encerrar...");
    }
}

[HelloLogging.java]

O exemplo esta um pouco exagerado no que toca ao logging mas a ideia é mesmo essa, como podemos ver em determinadas situações é enviado para o System.err mensagens de loggin que permite saber o que aconteceu ou o que correr mau na execução do programa.

Mas trata-se de uma logging muito pobre, não sabemos se é error (linhas 10 e 14)ou apenas informação de execução (restantes System.err.println), nem sabemos quando ocorrer ou em que classe. É verdade que toda essa informação poderia ser colocada sob forma de String nos prints mas isso seria muito trabalhoso e contra produtivo.

Vejamos agora o mesmo exemplo mas utilizando o sistema de logging do Java

import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 * @author magician
 */
public class HelloLogging {

    /**
     * Cria um Logger para a classe HelloLogging
     */
    private static final Logger LOG = Logger.getLogger(HelloLogging.class.getName());

    public static void printMsg(String msg){
        if(msg == null){
            LOG.severe("A mensagem não pode ser null!");
            return;
        }
        else if(msg.isEmpty()){
            LOG.warning("A mensagem não deve ser vazia!");
        }
        JOptionPane.showMessageDialog(null, msg);
        LOG.info("A mensagem foi enviada com sucesso!");
    }

    public static void main(String args []){
        LOG.info("Programa a iniciar...");
        printMsg(null);
        printMsg("");
        printMsg("Programa simples sem sistema de logging");
        LOG.info("Programa a encerrar...");
    }
}

O uso do logging como se pode ver é bastante simples, basta criar o Logger para a classe e em seguida enviar os registos usando o método com o nível que queremos obtendo assim o seguinte output


28/Jul/2010 11:39:32 HelloLogging main
INFO: Programa a iniciar...
28/Jul/2010 11:39:32 HelloLogging printMsg
SEVERE: A mensagem não pode ser null!
28/Jul/2010 11:39:32 HelloLogging printMsg
WARNING: A mensagem não deve ser vazia!
28/Jul/2010 11:39:35 HelloLogging printMsg
INFO: A mensagem foi enviada com sucesso!
28/Jul/2010 11:39:35 HelloLogging printMsg
INFO: A mensagem foi enviada com sucesso!
28/Jul/2010 11:39:35 HelloLogging main
INFO: Programa a encerrar...

O log dá-nos bastante informação, data e hora, a classe e método de onde foi enviado o registo, o nível de gravidade que é bastante útil por exemplo para filtrar o registo do log e a mensagem enviado para o log.

Pode-se ainda ao invés de criar um Logger por classe, usar apenas um para toda a aplicação, ou seja um global que o sistema de logging cria automaticamente. Para isso basta trocar a linha 12 da classe HelloLoggin para

private static final Logger LOG = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

O resultado será o mesmo que o anterior, a desvantagem deste método é que como temos apenas um logger todas as configurações serão aplicadas a ele não fazendo distinções. Isto é ao definir um Handler ou um Level para o Logger Global ele será genérico.

Mas para alem de enviar o log para a consola é possível enviar para outros destinos como ficheiros, Streams, Sockets ou até mesmo para um destino personalizado como email por exemplo. Para além dos destino podemos ainda definir o formato das mensagens que por defeito é texto mas pode ser XML ou outro formato personalizado tal como HTML ou outros.

Vejamos o exemplo anterior mas agora o log será guardado em um ficheiro e terá o formato XML

import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.XMLFormatter;
import javax.swing.JOptionPane;

/**
 * @author magician
 */
public class HelloLogging {

    private static final Logger LOG = Logger.getLogger(HelloLogging.class.getName());

    public static void printMsg(String msg){
        if(msg == null){
            LOG.severe("A mensagem não pode ser null!");
            return;
        }
        else if(msg.isEmpty()){
            LOG.warning("A mensagem não deve ser vazia!");
        }
        JOptionPane.showMessageDialog(null, msg);
        LOG.info("A mensagem foi enviada com sucesso!");
    }

    public static void main(String args []){
        try {
            Handler console = new ConsoleHandler();
            Handler file = new FileHandler("C:\\hellologgin.xml");
            /*Define que na consola apenas aparece log com nível superior ou
              a warning e no ficheiro deve aparecer o log de qualquer nível
             */
            console.setLevel(Level.WARNING);
            file.setLevel(Level.ALL);
            //Define o formato de output do ficheiro como XML
            file.setFormatter(new XMLFormatter());
            //Adiciona os handlers para ficheiro e console
            LOG.addHandler(file);
            LOG.addHandler(console);
            //Ignora os Handlers definidos no Logger Global
            LOG.setUseParentHandlers(false);
        }
        catch(IOException io){
            LOG.warning("O ficheiro hellologgin.xml não pode ser criado");
        }
        LOG.info("Programa a iniciar...");
        printMsg(null);
        printMsg("");
        printMsg("Programa simples sem sistema de logging");
        LOG.info("Programa a encerrar...");
    }
}

O exemplo acima limita o output da consola a registos de nível igual ou superior a warning ou seja todos os restantes registos são ignorados pelo handler e não aparecem no output, por outro lado o handler para o ficheiro XML guarda os registo de qualquer nível e com o formato XML e não simple text como no caso da consola.

Para além do SimpleFormatter e XMLFormatter podemos ainda criar os nossos próprios formaters para isso basta fazer uma classe que extende a classe java.util.Formatter e nela definirmos os nossos parâmetros de formatação. Por exemplo um Formatter que gera um log em HTML ao invés de XML ou simple text.

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;

/**
 * @author magician
 */
public class HTMLFormater extends Formatter {

    @Override
    public String format(LogRecord record) {
        StringBuilder sb = new StringBuilder();
        sb.append("<tr>\n");
        sb.append("<td>\n");
        if(record.getLevel().intValue() >= Level.WARNING.intValue()){
            sb.append("<strong>color=red>");</strong>
            sb.append(record.getLevel());
            sb.append("</b></font>");
        }
        else {
            sb.append(record.getLevel());
        }
        sb.append("</td>\n");
        sb.append("<td>\n");
        sb.append(milliToDate(record.getMillis()));
        sb.append("</td>\n");
        sb.append("<td>\n");
        sb.append(formatMessage(record));
        sb.append("</td>\n");
        sb.append("</tr>\n");
        return sb.toString();
    }

    private String milliToDate(long millis){
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
        return format.format(new Date(millis));
    }

    @Override
    public String getHead(Handler h) {
        return "\n\n
                " charset=utf-8\">\n\n\n
<pre>\n"</pre>
 + "
<table>border>\n  "</table>
 + "<tr><th>Nivel</th><th>Data</th><th>Mensagem</th></tr>\n";
 }

 @Override
 public String getTail(Handler h) {
 return "\n  \n\n";
 }
}

Ao aplicar o HTMLFormatter ao nosso FileHandler vamos passar a ter um log com o formato HTML que definimos no Formatter o que vai resultar em algo como

Nivel Data Mensagem
INFO 28/07/2010 14:10:13 Programa a iniciar...
SEVERE 28/07/2010 14:10:13 A mensagem não pode ser null!
WARNING 28/07/2010 14:10:13 A mensagem não deve ser vazia!
INFO 28/07/2010 14:10:14 A mensagem foi enviada com sucesso!
INFO 28/07/2010 14:10:14 A mensagem foi enviada com sucesso!
INFO 28/07/2010 14:10:14 Programa a encerrar...

Para destas configurações em código podemos ainda configurar os loggers através de um ficheiro de propriedades, isto danos a vantagem de poder alterar o comportamento dos loggers sem ter que recompilar todo o programa.

Podemos fazer o exemplo anterior e obter o mesmo resultado utilizando esse ficheiro de configuração da seguinte forma

import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 * @author magician
 */
public class HelloLogging {

    private static final Logger LOG = Logger.getLogger(HelloLogging.class.getName());

    public static void printMsg(String msg){
        if(msg == null){
            LOG.severe("A mensagem não pode ser null!");
            return;
        }
        else if(msg.isEmpty()){
            LOG.warning("A mensagem não deve ser vazia!");
        }
        JOptionPane.showMessageDialog(null, msg);
        LOG.info("A mensagem foi enviada com sucesso!");
    }

    public static void main(String args []){
        LOG.info("Programa a iniciar...");
        printMsg(null);
        printMsg("");
        printMsg("Programa simples sem sistema de logging");
        LOG.info("Programa a encerrar...");
    }
}

Com o seguinte ficheiro logging.properties

java.util.logging.FileHandler.formatter = HTMLFormatter
java.util.logging.FileHandler.level = ALL
java.util.logging.FileHandler.pattern = C:\\hellologging.html

java.util.logging.ConsoleHandler.level = WARNING
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

HelloLogging.level = ALL
HelloLogging.handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler

E à semelhança do exemplo anterior na consola irão aparecer os registos com nível igual ou superior a warning e no ficheiro HTML irão aparecer todos os registos.
Para que o programa carregue o ficheiro de configuração é preciso adicionar a seguinte opção é VM

java -Djava.util.logging.config.file=logging.properties <classe>

Podemos ainda dispensar o uso desta opção da VM se no “topo” de execução da nossa aplicação colocarmos o seguinte bloco de código

static {
        Properties prop = System.getProperties();
        prop.setProperty("java.util.logging.config.file","logging.properties");
}

Neste exemplo coloquei este bloco por cima da criação do Logger LOG, mas o importante será colocar a atribuição de propriedades antes de qualquer chamada à API logging. Existem outras formas como usar o LogManager para carregar as configurações mas acho esta mais simples e rápida.

Referencia:

Java Logging Overview

java.util.logging

Attaching handlers to loggers using logging config file

Padrão Observer em Java

Observer é um padrão de desenvolvimento de software que define uma relação de dependência de um para muitos (1-N) entre objectos, desta forma quando um objecto muda de estado todos os outros que se encontram dependentes deles são notificados e actualizados automaticamente.

É possível fazer uma analogia a um assinatura de um serviço,  por exemplo um serviço de noticias em que os assinantes são notificados sempre que há uma nova noticia. Eles podem ainda assinar ou cancelar a assinatura a qualquer momento.

Um exemplo de uso deste padrão pode ser visto no sistema de eventos da API Swing do Java, em que ao ocorrer determinado evento todos os objectos registados para esse tipo de evento são notificados de que ele ocorreu.

Embora existe na uma implementação deste padrão na API do Java, vamos antes fazer uma implementação manual que é semelhante para varias linguagens OO e em seguida passamos então à API.

O padrão é constituído por duas “peças” base, o Subject e os Observers.

  • Subject – Representa o serviço, controla o estado dos Observers.
  • Observer – Representa o assinante, estão dependentes do Subject para se actualizarem.

Na implementação do padrão devemos tentar ter acoplamento fraco entre os objectos que interagem, o Subject apenas conhece a interface Observer e não as suas implementações, os Observers podem ser adicionados e removidos em qualquer altura nem necessitar alterar o Subject e mudanças quer no Subject quer nos Observers não afectam um ao outro.

Para começar definimos o interface Subject

package observer;

/**
 * @author magician
 */
public interface Subject {

    /**
     * Adiciona um novo Observer à lista a notificar.
     */
    public void addObserver(Observer obs);

    /**
     * Remove um Observer da lista.
     */
    public void delObserver(Observer obs);

    /**
     * Notifica todos os Observers registados.
     */
    public void notifyObservers();

}

[Subject.java]

Em seguida definimos o interface Observer

package observer;

/**
 * @author magician
 */
public interface Observer {

    /**
     * Método que faz a notificação e recebe as actualizações.
     */
    public void update(Object obj);

}

[Observer.java]

Neste momento já temos os interfaces necessários para o padrão Observer, vamos agora passar à implementação.
Começamos por implementar a classe Reader que implementa o interface Observer

package observer;

/**
 * @author magician
 */
public class Reader implements Observer {

    /**
     * Ultima mensage lida
     */
    private String message;

    /**
     * Identificador do leitor, apenas para distinguir os prints
     */
    private int id;

    public Reader(int id) {
        this.id = id;
        this.message = "";
    }

    /**
     * Actualiza a ultima mensagem lida e imprime a mensagem
     */
    public void update(Object obj) {
        this.message = (String)obj;
        readAndPrint();
    }

    /**
     * Imprime a ultima mensagem lida
     */
    public void readAndPrint(){
        System.out.println(String.format("Reader %d -> Message: %s",
                this.id, this.message));
    }
}

[Reader.java]

Em seguida temos a implementação da classe MessageBoard que implementa o interface Subject

package observer;

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

/**
 * @author magician
 */
public class MessageBoard implements Subject {

    /**
     * Lista com todos os Observers a notificar.
     */
    private List <Observer> observers;

    /**
     * Ultima mensagem registada.
     */
    private String message;

    public MessageBoard(){
        this.observers = new ArrayList<Observer>();
    }

    public String getMessage() {
        return message;
    }

    /**
     * O método set para alem de atribui um novo valor à
     * mensagem executa o método para notificar os Observers.
     */
    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }

    /**
     * Adiciona um novo Observer a ser notificado
     */
    public void addObserver(Observer obs) {
        this.observers.add(obs);
    }

    /**
     * Remove um Observer da lista de Observers a serem notificados.
     */
    public void delObserver(Observer obs) {
        this.observers.remove(obs);
    }

    /**
     * Notifica e actualiza todos o observers registados.
     * Para alem de notificar envia também a nova mensagem.
     */
    public void notifyObservers() {
        for(Observer obs : this.observers){
            obs.update(this.message);
        }
    }
}

[MessageBoard.java]

Agora apenas falta criar a classe Main que ira criar um MessageBoard, alguns Readers e efectuar mudanças de estado no MessageBoard

package observer;

/**
 * @author magician
 */
public class Main {

    public static void main(String args []){
        MessageBoard board = new MessageBoard();
        Reader r1 = new Reader(1);
        Reader r2 = new Reader(2);
        Reader r3 = new Reader(3);

        /**
         * Adiciona os objectos a notificar
         */
        board.addObserver(r1);
        board.addObserver(r2);
        board.addObserver(r3);

        /**
         * Actualiza a ultima mensagem o que faz com que os Observers
         * sejam notificados da ocorrencia.
         */
        board.setMessage("Message 0001");
        board.setMessage("Message 0002");

        /**
         * Remove o Observer r2 da lista de Observers a registar.
         */
        board.delObserver(r2);

        /**
         * Actualiza novamente a utima mensagem.
         */
        board.setMessage("Message 0003");

        /**
         * Imprime a ultima mensagem lida pelo Observer r2
         */
        r2.readAndPrint();
    }
}

[Main.java]

Ao executar o Main obtemos o seguinte output

Reader 1 -> Message: Message 0001
Reader 2 -> Message: Message 0001
Reader 3 -> Message: Message 0001
Reader 1 -> Message: Message 0002
Reader 2 -> Message: Message 0002
Reader 3 -> Message: Message 0002
Reader 1 -> Message: Message 0003
Reader 3 -> Message: Message 0003
Reader 2 -> Message: Message 0002

Como se pode ver sempre que o nosso objecto MessageBoard é actualizado todos os Readers registados também o são automaticamente. Se reparar-mos as linhas 7, 8 e 9 do output diferem das sequências anteriores, isto acontece porque na linha 31 do Main o Reader r3 foi removido da lista de objectos a notificar e por isso na mensagem não foi actualizada.

A implementação é muito simples e incrivelmente útil como podemos ver, mas para quem não quer ou não acha necessário fazer esta implementação o Java já trás na sua API uma implementação deste padrão.

A classe java.util.Observable é uma implementação que corresponde ao nosso interface Subject do exemplo acima e o interface java.util.Observer é o correspondente ao nosso interface Observer do exemplo e neste caso a unica diferença está no método update que recebe a actualização e o objecto a actualizar face ao exemplo que apenas recebia a actualização.

Embora o uso da API poupe o trabalho da reimplementar o padrão ela tem uma desvantagem, o Subject neste caso o Observable é uma classe e não um interface e dado que em Java uma classe só pode ter uma super classe ao usar a classe Observable a classe que a irá implementar já não pode fazer extends a mais nenhuma classe.

Aqui podem encontrar um exemplo semelhante ao apresentado acima mas usando a API ao invés da implementação manual mostrada acima.

http://www.java2s.com/Code/Java/Design-Pattern/Observableandobserver.htm

Adicionar Serviços à SuSEfirewall2

A SuseFirewall pode ser facilmente configurada através do yast, mas infelizmente o interface de configuração não permite adicionar serviços para além dos que já se encontram definidos.
É possível adicionar regras para abertura de portas extra através de opções avançadas de serviços ou adicionando regras personalizadas. Para quem gosta de ter tudo organizado e simples de utilizar há ainda outra opção.
É possível adicionar serviços através de packages, no fundo são apenas pequenos ficheiros de configuração que são colocados no directório de configuração da firewall.

A lista de serviços encontra-se em /etc/sysconfig/SuSEfirewall2.d/services
Aqui existem pequenos ficheiros de texto que contém as especificações de cada serviço.

Existe também um ficheiro TEMPLATE que pode ser utilizado como molde para criar novos serviços.

# Do not edit this file as it's just a template and will be
# overwritten on package updates! Copy to a new file instead.
# Fill in the required variables and delete the unused ones.
# If in doubt ask security@suse.de
#
# Only the variables TCP, UDP, RPC, IP, BROADCAST, RELATED and
# MODULES are allowed. More may be supported in the future.
#
# For a more detailed description of the individual variables see
# the comments for FW_SERVICES_*_EXT in /etc/sysconfig/SuSEfirewall2
#

## Name: template service
## Description: opens ports for foo in order to allow bar

# space separated list of allowed TCP ports
TCP=""

# space separated list of allowed UDP ports
UDP=""

# space separated list of allowed RPC services
RPC=""

# space separated list of allowed IP protocols
IP=""

# space separated list of allowed UDP ports that accept broadcasts
BROADCAST=""

### variables below are only needed in very special cases

# space separated list of net,protocol[,sport[,dport]]
# see FW_SERVICES_ACCEPT_RELATED_EXT
# net 0/0 means IPv4 and IPv6. If this sevice should only work for
# IPv4 use 0.0.0.0/0
RELATED=""

# additional kernel modules needed for this service
# see FW_LOAD_MODULES
MODULES=""

Basta criar um novo ficheiro á semelhança do template, colocar as configurações que pretendemos e está pronto.
Vamos aqui criar os serviço BitTottorrent que vai abrir a porta 51413 que será utilizada pelo cliente bittorrent.

## Name: Bittorrent
## Description: Abre as porta TCP e UDP para o Torrent Client

# space separated list of allowed TCP ports
TCP="51413"

# space separated list of allowed UDP ports
UDP="51413"

É uma exemplo muito simples, é possível colocar outras opções como se pode ver no TEMPLATE mas neste caso estas são suficientes

Agora basta reiniciar a firewall e através do Yast adicionar o novo serviço aos serviços permitidos.

Python XML-RPC

XML-RPC é um protocolo RPC (Remote Procedure Call) codificado em XML, é talvez o protocolo RPC mais simples face a outras implementações como RMI, CORBA, SOAP, etc…

Basicamente existe um servidor e um ou mais clientes que trocam mensagens em XML entre si.
O cliente pede ao servidor que execute determinado procedimento através de uma mensagem XML, o servidor executa o procedimento pedido e retorna o resultado também através de uma mensagem XML.

Desta forma é possível ao cliente fazer uso das funcionalidades do servidor sem ter de se preocupar com a implementação dos procedimentos. Uma grade vantagem desta implementação é a de permitir a integração entre linguagens, por exemplo podemos ter o lado do servidor implementados em Java e ter um cliente em PHP ou Python e vice-versa. Como é óbvio é possível que se encontrem algumas contradições entre as linguagens em especial nas estruturas de dados, mas como as mensagens são em XML ou seja texto é sempre possível contornar os pequenos obstáculos que possam aparecer.

Python vem de core com suporte para este protocolo de comunicação, para isso tem o modulo xmlrpclib para o interface de cliente e o modulo SimpleXMLRCPServer para o interface de servidor.

Para exemplificar algumas das funcionalidades destes módulos vamos implementar um pequeno servidor e cliente ambos em Python recorrendo ao uso dos referidos módulos.

# -*- coding: UTF-8 -*-
from SimpleXMLRPCServer import SimpleXMLRPCServer
from time import strftime

class User (object):

	def __init__(self, username, password):
		self.__username = username
		self.__password = password
		self.__logged = False

	def getUsername(self):
		return self.__username

	def getPassword(self):
		return self.__password

	def session(self, state=None):
		if state != None:
			self.__logged = state
		return self.__logged

class Session (object):
	"""
	Fornece os funcionalidades básicas para um sistema
	de controlo de sessão.
	"""

	def __init__(self):
		"""
		Inicia um dicionário vazio que servirá com
		base de dados.
		"""
		self.__db = {}

	def login(self,username, password):
		"""
		Altera o estado da sessão para True.
		Args: username, password
		Return: True ou False
		"""
		user = self.__db[username]
		if user.getPassword == password:
			user.session(True)
		return user.session(True)

	def register(self,username, password):
		"""
		Insere um novo user na base de dados.
		Args: username, password
		Return: True ou False
		"""
		self.__db[username] = User(username, password)
		return self.__db.has_key(username)

	def logout(self,username):
		"""
		Altera o estado da sessão para False.
		Args: username
		Return: True ou False
		"""
		user = self.__db[username]
		return not user.session(False)

def currentTime():
	"""
	Permite obter a data e hora actual do servidor.
	Args: None
	Return: String com Data e hora
	Formato da Resposta: YYYY-mm-DD HH:MM:SS
	"""
	return strftime("%Y-%m-%d %H:%M:%S")

def start(addr="localhost",port=8080):
	#Cria um servidor XML-RPC no endereço e port definido.
	server = SimpleXMLRPCServer((addr,port))
	#Permite aos clientes fazer introspecção ao servidor
	server.register_introspection_functions()
	#Permite aos clientes fazerem vários pedidos como um só
	server.register_multicall_functions()
	#Regista um objecto Session, o mapeamento dos métodos é automático
	server.register_instance(Session())
	#Regista a funcão currentTime no servidor com o nome time
	server.register_function(currentTime,"time")
	#Inicia o servidor XML-RCP em loop infinito
	server.serve_forever()

if __name__ == "__main__":
	start()

[Servidor]

Como se pode ver é um exemplo muito muito simples e não totalmente realista, mas para o que se pretende serve perfeitamente. Basicamente temos o servidor que permite registar, fazer login e logout de utilizadores bem como saber a data/hora actual do servidor.

Os procedimentos podem ser disponibilizados através de funções isoladas ou através de instâncias de classe, ao registar um instância todos os métodos públicos são automaticamente publicados para os cliente XML-RPC.

Passamos agora ao cliente, o cliente vai ter duas formas de fazer os pedidos ao servidor, o método directo em que cada pedido é feito com uma mensagem e o método multicall que faz os pedidos todos de uma só vez e recebe as respostas também todas juntas.

# -*- coding: UTF-8 -*-
import xmlrpclib
from datetime import datetime

#Cria uma ligação ao servidor XML-RCP
server = xmlrpclib.ServerProxy("http://localhost:8080/")

def pedidoSimples():
	#Pede ao servidor que execute o procedimento time
	print server.time()
	#Pede ao servidor que liste todos os procedimentos disponíveis
	print server.system.listMethods()
	#Retorna o pydoc associado ao método
	print server.system.methodHelp("time")
	#Envia os dados para o método junto com o pedido
	print server.register("magician","123")
	print server.login("magician","123")
	print server.logout("magician")

def pedidoMultiCall():
	#Gera um pedido MultiCall
	multirequest = xmlrpclib.MultiCall(server)
	#Adiciona ao pedido multi call os pedidos pretendidos
	multirequest.time()
	multirequest.register("magician","123")
	multirequest.login("magician","123")
	#Faz o pedido e aguarda a resposta que é um gerador
	result = multirequest()
	#Um a um extrai os dados da resposta do gerador
	for resp in result:
		print resp

if __name__ == "__main__":
	#Envia uma sequência de pedidos um a um
	pedidoSimples()
	#Envia uma sequência de pedidos como um só
	pedidoMultiCall()

[Cliente]

2010-07-23 16:55:49
['login', 'logout', 'register', 'system.listMethods', 'system.methodHelp', 'system.methodSignature', 'system.multicall', 'time']
Permite obter a data e hora actual do servidor.
Args: None
Return: String com Data e hora
Formato da Resposta: YYYY-mm-DD HH:MM:SS
True
True
True
2010-07-23 16:55:49
True
True

[Output]

Como se pode ver o processo é muito simples tanto o método de pedido directo como o de multicall são bastante intuitivos, a vantagem do método multicall é que ao invés de fazer vários pedidos e receber varias respostas faz apenas um pedido e recebe apenas uma resposta o que pode ajudar bastante no que toca a performance principalmente no congestionamento do servidor.

Outra coisa importante sobre o MultiCall é que cada objecto deste classe embora seja reutilizável os pedidos já lá registados não são removidos mesmo depois de ser executado. Ou seja neste exemplo se após o loop para imprimir os resultados se fosse adicionado outro pedido e se volta-se a ser executado ele iria enviar novamente todos os pedidos anteriores mais o novo pedido. Por isso caso se pretenda fazer novos pedidos em bloco (multicall) deve-se criar um novo objecto MultiCall.

Basicamente é isto, existem mais alguns pormenores em relação aos modulos mas nada que um leitura pela documentação e uma vista de olhos pelos exemplos lá colocados não clarifique.

Referencias:

Python SimpleXMLRPCServer

Python xmlrpclib

XML-RPC Spec

XML-RPC Instrospection

XML-RPC Errata

XML-RPC HOW-TO

Ruby Inline vs Block vs Proc

Algo que estranhei bastante ao começar a programar em Ruby foi sem duvida os Blocos e os Procs, para alguém que tinha Java como linguagem preferida foi algo uma tanto “absurdo”.

Pensar mas para que raio é isto!?!? Se tenho um objecto porque não chamo simplesmente os seus métodos e pronto!?!?

Depois de algum tempo comecei a adaptar-me ao uso dos blocos a nível de código realmente consegue simplificar muita a vida do programar e tornar as muitos casos as coisas bem mais simples.

Então ai surgiu outra duvida! Será que o uso de blocos e procs trás melhores ou piores níveis de performance face ao simples código inline ?

Depois de alguma pesquisa encontrei o seguinte artigo http://www.pluralsight.com/community/blogs/dbox/archive/2006/05/09/23068.aspx  onde era discutido exactamente essa questão.

O autor questiona-se o porque da adoração dos programadores de Ruby pelos blocos em relação ao código inline, para tentar chegar a algumas conclusões ele cria o seguinte código.

require "benchmark"

IterationCount = 1000000
$x = 0

def UseBlock
    yield
end

def UseProc(&amp;p)
    p.call
end

def InlineStatic()
    i = 0
    while (i &lt; IterationCount)
        $x = $x + 1
        i = i + 1
    end
end

def InlineLocal()
    i = 0
    y = 0
    while (i &lt; IterationCount)
        y = y + 1
        i = i + 1
    end
end

def BlockStatic()
    i = 0
    while (i &lt; IterationCount)
        UseBlock do $x = $x + 1 end
        i = i + 1
    end
end

def BlockLocal()
    i = 0
    y = 0
    while (i &lt; IterationCount)
        UseBlock do y = y + 1 end
        i = i + 1
    end
end

def ProcStatic()
    i = 0
    while (i &lt; IterationCount)
        UseProc do $x = $x + 1 end
        i = i + 1
    end
end

def ProcLocal()
    i = 0
    y = 0
    while (i &lt; IterationCount)
        UseProc do y = y + 1 end
        i = i + 1
    end
end

Benchmark.bm { |r|
    r.report { InlineStatic() }
    r.report { InlineLocal() }
    r.report { BlockStatic() }
    r.report { BlockLocal() }
    r.report { ProcStatic() }
    r.report { ProcLocal() }
}

Ao executar este código no Ruby 1.8.7 obtive os seguintes resultados


user     system   total     real
0.570000 0.000000 0.570000 ( 0.584075)
0.500000 0.000000 0.500000 ( 0.510654)
1.320000 0.330000 1.650000 ( 1.662194)
1.280000 0.270000 1.550000 ( 1.555471)
3.240000 0.350000 3.590000 ( 3.623273)
3.100000 0.390000 3.490000 ( 3.507118)

Como podemos ver os resultados difererem como podemos ver o codigo inline é o com melhor performance, seguido pelos blocos com aproximadamente 3 vezes menos performance e por fim os procs com 7 vezes menos performance que o inline.

Claramente os Procs oferencem uma menor performance face ao inline e aos blocos. Em relação ao inline vs blocos é algo discutivél, cabe ao programador avaliar se a diferença é significativa para o objectivo em questão, tambem há que ter em consideração que se trata apenas de uma exemplo com contagem de 0 a 1000000, ao usar outras sequencias de codigo é possivel obter outros resultados embora os procs vejam sempre a estar em ultimo lugar dado que o uso do yield nos blocos cria um proc apenas parcial e não total como no caso do proc.

A titulo de curiosidade resolvi executar o mesmo codigo no Ruby 1.9.0 e os resultados embora tenham mantido a mesma sequencia no que toca a performance, tiveram valores um tanto diferentes…


user system total real
0.080000 0.000000 0.080000 ( 0.078453)
0.060000 0.000000 0.060000 ( 0.056817)
0.190000 0.000000 0.190000 ( 0.187863)
0.160000 0.000000 0.160000 ( 0.169452)
0.580000 0.000000 0.580000 ( 0.580614)
0.570000 0.000000 0.570000 ( 0.567870)

Os valores são claramente muito inferiores aos do Ruby 1.8 é sem duvida uma grande melhoria no que toca á performance do Ruby!

E tambem uma aproximação da performance entre o código inline e os blocos face aos blocos e proc que ficam ainda mais distanciados do que anteriormente.

Concorrência em Ruby com Threads

Uma Thread é uma forma de dividir um processo em diversas tarefas que podem ser executadas simultaneamente. O suporte de Thread é dado pelo sistema operativo, conhecido por Kernel-Level Thread ou implementada por uma biblioteca de uma determinada linguagem, sendo neste caso User-Level Thread.
Uma Thread permite que o utilizador do programa utilize determinada funcionalidade do ambiente enquanto outras Threads processão outras operações.
Em sistemas com um único CPU, cada Thread é processada aparentemente em simultâneo, a mudança entre cada Thread é feita de forma muito rápida dando a ideia de que o processamento é paralelo. Em sistemas com múltiplos CPUs ou Multi-Core as Threads podem ser executadas realmente de forma paralela.

Os exemplos apresentados iram utilizar a API Thread da linguagem Ruby, esta API não utiliza Threads nativas ou seja Kernel-Level Thread, mas sim User-Level Thread, desta forma é sacrificada a eficiência oferecida pelas Threads nativas ao Sistema Operativo mas ganha em portabilidade, dados que as Threads escritas em um Sistema Operativo irá funcionar em qualquer outra que suporte a linguagem Ruby.
Versões futuras como Ruby 2.0 poderão vir a suportar Threads nativas dando ao programador mais possibilidades de desenvolvimento.

Criar e Executar Threads

Em Ruby as Threads podem ser criadas como qualquer outro objecto, utilizando o método new, ao chamar este método devemos passar à Thread um bloco com o código a ser executado por ela. Vejamos uma exemplo muito simples da utilização de Threads em Ruby.

palavras = ["Um","Dois","Tres","Quatro"]

 
numeros = [1,2,3,4]
 
puts "Sem Threads...."

 
palavras.each { |palavra| puts(palavra) }
 
numeros.each { |numero| puts(numero) }

 
 
puts "Com Threads...."
 
Thread.new {
  palavras.each { |palavra| puts(palavra + " - ") }

}
 
Thread.new{
  numeros.each { |numero| puts(numero) }

}
 
sleep(5)

output

Sem Threads....
Um
Dois
Tres
Quatro
1
2
3
4
Com Threads....
Um - 1
Dois - 2
Tres - 3
Quatro – 4

Como podemos ver a diferença é bem visível, com apenas a Thread principal primeiro são processadas as palavras e em seguida os números, por sua vez com varias Threads em simultâneo, podemos ver que tanto as palavras como os números são processados em simultâneo, embora não seja um simultâneo real como vimos anteriormente. O comando sleep(5) no final faz com que o nosso programa espere 5 segundos, este comando é utilizado para que seja possível ver os resultados do programa com Threads. Mais à frente veremos como resolver este pequeno problema sem ter de recorrer a delays.

A Main Thread

Mesmo que não criemos Threads explicitamente, existe sempre pelo menos uma Thread em execução, esta Thread é chamada de Main Thread é nesta Thread que o programa está a correr. Podemos verificar isso através do seguinte código.

p(Thread.main)

output

#<Thread:0xb7ddc1bc run>

É retornado o ID em hexadecimal e o seu estado “run”, ou seja a informação sobre a Thread principal que arranca quando um interpretador Ruby começa a execução.

Estados de uma Thread

Cada Thread pode encontrar-se em um dos seguintes estados

* run           - A Thread está a ser executada.
* sleep         - A Thread está em espera ou sleep.
* aborting      - A Thread está a ser abortada.
* false         - A Thread terminou normalmente.
* nil           - A Thread terminou com uma excepção.

Podemos obter o estado de uma Thread usando o método status, ao pedido do estado de uma Thread podemos ainda obter “dead” no caso dessa Thread já não existir.
Vejamos um simples exemplo que permite ilustrar todos os estados da Thread.

puts(Thread.main.inspect)

 
puts(Thread.new{ sleep }.kill.inspect)

 
puts(Thread.new{ sleep }.inspect)
 
t1 = Thread.new{ }

 
puts(t1.status)
 
t2 = Thread.new{ raise("Exception") }

 
puts(t2.status)

output

#<Thread:0xb7dc41ac run>
#<Thread:0xb7db66c4 dead>

#<Thread:0xb7db6660 sleep>
false
nil

Obter o estado de uma Thread é uma operação bastante simples como se pode ser no exemplo acima.

Garantir que a Thread é Executada

Voltamos agora ao problema mostrado no nosso primeiro exemplo em que o programa terminava antes que as Threads fossem executadas, nesse exemplo resolvemos o problema com a inserção de um sleep no nosso programa, mas inserir delays na aplicação não é de forma alguma a melhor solução.
Para isso usamos o método join, este método obriga a que a Thread invocadora espere que a nova Thread termine e só então esta continua a execução.
Modificando então o nosso exemplo obtemos o seguinte exemplo.

palavras = ["Um","Dois","Tres","Quatro"]
 
numeros = [1,2,3,4]

 
t1 = Thread.new {
  palavras.each { |palavra| puts(palavra + " - ") }

}
 
t2 = Thread.new{
  numeros.each { |numero| puts(numero) }

}
 
t1.join()
t2.join()

output

Um - 1
Dois - 2
Tres - 3
Quatro - 4

Como podemos ver o resultado é precisamente o pretendido e desta vez sem delays artificiais e sem o tempo de execução negligenciado. O método join pode ainda ter como argumento um inteiro que define o tempo em segundos pelo qual a Thread chamadora deve esperar no máximo pela Thread invocada, basicamente é um timeout para o join.

Prioridades das Threads

Até agora demos ao Ruby total liberdade de gerir o tempo passado em cada uma das Threads. Mas em determinadas situações certas Threads são mais importantes que outras. Por exemplo temos uma Thread que guarda uma determinada quantidade de dados num ficheiro e outras que mostra o progresso da gravação, faz sentido que a Thread responsável pela escrita no ficheiro tenha disponível mais tempo que a Thread que apenas mostra o progresso da gravação.
Para isso o Ruby permite a utilização de inteiros para indicar a prioridade de cada Thread, em teoria Threads com prioridade mais alta tem mais tempo de execução que Threads com prioridade mais baixa, na pratica não é tão linear devido a outros factores como por exemplo o numero de Threads que estão a correr.
Uma vez que as prioridades em pequenos programas são praticamente impossíveis de visualizar vamos utilizar a uma função factorial que será chamada 100 vezes por cada um das Threads.

def fac(n)
  n == 1 ? 1 : n * fac(n - 1)

end
 
t1 = Thread.new{ 
  0.upto(100) {

    fac(50)
    puts("t1\n")
  }

}
 
t2 = Thread.new{
  0.upto(100) {

    fac(50)
    puts("t2\n")
  }

}
 
t3 = Thread.new{
  0.upto(100) {

    fac(50)
    puts("t3\n")
  }

}
 
t1.priority=0
t2.priority=0
t3.priority=1
 
t1.join()

t2.join()
t3.join()

Como podemos ver são criadas três Threads ambas com as mesmo funcionalidade, apenas com identificadores diferentes. Se colocar-mos todas as Threads com a mesma prioridade o output será o seguinte.

output

t1
t2
t3
t1
t2
t3
t1
t2
t3

Ou seja Thread 1 seguida pela Thread 2 e por fim a Thread 3, a sequência repete-se até ao final. Por outro lado se por exemplo dermos prioridade 1 à Thread t2 e 0 à Thread 1 e 2 como mostrado no código acima o resultado será o seguinte.

output

t1
t2
t3
t1
t3
t3
…
t3
t2
t1
t2
t1
t2

Ou seja a Thread 3 passa a ter mais tempo de execução que a Thread 1 e 2 assim sendo é executada e termina primeiro que as restantes e em seguida as Thread 1 e 2 correr em paralelo dado terem a mesma prioridade. Também podem ser utilizados numero negativos para definir a prioridade das Threads, por vezes pode até ser preferível o uso deste numero devido à Thread Main que tem prioridade 0 como vamos ver em seguida.

Prioridade da Main Thread

Como qualquer Thread também a Main Thread tem um grau de prioridade que por defeito é 0. Sendo assim ao atribuir valores positivos para as restantes Threads estamos a dar maior prioridade a estas Threads em relação à Main. Ao usar valores negativos para as outras Threads estamos a garantir que a Main Thread estará sempre a um nível superior em relação a todas as outras Threads garantindo a coerência de execução. No caso de se preferir usar números positivos podemos definir a prioridade da Main Thread com uma numero elevado que não será superado pelas restantes, por exemplo 100.

Thread.main.priority=100

Criar mas não executar uma Thread

Todos os exemplos que vimos até agora ao criar uma Thread ela automaticamente começa a sua execução, alias no exemplo anterior com prioridades podemos ver que antes das prioridades serem definidas as Thread são executadas pela ordem que são criadas e só em seguida passam a ser executadas pelas prioridades que lhes são dadas, para resolver este pequeno problema basta usarmos o método stop no inicio da definição da nossa Thread.

t1 = Thread.new{
  Thread.stop

  0.upto(100) { |i|
    puts i
  }
}

 
t1.run
t1.join()

output

0
1
...
99
100

Desta forma a Thread t1 apenas é executada após ser chamado o método run da Thread, caso este método não seja chamado a Thread não entra em execução, embora esteja criada. Se verificarmos o seu estado antes de chamar o método run podemos ver que a Thread se encontra no estado sleep.

Mutexes

Em alguns casos é necessário que duas ou mais Threads utilizem o mesmo recurso global, por exemplo uma variável global, nesta situação podemos obter resultados imprevisíveis uma vez que pode acontecer uma Thread alterar esse recursos no mesmo instante que outra Thread o utiliza, fazendo com que esta use uma recurso obsoleto. Vejamos o exemplo seguinte do que pode acontecer.

$i = 0

 
a = Thread.new { 100000.times { $i += 1 } }

 
b = Thread.new{ 100000.times{ $i += 1 } }

 
 
a.join
b.join
 
puts($i)

O que o nosso programa faz é simplesmente usar duas Threads para incrementar uma unidade à variável global “i” 100000 em cada Thread. O esperado seria termos o resultado de 200000 mas como podemos ver não é bem isso que obtemos mas sim o valor de 109589.
A razão pela qual isto acontece é porque ambas as Threads estão a utilizar o mesmo recurso e uma vez que ambas são executadas praticamente em paralelo em determinados pontos estas Threads vão utilizar um recurso que na realidade já não existe, ou seja a Thread “a” incrementa a variável mas nesse mesmo instante a Thread “b” também o faz uma vez que não existe controlo de acesso à variável. Vejamos um exemplo simples, a variável i tem valor 1000 a Thread “a” vai buscar esse valor e incremente passando a 1001 ao mesmo tempo a Thread “b” também vai buscar o valor e também o incrementa para 1001, perdendo-se então 1 unidade.

Para resolver este problema temos de garantir que uma Thread só terá acesso ao recursos depois que este tenha sido libertado por outra Thread que o tenha utilizado. Neste sentido o Ruby fornece a classe Mutex e através do método synchronize podemos garantir isso mesmo. Vejamos então o exemplo.

require "thread"
 
$i = 0;
 
semaphore = Mutex.new

 
a = Thread.new{
  semaphore.synchronize {
    100000.times { $i += 1 }

  }
}
 
b = Thread.new{
  semaphore.synchronize {

     100000.times { $i += 1 }
  }

}
 
a.join
b.join
 
puts($i)

E como esperado o valor obtido é 200000 isto deve-se o método synchronize bloqueia os recursos globais usados dentro do seu bloco e apenas os liberta após o bloco ter terminado.

Passagem de Argumentos à Thread

O método new permite ainda a passagem de argumentos para a Thread, tornando assim possível enviar dados para serem processados na Thread sem ter de recorrer a recursos globais como por exemplo variáveis globais.

t1 = Thread.new("magician") { |arg|
  Thread.stop

  puts(arg)
}
 
puts("Hello")
 
t1.run

t1.join

Como podemos ver enviamos uma string que depois é processada pelo método puts que imprime a string. E por sua vez a Main Thread imprime a String “Hello” obtendo assim o output seguinte.

output

Hello 
magician

Retornar Valores de uma Thread

É também possível retornar valores de uma Thread, para isso utilizamos o método value. Este método retorna o valor final após a execução do bloco da Thread, caso nenhum valor tenha sido obtido o resultado obtido por este método será nil. Vamos então ver alguns casos de como podemos obter estes valores.

t1 = Thread.new{
  puts "T1"

}
 
t2 = Thread.new{
  5 + 5
}

 
t3 = Thread.new{
  "X"
}
 
t4 = Thread.new{

  4 + 4
  "Y"
}
 
t5 = Thread.new{

  val = 1 + 1
  "T5"
  val
}
 
t6 = Thread.new{

  2 + 2
  "T"
  puts "T6"
}

 
puts("Valor = " + (t1.value == nil ? "nil" : t1.value.to_s))

puts("Valor = " + t2.value.to_s)
puts("Valor = " + t3.value)

puts("Valor = " + t4.value)
puts("Valor = " + t5.value.to_s)

puts("Valor = " + (t6.value == nil ? "nil" : t6.value.to_s))

output

T1
T6
Valor = nil
Valor = 10
Valor = X
Valor = Y
Valor = 2
Valor = nil

Como é possível ver a Thread t1 não retorna qualquer valor como tal o valor resultante é nil, por sua vez a Thread t2 retorna um inteiro com a soma de 5 + 5 da mesma forma que a Thread t3 retorna uma string com “X”. As Threads 4, 5 e 6 mostram que o valor retornado é sempre o último a ser processado no bloco, embora não seja colocada a palavra “return” o Ruby assume que a ultima instrução contem o valor a retornar como podemos ver na Thread t6.

Matar uma Thread

Por vezes pode ser necessário terminar o Thread antes que ela termine a execução do seu bloco de código. Isto pode acontecer por exemplo quando queremos cancelar uma operação que por algum motivo já não queremos que seja executadas mas em que a sua execução já foi iniciada.
Vejamos o exemplo seguinte em que uma Thread imprime a cada segundo a String “T1” indefinidamente e após esperar 10 segundos o nosso programa força a nossa Thread a terminar.

t1 = Thread.new{
  while true
    puts("T1")

    sleep(1)
  end
}
 
 
sleep(10)

 
t1.terminate
puts(t1.alive?)

output

t1
t1
...
t1
t1
false

O método kill obriga a que a Thread termine e retorna-a em seguida passa a execução para outra Thread. Caso não existam mais Threads a executar ou a Thread terminada seja a Main Thread então é processado o encerramento do programa. Para além do método kill, podemos ainda usar os métodos exit ou terminate, qualquer um deles realiza a mesma operação.

Passar a Execução para a proxima Thread

Em determinadas situações podemos querer que certa Thread que esteja em execução, passe a execução para outras Threads, por exemplo queremos que após a Thread X executar as três primeiras operações ela passe a execução para a Thread Y par que esta execute o seu bloco de execução. Para isso podemos utilizar o método pass da classe Thread, este método faz isso mesmo, invoca o “calendário” de Threads passando a execução à próxima Thread. Vejamos um exemplo que ilustra o uso deste método.

s = ""

 
t1 = Thread.new{
  s << "a"
  Thread.pass

  s << "b"
  Thread.pass
  s << "c"
}

 
t2 = Thread.new{
  s << "x"
  Thread.pass

  s << "y"
  Thread.pass
  s << "z"
}

 
t1.join
t2.join
 
puts(s)

Neste caso são iniciadas duas Threads a primeira t1, concatena a string “a” à string na variável “s” e passa a execução à Thread t2 que concatena também a String e passa a execução novamente para a Thread t1 e por ai fora até ambas terminarem os seus blocos de execução. Resultando o seguinte output.

output

axbycz

Caso não fosse usado o método pass o resultado seria diferente, a Thread t1 iria executar todo o seu bloco de código dado ser pequeno e em seguida seria a Thread t2 a fazê-lo. Nesta situação o output obtido seria o seguinte.

output

abcxyz

Variáveis Locais da Thread

É ainda possível criar e obter variáveis locais numa Thread, podemos cria-las dentro da Thread ou fora dela bem como ter acesso a elas. O processo é muito semelhante a usar uma Hash, passemos a uma exemplo que ilustra o processo.

t1 = Thread.new{

  Thread.current[:nome] = "magician"
  Thread.current[:lvl] = 3

}
 
puts("LVL ? " + t1.key?(:lvl).to_s)

puts("Nome = " + t1[:nome])
 
t1[:lang] = "ruby"

 
puts(t1.keys)
t1.join

output

LVL ? true
Nome = magician
lang
lvl
nome

Pelo exemplo podemos ver que é extremamente simples a utilização deste recurso, caso estejamos dentro da Thread basta usar o current e trabalhar de igual forma como se fosse uma Hash, caso estejamos fora da Thread basta usar a variável que detém o objecto Thread como se fosse uma Hash. Isto permite por exemplo a passagem de valores para dentro da Thread caso esta já tenha sido iniciada pelo método new, ou caso queiramos guardar vários valores que podem depois ser consultados no exterior da Thread.

Subclasses da Classe Thread

Para além da forma mais comum de criar Threads que é utilizando o método new, é possível, implementar Threads criando subclasses da mesma. Embora esta forma seja mais comum em linguagens por exemplo como Java e C# é também possível fazê-lo em Ruby. Vamos então ver um exemplo simples de uma subclasse de Thread.

class MinhaThread < Thread

 
  def initialize(*args)
    super{
      Thread.stop

      args.each { |arg|
        print(arg+"\n")
      }

    }
  end
 
  def run
    print("Thread a iniciar...\n")

    super
  end
 
end
 
 
m = MinhaThread.new("white", "magician", "P@P")

m.run
m.join

output

Thread a iniciar...
white
magician
P@P

Embora não seja tão trivial como os exemplos dados anteriormente este é uma exemplo simples de uma subclasse da classe Thread em que reescrevemos os métodos run e initialize de forma a que façam mais algumas acções, poderíamos também reescrever outros métodos como por exemplo o exit fazendo com que para além de terminar a Thread realiza-se outra operação que estaria implícita ao terminar da Thread.

ThreadGroups

Para finalizar vamos falar de ThreadGroups, no fundo um ThreadGroup é apenas um conjuntos de Threads, imaginemos o group como um array que contem N Threads. Uma Thread apenas pode pertencer a um grupo, ao ser adicionada ao grupo x caso esteja no grupo y ela é removida do grupo y no instante em que é adicionada ao grupo x.
No momento em que é criada uma Thread pertence ao mesmo grupo a que pertence a Thread que a criou, Threads terminadas ou “mortas” tem ThreadGroup nil ou seja não estão em nenhum grupo.
Para além de permitir adicionar Threads, um grupo permite criar uma lista de todas as Threads que pertencem a esse grupo, para isso basta usar o método list. Permite ainda bloquear o grupo ou seja o grupo não deixa serem adicionadas novas Threads nem que sejam removidas as Threads existentes. Temos agora um pequeno exemplo de como usar ThreadGroups.

tg = ThreadGroup.new
 

t1 = Thread.new {
  Thread.stop
}
 
t2 = Thread.new {

  Thread.stop
}
 
 
puts(t1.group.to_s + " == " + t2.group.to_s + " == " + Thread.main.group.to_s)

 
tg.add(t1)
tg.add(t2)
 
puts(t1.group.to_s + " == " + t2.group.to_s + " != " + Thread.main.group.to_s)

 
puts("Threads = " + tg.list.to_s)
 
t3 = Thread.new {

 
}
 
puts(t3.group)
 
tg.enclose
 
t4 = Thread.new{

  Thread.stop
}
 
tg.add(t4)

output

#<ThreadGroup:0xb7ca81c4> == #<ThreadGroup:0xb7ca81c4> == #<ThreadGroup:0xb7ca81c4>
#<ThreadGroup:0xb7c99fc0> == #<ThreadGroup:0xb7c99fc0> != #<ThreadGroup:0xb7ca81c4>

Threads = #<Thread:0xb7c99f48>#<Thread:0xb7c99fac>
nil
…`add': can't move to the enclosed thread group (ThreadError)...

Pelo exemplo podemos ver que as Threads ao serem criadas ficam com o mesmo grupo que a Thread que as criou, neste caso a Main Thread, depois de serem adicionadas a outro grupo, deixam de pertencer ao grupo onde se encontra a Main Thread e passa a pertencer ao novo grupo. Por sua vez as Threads terminadas tem grupo nil como é o caso da Thread t3, em seguida podemos ver que ao bloquear o grupo com o enclose e ao tentar-mos adicionar uma nova Thread a esse grupo é lançado um ThreadError dado que não é possível adicionar ou remover Threads deste grupo. É de sublinhar que um grupo que seja definido como enclose, irá permanecer neste estado, uma vez que não é actualmente possível inverter o processo de bloqueio.

Conclusão

Ao longo deste artigo foram exploradas diversas vertentes das Threads em Ruby, conteúdos como a sua criação, controlo, processamento, finalização e até organização foram explicados e exemplificados de forma simples. É possível agora criar Threads bastante complexas e funcionais a partir dos conhecimentos básicos aqui explicados.
Como tudo em programação também as Threads devem ser usadas com ponderação e controlo, e não usar-las por tudo e por nada ou então apenas não as usar.

Referencias

http://www.ruby-doc.org/core/classes/Thread.html

http://www.ruby-doc.org/core/classes/ThreadGroup.html

Autoria

Este artigo foi originalmente escrito por Fábio Correia

Revista Programar – 20ª Edição Junho 2009

Download da Revista

Download da Revista

Estando já na recta final do ano lectivo, e com algum esforço devido ao calendário de exames, a Revista PROGRAMAR volta a trazer uma nova edição. Desta vez, no 20º número poderá encontrar um artigo que explora o tema da metaprogramação e templates em C++, as continuações dos artigos acerca da linguagem AWK e do Google Web Toolkit, um novo artigo sobre o Arduino e uma breve apresentação do projecto DEI@Academy.

Aproveitamos também para tornar público o apreço pelos autores e principalmente pela incansável equipa de revisão, cujo trabalho já produz resultados bem visíveis na qualidade da revista.

http://www.revista-programar.info/front/edition/20

Ruby include ou extend?

A resposta é os dois! Ou seja ambos tem um funcionamento semelhante mas o resultado final é diferente dai poder usar um ou outro dependendo do resultado final que queremos obter.

O include é chamado dentro de uma classe, basicamente o que ele faz é “pegar” nos métodos do módulo e adicionar esses métodos à classe como métodos de instância ou seja os métodos do módulo podem ser acedidos através dos objectos dessa classe. Por exemplo.

x = X.new
x.metodo_do_modulo(10)

Por sua vez o extend é também chamado dentro de uma classe, também ele “pega” nos métodos do módulos e adiciona esses métodos à classe mas neste caso como métodos de classe ou seja os métodos do módulos podem ser acedidos através da classe. Vejamos o exemplo

X.metodo_do_modulo("Hello")

É essa a diferença entre os dois, o include insere os métodos na instância da classe enquanto o extend insere na classe. Temos agora um pequeno exemplo na utilização dos dois.

module A
  def print_nome(nome)
    puts("Nome = " + nome)
  end
end

module B
  def print_hello()
    puts("Hello")
  end
end

class Classe
  include A
  extend B
end

a = Classe.new

a.print_nome("Magician")
Classe.print_hello()
a.print_hello()

output

Nome = Magician
Hello
22: undefined method `print_hello' for #<Classe:0xb7c3d428> (NoMethodError)

Como podemos ver o módulo A é adicionada com include como tá os seus métodos pode ser acedidos a partir do objecto, por outro lado o módulo B que é adicionado com extend os seus métodos apenas podem ser acedidos através da classe. Ao tentar aceder o métodos do módulo B como um método de instância é retornado um erro, dado que é um método de classe e não de instância.