Codemountain, Paulo Suzart's Blog

OO Funcional: Clojure, Coherence e JavaScript

with 2 comments

Nossa! Estou escrevendo cada vez menos. Isso me deixa um pouco triste. Mas é consequencia de algumas mudanças, até agora positivas.

Durante o carnaval decidi implementar uma ideia que havia comentado com @danielamadei. É uma idea simples: Criar uma fina camada REST em cima do Coherence, expandindo assim sua aplicabilidade para além do Java, .Net e C. Bom, o objetivo mesmo é estudar e aplicar o que tenho aprendido em algum caso que eventualmente pode se tornar útil. A mistura de Coherence com Clojure e Javascript motivou a elaboração do post e o título dele.

O resultado foi: Clojure, Ring, Rhino e Moustache. As libs contrib usadas foram: Duck-streams, json e string.

Mas, antes tentei fazer uma coisa mais louca com Aleph e Lamina. Aleph é um framework de comunicação  assíncrono escrito sobre lamina e JBoss Netty criado pelo mesmo autor do Lamina. É interessante, mas o Aleph acabou se mostrando com um nível de maturidade insuficiente para o que queria fazer, embora fosse simples.

Na lib que dei o nome de CoheREST, fiz a parte de inserção e busca pela chave. A parte mais interessante foi usar o Rhino pra executar funções Javascript submetidas via http para o servidor Ring. Bom, a abstração para acesso ao Coherence ficou assim:

    (with-cache "My-Cache"
        (put-val 1 {"name" "Paulo" "age" 20}))

É isso mesmo, basta esse código para iniciar, criar e inserir um Map com a chave 1 no cache de nome “My-Cache”. Analogamente, pare obter o valor, basta usar uma função chamada get-val. Mas isso foi só pra ilustrar como ficou a simples abstração.

Ok! O Coherence tem uma feature muito interessante. Ao invés de pesquisar no cache uma certa quantidade de entradas, efetuar algum tipo de operação e depois submeter as mudanças ao cache, ele permite que o processamento seja emitido por todo o cache (EntryProcessor). Isto faz com que o processamento ocorra localmente – no nó do grid em que a entrada reside – sem onerar o nó emissor do processamento. Isto é obtido emitindo para o cache uma implementação de AbstractProcessor.

Para emitir um Entry Processor, é possível fazer como se faz para por ou resgatar uma entrada no cache:

    (with-cache "My-Cache"
        (process 1 (coherest.processor.JSProcessor. some-js)))

Só uma observação, nesta versão implementei a emissão de um EntryProcessor para uma única entrada, aqui com a chave 1. E agora começa a parte interessante. coherest.processor.JSProcessor é executado por um EntryProcessor (apresentado mais abaixo), e é ele quem executa javascript no cluster, desde que o javascript tenha o seguinte formato:

    function(e) {//e é a entrada no cache
         e.age = e.age + 1;
         return e;
     }

Isto é, caso a entrada seja encontrada, ela será passada no formato JSON para a função que pode alterar a entrada e retornar o resultado do processamento. Aqui o exemplo é um  simples incremento da propriedade age da entrada. A função é a string some-js passada na construção do JSProcessor. E é aqui que o OO funcional começa.

JSProcessor é um type que implementa um Protocol (leia-se interface em clojure) que define apenas uma função: process. O Protocol e o type ficam assim:

    (defprotocol PProcessor
        (process [this entry]))

    (deftype JSProcessor
		[^String script] java.io.Serializable PProcessor
		(process [this entry]
                       ;; Implementation went here))

Simples, o Coherence vai invocar apenas o método process(entry) na Implementação de AbstractProcessor. Como o processor é espalhado pelo cluster, ele também precisa implementar Serializable. Mas no início do post falei que um EntryProcessor deve implementar AbstractProcessor, mas esta classe não apareceu em lugar nenhum aqui até agora. Acontece que por questões de design, meu Protocol PProcessor é na verdade invocado por uma implementação anonima de AbstractProcess. Esta implementação anonima é que contem a implementação do método process, que por sua vez se encarrega em a função process do JSProcessor, passando como argumento a entrada encontrada no cache. Veja como ficou:

(defn make-processor
	"Create a valid Coherence EntryProcessor from the PProcessor passed
	 as agurment"
	[processor]
	(proxy [AbstractProcessor] []
	    (process [entry]
		(process processor entry))))

Ufa! make-processor é uma função que recebe a instância de JSProcessor, cria um proxy de AbstractProcessor, onde sua implementação é justamente a invocação da função process nele passando a entrada encontrada. A implementação de AbstractProcessor não passa de uma porta de entrada para a invocação das funções definidas pelo PProcessor. E usar um Protocol para o carro chefe na execução de funções no cluster permite que criemos instâncias deste protocol que façam o que desejarmos. O JSProcessor no caso, usa o Rhino pra executar funões no grid.

O CoheREST foi feito sem nenhuma intenção comercial, tão pouco para uso em ambiente corporativo. Isto por que ainda preciso verificar algumas questões de licença. Aí posso liberar o fonte no meu github. O post já foi bastante longo e não pude entrar em detalhes sobre o CoheREST ou sobre o Ring e o Rhino sendo usado direto em Clojure. Mas fica uma deixa pra próximos posts.

Bom, e aqui tivemos herança, programação com interfaces, etc, tudo em clojure, uma linguagem não orientada a objetos, mas que não nos limita em nada a usar conceitos OO. Obrigado e lembre-se: @paulosuzart.

Written by paulosuzart

março 14, 2011 às 1:07 pm

Publicado em clojure, coherence

Tagged with ,

2 Respostas

Subscribe to comments with RSS.

  1. Cara ficou muito show de bola!!! Não sei pq, mas amo desenvolver qualquer coisa usando linguagens embarcadas.

    Abraao

    março 14, 2011 at 1:11 pm

  2. […] já precisei usar um misto de Protocols, types e Proxy como você pode ver aqui. Era justamente um caso de interoperabilidade com classe puramente […]


Deixe um comentário