21 de dezembro de 2009

Adoção do protocolo IPv6 terá ritmo acelerado em 2010

Há algum tempo corre uma previsão dizendo que o crescimento da Internet deve em breve esgotar a disponibilidade de endereços IP, o que impediria a expansão da rede, isto é, seria impossível dar uma identificação única a novos computadores e outros dispositivos ao acessarem a Internet. Essa previsão dá atualmente a data para esgotamento como sendo Julho de 2011 com o modelo atual, chamado de IPv4. Assim, a Internet Engineering Task Force (IETF) desenvolveu um novo modelo - o IPv6 - que comporta uma quantidade muito maior de endereços e deverá substituir o IPv4.

O IPv6 vem sendo implantado gradualmente mas ainda representa uma porcentagem bastante pequena dos endereços existentes. Veja abaixo a reprodução de uma reportagem que saiu no site IDG Now! sobre esse assunto. Para acessar a matéria original, clique aqui.
Após observar uma aceleração nas atividades em torno do novo protocolo de comunicação da internet, o IPV6, provedores de acesso e empresas que fornecem equipamentos de redes apostam em 2010 como o ano da atualização.

A comunidade de engenharia de internet já espera há uma década pela adoção em massa do IPv6 e afirma que a migração é necessária porque o atual protocolo IPv4 está ficando sem endereços.

O IPv4 usa endereços de 32 bits e pode suportar aproximadamente 4,3 bilhões de dispositivos com endereços específicos na internet. Já o IPv6 usa endereços de 128 bits e é capaz de suportar uma quantidade tão grande de dispositivos que somente uma expressão matemática - 2 elevados à 128ª potência - pode medir.

A migração para o IPv6 é um processo contínuo, que já foi iniciado em diversos países, incluindo o Brasil, "mas não há prazo para terminar", explica o diretor de serviços e tecnologia do Registro.br, órgão responsável pelo controle de domínios criados no Brasil, Frederico Neves.

Nos Estados Unidos, provedores de acesso registram uma aceleração no uso de protocolos IPv6 este ano. A Hurricane Electric, da Califórnia, viu sua rede IPv6 dobrar de tamanho este ano, com conexões a outras 600 redes no novo protocolo.

A NTT America também observou 100% de crescimento no tráfego usado por seus clientes com o novo protocolo. "Tivemos 40% de alta no número de portas que vendemos para IPv6", diz o diretor de engenharia de produtos da empresa, Cody Christman.

A Global Crossing também prevê aumento na oferta de serviços via IPv6 em 2010. A empresa informa que o número de pedidos de serviços no novo protocolo triplicou em 2009, sendo mais intenso no segundo semestre.

"O IPv6 representou 6,3% de novos pedidos em nossa linha de produtos de internet - alta de 2,1% em relação a 2008", afirma o diretor de serviços de acesso à internet da Global Crossing, Matt Sewell. Em 2010, o executivo estima um incremento de 10% em novos pedidos ligados ao IPv6. "Muitos clientes perguntam se estamos preparados para o IPv6".

O número de provedores de conteúdo e aplicações que suportam o IPv6 também cresceu em 2009. O movimento mais expressivo foi do Google, que adicionou o IPv6 a diversas aplicações incluindo buscas, Google Docs, Gmail e News, bem como o navegador Chrome e a plataforma móvel Android. Em 2010 será a vez do YouTube, informam engenheiros da empresa de internet.

Entre outras empresas de internet que apostaram no IPv6, nos Estados Unidos, estão o serviço de streaming de vídeo, Limelight Networks, e a distribuidora de filmes online, NetFlix.

Um dos maiores provedores de conteúdo dos Estados Unidos, Comcast, deve alavancar a adoção do IPv6 em 2010, quando a planeja testar o novo protocolo em residências. A empresa também trabalha na migração de sua rede para o IPv6, bem como na oferta de alguns conteúdos próprios adequados ao novo protocolo, o que inclui os sites dos canais E! Entertainment Television, Style Network e Golf Channel.

"Vamos anunciar um portal para IPv6, em um futuro não muito distante", informa o chefe de arquitetura do projeto de IPv6 da Comcast, John Jason Brzozowski. Na previsão da Comcast, em 2010, consumidores norte-americanos começam a observar os benefícios do IPv6 em equipamentos residenciais de rede e outros componentes eletrônicos adaptados ao novo padrão.

Apesar do crescimento em sua adoção este ano, o IPv6 ainda representa 1% de todo o tráfego mundial da internet, segundo estimativas da indústria. O Google informa que 0,2% de seus usuários possuem acesso via IPv6.

A fabricante de equipamentos de rede, Cisco Systems, acredita que 2010 pode ser um marco para a adoção do IPv6 por conta dos novos serviços de internet que demandarão mais dispositivos conectados à rede.

Entre as aplicações que devem alavancar o uso do IPv6, a empresa aposta no avanço das 'smart grids' para o setor de energia elétrica, bem como nos dispositivos conectados para a área de saúde. Sistemas de monitoramento e administração de redes para redução do consumo de água e energia também devem aderir rapidamente ao novo protocolo.


18 de dezembro de 2009

Comunicação entre processos : Sockets - Client

No último post, mostrei como construir um Serviço usando um Socket servidor para monitorar requisições de Clients que conheçam a sintaxe apropriada para se comunicar com esse Serviço. No caso, apenas uma mensagem foi implementada no Servidor e ela retorna uma lista de Certificados Digitais instalados no computador onde o Serviço está em execução. Neste post, mostro a outra ponta da implementação, isto é, como construir um Client capaz de se comunicar com esse Servidor e obter a tal lista.

Para a aplicação Client poder se conectar com o Serviço, ela tem que configurar uma instância do componente TClientSocket, informando nela tanto o endereço do servidor quanto a porta que ele está monitorando. Depois, enviar uma mensagem com a solicitação desejada e aguardar a resposta.
_StatusMens = 0; /* Status da mensagem */
_MemorySize = 0;
_Memory->Clear ();
_ClientSocket->Port = 1024;
_ClientSocket->Host = "PC_151";

aguarde ("Obtendo certificados");
try {
_ClientSocket->Active = true;

/* Aguarda conectar */
while (!_ClientSocket->Active && _StatusMens == 0)
Application->ProcessMessages ();

if (_ClientSocket->Active)
{
_ClientSocket->Socket->SendText ("#GET_CERTIFICATE");

/* Aguarda retorno */
while (_StatusMens == 0)
Application->ProcessMessages ();
}

if (_Status < 0)
col_erro ("Não foi possível obter certificados.");

_ClientSocket->Active = false;
}
__finally {
fim_aguarde ();
}

Note no trecho reproduzido acima que é possível especificar o Host pelo nome, o que é recomendado principalmente se o IP do Servidor for variável. Note também que após a conexão há um laço para aguardar o retorno. Isso é necessário porque a resposta do Servidor não é síncrona, isto é, a função que envia mensagens não recebe o retorno enviado pelo Servidor. Aqui, o Client envia o texto "#GET_CERTIFICATE". Estes caracteres representam para o meu Serviço o nome do comando que deve retornar os certificados existentes no Servidor. Obviamente, esse nome foi estabelecido por mim quando o Serviço foi projetado e o Client deve saber dessa informação de antemão para poder submeter corretamente a requisição.

Para o Client receber o retorno, é preciso responder ao evento OnRead do TClientSocket e ir lendo os bytes enviados em blocos pelo Servidor. Portanto, o OnRead pode ser executado mais de uma vez antes que a resposta toda seja recebida:
void __fastcall TFrmCert::ClientSocketRead(TObject *Sender, TCustomWinSocket *Socket)
{
char *lBuffer = NULL;
int lLen = Socket->ReceiveLength();

if (lLen > 0)
{
lBuffer = new char [lLen];

try {
/* Guarda no buffer interno os bytes recebidos */
Socket->ReceiveBuf ((void *)lBuffer, lLen);

if (memcmp (lBuffer, "#CERTIFICATE_ERROR", 18) == 0)
_StatusMens = -1;
else
{
/* Primeiro bloco recebido ? Então recupera o tamanho total do retorno enviado pelo servidor */
if (_MemorySize == 0)
memmove (&_MemorySize, lBuffer, sizeof (_MemorySize));

{ Guarda no Memory stream todo o buffer recuperado. Cada bloco lido é acrescentado ao fim do último bloco até receber a mensagem toda. }
_Memory->Write (lBuffer, lLen);

{ Recebeu todos os blocos ? }
if (_Memory->Size == (_MemorySize + (int) sizeof (_MemorySize)))
_StatusMens = 1;
}
}
__finally {
delete [] lBuffer;
}
}
}

A leitura do retorno mostrada acima é automaticamente executada numa thread separada e é por isso que no trecho anterior há o laço de aguarde logo após o envio da requisição. Aquele código monitora a variável que controla o status da recepção. Mudanças no valor do status sinalizam que a recepção foi concluída - ou, se for o caso, que um erro ocorreu. após o término do laço e se não houve erro, a variável stream _Memory conterá todos os bytes enviados pelo Servidor. O Client, então, poderá tratar esse conteúdo da forma que julgar necessário, baseado no conhecimento prévio sobre o significado dos bytes retornados.

Se você estabelecer que o seu Servidor receberá solicitações em um formato XML bem definido, contendo tags com o nome de função a ser chamada e os parâmetros a serem supridos, você terá uma versão sua simplificada do conceito de WebServices.

17 de dezembro de 2009

Comunicação entre processos : Sockets - Servidor

Em junho de 2009, eu postei um texto introduzindo o assunto "Comunicação entre Processos". Naquela ocasião, dei um exemplo em C++ Builder usando a API do Windows para fazer duas aplicações no mesmo computador compartilhar parte da memória para trocar informações usando File Mapping. Agora, vou complementar o assunto mostrando uma forma básica de fazer com que dois programas na mesma rede possam trocar informações. Para isso, vou usar Sockets.

Um Socket é um componente de software disponibilizado pelo Sistema Operacional para permitir que um programa receba corretamente pacotes de dados direcionados a ele. O Socket não faz qualquer inferência sobre o conteúdo do pacote de dados, podendo ser qualquer sequência de bytes que se faça necessária. A ideia é a seguinte: um programa cria um Socket Server e o ativa para poder receber solicitações. Este Socket Server é caracterizado por um endereço (o IP do próprio computador) e um número de porta de modo que outros programas que conheçam esse par endereço/porta podem se comunicar com o programa servidor.

Mas, se o Socket não estipula um formato para os dados trocados, como é que o Servidor e o Client conseguem se entender ? A interpretação dos dados é de responsabilidade do programa Servidor e é ele quem dita as sequências de bytes que devem ser enviadas para a mensagem fazer sentido. Qualquer Client que queira se comunicar com ele deve saber como montar as sequências de bytes corretamente para ter suas solicitações atendidas. Ou seja, o Servidor estabelece seu próprio protocolo e os Clients devem segui-lo para poderem se comunicar.

Na ABC71, nós construimos um serviço que, entre outras coisas, lê os certificados digitais válidos instalados no Servidor e envia a lista à aplicação Client para que o usuário possa selecionar qual dos certificados deve ser usado para assinar documentos digitalmente. Vou usar esse cenário para exemplificar o uso dos Sockets.

O C++ Builder e o Delphi têm um componente chamado TServerSocket (na minha versão, ele fica na guia de componentes Internet). O trecho abaixo foi montado no C++ Builder como resposta ao evento OnExecute de um projeto para construção de um Serviço:
class TWServiceOmegaNFe : public TService
{
__published: // IDE-managed Components
TServerSocket *ssCert;
void __fastcall ServiceExecute(TService *Sender);
/* ... */
};

/* ... */
void __fastcall TWServiceOmegaNFe::ServiceExecute(TService *Sender)
{
ssCert->Port = 1024;
ssCert->Active = true;
try {
while (!Terminated)
ServiceThread->ProcessRequests (true);
}
__finally {
/* finalizando o serviço ... */
ssCert->Active = false;
}
}

O código acima apenas configura uma porta no Socket e o ativa para iniciar o monitoramento de requisições dos Clients. No exemplo, escolhi e fixei o número 1024 mas pode ser qualquer porta livre. O comando ServiceThread->ProcessRequests (true) instrui o serviço a receber e tratar quaisquer mensagens enviadas diretamente para a thread principal - por exemplo, quando o usuário solicita a interrupção do Serviço através do console de Serviços do Windows.

Quando um socket Client envia uma mensagem para este Servidor, o socket server dispara o evento OnClientRead, ocasião em que o Servidor interpreta a mensagem e devolve as informações solicitadas - ou uma mensagem de erro, se for apropriado:
void __fastcall TWServiceOmegaNFe::ssCertClientRead( TObject *Sender, TCustomWinSocket *Socket)
{
int lLen = Socket->ReceiveLength();
char *lBuffer = NULL;
TMemoryStream *lMemoryStream, *lStream;

if (lLen > 0)
{
lBuffer = new char [lLen + 1];
lMemoryStream = new TMemoryStream ();

/* lStream não pode ser destruído, pois ele será destruído pelo socket */
lStream = new TMemoryStream ();

try {
memset (lBuffer, 0, lLen + 1);
Socket->ReceiveBuf ((void *)lBuffer, lLen);

/* Este server só reconhece a mensagem #GET_CERTIFICATE mas outras podeam ser incluidas aqui*/
if (memcmp (lBuffer, "#GET_CERTIFICATE", 16) == 0)
{
/* Monta em lMemoryStream um XML com os certificados */
BuscaCertificadoDigital (lMemoryStream);
lLen = lMemoryStream->Size;
if (lLen > 0)
{
/* Para que um Client saiba quantos bytes devem ser lidos, inclui essa informação no início */
lStream->Write (&lLen, sizeof (lLen));

/* Acrescenta o Stream com os certificados */
lStream->Write (lMemoryStream->Memory, lLen);

/* Reposiciona o Stream para que o Socket envie todo o conteúdo */
lStream->Seek (0, soFromBeginning);

/* Envia o Stream para o Client */
Socket->SendStream (lStream);
}
else /* Erro ! */
Socket->SendText ("#CERTIFICATE_ERROR");
}
}
__finally {
delete lMemoryStream;
delete [] lBuffer;
}
}
}

Veja que o Socket servidor recebe a mensagem num buffer - um texto que em princípio é interpretado como um nome de função, indicando qual é o tipo de informação solicitada pelo Client - e depois prepara o stream de retorno e o envia usando o próprio Socket servidor. Para o Client, entretanto, a resposta virá em momento diferente, assincronamente, como veremos.

Mostro no próximo post como montar uma aplicação Client para se comunicar com esse Servidor e obter a lista de Certificados.

Mais Informações
Criação de Serviços (parte 1 e parte 2)

10 de dezembro de 2009

Design Patterns com Delphi : FlyWeight - parte 2

Há alguns, falei aqui no blog sobre o conceito e terminiologia associados ao Design Pattern estrutural FlyWeight. Faltou mostrar como organizar e implementar as classes usando Delphi. Recordando o conceito, esse design pattern tem por objetivo minimizar o uso de memória (e outros recursos) quando a criação de uma grande quantidade de instâncias de classe é necessária para uma determinada aplicação.

Para facilitar a compreensão, reproduzo novamente abaixo o diagrama UML que exemplifica o uso do FlyWeight:
Diagrama UML para FlyWeight

Baseado no diagrama, teremos que declarar a classe TWFonte que atua como uma interface, isto é, sua característica principal é introduzir o comportamento (as funções) que permite compartilhar dados intrínsecos da classe e aplicar temporariamente a esses dados outras informações externas (os dados extrínsecos) particulares a cada contexto.

É bastante comum que esse tipo de classe seja abstrata pura, isto é, que não possua variáveis internamente - esta característica é reservada às versões concretas da classe - mas isso não é uma obrigação. No exemplo do diagrama, há informações comuns aos tipos de Fonte que fazem sentido ser colocados nessa classe:
TWFonte = class
private
{ Nome do Fonte }
_Nome : String;
{ Lista de imagens disponíveis para o Fonte }
_Imagens : TList;

constructor Create(ANome : String); overload;
public
constructor Create;reintroduce; overload;
procedure Desenha(AContext: TWContext; AStyle: TWStyle) = 0;
end;
{ ... }
constructor TWFonte.Create(ANome : String);
begin
_Nome := ANome;
{ ... }
end;

constructor TWFonte.Create;
begin
Raise Exception.Create('Classe deve ser construída a partir da Factory');
end;

Veja que há dois construtores com o nome de Create, um privado e outro público. A intenção por trás disso é forçar o uso da classe de Factory quando for necessário obter instâncias do TWFonte já que é o Factory quem garante que apenas uma instância de cada herança concreta existe e, em última análise, garante o sucesso do compartilhamento dos dados intrínsecos de cada fonte. Para maiores detalhes sobre essa técnica de esconder um construtor, veja nesse endereço a seção Hiding a Constructor.

Como dito no parágrafo anterior, a classe Factory para o pattern Flyweight funciona de forma ligeiramente diferente daquela estabelecida pelo pattern Factory Method. Aqui será preciso ter uma instância da classe Factory (provavelmente um Singleton) para gerenciar as instâncias criadas:
TWFonteFactory = class
private
_Fontes : TStringList;
{ ... }
public
function GetFonte (ANome: String) : TWFonte;
end;
{ ... }
function TWFonteFactory.GetFonte (ANome: String) : TWFonte;
var idx : integer;
begin
Result := nil;
idx := _Fontes.IndexOf(ANome);
if (idx >= 0) then
Result := _Fontes.Objects[idx] As TWFonte
else begin
if ANome = 'Arial' then
Result := TWArial.Create
else
if ANome = 'TimesNewRoman' then
Result := TWTimesNewRoman.Create;
end;
end;

Talvez a diferença mais importante nesse Factory é que ele mantem internamente as instâncias criadas e, quando solicitado, retornam um ponteiro para uma instância para uso externo. Então, embora não apareça no trecho de código acima, o destructor da classe TWFonteFactory deve destruir todas as instâncias de TWFonte criadas e armazenadas por ela. Com isso, deve-se tomar cuidado para não usar uma instância do TWFonte após o destrutor da Factory ter sido chamado.

As últimas classes relevantes para o FlyWeight são as que implementam concretamente a interface de compartilhamento. No exemplo, as ConcreteFlyWeight são a TWArial e a TWTimesNewRoman, que são codificadas como heranças simples:
TWArial = class(TWFonte)
public
procedure Desenha(AContext: TWContext; AStyle: TWStyle);
end;
{ ... }
procedure TWArial.Desenha(AContext: TWContext; AStyle: TWStyle);
var lImagem : TImagem;
begin
lImagem := GeraImagem (_Imagens, AStyle, AContext.Texto);
DesenhaImagem (AContext, lImagem);
end;

A destacar nesse trecho é a forma como a função Desenha faz uso das propriedades intrínsecas e dos parâmetros para obter o resultado desejado. Os parâmetros são propriedades extrínsecas - isto é, de fora da classe - e, portanto, não são compartilhados. A função interna GeraImagem cria uma imagem para cada caractere do texto e concatena todas elas para gerar a imagem correspondente ao texto informado no Contexto. A imagem de cada caractere é escolhida na lista interna de acordo com o estilo e tamanho requisitado pelos parâmetros. Para finalizar, a função interna DesenhaImagem toma a imagem gerada e a desenha usando as informações estipulada pelo contexto - handle do canvas, localização, etc..

8 de dezembro de 2009

ABC71 disponibiliza gratuitamente e-book sobre SPED

A Escrituração Digital - SPED - é o projeto do Governo Federal que tem como objetivo receber digitalmente dos contribuintes informações fiscais exigidas pela legislação. Do ponto de vista estritamente técnico, o projeto tem alguns desafios para as Empresas, como o uso de assinatura digital nos documentos e a forma de comunicação (Web Services) com a Receita Federal. O grande nó, entretanto, é desvendar as informações que devem constar nos arquivos digitais a serem gerados e enviados para o Governo. Portanto, entender o escopo do projeto de SPED e o impacto que sua implantação tem para a Empresa passa a ser mandatório.

Para ajudar a desatar esse nó, a ABC71 apoiou a publicação de um livro sobre o assunto e agora disponibiliza gratuitamente uma versão digital dele. Veja abaixo a nota, que foi produzida originalmente pela SOMA Agência de Comunicação Sustentável, que dá mais detalhes:
Com mais de 60 projetos implementados em SPED – Sistema Público de Escrituração Fiscal -, a ABC71 a fim de auxiliar ainda mais as empresas que precisam se adequar aos novos procedimentos da Receita Federal oferece ao público a versão eletrônica do livro "Big Brother Fiscal III: o Brasil na Era do Conhecimento", de Roberto Dias Duarte, uma das grandes referências no tema SPED.

Voltado aos empresários, diretores e gerentes das áreas de negócios, esta terceira edição do livro conta com informações atualizadas sobre certificação digital, Nota Fiscal Eletrônica (NF-e), Nota Fiscal Eletrônica de Serviços (NFS-e), escrituração contábil digital, escrituração fiscal digital, sistemas integrados de gestão (ERP), entre outros assuntos. O livro foca a gestão e sua respectiva melhoria de processos por conta da nova legislação e dos novos padrões contábeis e fiscais que as instituições deverão atender.

"A ABC71 adquiriu muita experiência na implantação de projetos de SPED em 2009 e tem hoje aproximadamente 700 clientes. Para nós é um prazer apoiar esta terceira edição, pois a importância deste livro para as áreas gerenciais e decisórias da empresa é essencial. Saber como irá funcionar a nova legislação, observar quais serão os pontos-chave para atender ao SPED, a importância de um sistema de gestão seguro na empresa são algumas das principais dúvidas do mercado quanto ao que fazer e como fazer", explica Selene Sidney, gerente de marketing da ABC71.
As empresas e suas respectivas áreas de TI e administrativo-fiscal são geralmente as responsáveis pelo sucesso e/ou fracasso da nova abordagem legal perante o governo. "Os profissionais das áreas contábeis, fiscais, tributárias e também de tecnologia precisam necessariamente entender os impactos dessa nova forma de envio de informações ao Fisco, além de compreender melhor como toda a cadeia produtiva está intrinsecamente ligada a essa nova modalidade", analisa Selene.

Além disso, um sistema de gestão amplia as possibilidades de melhoria nos processos: desde um controle de estoques mais efetivo, recebimento de mercadorias, produção até a saída do produto final e entrega no cliente. Sabendo que no SPED a qualidade das informações é imprescindível para que não ocorram problemas junto à Receita, um sistema de gestão onde os usuários do sistema possam inserir dados fidedignos é de extrema importância para que não só sejam atendidas as demandas fiscais e contábeis, mas que a empresa possa extinguir riscos, erros e retrabalhos.

O e-book "Big Brother Fiscal III: o Brasil na Era do Conhecimento" possui também depoimentos de empresas que já estão adequadas ao SPED e que já contam com os benefícios de uma gestão integrada proporcionada pelo ERP Omega, solução de gestão criada pela ABC71.

Para os interessados em receber a versão eletrônica do livro, basta preencher um cadastro no site www.abc71.com.br. O e-book estará disponível a partir do dia 08/12.

Mais informações sobre o livro "Big Brother Fiscal III: o Brasil na Era do Conhecimento" acesse: www.robertodiasduarte.com.br.


4 de dezembro de 2009

Design Patterns com Delphi : FlyWeight - parte 1

Mesmo com o barateamento dos chips de memória e o consequente aumento da disponibilidade desse recurso nos computadores mais modernos é uma boa ideia que, na construção de programas, se use com inteligência esse recurso. Programas que usam menos memória são mais leves para o Sistema Operacional gerenciar e acabam sendo mais responsivos ao usuário, isto é, atendem mais prontamente qualquer ação requisitada pelo usuário.

Imagine que você está criando um editor de textos que permite a formatação de trechos com fontes com características diferentes - negrito ou uma cor, por exemplo - em qualquer ponto do texto. O primeiro impulso é o de criar uma classe que tenha como membros o pedaço de texto em questão e a formatação aplicada - inclusive uma instância da Fonte. Num texto muito longo e ricamente formatado, pode haver centenas de ocorrências. Então, o consumo de memória com a solução apresentada pode tornar a navegação no texto bastante lenta, prejudicando a experiência do usuário.

Por isso, racionalizar a criação de instâncias de Fontes é uma boa saída. Para cenários como o descrito acima, foi criado o Design Pattern estrutural FlyWeight - ou Peso Mosca, numa referência à categoria de lutadores de boxe mais leves. Nesse pattern, a racionalização do uso da memória se dá através do compartilhamento das partes imutáveis das instâncias das classes. No caso dos fontes, isso significa criar uma parte fixa com a representação gráfica de cada caractere (e outras informações relevantes para desenhá-los) enquanto as variações de formatação permitidas, como negrito, itálico e a cor da letra, podem ser armazenadas da forma tradicional, dentro da própria instância da classe aonde a formatação está aplicada.

O conjunto dos valores imutáveis que compõem a instância da Fonte é chamado de Propriedades Intrínsicas, porque são elas que definem quem a Fonte é, independente do contexto onde ela será usada. É esse conjunto que será compartilhado para poupar o uso de memória. Em contrapartida, os valores que poderão variar de acordo com o contexto são denominados Propriedades Extrínsicas. Elas são livres para assumir qualquer valor que seja necessário para o contexto em que são usadas e, por isso, não podem ser compartilhadas, sendo que cada classe que fará uso delas deverá ter sua própria cópia.

Segue um esquema UML representando as classes envolvidas no pattern FlyWeight e suas associações:
Diagrama UML para FlyWeight

Conforme representado no diagrama acima, a nomenclatura para as classes envolvidas na implementação do FlyWeight é a seguinte :
FlyWeight contem as propriedades intrínsecas comuns do objeto, isto é, todos os dados que serão compartilhados. Ela também declara como interfaces as operações que poderão ser aplicadas com dados extrínsecos, dados esses que são passados como parâmetros na operação. A operação, então, usa as informações fornecidas como parâmetro para obter o resultado desejado, preservando no entanto o estado original do objeto. No exemplo, esse papel é exercido pela classe TWFonte, que introduz todas as informações que permitem desenhar os caracteres no padrão do fonte. A operação Desenha publicada pela classe aceita como parâmetros o local para desenho (contexto) e características temporárias que configuram cada operação de desenho. Por exemplo, esta operação pode desenhar um texto em negrito e vermelho num momento e em outro instante desenhar em itálico e verde - tudo calculado localmente com base nos dados globais de desenho e nas configurações estipuladas pelos parâmetros.
Os ConcreteFlyWeight são classes concretas que implementam as operações exigidas pela interface FlyWeight. Eles podem também introduzir novos dados intrínsecos, isto é, que serão usados pelas operações mas que são preservados - e, consequentemente, compartilhados por todas as instâncias. No exemplo, as classes TWArial e TWNewTimesRoman exercem esse papel.
O FlyweightFactory é uma espécie de Factory Method que mantem uma lista com as instâncias compartilhadas dos FlyWeights. A ideia é que sempre que for necessário obter uma instância de classe FlyWeight, aciona-se o Factory. Se uma instância já existir, ela é retornada; caso contrário, o Factory cria uma nova instância e adiciona-a à sua lista interna. Neste cenário, o Factory é reponsável por liberar a memória consumida pelas instâncias criadas por ele, quando for apropriado. No diagrama mostrado acima, a classe TWFontFactory desempenha essa tarefa.
O Client é toda classe que faz uso das operações publicadas pelo FlyWeight. Ela deve armazenar os dados extrínsecos para poder informá-los quando tiver que comandar uma das operações. Ela também pode manter referências à instâncias das classes FlyWeight. A classe TWTextoFormatado representada no diagrama é um exemplo desse papel.



1 de dezembro de 2009

Algumas dicas para usar o TStringGrid

Esses dias tenho trabalhado num novo projeto para a ABC71 e no ponto atual tive que mexer bastante com String Grids. No começo do mês, publiquei sobre a customização da aparência deles usando o Canvas associado ao grid e o evento OnDrawCell. O que vou mostrar aqui é um pouco mais próximo do funcionamento básico do String Grid.

A proposta mais básica do String Grid é exibir textos e, para tanto, temos que colocar os textos desejados na propriedade Cells. Essa é uma propriedade indexada pela linha e coluna da célula de modo que é possível associar um valor para cada combinação de linha/coluna.
sg.ColCount := 2;
sg.RowCount := 5;
{ Primeiro é a coluna, depois a linha }
sg.Cells [0,0] := 'Módulo';
sg.Cells [1,0] := 'Responsável';

{ comprimento em pixels da coluna 'Módulo' }
sg.ColWidths [0] := 130;
{ comprimento em pixels da coluna 'Responsável' }
sg.ColWidths [1] := 240;
{ Altura de cada célula }
sg.DefaultRowHeight := 18;
{ configuração do Grid - inclui permissão para que o usuário edite valores diretamente no Grid }
sg.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goEditing];

Também é possível associar todo um objeto a cada célula e trabalhar com ele no evento OnDrawCell. Esse recurso permite enriquecer a forma com que os dados são apresentados, incluindo imagens, cores, textos com formatações diferenciadas na mesma célula, etc.A propriedade que permite criar esse tipo de associação é a Objects. Da mesma maneira que o Cells, o Objects também é acessível por linha e coluna, garantindo que cada célula possa ser associada a seu próprio conjunto de informações.

Como o Objects é do tipo TObject e todas as classes em Delphi são heranças dela, podemos associar nesta propriedade a instância de qualquer classe. Em C++ Builder, a herança deve ser explicitamente declarada. No trecho de código abaixo, a instância diferente de uma classe de informações é associada a cada uma das células da coluna 0 (zero).
var i : Integer;
Obj : TWInfoModulo;
begin
for i := 0 to Grupo.Count - 1 do begin
Obj := Grupo.ModuloAt(i);
sg.Objects [0,i+1] := Obj;
end;
end;

A associação poderia ter sido feita em cada uma das células do Grid se fosse necessário. No exemplo, entretanto, cada linha é relativa a apenas um objeto e por isso associei-o a apenas uma coluna e usei a informação compartilhada com as demais colunas da linha. Essa informação pode, então, ser usada no OnDrawCell para customizar a aparência do Grid.

Na verdade, usar o Objects é útil em qualquer um dos demais eventos. Por exemplo, para obter tanto a formatação da entrada de dados quanto os próprios valores para a edição do texto em cada uma das células, além da validação dos valores entrados pelo usuário:
procedure TWCenaRespons.DoOnSgCellChange (Sender: TObject; ACol, ARow: Longint; const Value: string);
var sg : TStringGrid;
lValue : String;
Obj : TWInfoModulo;
begin
sg := Sender As TStringGrid;

{ Só faz a verificação quando a célula sai do modo de edição }
if not sg.EditorMode then begin
lValue := UpperCase (Trim (Value));
Obj := sg.Objects [0,ARow] As TWInfoModulo;

{ Se não é um valor válido, retorna ao modo de edição na mesma linha/coluna }
if not Obj.ResponsavelValido (lValue) then begin
sg.Row := ARow;
sg.Col := ACol;
sg.EditorMode := true;
end;
end;
end;

O evento OnCellChange é acionado a cada novo caractere introduzido ou removido pelo usuário quando editando o valor de uma célula. Isso exige alguns cuidados para construir a resposta ao evento. Na resposta reproduzida acima, por exemplo, eu testo antes se o Grid está em modo de edição e só faço a validação do texto entrado pelo usuário quando a edição termina.

Ou seja, o evento também é acionado quando o editor perde o foco por qualquer razão e, nesta situação, a propriedade EditorMode do Grid é ajustada para falso, indicando que a célula não está mais em modo de edição. Caso o novo valor digitado pelo usuário seja inválido, uso essa mesma propriedade para forçar que uma nova entrada seja digitada para corrigir o problema.

26 de novembro de 2009

Delphi terá em 2010 versões para Linux e Mac OS

No evento Delphi Live! 2009 foi publicado um Roadmap listando uma série de novas funcionalidades e recursos planejados para serem incluídos em versões futuras do Delphi. Alguns dos recursos já fazem parte da versão 2010, que chegou recentemente ao mercado. Outras estão sendo anunciadas para breve, como é o caso das versões para as plataformas Linux e Mac OS - planejadas para sairem em meados de 2010. Veja abaixo a notícia que foi publicada na ComputerWorld:

O programa Delphi, ferramenta para desenvolvimento de aplicações, principalmente para PCs, ganhará no próximo ano versões para Linux e Mac OS. A informação é do vice-presidente para relacionamento com desenvolvedores da Embarcadero e evangelista da tecnologia, David Intersimone. Ele esteve esta semana no Brasil reunido com cerca de 600 desenvolvedores.

Intersimone, mas conhecido por David I, e que tem um blog no site Computerworld americana, veio ao Brasil para apresentar à comunidade que trabalha com a ferramenta de desenvolvimento as inovações incorporadas ao Delphi 2010, versão que chegou recentemente ao mercado.

O programa concorre com plataformas como .Net da Microsoft, Visual Studio e Java, passou para as mãos da norte-americana Embarcadero no meio do ano passado. Na época, a companhia comprou a divisão CodeGear da Borland, unidade responsável por ferramentas multiplataforma para bancos de dados. Entre os quais estavam o Delphi.

A Embarcadero ainda não está presente no Brasil e o atendimento aos usuários de Delphi no país continua sendo realizado pelo time da antiga Borland, comprada no ano passado pela Micro Focus.

David I diz que o Brasil tem uma comunidade atuante de Delphi e acredita que o suporte da tecnologia a outros ambientes deverá dar mais flexibilidade aos que utilizam a ferramenta para desenvolver aplicações. Até então, o programa falava somente Windows. Ele estima que a versão do produto compatível com o sistema operacional de código aberto Linux e o Mac OS da Apple deverá chegar ao mercado no meio do próximo ano.

Ao comentar sobre as novidades do Delphi 2010, o especialista destacou, que além do produto já está preparado para Windows 7, vem com interfaces baseadas no recurso touch. Essa funcionalidade vai permitir aos desenvolvedores criarem mais rapidamente aplicações interativas para serem acessadas com toque na tela para rodar em celulares, PCs, terminais bancários e em outros tipos de hardware.

David I afirma que uma das vantagens do recurso touch para os desenvolvedores e que eles vão poder levar essa funcionalidade também para as aplicações já existentes.

O Roadmap que citei no começo menciona ainda um tal Projeto Commodore, que seria a possibilidade de se compilar programas Delphi nativamente para versões 64-bits do Windows. Infelizmente, o Roadmap não estipula uma data para o lançamento dessa versão. Em outra ocasião a entrega foi prometida também para o meio de 2010 mas parece que esse não é um prazo realista.

24 de novembro de 2009

Criando Serviços do Windows - parte 2

O Delphi e o C++ Builder têm um tipo de projeto específico para a criação de Serviços do Windows. Então, o procedimento para criar uma estrutura básica de um Serviço nesses ambientes é selecionar o menu File -> New e escolher Service Application.

Um novo projeto será criado e incluirá um fonte com uma classe que representa o Serviço do Windows. A estrutura desse fonte é similar a um Data Module e permite que sejam adicionados componentes não-visuais para uso pelo Serviço - lembre-se que o serviço não tem interface gráfica e, portanto, não faz sentido admitir a inclusão de componentes visuais. O trecho abaixo reproduz a declaração inicial da classe gerada:
type
TWMyService = class(TService)
private
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;

Veja que a base para a classe de Serviço chama-se TService. É essa classe da VCL que encapsula um Serviço e quem introduz as propriedades que devemos configurar e os eventos que teremos que responder para que o Serviço execute suas tarefas.

O passo seguinte, então, é configurar visualmente as propriedades, conforme a necessidade. Segue abaixo uma lista com as principais propriedades da classe TService e uma breve explicação de sua utilidade.
DisplayName é o nome que identificará nosso serviço no Service Control Manager (SCM), que é a parte do Windows responsável pelo gerenciamento de Serviços e que pode ser acessado através da aplicação Services existente no Painel de Controle do Windows.
ServiceType é o tipo de Serviço que se está criando. O valor padrão (e situação mais comum) é stWin32, indicando que se trata de um serviço convencional. Os outros valores permitidos transformam o Serviço num Device Driver ou File System Driver.
ServiceStartName e Password indicam o nome de usuário e senha com o qual o Serviço irá se apresentar ao Windows (quando ServiceType está configurado com stWin32. As credenciais (permissões de acesso) desse usuário é que determinarão o que o Serviço pode ou não fazer dentro do Windows. Conforme dito no post anterior, pode-se informar um dos usuários criados e mantidos pelo Windows (Local System, LocalService ou Network Service). Se for deixado em branco, o Windows assume que deve ser considerado o usuário Local System.
Dependencies lista os Serviços dos quais nosso Serviço depende. Isto é, se nosso Serviço só funcionar quando um ou mais Serviços também estiverem funcionando corretamente, os nomes desses Serviços devem ser incluídos nesta lista. O SCM, então, só poderá iniciar a execução do nosso Serviço após receber confirmação de que os Serviços dos quais ele depende estão com status ok.
StartType indica o modo de inicialização do Serviço. O valor padrão é stAuto, sinalizando que o Serviço deve ser iniciado automaticamente, junto com o Windows e sem que seja necessário um usuário se logar.
WaitHint é o tempo máximo (em milissegundos) que o SCM deve aguardar quando interagindo com o Serviço. Assim, se o tempo se esgotar o SCM interpretará que o serviço "pendurou". Caso as tarefas executadas pelo Serviço sejam demoradas, é conveniente chamar em pontos estratégicos do código a função ReportStatus da classe TService. Isso permitirá ao SCM detectar que o Serviço está ativo mas executando uma operação demorada.
A propriedade Terminated informa ao Serviço que ele deve cessar a execução. Isso pode ser devido ao fato de que alguém solicitou a interrupção do Serviço ou que o próprio Serviço detectou que sua execução não é mais necessária.

Em relação aos eventos disponíveis para a classe TService, o principal é o OnExecute. É aqui que o funcionamento do Serviço ocorre, seja através do monitoramento de eventos, seja através da publicação de meios para comunicação com outros processos (ou ambos). O evento OnExecute é disparado uma única vez e, quando ele termina sua execução, determina também o término da execução do Serviço. Juntando esse fato com o funcionamento da propriedade Terminated citada antes, a codificação do OnExecute tem, na maioria das vezes, a seguinte aparência:
procedure TWMyService.ServiceExecute(Sender: TService);
begin
while (not Terminated) do
begin
{ Processa aqui as tarefas relacionadas com o Serviço }
{ ... }

ReportStatus;
end;
end;

No caso do serviço que executa processos agendados na ABC71, esse laço dentro do evento OnExecute verifica de tempos em tempos os registros existentes no banco de dados do ERP e executa o processo numa thread separada quando é atingida a data e hora agendada para execução.

Isso levanta uma outra questão. Onde armazenar as configurações para conexão com o Banco de Dados ? É muito comum que isso seja feito através de um arquivo mas optei por armazenar tais informações no Registry do Windows, mais apropriado na minha opinião. Foi construído, então, um programa com interface gráfica que permite aos usuários configurar a conexão e monitorar a execução dos processos. Como o usuário usado pelo Serviço é diferente do usuário logado no Windows para realizar as confugurações, a gravação no Registry deve ser feita sob a chave HKEY_LOCAL_MACHINE, facilmente acessível por ambos.

23 de novembro de 2009

Criando Serviços do Windows - parte 1

Um Serviço do Windows é um tipo especial de programa projetado para executar sob demanda uma tarefa ou um conjunto de tarefas correlacionadas - o serviço fornecido. A característica mais marcante dos Serviços é o fato de que eles não possuem interface gráfica, isto é, não se espera a intervenção direta de um usuário. Por isso, o funcionamente padrão deles é ficar em execução indeterminadamente, aguardando solicitações de outros programas ou outro evento qualquer para o qual tenha sido programado.

Assim, o objetivo desse tipo de programa é fornecer um "serviço" especializado bem determinado, de modo que os outros programas podem se ater na sua própria execução e delegar ao Serviço aquelas tarefas que ele disponibiliza. Veja que essa característica torna o Serviço uma espécie de componente, capaz de atender diversos programas diferentes, mesmo que esses outros programas não tenham sido criados por você. Os programas que queiram se beneficiar do Serviço, então, terão apenas que saber como acessá-lo, isto é, qual é a sua interface. Alguns exemplos de situações onde se pode aplicar esse conceito são listados a seguir : banco de dados, Web Server, Agendador de Tarefas, Assinatura digital de documentos, etc.

De forma simplificada, podemos classificar os Serviço em duas categorias básicas, de acordo com a técnica utilizada para ativar a execução de uma tarefa. Há os que são acionados deliberadamente por outro programa e que, portanto, apenas reagem a uma solicitação, executando imediatamente a tarefa requisitada e retornando algum resultado (quando for o caso). Se encaixam nessa categoria serviços para atender ações em bancos de dados ou num Web Server ou ainda um serviço específico para assinar documentos digitalmente. A outra categoria são os Serviços que monitoram a ocorrência de um evento e executam algum processamento quando o evento é detectado. Na ABC71, por exemplo, temos um Serviço que executa processos e relatórios agendados em qualquer das estações com o Client do ERP. Ele faz isso monitorando os agendamentos registrados em uma tabela no banco de dados e executando os processos em fila quando chega a data e hora configuradas. Da forma como está exposto aqui, um mesmo serviço pode ser projetado para funcionar sem problemas até mesmo com ambas as maneiras, dependendo da situação.

Uma outra propriedade importante diz respeito à inicialização do Serviço: ele pode ser configurado para iniciar junto com o Windows, sem que seja necessário fazer login no Windows. Isso é particularmente útil quando o Serviço é instalado num servidor para atender às demandas através de uma rede, pois neste cenário o Serviço deve estar disponível independentemente se um login foi feito no Windows do servidor ou não.

Mas, se não há um login feito, quais políticas de segurança são aplicadas ao programa ? Isto é, como é determinado o que o Serviço pode ou não pode acessar ? Quais arquivos, entradas no Registry ou outro recurso qualquer do Windows ? Uma das configurações permitidas num Serviço é justamente um nome de usuário e sua senha, de modo que as permissões atribuídas a esse usuário é que determinam o que um Serviço pode ou não fazer. É permitido também escolher um dos 3 usuários padrões, criados e mantidos pelo Windows (Local System, LocalService ou Network Service), cada um com seu próprio conjunto de credenciais. Veja aqui uma explicação bem curta de cada uma dessas contas.

O Windows possui uma API própria para a criação e manipulação de Serviços. No entanto, o RAD Studio (Delphi e C++ Builder) e o Visual Studio (C#, Visual Basic) encapsulam em classes uma solução mais prática, que não exige acesso direto às APIs. Num próximo post, mostro a classe publicada no Delphi/C++ Builder e monto um exemplo simples de Serviço.

19 de novembro de 2009

Customizando a aparência de um Grid em Delphi - parte 3

Há alguns dias, tratei num post a customização da aparência de um Grid, onde mostrei alguns conceitos básicos do objeto Canvas do Delphi e C++ Builder, usado para fazer a customização num StringGrid. Os mesmos conceitos são válidos para os DBGrids mas, como há algumas diferenças, vou abordar aqui a customização desse tipo de grid.

A diferença mais importante está nos parâmetros que são passados para o evento de pintura de uma célula:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState);

No caso do DBGrid, ao invés de nos informar a linha e a coluna que se está desenhando, o evento nos dá o campo atual da query, representado pelo parâmetro Field. Para ficar mais claro o que está acontecendo, a ordem de desenho das células é a seguinte: primeiro, um registro da query é posicionado; depois, o evento de desenho é disparado para cada uma das colunas (os campos ou Fields) selecionados na query. Novos registros são posicionados até que a parte visível do grid esteja completa ou que o fim do result set seja atingido. Com isso, cada novo registro é desenhado numa nova linha e, obviamente, cada campo ocupa uma coluna.

O trecho de código abaixo pinta o valor de um Field numérico de azul (quando positivo) ou vermelho (quando negativo).
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState);
var yCalc, xCalc : integer;
Texto : String;
lCanvas : TCanvas;
DBGrid : TDBGrid;
begin
{ Salva em variáveis locais para facilitar a clareza }
DBGrid := Sender As TDBGrid;
lCanvas := DBGrid.Canvas;

if (not (gdFixed in State) ) and
(Field.FieldName = 'VALOR')
then begin
{ Formata o valor para apresentação }
texto := FormatFloat ('###,##0.00', Field.AsFloat);

{ Cor de fundo para célula depende se está selecionada }
if gdSelected in State then
lCanvas.Brush.Color := clHighlight
else
lCanvas.Brush.Color := DBGrid.Color;

{ Cor do fonte para o campo, de acordo com o valor }
if Field.AsFloat >= 0 then
lCanvas.Font.Color := clBlue
else
lCanvas.Font.Color := clRed;

{ Preenche com a cor de fundo }
lCanvas.FillRect(Rect);

{ Calcula posição para centralizar o texto na vertical }
yCalc := lCanvas.TextHeight (texto);
yCalc := Rect.Top + (Rect.Bottom - Rect.Top - yCalc) div 2;

{ calcula posição para alinha valor numérico à direita }
xCalc := lCanvas.TextWidth (texto);
xCalc := Rect.Right - xCalc - 3;

lCanvas.TextRect (Rect, xCalc, yCalc, texto);
end
else
DBGrid.DefaultDrawDataCell (Rect, Field, State);
end;

Observe que este código precisa tratar apenas o Field que queremos - distinguido pelo nome do campo no banco de dados, ao contrário do código postado a respeito da aparência do StringGrid. Aqui, o tratamento para os demais fields e states é repassado para a função DefaultDrawDataCell do Grid, que cuida de desenhar e formatar as células que não nos interessa customizar.

E é possível determinar o número da linha atual? Há uma propriedade no DataSet chamada RecNo que indica o número sequencial do registro que está atualmente posicionado. Como o TField está associado a um DataSet (query ou table), posso usar o RecNo para, por exemplo, pintar as linhas do grid com cores alternadas, dando o efeito de zebrado:
{ Cor de fundo para as linhas }
if (Field.DataSet.RecNo mod 2) = 0 then
lCanvas.Brush.Color := clSilver
else
lCanvas.Brush.Color := DBGrid.Color;

A diferença entre os eventos OnDrawDataCell e OnDrawColumnCell é que esse segundo recebe um parâmetro com as configurações da coluna do Grid, isto é, um TColumn) ao invés do TField. Como uma das propriedades do TColumn é justamente o Field associado, adaptar o código mostrado neste post é simples. E, de acordo com o help do Delphi, é preferível usar a versão com o TColumn pois a outra é considerada obsoleta.

Embora as funções e propriedades do objeto Canvas tenha sido apresentado num contexto específico nessa série de posts sobre aparência de Grids, os conceitos podem ser usados para trabalhar em qualquer contexto onde haja um Canvas.

Mais Informações
Customizando a aparência de um Grid em Delphi - parte 1 e parte 2

15 de novembro de 2009

Google anuncia novos projetos

Parece que o pessoal do Google não consegue parar de se mexer... Além do estardalhaço que eles vêm fazendo para divulgar o Google Wave (que ainda não foi oficialmente lançado), esta semana eles fizerem mais dois anúncios relacionados a novas linhas de pesquisa em que a empresa está se envolvendo. Na verdade, o que eles anunciaram foram os primeiros resultados dessas novas linhas.

O primeiro anúncio foi que eles estão construindo uma nova linguagem de programação, chamada GO. Mas porque criar uma nova linguagem de programação se já há uma infinidade delas por aí, com os mais variados propósitos e uma vasta gama de características diferentes ?

A proposta, segundo o site oficial do GO, é resolver duas frustrações que os desenvolvedores da linguagem têm. Para eles, as linguagens existentes hoje cujo código resulta em programas com melhor performance - como C/C++ - têm um processo de compilação bastante lento. Dentre as razões alegadas para essa lentidão, eles citam a existência de arquivos header que contêm as pré definições necessárias para se usar funções e classes num programa e a tipologia de dados do C/C++. Ambas as questões são resolvidas, com a eliminação das pré definições e com a introdução de tipos de dados dinâmicos, no mesmo estilo dos tipos existentes no JavaScript. Para finalizar, a nova linguagem não possui a ideia de ponteiros no sentido entendido pelo C/C++ e outras, o que permitiu também a criação de um garbage collector mais eficiente. Isto é, o gerenciamento da memória do GO é automatizado como no Java e no .NET.

A outra frustração que a linguagem procura resolver é o fato de que o hardware dos computadores tem evoluido muito, em especial em relação ao número de processadores, enquanto as linguagens de programação não têm acompanhado tal evolução. Neste sentido, a sintaxe do GO procura facilitar a criação de programas que tirem o máximo desse tipo de arquitetura, de modo que o resultado são programas pequenos e leves com capacidade de trocar informações entre si e sincronizar suas execuções de forma nativa, isto é, tudo contemplado na própria sintaxe da linguagem.

Embora os engenheiros do Google estejam usando internamente esta ferramenta, o site do GO alerta que ela ainda não é madura o suficiente pra uso em projetos comerciais e que a divulgação da linguagem neste ponto tem justamente o objetivo de obter sugestões da comunidade de desenvolvedores para complementá-la e testá-la.

A segunda linha de pesquisas que eles revelaram diz respeito à comunicação entre os navegadores de internet (os browsers) e os servidores da Internet. O mecanismo existente hoje é o protocolo HTTP, estabelecido como padrão nos primórdios da internet comercial. A pesquisa do Google tenta minimizar alguns gargalos decorrentes da forma como esse protocolo permite a um navegador de internet receber certos tipos de informação do servidor Web e como o servidor as disponibiliza. São recursos como streams de áudio e vídeo e a compactação de dados de controle do HTTP, além do modo como o servidor prioriza as requisições feitas pelo navegador.

O novo protocolo foi chamado de SPDY (speedy) e, segundo o blog dos pesquisadores, os testes preliminares em laboratório foram animadores, resultando em cargas de páginas até 55% mais rápidas. Para o teste, foi usado um servidor de Web criado para se comunicar usando o SPDY e uma versão modificada do Chome, compatibilizado para entender o novo protocolo. Novamente, a pesquisa ainda não gerou um produto e a fase atual do desenho no novo protocolo é de abertura para que a comunidade de desenvolvedores possa conhecê-lo e dar sugestões para melhorá-lo.

Veja neste link a notícia publicada no site IDG Now! sobre o SPDY.

10 de novembro de 2009

Customizando a aparência de um Grid em Delphi - parte 2

Continuando o post anterior, sobre como customizar a aparência de grids em Delphi, mostro aqui exemplos práticos dessa customização.

Os parâmetros aceitos pelo evento OnDrawCell num StringGrid são mostrados abaixo.
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);

Este evento é disparado uma vez para cada célula do Grid que precisar ser desenhada. Além do tradicional Sender (que representa o próprio grid que está gerando o evento), o evento também trás a linha e coluna que identifica qual é a célula desenhada. O parâmetro Rect indica um retângulo dentro do Canvas cujas coordenadas delimitam a célula a ser desenhada, isto é, é o espaço do Canvas reservado para o desenho da Célula indica pelos parâmetros ACol, ARow. O último parâmetro - State - é um conjunto de indicadores que sinalizam condições especiais para o desenho da célula. São estados tais como se a célula está selecionada ou não, se está com o foco para entrada de dados e se está numa das linhas ou colunas fixas do Grid.

Antes de prosseguir com a inclusão de código como resposta a esse evento, vou modificar a propriedade DefaultDrawing do StringGrid para False. Isso avisa a VCL que nós estamos assumindo o controle do desenho das células e que a VCL não deve executar as suas rotinas internas associadas à exibição dos dados de cada célula. Isto dito, segue um exemplo de resposta padrão para modificar as cores apresentadas no texto de um stringGrid:
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var yCalc : integer;
Texto : String;
lCanvas : TCanvas;
begin
{ Salva em variáveis locais por questão de clareza do código }
texto := StringGrid1.Cells[ACol, ARow];
lCanvas := StringGrid1.Canvas;

if gdFixed in State then begin
{ Cor do fonte e de fundo para linhas e colunas fixas }
lCanvas.Font.Color := clBlack;
lCanvas.Brush.Color := StringGrid1.FixedColor;
end else
if gdSelected in State then begin
{ Cor do fonte e de fundo para linhas e colunas selecionadas }
lCanvas.Font.Color := clWhite;
lCanvas.Brush.Color := clHighlight;
end
else begin
{ Cor do fonte e de fundo para linhas e colunas com dados }
if ACol mod 2 = 0 then
lCanvas.Font.Color := clBlue { azul nas colunas pares}
else
lCanvas.Font.Color := clRed; { vermelho nas ímpares }
lCanvas.Brush.Color := StringGrid1.Color;
end;

{ Preenche com a cor de fundo }
lCanvas.FillRect(Rect);

{ Calcula posição para centralizar o texto na vertical }
yCalc := lCanvas.TextHeight (texto);
yCalc := Rect.Top + (Rect.Bottom - Rect.Top - yCalc) div 2;
lCanvas.TextRect (Rect, Rect.Left + 3, yCalc, texto);
end;

Seguem algumas considerações sobre o trecho acima. A primeira coisa a observar é que grande parte do código é dedicada a configurar as propriedades do Canvas - o Brush para pintar a cor de fundo da célula e a Font para determinar a cor do texto. Algumas dessas configurações consideram o "Estado" da célula, isto é, se ela está selecionada ou se é parte da linha e coluna fixa. Poderia ainda ter levado em consideração também qual é a célula que está com foco e dar-lhe algum destaque.

O outro objeto disponível (Pen) não é alterado neste exemplo mas poderia ter sido caso quiséssemos desenhar linhas com características distintas. Isso inclui a borda da célula. Repare apenas que o Rect passado no parâmetro não considera a borda da célula, de modo que teríamos que expandir manualmente este retângulo caso quiséssemos aplicar uma cor ou espessura diferente à borda.

Uma coisa importante a ressaltar é que o Grid em si possui uma propriedade Font cuja configuração é usada como padrão para desenhar os textos, ou seja, o que configuramos nela é repassado para o Canvas antes da pintura do Grid iniciar. Modificar a Font do Grid, então, dispara a pintura do componente como um todo - e não de uma célula específica. Por isso, alteramos diretamente a fonte do Canvas no evento de pintura da célula, evitando que novos eventos de pintura sejam gerados eternamente.

No exemplo, as colunas pares são escritas em azul e as ímpares em vermelho. Claro que faria mais sentido interpretar o texto da célula e aplicar-lher cores mais adequadamente.

A linha de código lCanvas.FillRect(Rect) usa as configurações feitas no Brush para pintar o fundo da célula antes de aplicarmos o texto associado a ela. O comando lCanvas.TextRect (Rect, Rect.Left + 3, yCalc, texto) usa as configurações de fonte do Canvas para desenhar o texto da célula centralizado na vertical em relação ao retângulo reservado para a célula.