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.