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.

quarta-feira, 27 de junho de 2007

A demo é do "demo"

Bem, eu já mencionei que trabalho aqui na Celesc, mas creio que nunca disse exatamente o que eu faço. Bem, nem vai ser hoje, porque eu acabo fazendo um monte de coisas, hehehe XD.

Uma das tarefas a qual fui designado, foi acompanhar a implantação do novo sistema de suprimentos. Este sistema, chamado Alphalinc, foi comprado/licenciado pela Celesc, e é originalmente desenvolvido pela Disclinc, empresa australiana.

Este sistema, originalmente desenvolvido como sendo um gerenciador de cadeia de suprimentos, está sendo atualmente sendo adequado para o uso aqui na Celesc, pois como empresa 'pública', tem uma série de peculiaridades com relação ao resto do mundo. Estas modificações, o pessoal da empresa, da própria Disclinc-br e sua parceira E-biz, chamam de "customizações".

Só para esclarecer, meu papel neste projeto não é exatamente de desenvolvedor, arquiteto, gerente, ou coisa alguma. Na verdade, eu até ajudo alguma coisa como desenvolvedor, mas a minha verdadeira atribuição é "ficar de olho", já que por um bom tempo, ninguém do DPTI (o departamento de informática) sabia o que acontecia.

(Bem, daí dá pra ver que a Celesc não tem cultura de trabalhar em projetos)

Pois bem, como todo projeto, este novo sistema de suprimentos está atrasado. Opinião pessoal (que já expressei para o mundo todo): está muuuuuuito atrasado, e nem de longe termina no prazo (que por acaso, é outubro).

Hoje termina uma demonstração deste novo sistema, para alguns usuários.

IMHO, esta demonstração foi um pouco a frente do tempo, já que para o usuário final, o que importa são as telas funcionando do jeito que ele quer. Como muitos módulos ainda não foram terminados, muita coisa falta. E para o usuário, não importa muito se a tela está 75% da sua funcionalidade original pronta: ele vai se focar nos 25% que faltam.

Juntando isso com algumas falhas na especificação, polêmicas entre e com usuários, o que sai? Uma demonstração (demo) que deixa usuários frustrados, desenvolvedores tensos e gerentes estressados.

OK, pra ser justo, não foi (ou está sendo, pois escrevo este post durante a apresentação) um pandemônio. E a frustração dos usuários também não foi tão grande (feliz ou infelizmente, com o passar do tempo, as expectativas dos usuários aqui foram ficando cada vez menores).

Erros que identifiquei, e que podem ser úteis para algum leitor deste nem tanto humilde blog:

- pressa em mostrar serviço: se você tem diversos módulos, e todos os módulos não estão completos (mesmo que faltem apenas 10 ou 5%), vc não tem NADA completo. Não se apresse, complete o que tem que ser completado, e depois mostre para o usuário.

- tentar entregar vários módulos de uma vez: entregue poucos módulos, mas entregue-os bem feitos. Nem tudo pode ser modularizado de forma a ficar funcional, mas muitas coisas podem ser feitas. Muitos módulos são interdependentes, neste caso, talvez modularizar ainda mais a sua arquitetura seja interessante. Em vez de um módulo monstruoso, refatore. As vezes não dá, mas isso não é regra.

- espera para entregar algo palpável para o usuário: entregue aos poucos, mas sempre - seja ágil, adote uma metodologia de entregas frequentes, mesmo que estes módulos sejam pequenos (mas devem ser funcionais). Isso é uma mão na roda na hora de identificar erros de especificação (pq o usuário vai chiar), um dos erros mais custosos no desenvolvimento.

Vamos às conclusões e considerações finais.

Listei acima alguns erros do projeto. Veja bem, eles NÃO SÃO OS ÚNICOS PROBLEMAS!! Mas são problemas que nós (da área de informática como um todo), poderíamos ter trabalhado. Muitos outros problemas, que não irei comentar aqui (porque este post iria ficar MUUUUITO maior do que já está), estão fora do nosso alcance.

Este projeto vai vingar? Não sei, e ainda não desenvolvi poderes pra saber o futuro (apesar de eu ser japonês rechonchudo, não tenho nem katana nem dobro o espaço-tempo - se não entendeu, assista Heroes).

Talvez daqui a uns seis meses, eu faça outro post relatando um caso de sucesso. Ou não...

sexta-feira, 8 de junho de 2007

Operadores

Já faz algum tempo que descobri algo no mínimo peculiar sobre a linguagem do Caché, e hoje vou publicar aqui. Não é necessariamente um bug, pois está devidamente registrado na documentação, mas que é algo não natural, isso é.

Se vc se lembra das aulas de matemática do primeiro grau, deve acertar quanto é a expressão 1 + 2 * 3. Se vc respondeu 9, deve ter matado aula, pois a resposta é 7. Isso porque na matemática, o operador de multiplicação tem precedência sobre o operador de adição, ou seja, se você não coloca parênteses (que é o meio "certo" de dizer qual operação vc quer que seja efetuada primeiro), vc deve multiplicar antes de somar.

Bem, a maioria das linguagens que eu conheço leva em conta a matemática (o que é absolutamente normal, visto que a computação como ciência nasceu da matemática).

Exemplos:
No oracle, faça um SELECT 1 + 2 * 3 FROM DUAL, a resposta é 7. Em java, c/c++, delphi/pascal, a resposta também é 7.

Em Caché Object Script, a resposta é 9. Experimente abrir um terminal do Caché, e fazer um WRITE 1 + 2 * 3, a resposta é 9.

Isso porque essa linguagem não segue o padrão matemático, muito menos o padrão da maioria das linguagens. OK, questão de implementação, e está também na documentação, mas não deixo de ver isso com maus olhos. Especialmente se vc está em conformidade com o resto do mundo, acostumado com a precedência certa dos operadores (por "certo", quero dizer conforme a matemática).

Tudo bem, é questão de escolha da linguagem seguir ou não qualquer padrão. Mas isso pode produzir alguns erros. Eu fui atentar para esse fato porque um loop meu estava estourando os limites. Acostumado a programar em outras linguagens, não achava que uma simples continha pudesse estar dando errado. Depois de verificar o algoritmo umas duas vezes, uma luz se acendeu, e resolvi fazer um teste, e descobri que devia ter colocado explicitamente parênteses na expressão. Depois chequei a documentação, e realmente estava lá: a precedência é estritamente da esquerda para a direita.

Mas por que os designers da linguagem fizeram esta escolha? Será que eles queriam ser "cool", diferentes do resto do mundo, ou seriam eles rebeldes com ou sem causa?

Apesar de serem explicações, acredito que não. Acredito que tenha sido por causa implementação da execução do script, que é mais fácil de implementar quando não se considera precedência matemática. Ou seja: preguiça. A lei do menor esforço que move este mundo =P

Se bem que mesmo sendo uma linguagem interpretada, de script, é muito desculpa. Javascript também não vai contra o mundo. Experimenta um 'document.write(1 + 2 * 3)', que ele te devolve 7.

U_U