Servidor HTTP light e descomplicado no Java 6

| | Comentários(11) | TrackBacks (1)
lightweight-1.jpg Poucas pessoas sabem, mas a versão 6 do JDK da Sun vem com um HttpServer light nativo. Não é necessário mais escrever http sobre socket ou utilizar alguma biblioteca externa. Mas atenção: esta classe está em um pacote da sun e não há garantias que outras implementações java terão estas classes.

De qualquer forma, é muito fácil utilizar a classe para resolver problemas onde seja necessário um HttpServer out-of-the-box.

Tudo o que deve ser feito é criar uma nova instância da classe HttpServer (pacote com.sun.net.httpserver) através do método estático create, passando o IP e a porta aos quais o servidor irá ouvir, além do backlog, que é o tamanho da fila que o servidor irá armazenar com requests que estão esperando para serem atendidos.
HttpServer server = 
	HttpServer.create(new InetSocketAddress(8088), 0);
Depois disso, você deve criar uma classe que implemente a interface HttpHandler para atender suas requisições. Nela é colocada a regra de negócio do que deve ser feito e o que deve ser respondido ao cliente.

A cada novo request, o método handle é chamado, tendo um HttpExchange como parâmetro. Esta classe tem uma série de propriedades que facilitam a interação com o servidor.

Por fim, você deve criar um contexto no qual este handler irá ser utilizado. O contexto nada mais é do que uma espécie de virtual folder para sua URL. Por exemplo, se você utilizar o contexto "/exemplo", o handler irá responder quaisquer requisições para http://seuservidor:8088/exemplo (incluindo arquivos dentro desta pasta, parâmetros e sub-pastas). Veja este exemplo:
public static void main(String[] args) throws IOException {
	// cria um novo servidor que vai ouvir a porta 8088 local
	HttpServer server = 
		HttpServer.create(new InetSocketAddress(8088), 0);

	// cria um contexto com um HttpHandler dinâmico
	server.createContext("/exemplo", new HttpHandler() {

		@Override
		public void handle(HttpExchange xchg) 
			throws IOException {

			// corpo da resposta
			StringBuffer response = new StringBuffer();

			// o corpo da resposta será apenas uma indicação
			// de qual foi o método do request (GET / POST)
			response.append("<html><body>");
			response.append("Request method: ");
			response.append(xchg.getRequestMethod());
			response.append("</body></html>");

			// envia uma resposta de código 200 (OK) para 
			// o cliente e com o Content-Length, que é o 
			// tamanho da resposta
			xchg.sendResponseHeaders(200, response.length());
			
			// recupera o stream de saída
			OutputStream os = xchg.getResponseBody();
			// imprime nele a resposta
			os.write(response.toString().getBytes());
			// e fecha o stream (com try..finally talvez?)
			os.close();
		}
		
	});
	
	// por fim inicializamos o servidor
	server.start();
}
Se você rodar esta classe e apontar seu servidor para o endereço mencionado, você vai ver a mensagem conforme abaixo:

httpserver-screenshot.jpg Porém se você for mais além, e tentar abrir várias requisições em paralelo, verá que a aplicação começará a responder de forma bem lenta. Para resolver o problema, precisamos criar uma thread para cada requisição. No meu caso criei uma classe que estende thread para atender cada uma das requisições:
public class HandleThread extends Thread {
    public HttpExchange xchg;
    
    public HandleThread(HttpExchange xchg) {
        this.xchg = xchg;
    }
    
    @Override
    public void run() {            
        try {
            // corpo da resposta
            StringBuffer response = new StringBuffer();

            // o corpo da resposta será apenas uma indicação
            // de qual foi o método do request (GET / POST)
            response.append("<html><body>");
            response.append("Request thread: ");
            response.append(Thread.currentThread().getId());
            response.append("</body></html>");

	    // o resto continua igual (...)
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}
Finalmente, alteramos a classe principal para criar uma thread a cada nova requisição:
// (...)
// cria um contexto com um HttpHandler dinâmico
server.createContext("/echo", new HttpHandler() {

    @Override
    public void handle(HttpExchange xchg) throws IOException {
        HandleThread ht = new HandleThread(xchg);
        ht.start();
    }
    
});
// (...)
Vale a pena salientar que existe suporte nas APIs também a HTTPS (veja Javadoc nos links abaixo).

Estes exemplos têm apenas um caráter didático. Em uma aplicação séria, não seriam criadas threads a esmo: usaríamos um pool de threads e técnicas mais apuradas para controle de requisições.

Mas acho que a idéia era esta: dar um apanhado geral de como usar estas novas classes, oferecidas pelo JDK 6.

Aguardo seus comentários sobre o que você pensa destas idéias.



Links:

1 TrackBacks

Veja abaixo a lista de Blogs que referenciam este artigo: Servidor HTTP light e descomplicado no Java 6.

URL de TrackBack para o artigo: http://blogs.felipecoury.com/mt-tb.cgi/7

Servidor HTTP descomplicado no Java 6 proveniente de Felipe via Rec6, postado em February 13, 2008 7:17 PM

Veja como implementar de uma forma bem simples um HTTP Server light no Java 6. Leia Mais

11 Comments

Parabens pelo post!
Para uma aplciacao desktop que precisa expor algo via web, é interessante o uso desse servidor. Tambem para criar os webservices sem uso de nenhuma outra biblioteca ou servidor.

Paulo, muito obrigado! Eu mantenho uma plataforma com um grau razoável de paralelismo, obviamente desenvolvida em java, que recebe requisições via HTTP. Hoje nós usamos Socket direto, com algumas otimizações.

Estou pensando em migrar gradualmente para este HttpServer para ver o que acontece. Pretendo postar algo a respeito aqui no meu Blog tão logo isso seja possível.

Abração!

Felipe, recomendo que voce olhe o Jetty, que tem um kernel que da para isolar e usa-lo separadamente, de maneira muito simples. Ele usa java.nio e tem serios ganhos de performance em relacao ao HttpServer do java6 ou ate mesmo ao Tomcat...

Boa dica, não tinha conhecimento do Jetty não! Vou dar uma pesquisada nele. Obrigado!

Parabéns Felipe pelo post, esta muito bom!

Obrigado Diego!

Olá Felipe!

Muito legal esse artigo.

Coloquei uma referência no meu blog.

Valeu!

Valeu Roberto! Abraços!

Legal essa informação, muito útil. Não é coisa que se encontra na documentação padrão :)

Felipe, foi de grande valia o seu post. Grato e parabéns!

Felipe,

Gostei muito do seu post, é um recurso muito útil.

Saberia como enviar e tratar dados via POST para um servidor destes? Tentei vários métodos mas não consegui nada ainda.
Se puder dar uma ajuda, agradeço.

Deixe seu Comentário

Sobre este Artigo

Esta página contém um único artigo, escrito por Felipe Coury e publicado em February 13, 2008 6:05 PM.

Faça seu próprio mouse ergonômico é o artigo anterior deste blog.

log5j é o próximo artigo deste blog.

O conteúdo atual está no índice principal o procure nos arquivos, onde você encontra todo o conteúdo.

Clicky Web Analytics