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