Persistência de dados com Redis

Conhecendo o Redis

Redis é um banco de dados para persistência de dados no modelo Key/Value escrito em C ANSI para sistemas POSIX, com uma interface de rede embutida. Isso é o que diz no site.

O que quer dizer que Redis é um "Key/Value storage engine", parecido com o memcached mas persistente. Suporta dados em String e Intergers, Lists, Sets e Ordered Sets além de Hash Tables (que suportam apenas Strings e Intergers). Usa operações atômicas (uma alteração por vez), e suporta get/set, push/pop, add/remove, union, intersect, diffs e muito mais, incluindo opções de ordenação.

Ele mantêm, por padrão, todos os dados em memória, e salva snapshots para o disco eventualmente de forma assíncrona, ou escrevendo cada mudança para um "Append Only File" (o que é mais interessante). Ele é capaz de refazer o AOF em background. Também tem suporte trivial a replicação master-slave.

O Redis é insamente rápido para escritas e leituras(muito mesmo!), tem bibliotecas para "tipo" todas as linguagens, é desenvolvido em ritmo alucinante e não funciona bem no windows.

A prioridade para próxima versão é o projeto Redis Cluster.

Instalação e Configuração do Redis

Sendo um sistema de evolução muito rápido fique de olho no código-fonte no no site oficial e nos branches do github e pegue a ultima versão. Aqui vou usar a versão 2.0 do github, seguem passos para instalação:

$ git clone git://github.com/antirez/redis.git$ cd redis$ make

Pronto o sistema está construído. Você pode agora configurar o servidor redis pelo arquivo redis.conf. Alguns detalhes interessantes da configuração:

Uma boa opção é usar o Redis com AOF (append only file), a cada escrita ele vai adicionar esse dado ao log que será a forma de persistência. Basta usar a opção "appendonly" como "yes".

Importante novidade no Redis é o uso de memoria virtual (VM), como o Redis mantém tudo em memoria isso limita a capacidade de armazenamento a memoria disponível. Usando VM o Redis pode armazenar mais dados que a memoria disponível fazendo paginação de forma inteligente.

Para ativar a VM use a opção "vm-enable" como "yes". Você pode definir o máximo de memoria para a VM usando a opção "vm-maxmemory", se esse valor for zero o sistema vai manter apenas as chaves em memoria e todo os dados em disco. Tem ainda a opção do número de threads trabalhando na paginação com "vm-max-threads", você pode usar seu numero de "cores" (+2) ou 0 para ter blocking de leitura.

Usar paginação pode ser mais lento, mas ainda assim é bem rápido e permite uma capacidade maior de armazenamento. Claro, só vai ficar mais lento se usar o sistema até ser necessário paginar.

O ideal é usar o sistema com AOF para maior consistência e durabilidade (não há impacto na performance) e usar VM com um número razoável de threads, para caso seu sistema cresça bastante você não ter nenhum susto.

Você tem ainda a opção "slaveof" para torná-lo slave de outro servidor e pode definir a opção "requirepass" para usar autenticação.

Para iniciar o servidor basta executar:

$ ./redis-server

Feitas as configurações você pode rodar os testes de sistema(demora bastante!):

$ make test

E tem também um utilitário de benchmark, bom para testar diferentes configurações:

./redis-benchmark -n 1000 -q

Usando Redis

Com o servidor iniciado (./redis-server) você pode testar o redis usando o shell interativo, basta lançá-lo usando:

$ ./redis-cli

Você pode passar para o cli as operações que quer testar, ex:

$ ./redis-cli set foo "bar"$ ./redis-cli get foo

Ou apenas usar o redis-cli para entrar em um prompt interativo.

Primeiro vamos ver um pouco dos tipos de dados do Redis.

String: O tipo mais básico, não tem mistério, são strings ou inteiros. É, inteiros também.

redis> set foo "bar"OKredis> get foo"bar"redis> set hello "Hello, "OKredis> append hello "Redis!"(integer) 13 redis> mget foo hello1."bar"2."Hello, Redis!"redis> substr hello 2 4"llo"redis> set um 1OKredis> incr um(integer) 2redis> incrby um 5(integer) 7redis> decr um(integer) 6redis> get um"6"

List: Listas no Redis não são arrays, mas Linked Lists. São mais simples e rápidas, não são associativas, aceitam Strings (que podem ser inteiros, lembra). São muito rápidas para acesso nas pontas e para adição de itens e não tão rápidas para acessos aleatórios (pelo índice).

redis> rpush nums 1redis> rpush nums 2redis> lpush nums 0redis> lrange nums 0 21."0"2."1"3."2"redis> lrange nums 0 11."0"2."1"redis> lrange nums 0 -11."0"2."1"3."2"

Explicando um pouco, "rpush" insere um item a direita na lista (final), "lpush" a esquerda (inicio) e "lrange" retorna parte da lista.

redis> rpush msgs "Oi"redis> rpush msgs "Meu nome"redis> rpush msgs "É"redis> rpush msgs "Redis!"redis>lrange msgs 0 -11."Oi"2."Meu nome"3."É"4."Redis!"redis> rpop msgs"Redis!"redis> rpop msgs"É"redis> lpop msgs"Oi"

O comando "lpop" retorna o elemento a esquerda da lista (começo) e o remove, o "rpop" o mesmo para a direita (final).

Set: É uma coleção de Strings não ordenadas.

redis> sadd users "diogok"redis> sadd users "voce"redis> sadd users "ninguem"redis> smembers users1."ninguem"2."voce"3."diogok"redis> sadd admins "ninguem"redis> sinter users admins1."ninguem"redis> sunion users admins1."ninguem"2."diogok"3."voce"redis> sdiff users admins1."diogok"2."voce"redis> sdiff admins users(empty list or set)redis> srem "ninguem"

A vantagem dos sets é permitir operações de grupos como uniões (sunion), intercessões (sinter) e diferenças (sdiff).

Sorted Set: Parecido com Sets porem ordenados.

redis> zadd log 2210 "login"redis> zadd log 2215 "view email"redis> zadd log 2225 "view twitter"redis>zrange log 0 -1redis>zrevrange log 0 -1redis> zrem log 2210

Hash: É um tipo novo (>= 2.0) que define hash tables, que contém chaves e conteúdos string.

redis> hset diogok name "Diogo"redis> hmset diogok email "manifesto at manifesto.blog.br" password "123"redis> hget diogok name"Diogo"redis> hkeys diogok1."name"2."email"3."password"redis> hvals1."Diogo"2."manifesto at manifesto.blog.br"3."123"redis> hgetall diogok1."name"2."Diogo"3."email"4."manifesto at manifesto.blog.br"5."password"6."123"redis> hset diogok counter 1redis> hincrby diogok counter 3redis> hget diogok counter4redis> hdel diogok counter

Conclusão

Isso cobre o básico do uso do Redis, agora é escolher a biblioteca para sua linguagem e consultar a documentação do Redis e a lista completa de comandos.

O Redis se mostra muito interessante por se extremamente rápido, principalmente na escrita, e seus tipos de dados como set e ordered list o torna muito prático para certas aplicaçções:

  • Os Ordered Set são ótimos para listas manterem ordem temporal (noticias, tweets, logs…)
  • Os Sets servem muito bem para traçar grafos e comparar listas (amigos em comum, aprovações…)
  • Lists são perfeitos para queues ou listas de processamentos (urls para minerar, mensagens para enviar, dados a processar…)
  • Hash Tables com Strings armazenam dados genéricos de forma mais compacta.
  • Cada um desses casos tem performance excelente!

O mais importante ao adotar o Redis(ou qualquer “nosql” na verdade) é entender a nova modelagem. Um sistema em memória não é bom entulhar com dados mal formatados, deve-se retirar periodicamente esses dados para uma armazenagem secundária. Deve-se pensar também numa estratégia consisa de chaves, ex:

redis> sadd users diogokredis> hmset users:diogok name "diogo" web "manifesto.blog.br"redis> sadd users voceredis> hmset users:voce name "voce" web "google.com"redis> sadd conhecidos:diogok voceredis> sinter conhecidos:diogok conhecidos:voce(nenhum conhecido em comum)

Você pode ver um clone do twitter com php+redis ou uma aplicação de notas com ruby+redis e ainda um motor de busca com python+redis como exemplo.

Em novo servidor

É isso ae, esse post é apenas para registrar que o Manifesto está rodando em novo servidor, um vps na DreamHost. Ia ser Linode, mas meu chefe evangelizou a DreamHost bem demais para eu ignorar.

O VPS roda apenas LAMP mesmo, com alguns Joomla!s e um Drupal. A migração foi indolor, nada que tar + scp + mysqldump + mysql não resolva e aparentemente está tudo ok. Meus e-mails agora são todos do google (não estou exatamente satisfeito mas não farei nada por enquanto), e o google apps for your domain é “tri-legal”.

No mais peguei meu domínio: diogok.net e só falta uma página.

É isso ae!

Como construir projetos Clojure com Leiningen

Dois pontos essenciais, não diretamente relacionados ao código, em projetos sérios são gestão de código (ou controle e versionamento de código) e ferramentas de automação de build. Para uma tecnologia tão prática como clojure não faz sentido usar tecnologia arcaicas e duras, então para versão de código você vai usar o GIT.

Para quem trabalha com java provavelmente está acostumado a usar o Ant ou o Maven para gerir os projetos (provavelmente parcialmente disfarçados por uma IDE), e quem já editou um pom.xml de um projeto sabe o inferno que isso se torna. Então, como uma ferramente elegante que é o Clojure, uma ferramenta de build e automação tão elegante quanto é necessária. Eis que temos o Leininghen.

Leininghen (não sei pronunciar) é uma ferramente de build para clojure em clojure desenhada para não te perturbar (adaptação minha da explicação oficial). Não tem XML, hail that. Você tem um arquivo de configuração escrito em, vejam só, Clojure! E é bem simples até (e programável, já que é de fato um programa), e pequeno.

Ele serve basicamente para:

  • Criar um projeto em branco com uma estrutura padrão
  • Baixar as depend&eecirc;ncias
  • Rodar os testes
  • Compilar e empacotar

Ele usa ainda o Clojars.org , um repositório de bibliotecas jar para clojure, como base para dependências, além de visar o GIT.

Mão na massa:

Primeiro você entra na github do leiningen e baixa o script, e coloque-o no seu path e rode o instalador:

$ wget -O lein http://github.com/technomancy/leiningen/raw/stable/bin/lein  $ chmod +x lein  $ sudo mv lein /usr/bin/lein  $ lein self-install  

Isso vai baixar o próprio lein para seu usuário pode usar. Agora começamos um projeto.

lein new meuprojeto  

Isso vai criar um projeto “meuprojeto” mínimo, com a estrutura básica de pastas. Mais ou menos assim:

/meuprojeto  /meuprojeto/classes  /meuprojeto/lib  /meuprojeto/src/meuprojeto/core.clj  /meuprojeto/test/meuprojeto/core.clj  /meuprojeto/project.clj  /meuprojeto/README  /meuprojeto/.gitignore  

Ele vem com as pastas para as classes compiladas, para as dependências, código fonte e testes (e o primeiro arquivo).

O project.clj é a configuração, o README é para explicação do projeto (e para o GIT) e o .gitignore também. Então agora temos que configurar o project.clj. Ele vem assim:

(defproject meuprojeto "1.0.0-SNAPSHOT"    :description "FIXME: write"    :dependencies [[org.clojure/clojure "1.1.0"]                   [org.clojure/clojure-contrib "1.1.0"]])  

Podemos editá-lo para tornar nosso projeto mais completo.

(defproject meuprojeto "1.0.0-SNAPSHOT"    :description "Projeto de teste do lein"    :main meuprojeto.core    :url "http://www.meuprojeto.com"    :dependencies [[org.clojure/clojure "1.1.0-master-SNAPSHOT"]                   [org.clojars.okkop/clojure-couchdb "0.2"]                   [org.clojure/clojure-contrib "1.1.0-master-SNAPSHOT"]])  

As informação de versionamento e descrição, além de organização, vão ser usados se enviar sua biblioteca ao clojars.org. O main será definido na geração do jar, e as dependências para construir tudo e os paths.

Com o project.clj pronto você pode fazer o lein instalar as dependências:

$ lein deps

Isso vai baixar os jars na pasta lib,e ele acerta o path na hora rodar.

Agora você pode programar, criar seus testes e fazer eles passarem. Conforme programando você pode rodar todos os testes ou apenas um namespace usando o lein:

$ lein test$ lein test meuprojeto.dbclient-test

Então quando esse release estiver pronto para distribuir, o código já no repositório (você fez os commits e os pushs e etc) você pode construir os pacotes:

$ lein uberjar

Esse vai compilar as suas classes, criar um jar “clean” (sem as depend&eecirc;ncias) e um standalone, que vai conter as bibliotecas que estão prontos para distribuir e ser executado

BONUS: Se você construiu uma biblioteca e quer torná-la disponível pelo lein você pode publicá-la no clojars.org, basta se cadastrar e depois enviar o jar “clean”: 

$ lein jar$ scp meuprojeto.jar clojars@clojars.org:

Presto!

Desenvolvimento de módulos para o Drupal

Desenvolvimento de módulos para o Drupal.

Minhas atividades web receberam recentemente um adereço interessante: O Drupal. E uma das minhas tarefas recentes foi o estudo para uma apresentação sobre desenvolvimento de módulos para o mesmo. Embora apresentação não esteja tão boa quanto gostaria estou disponibilizando o conteúdo. Pretendia acertar algumas coisas antes, mas vou deixar isso para depois.

A apresentação tem duas partes, os slides e um documento que criei antes para me basear.

Você baixar o PDF da Apresentação sobre Desenvolvimento de Módulos para Drupal. Vou reproduzir o documento (rascunho) na integra abaixo.

O Drupal e desenvolvimento de módulos para Drupal

O que é o Drupal:

Drupal é um framework para gestão de conteúdo. É uma ferramenta livre, de código aberto e comunidade atuante.
Drupal fornece a base para construção de portais de internet e intranet para gestão de conteúdo (e conhecimento), permitindo e incentivando a atuação colaborativa, compartilhamento de recursos e publicação de conteúdo de uma forma geral.
É extensível através de módulos, que são como plugins ou add-ons para acrescentar ou alterar funcionalidades no sistema. Quase tudo no Drupal é um módulo.
É desenvolvido em PHP e roda sobre a plataforma Apache + MySql (ou PostgreSQL).
Praticamente todas as funcionalidade do Drupal são módulos, como o conteúdo, o controle de acesso e etc, e todo funcionamento do Drupal é padronizado através de suas interfaces de Hooks e API. 

Drupal: http://drupal.org/

Conteúdo (nodes):

O Conteúdo no Drupal tem sua base em alguns elementos:

  • Campos (Fields)
  • Tipos de conteúdo (Content type)
  • Conteúdo (nó ou node)

Os tipos de conteúdo possuem uma série de campos que podem ser de vários tipos (texto, mapa, data, arquivos, imagens, relacionamentos com outros tipos e etc) que definem o modelo de um conteúdo. Cada conteúdo (ou página) é um nó no drupal, e possui um URL, identificador e o conteúdo dos seus campos de acordo com o tipo de conteúdo ao qual pertence.

Hooks:

Hooks são as formas dos módulos se conectarem ao Drupal e aos outros módulos. O próprio núcleo (core) do Drupal implementa os Hooks que precisa.

Um hook é um gancho a uma chamada do sistema, são funções definidas em pontos chaves do sistema (como carregar um conteúdo, renderizar um bloco, listar itens de menu, salvar um conteúdo, enviar um formulário, etc), assim os módulos registram funções próprias para serem chamadas nesses pontos do sistema.

Quando o sistema vai executar uma ação que possua um gancho (hook), ele executa então todas as funções registradas para esse gancho, fazendo assim a integração do sistema.

Para se registrar um hook basta criar uma função com o nome sendo o o nome do módulo + “_” + o nome do hook. Assim para implementar o hook “menu” no módulo “meumodulo” basta criarmos a função “meumodulo_menu”. 

Exemplo:

A ação de visualizar uma página especifica de um conteúdo, no momento de carregar esse conteúdo existe um gancho chamado “nodeapi”. Então quando o sistema prepara esta página, ao carregar o conteúdo ele passa esse “node” a ser exibido para todas os módulos que registraram uma função no hook “nodeapi”, ele podem então executar ações e alterar esse node, que depois será exibido para o usuário, através da implementação da hook “nodeapi” do próprio módulo “node”.

Mesmo módulos podem criar hooks para que outros módulos possam usar. É assim que drupal integra suas próprias funcionalidades.

São mais de 80 Hooks no núcleo do drupal, mais o que cada módulo pode oferecer (cck, node, contentaccess, etc).

Alguns Hooks importantes são:
 
  • form – Formulários
  • nodeapi – Trabalhar com nodes
  • menu – Itens de menu e páginas (urls)
  • block – Blocos de conteúdo
  • user – Controle de usuário
  • cron – Ações agendadas
Para mais sobre hooks: http://api.drupal.org/api/group/hooks.

APIs:

Outra parte essencial do Drupal são as APIs. APIs são conjuntos de funções relacionadas a alguma utilidade ou parte do sistema. Diferentes dos hooks elas são para serem chamadas livremente aonde quer que os módulos precisem de suas funcionalidades. As APIs são basicamente todas as funções fornecidas pelo core e por todos os módulos do Drupal.

Toda função de todo módulo é disponível para todo outro módulo, tornando o sistema extremamente padronizado através do reuso de recursos.

Então as APIs do drupal são funções fornecidas pelo sistema, como:

  • db_query – Para consultas ao banco de dados
  • t – Para recuperar string traduzidas (internacionalização)
  • drupal_set_message – Mensagens para o usuário
  • node_load – Carregar módulos
  • user_load – Buscar dados de um usuário
  • form_set_error – Mensagens de erro

São milhares de funções disponíveis: Cache API, File API, Field API, Database API, RDF API, Schema API, etc. Mais o que cada módulo pode disponibilizar, e existem muitos módulos que lançam extensas APIs: CCK, Openlayers e etc oferecem APIs.

Para mais sobre API: http://drupal.org/node/326 .

Desenvolvendo seu módulo:

Você não precisa desenvolver um módulo.

O sistema de conteúdo do Drupal é muito flexível, ele pode por si só atender uma grande demanda. E também existem milhares de módulos prontos para as mais variadas funções, alguns extremamente flexíveis como o CCK (que estende o sistema de conteúdo do Drupal). Ainda assim alguém já pode estar trabalhando em um módulo parecido com o que você acha que precisa, e é preferível que se unam os esforços em um só módulo, ou até se já existe um módulo com funcionalidade próxima é mais aconselhável que se unam num módulo só.

A comunidade drupal preza muito por padronização e qualidade, e sempre recomenda (e efetua) a fusão de módulos semelhantes por um módulo mais completo e de maior qualidade.

Mais ainda assim não existe tudo pronto, e em casos de necessidades especificas pode-se precisar de um novo módulo.

O primeiro passo é o http://drupal.org/project/modules aonde você pode conferir os módulos já existentes e ver tem algum que vale a pena usar ou adaptar. A comunidade Drupal é realmente muito forte em integração e união. 

O segundo lugar a visitar é http://groups.drupal.org/contributed-module-ideas , é um grupo de ideias e pedidos para módulos, da mesma forma que pode achar o que precisa  já pronto, pode-se achar ainda em planejamento e até inserir sua ideia e conseguir colaboradores (ou alguém que indique aonde isso já foi feito).

Se for então começar a colocar a mão na massa, a principal referência é http://drupal.org/contributors-guide .

Desenvolvendo seu módulo, mão na massa:

Decidido o módulo que quer, as funcionalidade e etc é hora de começar a programar mesmo. Existe uma padronização importante no código do drupal, que facilita manutenção e colaboração, e está definido no http://drupal.org/coding-standards , de fácil e rápida leitura. A parte de SQL (http://drupal.org/node/2497) também é importante e simples.

A estrutura mais básica de um módulo consiste em três arquivos:

  • nome_do_modulo.info
  • nome_do_modulo.module
  • nome_do_modulo.install

O arquivo de INFO contêm a descrição e informações básicas do módulo, como um arquivo de propriedades, as principais informações são:

  • name: O nome do módulo
  • description: A descrição
  • core: Versão do drupal ao qual se destina
  • version: Versão da instalação [opcional se hospedado no drupal.org]
  • dependencies: Módulos do qual depende [opcional]
  • package: O “pacote” de módulos ao qual pertence, ex: “content”, “views”,”others” [opcional]

Segue um exemplo de arquivo .info:

name = Meu Primeiro Módulo
description = Testa o sistema de módulos do Drupal
version = 0.1
core = 6.x
dependencies[] = cck
O arquivos INSTALL contêm as instruções de instalação (se houver alguma), e é escrito em PHP seguindo o esquema de “hooks”. O arquivo pode implementar dois hooks: “install” e “uninstall”, então se o o módulo se chama “meumodulo”, basta implementar esses dois hooks criando as funções “meumodulo_install” e “meumodulo_uninstall”:
<?php
function meumodulo_install() {
}
function meumodulo_uninstall() {
}

?>

Dentro das funções programa-se os passos da instalação.

Parte comum da instalação de um módulo é a criação de tabelas no banco de dados, para isso usa-se o hook de schema do drupal, implementa o hook como “meumodulo_schema”, no mesmo arquivo:

function meumodulo_schema() {
    $schema['meumodulo'] = array(
    ’description’=>’descrição’,
    ’fields’=>array(
        ’id’=>array(‘type’=>’int’,'not null’=>true),
        ’name’=>array(‘type’=>’text’,'default’=>”)
    ) ,
    ’primary key’=> array(‘id’)
    );
    return $schema;    
}

Nesse caso precisamos ainda definir que queremos instalar um schema nos hooks install e uninstall:

function meumodulo_install() {
    drupal_install_schema(‘meumodulo’);
}
function meumodulo_uninstall() {
    drupal_uninstall_schema(‘meumodulo’);
}

Finalmente o arquivo MODULE contém os códigos do modulo em si, implementando os hooks e as funcionalidades que o módulo vai oferecer, também é um arquivo PHP comum. Supondo que o módulo adicione uma página simples que exibe alguns dados salvos no banco de dados. Primeiro para criar uma página implementamos o hook “menu” para essa url:

<?php
function meumodulo_menu() {
    $menu = array();
    $menu['meumodulo/lista'] = array(
        ’title’ => ‘Lista de itens do meu modulo’,
        ’description’ => ”,
        ’page callback’ => ‘meumodulo_lista’,
        ’type’ => MENU_NORMAL_ITEM
    ) ;
    return $menu;
}

Agora implementamos o “meumodulo_lista” para montar a página:

function meumodulo_lista() {
    $query = db_query(‘SELECT nome FROM {meumodulo}’);
    $rows= array();
    while($row = db_fectch_array($query)) {
        $rows[] = array($row['nome']);        
    }   
    $header = array(‘Nomes’);
    return theme(‘table’,$header,$rows);        
}

Nessa função usamos 2 APIS importante: A de banco de dados (db_*) e de temas.

A de banco de dados, db_query monta uma consulta SQL e realiza essa no banco de dados usado no Drupal, enquanto db_fetch_array transforma o resultado da consulta em um array e avança o cursor nesse resultado.

A função theme recebe o tipo de thema a ser usado, e os parâmetros de acordo com esse tema, e então monta o html de acordo com o tema atual do drupal. No caso usamos o template de table e passamos a lista de cabeçalhos e as linhas da tabela. Combinações mais complexas são possíveis.

Agora vamos adicionar uma página que permitiria inserir esses dados no banco de dados, primeiro acrescentariamos na nossa implementação do “menu” a nova página:

function meumodulo_menu() {
(…)
    $menu['admin/settings/meumodulos'] = array(
        ’title’ => ‘Admin da lista de items do meu modulo’,
        ’description’ => ”,
       ’page callback’ => ‘drupal_get_form’,
        ’page arguments’ => array(‘meumodulo_admin’),
        ’access arguments’ => array(‘access administration pages’),

        ’type’ => MENU_NORMAL_ITEM
    ) ;
(…)
}

Dessa vez adicionamos a restrição de apenas quem tenha acesso a páginas administrativas, e ao invés de montarmos a página vamos usar a api de formulário drupal_get_form, implementamos então o meumodulo_admin:

function meumodulo_admin {
    $form = array();
    $form['meucampo'] = array(
        ’#type’=>’text’,
        ’#title’=>’Nome’,
        ’#default_value’=>’Insira um nome’
    );
    $form['submit'] = array(
        ’#type’=>’submit’,
        ’#value’=>’Save’,
        ’#submit’=>array(‘meumodulo_admin_submit’)
    );
    return $form;
}
function meumodulo_admin_submit($form,$state) {
    $novo_nome = $state['meucampo'];
    db_query(‘insert into {meumodulo}  (nome) values (‘%s’);’,$novo_nome);
}

E assim a primeira funcionalidade do módulo estaria pronta, e pronto para ser instalado.

A instalação de um módulo no Drupal se dá apenas copiando sua pasta para a pasta “modules” do drupal e ativando o módulo através da interface administrativa do drupal.

E depois?

Acontece que geralmente um módulo não é tão simples, e precisa de várias funcionalidades, e como não é possível saber todas ou cobrir todas aqui, segue um roteiro para facilitar o desenvolvimento:

  1. Pense bem no que o módulo vai fornecer e no esquema de banco de dados
  2. Separe as funções do módulo em partes funcionais:
    1. Páginas
    2. Formulários
    3. Listas
    4. Integrações com outros módulos (cck, biblio, openlayer)
    5. Interfaces (blocos/views/temas)
    6. Interações com conteúdo (nodes)
    7. Etc
  3. Para cada função do módulo consulte a lista de hooks para ver qual pode ser usado.
  4. Escolhido o(s) hook(s) a implementar veja se há uma API que ajude na tarefa
  5. Implemente conforme imaginado
  6. Teste
  7. Publique

Outros links importantes:

MagicSQL: O pior framework de ORM e SQL para PHP.

Disclaimer: eu que fiz.

A muito tempo atrás, numa terra distante, um amigo me perguntou sobre Orientação a Objetos em PHP, e sobre ORM, como resposta eu fiz essa pequena biblioteca para demonstrar o básico do básico. O “framework” MagicSQL!

Sinceramente tenho vergonha dela, não era nada sério e nem tomei muitos cuidados na época com boas práticas e testes e documentação, era só para ver alguns recursos. Mas ela funciona e eu usei em alguns projetinhos(com isso ela até evolui um bocado), mas se for escolher algum ORM para PHP, faça uma escolha mais séria.

De qualquer forma estava revendo e resolvi publicar o código, “just because”. Você pode conferir o código no Repositório do MagicSQL. Vamos a uma análise chata:

Primeiro ponto: ela funciona. Como eu usei ela um pouco então o que está lá está funcionando nos casos testados(e mais alguns). Ela consegue fazer selects, updates, inserts, deletes e joins sozinha. E_STRICT.

Segundo ponto: não tem configuração. Nenhuma. Você cria a conexão no código mesmo (como se fosse PDO) e instancia o Repositório. O resto é mágica (e não confie em mágica). Ela advinha seu schema usando describe e show tables, e faz uns cálculos para adivinhar os joins (Essa é uma parte engraçada do código).

Not lazy: Cada “requisição” faz só uma query, mesmo com joins. Ou seja, mesmo quando você carrega uma lista de objetos com listas de objetos relacionados, não importa o tamanho do join, é uma query só e tudo é carregado numa tacada só e transformado em objetos. Não queira ver esse código também.

StdClass pública: ele não usa seus objetos, apenas StdClass com atributos públicos. Whatever.

PDO e ArrayObject: Toda lista e resultado são ArrayObject (na verdade MagicCollection), então $arr[0] == $arr->get(0). Sua conexão é um PDO, caso você precise.

Quase sem documentação: Acho até que tem alguns comentários quaisquer, mas não tem PHPDoc e a documentação em si é quase nula. Mas está lá no README, example.php e test.php.

Noob testing: Bom, pelo menos tem testes unitários (ou algo parecido). Na época não tinha escolhido um framework, e como foi algo bem rápido e sujo, tem só um monte de chamadas com ifs e testes para saber se tudo correu bem. Tem até um teste que ainda falha por que nunca precisei da sua implementação (ou porque perdi a implementação).

MySql e SQLite: O objetivo inicial foi o sqlite, depois o MySql porque é o que mais uso. Apesar de usar PDO o método de “discovery” do schema não é padrão.

Usem um ORM PHP de verdade, e não esse, mas o código está ai para sua diversão.

Namespaces on clojure, and compilation.

O uso de namespaces em clojure pode ser bem simples, mas pode ser bem complicado. Vou focar no simples :)

O namespace de clojure está próximo da declaração pacote/classe no java, assim o padrão é o mesmo para nomenclatura (apesar de poder variar), dessa forma o arquivo clojure em src/twitter/search.clj teria normalmente o ns (ns = namespace) twitter.search.

A declaração básica de um namespace é a através do macro “ns”:

(ns twitter.search)  

Com os arquivos definidos com seus devidos namespace, e o classpath (cp) do java configurado certo (apontando para o src/, por exemplo, você pode chamar outros arquivos clojure pelo seu ns:

(ns twitter.main  	(:require twitter.search)  )  

Agora você teria o ns twitter.search disponível em seu main, mas a forma mais prática é a seguir:

(ns twitter.main  	(:require [twitter.search :as search)  )  

Assim o conteúdo do ns twitter.search está no simbolo alias search, dessa forma para chamar a função "search-timeline" do twitter.search você faz:

(search/search-timeline "clojure")  

Outra forma útil no ns é o "use", que traz os simbolos do outro ns para o ns atual, assim:

(ns twitter.main (:use twitter.search))  (search-timeline "clojure")  

Agora podemos também compilar nossa aplicação clojure, tendo por exemplo a seguinte estrutura de arquivos:

/app  /app/src  /app/src/twitter  /app/src/twitter/main.clj  /app/src/twitter/search.clj  /app/classes  /app/libs  

Considerando que seus .jars do clojure estão no libs, conforme este texto, você deve então passar a lancar o clojure com o seguinte comando (veja o novo classpath):

java -cp ./:./src/:./classes/:libs/clojure.jar:libs/clojure-contrib.jar clojure.main $1  

Agora podemos definir um método main no nosso twitter/main.clj :

(ns twitter.main    (:require [twitter.search :as search]))  (defn -main [word]    (let [tweets (search/search word)]      (println (reduce (fn [t0 t1]                          (str t0 "\n"                           (get t1 "at") " @" (get t1 "from") " : " (get t1 "text")                          )) "Search result" tweets ))  ))  

E o twitter/search.clj:

(ns twitter.search    (:use clojure.contrib.json.read)    (:require [clojure.contrib.http.agent :as http ])    )

  (defn search [word]    (let [json (http/string                     (http/http-agent                         (str "http://search.twitter.com/search.json?q=" word)) )]      (map         (fn [tweet] {"from" (get tweet "from_user")                     "text" (get tweet "text")                     "at" (get tweet "created_at")})        (get (read-json json) "results"))    )  )  

Como pode-se ver usamos tanto o use como o require, assim como as funções mega-hyper-uteis de map e reduce para excercício.

Mais então para compilar o projeto, lance o REPL executando apenas o “./clj” e mande compilar com:

(compile 'twitter.main)  

Agora suas classes estão na pasta classes, você pode executar a aplicação rodando:

$ java -cp ./libs:./classes:./libs/clojure.jar:./libs/clojure-contrib.jar twitter.main clojure  

Assim vai rodar o -main recebendo o “clojure” como argumento. Crie agora o script de start-up e pronto.

Bonus: uma versão em um script de 30 linhas formatadas de uma busca no twitter com clojure. Basta executar o .clj.

Hello World, em Clojure, novamente.

Já escrevi um pouco sobre clojure aqui, sobre o básico de programação funcional e o básico de clojure, mas resolvi pegar do começo agora, do hello world.

Para preparar o ambiente para o clojure basta pegar as bibliotecas do clojure e do clojure-contrib:

wget http://clojure-contrib.googlecode.com/files/clojure-contrib-1.1.0.zipwget http://clojure.googlecode.com/files/clojure-1.1.0.zip  

Descompacte e pegue os jars clojure.jar e clojure-contrib.jar e coloques numa mesma pasta “libs”, agora crie um lançador simples para o clojure, com o nome de “clj” (com permissão de execução, com o seguinte conteúdo:

#!/bin/shjava -cp .:libs/clojure.jar:libs/clojure-contrib.jar clojure.main $1  

Agora basta chamar o “clj” para ter o REPL, ou passar como parâmetro o arquivo clojure à ser executado.

Agora como primeiro programa, o clássico “Hello world”. Basta criar um arquivo hello.clj com o seguinte conteúdo:

  (println "Hello World")  (defn fac "Nice factorial impl" [x] ( reduce * (range 2 (inc x))))  (println "Factorial of 4 is" (fac 4))   (def myJson "{\"hello\":\"World\"}")  (use 'clojure.contrib.json.read)  (def obj (read-json myJson))  (println (get obj "hello"))  

No exemplo, linha a linha, imprimi o Hello World, criei uma função para calculo de fatorial e imprimi o fatorial de 4, criei uma string no formato do json, importei uma biblioteca de json do clojure-contrib, fiz o parse do json e exebi sua propriedade. Basta executar:

$ ./clj hello.clj  

Simples assim.

Instalando e configurando NGINX e PHP5 no Debian Lenny

História

Vou migrar de servidores para um VPS na Linode, o 360, então primeiro preciso ter certeza que tudo vai funcionar lá, para tanto preparei um máquina virtual com a mesma configuração de software que vou preparar por lá, o objetivo é rodar uma cópia de algumas instalações do Joomla, WordPress e agora um Drupal.

Como o VPS vai ser bem, digamos, compacto, fiz uma instalação básica do Debian Lenny (netinst) sem nada e parti daí. Segue agora como configurar o stack moderninho 2.0. (Uma solução até de preguiçoso, por usar o Fastcgi ao invés do FPM, mas não quero compilar o PHP agora, deixa para o próximo release).

NGINX

Comçando pelo NGINX, o ideal é pegar o código-fonte e compilar, seguem comandos:

# aptitude install build-essential libssl-dev libpcre3-dev -y  # wget http://nginx.org/download/nginx-0.8.33.tar.gz  # tar -zxvf nginx-0.8.33.tar.gz  # cd nginx-0.8.33  # ./configure --sbin-path=/usr/local/sbin --with-http_ssl_module \ --without-mail_pop3_module --without-mail_imap_module \ --without-mail_smtp_module --with-http_stub_status_module  # make  # make install  # ln -s /usr/local/nginx/conf /etc/nginx  # cd ..  # wget htt://www.manifesto.blog.br/extras/nginx  # mv nginx /etc/init.d/nginx  # chmod +x /etc/init.d/nginx  # update-rc.d nginx defaults  

Vamos acertar ainda os logs do nginx, editando o arquivo /etc/logrotate.d/nginx:

/var/log/nginx/*.log {          daily          missingok          rotate 7          compress          delaycompress          notifempty          create 640 root adm          sharedscripts          postrotate                  [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`          endscript  }  

E ainda a configuração do Nginx:

# mkdir /var/log/nginx  # mkdir /var/www  

E o conteúdo do arquivo /etc/nginx/nginx.conf :

user  www-data;  worker_processes  4;  error_log  /var/log/nginx/error.log;  events {      worker_connections  1024;  }  http {      include       mime.types;      default_type  application/octet-stream;      access_log  /var/log/nginx/access.log;      sendfile        on;      keepalive_timeout  60;      tcp_nodelay        on;      gzip on;      gzip_http_version 1.0;      gzip_comp_level 2;      gzip_proxied any;      gzip_min_length  1100;      gzip_buffers 16 8k;      gzip_types text/plain text/css application/x-javascript text/xml \                           application/xml application/xml+rss text/javascript \                           image/gif image/jpeg image/png;      gzip_disable "MSIE [1-6].(?!.*SV1)";      gzip_vary on;      server {          root  /var/www/;          server_name     localhost;          listen          80;          location / {                  index  index.php index.html index.htm;          }          location ~ \.php$ {                  fastcgi_pass   127.0.0.1:9000;                  fastcgi_index  index.php;                  include fastcgi_params;                  fastcgi_param SCRIPT_FILENAME /var/www/$fastcgi_script_name;                  fastcgi_param SERVER_NAME $http_host;                  fastcgi_ignore_client_abort on;          }      }  }   

Pronto, agora o nginx está pronto, basta criar o arquivo /etc/init.d/nginx para controlar o serviço, com o seguinte conteúdo:

! /bin/sh  PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local  DAEMON=/usr/local/sbin/nginx  NAME=nginx  DESC=nginx  test -x $DAEMON || exit 0  if [ -f /etc/default/nginx ] ; then          . /etc/default/nginx  fi  set -e  case "$1" in    start)          echo -n "Starting $DESC: "          start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \                  --exec $DAEMON -- $DAEMON_OPTS || true          echo "$NAME."          ;;    stop)           echo -n "Stopping $DESC: "          start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \                  --exec $DAEMON || true          echo "$NAME."          ;;     restart|force-reload)          echo -n "Restarting $DESC: "          start-stop-daemon --stop --quiet --pidfile \                  /var/run/$NAME.pid --exec $DAEMON || true          sleep 1          start-stop-daemon --start --quiet --pidfile \                  /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS || true          echo "$NAME."          ;;    reload)          echo -n "Reloading $DESC configuration: "          start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/$NAME.pid \              --exec $DAEMON || true          echo "$NAME."          ;;    configtest)          echo -n "Testing $DESC configuration: "          if nginx -t > /dev/null 2>&1          then            echo "$NAME."          else            exit $?          fi          ;;    *)          echo "Usage: $NAME {start|stop|restart|reload|force-reload|configtest}" >&2          exit 1          ;;  esac  exit 0  

E então o ativamos para iniciar automáticamente:

# chmod +x /etc/init.d/nginx  # update-rc.d nginx defaults  # /etc/init.d/nginx start  

PHP

Seguimos com a instalação do PHP5, APC e seus principais pacotes, usando os do dotdeb.org para termos versões mais recente, então primeiro adcione o dotdeb aos seus repositórios, adcionando as seguintes linhas ao /etc/apt/sources.list:

deb http://dotdeb.mirror.somersettechsolutions.co.uk/ stable all  deb-src http://dotdeb.mirror.somersettechsolutions.co.uk/ stable all

E agora instalamos o php5 sem o apache, e com os principais modulos:

# aptitude update   # aptitude install php5 php5-cli php5-cgi php5-curl php5-gd php5-common \ php5-memcache php5-mysql php5-pgsql php5-sqlite php5-apc  

Configuramos e agora criamos o script para controlar o serviço do fastcgi do php, em /etc/init.d/php-fastcgi :

#!/bin/bash  BIND=127.0.0.1:9000  USER=www-data  PHP_FCGI_CHILDREN=15  PHP_FCGI_MAX_REQUESTS=1000  PHP_CGI=/usr/bin/php-cgi  PHP_CGI_NAME=`basename $PHP_CGI`  PHP_CGI_ARGS="- USER=$USER PATH=/usr/bin \ PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN \ PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS \ $PHP_CGI -b $BIND"  RETVAL=0  start() {        echo -n "Starting PHP FastCGI: "        start-stop-daemon --quiet --start --background --chuid "$USER" \          --exec /usr/bin/env -- $PHP_CGI_ARGS        RETVAL=$?        echo "$PHP_CGI_NAME."  }  stop() {        echo -n "Stopping PHP FastCGI: "        killall -q -w -u $USER $PHP_CGI        RETVAL=$?        echo "$PHP_CGI_NAME."  }

  case "$1" in      start)        start    ;;      stop)        stop    ;;      restart)        stop        start    ;;      *)        echo "Usage: php-fastcgi {start|stop|restart}"        exit 1    ;;  esac  exit $RETVAL  

E então o ativamos para iniciar automáticamente:

# chmod +x /etc/init.d/php-fastcgi  # update-rc.d php-fastcgi defaults  

Agora com o PHP configurado, terminamos criando uma página e acessando o nosso servidor local, crie em /var/www/index.php como “< ? phpinfo() ? >” e acesse o ip do servidor para ver se funcionou… ou não.

Rede de máquinas virtuais com KVM e VDE

História

Como parte da migração de servidores que estou fazendo, montei uma pequena estrutura virtual para testes… na verdade levantei uma maquina virtual com o KVM com configuração semelhante a que vou usar no VPS.

O objetivo inicial é levantar a máquina com acesso SSH, para poder configurar o servidor (nginx, php e mysql), então segue o passo-a-passo para rede no KVM. O básico do KVM/QEmu pode-se conferir nesse mini tutorial do qemu e nesse micro guia do KVM.

Preparando a VM

Então comece criando o HD, baixando a ISO de sua distro favorita, que é claro que é o Debian, e siga a instalação padrão. Para manter mais parecido com o VPS mantive tudo em uma partição só e deixei um espacinho para o swap.

# qemu-img -f qcow2 debian.qcow 10G  # kvm -monitor stdio -smp 2 -m 360 -localtime -hda debian5.qcow -cdrom debian-lenny.iso-boot d -net nic,vlan=0 -net user,vlan=0 -name "debian-lenny"  

Esses comandos só criam um HD de 10GB e iniciam o KVM com dois processadores virtuais, 360MB de ram, usando as imagens devidas e rede local padrão. Basta seguir a instalação padrão, depois para carregar a VM use o mesmo comando mas sem a imagem do cd e com boot do hda:

# kvm -monitor stdio -smp 2 -m 360 -localtime -hda debian5.qcow -boot c -net nic,vlan=0-net user,vlan=0 -name "debian-lenny"  

Rede do KVM com VDE

Agora parte importante para ter uma rede lega é usar o VDE (virtual distributed ethernet) para configurar a rede do KVM, assim podendo acessar ele como um servidor normal, e ao contrário, assim como em outras VMS.

Primeiro instale o instale o VDE, para a rede distribuída, e o dnsmasq, para servir o DHCP para os guests, no host:

# aptitude install vde2 dnsmasq   # modprobe tun  # adduser seu_usuario vde2-net  $ newgrp vde2-net   

E configure suas interfaces de rede para o vde2, em /etc/network/interfaces adicione:

auto tap0  iface tap0 inet static  address 10.0.2.1  netmask 255.255.255.0  network 10.0.2.0  broadcast 10.0.2.255  pre-up tunctl -u diogo -t tap0  pre-up /etc/init.d/dnsmasq restart   up iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE  up echo 1 > /proc/sys/net/ipv4/ip_forward  vde2-switch -  

Troque “diogo” pelo seu usuário que vai iniciar a VM, e as faixas de ip se precisar.

Configure por final o DNSMASQ, ao final do /etc/dnsmasq.conf adcione:

user=nobody   domain=qemu.lan   interface=tap0   dhcp-range=10.0.2.1,10.0.2.253,255.255.255.0,10.0.2.255,8h   

Ainda para cada guest que planeje ter pode definir um ip estatico pelo seu mac address, adcionando um linha para cada conforme o exemplo:

dhcp-host=11:22:33:44:55:66,10.0.2.5  

E recarregue sua rede

# /etc/init.d/networking restart  

Agora com o host configurado é a hora do guest, lance sua maquina virtual com o comando final:

kvm -monitor stdio -smp 2 -m 360 -localtime -hda debian5.qcow -boot c-net nic,macaddr=1a:2b:3c:4d:5e:6f,model=rtl8139,vlan=0-net vde,vlan=0,sock=/var/run/vde2/tap0.ctl-name "debian-lenny"  

Agora na VM configure a rede:

# ifconfig eth0 up   # dhclient eth0   # ping 10.0.2.1   # ifconfig eth0 gateway 10.0.2.1   # ifconfig eth0   

Presto! O ultimo comando vai mostrar o ip que o guest recebeu (se reservou o ip estático deve ser o 10.0.2.5), o ip do host é 10.0.2.1.

Agora para deixar melhor instale o servidor SSH para acessar a maquina da forma como é devido:

# aptitude install openssh-server  

Agora você pode entrar na maquina usando o ssh como root na primeira vez para criar seu usuário, e depois iniciar a vm com a opção “–nographic” e passar a entrar apenas com o SSH.

Divirta-se!

Troca de mensagens com criptografia em Java, usando AES.

O escopo de uma aplicação incluía a troca de mensagens sensíveis como parte de um WebService, e além do uso de HTTPS, deveria ser usada alguma forma de criptografia na parte crítica do serviço.

Logo deveria haver uma forma de criptografia disponível tanto no servidor como nos clientes, inicialmente um serviço em Java e o cliente seria no Android (depois outros surgiriam), com isso tem quer ser usado um algorítimo de chave simétrica, que permitisse recuperar a mensagem original de posse da chave de criptografia usada. Obviamente deveria ser um mecanismo eficiente.

Escolhi o AES por acreditar ser o padrão ideal atualmente, “lançado” em 2001 entrou em estudo pelo governo dos EUA e hoje é o seu padrão para troca de documentos, incluindo os “tops”. Usando mesmo uma chave de apenas 128bits seria extremamente seguro, a chave pode ser de 128bits, 192bits ou 256bits.

Se você está desenvolvendo em Java EE 5 ou no Android (Ou qualquer Java 6, acredito eu, mas não confirmei), ambos já possuem a implementação deste algoritmo, caso a plataforma não possua você pode usar a implementação do Bouncy Castle, geralmente basta o Provider do bcprov-jdk16-*.jar .

O primeiro passo para a criptografia é definir uma chave, ela pode ser gerada automaticamente pelos mecanismos do sistema:

public byte[] key() throws NoSuchAlgorithmException {      KeyGenerator keyGen = KeyGenerator.getInstance("AES");      keyGen.init(128);      SecretKey key = keyGen.generateKey();      return key.getEncoded();  }  

Esse método retorna o array de bytes de uma chave de 128bits, podemos usar 256bits mas esse algorismo nem sempre está disponível ou requer configurações especificas do JRE.

A criptografia trabalha com valores em Hexadecimal, assim a chave também usa valores em hexa, você pode criar uma chave com um array de hexas do tamanho que deseja da chave, uma chave de 128bits são 8 bytes (128/16), assim:

byte[] key = { 0x0f, 0xad, 0×54, 0×12, 0×00, 0xaf , 0×34, 0xff }

Em posse de uma chave de criptografia basta aplica-la a uma engine de criptografia, junto dos bytes da mensagem a ser encriptada, com o seguinte método:

public byte[] encode(byte[] input, byte[] key) throws NoSuchAlgorithmException,                                            InvalidKeyException, IllegalBlockSizeException,                                          BadPaddingException, NoSuchPaddingException {      SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");      Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");      cipher.init(Cipher.ENCRYPT_MODE, skeySpec);      byte[] encrypted = cipher.doFinal(input);      return encrypted;  }  

Detalhe para o “AES/ECB/NoPadding” , isso usa o algoritmo AES e não faz o padding da mensagem.

Por trabalhar em hexadecimal, o algoritmo trabalha com blocos de 16 bytes, então a mensagem(seus bytes) devem ter uma tamanho múltiplo de 16 para não haver erros de “BadPaddingException: pad block corrupted, para isso podemos usar os seguintes métodos, que preenchem os espaços que faltam até formar o tamanho necessário com “null”:

public String nullPadString(String original) {      StringBuffer output = new StringBuffer(original);      int remain = output.length() % 16;      if (remain != 0) {          remain = 16 - remain;          for (int i = 0; i < remain; i++) {              output.append((char) 0);          }      }      return output.toString();  }  

Então podemos encriptar uma mensagem assim:

String mensagem = "Hello World!";  byte[] enc = encode(nullPadString(mensagem).getBytes(), key);  

Para descriptografar a mensagem não tem mistério, basta o seguinte método:

public byte[] decode(byte[] input, byte[] key) throws NoSuchAlgorithmException,                                             InvalidKeyException, IllegalBlockSizeException,                                            BadPaddingException, NoSuchPaddingException {      SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");           Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");      cipher.init(Cipher.DECRYPT_MODE, skeySpec);      byte[] decrypted = cipher.doFinal(input);      return decrypted;  }  

A única diferença é o parâmetro passado ao Cipher, para de descriptografar.

Agora que podemos criptografar e decodificar as mensagens, precisamos garantir seu formato ao ser enviado via HTTP/HTTPS.

Novamente, como trabalhamos com Hexadecimal, ele não é simplesmente convertido para String, não sem perda, e a conversão de volta também traria riscos, ainda mais se for em plataformas diferentes.

Precisa-se então converter esses valores hexadecimais para uma representação “por extenso” destes, e que possa ser transformado de volta, e podemos alcançar isto com os seguintes métodos:

public String fromHex(byte[] hex) {      StringBuffer sb = new StringBuffer();      for (int i=0; i < hex.length; i++) {          sb.append( Integer.toString( ( hex[i] & 0xff ) + 0x100, 16).substring( 1 ) );      }      return sb.toString();  }

  public byte[] toHex(String s) {      int len = s.length();      byte[] data = new byte[len / 2];      for (int i = 0; i < len; i += 2) {          data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)          + Character.digit(s.charAt(i+1), 16));      }      return data;  }  

Então para enviar uma mensagem podemos proceder da seguinte forma (contando que ambos possuem a mesma chave):

String mensagem = "Hello World!";  byte[] enc = encode(nullPadString(mensagem).getBytes(), key);  String msgParaEnviar = fromHex(enc);  

Enviamos então via http a string msgParaEnviar, e ao recebe-la procedemos a decodificação:

byte[] msgEnc = toHex(msgRecebida);  byte[] msg = decode(msgEnc,key);  String mensagem = new String(msg).trim();  

Assim temos a mensagem nas duas pontas, o trim é necessário devido ao “padding”.

Veja o código da Classe de Criptografia que uso.