TorqueBox – A colisão de dois mundos

Propaganda
TorqueBox_
TorqueBox
A colisão de dois mundos
O amadurecimento da JVM permitiu que linguagens dinâmicas e
estaticamente tipadas possam coexistir na mesma máquina virtual
em busca de maior desempenho, melhor suporte à integração de
código e uso de tecnologias preexistentes.
Descubra neste artigo como uma linguagem dinâmica como Ruby
pode tirar proveito de um dos mais populares servidores de aplicação do mundo Java.
A
evolução das linguagens trouxe à tona novas possibilidades para o desenvolvimento de software.
Hoje em dia, é possível resolver um único problema
com mais de uma linguagem.
Ruby surgiu com o propósito de permitir ao desenvolvedor escrever código mais intuitivo, alcançou
maior popularidade após o surgimento de um dos
seus frameworks Web, o Rails. Por outro lado sabemos que a plataforma Java possui uma série de soluções já amadurecidas para muitos dos problemas
vividos por linguagens consideradas “modernas”.
Olhando sob essa perspectiva começaram a surgir ideias com foco em reaproveitar a grande quantidade de frameworks e ferramentas existentes na
plataforma Java junto à expressividade do Ruby.
JRuby
Em 2006 foi criado o JRuby permitindo ao desenvolvedor cruzar os limites entre Java e Ruby dentro
da máquina virtual, abrindo novas possibilidades
para as duas plataformas. Para entender um pouco
de como funciona internamente o JRuby na JVM recomendo o artigo sobre invokeDynamic, disponível
na MundoJ 49.
uma vez que Java e Ruby são executadas na mesma JVM, sua integração ocorre de forma natural,
conforme apresentado na listagem 1.
/ 26
Listagem 1. Utilização de uma coleção Java em um
script Ruby.
require ‘java’
list = java.util.ArrayList.new
list << ‘string qualquer’
list << 10
list.each do |i|
puts “Item: #{i} - classe: #{i.class}”
end
Dessa forma é possível reutilizar parte do nosso
legado em Java, tirando proveito da sintaxe do Ruby.
Então, por que não utilizar um servidor de aplicação
Java executando aplicações Ruby?
TorqueBox
Apesar da naturalidade de integração entre as
linguagens com JRuby, teríamos que encapsular
grande parte das APIs Java dentro de gems. O código
final poderia se tornar muito verboso ao trazer características da linguagem Java pra dentro do Ruby.
um dos grandes impeditivos para adoção da linguagem Ruby em ambientes enterprise consiste na
possibilidade de repetir os mesmos erros do passado
com uma “nova” linguagem.
Bob McWhirter então teve a ideia de usar Ruby
Bruno Oliveira | [email protected]
É JBoss core developer pela Red Hat e apaixonado por opensource, colabora com os projetos Aerogear,
TorqueBox, Immutant e DynJS.
dentro de um servidor de aplicações JBoss, beneficiando-se do stack enterprise, fazendo uso de mensageria, jobs, injeção de dependências, transações e
segurança, criando assim o TorqueBox. Com isso,
tornou-se possível que os desenvolvedores pudessem usufruir da plataforma Java mesmo sem o conhecimento da linguagem.
Como funciona?
Ao criarmos qualquer aplicação Ruby que exija
agendamento de processos e comunicação assíncrona, por exemplo, é comum especificarmos as dependências existentes dentro de um arquivo Gemfile
utilizado pelo Bundler como padrão. Dessa forma
toda complexidade de infraestrutura e arquivos de
configuração vai para nossa aplicação, conforme figura 1.
TorqueBox foi construído não apenas com o
objetivo de suportar aplicações Rails, Rack e Sinatra, mas com o intuito de inverter a regra do jogo. A
partir de agora todas as dependências referentes à
infraestrutura passam a ser de responsabilidade do
servidor de aplicações. Veja na figura 2.
Pensando no ganho de produtividade, e pelo
lado prático, todas as features mais interessantes foram encapsuladas dentro de gems com JRuby, permitindo que aplicações Ruby façam uso do JBoss.
gunda abordagem, conforme exemplificado a seguir.
wget
http://torquebox.org/release/org/
torquebox/torquebox-dist/2.0.0/torqueboxdist-2.0.0-bin.zip
cd /opt
unzip torquebox-dist-2.0.0-bin.zip
O arquivo acima foi extraído para o diretório
opt, porém qualquer outro diretório pode ser definido. Apenas tenha cuidado com o caminho do servidor nas variáveis de ambiente que veremos nas
sessões a seguir.
Ao realizar o download e descompactar o TorqueBox por padrão existirão duas pastas JRuby contendo a release 1.6.7 e JBoss 7.1.1.Final contendo a
última versão estável do AS, conforme a figura 3.
Apesar da release existente do JBoss a primeira
vista parecer bem similar a qualquer outra existente,
é totalmente desaconselhável substituí-la. Os arquivos de configuração do TorqueBox são diretamente
Instalação
O lançamento da release 2.0 acontecerá em breve, por esse motivo neste artigo criaremos uma aplicação Rails utilizando a versão 2.0.0 do TorqueBox
com um sistema Unix. Para sistemas Windows, o leitor pode seguir as referências ao fim deste artigo. O
download pode ser feito de duas maneiras: rubygems
ou download direto. Neste artigo utilizaremos a se-
Figura 1. Aplicação Ruby tradicional.
Figura 3. Estrutura de diretórios do TorqueBox.
Figura 2. Aplicação Ruby no TorqueBox.
27 \
vinculados a essa distribuição a fim de assegurar a
compatibilidade.
Antes de iniciarmos, certifique-se que o Java 6
está instalado conforme Listagem 2 e posteriormente defina as variáveis de ambiente do servidor conforme Listagem 3. Dessa forma todos os binários estarão disponíveis na linha de comando, assim como a
versão do JRuby a ser utilizada.
Listagem 2. Versão do JDK utilizada.
java -version
java version “1.6.0_07”
Java(TM) SE Runtime Environment (build
1.6.0_07-b06-153) Java HotSpot(TM) 64-Bit
Server VM (build 1.6.0_07-b06-57, mixed mode)
Listagem 3. Definição de variáveis de ambiente.
export
TORQUEBOX_HOME=/opt/torquebox-dist-2.0.0
export
jboss
JBOSS_HOME=$TORQUEBOX_HOME/
export
jruby
JRUBY_HOME=$TORQUEBOX_HOME/
export
PATH=$JAVA_HOME/bin:$JRUBY_HOME/
bin:$TORQUEBOX_HOME/bin:$PATH
Setup da aplicação
Durante o desenvolvimento, algumas gems são
necessárias para o funcionamento correto da aplicação. Para garantir que temos todas as dependências,
vamos utilizar um dos templates para Rails existentes no TorqueBox a partir da linha de comando.
rails new email -m $TORQUEBOX_HOME/share/
rails/template.rb
O arquivo Gemfile, comumente usado em
aplicações Rails, é responsável por listar todas
as dependências existentes na aplicação. A
utilização do template do TorqueBox garante que
eu tenha todas as dependências necessárias para
começar o desenvolvimento, conforme conteúdo
abaixo:
gem ‘activerecord-jdbcsqlite3-adapter’
gem “torquebox”, “2.0.0”
gem “torquebox-rake-support”, “2.0.0”
Verifique se o servidor está configurado corretamente em http://localhost:8080 após a execução da
task rake run existente no diretório da aplicação.
Deployment
rake torquebox:deploy[context_path,name]
#
Deploy the app in the current d...
rake torquebox:undeploy[name]
# Undeploy
the app in the current...
Injeção de dependências
Vamos imaginar o seguinte cenário: desenvolvemos um projeto com Ruby on Rails, o valor foi entregue ao cliente no menor tempo possível e ele te
pede pra integrar com um sistema Java já existente.
A primeira ideia seria construir uma interface REST
do lado Java para que seja facilmente acessível por
outros sistemas.
O ‘porém’ é que, em algumas situações, alterar o
legado é custoso, além de aumentar o throughput de
rede, quando o que desejamos é apenas fazer uso de
uma API. Uma das features presentes no TorqueBox
é a utilização de CDI para injeção de objetos Java no
Ruby, como veremos adiante.
Inicialmente será criada uma classe Java simples
que terá como resultado final um jar, conforme Listagem 4. Por convenção no TorqueBox, quando precisamos fazer esse tipo de chamada, copiamos a biblioteca Java para a pasta lib do projeto Rails, conforme
Listagem 5.
Listagem 4. Criação de uma classe simples Java com
CDI.
Criação do projeto Java:
package br.com.mundoj;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class EmailService {
public void enviar(String mensagem){
//TODO Alguma implementacao real qualquer de
//envio de e-mail
System.out.println(“Enviar para: “ + mensagem);
}
}
TorqueBox faz uso do Weld (JSR-299), responsável pela injeção de dependências na plataforma JEE,
portanto por convenção o arquivo beans.xml deve
estar incluso na pasta META-INF.
Listagem 5. Deploy do jar na aplicação Rails.
app/
models/
views/
controllers/
lib/email-api.jar
Feito isto basta criar a classe Ruby, conforme LisO deployment da aplicação pode ser feito via o
tagem
6, incluindo os Injectors do TorqueBox como
comando torquebox ou com as rake tasks listadas a
seguir:
/ 28
dependência, perceba que para a injeção ocorrer de Listagem 7. Configuração do Infinispan em uma
maneira correta o caminho completo da classe deve applicação Rails.
ser especificado com o prefixo Java.
module Email
class Application < Rails::Application
Listagem 6. Injeção de dependências com Ruby.
config.cache_store = :torque_box_store
class EmailService
end
include TorqueBox::Injectors
end
def initialize
@service = inject( Java::br.com.mundoj. O comportamento padrão do Infinispan será sempre
EmailService )
Invalidation, mas podemos facilmente utilizar outro
end
modo de replicação com a alteração de alguns
def notificar(mensagem)
parâmetros dentro do application.rb, conforme
@service.enviar(mensagem)
Listagem 8.
end
Listagem 8. Alteração para modo replicated.
end
Caching
A utilização de caching é comum quando aplicações precisam atender a um elevado número de requisições. Ir ao banco de dados para fazer uma consulta
ao endereço do cliente a todo instante, por exemplo,
é custoso, esses dados poderiam ser armazenados em
algum lugar, evitando desperdício de recursos.
Uma das opções no Rails, quando precisamos de
caching, é configuração no próprio controller, porém
este Cache representa uma cópia local do objeto,
em caso de queda do servidor esse objeto não será
replicado para outras instâncias do nó. Em servidores JBoss, a implementação padrão para suprir essa
necessidade é o Infinispan, um dos NOSQL data grids
existentes, uma evolução do JBoss Cache.
O Infinispan possui três modos de caching distribuídos: replication, invalidation, distribution conforme
mostra a figura 4.
Quando o cluster é iniciado em modo Invalidation, as instâncias serão notificadas sobre a existência
de uma versão mais atual do cache e o antigo será
invalidado. Caso configurado como Replicated, ocorre
o broadcast pela rede para adição de outros nós para
que o cache seja replicado. Por fim, quando utilizado
Distribution o cluster usa consistent hashing para determinar em qual dos nós o cache deverá ser armazenado, eliminando pontos de falha.
Por default o TorqueBox irá utilizar cache local
para aplicações Ruby. Para utilizarmos o Infinispan é
necessário deixar isso explícito ao servidor dentro da
aplicação no arquivo application.rb, conforme Listagem 7.
module Email
class Application < Rails::Application
config.cache_store = :torque_box_store, {:mode
=> ‘replicated’, :sync =>false}
end
end
Agendamento de Jobs
A execução agendada de processos, também conhecidos como jobs, à primeira vista parece trivial, ou
seja, basta indicar o que você quer que seja executado,
configurar o agendador de tarefas padrão do sistema
operacional (uma cron, por exemplo) e aguardar seu
processamento.
Um dos grandes trade-offs ao delegar este tipo
de tarefa para o sistema operacional é abrir mão de
todos os recursos que o servidor de aplicação pode
oferecer, como injeção de dependências, consumo de
filas e outros benefícios. Escrever um script Ruby pra
fazer acesso à base de dados, selecionar os contatos
e disparar um e-mail não seria muito viável. Afinal,
qual decisão tomar quando o servidor cair no meio
da execução do processo? RollBack? E a segurança?
Em muitos sistemas o processamento de jobs é
considerado crítico, por exemplo, uma campanha de
marketing. Caso o sistema operacional sofra alguma
parada momentânea, a informação não terá mais valor minutos depois.
No TorqueBox, realizar o agendamento de Jobs
é trivial conforme Listagem 9 e Listagem 10, além
disso ganhamos a replicação entre os vários nós do
cluster JBoss.
Figura 4. Modo de replicação de cache no Infinispan.
29 \
Listagem 9. Criação de um Job no TorqueBox.
class EmailJob
def initialize(config={})
puts “Job iniciado #{Time.now} config
#{config[‘enviar’]}”
end
def run
puts “Job executado #{Time.now}”
end
end
O método initialize pode ser utilizado para qualquer
inicialização de variáveis, mas também definição de
configurações existentes no arquivo YAML, run será
invocado no momento da execução do Job.
Para configuração do Job, por convenção criaremos o arquivo torquebox.yml dentro da pasta config
da própria aplicação. De forma alternativa poderíamos criar o arquivo jobs.yml.
Listagem 10. Arquivo de configuração YAML.
jobs:
dispara_email:
job: EmailJob
cron: ‘0 0 6 * * ?’
singleton: true
config:
enviar: true
Perceba que, ao adicionarmos o parâmetro singleton, informamos ao servidor que esse job deverá ser
replicado entre todos os nós do cluster.
Services
É comum em sistemas operacionais multitarefas,
como Unix, a existência de daemons, permitindo que
processos possam ser executados em background durante o startup do OS. No TorqueBox ,os services são
responsáveis pela criação de processos persistentes
com Ruby, com o mesmo objetivo, porém com o benefício da injeção de dependências. Dessa forma, é
possível, em tempo de inicialização da aplicação, realizar a leitura de uma fila, disparar uma mensagem de
notificação ou conectar a um serviço remoto.
Para criação de um service em Ruby, por padrão
ele deve implementar os métodos start e stop. De forma alternativa é possível passar um hash como argumento no initialize, conforme Listagem 11.
Listagem
11. Definição
do
service.
class StatusEmailService
def initialize(args = {})
@descricao = args[‘descricao’]
end
def start
puts “#{@descricao} foi iniciado”
end
/ 30
def stop
puts “#{@descricao} foi interrompido”
end
end
A configuração de um service para deployment é feita
através de YAML conforme Listagem 12. Esses serviços podem ser clusterizados também em diversos nós
de um cluster JBoss. Para tal, ele deve ser configurado
como singleton.
Listagem 12. Arquivo torquebox.yml.
services:
StatusEmailService:
config:
descricao: Monitoramento do serviço de e-mail
singleton: true
Mensageria
À medida que ocorre o crescimento do número
de requisições de um sistema, é inevitável começar a
pensar no uso de transações assíncronas, permitindo
atender a uma quantidade maior de requisições, além
de prover baixo acoplamento entre sistemas de diferentes linguagens. Existem diversos message brokers
disponíveis no mercado, utilizado como padrão no
JBoss é o HornetQ.
No contexto de mensageria é comum ouvirmos
falar de Destinations, que nada mais é do que o lugar
onde as mensagens assíncronas serão armazenadas.
Existem dois tipos, as queues e topics. No TorqueBox
podemos criar filas JMS através do arquivo YAML
para que a aplicação possa fazer uso, conforme Listagem 13.
Listagem 13. Configuração de filas JMS no torquebox.yml.
queues:
/queues/email:
/queues/log:
messaging:
/queues/log:
LogAcessoProcessor:
Para que possamos testar efetivamente se as filas de fato foram corretamente configuradas, vamos
criar um serviço, responsável por enviar e realizar a
leitura de mensagens JMS na Listagem 14.
Listagem 14. Envio de mensagens assíncronas.
class SimpleMessagingService
def initialize(args = {})
@descricao = args[‘descricao’]
end
def start
puts “#{@descricao} foi iniciado”
queue = TorqueBox::Messaging::Queue.new ‘/
queues/email’
Thread.new do
queue.publish “Queue #{queue.name}:
Mensagem publicada no HornetQ com sucesso”
puts queue.receive( :timeout => 1000 )
end
end
def stop
puts “#{@descricao} foi interrompido”
end
end
teners. Dentro do TorqueBox podemos fazer tal analogia, uma vez que a funcionalidade dos Processors é
aguardar a chegada de uma mensagem. Para que isso
ocorra, basta estendermos um MessageProcessor conforme Listagem 17 e definir o método on_message
para leitura das mensagens.
Listagem 17. Utilização de Processors.
Include TorqueBox::Messaging
class LogAcessoProcessor < TorqueBox::Messagin
g::MessageProcessor
def on_message(body)
puts “Processando #{body}”
end
Tasks
Vamos imaginar que queremos executar uma
determinada tarefa, porém ela pode ser assíncrona.
Para tal, criaremos uma task do TorqueBox, conforme
Listagem 15.
Listagem 15. Criação de uma task assíncrona.
class RegistraAcessoTask <
TorqueBox::Messaging::Task
end
Por fim precisamos realizar a configuração do processor no arquivo YAML, para tal consulte Listagem 13.
Segurança
JAAS (Java Authentication and Authorization Service) é um dos frameworks de segurança mais utilizados no contexto enterprise do Java e embarcado
def log(payload)
dentro do JBoss. No TorqueBox foi criada uma API
Ruby com o intuito de prover de forma transparente
acesso = “Informação acessada
acesso a recursos de autenticação e autorização sem
#{payload[:nome]} <#{payload[:email]}> em
todos os detalhes de implementação Java, permitin#{Time.now}”
do que aplicações Rails possam usufruir das políticas
puts “#{acesso}”
de segurança existentes no JBoss, através do arquivo
end
login-config.xml.
Ao realizar o deployment da aplicação, a política
end
padrão usará o domain torquebox, fazendo uso da clasAgora basta inserirmos no nosso controller uma se SimpleServerLoginModule internamente responsáchamada para o método assíncrono da Task conforme vel por verificar a autenticidade do login. De forma
trivial ele verifica caso vazio o usuário é autenticado
Listagem 16.
como guest (convidado), caso contrário a identidade
Listagem 16. Definição da task no controller.
será associada ao login e senha fornecidos. Para fazer
uso do domain já existente basta defini-lo conforme
class ContatosController < ApplicationController
Listagem 18 no arquivo torquebox.yml ou auth.yml,
def show
também definiremos uma credencial válida para o
@contato = Contato.find(params[:id])
usuário teste.
RegistraAcessoTask.async(:log, :nome => @
contato.nome, :email => @contato.email)
end
end
Listagem 18. Utilização do domain.
auth:
default:
domain: torquebox
credentials:
teste: testepassword
Processors
Agora criaremos uma classe Ruby para realizar o
No mundo Java são comumente chamados de Lis- teste de autenticação conforme Listagem 19.
31 \
Listagem 19. Classe Ruby utilizando JAAS.
require ‘torquebox’
require ‘torquebox-security’
class LoginController < ApplicationController
def create
usuario = params[:usuario]
senha = params[:senha]
puts “#{usuario} informado”
puts “#{senha} informada”
redirect_to login_path if usuario.blank? ||
senha.blank?
authenticator = TorqueBox::Authentication.
default
if authenticator.authenticate(usuario, senha)
puts “Autenticado com sucesso”
redirect_to contatos_path
else
mensagem = “Nao autorizado”
redirect_to login_path
puts mensagem
end
end
end
Gerenciamento
O gerenciamento do servidor não é uma tarefa
tão trivial, muitas vezes com a quantidade crescente de aplicações podemos perder o controle do número de filas existentes ou jobs sendo processados.
Para isso, existe o projeto desenvolvido pela equipe
do próprio TorqueBox, o Backstage, uma aplicação
desenvolvida em Sinatra, que permite ter controle do
que ocorre dentro do servidor, conforme mostra a figura 5.
StompBox é outra ferramenta interessante para
controle dos deployments a serem realizados. A referência para download pode ser encontrada no fim
deste artigo.
Polyglot
Recentemente, o time do TorqueBox começou
a portar parte das funcionalidades existentes para
um projeto chamado Polyglot. O objetivo principal é
que demais linguagens pudessem se beneficiar disso.
Dessa forma surgiu o Immutant, um dos projetos com
o mesmo propósito que o TorqueBox, porém escrito
em Clojure.
O Polyglot hoje permite que aplicações escritas
em Ruby dentro do servidor JBoss possam realizar
chamadas de forma assíncrona para aplicações Clojure.
Considerações finais
A JVM há muito tempo deixou de ser meramente
uma plataforma para execução de aplicações Java e
se tornou uma plataforma poliglota. Consequentemente, novas possibilidades se abrem para servidores
de aplicações enterprise. Podemos esperar do futuro
sistemas com arquiteturas de tecnologia mistas, em
que não mais existirão especialistas apenas em uma
única linguagem.
/para saber mais
Você pode encontrar mais informações sobre aplicações
Ruby na edição 36.
/referências
> http://github.com/abstractj/torquebox-exemplos
Implementação dos exemplos existentes neste artigo
> http://torquebox.org/
Artigos, documentação e binários do TorqueBox
> http://github.com/torquebox
Código-fonte do Torquebox
> http://immutant.org
Artigos, documentação e binários do Immutant
> http://planet.jboss.org/post/jboss_polyglot_death_of_java
> JBoss polyglot - death of Java?
> http://torquebox.org/news/2011/12/05/devignition-preso/
Figura 5. Interface de administração do Backstage.
/ 32
> DevIgnition Polyglot AS Presentation
Download