Criando um contador de tempo de sessão em aplicativo web

Criando um contador de tempo de sessão em aplicativo web

Olá,

Realmente estou um pouco desapontado comigo mesmo, pois desde o último post se passou 3 anos… “Shame on you”… Mea culpa, minha vida mudou muito desde o último post, mudei de cidade, de emprego, enfim, nada justifica…

Voltando ao que interessa, gostaria de compartilhar com vocês uma solução bacana que utilizamos em um projeto que é o contador de tempo de sessão, bem parecido com aqueles cronômetros que existem em nossos internet bankings.

A solução basicamente é um mash up (uma mistura) de um User Controls e um objeto externo, ambos disponíveis no marketplace do GeneXus: Web.Config reader e SessionTime. A idéia é utilizar o user control Session Time, porém, ao invés de configurá-lo com um tempo fixo, obter o valor do tempo de sessão do web.config.

  1. A primeira coisa que precisa ser feita é o download e a instalação do U.C. e do objeto externo, caso necessite de ajuda nesse ponto, poderá encontrar a documentação aqui.
  2. O external object ConfigManager não necessita instalação, mas é necessário a importação dos objetos na sua KB, para isso, após efetuar o download, descompacte o arquivo webconfigreader.xpz para alguma pasta;
    • Vá no menu Knowledge Manager e selecione Import; Selecione o caminho onde foi posto o arquivo .xpz e selecione import.
    • Feito isso serão importados 1 External Object, 2 Files e 1 Webpanel de teste;
    • Se desejar, execute-a, para verificar se está tudo OK com o leitor de web.config:
    • Uma chave que pode ser lida sem problemas do web.config é a de nome DocumentType que por padrão está vindo com o valor HTML5.
  3. A seguir, necessitaremos incluir o user control em nossa masterpage, para que o mesmo fique sempre visível. eu recomendo adicioná-lo no cabeçalho da mesma: Sem títuloAo executar a aplicação podemos ver que agora existe um contador de sessão na masterpage:

    Sem título 2

    Também podemos notar 2 coisas:

    1. O tempo de sessão está sendo definido de forma manual, isso é, está sendo configurado diretamente no user control, ao invés de ser obtido do tempo de sessão real que o IIS nos oferece;
    2. O Layout está um pouco grande demais.
  4. Para corrigir o problema da integração com o IIS é que instalamos o external object Webconfig Reader, ele que será encarregado de obter o valor do tempo de sessão configurado na nossa aplicação. Para fazer isso, basta adicionar a seguinte linha no evento start da página onde está posicionado seu user control de tempo de sessão, no nosso caso, está na própria masterpage:
    ControlSessao1.Minutes = ConfigManager.getSessionStateTimeOut()
    
    Onde ControlSessao1 é o nome do controle utilizado na tela.
  5. Para ajustar o layout necessitaremos de um pouco de conhecimento em CSS, mas não é nada muito difícil: Todo user control é “renderizado” em tempo de execução, ou seja, no objeto gerado será feita uma referência ao código que deverá ser inserido no html, essa inserção será feita em tempo de execução. Logo, para alterar alguns detalhes do user control, basta editar o arquivo que está sendo referenciado, nesse caso se chama ControlSessaoRender.js e está na subpasta de nome ControlSessao, dentro da pasta Web de nossa aplicação.Uma maneira rápida é utilizar a configuração da fonte para que seja definida uma classe, ao invés do tipo de letra. Para isso, basta substituir a linha 40 do arquivo, onde diz:
    var buffer = '<a id="Sessao" href="#"><font face="' + this.FontFace + '" color="' +  this.FontColor + '" size="' + this.FontSize + '">'+ relogio + '</font></a>';

    por isso:

    var buffer = '<a id="Sessao" href="#"><font class="' + this.FontFace + '">'+ relogio + '</font></a>';

    O que estamos fazendo aqui é utilizando a propriedade FontFace do user control para que ao invés de configurar o tipo de letra, seja configurado a classe CSS a qual se deseja utilizar para esse texto.

    Também necessitaremos colocar o código a seguir, logo abaixo da linha que inserimos no evento start no item anterior:

    ControlSessao1.FontFace=ThemeClass:Label

    O que está acontecendo aqui é que estou dizendo que a fonte do controle de sessão agora receberá o nome da classe “Label”, notar que essa notação faz com que a classe passe a ter integridade referencial, ou seja, ao tentar remover a classe “Label” do meu objeto tema, ocorrerá um erro dizendo que ela está sendo referenciada. Nesse exemplo também poderia ser utilizado o código:

    ControlSessao1.FontFace="Label"

    Porém perderemos a integridade e caso essa classe fosse removida, nosso user control aparecerá desconfigurado.

    Assim, veremos agora como ficou nossa página:

    Sem título 3

  6. Como etapa opcional, complemento dizendo que ainda é possível melhorar um pouco mais, assim como no item anterior editamos somente a classe do texto, podemos editar o próprio texto, de forma que fique um pouco mais intuitivo, na linha 39 do mesmo arquivo ControlSessaoRender.js, temos o texto que está sendo utilizado:
    var relogio = this.tempo.getMinutes() + " Minutos : " + this.tempo.getSeconds() + " Segundos."

    Podemos, por exemplo, alterá-lo para:

    var relogio = “Tempo restante: “+ this.tempo.getMinutes() + ” min.  e ” + this.tempo.getSeconds() + ” seg.”

    O bacana é que a alteração do arquivo .JS é refletida automaticamente, basta recarregar a página que o texto aparece alterado.

    Finalmente, nossa página ficou assim:
    Sem título 4

 

Espero ter contribuído de alguma forma, qualquer dúvida ou problema que tiverem podem me deixar uma mensagem aqui embaixo, respondo ASAP.

Um abraço e até a próxima.

 

Acessando variáveis no web.config

Ola a todos.

Muito tempo se passou desde meu último post e peço desculpas, mas tudo na vida da gente acontece por alguma razão e desde minha última aparição muita coisa mudou, meu emprego, minha cidade atual, dentre outras, bueno, nada disso justifica eu sei, mas tive de repensar em um monte de coisas de um dia para o outro e acabei me esquecendo desse projeto, por isso as desculpas.

Recentemente estive trabalhando em um projeto realmente pequeno com apenas três tabelas no DBMS e fiquei me perguntando se realmente havia a necessidade de criar uma quarta tabela apenas para guardar parâmetros… Decidi que não, não queria.

Então lembrei que é comum no desenvolvimento tradicional (aka “a mão”) armazenar configurações de acesso rápido no arquivo web.config, então resolvi que iria fazer isso.

Tive alguns problemas quando tentei gravar valores no web.config utilizando a classe ConfigurationManager, depois fui descobrir que por motivos de segurança a gravação não foi implementada, o método para escrever até existe e ao ser executado não retorna erro, mas o web.config simplesmente não é atualizado. Fiquei um pouco confuso, mas depois analisando com calma vi que era uma sábia decisão… hehehehe

A solução foi mais simples que eu pensava, existe uma classe no C# que acessa diretamente a seção appSettings de dentro do web.config, a única coisa que faltava era criar o Wrapper, o External Object e utilizá-los.

O resultado final disso acabei utilizando como piloto para meu primeiro projeto no marketplace do GeneXus.

Resumindo, o que você precisa fazer é:

1 –  Baixar a extensão nessa URL: http://marketplace.genexus.com/product.aspx?web.config.reader
2 – Importar o arquivo “WebConfigReader.xpz” para a sua base de conhecimento;
3 – Certifique-se que o arquivo ConfigManager foi corretamente copiado para o diretório da aplicação <app>\bin; (Os arquivos ConfigManager.cs e ConfigManager.dll estão embutidos no .xpz);
4 – Efetue um build all;
5 – Execute a webpanel WebConfigReader para testar;

Obs.: Esse External Object foi criado utilizando GeneXus X Evolution 2 Upgrade 2, mas acredito que deva funcionar também na Evolution 1.

Sinta-se livre para utilizá-la onde achar melhor, e por favor, se precisar de ajuda não hesite em perguntar.

Se desejar, no .zip está o fonte e um .bat para auxiliar na compilação.

Abraços a todos e até a próxima.

Autenticando usuários de aplicações no Windows e SQL Server

Hola! Que tal? (Como dizem nossos amigos uruguaios)

Estive ausente já faz um bom tempo, e de fato iria continuar por mais algum tempo, mas recentemente (hoje, pra ser mais sincero) implantei um pequeno sistema que se autentica com usuários do domínio (AD) do windows diretamente no SQL Server.

Oras, mas pra que serve isso? Basta deixar um usuário fixo da aplicação no sql server e pronto! Realmente isso funciona 100% adrenalina (como diria meu amigo Nataniel…).

Sim funciona sim, mas suponha os seguintes cenários:

  1. Sua aplicação está consumindo grandes recursos de processamento;
  2. Você sabe que somente sua aplicação está utilizando o DBMS;
  3. Você gostaria de saber qual usuário está realizando a query “from hell”;
  4. Você gostaria de criar auditorias em algumas tabelas, via trigger;

Em algumas dessas situações, se você utiliza autenticação pelo SQL Server (e não pelo domínio, ou Active Directory), saber exatamente qual usuário pode ser um problema, visto que todas as conexões vindas do servidor de aplicação fazem login com o mesmo usuário do SQL Server.

Nesses casos (e também caso aquele dba esteja te solicitando) a aplicação pode se autenticar pelo domínio do windows, sem ter que fazer nenhuma alteração no GeneXus, somente alterando configurações do próprio IIS. Vamos a elas…

Pré-requisito opcional: Recomendo a instalação do IIS Admin Pack, que é um pacote que adiciona algumas funcionalidades bacanas ao IIS Manager, entre elas a possibilidade de se editar graficamante configurações do web.config que antes eram somente na raça (editando o web.config). Esse pacote pode ser baixado da própria MS: http://www.iis.net/download/AdministrationPack

As configurações abaixo foram testadas em aplicativos GX 9 e X evolution 1, com IIS7 rodando em windows 7 e em um servidor windows 2008;

  1. Configurar o pool do aplicativo para fazer autenticação pelo ASPNet: Selecionar o pool de aplicativos vinculado a sua aplicação, e clicar em configurações avançadas:
    Image
  2. Configurar sua aplicação para autenticação integrada: Editar o web.config, ou pelo Genexus, ou pelo editor de configurações do IIS, trocar “Integrated Security” de “no” para “yes”:
    Image
    e depois:
    Image
  3. Desabilitar a autenticação anônima, e habilitar a autenticação do windows e a personificação do ASPNET:
    Image
    e depois:
    Image
  4. e, finalmente, forçar o IIS a utilizar a autenticação do ASPNET, aqui se torna extremamente fácil se você instalou o Administrator Pack: Abrir o Editor de configurações, selecionar system.webServer/security/authentication/windowsAuthentication, e alterar o valor “useAppPoolCredentials” de “False” para “True”:
    Image
    e depois:
    Image

    Aqui na propriedade “providers” é interessante deixar apenas NTLM, basta clicar nos “…” e remover a opção “Negotiate”

Nesse último passo, se você não instalou o admin pack, terá que editar “a mão” o arquivo web.config para adicionar as sessões relativas a essas configurações, que eu não sei exatamente quais são;

E é isso, fazendo esses pequenos passos os dba’s agradecem! 🙂

P.S.: Não esqueçam de autorizar seu usuário do windows no SQL Server, ou então irá receber uma mensagem de erro, dizendo que seu usuário não pode fazer login na base de dados…

Um abraço, e até a próxima!
Sandro M. Vianna

Behind GeneXus ou Beyond GeneXus???

Se você está se perguntando: “Epa, peraí, acessei esse site ontem (ok, ok, semana passada) e ele estava aqui, mas não está mais???

Não é um erro não, mudei o nome e o endereço do site…

Antes era behindgenexus.wordpress.com

Agora é beyondgenexus.wordpress.com

Os Motivos? Bom, tenho alguns que com certeza não justificarão a latência com que os resultados do google irão se atualizar, nem justificarão as prováveis perdas de lugares nos algoritmos do google. Mas os fins justificam os meios… enfim, vamos a eles:

1) Já existia um site com o mesmo nome, porém em outro serviço de blog (behindgenexus.blogspot.com). É de um velho conhecido meu, Gastón Milano, que trabalha na ARTech (fabricante do Gx). Apesar de que, logo que criei o site e notei (buscando pelo google) que já existia o mesmo nome, questionei-o sobre seu possível aborrecimento, ou simples  “Não curti” (Aliás é algo que o facebook deveria implementar…), em Porto Alegrês seria algo como “Não era”.
Bom, o que importa é que esse fato estava ME aborrecendo um pouco, primeiro porque não gosto de tomar créditos pelo trabalho de ninguém, por NADA de ninguém, se faço algo é para ser bem feito e que siga meus princípios… ou seja, talvez eu não tivesse gostado se EU tivesse um blog e outra pessoa criasse um blog com o MESMO nome e que trate do MESMO assunto…

2) O nome “behind” inconscientemente me leva ao tema “por trás”, “coisa ruim”, “traição”, hehehe, to zoando, mas me remete ao tema “Bom, irei abordar tudo que o GeneXus não aborda.” onde na verdade o que eu queria dizer era “Bom, irei abordar tudo o que o GeneXus aborda e MAIS um pouco”, coisas que vão além do GeneXus, coisas que o genexus faz OU que não faz.

Basicamente são esses 2 motivos, mas aí você se pergunta: “Ta seu idiota, mas porque tu não manteve os dois rodando e foi migrando aos poucos???”

Respondo-lhes: “Simplesmente porque… foi cagada mesmo… hehehe, tentei e tentei até que fiz um daqueles Next Next Finish de um assistente de exclusão do WordPress… até que ‘pluft…’ Odeu-se, como diria meu amigo Didi…”

Ai vai aposto que vai vir outro perguntando: “Ta, mas tú ficou 6 meses sem escrever nada e veio me fazer essa besteira? Quanto tu vai atualizar esse blog?”

Respondo-lhes: “Em breve… muito breve… Já está saindo do forno um tema com bastante desavenças: Stored Procedures em Oracle!” “Ok, não foi tão breve assim, nem foi esse o assunto…”

Recompilando (toda) a aplicação sem fazer Build All

Boa tarde!

Desculpem-me a demora mas antes tarde do que mais tarde, né?

Hoje quero falar um pouco sobre recompilação da aplicação, vamos lá… O Problema: Recentemente compilei uma aplicação *inteira* em .Net, só que utilizando o framework de 64 bits, e para piorar, utilizando a versão 4.0.

Como era uma versão de produção e alguns clientes podem não ter servidores 64 bits, resolvi então recompilar *toda* a aplicação novamente. Porém, se você for simplesmente no menu de execução do genexus (da versão 9.0 e anteriores, claro), selecionar o objeto e clicar “Compile” ou “Vai”, simplesmente não vai acontecer nada, pois o objeto *já* está compilado, certo?

E agora? Vou precisar fazer um Build All de mais de 5000 objetos (dos quais mais de 1000 são “main”) simplesmente para poder compilar em outra versão do Framework?

A resposta é não, você não precisa: O Genexus controla a necessidade de compilação através do timestamp do arquivo fonte, ou seja, se o timestamp do fonte for menor que o timestamp do arquivo compilado, não há necessidade de compilação. Ou seja, basta ter uma maneira de mudar o timestamp do(s) arquivo(s) fonte(s) necessário(s) para conseguirmos “marcar” determinados objetos como necessários (ou required na janela de compilação).

Para alcançar essa façanha, então, é necessário um utilitário chamado touch.exe ele está em qualquer diretório de modelo de aplicações geradas com java.

Copiar o utilitário para o diretório do modelo que você deseja “tocar”, aí então fazer o seguinte:

  1. Abra um prompt do DOS (Iniciar, executar, cmd.exe, ok);
  2. Posicione-se no drive e pasta do modelo onde foi estão os fontes a serem “tocados” (e onde deveria ter copiado o executável touch.exe). No meu caso foi:
    1. D:
    2. Cd \Modelos\KB20110727\DataNETSQLProducao
    3. touch hhome.cs
  3. Feito isso a webpanel home deverá aparecer novamente como “required” na lista de compilação, basta selecioná-la e clicar em “Agora Vai”, digo, “Compile”

Simples não?

Aposto que você deve estar se perguntando: “Tá, beleza, vou ter que fazer isso para cada um dos 1500 “mains” que tenho para serem recompilados?”

A resposta é novamente: “Não”: São poucas (acho) as pessoa que acompanharam a evolução do DOS, quem realmente acompanhou sabe que o Windows 95, 98 e o Millennium foram “construídos” em cima do DOS, ou seja, eles eram ambientes operacionais e não sistemas operacionais de fato (diferente da série NT: NT 3.5, NT 4.0, Windows 2000, Windows XP…). Bom estou lembrando isso pois o DOS continua por trás de *todas* essas versões, e um dos comandos DOS que pouca gente utiliza (ou conhece) é um pequeno comando de três letras chamado: FOR.

Esse comando faz exatamente o que faz nas linguagens de programação: Repetição… Repetição, 1499 mains a serem “tocados”… Parece ter algo a ver…

Pois bem, tem sim, e vamos a sintaxe  desse comando que resolve boa parte dos casos:

FOR <nome do conjunto> IN (<critérios de filtro>) DO <comando> [<nome do cojunto>]

Para o meu caso, executei (os nomes de conjuntos devem começar com ‘%’):

FOR %1 IN (*.cs) DO touch %1

Simples não? Também acho

Não achou simples? Tudo bem, você pode fazer o Build com Force Generation dos 1500 objetos mains que terá o mesmo resultado, só que vai levar um tempinho a mais… Só umas duas ou dez horinhas…

Um abraço, e até a próxima!

Manutenção de KB’s

Buenas!

Demorei mas voltei…

Hoje quero falar um pouco sobre as KB’s GeneXus (até a versão 9.0).

Todos os objetos GeneXus são armazenados em arquivos .dat, esses arquivos guardam todo tipo de informações relativas à sua KB em forma de tabela, por exemplo: Se tem uma tabela de modelos (Design, Protótipo, Produção1, 2, 3, etc.), objetos, atributos, cross references, etc. (Para mais informações sugiro pesquisar sobre o PublicGx – que brevemente falando, disponibiliza sua KB em forma de tabelas).

As KB’s GeneXus são armazenadas em arquivos, originados da tecnologia ISAM, isso é, são arquivos de dados juntamente arquivos de índices (semelhante ao nosso velho amigo DBF).

Isso implica que, as exclusões de registros são lógicas, ou seja, são marcados os registros como excluídos, porém eles não são de fato eliminados dos arquivos físicos.

Com o passar do tempo, pode acontecer das KB’s acumularem bastante registros nesse estado, o que, nesse caso, é aconselhável realizar um rebuild.

O que esse rebuild faz? Basicamente reescreve todos os arquivos .dat e .idx da sua KB ,eliminando as exclusões lógicas, resultando (não necessariamente) em arquivos menores, mais enxutos.

Outro grande benefício do rebuild é que ele reindexa todos os índices existentes (um para cada arquivo .dat). Fazendo com que objetos que não mais existam, sejam removidos do índice, por exemplo.

Ou, ainda, se você excluiu um modelo inteiro, porque não precisava mais dele (Sim, você pode excluir qualquer modelo da sua KB – exceto o Design, claro – Basta ir para o modelo de Design e clicar em File/Delete Model), é extremamente aconselhável que seja feito um rebuild para enxugar a sua KB, visto que a tabela que armazena os objetos tem como chave primária o número do modelo, ou seja, para cada modelo que você crie essa tabela é duplicada (em relação aos objetos) com o novo número do modelo gerado.

Bom, em primeiro lugar gostaria de salientar que antes de fazer qualquer um dos passos a seguir é recomendável um BackUp da KB! Não necessariamente TODA a KB, apenas os arquivos .dat e .idx que são os que serão alterados aqui.

Para fazer um rebuild (GeneXus 9.0):

  1. Fechar sua KB!
  2. Abrir um prompt do DOS:
    1. Iniciar, Executar, Cmd, [OK] .
  3. Posicionar-se no diretório da KB, ex.:
    1. d : [ENTER]
    2. cd \projetos\KB90\Minha_KB_90 [ENTER]
  4. Digitar “c:\Program Files\ARTech\GeneXus\GeneXus90\rbld” -y

Para fazer um rebuild (GeneXus 8.0):

  1. Fechar sua KB!
  2. Abrir um prompt do DOS.
  3. Posicionar-se no diretório da KB, ex.:
    1. d : [ENTER]
    2. cd \projetos\KB80\Minha_KB_GX80 [ENTER]
  4. Digitar “c:\Program Files\ARTech\GeneXus\Gxw80\rbld” -y

À partir do ponto 3 serão exibidos sucessivos ‘d’  e ‘i’ na tela, onde ‘d’ significa um arquivo .dat sendo reconstruído e ‘i’ significa um arquivo .idx sendo reconstruído.

Após a finalização, pode-se entrar normalmente na sua KB, mas guarde aquele BackUp até que se tenha conseguido abrir todos objetos sem problemas.

Nota 1: Se você tem a versão brasileira do windows, substituir “Program Files” por “Arquivos de Programas”.

Nota 2: Se você tem uma versão 64-bit do windows, substituir “Program Files” por “Program Files (x86)”.

Nota 3: Se a nota 1 e a nota 2 forem verdadeiras… Acho que não preciso dizer, certo?

Nota 4: Se você chegou até aqui esperando algo a mais, desista… o artigo já acabou…

Nota 5: Feliz Natal!.

Nota 6: Próspero Ano Novo!

Um abraço, e boas festas.

Lendo e gravando SDT’s em arquivos físicos

Buenas mi amigos!!!

Demorei um pouco, mas estou de volta para felicidade de alguns e infelicidade de nenhuns… hehehehe

Hoje queria falar rapidamente sobre SDT’s e XML’s

À partir da versão 8.0 do GeneXus, foi implementado o SDT (link para o release notes aqui)…

SDT? Super Dupper Tuppleware??? Simples Didático e Tal??? Não… nada disso… significa Structured Data Types, tipos de dados estruturados… Lembram do struct da linguagem C? Pois é, bem parecido, porém bem mais robusto e simples de ser utilizado.

Imagine um simples XML com a estrutura:

<Clientes>
<Cliente>
<Id>1</Id>
<Nome>Asdrubal </Nome>
<Telefone>3333-3131</Telefone>
</Cliente>
<Cliente>
<Id>2</Id>
<Nome>Pafúncio</Nome>
<Telefone>3332-3232</Telefone>
</Cliente>
</Clientes>

Até a versão 8.0 não se tinha como representar essa estrutura, em memóra, exceto com um vetor (ou matriz em casos um poucos mais complexos). O que tornava sua operação complicada…

Porém na versão 8.0 pode-se trabalhar com o conceito de SDT, ou seja uma variável (qualquer) estruturada da forma que quisermos.

Então, poderia-se representar essa estrutura através de duas variáveis SDT no genexus, porque duas? Simples, pois uma variável guardará a coleção de todos os clientes (no nosso exemplo só temos 2, mas poderiam-se ter vários…) e outra que será utilizada para navegar em cada item dessa coleção de clientes, simplificando, acessaria as propriedades ID, Nome e Telefone de cada cliente da coleção.

Temos então a variável &Clientes_Col, sendo do tipo de dados SDTClientes e a variável &Cliente_Item sendo do tipo de dados SDTClientes.Item (o nome item é configurável no momento da criação da SDT).

À partir daqui vou dividir em dois subtópicos (Gravando em um SDT e Lendo em um SDT).

    1. Gravando em um SDT. Diante das estruturas e das variáveis acima, seria fazer isso:
       //Adiciono estaticamente os valores a cada uma das propriedades
       &Cliente_Item.ID = 1
       &Cliente_Item.Nome = 'Asdrubal'
       &Cliente_Item.Telefone = '3333-3131'
       //Adiciono esse item dentro da minha coleção de clientes
       &Clientes_Col.Add(&Cliente_Item)
       //Instancio (ou crio) uma nova variável de clientes para o próximo item...
       //Notar que após o comando New, é utilizado uma string disponível no menu Inserir/Structured Data Type juntamente com '(' e ')'
       &Cliente_Item = New SDTClientes.Item()
       //Adiciono o segundo cliente, também estaticamente
       &Cliente_Item.ID = 2
       &Cliente_Item.Nome = 'Pafúncio'
       &Cliente_Item.Telefone = '3332-3232'
       //Existe uma alternativa que também funciona, e tem o mesmo efeito das linhas onde é adicionado o item à coleção e a criação de uma nova instância do item
       //(últimas duas linhas antes do segundo item) que é:
       &Clientes_Col.Add(&Cliente_Item.Clone())
      //Essa linha adiciona na coleção uma cópia do item &Cliente_Item
      //Porém costumo usar a primeira alternativa por tornar-se mais intuitiva para quem lê

      Pronto, isso bastaria para popular meu SDT com dois clientes, conforme a estrutura XML do início…

      Você pode estar se perguntando.. Peraí, eu instancio um novo item após a adição desse item à coleção??? E o primeiro item? onde foi instanciado…

      É amigo, lembre-se, estamos utilizando GeneXus e não uma linguagem tradicional… O primeiro item já está “instanciado”, porém sem valores, e é por esses motivo que não instanciei um novo item ao final da 2ª adição, pois não vou precisar de outro item, da mesma forma que se eu acessar, após todos esses comandos, a variável &Cliente_Item ainda vou ter o valor do último item, em memória, sim, essa é a grande diferença de se adicionar com o método Add(&Variavel) + New Item() e Add(&Variavel.Clone()) , ou seja, quando utiliza-se add(&item) + new é criada uma nova instância da variável de item, perdendo-se seus valores das propriedades atuais.

    2. Lendo em um SDT. Diante das estruturas gravadas acima, vou utilizar as mesmas variáveis somente para simplificação, com exceção das variáveis &Cliente_ID, &Cliente_Nome e &Cliente_Tel que utilizei somente para demontração:
      //A sintaxe é For <Item SDT> in <Coleção SDT>
      For &Cliente_Item in &Clientes_Col
          &Cliente_ID = &Cliente_Item.ID
          &Cliente_Nome = &Cliente_Item.Nome
          &Cliente_Tel = &Cliente_Item.Telefone
      EndFor

      A leitura é infinitamente mais simples que a gravação, certo?

      Como diria meu professor de Cálculo B: Daqui pra frente é trivial…

Bacana, super supimpa, mais que demais… só que isso todos que trabalham com genexus já estão carecas de saber… certo???

Porém a questão é… E se e quiser gravar esse SDT em um arquivo… e… lê-lo de um arquivo??

A resposta é… Bom… O SDT não foi feito para isso, mas você pode fazer…

É, mas os SDT’s não tem métodos para abrir e fechar, nem ler ou gravar em arquivo…

A resposta é… Sim, eles não tem… mas para isso existem outros tipos de dados que trabalham com XML… XML Reader e XML Writer… Calma gente… é bem mais simples do que parece…

Vou separar novamente em dois tópicos… Gravando um SDT em arquivo, e Lendo um SDT em Arquivo…

  1. Gravando um SDT em arquivo
    //Para esse tópico e para o próximo, utilizarei a variável &XML (Varchar de 100.000 posições)...
    //A variável &Writer é do tipo Xml Writer.
    //Carrego a variável &XML com o conteúdo do SDT (em forma de XML)
    &XML = &Clientes_Col.ToXml()
    //Abro o arquivo para escrita
    &Writer.Open('arquivo.xml')
    //Escrevo o conteúdo da variável &XML
    &Writer.WriteRawText(&XML)
    //Ou: &Writer.WriteRaeText(&Clientes_Col.ToXml())
    //Fecho o arquivo
    &Writer.Close()
  2. Lendo um SDT à partir de um arquivo
    //Para esse tópico utilizarei duass novas variáveis: uma chamada &Reader do tipo de dados XMLReader, e outra chamada &Erro, numérica de 2 posições.
    //Abro o arquivo .xml
    &Reader.Open('arquivo.xml')
    //Verifico se não aconteceu algum erro (arquivo inexistente, arquivo em uso, etc.)
    &Erro = &Reader.ErrCode
    
    If &Erro = 0 //zero
     	//Aqui sim, efetuo a leitura do arquivo inteiro para a variável &XML
    	&XML = &Reader.ReadRawXML()
    	//Populo o SDT à partir da variável &XML
    	&Clientes_Col.FromXML(&XML)
    	//Sim, eu poderia fazer os dois comandos em uma linha somente:
    	//&Clientes_Col.FromXml(&Reader.ReadRawXML())
    	//Por motivos de facilidade de entendimento utilizei uma variável auxiliar...
    	//Fecho o arquivo
    	&Reader.Close()
    Else
    	Msg("Ocorreu um erro ao abrir o arquivo.")
    Endif

Bom, espero ter contribuído mais um pouquinho…

Um abraço e até a próxima…

Genexus 9 vs. Funções externas Java

 

Buenas…

Demorei um pouco, mas estou de volta. Hoje quero falar um pouco sobre funções externas, mais especificamente utilizando GeneXus 9, já que a versão X nos possibilita fazer isso de forma transparente…

Resumidamente, o que se deve fazer é:

1) Personalizar uma classe externa, para que o genexus consiga chamar o método execute() de forma transparente;
2) Compilar a classe externa na mesma package da aplicação;
3) Adicionar o call() no objeto genexus;

Vamos aos detalhes:

1)  Personalizar a classe externa:

Crie uma classe externa java, a que eu montei é uma classe para compactação de arquivos utilizando métodos nativos do java (Olhar no final desse post). O grande segredo aqui são 3 coisas:

  1. Ter um construtor específico que serve para o genexus utilizá-lo quando for instanciar o objeto.
  2. Ter um método execute(), que o genexus irá utilizar para realizar a chamada de fato.
  3. Ter um package name compatível com a aplicação, pois facilita a compilação tanto da classe externa quanto da aplicação genexus, visto que para essa última a classe já está automaticamente adicionada.

 

2) Compilar a classe externa:

Para compilar a classe externa é necessário colocá-la no mesmo diretório onde estão as demais classes geradas da aplicação (normalmente dentro do diretório da KB\diretório do modelo\web, levar em conta que se estiver utilizando um package name terá mais um ou dois subdiretórios dentro de web\). Após feito isso, utiliza-se o mesmo método que o genexus utiliza para compilá-las, ou seja:

  1. Abrir um prompt do DOS e posicioná-lo na pasta web\ do modelo da KB. Como o exemplo feito na minha KB:
    • Iniciar, Executar, CMD ;
    • D: ;
    • Cd D:\Projetos\MinhaKB\Data002\Web;
  2. Configurar a variável CLASSPATH do Java (disponível no menu de execução do GeneXus):
    • SET CLASSPATH=gxclassr.zip;GxUtils.jar;.;
  3. Executar:
    • callmake.bat “C:\Program Files\ARTech\GeneXus\GeneXus90\gxjava\GXJMake.exe” “C:\Program Files\Java\jdk1.6.0_16\bin\javac.exe” gxzip com\genexus\ -O

sendo

    • c:\Program Files\ARTech\GeneXus\GeneXus90 o caminho onde está o genexus 9 na minha máquina e
    • c:\Program Files\Java\Jdk1.6.0_16\ o caminho onde está o JDK.
    • gxzip: o nome da minha classe externa (arquivo .java)
    • com\genexus: o “package name” da minha aplicação.

E, finalmente,

 

3) Adicionar a chamada ao genexus

Essa é a parte mais simples, localize o evento onde deseja-se adicionar a chamada e inclua:

  • &Origem (string) = caminho absoluto de uma pasta ou arquivo a ser compactados;
    • Se for um arquivo, o .zip conterá somente esse arquivo.
    • Se for uma pasta, o .zip conterá todos os arquivos na pasta citada, com exceção de:
      • subpastas;
      • arquivos com extensão .zip, .7z e .jar
  • &Destino (String) = caminho absoluto para o arquivo .zip a ser gerado, se já existir será sobrescrito;
  • Call(‘gxzip’,&Origem, &Destino)

 

Bom, por hoje é só, espero ter sido claro como a água do rio Tietê e discreto como um elefante em uma loja de cristais… hehehe

Qualquer coisa não exite em não me chamar…

Um abraço e boa noite…

/*
               File: GXZip
        Description: Classe para compactação
             Author: Sandro M. Vianna
         Created on: 29/09/2010 16:24:55.75

*/

package com.genexus ;
import java.io.*;
import java.util.zip.*;
import com.genexus.*;

public class  gxzip {

    static final int BUFFER = 2048;

        //Esse construtor vazio é para ser utilizado pelo método main()
	public gxzip()
	{
	}

        //Aqui está o primeiro segredo, é necessário ter um construtor com exatamente esses parâmetros,
        //para que o genexus consiga "enxergar" como uma classe dele
	public gxzip(int remoteHandle, ModelContext context)
	{
	}

    //Deixei o método main para que a classe possa ser chamada pela linha de comandos,
    //Mas é completamente opcional...
    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println(
   "Usar: java gxzip <arquivos> <novo arquivo compactado> ");
            return;
        }
        try {
            String filename = args[0];
            String zipfilename = args[1];
            gxzip list = new gxzip( );
            list.doZip(filename,zipfilename);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

        //Aqui está o segundo segredo, esse método será utilizado no comando call() do gx
        //Aqui se está recebendo duas strings, apesar de serem recebidas como array de string.
        //Se utiliza sempre o indice 0
        public static void execute(String[] gxFiles, String[] gxZipFile ) {
	  try {
	    String filename = gxFiles[0];
            String zipfilename = gxZipFile[0];
            gxzip list = new gxzip( );
            list.doZip(filename,zipfilename);
	  } catch (Exception e) {
            e.printStackTrace();}
	}

        //Esse é o método que realiza de fato a compactação, é chamado tanto do método main() quanto do execute()
	public void doZip(String filename,String zipfilename) {
            try {
		File ft = new File(filename);
		String entryname, foldername = null;
		final String zipfile = zipfilename;

		if (ft.isFile()){
			//Obtenho o nome do arquivo e da pasta
			//int index = filename.lastIndexOf('\\')+1;
			//String entryname = filename.substring(index);
			//String foldername = filename.substring(0,index-1);
			entryname = ft.getName();
			foldername = ft.getParent();
		}else{
			entryname = "";
			foldername = filename;
			int index = filename.lastIndexOf('\\')+1;
			if (index == filename.trim().length()) foldername = foldername.substring(0,index-1);
		}

		//Obtenho os arquivos contidos na pasta
		File f = new File(foldername);
		String files[] = null;

		//Filtros para não carregar subpastas, arquivos compactados, e o próprio .zip
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File f, String name) {
			File ftemp = new File(f.getPath()+'\\'+name);
			return !ftemp.isDirectory() &&
				!name.endsWith(".zip") &&
				!name.endsWith(".7z") &&
				!name.endsWith(".jar") &&
				!name.equals(zipfile);
				}
		};

		//Se foi configurado um nome de arquivo como origem, carrego somente ele na listagem
		if (entryname.length() > 0)
		{
			files = new String[1];
			files[0] = entryname;
		}else{
			//Senão, carrego a todos arquivos na pasta.
			files = f.list(filter);
                }

                byte[] buf = new byte[BUFFER];
		BufferedInputStream origin = null;
		ZipOutputStream s = new ZipOutputStream((OutputStream)new FileOutputStream(zipfilename));

                for (int i=0; i<files.length; i++) {

	        	FileInputStream fis = new FileInputStream(foldername+'\\'+files[i]);
         		origin = new BufferedInputStream(fis, BUFFER);
			s.setLevel(6);
			ZipEntry entry = new ZipEntry(files[i]);
			s.putNextEntry(entry);
			int count;
			while((count = origin.read(buf, 0, BUFFER)) != -1) {
				s.write(buf, 0, count);
			}
			origin.close();
		}
		s.finish();
		s.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Comparação de XPZ’s e KB’s

Bom dia!

À partir da versão 9.0 do GeneXus, a artech disponibilizou uma ferramenta de comparação, que pode ser executada (até então) somente pelo menu de consolidação de conhecimento (Knowledge Manager, Consolidate, botão Compare).

O que esse botão faz? Basicamente duas coisas:

  1. Distribui TODOS os objetos da KB atual
  2. Compara o XPZ a ser consolidado com o XPZ gerado.

O utilitário é muito bom no detalhamento das informações, porém peca no fato de não se poder selecionar as linhas, para copiá-las, por exemplo.

O que descobri é que se você já possui os doiz XPZ’s a serem comparados, existe uma maneira simples de executar esse utilitário “por fora” do Genexus, basicamente o que deve ser feito é :

Executar a linha de comando:

“C:\Program Files\Artech\GeneXus\GeneXus90\Comparer\XmlModelComparer.exe” “C:\Temp\GXx397.tmp.xml” “C:\Temp\XPZ_Full.xpz” Genexus genexus.xml

Supondo que meu GeneXus 9.0 tenha sido instalado em “C:\Program Files\Artech\GeneXus\GeneXus90\”

Como pode ser observado, o primeiro parâmetro é o caminho para o XPZ (ou .xml) da esquerda, o segundo parâmetro é o caminho para o XPZ (ou .xml) da direita e o terceiro e quarto parâmetros são fixos, necessários pelo utilitário.

Voilá!

Descobri também, que para a diferença entre os dois XPZ’s é gerado um terceiro .XML que pode ser aberto para automatizar-se a recuperação dos nomes dos objetos, por exemplo.

Esse .xml estará localizado, por padrão, em C:\Program Files\Artech\GeneXus\GeneXus90\Comparer\diff.xml

Lembrando que esse comparador funcionará para .XPZ’s da versão 9.0, bem possivelmente de versões anteriores, porém, não acredito que funcionará para versões mais recentes (X, X evol. 1).

Também não consegui localizar esse comparador na instalação do GeneXus X…

Bom, fica a dica…

Um abraço e bom final de semana

Bem vindos

Boa noite…

Relutei bastante para criar um Blog, porque sei que não vou ter tempo para dar a atenção que gostaria de dar: Atualizar com freqüência, ver o que as pessoas estão falando, etc… Mas resolvi tentar, não como uma maneira de entrar (ainda que tardia) na “modinha”, até porque não sou disso, mas sim como uma maneira de compartilhar os mais de dez anos de convivência que tenho com a ferramenta.

Ah sim, aqui falarei bastante sobre o GeneXus, que, para quem não conhece, foi designada originalmente para ser uma ferramenta CASE (Computer Aided Software Engineering, ou Engenharia de Software Auxiliada por Computador), mas é mais do que isso, ele pode ser considerado até como um seguro contra evolução tecnológica (frase que escutei muito do meu amigo e guru Daniel Strack).

O que ele  faz? Para que serve? Vou tentar fazer uma analogia, que ainda considero uma das melhores formas de se explicar algo:

Imagine que você tenha que construir uma casa, essa casa poderá ser para você ou não, como método mais próximo a realidade do mercado de softwares, vamos concordar que, em uma empresa no mercado, 90% das vezes você estará construindo casas para outras pessoas (seus clientes).
Você então terá que montar um projeto onde terá recursos (humanos, materiais), prazo, orçamento, e um escopo (a casa em si).

Agora imagine que, no desenvolvimento de software tradicional (analogamente falando), você construa a casa da maneira que todos conhecem: Encaixando tijolo por tijolo, onde, por exemplo, 4 pessoas se encarregarão de levantar quatro paredes. Para elas fazerem isso, cada uma delas terá que ler um documento dizendo exatamente o que elas devem fazer, vamos supor que elas deverão:

  • Montar os tijolos sob uma superfície, com massa entre o espaçamento de 2 cm entre cada tijolo;
  • Passar uma camada de massa sobre todos os tijolos; E assim por diante até que a parede esteja completa; Fazendo isso terão que:
  • Passar mais uma camada de salpique em toda a parede, e, para finalizar:
  • Uma camada de reboco, também em toda a parede.

Podemos afirmar então que cada uma dessas 4 pessoas utilizaram um método, ou tecnologia, na linguagem de software, para construir as paredes.

Agora, na mesma analogia, com o GeneXus, utilizando a mesma tecnologia, não sejam necessárias 4 pessoas, e sim uma somente e essa pessoa não montaria tijolo por tijolo, massa por massa, salpique, reboco, ela simplesmente declararia algo assim:

  • Parede de tijolos (Casa,10, 3, 4); onde Casa seria o objeto composto pelas 4 paredes conectadas, 10 seria a largura, 3 a altura e 4 a quantidade de paredes.
  • Salpicar paredes (Casa); e, finalmente,
  • Rebocar paredes (Casa);

Declarando isso ela usaria uma ferramenta que automaticamente montaria todos os tijolos, passaria a massa, conectaria as quatro paredes, aplicaria o salpique e o reboco;

Diante dessa analogia podemos contatar alguns fatos:

1) O intelecto necessário para realizar a tarefa de subir as paredes é diferente, na metodologia tradicional seriam necessários 4 pedreiros, com o Genexus é necessário um engenheiro.

2) O fator tempo é algo extremamente diferente, o tempo que as 4 pessoas demorariam para subir as 4 paredes, é aproximadamente o dobro que uma pessoa levaria utilizando o GeneXus.

3) o fator custo (tanto o custo final quanto o custo do projeto), por conseqüência também é severamente afetado pela suposição acima.

4) Agora supondo que surgiu uma nova maneira (tecnologia) para se construir paredes, onde torna-se necessário treinar toda a equipe novamente (isso é BEM comum no desenvolvimento tradicional) e essa equipe terá que esquecer tudo o que aprendeu e aprender novamente. Com o GeneXus não é necessário re-treinar a equipe, pois o engenheiro irá continuar declarando paredes da mesma maneira. O Genexus sim terá que ser atualizado para essa nova tecnologia, e, nesses 10 anos que venho acompanhando, ele sempre o fez, com maestria e as vezes antes mesmo da tecnologia ser disponibilizada publicamente (.NET Early Adoption é um exemplo, em outro post irei comentar sobre a demonstração dos engenheiros de software da ARTech em um evento da Microsoft). Atualizado o “motor” do GeneXus, tudo pronto…

5) Supondo agora que existe uma reforma a ser feita na casa, e será necessário destruir uma parede e construir duas novas…Bom… acho que já alcancei o ponto, não??? Somente para constar o GeneXus costuma ser 20 vezes mais rápido na manutenção do que no desenvolvimento comum…

Alguns pontos chaves que considero interessante da ferramenta:

  • Geração de código 100% automática;
  • Geração de scripts de criação e alteração da base de dados 100% automático;
  • Programação declarativa;
  • Multiplataforma;
  • Sem runtime (gera código 100% nativo da linguagem);
  • Baseado em desenvolvimento incremental;

Bom, aos leigos, sejam bem vindos ao mundo GeneXus, aos que já conhecem, em breve estarei “postando” algo interessante, e também estou aberto a sugestões, sempre, afinal, foi a comunidade GeneXus o maior coadjuvante para o crescimento da ferramenta…

Para mais detalhes:

http://www.genexus.com/portal/hgxpp001.aspx?2,61,1006,O,P,0,MNU;E;226;1;236;2;MNU;,

http://www.gxtechnical.com/

Um abraço e até a próxima…