Arquivo da categoria: Arquitetura

Entenda os MVCs e os frameworks Action e Component Based

É muito comum encontrar desenvolvedores que aprendem componentes do JSF como o Primefaces e o Richfaces e gostariam de usá-las em seus projetos que utilizam frameworks web como o Spring MVC, Struts e VRaptor, porém, isso não é possível. Por que? Entenda os diferentes funcionamentos do MVC, aprenda como funcionam os frameworks e tire para sempre essa e outras dúvidas!

Um @Controller do SpringMVC

Quando trabalhamos com o SpringMVC, tratamos as requisições enviadas pelos usuários dentro de classes conhecidas como Controller. É uma classe simples, anotada com @Controller onde implementamos métodos que tratam a requisição para uma determinada URL, indicada através da anotação @RequestMapping.

@Controller
public class ProdutosController {
   @RequestMapping("adicionaProduto")
   public void adiciona(Produto p, HttpServletRequest req) {
      // pega o produto recebido no request e grava no banco de dados
   }
}

Quando a requisição é submetida para o endereço adicionaProduto e envia os parâmetros adequados para preencher o objeto Produto, ele é gravado no banco. Porém, precisamos devolver uma resposta para o usuário, que deverá conter o nome do produto adicionado, então podemos disponibilizar um atributo ao JSP através do request que recebemos no nosso método.

@RequestMapping("adicionaProduto")
public void adiciona(Produto p, HttpServletRequest req) {
   // pega o produto recebido no request e grava no banco de dados
   req.setAttribute("nome", p.getNome());
}

Então logo após a execução da ação, indicamos para o SpringMVC que o produto-adicionado.jspdeve ser mostrado. Para isso, basta fazermos nosso método retornar uma String com o nome do JSP.

@RequestMapping("adicionaProduto")
public String adiciona(Produto p) {
   // pega o produto recebido no request e grava no banco de dados
   req.setAttribute("nome", p.getNome());
   return "produto-adicionado";
}

Dessa forma, após a execução, o JSP será exibido para o usuário.

<h2>Produto ${nome} adicionado com sucesso</h2>

Onde está o MVC aí?

O mais importante nisso tudo, é notar que existe uma separação clara entre as responsabilidades no tratamento da requisição.

Quando um usuário submete uma requisição para o endereçohttp://localhost:8080/projeto/adicionaProduto, o SpringMVC a recebe e descobre, de alguma maneira, qual é o @Controller e qual método deve ser responsável por tratá-la. Nesse caso, o SpringMVC está fazendo o papel do Front Controller, que delega a responsabilidade para umController secundário, no caso a classe ProdutosController e o método adiciona. Chamamos o responsável por esse trabalho de Controlador.

Os passos do controlador

O próximo passo é a execução das regras de negócio, que podem envolver a execução da persistência das informações, validações e outras tarefas que dizem respeito aos requisitos funcionais da aplicação. Chamamos as classes que fazem esse trabalho de Modelo.

@Controller invoca o Modelo para realizar as regras de negócio

Após a execução do Modelo e de todas as as regras de negócio, o JSP contendo a resposta adequada deve ser exibido para o usuário. Nesse ponto, temos as regras de visualização implementadas, como por exemplo, exibir as informações em vermelho, dada alguma característica. Essas regras ficam todas centralizadas no JSP, que nesse caso, faz um papel que chamamos de Visão, ou seja, é o responsável pelo que o usuário irá visualizar como resultado.

@Controller indica qual visão será devolvida para o usuário

Juntando tudo o que vimos, temos o MVC – Modelo, Visão e Controlador, cujo objetivo é separar as responsabilidades da aplicação, fazendo com que tenhamos um código mais fácil de ser mantido.

Os frameworks Action Based e o MVC Push

….

Saiba mais em: http://blog.caelum.com.br/entenda-os-mvcs-e-os-frameworks-action-e-component-based/

Fonte: Adriano Almeida / Caelum

Enciclopédia do Heroku

Fonte: AkitaOnRails

Atualização 05/06: Depois que escrevi este artigo, encontrei um outro muito bom que recomendo a leitura por ter mais detalhes para complementar. O artigo se chamaHeroku isn’t for Idiots

Se você quer lançar uma aplicação Rails rapidamente, não existe melhor solução do que o Heroku. Para quem não conhece, o Heroku é um Paas (Platform as a Service) que roda sobre o Amazon EC2 (que é um IaaS ou Infrastructure as a Service). O Heroku automatiza a criação de uma nova máquina virtual (volátil! isso é importante) e configura todo o ambiente para rodar Ruby.

O Heroku usa uma unidade de máquina virtual chamada “Dyno”, a grosso modo, considere um Dyno como uma máquina virtual “pequena” com 4 cores e até 512Mb de RAMsem swap file e sem suporte a persistência de arquivos (não faça uploads diretamente no diretório public/uploads ou algo assim, sempre configure para mandar para a Amazon S3, aprenda como neste tutorial). Configurar um novo ambiente é simples, o próprio Heroku tem uma boa documentação ensinando como e recomendo ler antes de continuar.

Subir uma única dyno usando um banco de dados compartilhado PostgreSQL é de graça, o que é excelente para testar sua aplicação. Obviamente apenas um único dyno é pouco para qualquer aplicação séria lançada em produção para o público.

O Heroku fornece “stacks” padrão que é o perfil pré-configurado de um dyno para uma determinada plataforma. Para Ruby e Rails a mais atual (na data de publicação deste post) é a Celadon Cedar, a anterior era a Badious Bamboo portanto se encontrar um tutorial qualquer de Heroku por aí, cheque sobre qual stack estamos falando, só use se for para Cedar.

Concorrência num Dyno

A primeira coisa que me chamou a atenção é que a configuração recomendada é executar uma aplicação Ruby usando o servidor Thin. Pense no Thin como uma evolução do venerado Mongrel mas que suporta executar Eventmachine internamente. Na prática é um Mongrel melhorado, o que significa que cada Dyno, por padrão, não suporta mais do que 1 única execução concorrente (não confundir com “requisições por segundo”!! Muita gente erra isso. Um único processo com uma única execução concorrente pode executar várias requisições por segundo, basta cada requisição demorar menos de 1 segundo).

Executar múltiplos Thins poderia ser possível mas se queremos mais processos rodando simultaneamente para responder mais requisições ao mesmo tempo, a melhor opção é usar Unicorn. Leiam a documentação para aprender as peculiaridades do Unicorn, na prática pense nele como um controlador de processos Ruby. O melhor tutorial para usar Unicorn no Heroku continua sendo o do Michael van Rooijen. Não vou repetir tudo que ele disse, mas as partes importantes são:

Substituir a gem thin pela unicorn na sua Gemfile:

group :production, :staging do
  gem 'unicorn'
  ...
end

Em seguida precisamos declarar quantos processos do Unicorn queremos de cada vez, então crie um arquivo config/unicorn.rb com o seguinte:

worker_processes 4 # amount of unicorn workers to spin up
timeout 50         # restarts workers that hang for 30 seconds

Leiam atentamente o post original do Michael, ele explica alguns benchmarks que ele fez para determinar a quantidade ótima de workers de Unicorn e o ideal é entre 3 e 4 processos. A informação não-oficial que eu tenho é que cada dyno tem até 4 CPUs, o que justifica esse número de processos. Mais do que isso, chequem sempre quanto de memória cada processo consome (ferramentas como NewRelic ajudam nisso) pois a somatória precisa ser menor que 512Mb ou você terá problemas.

Finalmente, a stack Cedar permite configurar perfis de dynos num arquivo chamado Procfile que fica na raíz do seu projeto. Para que a dyno levante com Unicorn coloque o seguinte:

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

Continue aqui: http://www.akitaonrails.com/2012/04/20/heroku-tips-enciclopedia-do-heroku?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+AkitaOnRails+%28Akita+On+Rails%29#.UDYbXdZlT3w

Conhecendo o Play Framework

Quem já experimentou frameworks como Django e Rails sabe bem que o desenvolvimento de aplicações web pode perfeitamente ser produtivo e até divertido. Isso estava fora da realidade do mundo Java antes do surgimento do Play Framework!.

O Play é um framework full stack, stateless, focado em produtividade, bastante parecido com Django e Rails. É construído sobre uma arquitetura assíncrona baseada em eventos, sendo muito simples de escalar. Possui suporte a módulos, gerenciamento de dependências e configuração baseada em arquivos properties. Não é necessário compilar e realizar deploy em um servidor de aplicações, o Play vem com um servidor embutido, e durante o desenvolvimento basta alterar o código, salvar e recarregar o browser. O próprio framework compila e recarrega a aplicação, como em linguagens interpretadas. E se houverem erros, eles serão exibidos no próprio browser, de forma intuitiva.

O Play vem com um conjunto de ferramentas em modo console para várias tarefas, como criar novos projetos, iniciar a aplicação com o servidor embutido, executar testes, configurar dependências, gerar um pacote WAR, etc, o que o torna bastante produtivo tanto para novos projetos quanto para dar manutenção em um projeto já existente. É possível desenvolver em Java ou Scala (sim, Scala!).

Vamos enumerar alguma das caracteristicas do framework:

  • Limpo, Rápido e Dinâmico
  • Produtivo, Poderoso e Divertido!
  • Puro Java (Permite a importação e uso de qualquer biblioteca Java)
  • Utiliza o sistema de template Groovy(Groovy trabalha de forma transparente com todos os objetos e bibliotecas Java existentes e compila diretamente para Java bytecode tanto no desenvolvimento de aplicações como no modo script.)
  • Utiliza sistemas de tradução de Rotas
  • Facilidade na resolução de erro
  • Padrão MVC
  • Assíncrono (I/O não bloqueante)
  • Stateless
  • Arquitetura RESTful ( REST (ou Representational State Transfer) é um estilo de desenvolvimento para sistemas distribuídos que privilegia a trasferência de estado sem envolver camadas de mapeamento (como o SOAP faz). Assim, os verbos GET, POST, PUT e DELETE mapeam quase que diretamente para ações READ, UPDATE, CREATE e DELETE (CRUD).
  • Persistência com JPA (JPA é uma especificação de Persistência da plataforma Java, para persistir as classes java em bancos de dados relacionais. )
  • Extensível (Através de Módulos / Bibliotecas Java)
  • TDD – Test Development Drive (Test Driven Development (TDD) ou em português Desenvolvimento dirigido por testes )

 

Como iniciar uma aplicação no Play?

É facil, basta usar o comando no consolhe (Terminal no Linux e Prompt de comando no Windows, lembrando que você terá que estar dentro da pasta do framework no windows e no linux ter adicionado o caminho da pasta ao PATH para executar o comando play):

play new nomedaaplicacao

Toda a estrutura do projeto será criada, incluindo arquivos de configuração e o roteamento inicial da aplicação. Se você deseja abrir o projeto no Eclipse ou no Netbeans, o Play! cria o projeto para você. Basta fazer assim:

play eclipsify nomedaaplicacao

Ou se preferir o Netbeans:

play netbeansify nomedaaplicacao

Dessa forma, você poderá importar o projeto de sua IDE e todas as configurações estarão realizadas. Outro comando importante é a gerador de war (deploy).

 play war nomeDoProjeto -o nomeDoProjeto.war

Pronto, feito isto basta começar a programar no projeto usando Java ou Scala 🙂 !

Slide da apresentação feita na SWX: play-framework-apresentaçao

Código de uma pequena aplicação de uma agenda feita na SWX:

No diretório /conf no arquivo application.conf , caso use mysql, basta localizar e modificar o padrão para seu usuário e senha do mysql e o nome do banco (No exemplo a seguir está sem senha).

# To connect to a local MySQL5 database, use:
db=mysql:acson@minha_agenda
// acson -> usuário do banco,  minha_agenda -> banco

No diretório /app/controllers , modifique o Application.java (ou crie se não existir)  para:

package controllers;
import play.*;
import play.mvc.*;
import play.data.validation.*;
import java.util.*;
import models.*;
public class Application extends Controller {
public static void index() {
 List<Contato> contatos = Contato.find("order by nome asc").fetch();
 render(contatos);
 }
public static void inserir() {
 render();
 }

 public static void visualizar(String email) {
 Contato contato = Contato.find("email", email).first();
 render(contato);
 }

 public static void editar(String email) {
 Contato contato = Contato.find("email", email).first();
 render(contato);
 }

 public static void excluir(String email) {
 Contato contato = Contato.find("email", email).first();
 contato.delete();
 index();
 }

 public static void cadastrar_contato(@Required String nome, @Required String email, String telefone, String twitter, String skype) {
 Contato contato = new Contato(nome, email, telefone, twitter, skype);
 if (validation.hasErrors()) {
 render("Application/inserir.html", contato);
 }

 contato.save();
 index();
 }

 public static void editar_contato(long id) {
 validation.required(request.params.get("nome"));
 validation.required(request.params.get("email"));

 Contato contato = Contato.find("id", id).first();

 if (validation.hasErrors()) {
 render("Application/editar.html", contato);
 }

 contato.nome = request.params.get("nome");
 contato.telefone = request.params.get("telefone");
 contato.email = request.params.get("email");
 contato.twitter = request.params.get("twitter");
 contato.skype = request.params.get("skype");

 contato.save();
 index();
 }
}

 

No diretório /app/models  , crie a classe Contato (Contato.java) com o seguinte código:

package models;
import play.*;
import play.db.jpa.*;
import javax.persistence.*;
import java.util.*;
@Entity
public class Contato extends Model {

 public String nome;
 public String email;

 public String telefone;
 public String twitter;
 public String skype;

 public Contato(String nome, String email, String telefone, String twitter, String skype) {
 this.nome = nome;
 this.email = email;
 this.telefone = telefone;
 this.twitter = twitter;
 this.skype = skype;
 }

}

No diretório /app/views  , crie o arquivo application.html com o seguinte código:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>.: Agenda de Contatos - #{get 'title' /}</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/principal.css'}">
 #{get 'moreStyles' /}
 <script src="@{'/public/javascripts/jquery-1.4.2.min.js'}" type="text/javascript" charset="utf-8"></script>
 #{get 'moreScripts' /}
</head>
<body id="page1">
 <div id="geral">

 <div id="conteudo">
 <div id="logo"></div>
 <div class="linha">
 <div class="titulo">.: Agenda de Contatos</div>
 #{if request.actionMethod != "inserir" && request.actionMethod != "editar"}
 <div class="text"><a href="@{Application.inserir()}" class="button">+ Incluir Contato</a></div>
 #{/if}
 </div>
 <div class="linha"></div>
 #{doLayout /}
 </div>
 </div>
 </div>
</body>
</html>

No diretório /app/views/Application  , crie o arquivo inserir.html com o seguinte código:

#{extends 'application.html' /}
#{set title:'Inclusão' /}
#{form @Application.cadastrar_contato()}
 #{ifErrors}
 <div class="linha"><p style="color:red;"><strong>ATENÇÃO: </strong>Preencha todos os campos obrigatórios.</p></div>
 <div class="linha"></div>
 #{/ifErrors}
<div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="nome">Nome:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="nome" id="nome" value="${params.nome}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="telefone">Telefone:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="telefone" id="telefone" value="${params.telefone}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="email">Email:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="email" id="email" value="${params.email}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="twitter">Twitter:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="twitter" id="twitter" value="${params.twitter}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="skype">Skype:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="skype" id="skype" value="${params.skype}" /></p>
 </div>
 </div>
 </div>
 <div class="btn-submit">
 <p><input type="submit" value="Cadastrar o Contato" /></p>
 </div>

#{/form}

No diretório /app/views/Application  , crie o arquivo visualizar.html com o seguinte código:

#{extends 'application.html' /}
#{set title:'Visualiza‹o' /}
<div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="nome">Nome:</label></p>
 </div>
 <div class="dir">
 <p>${contato.nome}</p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="telefone">Telefone:</label></p>
 </div>
 <div class="dir">
 <p>${contato.telefone}</p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="email">Email:</label></p>
 </div>
 <div class="dir">
 <p>${contato.email}</p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="twitter">Twitter:</label></p>
 </div>
 <div class="dir">
 <p>${contato.twitter}</p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="skype">Skype:</label></p>
 </div>
 <div class="dir">
 <p>${contato.skype}</p>
 </div>
 </div>
 </div>
 <div class="btn-submit">
 <p>
 <a href="@{Application.index()}">Cancelar</a>
 <a href="@{Application.editar(contato.email)}">Editar</a>
 <a href="@{Application.excluir(contato.email)}" onClick="if(!confirm('Deseja excluir este contato?')){return false;}">Excluir</a>
 </p>
 </div>

No diretório /app/views/Application  , crie o arquivo editar.html com o seguinte código:

#{extends 'application.html' /}
#{set title:'Edição' /}
#{form @Application.editar_contato(contato.id)}
 #{ifErrors}
 <div class="linha"><p style="color:red;"><strong>ATENÇÃO: </strong>Preencha todos os campos obrigatórios.</p></div>
 <div class="linha"></div>
 #{/ifErrors}
<div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="nome">Nome:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="nome" id="nome" value="${contato.nome}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="telefone">Telefone:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="telefone" id="telefone" value="${contato.telefone}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="email">Email:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="email" id="email" value="${contato.email}" /></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="twitter">Twitter:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="twitter" id="twitter" value="${contato.twitter}"/></p>
 </div>
 </div>
 </div>
 <div class="linha">
 <div class="duas-colunas">
 <div class="esq">
 <p><label for="skype">Skype:</label></p>
 </div>
 <div class="dir">
 <p><input type="text" name="skype" id="skype" value="${contato.skype}" /></p>
 </div>
 </div>
 </div>
 <div class="btn-submit">
 <p><input type="submit" value="Editar o Contato" /></p>
 </div>

#{/form}

No diretório /app/views/Application  , crie o arquivo index.html com o seguinte código:

#{extends 'application.html' /}
#{set title:'Listagem' /}
#{if contatos.size() == 0}
 <div class="linha">
 <p>Nenhum contato cadastrado.</p>
 </div>
#{/if}
#{else}
 #{list items:contatos, as:'contato'}
 <div class="linha">
 <p><a href="@{Application.visualizar(contato.email)}">&raquo; ${contato.nome} - ${contato.email}</a></p>
 </div>
 #{/list}
#{/else}

E para ficar mais agradável e padronizado, vamos mudar o arquivo de rotas que fica em  /conf , o arquivo é o routes

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / Application.index
GET /cadastrar Application.inserir
GET /visualizar/{email} Application.visualizar
GET /editar/{email} Application.editar
GET /excluir/{email} Application.excluir
# Map static resources from the /app/public folder to the /public path
GET /public/ staticDir:public
# Catch all
* /{controller}/{action} {controller}.{action}

Quando o usuário chamar o método de inserir que está no Application.java, no browser aparecerá dominio/cadastrar, e assim por diante.
Terminando isso o aplicativo está pronto para uso.
Abaixo algumas telas de uma pequena aplicação que fizemos, uma agenda de contados:

Site oficial do Play Framework: http://www.playframework.org/

Github do Play Framework:  https://github.com/playframework/play

Algumas fontes consultadas: Imasters, Bragil.net

Desenvolvimento de Web APIs evolui no ASP.NET MVC 4

Fonte: Elemar Jr./InfoQ

A nova versão do framework para desenvolvimento de aplicativos Web da Microsoft, o ASP.NET MVC 4, amplia muito o suporte ao desenvolvimento de aplicações Web API. O framework, agora integralmente open source e disponível no Codeplex, possibilita o desenvolvimento de serviços RESTful com pouco esforço.

Entre as novidades na versão 4, destacam-se a facilidade para criar rotas para recursos (evitando colisão com rotas padrões MVC), a escrita de Actions para cada um dos métodos HTTP, o suporte nativo ao fornecimento de objetos em formato JSON e XML, além do suporte ampliado a URLs de consulta compatíveis com OData.

O ASP.NET MVC Web API permite um novo modelo de desenvolvimento web, em que o servidor provê apenas conteúdo e a geração da apresentação ocorre inteiramente no browser. Ou seja, no lugar de prover o HTML pronto, o servidor pode entregar um composto HTML + JavaScript, deixando que os dados sejam requisitados conforme demanda, via Ajax, para um serviço RESTful. (Importante destacar que o template padrão para aplicações ASP.NET MVC já possui referências para JQuery.) A possibilidade de gerar, automaticamente, respostas em formato JSON facilita consideravelmente a manipulação de código JavaScript.

As novas funcionalidades já são suportadas tanto no novo Visual Studio 11 (ainda beta e disponível para download na versão Ultimate), quanto no Visual Studio 2010 (mediante instalação).

Conforme noticiamos aqui, embora o ASP.NET MVC 4 não esteja completo, a Microsoft já recomenda sua utilização em produção. A Microsoft disponibilizou um mini-tutorial sobre o framework, com artigos e vídeos.

A arquitetura do SpiderDuck em detalhes: o novo serviço de processamento de links do Twitter

O SpiderDuck, é um novo serviço do Twitter para gerenciar e otimizar o processamento em tempo real de URLs inseridos em tweets. Foi projetado com 6 componentes principais, distribuindo a responsabilidade de consultar, processar e armazenar as informações das URLs. A arquitetura do sistema tem o foco em reduzir o tempo de resposta e a latência do tráfego de dados, além de permitir o aumento em escala conforme o crescimento da demanda.

Links em tweets devem ser processados pelo Twitter quanto ao tipo e a relevância do conteúdo referenciado. Caso a URL aponte para uma foto ou vídeo, por exemplo os aplicativos do lado do cliente (web, mobile etc.) precisam apresentar a mídia correspondente junto ao texto do tweet. Os links também são processados pelo módulo de segurança, que verifica a presença de malware no endereço referenciado, entre outras coisas, e os “Tweet Buttons” contabilizam o número de compartilhamentos de cada link.

Antes do SpiderDuck o Twitter usava um serviço mais simples para análise de URLs compartilhadas, fazendo requisições via HEAD e eventuais redirecionamentos. Mas essa solução apresentava algumas limitações:

O serviço resolvia o endereçamento da URL, mas não realizava o download do conteúdo. As informações sobre a resolução da URL não eram persistidas em disco, permanecendo apenas em memória, no cache. Caso uma instância do cache ficasse fora do ar, os dados seriam perdidos.

O serviço também não leva em conta regras para bots modernos, como limitação da taxa de transferência; também não seguia diretivas do controlador de buscas de robots (robots.txt).

O time de engenheiros do Twitter chegou à conclusão que seria necessário desenvolver um serviço mais sofisticado para processamento de URLs, que atendesse à necessidade da empresa no longo prazo:

Nossa primeira ideia foi construir o sistema em cima de um crawler existente, que fosse open source. Mas percebemos que todos os crawlers disponíveis possuem duas características de que não precisávamos: os crawlers realizam a leitura de URLs de forma recursiva o que aumenta sua complexidade; além disso, são otimizados para a leitura em grande escala, e precisávamos de algo mais leve, capaz de fazer a leitura de URLs em tempo real. Decidimos então criar um novo sistema que atendesse às necessidades específicas do Twitter.

O SpiderDuck utiliza algumas soluções open source já adotadas em outros serviços do Twitter, como o agregador de conteúdo Scribe; o Finagle, um protocolo RPC criado para realizar o tráfego intenso de dados; e o Twitter Commons, um conjunto de bibliotecas que opera sobre a JVM.

Arquitetura

A arquitetura do SpiderDuck é modular (veja a figura), o que contribui para a escalabilidade do serviço.

Componentes do SpiderDuck (fonte: Twitter)

O SpiderDuck é composto por seis módulos principais:

  • Kestrel. Sistema de mensageria utilizado no Twitter para enfileiramento dos tweets mais recentes. O Kestrel é o ponto de partida do SpiderDuck, assim que recebe um tweet contendo uma URL, solicita a busca das informações dessa URL para outro módulo, o Scheduler.
  • Schedulers. Determinam se a busca do conteúdo da URL deve ser agendada ou não, e caso necessário faz o redirecionamento da URL. Esse módulo realiza o parse e o download do conteúdo, extrai os metadados e armazena toda essa informação em duas estruturas de armazenamento do sistema, o Metadata Store e o Content Store. Cada schedule trabalha de forma independente, permitindo que novos schedulers sejam adicionados, favorecendo a escalabilidade horizontal do sistema.
  • Fetchers. Componente que realiza a requisição HTTP em busca do conteúdo da URL. São implementados com servidores Thrift, com inteligência para tratar os limites de taxa de transferência e reconhecer as diretivas do controlador de buscas via bots, resolvendo umas das principais limitações da solução anteriormente usada.
  • Memcached. Um cache distribuído utilizado pelo Fetcher para armazenar temporariamente as informações dos arquivos robots.txt.
  • Metadata Store. Distribuição customizada do Cassandra que permite armazenar informações sobre a resolução e os metadados da URL. Essas informações são mantidas em uma tabela de hash, utilizando a URL como chave. O repositório é utilizado pelos aplicativos cliente do Twitter, para consumir informações da URL em tempo real.
  • Content Store. Um cluster com Hadoop Distributed File System (HDFS), um sistema para replicação de dados distribuídos extremamente rápido, que formam o repositório com o conteúdo de todas as URLs processadas.

Performance

A separação entre o Schedulers e os Fetchers permite que múltiplos tweets com a mesma URL sejam processados somente uma vez. Isso reduz a latência e aumenta a velocidade no processamento. Veja o que dizem sobre isso os engenheiros do projeto:

O SpiderDuck processa centenas de URLs a cada segundo, e o Metadata Store trata perto de 10 mil requisições por segundo. Normalmente cada requisição é realizada para uma URL especifica, sendo possível uma requisição para um lote de até 300 URLs ao mesmo tempo.

Para processar uma nova URL, que ainda não foi armazenada pelo SpiderDuck, partindo da criação do tweet e indo até a requisição externa e o armazenamento dos dados, o sistema gasta em média cinco segundos de processamento. A maior parte desse tempo é consumida no Fetcher, no momento em que o sistema extrai as informações da URL em um site externo.

Quando um novo tweet menciona uma URL que foi armazenada pelo SpiderDuck, o tempo para processar as informações da URL se reduz para dois segundos em média. Ou seja, uma aplicação cliente do Twitter tem de esperar por dois segundos para obter todas as informações da URL. E boa parte das 10 mil requisições por segundo recebidas pelo Metadata Store trata de consultas que em média, levam de quatro a cinco milissegundos de processamento.

Integração

Os serviços do Twitter consomem dados do SpiderDuck de várias maneiras; a mais frequente é a busca de informações complementares (metadados e resolução) da URL no Metadata Store. Outros serviços acessam os logs do SpiderDuck no HDFS para agregar informações utilizadas pelos sistemas de analise e métricas internas, o dashboard do Twitter.

No SpiderDuck serviços não solicitam o processamento de uma URL. O próprio sistema resolve e processa o conteúdo das URLs de tweets recentes. Após processar as URLs o sistema disponibiliza as informações para consumo dos serviços.

Conclusões


Uma forma efetiva para gerenciar URLs é apenas um dos desafios da do Twitter, que vem fazendo muanças constantes em arquitetura e tecnologias (como já reportamos aqui). Através do blog de engenharia do Twitter e da conta @twittereng, os engenheiros do Twitter compartilham um pouco sobre esses desafios, apresentando informações e estratégias relacionadas a arquitetura do serviço.

Fonte: Eder Magalhães/InfoQ

IcedTea, Harmony, IKVM e mais: altos e baixos do Java Open Source

Este artigo traz um panorama de projetos open source relacionados com o coração da plataforma Java, e em que tem havido extremos de altos e baixos. Em outubro, o IcedTea liberou sua versão 2.0, baseada no OpenJDK 7, que já está disponível como o Java 7 oficial do Fedora e do Ubuntu Linux; em novembro a Apache anunciou que o Harmony estava sendo rebaixado para o “sótão” da Fundação. São dois exemplos (e dois extremos) de projetos trazendo novidades que poderão afetar o futuro da tecnologia Java, mesmo sem o apoio de gigantes como Oracle e IBM.

IcedTea

Quando a Sun liberou em 2007 os fontes da sua JVM e sua biblioteca de classes, criando o projeto OpenJDK, atual implementação de referência do Java 7, o resultado não era imediatamente usável pela comunidade open source, pois alguns componentes licenciados de terceiros não puderam ser abertos naquele momento. Exemplos de APIs que não foram incluídas no código inicial do OpenJDK são as de gerência de fontes tipográficas e de interação com dispositivos de som. Sem falar de complementos indispensáveis para algumas aplicações, como o plugin web para Applets e o Java Web Start, que embora sejam parte do produto da Sun e Oracle há tempos, cobrem funcionalidades não-padronizadas como parte do Java SE.

Para remediar esses problemas, a comunidade open source criou, com patrocínio da Red Hat, o projeto IcedTea. O objetivo era substituir os componentes proprietários necessários para compilação e execução do OpenJDK, pelos equivalentes em outros projetos open source, especialmente o projeto GNU Classpath.

Na verdade, já havia conversas entre membros importantes da comunidade open source e da Sun bem antes do lançamento do OpenJDK, e não foi acidente a opção da Sun pela licença GPL, em vez das licenças usadas no Glassfish e no extinto Open Solaris. O objetivo era permitir a mistura de código com o ecossistema do GNU Classpath, baseado na GPL.

Já há alguns anos, as distribuições do Linux incluem binários certificados do IcedTea, mas que são distribuídos usando o nome OpenJDK como parte de um acordo com a Sun para acesso ao TCK, o kit de testes de compatibilidade do Java.

Algumas distribuições ofereciam como alternativa o Java proprietário da Sun, empacotado nos repositórios “não-livres”, usando a licença DLJ (Operating System Distribution License for Java). Mas a DLJ foi descontinuada pela Oracle com a liberação do Java 7, não apenas para o novo Java mas também para as versões anteriores. Por isso o IcedTea se tornou o único Java certificado a ser fornecido como parte dos sistemas Fedora, Ubuntu e Debian, além de se tornar também o preferencial para uso em distribuições comerciais com RHEL e SuSE.

O escopo do projeto IcedTea aumentou desde a sua criação. Ele implementou melhorias sobre o OpenJDK, para melhor compatibilidade com o Linux, por exemplo o suporte ao PulseAudio para multimídia; e evoluiu para uso de JVMs alternativas, como a JamVM, entrando no lugar do HotSpot do OpenJDK original.

Ampliando o alcance do OpenJDK

A maior contribuição do IcedTea para o OpenJDK provavelmente foi os projetos Zero e Shark. O Zero é um interpretador genérico de alta performance, que torna o OpenJDK viável em várias plataformas embarcadas, em que o custo de memória e bateria de um compilador JIT seria proibitivo.

Já o Shark utiliza a infraestrutura do LLVM para criar um compilador JIT portável para múltiplas arquiteturas de CPU, evitando a necessidade de se escrever um novo JIT para cada arquitetura de processador. Graças a estes projetos o OpenJDK vem se tornando a implementação do Java preferencial em algumas CPUs populares em plataformas voltadas para a computação embarcadas, como ARM e MIPS.

Note que o IcedTea não é um fork do OpenJDK. Alguns dos seus contribuidores estão no Community Board do OpenJDK e vários dos patches do projeto migraram para o OpenJDK e se tornaram parte do Java 7 oficial.

Outra contribuição importante do IcedTea é um processo de compilação mais simples para o OpenJDK, reduzindo a barreira de entrada para potenciais contribuidores. O projeto OpenJDK não fornece binários para nenhuma plataforma, somente fontes. O JDK fornecido pela Oracle à partir dos fontes do OpenJDK usa uma licença proprietária. Mas, graças ao processo simplificado do IcedTea hoje é possível baixar, sob uma licença open source, binários para Windows no site do Open SCG, que é o mesmo grupo responsável pelo porte nativo do PostgreSQL para Windows.

A certificação do Java SE é realizada sobre binários, não sobre fontes, e os binários do IcedTea inclusos no Ubuntu, Fedora e derivados (como RHEL e CentOS) são certificados desde 2009. Mas os binários do OpenJDK 7/IcedTea 2 inclusos nas distribuições do Linux não são certificados ainda, e nem os fornecidos pelo OpenSCG para Windows. Ambas as comunidades estão trabalhando no processo, e estão em busca de patrocinadores.

Ainda há espaço para outras JVMs open source?

A criação do projeto OpenJDK diminuiu o incentivo a continuidade de vários projetos de JVMs open source. O SableVM foi uma das vítimas. O desenvolvimento do GNU Classpath, que fornecia a biblioteca de classes do Java SE para a maioria destes projetos, ficou congelado no Java SE 5, e o venerável Kaffe apenas recentemente voltou a ter atividade, sendo transferido para o Github.

Poderia ser argumentado que não há mais necessidade de outros projetos de JVMs open source, uma vez que a Sun abriu sua implementação, e ainda mais agora que a Oracle oficializou o OpenJDK como a implementação de referência (RI) para o Java 7. A competição trazida por estes projetos foi benéfica porque estimulou a evolução e a abertura do JDK pela Sun, mas qual seu papel no futuro do Java?

Na época o Classpath estava chegando perto do 100% do Java SE 5 e se tornaria certificável. Com dois Javas open source plenamente funcionais (GCJ+Classpath e Apache Harmony) ficaria complicado manter restrições ao TCK do Java SE, e mais importante, o fluxo de caixa originado pelo licensiamento do JDK da Sun para fabricantes de plataformas móveis e embarcadas. A criação do OpenJDK sob licença GPL permitiu unir esforços à comunidade open source, sem abrir o TCK para projetos externos.

Por outro lado, sem projetos open source independentes, deixa-se de explorar alternativas de design para a JVM, que poderiam atender a nichos específicos ou se tornarem importantes em plataformas futuras. O fato de smartphones e tablets atuais em sua maioria ignorarem a plataforma Java SE (e também a Java ME) mostra que essas alternativas podem estar fazendo falta.

Por exemplo, o GCJ, que é parte da suite de compiladores GCC, tem a opção de gerar código nativo (que não necessita de uma JVM para executar). Era uma opção interessante para aplicações desktop e sistemas embarcados, tanto que foi utilizado pelo RHEL e pela comunidade Fedora para suportar o Eclipse e o Tomcat, antes da criação do OpenJDK. Sem falar na ampla cobertura de arquiteturas de CPU e SOs do GCC, muito superior à suportada pelo Java “oficial” na época.

As releases recentes do GCC continuam suportando o GCJ, e ele continua sendo incluído nas principais distribuições do Linux; mas, infelizmente não parece haver planos para atualizar seu suporte à linguagem Java, nem para completar o GNU Classpath ou atualizá-lo para além do Java 5.

JVMs alternativas ao OpenJDK

O OpenJDK, no entanto, não parece ter diminuído o ritmo do JikesRVM, que tem o diferencial de ser escrito quase que inteiramente em Java. Infelizmente ele não tem a pretenção de ser uma JVM para “uso real”, seu objetivo é ser uma plataforma de testes para novos algoritmos internos à JVM para projetos acadêmicos.

Dois dos projetos baseados no GNU Claspath, o JamVM e o Cacao continuam vivos. Eles colaboram com o projeto IcedTea e hoje podem utilizar a biblioteca de classes do OpenJDK em lugar do GNU Classpath, voltando assim a acompanhar a evolução da plataforma oficial. Apesar de os seus releases mais recentes terem mais de um ano, existe grande atividade nos repositórios de código do JamVM e do Cacao, e espera-se para breve novos releases que finalmente atualizarão suas respectivas JVMs para compatibilidade com as novas versões do Java.

Ambos o JamVM e o Cacao são focados em plataformas embarcadas, explorando diferentes designs para componentes da JVM, como o coletor de lixo, o interpretador de bytecodes e o compilador JIT. A comunidade do JamVM afirma, por exemplo, que é possível gerar um executável da sua JVM ocupando menos de 100 Kb.


Java no .NET

Outro projeto de JVM open source que continua vivo e pode ser interessante para alguns nichos é o IKVM, que roda dentro do CLR do .NET ou do Mono, e permite convivência “sem costuras” entre código escrito em Java e código escrito em C#, VB.Net ou outras linguagens que rodam sob o CLR.

O IKVM também permite empacotar aplicações Java como assemblies nativas do .NET. O projeto ganhou certa popularidade entre desenvolvedores que gostariam de continuar tendo acesso a bibliotecas Java em um projeto .NET. É também o projeto mais atualizado em relação aos padrões do Java, pois já oferece um release candidate com suporte ao Java 7.

Com o fim do Harmony, como fica o Android?

A DalvikVM, que é a base das aplicações Android, é baseada na biblioteca de classes do Java SE fornecida como parte do Apache Harmony. Originalmente este projeto tinha apoio da IBM, que contribuiu com a maior parte do código para a biblioteca de classes, mas quando a IBM decidiu focar seus esforços no OpenJDK, o Harmony ficou “órfão”. O projeto Harmony foi oficialmente descontinuado pela Apache Software Foundation em novembro deste ano. Ao contrário do que muitos esperavam, o Google não assumiu o projeto, mantendo o desenvolvimento do Dalvik como um fork separado, provavelmente devido ao processo aberto pela Oracle por violação de patentes.

O Dalvik não é exatamente uma JVM, pois roda seu próprio bytecode, e usa uma arquitetura interna bem diferente da usada no OpenJDK, embora semelhante ao JamVM e ao CacaoVM. O desenvolvimento das aplicações para o Dalvik é feito na linguagem Java, e os bytecodes Java são convertidos em bytecodes Dalvik. Está disponível apenas um subconjunto da API do Java SE, embora maior do que o incluso no Java ME, mas o Dalvilk não traz nada do Java oficial para celulares, sendo incompatível com MIDP ou CDC. Recursos que seriam parte do Java ME são fornecidos por APIs proprietárias para o Android, ou por acesso a serviços do Google.

A omissão do Google quanto ao Harmony dá margem a dúvidas sobre qual será o futuro do “Java” no Android:

  • O Google vai continuar evoluindo o Dalvik usando ideias do Java, de modo similar feito pela Microsoft com o .NET?
  • Ou será o HTML 5 quem poderá provocar um futuro abandono do DalvikVM e Java no Android?

Qualquer que seja o futuro do Dalvilk, ele representa uma linha que poderia ter sido incorporada ao Java oficial e que garantiria a popularidade do Java na computação móvel. A disputa entre Google e Oracle não atende aos interesses da comunidade Java, e a recente ênfase no HTML 5 mostra bem como o mercado ainda carece de uma solução para desenvolvimento interoperável entre smartphones e tablets de diferentes fornecedores.

Serviços nativos do Linux

O exemplo do Dalvik mostra que, apesar da necessidade de funcionalidades independentes de plataforma, há também necessidade de se incorporar suporte a recursos nativos. O Java 7 já foi uma evolução nessa direção, com a nova API de filesystem do NIO2 e se pode argumentar que o Eclipse deve grande parte do seu sucesso ao toolkit gráfico SWT, também baseado no acesso a recursos nativos de Windows, Unix e Mac.

Novamente vemos que a comunidade open source traz novidades neste sentido, dentro do ramo da computação embarcada. Por exemplo, o BUG System e o OpenEmbedded fazem uso de APIs Java para acesso ao desktop Gnome e o D-Bus – efetivamente colocando o Java em pé de igualdade com o código nativo, no acesso aos recursos do SO e Desktop do Linux.

Em vez de criar todo um “universo paralelo”, como feito pelo Android, estes projetos buscam maximizar a utilidade de recursos já conhecidos dos desenvolvedores em plataformas embarcadas, e abrem espaço para a plataforma Java como “linguagem de sistema” para o Linux, viabilizando o desenvolvimento de aplicações de gerência de hardware sem recorrer diretamente a código nativo.

Conclusões

Podemos ver que, apesar do foco de grandes empresas no projeto OpenJDK, ainda há vários outros projetos open source com potencial de contribuir para a evolução da plataforma, especialmente em nichos ainda negligenciados pela plataforma oficial, como o de dispositivos embarcados. Vale a pena acompanhar o amadurecimento destes projetos, pois de um deles pode sair o diferencial competitivo para os produtos da sua empresa, ou surgirem evoluções importantes para a plataforma oficial do Java.

Fonte: Fernando Lozano/InfoQ

REST tem impacto sobre NoSQL?

A partir do momento que grandes empresas como o Twitter e a Amazon abraçaram a arquitetura REST e também se tornaram defensores do NoSQL tornou-se inevitável assistirmos a essas duas tecnologias sendo utilizadas em conjunto. Em um post recente, Ganesh Prasad questiona como o sucesso do NoSQL nos leva a reavaliar os aspectos centrais do REST. Ganesh acredita que existem dois aspectos na tecnologia que representam obstáculos para a sua adoção:

Um deles é o uso do modelo cliente/servidor, ao invés do modelo de ponto a ponto, que acredito ser mais geral e útil. O segundo é a insistência na ausência de estado do cliente no servidor, forçando que essas informações sejam armazenadas no próprio cliente ou que sejam modeladas como recursos.

É no aspecto da ausência de estado que Ganesh se concentra. A questão de manutenção de estado e o REST já foi muito debatida em várias fontes, por exemplo Bill Burke discutiu essa questão anos atrás:

Em REST, a ausência de estado significa que não existem dados da sessão do cliente armazenados no servidor. O servidor somente cuida do estado dos recursos que expõe. Se houver necessidade de informações específicas da sessão de algum cliente, essas informações devem ser armazenadas no próprio cliente e transferidas para o servidor a cada requisição, quando necessário.

A camada de serviços que não mantém informações dos clientes conectados é muito mais simples de escalar, por precisar de menos informações ao se replicar em um ambiente em cluster. É mais simples de colocar em cluster, porque tudo o que é preciso fazer é adicionar mais máquinas.

Stefan Tilkov adota a mesma linha:

É importante reforçar que, embora REST inclua a ideia da ausência de estado, isso não significa que a aplicação que expõe suas funcionalidades não pode ter estado – isso tornaria essa abordagem pouco útil para a maioria dos cenários.


O REST obriga que o estado seja transformado em um recurso ou mantido no cliente. Em outras palavras, o servidor não deve manter nenhum tipo de informação associada ao estado da comunicação com o cliente, independente do da duração de uma requisição. A principal razão para isso é a escalabilidade: a capacidade de atendimento dos servidores fica severamente prejudicada quando o servidor necessita manter o estado das comunicações. (Note que essa restrição exige modificações na aplicação: não se pode simplesmente atribuir uma URI a um estado da sessão e chamar a aplicação de RESTful.)


Existem outros aspectos mais importantes: a ausência de estado isola o cliente de alterações dos servidores, uma vez que não depende da comunicação com o mesmo servidor em duas requisições consecutivas. O cliente pode receber um “documento” contendo links de um servidor e, enquanto o cliente processa essas informações, o mesmo servidor pode ser desligado; o disco removido e trocado; e o software atualizado e reiniciado; tudo isso sem impacto negativo. Se o cliente seguir os links obtidos a partir do servidor, ele não irá perceber.

Ganesh, apesar de entender que a ausência de estado colabora com demandas de escalabilidade e recuperação a falhas, acredita que tais restrições sobrecarregam a camada da aplicação, que passa a ter de tratar aspectos de distribuição e infraestrutura:

A abordagem REST é aceitável para elementos do domínio da aplicação, mas a existência de estado de comunicação com o cliente é útil para tratar elementos independentes do domínio, associados à qualidade do serviço como, por exemplo chaves de sessão para o controle de segurança. A recusa do REST em suportar aspectos como esses compromete a habilidade de oferecer essas características de forma transparente.

É para esse tipo de estado referenciado por Bill e Stefan que Ganesh chama atenção. Ganesh destaca que o NoSQL oferece uma solução escalável e tolerante a falhas para o problema. Ele se refere à implementação do Redis, entretanto a maioria (se não todas) as implementações NoSQL também tratam deste problema.

Está se tornando uma recomendação armazenar o estado da sessão em um banco de dados NoSQL como o Redis, ao invés de em memória. Delegar o armazenamento dos dados de sessão para um cluster NoSQL independente, e configurar os servidores de aplicação para obtê-las a partir deste cluster, torna os servidores de aplicações independentes dos clientes novamente. Um destaque no Redis é a baixa complexidade das operações GET e SET, que são de Ordem(1), ou seja, constante. Isto significa que (pelo menos em teoria) o número de clientes utilizando um banco de dados Redis pode aumentar indefinidamente, sem impacto na performance.

O artigo de Ganesh descreve como podemos utilizar o banco de dados NoSQL Redis para gerenciar os dados de sessão. Desta forma, acredita ele, aplicações REST devem ser levadas seriamente em consideração (neste caso sendo necessário atualizar/aprimorar a filosofia do REST). Ele conclui dizendo:

Seria bom se um mecanismo padrão e sem dependência de domínio pudesse evoluir para fornecer segurança e confiabilidade às interações baseadas em REST, utilizando mecanismos de armazenamento escalável de sessões em NoSQL.

A maioria das metodologias de desenvolvimento, frameworks e padrões mantém a sua relevância evoluindo para novas abordagens, assim que novas experiências são obtidas. NoSQL era apenas um exercício acadêmico quando a arquitetura REST foi proposta, portanto surge a questão: a arquitetura REST precisa evoluir face ao NoSQL, ou a natureza sem estado do REST continua a ser uma exigência, independentemente da abordagem utilizada para armazená-lo?

Fonte: Mark Little , traduzido por Lucas Machado/InfoQ