quarta-feira, 28 de novembro de 2007

Entrevista de Emprego

Um ótimo texto sobre carreira e entrevistas de emprego, visto no Teorias Cobalísticas, creditado ao grande Max Gehringer:

Quem mandou perguntar?

Há anos, com a coloboração de colegas recrutores de pessoal, eu venho colecionando respostas reais dadas por candidatos a emprego. Mas só recentemente descobri que a coleção pode ter a finalidade prática de confortar os aflitos. Quando alguém diz: "Não sei, acho que fui mal na entrevista", e aí vê essas preciosidades verbais, percebe que não foi tão mal assim. Alguns exemplos:

Entrevistador - E então, você está construindo uma networking?
Candidato - Veja bem, eu não sou engenheiro, sou administrador.

Entrevistador - Como você administra a pressão?
Candidato - Ah, tranqüilo. 1 por 7, no máximo 12 por 8.

Entrevistador - Manter sempre o foco é muito importante. E me parece que você tem alguns lapsos de concentração.
Candidato - O senhor poderia repetir a pergunta?

Entrevistador - Como você se sente trabalhando em equipe?
Candidato - Bom, desde que não tenha gente dando palpite, me sinto muito bem.

Entrevistador - Como você se definiria em termos de flexibilidade?
Candidato - Ah, eu faço academia. Sou capaz de encostar o cotovelo na nuca.

Entrevistador - Nós somos uma empresa que nunca pára de perseguir objetivos.
Candidato - Que ótimo. E já conseguiram prender algum?

Entrevistador - Vejo que você demonstra uma tendência para discordar.
Candidato - Muito pelo contrário.

Entrevistador - Em sua opinião, quais seriam os atributos de um bom líder?
Candidato - Ah, são várias coisas. Mas a principal é ter liderança.

Entrevistador - Noto que você não mencionou sua idade aqui no currículo.
Candidato - É que eu uso óculos, e isso me faz parecer mais velho.
Entrevistador - E qual é a sua idade?
Candidato - Com óculos ou sem óculos.

Entrevistador - Quais seriam seus pontos fracos?
Candidato - Ah, é só o joelho. Até tive de parar de jogar futebol.

Entrevistador - Há alguma pergunta que você queira me fazer?
Candidato - Eu parei meu carro aí na rua. Será que eu vou ser multado?

Entrevistador - Por que, dentre tantos candidatos, nós deveríamos contratá-la?
Candidato - Eu pensei que responder a isso fosse seu trabalho.

Entrevistador - Como você pode contribuir para melhorar nosso ambiente de trabalho?
Candidato - Bem, eu começaria trocando a recepcionista, que é muito feia.

Entrevistador - Quando digo "sucesso" qual é a primeira palavra que lhe vem à mente?
Candidata - Pode ser duas palavras?
Entrevistador - Pode
Candidato - Milho. Nário.

Entrevistador - Várias pessoas que se sentaram aí nessa mesma cadeira hoje são gerentes.
Candidato - Puxa, o fabricante da cadeira vai ficar muito feliz em saber disso.


Tentei verificar se esse texto é mesmo de autoria do Mr. Max (em tempos de Internet, o que mais se vê é texto creditado erroneamente), mas o máximo que consegui foi esta página, creditando o texto ao Max Gehringer, e dizendo que fora publicado na revista Exame.

segunda-feira, 26 de novembro de 2007

Listas em Cache - problemas

Como eu já havia comentado no post anterior, e o comentário do Carlos reforçou, ao se usar listas ou arrays do Caché, temos o problema do tamanho destas. Como o Carlos bem falou, quando estamos inserindo itens numa lista em memória, tudo ocorre bem. O problema é quando vamos salvar a classe que mantém essa lista.

Por que disso? A resposta passa por como o Caché persiste os objetos. No post anterior comentamos que as listas/arrays são do tipo serial, herdando de %SerialObject. Isso quer dizer que elas vão "embutidas" dentro de outros objetos ao serem persistidas, ou seja, essas classes não têm um ID próprio.

Olhem como fica um objeto persistido da classe que o Carlos passou no comentário, contendo no exemplo abaixo, uma lista com dez elementos:

^Teste.EstouroD(1) = $lb("",11,$lb(1,2,3,4,5,6,7,8,9,10))

Note como a lista vai "serializada" dentro da classe. Como no Caché um objeto persistido é na verdade um valor numa global, e valores nas globais são basicamente grandes Strings, ficamos limitados a objetos menores que 32Kb no total, já que esse é o limite de uma String (pelo menos até a versão 5.2).

Se tivermos uma classe com muitas propriedades, e cada propriedade for muito grande, se esse objeto for serializado dentro de uma lista, o tamanho da lista vai ficar ainda mais enorme. Nestes casos, vale a pena deixar a classe persistente, assim na lista vão ficar guardados os IDs dos objetos, e não os objetos em si serializados.

De qualquer jeito, se a sua lista contiver muitos valores, o erro deve persistir. Nestes casos, o negócio é abandonar um pouco a abordagem OO e partir para gerenciar dados nas globais mesmo.

P.S. Em outro comentário, o Carlos fala sobre o %System.Encription, pra resolver os problemas em relação a criptografia em Caché. Realmente, eu não tinha visto essa classe, e o help não ajudou muito, pq eu havia procurado pelo DES, e a classe só tem suporte para o AES (considerei o DES como sendo o menor denominador comum em casos de criptografia, por ele ser um dos mais antigos, bem documentados e relativamente fácil de implementar).

No caso, o AES não iria adiantar, porque no lado cliente, só tinhamos suporte pro DES. Mas, fica aqui a dica.

quarta-feira, 14 de novembro de 2007

Não se trata disso

Resposta genial, ilustra muito bem a sociedade e o tipo de educação que temos hoje em dia, retirado do blog Circuito Integrado da Folha:

Cerf estava em uma conversa, digamos, mais animada com um dos participantes da mesa, que argumentava que o Google deveria filtrar o conteudo usando a qualidade e credibilidade como criterios tambem.

"O Google nao se trata disso", disse Cerf.

"Mas para meu filho de nove anos o Google eh a verdade, ele diz: esta no Google", --rebateu o palestrante.

"Entao o problema nao eh com o Google, eh de criacao", rebateu Cerf, para aplausos de partes dos presentes e constrangimento de outros."


(Velhinho esperto - foto retirada da biografia no ICANN)

Só pra constar, Vinton Cerf é um dos vice-presidentes do Google, e um dos criadores do TCP/IP, que é uma das bases da Internet, ou seja, que possibilita vc ler isso agora.

sábado, 3 de novembro de 2007

Listas em Cache

Listas em Caché - abordagem orientada a objeto

Ao contrário do Java, no Caché da Intersystems não temos uma grande API quando se trata de estruturas de dados de coleções (listas, filas, conjuntos, árvores, etc). De fato, quando trabalhamos com orientação a objeto no Caché, basicamente existem dois conceitos de coleção: as listas e os arrays.

Vamos verificar então o que listas e arrays significam para o Caché. Listas são, como o nome bem indica, listas de objetos (como aqueles que você escreve) ou de tipos básicos (como %Integer ou %Float). Cada elemento de uma lista é associado a uma posição da lista. Neste caso, listas em Caché são bem parecidas com listas em Java (em termos de interface, de implementação não dá pra comparar bem, já que em Java podemos ter listas como ArrayList ou LinkedList.

Em Caché, a classe abstrata %Collection.AbstractList fornece a interface básica pela qual podemos trabalhar com listas. Adaptada da documentação, o código abaixo ilustra a criação de uma lista, quatro inserções de elementos nesta lista, e como podemos percorrer a lista, recuperando seus elementos:


; instancia nova lista
Set list=##class(%ListOfDataTypes).%New()
; adiciona quatro elementos na lista
Do list.Insert("DArtagnan")
Do list.Insert("Athos")
Do list.Insert("Porthos")
Do list.Insert("Aramis")
; percorre a lista
For i=1:1:list.Count() Write list.GetAt(i),!



Arrays no Caché, por outro lado, são bem diferentes do que o nome pode sugerir (pelo menos pra mim, que vim de linguagens como Java, Pascal, C/C++). Arrays do Caché são como objetos da interface Map do Java, eles necessitam além do elemento a ser guardado na estrutura (objeto ou tipo básico de dados), uma "chave" associada, para você acessar o elemento. Essas estruturas são muito úteis ao implementar coisas como tabelas de Hash, assim como a Hashtable.

Nada melhor que um exemplo para esclarecer, por isso vamos ver o exemplo adaptado da documentação da classe %Collection.AbstractArray, que serve como interface para as classes de array no Caché:

; instancia um objeto array 
Set arr=##class(%ArrayOfDataTypes).%New()
; coloca itens no array - o segundo parâmetro é a "chave"
Do arr.SetAt("red","color")
Do arr.SetAt("large","size")
Do arr.SetAt("expensive","price")
; iteração no array
Set key=""
For Set value=arr.GetNext(.key) Quit:key="" Write key,":",value,!



(Notem que o método SetAt, correspondente ao put do java.util.Map, tem os parâmetros invertidos com relação a este, enquanto no put a chave vem como primeiro parâmetro, no SetAt a chave vem no segundo.)

Em ambos exemplos acima, usamos tipos de dados básicos como elementos nas lista e array, mas poderíamos ter usado objetos. Para isso, ao invés de termos usado %ListOfDataTypes e %ArrayOfDataTypes, usaríamos %ListOfObjects e %ArrayOfObjects, respectivamente.

Quando usamos coleções como propriedades de classes, podemos ou declarar a propriedade explicitamente como objeto das classes de Array/List (como faríamos como qualquer outra classe), ou então podemos declarar assim:


; uma lista de %Strings:
; em vez de: Property Colors As %ListOfDataTypes
Property Colors As List Of %String


Vale lembrar que esses objetos arrays e lists são do tipo Serial, ou seja, herdam de %SerialObject, e que portanto vão "serializadas" dentro de outra classe, se forem persistidas. Se a sua lista for muito grande, ou mesmo que a lista não tenha muitos elementos, mas os seus elementos sejam %SerialObject, pode ocorrer um erro de MAXSTRING.

Considere a classe abaixo:


Class Teste.Estouro Extends %Persistent [ ClassType = persistent ] {

Property lista As list Of %String;

ClassMethod teste(qtde as %Integer)
{
set a = ##class(Teste.Estouro).%New()
w a.%Save()
For i=1:1:qtde {
do a.lista.Insert(i)
}
w a.%Save()
}
}


Experimente chamar de uma janela de terminal o método teste, passando diferentes valores. Ao se passar uns 10000, você já pode conferir o erro de MAXSTRING. Lembrando que isso vale para versão 5.2 do Caché. Ouvi dizer que na versão 2007, o limite de tamanho de Strings aumentou, então esse tipo de erro deve ocorrer menos frequentemente, mas não cheguei a verificar na documentação isso.

quarta-feira, 31 de outubro de 2007

Dicas de como fazer um bom curriculo

Originalmente iria postar só no outro blog, mas achei que valia apena postar aqui tb.

E aproveitando o embalo das tirinhas trabalhadores (assunto do outro blog), mais um post sobre o duro mercado de trabalho, aquele que a gente faz pra ganhar o leitinho (esperamos que não adulterado) para as crianças XD.

Desta vez, umas dicas para quem está precisando fazer ou atualizar o currículo. O texto abaixo foi retirado da revista Você S/A do mês passado, setembro de 2007. Se você não lê, eu aconselho, é uma das poucas revistas (tirando os meus quadrinhos/mangás, claro) que eu compro na banca.

Bem, esse post foi inspirado mesmo na folha online, que coincidentemente deu essas dicas, que também ajudarão a você formar um currículo nota dez (e se graças a isso ganhar um empregão, eu aceito um presentinho =P):


Então, sem mais delongas, aqui vai:

O currículo certo e o errado

Saiba como evitar os problemas que podem tirar o seu nome da disputa por uma vaga antes mesmo da entrevista
Por Françoise Terzian

Passar para a segunda fase de um recrutamento que começou com a seleção de currículos requer atenção a detalhes. Fotos inadequadas, português errado e letras coloridas chegam a derrubar um em cada quatro currículos enviados por candidatos a uma vaga. "Recebemos 180 000 currículos ao ano e 25% deles são descartados por ser muito longos ou conter erros gramaticais", diz Augusto Costa, diretor- geral da Manpower no Brasil. A consultoria de recursos humanos, de São Paulo, atende empresas como Motorola e Michelin. Aqui, Augusto e outros especialistas em recrutamento apontam quais são os sete erros fatais de um currículo. Sem eles, suas chances de conseguir a vaga aumentam. Veja, abaixo, exemplos resumidos de um currículo bom e de outro ruim.

(Um currículo bem feito)

(E outro nem tanto - veja as dicas abaixo)

EVITE

FOTOS E RG
  • Só mande foto (3x4 comportada, claro) se isso for pedido por quem está fazendo a seleção. "E nunca inclua o número do RG e CPF no currículo", diz Mara Turolla,consultora da Career Center,empresa de recolocação.

POLUIÇÃO VISUAL
  • Nada de firulas: a melhor apresentação são folhas brancas simples e fontes Arial ou Times New Roman — mais clássicas.

ERROS DE PORTUGUÊS
  • São os mais comuns — como os que estão marcados em amarelo ao lado — e podem reprovar um bom candidato. "Demonstram que ele foi desleixado.Ou que não sabe escrever", diz Augusto, da Manpower.

FALAR DIFÍCIL
  • Informações demais fazem o selecionador gastar tempo — e a média que eles dedicam a um currículo é 40 segundos. Não vá escrever que fez curso de datilografia, tampouco. "Prolixidade mascara as informações relevantes", diz Alexandre Ullmann, gerente de recrutamento da CPM Braxis, empresa de tecnologia da informação.

MENTIRAS
  • Há quem diga que concluiu um curso sem ter realmente chegado ao fim. Vale o mesmo para dizer que é fluente em inglês. Por fim, errar nas datas de permanência nas empresas acaba levantando suspeitas do entrevistador.

FALTA DE FOCO
  • Realce seus resultados numéricos mais do que as tarefas que você desempenhava. Em vez de "analisava as estratégias de marketing", use "elevei as vendas da empresa em 15% após analisar a concorrência".

DESTACAR O INÍCIO DA CARREIRA
  • Deixar os últimos empregos para o fim pode fazer seu currículo ser descartado prematuramente.As informações devem vir em ordem decrescente."Se sua carreira for muito longa, saliente os cargos mais recentes", diz Augusto Puliti, gerente da consultoria Michael Page.

...Fim da matéria

Não deixem de ler a Você S/A. E não, não estou ganhando nada pra fazer essa propaganda. XD

P.S. No título a palavra 'currículo' está sem acento, pro blogger não comer o 'i' da palavra no link que ele gera.

segunda-feira, 29 de outubro de 2007

A falta que um ROWNUM faz...

Hoje precisava calcular a média de um campo dentro de uma tabela no Caché, da Intersystems. A lógica era até bem simples: eu precisava ver a média dos preços das últimas N compras, depois de uma certa data.

O Oracle tem a pseudo-coluna ROWNUM, o que facilita bastante. Num SQL do Oracle, eu teria algo parecido:

SELECT AVG(PRECO)
FROM COMPRAS
WHERE DATA > '01/01/01' AND ROWNUM <= 20
ORDER BY DATA DESC


Neste exemplo simples, estamos pegando a média dos preços das 20 últimas compras, efetuadas depois do dia 01/01/01. Bem simples, não?

Fui procurar algo parecido no Caché, mas não encontrei nada. Bem, tem algumas coisas, como o nro de linhas retornadas numa consulta, ou mesmo o TOP, usado no SELECT. Infelizmente, nenhuma solução apareceu, então resolvi fazer um cursor (ou poderia tb ter feito um ResultSet, se quisesse), e iterar pela consulta e calcular a média 'na mão'.

Bem, a consulta ficou mais ou menos assim:

SELECT TOP 20 PRECO
FROM COMPRAS
WHERE DATA > '01/01/01'
ORDER BY DATA DESC


Notem o 'TOP 20', que diz que a consulta só precisa me retornar os 20 primeiros resultados. Infelizmente, não dá pra usar no WHERE, que é onde eu precisaria, para executar a função agregada de média, AVG.

Não vou descrever como eu fiz para iterar na consulta com cursor, e calcular a média, até porque isso é relativamente fácil, e eu já estou muito cansado hoje.

Fui.

sexta-feira, 26 de outubro de 2007

Executando código dinamicamente no Caché

...Ou Como avaliar e executar uma expressão entrada pelo usuário.

O cenário: na aplicação, o usuário pode cadastrar algumas fórmulas, fórmulas que podem conter algumas variáveis previamente cadastradas. Essas variáveis podem conter valores simples, ou ainda, podem ser dependentes de um parâmetro. Um exemplo de fórmula encontrada é a seguinte:

(0.50 * ([VAR1(I)] / [VAR1(0)])) + (0.50 * [VAR2])

onde:

  • VAR1(I) é uma variável, cujo valor depende do parâmetro "I".

  • VAR1(0) é uma variável, cujo valor depende do parâmetro "0".

  • VAR2 é a variável simples.


Notem que por convenção, na fórmula, as variáveis estão entre colchetes []. Muitas variáveis são na verdade, séries históricas com valores. Os parâmetros para elas refletem apenas qual a época/data que o valor deve ser considerado.

O problema: dado um intervalo de tempo, preciso saber do valor da fórmula. Ou seja, preciso executar a fórmula, como se fosse parte do programa, ou então fazer o parsing, etc. Em algumas outras linguagens de script, existe a função eval(), que converte um texto (ou seja, uma string) em código, executando-o. Em Caché Object Script, temos o comando XECUTE, que executa o conteúdo de uma String como se fosse código, ou seja, efetua macro substituição.

Mesmo com essa facilidade, ainda assim precisava identificar dentro da %String cada variável. Para isso, escrevi uma pequena função, que coloquei num arquivo .MAC, para ser usada.

(A função, o código-fonte está aqui.)

Para essa função, passamos a fórmula, bem como caracteres que delimitarão o que será considerado variável, tb passamos uma String que será colocada no lugar da variável na fórmula original, bem como um caracter delimitador (coisa muito usada em sistemas MUMPS-like).

O resultado da função é a fórmula, com as variáveis substituídas pela String, além de todas as variáveis encontradas, separadas na String pelo caracter delimitador. Usando como String de substituição "?", e como caracter delimitador "~", a função para fórmula do exemplo acima retorna:

(0.50 * (? / ?)) + (0.50 * ?)~VAR1(I)~VAR1(0)~VAR2

Em seguida, resolvemos o valor de cada variável, e substituimos na fórmula, e retiramos da String as variáveis. Não vou ilustrar esses passos aqui, pois são bem específicos para o modelo de dados. Neste momento, nossa String da fórmula tem algo como:

(0.50 * (12.3 / 10.88)) + (0.50 * 10)

Note que a sentença é matematicamente bem formada, e inclusive a precedência de operadores é bem explícita com o uso de parênteses, o que deve ser sempre feito, visto que o Caché não segue o padrão matemático quanto a precedência de operadores.

Bem, finalmente vamos executar a expressão. Primeiro, atribuimos o comando SET dentro de uma String, como por exemplo:

SET umaVariavel = "SET resultado = " _ stringComFormula

Depois fazemos o XECUTE:

XECUTE umaVariavel

E pronto! O valor da expressão está na variável "resultado"! Apenas tenham precaução ao usar o comando XECUTE, pois dependendo da situação (e do abuso), ele pode deixar o seu código muito ruim para manutenção.

quinta-feira, 11 de outubro de 2007

Fazendo upload de arquivos com Caché & CSP

Fazendo upload de arquivos com Caché & CSP - ou... Meu primeiro CSP

Como já mencionei por aqui (ou não), trabalho primariamente com o Ensemble da Intersystems (e consequentemente com o Caché, já que o Ensemble é construído sobre este). Portanto, praticamente não mexo com coisas que envolvam interface de usuário, mas essa semana precisei fazer uma página que desse um upload num arquivo para o servidor. Então, toca a aprender CSP, as famigeradas Caché Server Pages. Na verdade, aprender eu não aprendi não, só vi o necessário para os meus propósitos.

Como diz na página de introdução ao CSP, você pode ir por dois caminhos: escrever páginas html e imbutir tags dentro, da mesma maneira que JSP (Java Server Pages) ou PHP, gerando um arquivo .csp; ou escrever uma classe, e nesta classe você jogar outputs montado o html, a mesma abordagem dos Servlets.

Na parte de exemplos (namespace SAMPLES do Caché) já tinha um arquivo .csp com um exemplo de upload (adivinhe qual arquivo: csp/samples/upload.csp). Com base nesse arquivo, preparei o meu.



(Exemplo da página de upload em SAMPLES, e o resultado de um upload)

Bem, como não consegui colocar o código fonte de maneira legal no blogger, vou colocar uma figura, e deixar um link para o codigo fonte.


(Figura com o codigo fonte)

A idéia principal desta página pode ser resumida assim: ao chamar a página, verifica se o parâmetro 'campo' foi enviado, se não foi, nem precisa mostrar nada. Verifica se foi enviado para o csp um arquivo, se não foi, coloca um form html para dar o upload (sendo que o submit deste form vai para o próprio csp), e se já foi efetuado um upload, atribui o nome do arquivo a um campo do html que chamou este csp (este csp deve ter sido aberto de outra página), e exibe um link para fechar a janela.

A seguir, vamos detalhar alguns aspectos.

Como diria o retalhador, vamos por partes:

<CSP:CLASS INCLUDES="SOMEINCFILETOINCLUDE">

Aqui indicamos um arquivo .INC (omitindo a extensão) que vamos referenciar no código, ou seja, estamos importando o INC. Arquivos .INC no Caché geralmente são usados para definição de macros (estilo macros C/C++), tanto para código quanto para definição de constantes.

/***********************************

<csp:if condition='($data(%request.Data("campo",1)))'>

Aqui temos uma tag específica do csp, que determina uma condição, o famoso 'if'. A condição como é vista como o valor do atributo 'condition', da tag csp:if, e pode conter código COS (Caché Object Script), retornando verdadeiro ou falso. Neste caso, apenas estamos checando se a requisição http (objeto %request) contém um parâmetro de nome 'campo'. Mais informações sobre o objeto %request pode ser visto na página da classe %CSP.Request.

/***********************************

<csp:if condition='($data(%request.MimeData("FileStream",1)))'>

Outra vez um if, mas desta vez, se repararmos, não verificamos o parâmetro da requisição http com %request.Data, mas com %request.MimeData. Isso porque desta vez estamos checando se foi enviado um arquivo, usando o padrão MIME.

/***********************************


Arquivo enviado: <b>#(%request.MimeData("FileStream",1).FileName)#</b><br>
Tamanho: <b>#(%request.MimeData("FileStream",1).Size)#</b><br>
Tipo: <b>#(%request.MimeData("FileStream",1).ContentType)#</b><br>


Quando enviamos um arquivo, o %request.MimeData("NOMEPARAMETRO",1) retorna um objeto %CSP.BinaryStream. No código acima, mostramos na tela, algumas informações que podem ser obtidas desta stream. Notem que os valores que serão impressos na tela, estão entre '#(' e ')#'.

/***********************************

<script language="Cache" runat="server">

Aqui iniciamos um bloco de Caché Object Script que será executado no servidor, ou seja, neste bloco, se quisermos que algo apareça na tela, teremos que escrever na saída padrão, usando o comando Write.

/***********************************

set dname = $$$SomeDirectory

Ainda dentro do bloco aberto acima, aqui criamos e atribuimos um valor a variável 'dname'. Este valor é uma macro, indicada pelos três cifrados ($$$). Esta macro deverá estar definida em algum dos arquivos .INC, que como vimos anteriormente, podem ser importados para este CSP.

/***********************************


Set stream=##class(%FileBinaryStream).%New()
Set stream.Filename= dname _ fname

do stream.CopyFrom(%request.MimeData("FileStream",1))
do stream.Flush()
do stream.SaveStream()


Aqui criamos um stream para um arquivo (classe %FileBinaryStream), na variável 'stream', setamos o nome e caminho completo deste, e depois usamos o método CopyFrom, para copiar o conteúdo da stream que foi enviada via http, para o arquivo.

/***********************************

<csp:else>

Aqui fechamos a parte 'então' do if, e colocamos a parte 'senão'.

/***********************************

<form enctype="multipart/form-data" method="post" action="este_arquivo_csp.csp">
Arquivo: <input type=file size=30 name=FileStream>
<p>

<script language="Cache" runat="server">
New bytes
Set bytes="<input type=""hidden"" name=""campo"" value=""" _ %request.Data("campo",1) _ """ > "
Write bytes,!
</script>

<p> <ul><input type="submit" value="Upload file"></ul> <p>
</form>


A parte do 'else' contém a definição de um formulário html, destinado a receber o arquivo que será carregado no servidor. Notem que dentro da definição do form, misturamos uma parte com código Caché, destinado a colocar um campo escondido no form, a fim de preservar o valor que fora anteriormente passado, na requisição. Essa é uma solução, outra solução seria usar sessão, mas como este caso é bem simples, não fui verificar como o gerenciamento de sessão é realizado com os CSPs.

/***********************************

Bem, esse post já ficou maior do que o esperado, então terminamos por aqui. No arquivo csp existem outras funcionalidades, mas não vou comentá-las agora, tais como a parte de javascript, e as funções de diretórios do COS. E tb tirei toda a formatação, sendo um html bem simples. Além disso, como suprimi várias partes, pode ser que o csp não compile de primeira, já que eu não testei ele nessa versão 'mais light'.

Como já dizia o Pernalonga, por hoje é só, pessoal.

sexta-feira, 5 de outubro de 2007

Curiosidades do Caché

Tipagem fraca (bem, seria melhor dizer inexistente) do Caché da Intersystems as vezes produz alguns resultados curiosos, especialmente se o leitor estiver mais acostumado com matemática do que com "as coisas" que existem em TI.

Por exemplo, num terminal Caché, declaramos duas variáveis*:

USER>set x = "5.10"

USER>set y = "5.1"

USER>write x
5.10
USER>write y
5.1

Lembrando que no Caché (assim como no bom e velho C) o 1 (um) representa verdadeiro, e 0 (zero) representa falso, e o separador padrão de casas decimais é o ponto ".", vemos o resultado de algumas comparações*:

USER>write x = y
0
USER>write x > y
0
USER>write x >= y
1

Ou seja, (x = y) é falso, (x > y) tb é falso, mas (x >= y) é verdadeiro. Em bom português, "x" não é igual a "y", "x" também não é maior que "y", mas "x" é maior ou igual a "y", huhuhuhu. 8-)



Bem, vamos as explicações:

  • A primeira sentença (x = y) é falsa, pois neste caso, o Caché testa a igualdade das Strings, e a String "5.10" é diferente da String "5.1".

  • A segunda sentença (x > y) é falsa, mas não porque o Caché compara tamanhos de Strings (x < y também retorna falso), mas porque ao se usar o operador ">", o Caché dinamicamente converte os valores para numéricos, e como "x" NÃO É MAIOR que "y" (numericamente, "x" É IGUAL a "y", já que 5,1 = 5,10), temos o resultado falso.

  • Agora já dá pra entender porque (x >= y) retorna verdadeiro, não? Ao usar operadores "<", "<=", ">", ">=", o Caché tenta converter as variáveis para tipos numéricos, e como numericamente (lembrando que no Caché o separador decimal é o ponto ".") "5.1" É IGUAL a "5.10", a expressão (x >= y) é verdadeira.

Hehehe, não mostrem isso para não iniciados, isso deve confundir pra caramba, assim como a precedência de operadores no Caché, que eu já comentei.

* A saber: o comando "set" é usado para inicializar/atribuir um valor a uma variável, e o comando "write" escreve na saída padrão (no caso, o próprio terminal) um valor.

sexta-feira, 28 de setembro de 2007

Ensemble melhor software de integração do mercado? Não exatamente...

Outro dia estava pesquisando algo no Big G (não lembro exatamente o quê), e me deparo com essa notícia: Pesquisa reconhece Ensemble como o melhor software de integração do mercado. Fiquei atônito! Não que o Ensemble seja de todo ruim, mas o fato é que ele está muuuuuito longe do que eu gostaria (OK, ele tem partes realmente horríveis).

Segui o link, e lendo a página, descubro que como muitas chamadas de imprensa, essa aí tb é no mínimo incompleta. Vamos aos fatos: Realmente, uma pesquisa de mercado apontou isso. Entretanto, faltou mencionar que esse 'mercado' é bem restrito, mais especificamente o mercado de empresas de TI ligadas a saúde (Healthcare IT), nos States. Nada de mal nisso, mas a chamada ficou meio apelativa, já que a expressão 'do mercado' remete a uma idéia muito mais ampla.

A empresa que fez a pesquisa é a KLAS, empresa "especializada em monitoramento e relatórios de performance de vendedores e empresas de serviços profissionais de TI voltadas a área de saúde" (tradução livre que consta no relatório). A pesquisa propriamente dita não está disponível completa (pelo menos não gratuitamente no site), mas um pequeno "trailer" dela vc pode baixar aqui.

O resultado dessa pesquisa nem me surpreendeu muito (depois de eu ler a página). O Caché (e por tabela, o Ensemble, que é construído sobre este) é muito forte na área médica (TI em hospitais, etc) nos EUA, graças a herança do MUMPS. Apesar da Intersystems não gostar muito, o Caché é uma implementação do MUMPS, algo que por questões de marketing eu acho (e não sei porquê), eles não gostam de alardear por aí.

Na minha opinião, se fosse realmente uma pesquisa no mercado geral, o Ensemble não se sairia muito bem. Não tenho dados que confirmem isso, então se algum leitor que porventura tenha caído aqui souber de dados confiáveis, me avise! (por favor, sem aqueles 'white papers' da própria empresa, que o pessoal de marketing deve elaborar).

sexta-feira, 21 de setembro de 2007

Paralisação na Celesc - Tempo

Depois da ameaça (e efetiva paralisação hoje de manhã), a diretoria e o presidente Pinho Moreira (da Celesc)se sentaram para negociar. Por isso, a paralisação terminou. Vamos ver se não acontece mais nenhum perrengue, que obrigue a chegar a esse ponto (ou pontos mais fundos ainda).

quinta-feira, 20 de setembro de 2007

Paralisação na Celesc - Prólogo

Como já disse, trabalho na Celesc.

Bem, amanhã (dia 21/09) talvez haja uma paralisação, em decorrência da diretoria ficar enrolando (e pior, ficar enrolando de uma maneira que diz: "vamos te enrolar pq vcs são trouxas"), alguns assuntos pro acordo coletivo de trabalho.

Não sei se vou aderir, afinal, não pretendo continuar por muito tempo na empresa, e não quero ficar tendo dor de cabeça por causa de outras pessoas, muitas delas, mesmo tendo interesse no assunto, vão ficar só esperando pelos outros. (Isso sim é ser brasileiro: eu sou brasileiro e não desisto nunca. Não desisto de esperar alguém fazer por mim...)

Se ainda não notou, já perdi as esperanças nessa humanidade...

terça-feira, 4 de setembro de 2007

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

Finalizando a trilogia...

Como havia dito no fim do post parte 2, escrevendo o SOAP-Body na mão, eu consegui que fosse enviada a mensagem de requisição corretamente para o Web Service. Entretanto, por algum motivo que desconhecia, a mensagem de resposta não estava chegando corretamente. Primeiro pensei que não havia funcionado, mas depois analisando o tráfego da máquina, olhando a stream TCP, vi que o serviço estava retornando corretamente. O problema é que o Caché/Ensemble não entendia a resposta.

Testei com as classes cliente geradas automaticamente, e mesmo eu não mudando uma vírgula, o erro continuava. Conclusão: de nada adiantara toda a minha ginástica até agora. Analisando o código fonte, e pelas poucas pistas que o erro retornava, cheguei a conclusão de que o Caché não estava conseguindo instanciar uma classe com base na mensagem de resposta do Web Service:


(exemplo de uma resposta a uma requisição bem sucedida)

A esta altura, já cansado de tanto fuçar tanto a documentação quanto o código fonte das classes do pacote %SOAP do Caché, parti para o baixo nível: já que o pacote %SOAP não conseguia entender o Web Service, então desci na pilha de protocolos pra implementar de uma maneira que desse certo. O Caché puro não tem classes para lidar direto com o HTTP (pelo menos eu não achei), mas o Ensemble tem um "Adapter" HTTP. Lá vou fazer testes com ele, e descubro que não consigo usar no Web Service específico, porque esse Adapter não me deixa setar no cabeçalho um campo, e o serviço exige que contenha no cabeçalho da requisição HTTP o campo SOAPAction.

(Um parêntese: Adapters na linguagem do Ensemble são pedaços de softwares que têm a função de conectar o Ensemble a outros sistemas/tecnologias. A documentação desses Adapters deixa muuuito a desejar, pra ter uma idéia, na página que lista os Adapters nem está listada o Adapter HTTP, e muitos dos Adapters ali listados não consta do que eu tenho aqui disponível. Estranho, muito estranho)

Não conseguindo usar o HTTP, resolvi descer mais um nível na pilha, e usar direto o TCP. O Adapter TCP do Ensemble nada mais é do que um "wrapper" para os Sockets do Caché, um assunto que eu já dei um pitáculo neste dois posts anteriores aqui e aqui.


(pilha de protocolos... quase que chego no IP =P )

O bom de usar TCP é que eu tenho o controle de tudo, dos cabeçalhos do HTTP, do conteúdo da mensagem XML SOAP, etc. O ruim é que você pra usar isso, além de entender boa parte de toda essa parafernália, tem que fazer tudo no braço. Mas como as ferramentas prontas do Caché/Ensemble estavam só me dando erro, não tive muita escolha. XD

Olhem como ficou (mais ou menos, aqui tá simplificado):



E assim, apesar de ficar com um código horrível, usando mais anti-patterns possível, finalmente funcionou. E com tudo isso chamam o Ensemble de "Integrador"... Imagina se não fosse...

segunda-feira, 3 de setembro de 2007

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

Continuando a saga...

Eu havia me baseado na versão anterior do Web Service/Wsdl que me foi passado, pra implementar todas as estruturas internas no Ensemble (a saber, o BPEL e as "Business Operations", na linguagem deste).

Bem, na versão anterior, o wizard gerava todas as estruturas como classes, ou seja, se eu tivesse que atualizar uma estrutura "Pessoa", eu teria uma classe "Pessoa" com as propriedades "Nome", "Idade", etc... e o método receberia uma instância desta classe. Entretanto, agora o wizard gerou o método recebendo cada propriedade da estrutura como um parâmetro, ou seja, antes eu tinha:


atualizaPessoa(p as Pessoa)


E agora ele me gerou:


atualizaPessoa(nome as %String, idade as %Integer, ...)


E é CLARO que isso quebrou tudo o que eu havia feito. Para não ter que retrabalhar todas as outras classe, resolvi que seria melhor alterar na mão as classes que o wizard havia gerado, para se adaptarem aos objetos com que eu já trabalhava.

Então lá fui eu alterar a classe Web Client gerada:


(antes)


(depois)

Pensei que só isso já bastaria para funcionar. Compilei e mandei rodar. Não funcionou... Resumindo a luta, descobri que desse modo ele não estava escrevendo o Body do envelope SOAP conforme o Web Service esperava, contendo os namespaces corretamente declarados.

Procurando uma solução, me deparei com a propriedade "WriteSOAPBodyMethod" da classe %SOAP.WebBase, que é superclasse de %SOAP.WebClient. Essa propriedade permite definir um método que escreva de maneira personalizada o body de uma mensagem SOAP.

Usando essa propriedade, temos:


(escrevendo um elemento SOAP-Body personalizado)

Com isso, finalmente consegui preservar todo o código que eu havia feito anteriormente, e ainda acionava corretamente o Web Service. Entretanto, a saga não acaba aqui...

Continua...

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.

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

sexta-feira, 1 de junho de 2007

Juros baixinhos

Bem, quem não mora em Santa Catarina, não deve ter visto a campanha do Besc, o banco estadual (que foi federalizado e que agora está num rolo só, mas esse não é o ponto de discussão). Dentro desta campanha publicitária, que produziu alguns dos melhores comerciais que eu vi em um bom tempo, um dos comerciais (um dos últimos), me fez refletir um pouco, e exercer um paralelo com outras áreas da nossa vida, em especial, a nossa área de informática (não, eu não gosto do termo TI, mas eu acabo usando =/).

O referido comercial pode ser visto no youtube, e eu coloco ele aqui embaixo. Assistam ele antes de continuarem a leitura aqui.



Agora que já devem ter assistido o vídeo, vamos ao paralelo.

Assim como o gerente do banco que fica tentando "mascarar" os juros, temos vendedores e lobistas na área de informática que tentam de toda maneira, empurrar um produto ou serviço, elevando os pontos positivos às alturas, e "esquecendo" de mencionar alguma inconformidade e/ou problema que este venha a ter na empresa, tal como a integração com outros sistemas.

Isso é até de certo ponto, normal e aceitável (não é crime, mas eu pessoalmente acho imoral, mas eu geralmente sou minoria...), o problema é quando esses vendedores/consultores conversam com o pessoal que não é da área de informática, usuários finais, mas que têm poder de decisão, tal como diretores e presidentes.

Por exemplo, no organograma aqui da Celesc, o departamento de informática, vulgo DPTI, está subordinado a diretoria de assuntos financeiros. O que isso quer dizer na prática? Que quem toma decisões pra valer é o diretor financeiro, e que o chefe de TI da empresa, tem poderes (muito) limitados. "Nas empresas onde a área de TI está subordinada à área financeira, os sistemas são um pandemônio". Essa frase (ou melhor, algo parecido, já que eu não lembro palavra por palavra), eu li em algum lugar que eu também não lembro, mas que ilustra uma verdade, pelo menos por aqui.

E então voltamos a situação do nosso gerente de "juros baixos". Representantes, vendedores, lobistas mesmo, fazendo um grande alarde, omitindo "aspectos", e tentando fazer a cabeça de diretores e usuários (isso quando não envolvemos alguns "mimos" ou presentinhos, o que já é caso pra polícia). Se você já não viu essa cena, prepare-se, porque uma hora ou outra, vai. "Do you hear it, Mr. Anderson? It´s the sound of inevitability".

Nestas horas, mesmo o CIO com menos poder, mesmo sem ter CIO no crachá, deveria assumir uma postura. Uma postura que é não influenciar a decisão, mas apresentar TODOS os fatos, como eles são. Se mesmo cientes, os comandantes decidirem pelos "jurinhos mais baixos", aí temos outra discussão, por vezes técnica, mas muitas vezes ética.

Este é um assunto que rende "muito pano pra manga", mas me atenho por aqui.

(P.S.: Um último comentário: aqui não temos um chefe de TI, e sim uma chefa, o que é um fato muito legal. Ter uma mulher comandando TI, que ainda é uma área majoritariamente masculina, é uma alegria para mim, que ando cansado de tanto enxergar testosterona em TI =D)