segunda-feira, 3 de setembro de 2007

Web Services no Caché/Ensemble - A Saga - parte 1

Há um tempo atrás postei um pequeno elogio ao Caché/Ensemble no que concerne a suporte a Xml. Seguindo o pensamento, poderíamos crer que o suporte a SOAP e Web Services seria igualmente bom. Infelizmente, acabei encontrando problemas por aí. Neste post estou começando uma série, que deve ilustrar toda uma saga XD.

Inicialmente, eu tinha um Wsdl descrevendo alguns serviços de um software que a Celesc comprou, e que precisaríamos usar para integrar com os nossos sistemas (a saga para conseguir esse Web Service tb é longa, mas muito chata, não devo postá-la aqui).

Bem, já tendo realizado alguns testes criando clientes para outros Web Services, pensei: "moleza". Ledo engano. O Wizard que gera um cliente SOAP de cara me jogou um erro. Infelizmente, como pode-se ver no print screen abaixo, o Caché/Ensemble não é muito informativo, então fiquei na dúvida: será que me enviaram um Wsdl bixado, ou o Caché não entende certas coisas? (Neste ponto, ambas as hipóteses eram igualmente prováveis).


(Tela com o erro do wizard)

Resolvi testar o Web Service utilizando o soapUI, um programa feito em Java, muito bom, que permite vc testar Web Services. Eu falei dele no post anterior.

Usando o soapUI, eu consegui não só montar o esqueleto correto das mensagens SOAP, como também testar o serviço. Isso me levou a conclusão de que havia alguma coisa errada com o wizard do Caché. Mas como curioso que eu sou, fui procurar o que nesse Wsdl específico estava causando erro. Depois de perdido meio dia, comparando Wsdls, e fuçando nas especificações, finalmente encontrei o problema: a mensagem de requisição estava com dois elementos, quando o normal é ter apenas um:


(pedaço do Wsdl que estava me causando problema)

Comentando a tag do segundo elemento da mensagem, o wizard funcionou. Um alívio que durou pouco...

Numa versão antiga deste Web Service (que haviam me passado anteriormente), o wizard havia funcionado corretamente, e eu havia baseado a implementação toda nele. Claro que agora haviam algumas mudanças, mas achei que não era muitas. Eu estava totalmente errado...

Continua...

SoapUI

Hoje vou falar sobre um programa muito útil que eu uso aqui, especialmente quando se trata de testar Web Services SOAP. O programa se chama soapUI, e é open-source, mas com uma opção paga com suporte. A instalação desse programa é simples, e está bem documentada no site.

Depois de instalado, primeiramente criamos um projeto, (File -> New WSDL Project). Este projeto pode conter quantas referências a web services você quiser, é mais uma maneira de se organizar logicamente do que uma imposição.

Suponhamos que eu tenha criado um projeto chamado 'zzz'. Para adicionar um Wsdl, basta clicar com o botão direito em cima do projeto, e ir na opção de adicionar um Wsdl de uma Url ou de um arquivo.



Responda sim a questão de criar requisições padrão para todas as operações e pronto. Basta você ir abrindo na árvore a esquerda, e vai ver as operações listadas, bem como uma requisição padrão para cada uma delas. Na figura, vemos apenas uma operação, com uma requisição (mas você pode criar várias mensagens de requisição, e deixar todas ali registradas, facilitando os seus testes):



Agora, basta você preencher as tags com os valores que quiser testar, e clicar no botão de submeter requisição (o botão verde), e ver na no quadro a direita, o Xml com o resultado:



Agora é só instalar aí na sua máquina e testar um pouco.

quarta-feira, 29 de agosto de 2007

Tradutores

Eu gosto bastante do idgnow, e assino as newsletters por email deles.

Mas eu realmente acho que o pessoal deve deixar de usar aqueles tradutores automáticos, ou no mínimo contratar um revisor.

Olhem neste link:

"Paranóia nº 8: Seu provedor de internet sabe demais
Razão nº 8: Logaritmos detalhados de tudo que você já fez online"

Logaritmos? Base 10 ou base natural? =P

Trocar logs por logaritmos é dureza... Mas ninguém ter visto isso é pior.

domingo, 26 de agosto de 2007

Requisitos

Encontrei essa imagem jogada aqui no HD.

(Clique para aumentar)

É divertido porque é verdade. Pelo menos nos últimos casos que eu tenho visto.

quarta-feira, 22 de agosto de 2007

A cauda longa

Um trecho do livro 'A Cauda Longa':

"Para uma geração de clientes acostumados a fazer suas pesquisas de compra por meio de softwares de busca, a marca de uma empresa não é o que a empresa diz que é, mas o que o Google diz que é."

Ainda não terminei de ler o livro, mas estou achando muito interessante. Junto com 'Freakonomics', uma das minhas últimas melhores aquisições.

E isso contribui para algo que eu venho afirmando para um amigo meu: 'se vc quer conquistar o mundo, vai ter que passar primeiro em cima do grande G'. XD

segunda-feira, 13 de agosto de 2007

Criptografia em Caché/Ensemble

Há uns meses atrás, estava procurando APIs de criptografia dentro do Caché/Ensemble, pois iria implementar algum nível de segurança nos Web services (algo mais simples, nem de longe o WS-Security - especificação aqui).

Depois de muito procurar na ajuda online e perguntar para o grande G, desisti, me dei por vencido e vi que realmente não tem nenhuma API do Caché para isso. Fato aliás, que eu achei e acho estranho, já que até o Oracle tem a sua API de criptografia.

Deixando isso de lado, fui procurar uma solução. A primeira idéia foi a de usar um gateway Java. O que é isso? É nada mais nada menos do que instanciar uma máquina virtual Java de dentro do Ensemble, e poder interagir com objetos Java. Como Java tem uma boa API em se tratando de criptografia, pensei, tudo beleza... Infelizmente, essa integração JVM/Caché não foi muito feliz, sofrendo demais com bugs (que me fizeram inclusive reiniciar a máquina).

Com isso, me dei por vencido. Ou esperaria alguns anos até que alguém resolvesse colocar alguma API de criptografia, ou escreveria eu mesmo. Resolvi escrever eu mesmo.

A primeira decisão: qual algoritmo implementar? O esquema não era muito complexo, então já descartei de cara os algoritmos de chave pública/privada. Então, qual algoritmo simétrico implementar? Como eu não ia ganhar aumento de qualquer jeito, resolvi pegar um mais fácil (mas não a ponto de ser besta quebrar, como o de César).

Vi que realmente não tinha um muito fácil. Então, escolhi um que pelo menos tivesse um bom material disponível. Acabei escolhendo o DES. Não lembro de muito material de referência em português, então vou deixar aqui os links das páginas em inglês que eu usei pra entender o funcionamento e ajudar na implementação: The DES Algorithm Illustrated e DES Encryption.

Acabou que toda a implementação ficou em 3 classes (se bem que uma delas é mais enfeite). UPDATE: Não reproduzo aqui as classes, pq elas são enormes. Se quiser, baixe o fonte aqui.

A classe DadosLoginBean é só usada como um container para o usuário/senha.

A classe Util contém alguns métodos específicos para o algoritmo DES, mas também possui vários que podem ser usados em outras situações, como por exemplo, conversão de Strings ASCII para hexadecimal, ou de valores para/de bitstring (que é um array especial de bits, usado pelo Caché).

A classe DESCrypto é a principal. Não vou entrar em pormenores da implementação, basta dizer que essa classe cifra/decifra usando o algoritmo DES, com CBC (um modificador de bloco), e padding pkcs#5, com vetor de inicialização zerado.

Não se preocupe se vc não entendeu nada. Levei um bom tempo também, até entender todas essas letrinhas =P. De qualquer forma, o último método de DESCrypto, o método de classe decifraLogin ilustra como usar toda essa parafernália. Este método recebe como parâmetro uma mensagem criptografada (em formato hexadecimal, na primeira %String), e uma chave (tb em hexa, na segunda %String), e retorna um objeto contendo usuário e senha. O código aqui:


///
/// *******************************************************************
///
/// Método estático para decifrar uma mensagem contendo usuario/senha.
///
/// Instancia um objeto DESCrypto, seta a chave (chave padrão ou
/// não), converte o hexadecimal retornado para caracteres ASCII,
/// e retorna um bean com usuario e senha.
///
/// Caso aconteça algum erro, retorna vazio.
///
ClassMethod decifraLogin(msg As %String, chave As %String = "5C6F2F5F45793A50") As eey.security.bean.DadosLoginBean
{
set $ztrap = "EXCEPTION"

set des = ##class(eey.security.DESCrypto).%New()
set st = des.setKey(chave)
if ($System.Status.IsError(st)) goto EXCEPTION

set st = des.performChainCrypto(msg, .desc, "D")
if ($System.Status.IsError(st)) goto EXCEPTION

set userSenha = ##class(eey.security.Util).strHexToStringAscii(desc)

if (userSenha = "") goto EXCEPTION

set user = $piece(userSenha, "/")
set pass = $piece(userSenha, "/", 2)

set bean = ##class(eey.security.bean.DadosLoginBean).%New()
set bean.usuario = user
set bean.senha = pass

kill des

Quit bean
EXCEPTION
Quit ""
}



Se interessar, dê uma espiada no código, e qualquer dúvida, deixe um comentário.

P.S.: se interessar, o código acima está sob a licença WTFPL. =P

terça-feira, 24 de julho de 2007

O parâmetro esquecido

Enquanto pesquisava o código fonte do %IO.ServerSocket para escrever os posts anteriores (sobre sockets e ServerSockets criando processos), me deparei com algo inusitado, para dizer o mínimo.

O método ListenJob da classe %IO.ServerSocket tem como sexto parâmetro pForeground, um valor booleano cujo padrão é zero (0 - falso em Caché). A documentação deste método nem menciona este parâmetro. Pelo nome do parâmetro, tem-se a idéia de que poderíamos controlar os processos filhos que o método cria, deixando que eles rodassem em background ou não, ou ainda, se o próprio método rodaria em background ou não.

Bem, depois de fazer alguns testes, vi que o comportamento do método não mudava, apesar de alterar este parâmetro. Resolvi ver o código fonte da classe, e para minha surpresa, esse parâmetro NÃO É USADO EM LUGAR ALGUM. Sim, ele está lá declarado na assinatura do método, e em mais nenhum lugar.

Tabajarice, pensei...

Bem, resolvi dar uma chance, e verificar as possibilidades que levaram a tal fato.

Primeira teoria: esta classe deveria ser usada em versões antigas, e pra manter a compatibilidade, deixaram a assinatura do jeito que estava. Status da teoria: inválida. Aqui na documentação da versão 5.0, NEM EXISTE essa classe...

Segunda teoria: na versão mais nova, devem usar pra alguma coisa. Status da teoria: altamente improvável. Não tenho a versão 2007.1 instalada aqui, então não tenho como ver o fonte, mas pela documentação desta versão, não encontrei mudança nenhuma na classe.

Última teoria: alguém simplesmente esqueceu o pobre coitado do parâmetro ali, deixado sozinho e sem utilidade. Status da teoria: é a que eu aceito atualmente.

Só para constar, eu uso a versão 5.2, portanto geralmente quando posto links para a documentação, é para esta versão. Assim, nos posts anteriores sobre sockets, os links todos apontam para a 5.2, apesar de que pelo menos naquelas classes, eu olhei a documentação da 2007.1 e não vi diferença.

Certas coisas que eu vejo... ¬¬

segunda-feira, 23 de julho de 2007

Ainda Sockets em Caché - ServerSockets

Continuando a explorar o assunto do post passado, sobre sockets em Caché, vamos explorar outra possibilidade.

No post passado, mostrei um exemplo simples de como fazer para criar sockets, tanto do lado servidor, quanto do lado cliente. Entretanto, aquele exemplo tinha uma grande limitação: na parte do servidor, o código aceitava somente uma requisição depois encerrava. Como exemplo é válido, mas num cenário real, é muito limitador.

Entretanto, o objeto %IO.ServerSocket possui outro método, que nos permite deixar o socket servidor mais parecido com um ServerSocket em java. Este método é o ListenJob.

Antes de mais nada, vamos ver como fica um código simples:


Class teste.ServerSpawnSocket Extends %RegisteredObject
{

ClassMethod server()
{
set sock = ##class(%IO.ServerSocket).%New()
set st = $$$OK

w "Starting server...", !

// open serverSocket, param:
// port, timeOut, status
w sock.Open(7777, -1, st), !
w st, !

// server to listen incoming connections,
// and when it arrives, spawns a new job in background
// params:
//
// timeOut,
// classNameContainingMethodToBeCalled,
// jobArguments,
// socketClassName (default: %IO.ServerSocket),
// maxNumberOfJobs (default: -1),
// runInForeground (default: 0 - no),
// status
w sock.ListenJob(-1, "teste.ServerSpawnSocket", "", , , , st), !
w st, !

w "Closing server...", !
quit
}

//===================================================

ClassMethod OnConnected(StreamHandler As %IO.DeviceStream, AnotherArgs)
{
set st = $$$OK

// reading from socket, param:
// maxStringSize, timeout, status, lineTerminator
set ^someVar("read from tcp") = StreamHandler.ReadLine(50, 30, st, $c(13))

// writing to socket, param:
// stringToBeWritten, automaticFlush, status
d StreamHandler.Write("Goodbye Dolly", 1, st)

d StreamHandler.Close(st)
set ^someVar("status") = st

quit
}
}


O método server() é bem simples, tendo mais linhas de comentário do que propriamente código =P. Depois de criado o objeto, chamamos novamente o método open deste, definindo em que porta o socket ficará escutando. O método que mais nos interessa agora, é o seguinte, ListenJob.

Este método executa o seguinte: ao chegar nova requisição no socket, se não houver restrição ao endereço da requisição, o método cria um novo job (processo) que rodará em background, passando como parâmetro, um objeto %IO.DeviceStream, que contém a stream (fluxo) de dados criado pelo socket. Mas qual método o Caché invoca? Bem, depois de passear pelo código-fonte do %IO.ServerSocket, descubro que depois de invocar alguns métodos internos da própria classe, o objeto invoca o método de classe (em Java, seria um método static) OnConnected, da classe passada como segundo parâmetro na chamada de ListenJob.

O ListenJob ainda pode controlar o número máximo de jobs simultâneos, que é o quinto parâmetro. O primeiro parâmetro é o conhecido timeout, que se passado -1, nunca expira. O terceiro parâmetro pode ser composto de uma lista, contendo parâmetros adicionais a serem passados para o método OnConnected. O sexto parâmetro é inútil (mais sobre isso em um futuro post), e o último parâmetro é o conhecido objeto que retorna o status da execução do método.

No exemplo, pode-se ver que foi passado como parâmetro o nome da própria classe, mas poderia muito bem ser outra qualquer. O importante é que a classe passada contenha o método OnConnected, cuja assinatura pode ser vista no exemplo (IMPORTANTE: essa assinatura não é fixa, nem definitiva, muito menos oficial, já que na documentação não consta nada disso - pelo menos eu não achei em lugar algum, se você leitor, sabe onde está, deixe um comentário dizendo, por favor).

Dentro do método OnConnected, manipulamos a stream de dados como quisermos, o exemplo simplesmente lê algo da stream, jogando em uma variável global, e escreve algo na stream, antes de fechar a conexão.

Se foi ou não proveitoso o exemplo, deixe um comentário aqui embaixo.

quinta-feira, 19 de julho de 2007

Sockets em Caché

Outro dia, na lista de Caché em inglês no google groups, alguém perguntou sobre como usar sockets em Caché. Como eu também nunca havia usado sockets diretamente, resolvi investigar. Como já disse, a documentação do Caché está longe dos javadoc da Sun, então boa parte do descobri foi por "feeling" mesmo, até porque neste caso não precisei testar muito.

Então, abaixo vai o código exemplo que eu mandei, e que vc pode colocar em qualquer classe pra testar:


ClassMethod connect()
{
set sock = ##class(%IO.Socket).%New()
set st = $$$OK

w "Starting", !

// connecting, param:
// address, port, timeout, status
w sock.Open("127.0.0.1","7777",-1, st), !
w st, !

// reading from socket, param:
// maxStringSize, timeout, status, lineTerminator
w sock.ReadLine(50, 30, st, $c(13)), !

// writing to socket, param:
// stringToBeWritten, automaticFlush, status
d sock.Write("We are the champions", 1, st)

quit
}

//===================================================

ClassMethod server()
{
set sock = ##class(%IO.ServerSocket).%New()
set st = $$$OK

w "Starting server...", !

// open serverSocket, param:
// port, timeOut, status
w sock.Open(7777, -1, st), !
w st, !

// server to listen incoming connections, param:
// timeOut, status
w sock.Listen(-1, st), !
w st, !

// writing to socket, param:
// stringToBeWritten, automaticFlush, status
d sock.Write("I am the champion", 1, st)

// reading from socket, param:
// maxStringSize, timeout, status, lineTerminator
w sock.ReadLine(50, 30, st, $c(13)), !

w sock.Close(), !

w "Closing server...", !
}



O primeiro método, connect(), usa %IO.Socket, que implementa um socket básico. Se você já programou com sockets em Java, verá que é muito simples o seu uso. Na verdade, depois de criado o objeto, basta executar o método Open, passando o endereço do host, a porta, um timeout para conexão e como referência, o status.

Depois de conectado, pode-se usar os métodos de %IO.DeviceStream para ler e escrever no stream de dados.

//===================================================

O segundo método, server(), usa %IO.ServerSocket. A diferença é que o ServerSocket implementa o servidor, ou seja, um socket que fica 'escutando' requisições, ao passo que o %IO.Socket se conecta a um socket servidor. Ao contrário do Java, por exemplo, que um ServerSocket retorna um objeto Socket, no Caché o próprio objeto ServerSocket pode ser usado como se fosse um socket comum. No código acima, nota-se que os mesmos métodos usados para escrever/ler da stream são usados nos dois objetos socket.

Também é possível deixar o ServerSocket escutando e ao chegar uma conexão, iniciar outro Job (processo) em background, para lidar com a conexão. Entretanto, não explorei essa possibilidade, quem sabe mais para frente...

quarta-feira, 18 de julho de 2007

Caché e XML

Pra não dizerem que eu só falo mal, hoje vou elogiar um aspecto do Caché.

O suporte nativo a Xml é muito bom, e o que é melhor, é bem simples. Basta declarar que uma determinada classe extende a classe padrão, XML.Adaptor, que a sua classe já pode ser usada para enviar e/ou receber Xml. Como o Caché aceita herança múltipla, extender esta classe é mero detalhe.

No caso de se usar Web services, é o que basta. Claro que no caso dos Web services, o Caché já gera toda a estrutura de parsing e binding, o que não quer dizer que não dê pra fazer na mão. Além disso, este esquema é flexível o suficiente para se alterar algumas propriedades, como por exemplo, se você quer que no Xml determinada propriedade da classe seja mapeada com um nome diferente, isso é possível (alterando o parâmetro 'XMLNAME' da propriedade).

Infelizmente, nem tudo são flores. Apesar de ser muito bom, e ter uma ótima flexibilidade, a documentação é pobre, e por vezes confusa (o que aliás, se repete por muitos lugares da documentação). Isso faz com que: 1 - você tenha que ficar testando algumas coisas, porque a documentação não te esclarece; 2 - ou você se aventure por uns códigos que porventura já existam e tente entender, ou por exemplo, que o próprio Caché gera (no assistente de cliente de Web Services, por exemplo).

Vou ficando por aqui, deixando o link pra esse mini artigo (pessoalmente, não gosto deste tipo de artigo ser chamado de white paper, pois não gosto da definição de white paper como um produto de marketing, mas isso é outra história XD): Caché and XML-based Data Exchange Standards. Não é um artigo técnico, então caso você ainda não tenha familiaridade com Xml, pode ler tranquilo.