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

Anúncios

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.