quinta-feira, 15 de dezembro de 2011

Tomcat segundo round! 32 x 64, quem ganha!


Nas lutas que temos que enfrentar nesse mundo, acho que a pergunta não seria qual ganha 32 ou 64bits, mas sim, por quanto tempo você fica em pé após brigar com esses dois sujeitos. No meu caso, máquina rodando com 64bits e intalação do Tomcat + Java rodando com 32 bits, e adivinha! troca de golpes, alguns cruzados, um em cheio de direita e tive que recorrer ao treinador, toalha branca e enfim, lona!!   Fiquei na lona por mais de 8 segundos! e se não fosse com a ajuda dos universitários, meu filho, estaria numa enrascada até agora.

Uma das coisas interessantes nessa disputa é que o Genexus vem se esforçando para tornar a sopa menos salgada, automatizando ao máximo o processo para que não tenhamos que perder tanto tempo assim.  Mas quando falamos que temos que mesclar uma série de tecnologias distintas, a coisa, infelizmente, não fica apenas com o Genexus.

Tomcat e Java 32 Bits
Acho que o erro começou ai, mas fazer o que, tinha o Tomcat e Java 64 Bits e não conseguia fazer funcionar de jeito nenhum, então, em uma tentativa desesperada desinstalei os dois marmanjos e optei por versões não tão atuais de 32 bits, que segundo as boas linguas, eram mais estáveis. Estou falando do Tomcat 6.0.18 e do Java 1.6. 0_25, ambos de 32 bits.

E na esperança de que tudo se resolveria facilmente com versões mais estáveis parti para uma nova kb, as infelizmente nada funcionou direito.

Recebia uma sonora e insistente mensagem HTTP Status 500


type Exception report
message
description The server encountered an internal error () that prevented it from fulfilling this request.
exception

javax.servlet.ServletException: java.lang.NullPointerException at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.
java:169) at com.genexus.webpanels.GXWebObjectStub.callExecute(GXWebObjectStub.java:47) at com.genexus.webpanels.GXWebObjectStub.doGet(GXWebObjectStub.java:27) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.servlets.InvokerServlet.serveRequest(InvokerServlet.java:402) at org.apache.catalina.servlets.InvokerServlet.doGet(InvokerServlet.java:134) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286) at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:857) at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:565) at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1509) at java.lang.Thread.run(Thread.java:662) com.genexus.webpanels.GXWebObjectStub.callExecute(GXWebObjectStub.java:99) com.genexus.webpanels.GXWebObjectStub.doGet(GXWebObjectStub.java:27) javax.servlet.http.HttpServlet.service(HttpServlet.java:617) javax.servlet.http.HttpServlet.service(HttpServlet.java:717) org.apache.catalina.servlets.InvokerServlet.serveRequest(InvokerServlet.java:402) org.apache.catalina.servlets.InvokerServlet.doGet(InvokerServlet.java:134) javax.servlet.http.HttpServlet.service(HttpServlet.java:617) javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

note The full stack trace of the root cause is available in the Apache Tomcat/6.0.18 logs.



Apache Tomcat/6.0.18



Dicas para um final Feliz

Como não quero que voce apanhe, vou te dar algumas dicas.

1) Use o Genexus para realizar o deploy, não faça nada manualmente.

Aplique o valor Default nas propriedades Servlet e Static content directory na KB, e observe se o valor apresentado corresponde ao diretório de instalação do Tomcat.

No meu caso o valor default apontava para C:\Program Files\Apache Software Foundation\Tomcat 6.0\webapps\ProjetoTreinamento2JavaMysql\WEB-INF\classes, enquanto que a instalação do Tomcat estava em C:\Program Files (x86)\Apache Software Foundation\Tomcat 6.0\webapps\ProjetoTreinamento2JavaMysql\WEB-INF\classes.  Consegue ver alguma diferença?

Pois é o Default do Genexus não era o mesmo diretório de instalação do Tomcat, por que? o registro do Windows pode não está correto. Solução: corrija no REGEDIT.  Procurando pelas ocorrências de Tomcat e modificando os valores quando necessário.


No meu caso, não apenas o local mas a versão do Tomcat também não era equivalente à instalada.

2) Veja a instalação do Webapps do Tomcat apresenta todos os diretórios (gxmetadata, Metadata, classes DLL na raiz), enfim algo semelhante a:
Caso não estejam na instalação, então, o Gx não está conseguindo realizar o deploy corretamente, provavelmente devido ao problema detectado no item (1).

3) Siga o mecanismo tradicional de instalação de aplicações, caso tenha que recorrer a esse procedimento manualmente.

a) No diretório META-INF crie um arquivo chamado context.xml com o seguinte conteúdo

b) No diretório WEB-INF crie um arquivo chamado web.xml com o seguinte conteúdo.


E não fique nervoso se o Genexus alterar o conteúdo desse arquivo após o deploy, pois é exatamente o que ele vai fazer.

c) No diretório (tomcat)->Catalina->Localhost crie um arquivo com o mesmo nome da aplicação e extensão XML, com o seguinte conteudo:


d) No diretório (tomcat)->Work->Catalina->Localhost crie uma pasta com o mesmo nome da aplicação.

e) Não coloque a mão no Conf->web.xml, mudando o invoker e as outras dicas de posts anteriores.




A última dica é tenha o melhor treinador te ajudando e ai eu incluo o Alex Melo e o Pablo Mazzilli, da Artech, pois quem iria imaginar que na verdade o problema era no Tomcat e não no Genexus!!!!   Enfim, após essa perfeita esfolada acredito que a culpa nem era da compatibilidade com 64 bits e coisa e tal,  faltava apenas estudar o adversário, no REGEDIT!

terça-feira, 29 de novembro de 2011

Consulta CEP

Ainda bem que o mundo vem evoluindo não apenas na questão tecnológica, mas também humana, social e ambiental, e hoje a palavra chave é respeito ao próximo em primeiro lugar e também ao meio ambiente. Claro que poderíamos assistir essa onda vinda de forma mais rápida, ou na mesma velocidade em que ocorrem as degradações, mas fazer o que? Ainda estamos em construção e temos muito o que aprender. Se você não está surfando nessa onda 'verde', melhor repensar na maneira que vem descartando as coisas, pois antigamente a frase mais falada era: o que deixaríamos para nossos herdeiros se continuássemos destruindo o mundo, agora ouvimos que será da nossa própria geração.

E o que isso tem a ver com Consulta CEP? Podemos dizer que 'reciclar' informação também é uma coisa boa e quase imprescindível nos nossos sistemas, é quase ecológico.  CEP ou Código de Endereçamento Postal é um numero que representa endereços quase completos, tecnicamente só falta o número da residência, pois o restante (rua, bairro, cidade), já possuímos. A questão é como programamos um mecanismo que nos retorne os dados de um endereço a partir deste código. A resposta: WebService.




Onde Encontrar?
A fonte original é o serviço de Correios e Telégrafos que disponibiliza a base de dados de CEP do Brasil, mas esse material é pago. Porém, a mesma base é disponibilizada no ‘mercado negro’, mas cá entre nós, vamos levar a sério essa onda verde, pirataria não. Restam então os serviços benevolentes, que claro possuem limites, mas para nosso teste com caráter didático funciona.

Um desses serviços encontra-se no site www.buscarcep.com.br, que permite certo numero de consultas por minuto.  É necessário registrar-se no site para obter uma chave de acesso ao sistema, e desta forma usufruir dos benefícios.

Como Programar
Já existem exemplos sobre esse serviço, e o que vamos fazer é programar uma SDT simplificar a interpretação do XML de retorno. Desta forma, tiramos um pouco a poeira de nosso cérebro programando tipos estruturados, que é bem legal, diga-se de passagem, e também ao invés de um External Object estaremos consumindo o WebService com HttpClient, de forma a explorar um pouco mais esse recurso.

Esse SDT, apresentado na Figura 1, irá receber as informações do WebService, e seus tipos e nomes devem ser respeitados no momento da construção.
 

Para a implementação utilizamos o exemplo do HttpClient (http://www.genexando.com/2011/11/geolocalizacao-pelo-ip-com-httpclient.html), que possibilita apontar e executar uma ação remota na web, obtendo a resposta que desejamos, e para obtermos essa resposta o que você precisa é passar os parâmetros adequados, algo semelhante ao que aparece abaixo.


cep=13211111&formato=xml&chave=1VUSS3sdDo0Wu0NAo/0dPO5nDkHuTX


Portanto, uma variável &HttpClient, de mesmo tipo, pode receber a seguinte programação.



&HttpClient.Host =  'www.buscarcep.com.br'

&HttpClient.Port = 80

     

&urlString = '?cep='

&urlString += &CEP

&urlString += '&formato=xml&chave=1VUSS3sdDo0Wu0NAo/0dPO5nDkHuTX.'



Não se esqueça de criar sua própria chave e substituir o valor 1VUSS3sdDo0Wu0NAo/0dPO5nDkHuTX. O parâmetro &CEP representa o valor do CEP que desejamos consultar.

Para realizar a consulta executamos o método Execute que acionará o WebService para obter os dados.

&HttpClient.Execute('GET', &urlString)



Finalmente utilizamos uma variável &sdt do mesmo tipo do SDT declarado anteriormente (figura 1), para obter de forma organizada as informações.

&sdt.FromXml(&HttpClient.ToString())     


Simples, o resto é leitura do valor da variável &sdt.

&Endereco   = &sdt.retorno.logradouro

&Bairro     = &sdt.retorno.bairro

&CidadeId   = &sdt.retorno.ibge_municipio

Observe que no &sdt existem muitas informações interessantes a serem exploradas, no mesmo processo apresentado anteriormente.

Conclusão

Em termos de programação acho que não poderia ser mais simples e interessante, e reforça um pouco mais os conceitos de HttpClient, WebService, SDT's e por ai vai.

Agora quanto à onda verde, infelizmente ainda temos situações que apontam ao contrário, para citar um exemplo vamos ao 'Congresso Nacional' onde nessa semana nossos lideres discutem e comemoram a 'articulação feliz' de um novo Código Florestal que entre outros absurdos, anistia as multas, até certa data, 'daqueles cidadãos ilustres' que degradaram indiscriminadamente, irresponsavelmente, mais do que devia. O pior não é isso, ainda temos que ouvir as declarações do relator do projeto que 'articulamos o melhor para o Brasil'.


Não me tome por ecológico radical, mas creio que de fato, essa onda ainda deve virar um tsunami para que se dê a atenção e importância necessária, ou melhor, que de fato o interesse coletivo de toda uma população venha a ser de fato articulado.

sexta-feira, 11 de novembro de 2011

Expression

O próprio nome já diz que é coisa boa, parece até nome de carro chique. Alias, me desculpem os proprietários só estou falando do nome, mas tem carro com nome bem estranho por ai, movimento iniciado pela Besta, veja bem, Tiida é nome pra carro? Cerato? Enfim, como diria, gosto é gosto e não devemos discutir.

Voltemos a Expression, que, aliás, é o que nos interessa, considerando ser um dos recursos que nos remetem a flexibilização e parametrização de sistemas, assunto importante e que merece destaque em qualquer projeto. Quem não tem um daqueles usuários bem chatos que vivem pedindo para se alterar a formulas de calcular de certo imposto, não é mesmo?

Tipo Expression

Esse tipo Genexus é algo que merece respeito, visto que permite avaliar expressões aritméticas e lógicas registradas em formato texto. Entre outras palavras, se temos uma expressão: a + b / 2, então podemos fazer com que o valor obtido pela sua execução seja calculado por Expression , claro, desde que se forneçam os valores para a e b.

Algo do tipo, se a=1, b=10, então o valor resultante de a + b / 2 será 6. Não entendeu, o valor é diferente daquele que você chegaria? Isso acontece porque Expression também trata a hierarquia de operadores, primeiro realiza-se a divisão e depois a soma.

Como Funciona?

Simples, cria-se uma variável do tipo Expression e em seguida, realizam-se três operações fundamentais:

  1. Define-se a expressão a ser calculada
  2. Substitui-se os valores das variáveis na expressão
  3. Executa-se o calculo da mesma
Como exemplo, recorramos ao mesmo a + b / 2.

Event Start
   &expressao = 'a + b / 2'
EndEvent

Para permitir registrar expressões livres programamos uma variável &expressao que será manipulável na interface.


Portanto, criamos duas variáveis &a e &b para terem seus valores alteraveis. Para a execução da expressão, um botão ENTER.

Event Enter
   &expression.Expression = &expressao

   &expression.Variables.Set('a', &a)
   &expression.Variables.Set('b', &b)

   &resultado = &expression.Evaluate().ToString()
EndEvent

Simples, hein, define-se a expressão, substitui-se os valores com o método Variables, e finalmente Evaluate() para gerar o resultado.

Recursos
E quanto aos recursos, bem vem com Air-bag, freios ABS, ar-condicionado Digital, ...,  ou melhor, quanto aos operadores, é possível não apenas os operadores tradicionais ( + - * / ), mas também operadores lógicos e relacionais ( < <= > >= <> and or), mesmo porque é possível definir uma operação tipo if, ou melhor iif.

   iif( condicao , valor, valor)

Tudo isso, além das funções predefinidas, tais como tan() cos() pow() sqrt(), entre outras, que eu recomendo consultar o manual do proprietário para maiores informações.

Ah, e antes que eu me esqueça, também é possível o uso de parenteses na expressão, para resolver situações de hierarquia.

Vamos a um exemplo com iif.

Event Start
   &expressao = 'iif(a + b / 2 > 0, a + b / 2 , 0)'
EndEvent

Muito bem, se quiser fazer um test-drive recomendo abrir o Gx e começar, pois verá que o desempenho é ótimo, os recursos ilimitados, e claro, a solução final digna dos Rolls Royce, ou do Galaxy para os brasileiros mais antigos.

Ah, o endereço da concenssionária é http://wiki.gxtechnical.com/commwiki/servlet/hwiki?Expression+Data+Type,






Input parameter cannot be assigned

Uma das caracteristicas mais importantes em um programador é a sua capacidade de organização lógica, apesar de que raciocionio rápido, eficiência, velocidade também são fundamentais.  Por natureza todo programador é organizado, gosta de manter seu código limpo, comentado e claro, de forma que outros da equipe possam entender rapidamente o que foi feito, e por alguma necessidade, interferir realizando uma manutenção.  Estou enganado?

Pois é aqui buscamos duas coisas, uma é incentivar os desenvolvedores a documentarem e organizarem seu código, e a segunda é resolver a questão do erro Input parameter cannot be assigned.

'Melhores Práticas'

É uma pretensão gigante de minha parte criar aqui uma lista de melhores práticas de desenvolvimento, mesmo porque cada pessoa tem um perfil, cada empresa um padrão, ou seja, vamos direto ao ponto, que é como documentar os parametros utilizados para transferir informações de um lugar para outro.

Aprendi que existem dois tipos de pessoas no mundo, aquelas que fazem de tudo para esconder o que sabem e o que fizeram, e aquelas que sente prazer em falar o que sabe pra todo mundo.  Infelizmente o segundo tipo acaba se dando mal em algumas situações, experiência própria.  O primeiro tipo pensa o seguinte, vou programar de forma que somente eu possa entender o programa, assim, fico garantido porque ninguem mexe comigo, rs...., pode ser também uma questão de extrema desorganização, falta de tempo, ou seja, uma série de desculpas para não deixar a "coisa clara". Vamos ao exemplo, citando um amigo ficticio que chamaremos de Mario, cujo perfil é, mais ou menos, do primeiro tipo de pessoa.

Certo dia, o Mario chegou pra trabalhar em sua empresa e tinha que programar uma rotina complexa em uma procedure, algo relacionado a faturamento, impostos, ..., e como tinha que passar um montão de parametros, escreveu algo semelhante a:

parm(&invc, &xkpt, &sbtrr, &isn, &kkrg, &aaiins, &inss, &ir, &sbtstr, &opt1, &opt2, &opt3);

Nada muito complexo, sob a ótica do Mario, mesmo porque a procedure tratava todos esses parâmetros, com um extenso código de1200 linhas, mas que ele tinha tudo na cabeça, e claro, conhecia linha por linha desse procedimento e sabia exatamente o que tinha que passar de informação e o que tinha de receber da procedure. Já viu algo assim? pois é um dia eu vi e reforçou o que eu já fazia antes, que era programar de forma clara, mesmo porque levei dois dias para interpretar o que o Mario queria dizer com cada uma das variáveis malucas criadas.

Nem vou dizer a respeito de comentários de código, sub-rotinas documentadas e coisas do gênero, vamos apenas buscar melhorar a lista de parametros. Desta forma, acredito, na minha humilde e singela opinião, que uma prática melhor seria programar algo assim:

parm(

   &invc,    // indice de variacao

   &xkpt,    // pre-calculo final

   &sbtrr,   // valor da substituicao trib.

   &isn,     // valor do imposto recebido

   &kkrg,    // valor interno de calculo

   &aaiis,   // previa do iss 

   &iss,     // imposto iss calculado

   &ir,      // imposto ir calculado

   &sbtstr// valor final da subst. trib

   &opt1,    // opcao de calculo com ir

   &opt2,    // opcao de calculo com iss

   &opt3     // opcao de calculo com subst.

   );


'Input parameter cannot be assigned'

Vamos ao erro que motivou o post, Input parameter cannot be assigned, que quer dizer o seguinte, todo parametro recebido no parm não pode ser atribuido um valor diferente.  É causado toda vez que um parâmetro é envolvido em uma operação que altera seu valor, como no exemplo a seguir.

parm(&a, &b, &c);



&a = &a+1

&b = &b*&a

&c = &s+&b

Desta forma, se não se informa o contrário, todo parâmetro é de entrada, e não aceita alterações em seus valores.  E nessa lógica, se existe o conceito de parametro de entrada, temos também o conceito de parametros de saída e de entrada e saída.  E é exatamente para isso que servem os flags in:, out:, inout: providos pelo Gx.

O mais interessante é que parametros do tipo inout: tanto podem receber informações quanto devolver valores calculados, ou seja, a declaração do parametro desse tipo é suficiente para remover a mensagem Input parameter cannot be assigned.


Ja pensou então se definissemos no parm do Mario quais os parametros que seriam fornecidos a procedure, e quais a procedure nos devolveriam? Teriamos um código melhor mesmo, porque não precisariamos ligar toda hora pro Mario para perguntar o que é tal coisa.


parm(

      in: &invc,   // indice de variacao

     out: &xkpt,   // pre-calculo final

     out: &sbtrr// valor da substituicao trib.

      in: &isn,    // valor do imposto recebido

      in: &kkrg,   // valor interno de calculo

   inout: &aaiis// previa do iss 

     out: &iss,    // imposto iss calculado

     out: &ir,     // imposto ir calculado

     out: &sbtstr, // valor final da subst. trib

      in: &opt1,   // opcao de calculo com ir

      in: &opt2,   // opcao de calculo com iss

      in: &opt3    // opcao de calculo com subst.

   );

Enfim, parametros de saída são aqueles que em uma chamada com Call podem receber valores.


Tenho usado, na maioria das vezes, esse tipo de programação, e salvo a própria complexidade das rotinas programadas, acredito que a clareza e a documentação do código sejam práticas fundamentais para o sucesso nos projetos.

Agora muito cuidado com a história do Mario e do armário hein...



sábado, 5 de novembro de 2011

Geolocalização pelo IP com HttpClient

Existe um recurso muito interesante, com certa idade, meio timido pois fica alheio aos principais flashes na ferramenta, mas que até os dias atuais mostra-se de grande utilidade, é o HttpClient.  Porque? simplesmente pelo fato de que podemos usar o protocolo HTTP em modo de programação, ou seja, que se programe uma requisição, se envie para uma URL e se obtenha os resultados dessa chamada. Isso quer dizer, que se execute uma chamada HTTP em forma de programação, sem que a mesma seja apresentada no browser.

Aplicações para isso? Muitas, inclusive encontrar a geolocalização de um certo IP na web. Esse é o exemplo que será apresentado a seguir, e tem como objetivo obter informações de um determinado serviço na web.


Obtendo a geolocalização

Existem uma série de serviços na Web que realizam essa operação, selecionamos uma que devolve um XML, JSON ou CSV, a partir de um determinado IP fornecido, e que pode ser encontrado em (http://freegeoip.appspot.com/), que necessita apenas de dois 'parametros' para funcionar: o tipo de retorno e o IP.  O formato para a chamada pode ser algo semelhante ao seguinte exemplo: http://freegeoip.appspot.com/xml/72.14.247.141

O texto XML representa o formato do retorno do serviço, que nesse caso devolve algo como:



Outros formatos são disponiveis, em especial o JSON e CSV.

Programando


Não é necessária muita programação para obter essas informações, a não ser uma interface simples para obter um IP qualquer, e em seguida, acessar o WebService na web, algo semelhante a.


Para obter as informações, teriamos então a programação do HttpClient, apontando para o serviço na web, ou seja, o Host, Port e forma de Secure.

     &httpClient.Host = "freegeoip.appspot.com"
     &httpClient.Port = 80
     &httpClient.Secure = False

Os parâmetros são passados por meio de uma variável texto, incluindo-se o formato desejado e o IP que se deseja investigar. Em nosso caso optamos por um texto separado por vírgulas.

     &string  = "/csv/"
     &string += &ip

Em seguida a chamada ao WebService, por meio do método Execute.

     &httpClient.Execute("GET",&string )

Se não ocorrer nenhum erro nessa chamada, teremos um texto resultante da chamada, que pode ser obtida por.
          &texto = &httpClient.ToString()

O programa completo é apresentado a seguir, inclusive com o tratamento de erros.



Uma tabela foi programada para se mostrar o resultado, como aprsentado na figura 1, acima.

Voce pode encontrar maiores informações na documentação da Artech, agora, se não conseguiu visualizar uma boa aplicação para esse recurso, vou dar mais uma dica, consulte as páginas amarelas dos webservices em http://www.webservicelist.com/




sexta-feira, 28 de outubro de 2011

Mudança de URL do Genexando

Pessoal,
Estou mudando a URL do blog de http://profdouglasoliveira.blogspot.com/ para http://www.genexando.com, por gentileza atualizem seus links.
A atualização levará alguns dias para ser processada.
Ab
Douglas

segunda-feira, 24 de outubro de 2011

Afinal, o que significa Context Sensitive Interface?

Se você já se perguntou afinal o que são as interfaces sensitivas a contexto, ou já teve curiosidade de entender um pouco a respeito disso, vou te passar algumas dicas, já se você nunca ouviu falar, ou não gosta muito de interfaces web, então esse post pode te dar algumas novas visões de como planejar e construir uma interface gráfica web a partir de componentes, e quem sabe te anima a entrar nesse mundo tão interessante. Portanto, acho que esse assunto pode ser do interesse de muitos, foi pra mim, e acredito que poderá te ajudar um pouco, pois não tenho dúvidas que esse é um mecanismo que merece atenção no projeto de boas interfaces web.

Imagine uma interface montada com componentes apenas, onde não se passa nenhum parametro entre elas,assim como essa imagem apresentada a seguir.


Agora considere que as três possuem Grids e na medida em que se clica em alguma linha de um dos grids as demais respondem de acordo com o registro selecionado.  Ou seja, ao se selecionar um cliente qualquer na interface Clientes, a interface Compras e Produtos respondem filtrando informações relativas a esse cliente.


Não perca de vista a questão de que não passamos parametros, via Parm(), para os componentes, então como fazemos isso? como realizar a comunicação entre componentes isolados e agrupados em um painel?  A resposta: Interfaces Sensitivas a Contexto.

Em Genexus você já deve ter observado que quanto menos se programar melhor, veja os domínios que com um pouco de planejamento podem auxiliar na manutenção do sistema todo, o próprio for...each, por exemplo, pode ser otimizado com o objeto Data Selector, enfim, temos na montagem da interface elementos que poderiam ser reaproveitados em outras interfaces similares. 

Poderíamos dizer que um grid que seleciona um registro de uma tabela poderia muito bem ser aproveitado em diversas outras interfaces, desde que se tenha um pouco de planejamento anterior.  Assim poderíamos ter um modelo de projeto onde teríamos diversos componentes previamente planejados e nosso trabalho seria o de conectá-los ao nosso interesse. Soa bem isso não?  Isso é possível nas interfaces sensitivas a contexto.

O Que é Contexto?
Entenda contexto como sendo o item atualmente em foco na interface, alcançado por meio da tecla Tab ou do click do mouse.  Desta forma se ocorreu um click em um Grid, o contexto terá a linha corrente do grid, se em uma coluna do grid o contexto armazenará o valor da coluna selecionada, se em uma variável o valor da mesma e assim por diante.

Um evento chamado Track Context é utilizado para ler o contexto dos diversos controles na interface, e o interessante é que percebe a mudança assim que ocorre o foco no controle.  Em outras palavras, em nossa interface modelo, ao clicar em algum cliente no Grid apresentado à esquerda, o código do cliente ‘selecionado’ poderia então ser utilizado nos demais grids para apresentar suas compras e os produtos adquiridos pelo cliente.

Para facilitar um pouco, pense em Context como sendo um controle na interface (variável ou atributo), e que ao entrar em foco, imediatamente conseguimos ler seu valor.


Programando o Exemplo
Um web panel é montado apenas para integrar os três webcomponents, ou seja, não é necessário programar nada, a não ser o posicionamento dos componentes na interface, um exemplo poderia ser o apresentado na figura 1 acima.

O web componente Clientes, possui um grid simples do tipo Freestyle, com variáveis e atributos, e a propriedade NotifyContextChange é definida como True.


Os eventos programados nessa interface são meramente de carga do grid, nenhuma informação adicional.
 
Event Load
     &ClienteId   = ClienteId
     &ClienteFoto = ClienteFoto
     load
EndEvent

A segunda e terceira interface também são Web Componentes, programados com grids simples, como podem ser observados na Figura 4 e 5 abaixo.




O grid de Compras agrega um total de compras apenas, calculado na carga do grid.

Ambos os grids possuem um filtro ClienteId = &ClienteId. Observe que &ClienteId não existe em ambos os webcomponents, sendo o valor obtido do primeiro WebComponent (Figura 3), quando o usuário clicar em algum registro do Grid.  A pergunta é como &ClienteId é carregado? 

Um evento TrackContext foi adicionado para realizar a leitura do valor do contexto atual de &ClienteId, que na prática foi definido no outro no componente Cliente (Figura 3).  Não foi necessário programar nada no evento a não ser receber por parâmetro o valor de &ClienteId, do outro componente, por isso que o evento se encontra definido de forma tão simples. 

Event TrackContext(&ClienteId)
    
EndEvent

O evento TrackContext é o responsável por interceptar o Click e obter o cliente selecionado.

Em resumo, para que o grid notifique mudanças no contexto é necessário programar o NotifyContextChange=true, e para interceptar os clicks é necessário programar o Event TrackContext.

Entre parenteses se coloca as variáveis (apenas) que se deseja obter, e podem haver mais que um TrackContext na mesma interface.

Conclusão
Esse tema é muito importante e esse post está ficando um tanto quanto grande, acho que futuramente poderemos voltar a esse assunto, quem sabe tratar um pouco mais a respeito de TrackContext multiplos, e o SetContext que também é um evento bem interessante.

Não sei se entendeu o impacto disso, mas poderiamos pensar que todo WebComponent já programado poderia servir de base para montagem de outras interfaces completamente distintas, bastando programar o mecanismo de comunicação entre componentes, muito interessante não?

ps. Coloquei um vìdeo no YouTube mostrando o funcionamento da interface, caso tenha curiosidade. Interfaces Baseadas em Contexto




sexta-feira, 21 de outubro de 2011

Shortcuts

Sabe quais são as teclas utilizadas no IDE do Genexus para executar os comandos da ferramenta? Pois é, eu não sabia que existiam tantos. Vale a pena conferir.

http://wiki.gxtechnical.com/commwiki/servlet/hwiki?GeneXus+IDE%2FShortcuts

Order, when e otimizações

Esse tema, meio polêmico deixa muita gente nervosa, de cabelo em pé, e reflete um pouco a respeito das pesquisas e acessos realizados na base de dados, que sempre são uma preocupação grande devido ao desempenho desejado na aplicação, pois, Genexus oferece a programação de cláusulas condicionais, when, tanto em order quanto em where, oferecendo um mecanismo bastante interessante e flexível para acesso ao banco, mas o uso indiscriminado pode trazer complicações.

A idéia é que além do efeito visual obtido em uma navegação, no preenchimento de grids, por exemplo, tenhamos também um bom desempenho, ou seja, sempre um pequeno número de registros ordenados e processados corretamente.  Desta forma sem ler o resultado da navegação gerada, você estará correndo grandes riscos em sua aplicação, visto que o usuário não gostará muito de esperar minutos para que uma resposta seja apresentada na interface.

Um exemplo de programação que utiliza when pode ser vista a seguir.

     for each
          order ManualTitle when not &ManualTitle.IsEmpty()
          order ProcessId when &ProcessId<>0
          where ProcessId = & ProcessId when & ProcessId<>0
          where ManualTitle like '%'+&ManualTitle+'%' when not &ManualTitle.IsEmpty()

Ela diz que se houver titulo fornecido ordena-se e filtra-se por titulo, se houver processo, o mesmo, ordena-se e filtra-se por processo.  Mas o fato é que o resultado final não é bom, pois no mapa de navegação teremos a pesquisa à tabela toda (Navigation Filters), ou seja, lentidão.


Um pequeno detalhe, pois quando optar por programar com a cláusula when, todas as ordenações devem ser colocadas o when, caso contrário teremos um erro de sintaxe.

     for each
          order UserManualTitle
          order ProcessId when &ProcessId<>0

error: Expecting 'EndFor' command to close the 'For Each' block (Events, Line: 50, Char: 9#62ea5034-c4dd-4bb2-ad33-56cd112159d7)
error: '{0}' is an unexpected expression. (Events, Line: 50, Char: 25#1b09b22d-3400-46a8-8934-b7f4000a0192)
error: Unexpected command 'where' (Events, Line: 51, Char: 3#ea102e2d-fc92-4228-94bb-3cb856efd2b8)
error: '{0}' is an unexpected expression. (Events, Line: 51, Char: 9#7d0a990c-475e-4292-875e-65313180b500)
error: Unexpected command 'where' (Events, Line: 52, Char: 3#45f8ec05-0b6e-4b58-a4ac-8ed937fec794)


O operador like também contribui para uma situação de pesquisa não otimizada na base.
 
Uma saída (existem outras) para essa situação é criar um índice composto por várias ordenações, usando vírgulas, e eliminando-se a cláusula when quando for possível.

     for each
          order ProcessId, UserManualTitle
          where ProcessId = &ProcessId
          where UserManualTitle like '%'+&UserManualTitle+'%'

O resultado é uma pesquisa melhor, otimizada e mais rápida, apesar do efeito visual final não ser muito bom, visto que a consulta dependeria de dados para ser processada, desta forma o usuário teria uma interface vazia onde teria que fornecer informações para ver alguma coisa.



Considere otimizada o fato de Start from e Loop while apresentar valores condicionais ao invés de FirstRecord e NotEndOfTable.  Pode parecer meio estranha a principio, mas o resultado é muito melhor, principalmente na web onde tudo é mais lento, e o fato do usuário determinar o que quer ver, já significa que não perderá tempo com informações inúteis.

Enfim, cuidado com a cláusula when, principalmente em sistemas Web, caso contrário seu chefe vai colocar o exército chines atrás de voce.